From a86fd7c621a907f27d1d6be64c014a83551fe460 Mon Sep 17 00:00:00 2001 From: dP Date: Wed, 25 Dec 2024 20:34:06 +0500 Subject: [PATCH] Update to 15.0-beta1 --- .changelog | 17 - .dorpsgek.yml | 4 + .github/changelog.sh | 4 +- .github/unused-strings.py | 12 - .github/workflows/ci-build.yml | 404 +- .github/workflows/ci-emscripten.yml | 65 + .github/workflows/ci-linux.yml | 126 + .github/workflows/ci-macos.yml | 90 + .github/workflows/ci-mingw.yml | 86 + .github/workflows/ci-nightly.yml | 82 + .github/workflows/ci-windows.yml | 84 + .github/workflows/codeql.yml | 44 +- .github/workflows/preview-build.yml | 9 +- .github/workflows/release-docs.yml | 2 +- .github/workflows/release-macos.yml | 4 +- .github/workflows/release-source.yml | 4 +- .github/workflows/upload-gog.yml | 2 +- .github/workflows/upload-steam.yml | 2 +- .ottdrev | 2 +- .release_date | 2 +- .version | 2 +- CMakeLists.txt | 11 +- CODINGSTYLE.md | 44 +- Doxyfile.in | 32 +- bin/ai/CMakeLists.txt | 1 + bin/ai/compat_14.nut | 2 + bin/ai/compat_15.nut | 6 + bin/game/CMakeLists.txt | 1 + bin/game/compat_14.nut | 2 + bin/game/compat_15.nut | 6 + changelog.txt => changelog.md | 3594 ++++++++-------- cmake/CompileFlags.cmake | 12 +- cmake/FindOgg.cmake | 37 + cmake/FindOpus.cmake | 37 + cmake/FindOpusFile.cmake | 40 + cmake/InstallAndPackage.cmake | 4 +- cmake/Options.cmake | 2 +- cmake/PackageBundle.cmake | 1 + cmake/scripts/Baseset.cmake | 2 +- cmake/scripts/CreateGRF.cmake | 2 +- cmake/scripts/Desktop.cmake | 2 +- cmake/scripts/FindVersion.cmake | 16 +- cmake/scripts/GenerateWidget.cmake | 2 +- cmake/scripts/Regression.cmake | 2 +- cmake/scripts/SquirrelExport.cmake | 2 +- cmake/scripts/SquirrelIncludes.cmake | 2 +- docs/admin_network.md | 27 +- docs/importing_town_data.md | 97 + docs/landscape.html | 233 +- docs/landscape_grid.html | 74 +- docs/openttd.6 | 5 - docs/releasing_openttd.md | 40 +- known-bugs.md | 426 ++ known-bugs.txt | 389 -- media/baseset/openttd.grf | Bin 553738 -> 554417 bytes media/baseset/openttd.grf.hash | 2 +- media/baseset/openttd/CMakeLists.txt | 2 + media/baseset/openttd/openttd.nfo | 1 + media/baseset/openttd/road_waypoints.nfo | 14 + media/baseset/openttd/road_waypoints.png | Bin 0 -> 8580 bytes media/baseset/opntitle.dat | Bin 134652 -> 138710 bytes os/emscripten/Dockerfile | 5 +- os/emscripten/README.md | 4 +- os/emscripten/cmake/FindLibLZMA.cmake | 8 +- os/emscripten/emsdk-liblzma.patch | 198 - os/emscripten/ports/liblzma.py | 139 + os/macosx/Info.plist.in | 2 + os/macosx/openttd.entitlements | 11 + regression/regression/info.nut | 2 +- regression/regression/result.txt | 18 +- regression/stationlist/info.nut | 2 +- src/3rdparty/icu/CMakeLists.txt | 2 + src/3rdparty/icu/scriptrun.cpp | 7 - src/3rdparty/icu/tests/CMakeLists.txt | 4 + src/3rdparty/icu/tests/test_srtest.cpp | 66 + src/3rdparty/md5/md5.h | 4 +- src/3rdparty/squirrel/squirrel/sqcompiler.cpp | 5 +- .../squirrel/squirrel/sqfuncstate.cpp | 19 +- src/3rdparty/squirrel/squirrel/sqvm.cpp | 8 +- src/CMakeLists.txt | 27 +- src/ai/ai_core.cpp | 34 +- src/ai/ai_gui.cpp | 14 +- src/ai/ai_info.cpp | 2 +- src/aircraft.h | 22 +- src/aircraft_cmd.cpp | 48 +- src/aircraft_gui.cpp | 11 + src/airport.cpp | 12 +- src/airport.h | 30 +- src/airport_gui.cpp | 91 +- src/animated_tile.cpp | 89 +- src/animated_tile_func.h | 4 +- src/animated_tile_map.h | 44 + src/articulated_vehicles.cpp | 35 +- src/autocompletion.cpp | 66 + src/autocompletion.h | 46 + src/autoreplace_cmd.cpp | 2 +- src/autoreplace_gui.cpp | 111 +- src/autoslope.h | 23 +- src/base_media_base.h | 12 +- src/base_media_func.h | 4 +- src/base_station_base.h | 47 +- src/blitter/32bpp_anim.cpp | 10 +- src/blitter/32bpp_anim.hpp | 2 +- src/blitter/32bpp_anim_sse2.hpp | 2 +- src/blitter/32bpp_anim_sse4.cpp | 28 +- src/blitter/32bpp_anim_sse4.hpp | 4 +- src/blitter/32bpp_optimized.cpp | 38 +- src/blitter/32bpp_optimized.hpp | 8 +- src/blitter/32bpp_simple.cpp | 16 +- src/blitter/32bpp_simple.hpp | 4 +- src/blitter/32bpp_sse2.cpp | 20 +- src/blitter/32bpp_sse2.hpp | 8 +- src/blitter/32bpp_sse4.hpp | 2 +- src/blitter/32bpp_sse_func.hpp | 8 +- src/blitter/32bpp_sse_type.h | 2 +- src/blitter/32bpp_ssse3.hpp | 2 +- src/blitter/40bpp_anim.cpp | 12 +- src/blitter/40bpp_anim.hpp | 4 +- src/blitter/8bpp_optimized.cpp | 26 +- src/blitter/8bpp_optimized.hpp | 6 +- src/blitter/8bpp_simple.cpp | 16 +- src/blitter/8bpp_simple.hpp | 4 +- src/blitter/base.hpp | 4 +- src/blitter/factory.hpp | 16 +- src/blitter/null.cpp | 12 +- src/blitter/null.hpp | 4 +- src/bmp.cpp | 328 +- src/bmp.h | 23 +- src/bootstrap_gui.cpp | 54 +- src/bridge.h | 7 +- src/bridge_gui.cpp | 69 +- src/bridge_map.cpp | 5 +- src/build_vehicle_gui.cpp | 265 +- src/cachecheck.cpp | 224 + src/cargo_type.h | 6 +- src/cargoaction.cpp | 2 +- src/cargoaction.h | 5 +- src/cargomonitor.cpp | 3 +- src/cargomonitor.h | 19 +- src/cargopacket.cpp | 10 +- src/cargopacket.h | 22 +- src/cargotype.cpp | 122 +- src/cargotype.h | 29 +- src/cheat.cpp | 17 - src/cheat_func.h | 1 - src/cheat_gui.cpp | 40 +- src/clear_cmd.cpp | 49 +- src/clear_func.h | 2 +- src/command.cpp | 2 +- src/command_func.h | 16 +- src/command_type.h | 21 +- src/company_base.h | 25 +- src/company_cmd.cpp | 110 +- src/company_cmd.h | 12 +- src/company_func.h | 2 +- src/company_gui.cpp | 392 +- src/company_manager_face.h | 6 +- src/company_type.h | 10 +- src/console.cpp | 18 +- src/console_cmds.cpp | 343 +- src/console_func.h | 2 +- src/console_gui.cpp | 85 +- src/console_internal.h | 2 +- src/core/alloc_type.hpp | 4 +- src/core/backup_type.hpp | 15 +- src/core/bitmath_func.hpp | 19 + src/core/container_func.hpp | 19 +- src/core/enum_type.hpp | 36 +- src/core/geometry_func.hpp | 2 +- src/core/kdtree.hpp | 66 +- src/core/math_func.hpp | 14 +- src/core/mem_func.hpp | 2 +- src/core/overflowsafe_type.hpp | 10 +- src/core/pool_func.cpp | 2 +- src/core/pool_func.hpp | 10 +- src/core/pool_type.hpp | 6 +- src/core/random_func.cpp | 20 +- src/core/random_func.hpp | 76 +- src/crashlog.cpp | 16 +- src/currency.cpp | 11 +- src/currency.h | 31 +- src/date_gui.cpp | 21 +- src/debug.cpp | 48 +- src/debug.h | 8 +- src/dedicated.cpp | 12 +- src/depot_cmd.cpp | 2 +- src/depot_gui.cpp | 102 +- src/direction_type.h | 6 +- src/disaster_vehicle.cpp | 25 +- src/disaster_vehicle.h | 2 +- src/dock_gui.cpp | 48 +- src/driver.cpp | 25 +- src/driver.h | 16 +- src/{widgets => }/dropdown.cpp | 76 +- ...dropdown_type.h => dropdown_common_type.h} | 76 +- src/dropdown_func.h | 26 + src/dropdown_type.h | 60 + src/economy.cpp | 65 +- src/economy_base.h | 11 +- src/economy_func.h | 2 +- src/economy_type.h | 10 +- src/effectvehicle.cpp | 88 +- src/effectvehicle_base.h | 2 +- src/elrail.cpp | 30 +- src/elrail_func.h | 1 + src/engine.cpp | 104 +- src/engine_base.h | 48 +- src/engine_gui.cpp | 16 +- src/engine_gui.h | 16 +- src/engine_type.h | 80 +- src/error.cpp | 8 +- src/error.h | 2 +- src/error_func.h | 4 +- src/error_gui.cpp | 32 +- src/fileio.cpp | 341 +- src/fileio_func.h | 62 +- src/fileio_type.h | 36 + src/fios.cpp | 176 +- src/fios.h | 3 +- src/fios_gui.cpp | 164 +- src/fontcache.cpp | 130 +- src/fontcache.h | 23 +- src/fontcache/freetypefontcache.cpp | 76 +- src/fontcache/spritefontcache.cpp | 62 +- src/fontcache/spritefontcache.h | 13 +- src/fontcache/truetypefontcache.cpp | 117 +- src/fontcache/truetypefontcache.h | 33 +- src/framerate_gui.cpp | 114 +- src/game/game_core.cpp | 17 +- src/game/game_gui.cpp | 39 +- src/game/game_info.cpp | 2 +- src/game/game_text.cpp | 18 +- src/gamelog.cpp | 8 +- src/gamelog.h | 4 +- src/gamelog_internal.h | 16 +- src/genworld.cpp | 183 +- src/genworld.h | 3 +- src/genworld_gui.cpp | 175 +- src/gfx.cpp | 175 +- src/gfx_func.h | 14 +- src/gfx_layout.cpp | 95 +- src/gfx_layout.h | 38 +- src/gfx_layout_fallback.cpp | 35 +- src/gfx_layout_fallback.h | 2 +- src/gfx_layout_icu.cpp | 67 +- src/gfx_layout_icu.h | 2 +- src/gfx_type.h | 35 +- src/gfxinit.cpp | 87 +- src/goal.cpp | 2 +- src/goal_gui.cpp | 38 +- src/goal_type.h | 4 +- src/graph_gui.cpp | 746 +++- src/graph_gui.h | 3 + src/ground_vehicle.cpp | 4 +- src/ground_vehicle.hpp | 11 +- src/group.h | 1 + src/group_cmd.cpp | 18 +- src/group_cmd.h | 4 +- src/group_gui.cpp | 299 +- src/group_gui.h | 12 + src/gui.h | 4 +- src/heightmap.cpp | 103 +- src/heightmap.h | 2 +- src/help_gui.cpp | 35 +- src/highscore.cpp | 35 +- src/highscore_gui.cpp | 22 +- src/hotkeys.cpp | 20 +- src/house.h | 55 +- src/industry.h | 114 +- src/industry_cmd.cpp | 317 +- src/industry_cmd.h | 4 +- src/industry_gui.cpp | 456 +- src/industry_map.h | 24 +- src/industry_type.h | 2 + src/industrytype.h | 40 +- src/ini.cpp | 2 +- src/ini_load.cpp | 83 +- src/ini_type.h | 32 +- src/intro_gui.cpp | 20 +- src/landscape.cpp | 239 +- src/landscape.h | 41 +- src/landscape_type.h | 2 +- src/lang/afrikaans.txt | 63 +- src/lang/arabic_egypt.txt | 64 +- src/lang/basque.txt | 62 +- src/lang/belarusian.txt | 75 +- src/lang/brazilian_portuguese.txt | 207 +- src/lang/bulgarian.txt | 3797 ++++++++++------- src/lang/catalan.txt | 199 +- src/lang/chuvash.txt | 23 +- src/lang/croatian.txt | 63 +- src/lang/czech.txt | 99 +- src/lang/danish.txt | 183 +- src/lang/dutch.txt | 195 +- src/lang/english.txt | 195 +- src/lang/english_AU.txt | 195 +- src/lang/english_US.txt | 191 +- src/lang/esperanto.txt | 79 +- src/lang/estonian.txt | 116 +- src/lang/faroese.txt | 60 +- src/lang/finnish.txt | 241 +- src/lang/french.txt | 219 +- src/lang/frisian.txt | 55 +- src/lang/gaelic.txt | 62 +- src/lang/galician.txt | 191 +- src/lang/german.txt | 207 +- src/lang/greek.txt | 672 +-- src/lang/hebrew.txt | 67 +- src/lang/hindi.txt | 25 +- src/lang/hungarian.txt | 142 +- src/lang/icelandic.txt | 62 +- src/lang/ido.txt | 24 +- src/lang/indonesian.txt | 76 +- src/lang/irish.txt | 65 +- src/lang/italian.txt | 132 +- src/lang/japanese.txt | 204 +- src/lang/korean.txt | 261 +- src/lang/latin.txt | 62 +- src/lang/latvian.txt | 264 +- src/lang/lithuanian.txt | 695 ++- src/lang/luxembourgish.txt | 288 +- src/lang/macedonian.txt | 23 +- src/lang/malay.txt | 57 +- src/lang/maltese.txt | 23 +- src/lang/marathi.txt | 27 +- src/lang/norwegian_bokmal.txt | 195 +- src/lang/norwegian_nynorsk.txt | 113 +- src/lang/persian.txt | 51 +- src/lang/polish.txt | 745 ++-- src/lang/portuguese.txt | 215 +- src/lang/romanian.txt | 159 +- src/lang/russian.txt | 260 +- src/lang/serbian.txt | 75 +- src/lang/simplified_chinese.txt | 2273 +++++----- src/lang/slovak.txt | 103 +- src/lang/slovenian.txt | 62 +- src/lang/spanish.txt | 88 +- src/lang/spanish_MX.txt | 72 +- src/lang/swedish.txt | 213 +- src/lang/tamil.txt | 84 +- src/lang/thai.txt | 63 +- src/lang/traditional_chinese.txt | 951 +++-- src/lang/turkish.txt | 80 +- src/lang/ukrainian.txt | 326 +- src/lang/urdu.txt | 75 +- src/lang/vietnamese.txt | 453 +- src/lang/welsh.txt | 164 +- src/language.h | 8 +- src/league_gui.cpp | 51 +- src/league_type.h | 2 +- src/linkgraph/demands.cpp | 36 +- src/linkgraph/demands.h | 6 +- src/linkgraph/linkgraph_gui.cpp | 37 +- src/linkgraph/linkgraph_gui.h | 6 +- src/linkgraph/linkgraph_type.h | 2 +- src/linkgraph/linkgraphjob.h | 14 +- src/linkgraph/linkgraphschedule.cpp | 19 +- src/linkgraph/linkgraphschedule.h | 2 +- src/linkgraph/refresh.cpp | 4 +- src/linkgraph/refresh.h | 2 +- src/livery.h | 12 +- src/main_gui.cpp | 48 +- src/map.cpp | 43 +- src/map_func.h | 96 +- src/map_type.h | 14 +- src/misc.cpp | 4 - src/misc/CMakeLists.txt | 4 - src/misc/array.hpp | 116 - src/misc/binaryheap.hpp | 85 +- src/misc/countedptr.hpp | 213 - src/misc/dbg_helpers.h | 34 +- src/misc/endian_buffer.hpp | 16 +- src/misc/fixedsizearray.hpp | 158 - src/misc/getoptdata.cpp | 77 +- src/misc/getoptdata.h | 91 +- src/misc/hashtable.hpp | 149 +- src/misc/lrucache.hpp | 2 +- src/misc_cmd.cpp | 8 +- src/misc_cmd.h | 4 +- src/misc_gui.cpp | 222 +- src/mixer.cpp | 47 +- src/mixer.h | 3 +- src/music.cpp | 65 +- src/music/CMakeLists.txt | 9 +- src/music/allegro_m.cpp | 6 +- src/music/allegro_m.h | 6 +- src/music/bemidi.cpp | 6 +- src/music/bemidi.h | 6 +- src/music/cocoa_m.cpp | 8 +- src/music/cocoa_m.h | 6 +- src/music/dmusic.cpp | 99 +- src/music/dmusic.h | 6 +- src/music/extmidi.cpp | 10 +- src/music/extmidi.h | 6 +- src/music/fluidsynth.cpp | 6 +- src/music/fluidsynth.h | 6 +- src/music/midi.h | 2 +- src/music/midifile.cpp | 150 +- src/music/midifile.hpp | 11 +- src/music/music_driver.hpp | 2 +- src/music/null_m.h | 6 +- src/music/win32_m.cpp | 36 +- src/music/win32_m.h | 6 +- src/music_gui.cpp | 52 +- src/network/CMakeLists.txt | 3 + src/network/core/config.h | 23 +- src/network/core/core.h | 6 + src/network/core/http_winhttp.cpp | 22 +- src/network/core/network_game_info.cpp | 35 +- src/network/core/network_game_info.h | 12 +- src/network/core/os_abstraction.cpp | 2 +- src/network/core/packet.cpp | 90 +- src/network/core/packet.h | 17 +- src/network/core/tcp.cpp | 6 +- src/network/core/tcp_admin.cpp | 8 + src/network/core/tcp_admin.h | 60 +- src/network/core/tcp_content.h | 4 +- src/network/core/tcp_coordinator.h | 2 +- src/network/core/tcp_game.cpp | 20 +- src/network/core/tcp_game.h | 82 +- src/network/core/tcp_listen.h | 4 +- src/network/core/udp.cpp | 7 +- src/network/network.cpp | 231 +- src/network/network_admin.cpp | 171 +- src/network/network_admin.h | 6 + src/network/network_base.h | 3 + src/network/network_chat_gui.cpp | 167 +- src/network/network_client.cpp | 210 +- src/network/network_client.h | 21 +- src/network/network_command.cpp | 8 +- src/network/network_content.cpp | 81 +- src/network/network_content.h | 4 +- src/network/network_content_gui.cpp | 89 +- src/network/network_content_gui.h | 4 +- src/network/network_coordinator.cpp | 14 +- src/network/network_crypto.cpp | 495 +++ src/network/network_crypto.h | 302 ++ src/network/network_crypto_internal.h | 353 ++ src/network/network_func.h | 14 +- src/network/network_gui.cpp | 502 +-- src/network/network_gui.h | 3 +- src/network/network_internal.h | 3 - src/network/network_query.cpp | 2 +- src/network/network_server.cpp | 426 +- src/network/network_server.h | 24 +- src/network/network_stun.cpp | 2 +- src/network/network_turn.cpp | 2 +- src/network/network_type.h | 29 +- src/network/network_udp.cpp | 4 +- src/newgrf.cpp | 2752 ++++++------ src/newgrf.h | 30 +- src/newgrf_act5.h | 31 + src/newgrf_airport.cpp | 65 +- src/newgrf_airport.h | 61 +- src/newgrf_airporttiles.cpp | 18 +- src/newgrf_airporttiles.h | 6 +- src/newgrf_animation_base.h | 23 +- src/newgrf_callbacks.h | 15 +- src/newgrf_canal.cpp | 24 +- src/newgrf_cargo.cpp | 23 +- src/newgrf_cargo.h | 3 + src/newgrf_class.h | 51 +- src/newgrf_class_func.h | 188 +- src/newgrf_commons.cpp | 34 +- src/newgrf_commons.h | 25 +- src/newgrf_config.cpp | 104 +- src/newgrf_config.h | 53 +- src/newgrf_debug_gui.cpp | 200 +- src/newgrf_engine.cpp | 112 +- src/newgrf_engine.h | 12 +- src/newgrf_generic.cpp | 12 +- src/newgrf_gui.cpp | 220 +- src/newgrf_house.cpp | 205 +- src/newgrf_house.h | 17 +- src/newgrf_industries.cpp | 121 +- src/newgrf_industries.h | 9 +- src/newgrf_industrytiles.cpp | 22 +- src/newgrf_industrytiles.h | 4 +- src/newgrf_object.cpp | 41 +- src/newgrf_object.h | 22 +- src/newgrf_profiling.cpp | 17 +- src/newgrf_railtype.cpp | 4 +- src/newgrf_railtype.h | 4 +- src/newgrf_roadstop.cpp | 121 +- src/newgrf_roadstop.h | 59 +- src/newgrf_roadtype.cpp | 4 +- src/newgrf_roadtype.h | 4 +- src/newgrf_sound.cpp | 98 +- src/newgrf_sound.h | 3 +- src/newgrf_spritegroup.cpp | 20 +- src/newgrf_spritegroup.h | 55 +- src/newgrf_station.cpp | 90 +- src/newgrf_station.h | 89 +- src/newgrf_storage.h | 8 +- src/newgrf_text.cpp | 54 +- src/newgrf_text.h | 56 +- src/newgrf_text_type.h | 50 + src/newgrf_town.cpp | 9 +- src/newgrf_town.h | 4 +- src/newgrf_townname.cpp | 8 +- src/newgrf_townname.h | 12 +- src/news_func.h | 16 +- src/news_gui.cpp | 601 +-- src/news_gui.h | 2 +- src/news_type.h | 25 +- src/object_base.h | 4 +- src/object_cmd.cpp | 56 +- src/object_gui.cpp | 660 +-- src/object_map.h | 4 +- src/openttd.cpp | 304 +- src/openttd.h | 4 +- src/order_base.h | 31 +- src/order_cmd.cpp | 56 +- src/order_gui.cpp | 114 +- src/order_type.h | 10 +- src/os/macosx/font_osx.cpp | 60 +- src/os/macosx/font_osx.h | 2 - src/os/macosx/osx_main.cpp | 2 +- src/os/macosx/string_osx.cpp | 65 +- src/os/macosx/string_osx.h | 4 +- src/os/unix/font_unix.cpp | 5 +- src/os/unix/unix.cpp | 15 +- src/os/unix/unix_main.cpp | 2 +- src/os/windows/crashlog_win.cpp | 21 +- src/os/windows/font_win32.cpp | 92 +- src/os/windows/font_win32.h | 5 +- src/os/windows/library_loader_win.cpp | 2 +- src/os/windows/ottdres.rc.in | 4 +- src/os/windows/string_uniscribe.cpp | 41 +- src/os/windows/string_uniscribe.h | 4 +- src/os/windows/win32.cpp | 283 +- src/os/windows/win32.h | 4 +- src/os/windows/win32_main.cpp | 26 +- src/osk_gui.cpp | 40 +- src/palette.cpp | 41 +- src/palette_func.h | 22 +- src/pathfinder/CMakeLists.txt | 3 +- src/pathfinder/aystar.cpp | 176 + src/pathfinder/{npf => }/aystar.h | 97 +- src/pathfinder/follow_track.hpp | 348 +- src/pathfinder/npf/CMakeLists.txt | 8 - src/pathfinder/npf/aystar.cpp | 307 -- src/pathfinder/npf/npf.cpp | 1337 ------ src/pathfinder/npf/npf_func.h | 92 - src/pathfinder/npf/queue.cpp | 489 --- src/pathfinder/npf/queue.h | 112 - src/pathfinder/pathfinder_type.h | 13 - src/pathfinder/water_regions.cpp | 183 +- src/pathfinder/water_regions.h | 2 - src/pathfinder/yapf/nodelist.hpp | 124 +- src/pathfinder/yapf/yapf.hpp | 2 - src/pathfinder/yapf/yapf_base.hpp | 225 +- src/pathfinder/yapf/yapf_cache.h | 1 + src/pathfinder/yapf/yapf_common.hpp | 135 +- src/pathfinder/yapf/yapf_costbase.hpp | 6 + src/pathfinder/yapf/yapf_costcache.hpp | 120 +- src/pathfinder/yapf/yapf_costrail.hpp | 195 +- src/pathfinder/yapf/yapf_destrail.hpp | 97 +- src/pathfinder/yapf/yapf_node.hpp | 79 +- src/pathfinder/yapf/yapf_node_rail.hpp | 157 +- src/pathfinder/yapf/yapf_node_road.hpp | 19 +- src/pathfinder/yapf/yapf_node_ship.hpp | 19 +- src/pathfinder/yapf/yapf_rail.cpp | 221 +- src/pathfinder/yapf/yapf_road.cpp | 186 +- src/pathfinder/yapf/yapf_ship.cpp | 125 +- src/pathfinder/yapf/yapf_ship_regions.cpp | 105 +- src/pathfinder/yapf/yapf_type.hpp | 3 + src/pbs.cpp | 44 +- .../dropdown_func.h => picker_func.h} | 14 +- src/picker_gui.cpp | 679 +++ src/picker_gui.h | 224 + src/provider_manager.h | 107 + src/rail.cpp | 10 +- src/rail.h | 12 +- src/rail_cmd.cpp | 115 +- src/rail_cmd.h | 4 +- src/rail_gui.cpp | 1010 ++--- src/rail_gui.h | 2 +- src/rail_map.h | 24 +- src/rail_type.h | 2 +- src/random_access_file.cpp | 50 +- src/random_access_file_type.h | 17 +- src/rev.cpp.in | 14 +- src/rev.h | 4 +- src/road.cpp | 6 +- src/road.h | 6 +- src/road_cmd.cpp | 160 +- src/road_cmd.h | 2 +- src/road_func.h | 2 + src/road_gui.cpp | 1070 +++-- src/road_gui.h | 2 +- src/road_map.cpp | 6 +- src/road_map.h | 6 +- src/road_type.h | 6 +- src/roadstop.cpp | 38 +- src/roadstop_base.h | 21 +- src/roadveh.h | 47 +- src/roadveh_cmd.cpp | 68 +- src/roadveh_gui.cpp | 17 +- src/saveload/afterload.cpp | 282 +- src/saveload/ai_sl.cpp | 6 +- src/saveload/cargomonitor_sl.cpp | 6 +- src/saveload/company_sl.cpp | 26 +- src/saveload/compat/settings_sl_compat.h | 36 +- src/saveload/compat/station_sl_compat.h | 6 - src/saveload/engine_sl.cpp | 32 +- src/saveload/game_sl.cpp | 4 +- src/saveload/gamelog_sl.cpp | 6 +- src/saveload/group_sl.cpp | 7 +- src/saveload/industry_sl.cpp | 151 +- src/saveload/labelmaps_sl.cpp | 230 +- src/saveload/linkgraph_sl.cpp | 4 +- src/saveload/map_sl.cpp | 32 +- src/saveload/misc_sl.cpp | 4 +- src/saveload/newgrf_sl.cpp | 47 +- src/saveload/oldloader.cpp | 50 +- src/saveload/oldloader.h | 10 +- src/saveload/oldloader_sl.cpp | 60 +- src/saveload/order_sl.cpp | 4 +- src/saveload/saveload.cpp | 460 +- src/saveload/saveload.h | 149 +- src/saveload/saveload_filter.h | 14 +- src/saveload/saveload_internal.h | 3 +- src/saveload/settings_sl.cpp | 2 +- src/saveload/station_sl.cpp | 137 +- src/saveload/strings_sl.cpp | 2 +- src/saveload/town_sl.cpp | 21 +- src/saveload/vehicle_sl.cpp | 110 +- src/saveload/waypoint_sl.cpp | 21 +- src/screenshot.cpp | 149 +- src/screenshot.h | 2 - src/screenshot_gui.cpp | 18 +- src/script/api/Doxyfile_AI.in | 168 +- src/script/api/Doxyfile_GS.in | 168 +- src/script/api/ai_changelog.hpp | 23 +- src/script/api/doxygen_filter.awk | 15 +- src/script/api/doxygen_filter.sh | 1 + src/script/api/game_changelog.hpp | 23 +- src/script/api/script_admin.cpp | 6 +- src/script/api/script_admin.hpp | 4 +- src/script/api/script_airport.cpp | 6 +- src/script/api/script_airport.hpp | 3 +- src/script/api/script_basestation.cpp | 2 +- src/script/api/script_basestation.hpp | 5 +- src/script/api/script_bridge.hpp | 2 + src/script/api/script_bridgelist.cpp | 4 +- src/script/api/script_cargolist.cpp | 6 +- src/script/api/script_client.hpp | 5 +- src/script/api/script_company.cpp | 6 +- src/script/api/script_company.hpp | 45 +- src/script/api/script_companymode.hpp | 4 +- src/script/api/script_controller.hpp | 4 +- src/script/api/script_date.hpp | 24 + src/script/api/script_engine.hpp | 13 +- src/script/api/script_error.hpp | 2 + src/script/api/script_event_types.hpp | 17 +- src/script/api/script_goal.cpp | 12 +- src/script/api/script_goal.hpp | 2 +- src/script/api/script_group.cpp | 2 +- src/script/api/script_group.hpp | 6 +- src/script/api/script_grouplist.hpp | 8 +- src/script/api/script_industry.cpp | 29 +- src/script/api/script_industry.hpp | 30 +- src/script/api/script_industrylist.hpp | 11 +- src/script/api/script_info_docs.hpp | 2 +- src/script/api/script_infrastructure.cpp | 4 +- src/script/api/script_infrastructure.hpp | 9 +- src/script/api/script_league.cpp | 18 +- src/script/api/script_league.hpp | 6 +- src/script/api/script_list.hpp | 6 +- src/script/api/script_marine.hpp | 2 + src/script/api/script_news.cpp | 2 +- src/script/api/script_object.cpp | 17 +- src/script/api/script_object.hpp | 33 +- src/script/api/script_order.cpp | 4 +- src/script/api/script_order.hpp | 6 +- src/script/api/script_priorityqueue.hpp | 8 +- src/script/api/script_rail.cpp | 5 +- src/script/api/script_rail.hpp | 4 +- src/script/api/script_road.cpp | 136 +- src/script/api/script_road.hpp | 4 +- src/script/api/script_sign.cpp | 4 +- src/script/api/script_sign.hpp | 2 + src/script/api/script_signlist.hpp | 8 +- src/script/api/script_station.hpp | 2 + src/script/api/script_stationlist.cpp | 2 +- src/script/api/script_story_page.cpp | 12 +- src/script/api/script_story_page.hpp | 22 +- src/script/api/script_subsidy.cpp | 2 +- src/script/api/script_subsidy.hpp | 7 +- src/script/api/script_subsidylist.hpp | 8 +- src/script/api/script_text.cpp | 31 +- src/script/api/script_text.hpp | 19 +- src/script/api/script_tile.cpp | 8 +- src/script/api/script_tile.hpp | 2 + src/script/api/script_town.cpp | 8 +- src/script/api/script_town.hpp | 58 +- src/script/api/script_townlist.hpp | 11 +- src/script/api/script_tunnel.cpp | 5 +- src/script/api/script_tunnel.hpp | 2 + src/script/api/script_types.hpp | 52 +- src/script/api/script_vehicle.cpp | 6 +- src/script/api/script_vehicle.hpp | 30 +- src/script/api/script_vehiclelist.hpp | 11 +- src/script/api/script_waypoint.hpp | 2 + src/script/script_config.hpp | 1 - src/script/script_gui.cpp | 84 +- src/script/script_info.cpp | 4 +- src/script/script_info.hpp | 2 +- src/script/script_instance.cpp | 78 +- src/script/script_scanner.cpp | 8 +- src/script/squirrel.cpp | 32 +- src/settings.cpp | 124 +- src/settings_gui.cpp | 377 +- src/settings_gui.h | 16 +- src/settings_internal.h | 26 +- src/settings_table.cpp | 72 +- src/settings_type.h | 187 +- src/settingsgen/CMakeLists.txt | 2 +- src/settingsgen/settingsgen.cpp | 105 +- src/ship.h | 12 +- src/ship_cmd.cpp | 46 +- src/ship_gui.cpp | 1 + src/signal.cpp | 26 +- src/signal_func.h | 12 +- src/signal_type.h | 4 +- src/signature.cpp | 20 +- src/signs.cpp | 2 +- src/signs_gui.cpp | 57 +- src/{widgets => }/slider.cpp | 60 +- src/{widgets => }/slider_func.h | 19 +- src/slope_func.h | 2 +- src/slope_type.h | 2 +- src/smallmap_gui.cpp | 156 +- src/social_integration.cpp | 2 +- src/sortlist_type.h | 38 +- src/sound.cpp | 165 +- src/sound/allegro_s.cpp | 4 +- src/sound/allegro_s.h | 4 +- src/sound/cocoa_s.cpp | 11 +- src/sound/cocoa_s.h | 4 +- src/sound/null_s.h | 4 +- src/sound/sdl2_s.cpp | 4 +- src/sound/sdl_s.cpp | 4 +- src/sound/sdl_s.h | 4 +- src/sound/win32_s.cpp | 14 +- src/sound/win32_s.h | 6 +- src/sound/xaudio2_s.cpp | 8 +- src/sound/xaudio2_s.h | 6 +- src/sound_func.h | 2 + src/sound_type.h | 12 +- src/soundloader.cpp | 73 + .../countedobj.cpp => soundloader_func.h} | 31 +- src/soundloader_opus.cpp | 95 + src/soundloader_raw.cpp | 52 + src/soundloader_type.h | 32 + src/soundloader_wav.cpp | 101 + src/sprite.h | 22 +- src/spritecache.cpp | 168 +- src/spritecache.h | 28 +- src/spritecache_internal.h | 10 +- src/spriteloader/grf.cpp | 46 +- src/spriteloader/grf.hpp | 6 +- src/spriteloader/sprite_file.cpp | 8 +- src/spriteloader/sprite_file_type.hpp | 4 +- src/spriteloader/spriteloader.hpp | 31 +- src/station.cpp | 66 +- src/station_base.h | 57 +- src/station_cmd.cpp | 796 ++-- src/station_cmd.h | 8 +- src/station_func.h | 3 +- src/station_gui.cpp | 352 +- src/station_gui.h | 6 +- src/station_kdtree.h | 10 +- src/station_map.h | 183 +- src/station_type.h | 30 +- src/statusbar_gui.cpp | 16 +- src/stdafx.h | 70 +- src/story.cpp | 4 +- src/story_base.h | 6 +- src/story_gui.cpp | 40 +- src/story_type.h | 2 +- src/strgen/CMakeLists.txt | 2 +- src/strgen/strgen.cpp | 76 +- src/strgen/strgen.h | 10 +- src/strgen/strgen_base.cpp | 46 +- src/string.cpp | 114 +- src/string_func.h | 9 +- src/strings.cpp | 286 +- src/strings_func.h | 8 +- src/strings_internal.h | 57 +- src/strings_type.h | 30 +- src/subsidy.cpp | 9 +- src/subsidy_gui.cpp | 16 +- src/subsidy_type.h | 2 +- src/survey.cpp | 19 +- src/table/airport_defaults.h | 70 +- src/table/airport_movement.h | 38 +- src/table/build_industry.h | 92 +- src/table/cargo_const.h | 28 +- src/table/clear_land.h | 8 +- src/table/elrail_data.h | 10 +- src/table/engines.h | 28 +- src/table/industry_land.h | 14 +- src/table/landscape_sprite.h | 253 +- src/table/newgrf_debug_data.h | 153 +- src/table/object_land.h | 2 +- src/table/palette_convert.h | 4 +- src/table/roadveh_movement.h | 2 +- src/table/settings.h.preamble | 48 +- src/table/settings/company_settings.ini | 23 +- src/table/settings/currency_settings.ini | 3 +- src/table/settings/difficulty_settings.ini | 7 +- src/table/settings/economy_settings.ini | 5 +- src/table/settings/game_settings.ini | 19 +- src/table/settings/gui_settings.ini | 44 +- src/table/settings/linkgraph_settings.ini | 3 +- src/table/settings/locale_settings.ini | 7 +- src/table/settings/misc_settings.ini | 35 +- src/table/settings/multimedia_settings.ini | 5 +- .../settings/network_private_settings.ini | 5 +- .../settings/network_secrets_settings.ini | 14 +- src/table/settings/network_settings.ini | 21 +- src/table/settings/news_display_settings.ini | 3 +- src/table/settings/old_gameopt_settings.ini | 13 +- src/table/settings/pathfinding_settings.ini | 227 +- src/table/settings/script_settings.ini | 7 +- src/table/settings/win32_settings.ini | 5 +- src/table/settings/window_settings.ini | 5 +- src/table/settings/world_settings.ini | 7 +- src/table/sprites.h | 51 +- src/table/station_land.h | 28 +- src/table/strgen_tables.h | 1 + src/table/string_colours.h | 2 +- src/table/town_land.h | 5 +- src/table/train_sprites.h | 6 +- src/table/tree_land.h | 4 +- src/table/unicode.h | 4 +- src/terraform_cmd.cpp | 44 +- src/terraform_gui.cpp | 31 +- src/tests/CMakeLists.txt | 1 + src/tests/mock_fontcache.h | 1 - src/tests/mock_spritecache.cpp | 9 +- src/tests/string_func.cpp | 30 +- src/tests/strings_func.cpp | 2 +- src/tests/test_network_crypto.cpp | 279 ++ src/tests/test_window_desc.cpp | 23 +- src/textbuf.cpp | 66 +- src/textbuf_gui.h | 1 - src/textbuf_type.h | 3 + src/texteff.cpp | 18 +- src/textfile_gui.cpp | 156 +- src/textfile_gui.h | 18 +- src/tgp.cpp | 41 +- src/tile_cmd.h | 14 +- src/tile_map.cpp | 25 +- src/tile_map.h | 36 +- src/tile_type.h | 10 +- src/tilearea_type.h | 9 +- src/tilehighlight_type.h | 4 +- src/tilematrix_type.hpp | 144 - src/timer/timer_game_common.cpp | 26 +- src/timer/timer_game_common.h | 2 +- src/timetable_cmd.cpp | 10 +- src/timetable_gui.cpp | 56 +- src/toolbar_gui.cpp | 156 +- src/town.h | 24 +- src/town_cmd.cpp | 400 +- src/town_cmd.h | 5 +- src/town_gui.cpp | 651 ++- src/town_kdtree.h | 9 +- src/town_map.h | 33 +- src/town_type.h | 89 +- src/townname.cpp | 256 +- src/townname_type.h | 2 +- src/track_type.h | 6 +- src/train.h | 35 +- src/train_cmd.cpp | 218 +- src/train_gui.cpp | 16 +- src/transparency.h | 2 +- src/transparency_gui.cpp | 10 +- src/transport_type.h | 2 +- src/tree_cmd.cpp | 61 +- src/tree_cmd.h | 2 +- src/tree_gui.cpp | 27 +- src/tree_map.h | 48 +- src/tunnelbridge_cmd.cpp | 152 +- src/tunnelbridge_cmd.h | 6 +- src/tunnelbridge_map.h | 2 +- src/vehicle.cpp | 69 +- src/vehicle_base.h | 60 +- src/vehicle_cmd.cpp | 54 +- src/vehicle_cmd.h | 2 +- src/vehicle_func.h | 16 +- src/vehicle_gui.cpp | 369 +- src/vehicle_gui.h | 2 +- src/vehicle_gui_base.h | 44 +- src/vehicle_type.h | 11 +- src/vehiclelist.cpp | 72 +- src/vehiclelist.h | 2 +- src/vehiclelist_func.h | 2 +- src/video/allegro_v.cpp | 25 +- src/video/allegro_v.h | 4 +- src/video/cocoa/cocoa_keys.h | 2 +- src/video/cocoa/cocoa_ogl.h | 8 +- src/video/cocoa/cocoa_ogl.mm | 12 +- src/video/cocoa/cocoa_v.h | 6 +- src/video/cocoa/cocoa_v.mm | 14 +- src/video/cocoa/cocoa_wnd.mm | 38 +- src/video/dedicated_v.cpp | 8 +- src/video/dedicated_v.h | 4 +- src/video/null_v.cpp | 6 +- src/video/null_v.h | 4 +- src/video/opengl.cpp | 70 +- src/video/opengl.h | 12 +- src/video/sdl2_default_v.h | 2 +- src/video/sdl2_opengl_v.cpp | 12 +- src/video/sdl2_opengl_v.h | 6 +- src/video/sdl2_v.cpp | 93 +- src/video/sdl2_v.h | 6 +- src/video/sdl_v.cpp | 39 +- src/video/sdl_v.h | 4 +- src/video/video_driver.hpp | 2 +- src/video/win32_v.cpp | 51 +- src/video/win32_v.h | 12 +- src/viewport.cpp | 223 +- src/viewport_gui.cpp | 10 +- src/viewport_kdtree.h | 12 +- src/viewport_sprite_sorter_sse4.cpp | 2 +- src/viewport_type.h | 8 +- src/void_cmd.cpp | 7 +- src/water.h | 3 +- src/water_cmd.cpp | 129 +- src/water_map.h | 85 +- src/waypoint.cpp | 11 +- src/waypoint_base.h | 11 +- src/waypoint_cmd.cpp | 259 +- src/waypoint_cmd.h | 9 +- src/waypoint_func.h | 7 +- src/waypoint_gui.cpp | 50 +- src/widget.cpp | 621 ++- src/widget_type.h | 134 +- src/widgets/CMakeLists.txt | 6 +- src/widgets/company_widget.h | 2 - src/widgets/framerate_widget.h | 1 - src/widgets/graph_widget.h | 13 +- src/widgets/industry_widget.h | 2 + src/widgets/misc_widget.h | 1 - src/widgets/network_widget.h | 12 - src/widgets/object_widget.h | 8 - src/widgets/picker_widget.h | 34 + src/widgets/rail_widget.h | 14 - src/widgets/road_widget.h | 15 +- src/widgets/settings_widget.h | 3 + src/widgets/sign_widget.h | 1 - src/widgets/smallmap_widget.h | 2 + src/widgets/town_widget.h | 10 + src/window.cpp | 172 +- src/window_gui.h | 73 +- src/window_type.h | 19 +- src/zoom_func.h | 20 +- src/zoom_type.h | 40 +- vcpkg.json | 5 +- 963 files changed, 38070 insertions(+), 33798 deletions(-) create mode 100644 .github/workflows/ci-emscripten.yml create mode 100644 .github/workflows/ci-linux.yml create mode 100644 .github/workflows/ci-macos.yml create mode 100644 .github/workflows/ci-mingw.yml create mode 100644 .github/workflows/ci-nightly.yml create mode 100644 .github/workflows/ci-windows.yml create mode 100644 bin/ai/compat_15.nut create mode 100644 bin/game/compat_15.nut rename changelog.txt => changelog.md (82%) create mode 100644 cmake/FindOgg.cmake create mode 100644 cmake/FindOpus.cmake create mode 100644 cmake/FindOpusFile.cmake create mode 100644 docs/importing_town_data.md create mode 100644 known-bugs.md delete mode 100644 known-bugs.txt create mode 100644 media/baseset/openttd/road_waypoints.nfo create mode 100644 media/baseset/openttd/road_waypoints.png delete mode 100644 os/emscripten/emsdk-liblzma.patch create mode 100644 os/emscripten/ports/liblzma.py create mode 100644 os/macosx/openttd.entitlements create mode 100644 src/3rdparty/icu/tests/CMakeLists.txt create mode 100644 src/3rdparty/icu/tests/test_srtest.cpp create mode 100644 src/animated_tile_map.h create mode 100644 src/autocompletion.cpp create mode 100644 src/autocompletion.h create mode 100644 src/cachecheck.cpp rename src/{widgets => }/dropdown.cpp (85%) rename src/{widgets/dropdown_type.h => dropdown_common_type.h} (74%) create mode 100644 src/dropdown_func.h create mode 100644 src/dropdown_type.h delete mode 100644 src/misc/array.hpp delete mode 100644 src/misc/countedptr.hpp delete mode 100644 src/misc/fixedsizearray.hpp create mode 100644 src/network/network_crypto.cpp create mode 100644 src/network/network_crypto.h create mode 100644 src/network/network_crypto_internal.h create mode 100644 src/newgrf_act5.h create mode 100644 src/newgrf_text_type.h create mode 100644 src/pathfinder/aystar.cpp rename src/pathfinder/{npf => }/aystar.h (61%) delete mode 100644 src/pathfinder/npf/CMakeLists.txt delete mode 100644 src/pathfinder/npf/aystar.cpp delete mode 100644 src/pathfinder/npf/npf.cpp delete mode 100644 src/pathfinder/npf/npf_func.h delete mode 100644 src/pathfinder/npf/queue.cpp delete mode 100644 src/pathfinder/npf/queue.h rename src/{widgets/dropdown_func.h => picker_func.h} (58%) create mode 100644 src/picker_gui.cpp create mode 100644 src/picker_gui.h create mode 100644 src/provider_manager.h rename src/{widgets => }/slider.cpp (63%) rename src/{widgets => }/slider_func.h (69%) create mode 100644 src/soundloader.cpp rename src/{misc/countedobj.cpp => soundloader_func.h} (55%) create mode 100644 src/soundloader_opus.cpp create mode 100644 src/soundloader_raw.cpp create mode 100644 src/soundloader_type.h create mode 100644 src/soundloader_wav.cpp create mode 100644 src/tests/test_network_crypto.cpp delete mode 100644 src/tilematrix_type.hpp create mode 100644 src/widgets/picker_widget.h diff --git a/.changelog b/.changelog index 104add03a6..e69de29bb2 100644 --- a/.changelog +++ b/.changelog @@ -1,17 +0,0 @@ -14.1 (2024-05-03) ------------------------------------------------------------------------- -Add: Check that towns can build roads before generating map (#12503) -Fix #12228: Ships could get lost due to pathfinder not considering reversing in some cases (#12474) -Fix #12433: Width of unit number display was too narrow (#12534) -Fix #12502: Desync related to vehicle replacement (#12512) -Fix #12506: Desync after new oil rig is constructed (#12511) -Fix #12584: Crash on some tar files during tar scan (#12586) -Fix: [SDL2] Keypad numbers did not function (#12596) -Fix: Houses and industry tiles could accept incorrect cargo (#12547) -Fix: Map generation stage strings were incorrect (#12549) -Fix: [Script] Allow only 255 league tables, as 255 itself is the invalid id sentinel (#12545) -Fix: Mark vehicle status bars dirty when a vehicle leaves unbunching depot (#12516) -Fix: Do not show train waiting for unbunching as waiting for free path (#12515) -Fix: Smooth outliers in unbunching round trip calculations (#12513) - - diff --git a/.dorpsgek.yml b/.dorpsgek.yml index 4b09738b87..2a1dc5f0f5 100644 --- a/.dorpsgek.yml +++ b/.dorpsgek.yml @@ -14,3 +14,7 @@ notifications: pull-request: issue: tag-created: + workflow-run: + only: + - .github/workflows/release.yml + - .github/workflows/ci-nightly.yml diff --git a/.github/changelog.sh b/.github/changelog.sh index b057ad475f..26a66d6bdd 100755 --- a/.github/changelog.sh +++ b/.github/changelog.sh @@ -5,8 +5,8 @@ tag=$(git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null | sed 's@\ # If we are a tag, show the part of the changelog till (but excluding) the last stable if [ -n "$tag" ]; then grep='^[0-9]\+\.[0-9]\+[^-]' - next=$(cat changelog.txt | grep '^[0-9]' | awk 'BEGIN { show="false" } // { if (show=="true") print $0; if ($1=="'$tag'") show="true"} ' | grep "$grep" | head -n1 | sed 's/ .*//') - cat changelog.txt | awk 'BEGIN { show="false" } /^[0-9]+.[0-9]+/ { if ($1=="'$next'") show="false"; if ($1=="'$tag'") show="true";} // { if (show=="true") print $0 }' + next=$(cat changelog.md | grep '^[0-9]' | awk 'BEGIN { show="false" } // { if (show=="true") print $0; if ($1=="'$tag'") show="true"} ' | grep "$grep" | head -n1 | sed 's/ .*//') + cat changelog.md | awk 'BEGIN { show="false" } /^[0-9]+.[0-9]+/ { if ($1=="'$next'") show="false"; if ($1=="'$tag'") show="true";} // { if (show=="true") print $0 }' exit 0 fi diff --git a/.github/unused-strings.py b/.github/unused-strings.py index 527a938dc5..1ebb652982 100644 --- a/.github/unused-strings.py +++ b/.github/unused-strings.py @@ -39,7 +39,6 @@ def read_language_file(filename, strings_found, errors): skip = SkipType.NONE length = 0 common_prefix = "" - last_tiny_string = "" with open(filename) as fp: for line in fp.readlines(): @@ -114,17 +113,6 @@ def read_language_file(filename, strings_found, errors): name = line.split(":")[0].strip() strings_defined.append(name) - # If a string ends on _TINY or _SMALL, it can be the {TINY} variant. - # Check for this by some fuzzy matching. - if name.endswith(("_SMALL", "_TINY")): - last_tiny_string = name - elif last_tiny_string: - matching_name = "_".join(last_tiny_string.split("_")[:-1]) - if name == matching_name: - strings_found.add(last_tiny_string) - else: - last_tiny_string = "" - if skip == SkipType.EXTERNAL: strings_found.add(name) skip = SkipType.LENGTH diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 953efe7bca..8805550968 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -1,4 +1,4 @@ -name: CI +name: CI - Build on: pull_request: @@ -17,79 +17,22 @@ jobs: emscripten: name: Emscripten - 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 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup cache - uses: actions/cache@v4 - with: - path: /emsdk/upstream/emscripten/cache - key: 3.1.42-${{ runner.os }} - - - name: Patch Emscripten to support LZMA - run: | - cd /emsdk/upstream/emscripten - patch -p1 < ${GITHUB_WORKSPACE}/os/emscripten/emsdk-liblzma.patch - - - name: Build (host tools) - run: | - mkdir build-host - cd build-host - - echo "::group::CMake" - cmake .. -DOPTION_TOOLS_ONLY=ON - echo "::endgroup::" - - echo "::group::Build" - echo "Running on $(nproc) cores" - cmake --build . -j $(nproc) --target tools - echo "::endgroup::" - - - name: Install GCC problem matcher - uses: ammaraskar/gcc-problem-matcher@master - - - name: Build - run: | - mkdir build - cd build - - echo "::group::CMake" - emcmake cmake .. -DHOST_BINARY_DIR=../build-host - echo "::endgroup::" - - echo "::group::Build" - echo "Running on $(nproc) cores" - cmake --build . -j $(nproc) --target openttd - echo "::endgroup::" + uses: ./.github/workflows/ci-emscripten.yml + secrets: inherit linux: strategy: fail-fast: false matrix: include: - - name: Clang - Debug - compiler: clang-15 - cxxcompiler: clang++-15 + - name: Clang + compiler: clang + cxxcompiler: clang++ libraries: libsdl2-dev - - name: Clang - Release - compiler: clang-15 - cxxcompiler: clang++-15 - libraries: libsdl2-dev - extra-cmake-parameters: -DCMAKE_BUILD_TYPE=RelWithDebInfo -DOPTION_USE_ASSERTS=OFF - name: GCC - SDL2 compiler: gcc cxxcompiler: g++ libraries: libsdl2-dev - - name: GCC - SDL1.2 - compiler: gcc - cxxcompiler: g++ - libraries: libsdl1.2-dev - name: GCC - Dedicated compiler: gcc cxxcompiler: g++ @@ -99,332 +42,54 @@ jobs: name: Linux (${{ matrix.name }}) - runs-on: ubuntu-latest - env: - CC: ${{ matrix.compiler }} - CXX: ${{ matrix.cxxcompiler }} + uses: ./.github/workflows/ci-linux.yml + secrets: inherit - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup vcpkg caching - uses: actions/github-script@v7 - with: - script: | - core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); - core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - core.exportVariable('VCPKG_BINARY_SOURCES', 'clear;x-gha,readwrite') - - - name: Install vcpkg - run: | - git clone https://github.com/microsoft/vcpkg - ./vcpkg/bootstrap-vcpkg.sh -disableMetrics - - - name: Install dependencies - run: | - echo "::group::Update apt" - sudo apt-get update - echo "::endgroup::" - - echo "::group::Install dependencies" - sudo apt-get install -y --no-install-recommends \ - liballegro4-dev \ - libcurl4-openssl-dev \ - libfontconfig-dev \ - libharfbuzz-dev \ - libicu-dev \ - liblzma-dev \ - liblzo2-dev \ - ${{ matrix.libraries }} \ - zlib1g-dev \ - # EOF - - echo "::group::Install vcpkg dependencies" - - # Disable vcpkg integration, as we mostly use system libraries. - mv vcpkg.json vcpkg-disabled.json - - # We only use breakpad from vcpkg, as its CMake files - # are a bit special. So the Ubuntu's variant doesn't work. - ./vcpkg/vcpkg install breakpad - - echo "::endgroup::" - env: - DEBIAN_FRONTEND: noninteractive - - - name: Get OpenGFX - run: | - mkdir -p ~/.local/share/openttd/baseset - cd ~/.local/share/openttd/baseset - - echo "::group::Download OpenGFX" - curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip - echo "::endgroup::" - - echo "::group::Unpack OpenGFX" - unzip opengfx-all.zip - echo "::endgroup::" - - rm -f opengfx-all.zip - - - name: Install GCC problem matcher - uses: ammaraskar/gcc-problem-matcher@master - - - name: Build - run: | - mkdir build - cd build - - echo "::group::CMake" - cmake .. -DCMAKE_TOOLCHAIN_FILE=${GITHUB_WORKSPACE}/vcpkg/scripts/buildsystems/vcpkg.cmake ${{ matrix.extra-cmake-parameters }} - echo "::endgroup::" - - echo "::group::Build" - echo "Running on $(nproc) cores" - cmake --build . -j $(nproc) - echo "::endgroup::" - - - name: Test - run: | - ( - cd build - ctest -j $(nproc) --timeout 120 - ) - - # Re-enable vcpkg. - mv vcpkg-disabled.json vcpkg.json - - # Check no tracked files have been modified. - git diff --exit-code + with: + compiler: ${{ matrix.compiler }} + cxxcompiler: ${{ matrix.cxxcompiler }} + libraries: ${{ matrix.libraries }} + extra-cmake-parameters: ${{ matrix.extra-cmake-parameters }} macos: strategy: fail-fast: false matrix: include: - - arch: arm64 + - name: arm64 - Debug + arch: arm64 full_arch: arm64 + extra-cmake-parameters: -DCMAKE_BUILD_TYPE=Debug + - name: arm64 - Release + arch: arm64 + full_arch: arm64 + extra-cmake-parameters: -DCMAKE_BUILD_TYPE=RelWithDebInfo -DOPTION_USE_ASSERTS=OFF - name: Mac OS (${{ matrix.arch }}) + name: Mac OS (${{ matrix.name }}) - runs-on: macos-14 - env: - MACOSX_DEPLOYMENT_TARGET: 10.13 + uses: ./.github/workflows/ci-macos.yml + secrets: inherit - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup vcpkg caching - uses: actions/github-script@v7 - with: - script: | - core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); - core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - core.exportVariable('VCPKG_BINARY_SOURCES', 'clear;x-gha,readwrite') - - - name: Install vcpkg - run: | - git clone https://github.com/microsoft/vcpkg - ./vcpkg/bootstrap-vcpkg.sh -disableMetrics - - - name: Install OpenGFX - run: | - mkdir -p ~/Documents/OpenTTD/baseset - cd ~/Documents/OpenTTD/baseset - - echo "::group::Download OpenGFX" - curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip - echo "::endgroup::" - - echo "::group::Unpack OpenGFX" - unzip opengfx-all.zip - echo "::endgroup::" - - rm -f opengfx-all.zip - - - name: Install GCC problem matcher - uses: ammaraskar/gcc-problem-matcher@master - - - name: Build - run: | - mkdir build - cd build - - echo "::group::CMake" - cmake .. \ - -DCMAKE_OSX_ARCHITECTURES=${{ matrix.full_arch }} \ - -DVCPKG_TARGET_TRIPLET=${{ matrix.arch }}-osx \ - -DCMAKE_TOOLCHAIN_FILE=${GITHUB_WORKSPACE}/vcpkg/scripts/buildsystems/vcpkg.cmake \ - # EOF - echo "::endgroup::" - - echo "::group::Build" - echo "Running on $(sysctl -n hw.logicalcpu) cores" - cmake --build . -j $(sysctl -n hw.logicalcpu) - echo "::endgroup::" - - - name: Test - run: | - cd build - ctest -j $(sysctl -n hw.logicalcpu) --timeout 120 + with: + arch: ${{ matrix.arch }} + full_arch: ${{ matrix.full_arch }} + extra-cmake-parameters: ${{ matrix.extra-cmake-parameters }} windows: - strategy: - fail-fast: false - matrix: - os: [windows-latest] - arch: [x86, x64] - - name: Windows (${{ matrix.os }} / ${{ matrix.arch }}) - - runs-on: ${{ matrix.os }} - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup vcpkg caching - uses: actions/github-script@v7 - with: - script: | - core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); - core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - core.exportVariable('VCPKG_BINARY_SOURCES', 'clear;x-gha,readwrite') - - - name: Install vcpkg - run: | - git clone https://github.com/microsoft/vcpkg - .\vcpkg\bootstrap-vcpkg.bat -disableMetrics - - - name: Install OpenGFX - shell: bash - run: | - mkdir -p "C:/Users/Public/Documents/OpenTTD/baseset" - cd "C:/Users/Public/Documents/OpenTTD/baseset" - - echo "::group::Download OpenGFX" - curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip - echo "::endgroup::" - - echo "::group::Unpack OpenGFX" - unzip opengfx-all.zip - echo "::endgroup::" - - rm -f opengfx-all.zip - - - name: Install MSVC problem matcher - uses: ammaraskar/msvc-problem-matcher@master - - - name: Configure developer command prompt for ${{ matrix.arch }} - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: ${{ matrix.arch }} - - - name: Build - shell: bash - env: - NINJA_STATUS: "[%f/%t -- %e] " # [finished_edges/total_edges -- elapsed_time], default value is "[%f/%t] " - run: | - mkdir build - cd build - - echo "::group::CMake" - cmake .. \ - -GNinja \ - -DVCPKG_TARGET_TRIPLET=${{ matrix.arch }}-windows-static \ - -DCMAKE_TOOLCHAIN_FILE="${GITHUB_WORKSPACE}\vcpkg\scripts\buildsystems\vcpkg.cmake" \ - # EOF - echo "::endgroup::" - - echo "::group::Build" - cmake --build . - echo "::endgroup::" - - - name: Test - shell: bash - run: | - cd build - ctest --timeout 120 - - - msys2: strategy: fail-fast: false matrix: include: - - msystem: MINGW64 - arch: x86_64 - - msystem: MINGW32 - arch: i686 + - arch: x86 + - arch: x64 - name: MinGW (${{ matrix.arch }}) + name: Windows (${{ matrix.arch }}) - runs-on: windows-latest + uses: ./.github/workflows/ci-windows.yml + secrets: inherit - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 - with: - msystem: ${{ matrix.msystem }} - release: false - install: >- - git - make - mingw-w64-${{ matrix.arch }}-cmake - mingw-w64-${{ matrix.arch }}-gcc - mingw-w64-${{ matrix.arch }}-lzo2 - mingw-w64-${{ matrix.arch }}-libpng - mingw-w64-${{ matrix.arch }}-lld - mingw-w64-${{ matrix.arch }}-ninja - - - name: Install OpenGFX - shell: bash - run: | - mkdir -p "C:/Users/Public/Documents/OpenTTD/baseset" - cd "C:/Users/Public/Documents/OpenTTD/baseset" - - echo "::group::Download OpenGFX" - curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip - echo "::endgroup::" - - echo "::group::Unpack OpenGFX" - unzip opengfx-all.zip - echo "::endgroup::" - - rm -f opengfx-all.zip - - - name: Install GCC problem matcher - uses: ammaraskar/gcc-problem-matcher@master - - - name: Build - shell: msys2 {0} - env: - NINJA_STATUS: "[%f/%t -- %e] " # [finished_edges/total_edges -- elapsed_time], default value is "[%f/%t] " - run: | - mkdir build - cd build - - echo "::group::CMake" - cmake .. \ - -GNinja \ - -DCMAKE_CXX_FLAGS="-fuse-ld=lld" \ - # EOF - echo "::endgroup::" - - echo "::group::Build" - cmake --build . - echo "::endgroup::" - - - name: Test - shell: msys2 {0} - run: | - cd build - ctest --timeout 120 + with: + arch: ${{ matrix.arch }} check_annotations: name: Check Annotations @@ -433,7 +98,6 @@ jobs: - linux - macos - windows - - msys2 if: always() && github.event_name == 'pull_request' diff --git a/.github/workflows/ci-emscripten.yml b/.github/workflows/ci-emscripten.yml new file mode 100644 index 0000000000..13cce318c7 --- /dev/null +++ b/.github/workflows/ci-emscripten.yml @@ -0,0 +1,65 @@ +name: CI (Emscripten) + +on: + workflow_call: + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + +jobs: + emscripten: + name: CI + + runs-on: ubuntu-latest + container: + # If you change this version, change the number in the cache step too. + image: emscripten/emsdk:3.1.57 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Fix dubious ownership + run: | + git config --global --add safe.directory ${GITHUB_WORKSPACE} + + - name: Setup cache + uses: actions/cache@v4 + with: + path: /emsdk/upstream/emscripten/cache + key: 3.1.57-${{ runner.os }} + + - name: Add liblzma support + run: | + cp ${GITHUB_WORKSPACE}/os/emscripten/ports/liblzma.py /emsdk/upstream/emscripten/tools/ports/contrib/ + + - name: Build (host tools) + run: | + mkdir build-host + cd build-host + + echo "::group::CMake" + cmake .. -DOPTION_TOOLS_ONLY=ON + echo "::endgroup::" + + echo "::group::Build" + echo "Running on $(nproc) cores" + cmake --build . -j $(nproc) --target tools + echo "::endgroup::" + + - name: Install GCC problem matcher + uses: ammaraskar/gcc-problem-matcher@master + + - name: Build + run: | + mkdir build + cd build + + echo "::group::CMake" + emcmake cmake .. -DHOST_BINARY_DIR=../build-host + echo "::endgroup::" + + echo "::group::Build" + echo "Running on $(nproc) cores" + cmake --build . -j $(nproc) --target openttd + echo "::endgroup::" diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml new file mode 100644 index 0000000000..1abd005d06 --- /dev/null +++ b/.github/workflows/ci-linux.yml @@ -0,0 +1,126 @@ +name: CI (Linux) + +on: + workflow_call: + inputs: + compiler: + required: true + type: string + cxxcompiler: + required: true + type: string + libraries: + required: true + type: string + extra-cmake-parameters: + required: true + type: string + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + +jobs: + linux: + name: CI + + runs-on: ubuntu-24.04 + env: + CC: ${{ inputs.compiler }} + CXX: ${{ inputs.cxxcompiler }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup vcpkg caching + uses: actions/github-script@v7 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + core.exportVariable('VCPKG_BINARY_SOURCES', 'clear;x-gha,readwrite') + + - name: Install vcpkg + run: | + git clone https://github.com/microsoft/vcpkg ${{ runner.temp }}/vcpkg + ${{ runner.temp }}/vcpkg/bootstrap-vcpkg.sh -disableMetrics + + - name: Install dependencies + run: | + echo "::group::Update apt" + sudo apt-get update + echo "::endgroup::" + + echo "::group::Install dependencies" + sudo apt-get install -y --no-install-recommends \ + liballegro4-dev \ + libcurl4-openssl-dev \ + libfontconfig-dev \ + libharfbuzz-dev \ + libicu-dev \ + liblzma-dev \ + liblzo2-dev \ + libogg-dev \ + libopus-dev \ + libopusfile-dev \ + ${{ inputs.libraries }} \ + zlib1g-dev \ + # EOF + + echo "::group::Install vcpkg dependencies" + + # Disable vcpkg integration, as we mostly use system libraries. + mv vcpkg.json vcpkg-disabled.json + + # We only use breakpad from vcpkg, as its CMake files + # are a bit special. So the Ubuntu's variant doesn't work. + ${{ runner.temp }}/vcpkg/vcpkg install breakpad + + echo "::endgroup::" + env: + DEBIAN_FRONTEND: noninteractive + + - name: Get OpenGFX + run: | + mkdir -p ~/.local/share/openttd/baseset + cd ~/.local/share/openttd/baseset + + echo "::group::Download OpenGFX" + curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip + echo "::endgroup::" + + echo "::group::Unpack OpenGFX" + unzip opengfx-all.zip + echo "::endgroup::" + + rm -f opengfx-all.zip + + - name: Install GCC problem matcher + uses: ammaraskar/gcc-problem-matcher@master + + - name: Build + run: | + mkdir build + cd build + + echo "::group::CMake" + cmake .. -DCMAKE_TOOLCHAIN_FILE=${{ runner.temp }}/vcpkg/scripts/buildsystems/vcpkg.cmake ${{ inputs.extra-cmake-parameters }} + echo "::endgroup::" + + echo "::group::Build" + echo "Running on $(nproc) cores" + cmake --build . -j $(nproc) + echo "::endgroup::" + + - name: Test + run: | + ( + cd build + ctest -j $(nproc) --timeout 120 + ) + + # Re-enable vcpkg. + mv vcpkg-disabled.json vcpkg.json + + # Check no tracked files have been modified. + git diff --exit-code diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml new file mode 100644 index 0000000000..2a0cd00428 --- /dev/null +++ b/.github/workflows/ci-macos.yml @@ -0,0 +1,90 @@ +name: CI (MacOS) + +on: + workflow_call: + inputs: + arch: + required: true + type: string + full_arch: + required: true + type: string + extra-cmake-parameters: + required: false + type: string + default: "" + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + +jobs: + macos: + name: CI + + runs-on: macos-latest + env: + 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 + + - name: Setup vcpkg caching + uses: actions/github-script@v7 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + core.exportVariable('VCPKG_BINARY_SOURCES', 'clear;x-gha,readwrite') + + - name: Install vcpkg + run: | + git clone https://github.com/microsoft/vcpkg ${{ runner.temp }}/vcpkg + ${{ runner.temp }}/vcpkg/bootstrap-vcpkg.sh -disableMetrics + + - name: Install OpenGFX + run: | + mkdir -p ~/Documents/OpenTTD/baseset + cd ~/Documents/OpenTTD/baseset + + echo "::group::Download OpenGFX" + curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip + echo "::endgroup::" + + echo "::group::Unpack OpenGFX" + unzip opengfx-all.zip + echo "::endgroup::" + + rm -f opengfx-all.zip + + - name: Install GCC problem matcher + uses: ammaraskar/gcc-problem-matcher@master + + - name: Build + run: | + mkdir build + cd build + + echo "::group::CMake" + cmake .. \ + -DCMAKE_OSX_ARCHITECTURES=${{ inputs.full_arch }} \ + -DVCPKG_TARGET_TRIPLET=${{ inputs.arch }}-osx \ + -DCMAKE_TOOLCHAIN_FILE=${{ runner.temp }}/vcpkg/scripts/buildsystems/vcpkg.cmake \ + ${{ inputs.extra-cmake-parameters }} \ + # EOF + echo "::endgroup::" + + echo "::group::Build" + echo "Running on $(sysctl -n hw.logicalcpu) cores" + cmake --build . -j $(sysctl -n hw.logicalcpu) + echo "::endgroup::" + + - name: Test + run: | + cd build + ctest -j $(sysctl -n hw.logicalcpu) --timeout 120 diff --git a/.github/workflows/ci-mingw.yml b/.github/workflows/ci-mingw.yml new file mode 100644 index 0000000000..b1079a534c --- /dev/null +++ b/.github/workflows/ci-mingw.yml @@ -0,0 +1,86 @@ +name: CI (MinGW) + +on: + workflow_call: + inputs: + arch: + required: true + type: string + msystem: + required: true + type: string + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + +jobs: + mingw: + name: CI + + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: ${{ inputs.msystem }} + release: false + install: >- + git + make + mingw-w64-${{ inputs.arch }}-cmake + mingw-w64-${{ inputs.arch }}-gcc + mingw-w64-${{ inputs.arch }}-lzo2 + mingw-w64-${{ inputs.arch }}-libpng + mingw-w64-${{ inputs.arch }}-lld + mingw-w64-${{ inputs.arch }}-ninja + mingw-w64-${{ inputs.arch }}-libogg + mingw-w64-${{ inputs.arch }}-opus + mingw-w64-${{ inputs.arch }}-opusfile + + - name: Install OpenGFX + shell: bash + run: | + mkdir -p "C:/Users/Public/Documents/OpenTTD/baseset" + cd "C:/Users/Public/Documents/OpenTTD/baseset" + + echo "::group::Download OpenGFX" + curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip + echo "::endgroup::" + + echo "::group::Unpack OpenGFX" + unzip opengfx-all.zip + echo "::endgroup::" + + rm -f opengfx-all.zip + + - name: Install GCC problem matcher + uses: ammaraskar/gcc-problem-matcher@master + + - name: Build + shell: msys2 {0} + env: + NINJA_STATUS: "[%f/%t -- %e] " # [finished_edges/total_edges -- elapsed_time], default value is "[%f/%t] " + run: | + mkdir build + cd build + + echo "::group::CMake" + cmake .. \ + -GNinja \ + -DCMAKE_CXX_FLAGS="-fuse-ld=lld" \ + # EOF + echo "::endgroup::" + + echo "::group::Build" + cmake --build . + echo "::endgroup::" + + - name: Test + shell: msys2 {0} + run: | + cd build + ctest --timeout 120 diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml new file mode 100644 index 0000000000..15bc55a97c --- /dev/null +++ b/.github/workflows/ci-nightly.yml @@ -0,0 +1,82 @@ +name: CI - Nightly + +on: + schedule: + - cron: '0 3 * * *' + workflow_dispatch: + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + +jobs: + linux: + strategy: + fail-fast: false + matrix: + include: + - name: GCC - SDL1.2 + compiler: gcc + cxxcompiler: g++ + libraries: libsdl1.2-dev + + name: Linux (${{ matrix.name }}) + + uses: ./.github/workflows/ci-linux.yml + secrets: inherit + + with: + compiler: ${{ matrix.compiler }} + cxxcompiler: ${{ matrix.cxxcompiler }} + libraries: ${{ matrix.libraries }} + extra-cmake-parameters: + + macos: + strategy: + fail-fast: false + matrix: + include: + - arch: x64 + full_arch: x86_64 + + name: Mac OS (${{ matrix.arch }}) + + uses: ./.github/workflows/ci-macos.yml + secrets: inherit + + with: + arch: ${{ matrix.arch }} + full_arch: ${{ matrix.full_arch }} + + mingw: + strategy: + fail-fast: false + matrix: + include: + - msystem: MINGW64 + arch: x86_64 + - msystem: MINGW32 + arch: i686 + + name: MinGW (${{ matrix.arch }}) + + uses: ./.github/workflows/ci-mingw.yml + secrets: inherit + + with: + msystem: ${{ matrix.msystem }} + arch: ${{ matrix.arch }} + + check_annotations: + name: Check Annotations + needs: + - linux + - macos + - mingw + + if: always() + + runs-on: ubuntu-latest + + steps: + - name: Check annotations + uses: OpenTTD/actions/annotation-check@v5 diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml new file mode 100644 index 0000000000..789017bdb9 --- /dev/null +++ b/.github/workflows/ci-windows.yml @@ -0,0 +1,84 @@ +name: CI (Windows) + +on: + workflow_call: + inputs: + arch: + required: true + type: string + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + +jobs: + windows: + name: CI + + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup vcpkg caching + uses: actions/github-script@v7 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + core.exportVariable('VCPKG_BINARY_SOURCES', 'clear;x-gha,readwrite') + + - name: Install vcpkg + run: | + git clone https://github.com/microsoft/vcpkg ${{ runner.temp }}\vcpkg + ${{ runner.temp }}\vcpkg\bootstrap-vcpkg.bat -disableMetrics + + - name: Install OpenGFX + shell: bash + run: | + mkdir -p "C:/Users/Public/Documents/OpenTTD/baseset" + cd "C:/Users/Public/Documents/OpenTTD/baseset" + + echo "::group::Download OpenGFX" + curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip + echo "::endgroup::" + + echo "::group::Unpack OpenGFX" + unzip opengfx-all.zip + echo "::endgroup::" + + rm -f opengfx-all.zip + + - name: Install MSVC problem matcher + uses: ammaraskar/msvc-problem-matcher@master + + - name: Configure developer command prompt for ${{ inputs.arch }} + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{ inputs.arch }} + + - name: Build + shell: bash + env: + NINJA_STATUS: "[%f/%t -- %e] " # [finished_edges/total_edges -- elapsed_time], default value is "[%f/%t] " + run: | + mkdir build + cd build + + echo "::group::CMake" + cmake .. \ + -GNinja \ + -DVCPKG_TARGET_TRIPLET=${{ inputs.arch }}-windows-static \ + -DCMAKE_TOOLCHAIN_FILE="${{ runner.temp }}\vcpkg\scripts\buildsystems\vcpkg.cmake" \ + # EOF + echo "::endgroup::" + + echo "::group::Build" + cmake --build . + echo "::endgroup::" + + - name: Test + shell: bash + run: | + cd build + ctest --timeout 120 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c5a432d80b..b1b9a904e4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -26,6 +26,19 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Setup vcpkg caching + uses: actions/github-script@v7 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + core.exportVariable('VCPKG_BINARY_SOURCES', 'clear;x-gha,readwrite') + + - name: Install vcpkg + run: | + git clone https://github.com/microsoft/vcpkg ${{ runner.temp }}/vcpkg + ${{ runner.temp }}/vcpkg/bootstrap-vcpkg.sh -disableMetrics + - name: Install dependencies run: | echo "::group::Update apt" @@ -41,16 +54,33 @@ jobs: libicu-dev \ liblzma-dev \ liblzo2-dev \ + libopus-dev \ + libopusfile-dev \ libsdl2-dev \ zlib1g-dev \ # EOF + + echo "::group::Install vcpkg dependencies" + + # Disable vcpkg integration, as we mostly use system libraries. + mv vcpkg.json vcpkg-disabled.json + + # We only use breakpad from vcpkg, as its CMake files + # are a bit special. So the Ubuntu's variant doesn't work. + ${{ runner.temp }}/vcpkg/vcpkg install breakpad + echo "::endgroup::" env: DEBIAN_FRONTEND: noninteractive - - name: Set number of make jobs + - name: Prepare build run: | - echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV + mkdir build + cd build + + echo "::group::CMake" + cmake .. -DCMAKE_TOOLCHAIN_FILE=${{ runner.temp }}/vcpkg/scripts/buildsystems/vcpkg.cmake + echo "::endgroup::" - name: Initialize CodeQL uses: github/codeql-action/init@v3 @@ -58,8 +88,14 @@ jobs: languages: cpp config-file: ./.github/codeql/codeql-config.yml - - name: Autobuild - uses: github/codeql-action/autobuild@v3 + - name: Build + run: | + cd build + + echo "::group::Build" + echo "Running on $(nproc) cores" + cmake --build . -j $(nproc) + echo "::endgroup::" - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index 0bb79ed4b9..9cf6e9573c 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -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: | diff --git a/.github/workflows/release-docs.yml b/.github/workflows/release-docs.yml index 83868e5266..c811e01e18 100644 --- a/.github/workflows/release-docs.yml +++ b/.github/workflows/release-docs.yml @@ -11,7 +11,7 @@ jobs: docs: name: Docs - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Download source diff --git a/.github/workflows/release-macos.yml b/.github/workflows/release-macos.yml index e9cefa0813..db063ab8c4 100644 --- a/.github/workflows/release-macos.yml +++ b/.github/workflows/release-macos.yml @@ -12,7 +12,7 @@ jobs: macos: name: MacOS - runs-on: macos-14 + runs-on: macos-latest env: MACOSX_DEPLOYMENT_TARGET: 10.13 @@ -86,7 +86,7 @@ jobs: echo "::endgroup::" - name: Import code signing certificates - uses: Apple-Actions/import-codesign-certs@v2 + uses: Apple-Actions/import-codesign-certs@v3 with: # The certificates in a PKCS12 file encoded as a base64 string p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} diff --git a/.github/workflows/release-source.yml b/.github/workflows/release-source.yml index 331ba6821d..59de61e232 100644 --- a/.github/workflows/release-source.yml +++ b/.github/workflows/release-source.yml @@ -18,7 +18,7 @@ jobs: source: name: Source - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest outputs: version: ${{ steps.metadata.outputs.version }} @@ -127,7 +127,7 @@ jobs: fi mkdir -p build/bundles - cp .changelog build/bundles/changelog.txt + cp .changelog build/bundles/changelog.md cp .release_date build/bundles/released.txt cp README.md build/bundles/README.md echo "::endgroup::" diff --git a/.github/workflows/upload-gog.yml b/.github/workflows/upload-gog.yml index 1370988f3e..cbe81c6a84 100644 --- a/.github/workflows/upload-gog.yml +++ b/.github/workflows/upload-gog.yml @@ -11,7 +11,7 @@ jobs: upload: name: Upload (GOG) - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Download source diff --git a/.github/workflows/upload-steam.yml b/.github/workflows/upload-steam.yml index 47fa48d01b..1cf17239fd 100644 --- a/.github/workflows/upload-steam.yml +++ b/.github/workflows/upload-steam.yml @@ -14,7 +14,7 @@ jobs: upload: name: Upload (Steam) - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Download source diff --git a/.ottdrev b/.ottdrev index 695a33e3b0..dab280a505 100644 --- a/.ottdrev +++ b/.ottdrev @@ -1 +1 @@ -14.1 20240503 0 61342620bc4c0971b28b3578379b53d04d2f11f1 1 1 2024 +15.0-beta1 20241224 0 cd0e1fc47de2095cc0642d73ad5d42db35a63d43 1 0 diff --git a/.release_date b/.release_date index ee6772ad97..96f01fbe8a 100644 --- a/.release_date +++ b/.release_date @@ -1 +1 @@ -2024-05-03 20:51 UTC +2024-12-24 13:45 UTC diff --git a/.version b/.version index 9dc0691c48..0886b5458e 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -14.1 +15.0-beta1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e5b669870..3bf26bd03f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ if(NOT BINARY_NAME) endif() project(${BINARY_NAME} - VERSION 14.0 + VERSION 15.0 LANGUAGES CXX ) @@ -152,9 +152,10 @@ if(NOT OPTION_DEDICATED) find_package(Fontconfig) endif() find_package(Harfbuzz) - find_package(ICU OPTIONAL_COMPONENTS i18n) + find_package(ICU OPTIONAL_COMPONENTS i18n uc) endif() endif() + find_package(OpusFile) endif() if(APPLE) enable_language(OBJCXX) @@ -331,6 +332,8 @@ if(NOT OPTION_DEDICATED) link_package(Fontconfig TARGET Fontconfig::Fontconfig) link_package(Harfbuzz TARGET harfbuzz::harfbuzz) link_package(ICU_i18n) + link_package(ICU_uc) + link_package(OpusFile TARGET OpusFile::opusfile) if(SDL2_FOUND AND OPENGL_FOUND AND UNIX) # SDL2 dynamically loads OpenGL if needed, so do not link to OpenGL when @@ -388,8 +391,8 @@ if(EMSCRIPTEN) target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/CREDITS.md@/CREDITS.md") target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/CONTRIBUTING.md@/CONTRIBUTING.md") target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/COPYING.md@/COPYING.md") - target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/known-bugs.txt@/known-bugs.txt") - target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/changelog.txt@/changelog.txt") + target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/known-bugs.md@/known-bugs.md") + target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/changelog.md@/changelog.md") target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/admin_network.md@/docs/admin_network.md") target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/debugging_desyncs.md@/docs/debugging_desyncs.md") target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/desync.md@/docs/desync.md") diff --git a/CODINGSTYLE.md b/CODINGSTYLE.md index 7332316600..a4230c03a0 100644 --- a/CODINGSTYLE.md +++ b/CODINGSTYLE.md @@ -157,7 +157,7 @@ enum SomeEnumeration { * Use curly braces and put the contained statements on their own lines (e.g., don't put them directly after the **if**). * Opening curly bracket **{** stays on the first line, closing curly bracket **}** gets a line to itself (except for the **}** preceding an **else**, which should be on the same line as the **else**). * When only a single statement is contained, the brackets can be omitted. In this case, put the single statement on the same line as the preceding keyword (**if**, **while**, etc.). Note that this is only allowed for if statements without an **else** clause. -* All fall throughs must be documented, using a **FALLTHROUGH** define/macro. +* Non-trivial fall throughs must be documented, using a `[[fallthrough]]` attribute. * The NOT_REACHED() macro can be used in default constructs that should never be reached. * Unconditional loops are written with **`for (;;) {`** @@ -180,7 +180,7 @@ switch (a) { case 1: DoSomething(); - FALLTHROUGH; + [[fallthrough]]; case 2: DoMore(); @@ -191,7 +191,7 @@ switch (a) { int r = 2; DoEvenMore(a); - FALLTHROUGH; + [[fallthrough]]; } case 4: { @@ -248,12 +248,46 @@ Templates are a very powerful C++ tool, but they can easily confuse beginners. T * Templates are to be documented in a very clear and verbose manner. Never assume anything in the documentation. * the template keyword and the template layout get a separate line. typenames are either "T" or preceded by a "T", integers get a single capital letter or a descriptive name preceded by "T". ```c++ -template +template int Func(); ``` * If you are writing one or more template class in the dedicated header file, use file.hpp for its name instead of file.h. This will let others know that it is template library (includes also implementation), not just header with declarations. +### Code Comment Vertical Alignment + +When adding code or comments to an existing formatted section, follow the existing style if possible without editing the preexisting lines. + +If your addition cannot be aligned with existing code, do not align the comments with anything and use only a single space between the code and the comment. + +Good: + +```c++ +enum Vehicle { + BUS, ///< Take the bus. ++ CAR, ///< Drive your car. + BIKE, ///< Ride your bike ++ TRAIN, ///< Catch the train. +} +``` + +"Car" is shorter than Bike which allows you to easily align the new comment. "Train" is longer. It is *NOT* desirable to change the vertical comment alignment of this enum. + +Bad: + +```c++ +enum Vehicle { +- BUS, ///< Take the bus. +- BIKE, ///< Ride your bike ++ BUS, ///< Take the bus. ++ CAR, ///< Drive your car. ++ BIKE, ///< Ride your bike ++ TRAIN, ///< Catch the train. +} +``` + +OpenTTD used to vertically-align inline Doxygen comments as shown above. OpenTTD has since stopped strictly following this rule to keep diffs smaller and reduce pollution to the git blame history for non-functional changes. + ### Other important rules * Put a space before and after binary operators: "a + b", "a == b", "a & b", "a <<= b", etc.. Exceptions are ".", "->" and "[]" (no spaces) and "," (just space after it). * Put parenthesis where it improves readability: "*(b++)" instead of "*b++", and "if ((a & b) && c == 2)" instead of "if (a & b && c == 2)". @@ -448,7 +482,7 @@ Do not mention two keywords; if two apply, pick one that best represents the com The `
` part starts with a capital and does not end with a dot. Try to be descriptive to what the player will notice, not to what is actually being changed in the code. -See `changelog.txt` for inspiration. +See `changelog.md` for inspiration. To further structure the changelog, you can add components. Example are: * "Network" for network specific changes. diff --git a/Doxyfile.in b/Doxyfile.in index 4e287889af..0e8eee4b2e 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -3,6 +3,8 @@ # 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 . +# Doxyfile 1.9.4 + #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- @@ -13,6 +15,7 @@ PROJECT_BRIEF = PROJECT_LOGO = OUTPUT_DIRECTORY = ${CPACK_BINARY_DIR}/docs/source/ CREATE_SUBDIRS = YES +CREATE_SUBDIRS_LEVEL = 8 ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES @@ -35,8 +38,10 @@ STRIP_FROM_PATH = ./ STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES +JAVADOC_BANNER = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +PYTHON_DOCSTRING = YES INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 2 @@ -45,6 +50,7 @@ OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +OPTIMIZE_OUTPUT_SLICE = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES TOC_INCLUDE_HEADINGS = 0 @@ -60,16 +66,19 @@ INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = YES +EXTRACT_PRIV_VIRTUAL = NO EXTRACT_PACKAGE = NO EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = YES EXTRACT_ANON_NSPACES = YES +RESOLVE_UNNAMED_PARAMS = YES HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO @@ -78,6 +87,7 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +SHOW_HEADERFILE = YES SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO @@ -107,9 +117,11 @@ QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES +WARN_IF_INCOMPLETE_DOC = YES WARN_NO_PARAMDOC = NO WARN_AS_ERROR = NO WARN_FORMAT = "$file:$line: $text" +WARN_LINE_FORMAT = "at line $line of file $file" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -150,6 +162,10 @@ REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES +CLANG_ASSISTED_PARSING = NO +CLANG_ADD_INC_PATHS = YES +CLANG_OPTIONS = +CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -175,6 +191,7 @@ HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_FEEDURL = DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher @@ -197,12 +214,17 @@ GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = YES +FULL_SIDEBAR = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +OBFUSCATE_EMAILS = YES +HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = USE_MATHJAX = NO +MATHJAX_VERSION = MathJax_2 MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ MATHJAX_EXTENSIONS = @@ -221,6 +243,7 @@ GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex +LATEX_MAKEINDEX_CMD = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4 EXTRA_PACKAGES = @@ -232,9 +255,9 @@ PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain LATEX_TIMESTAMP = NO +LATEX_EMOJI_DIRECTORY = #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -244,7 +267,6 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -RTF_SOURCE_CODE = NO #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -259,12 +281,12 @@ MAN_LINKS = NO GENERATE_XML = NO XML_OUTPUT = xml XML_PROGRAMLISTING = YES +XML_NS_MEMB_FILE_SCOPE = NO #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- @@ -311,7 +333,6 @@ EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- -CLASS_DIAGRAMS = YES DIA_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO @@ -324,6 +345,8 @@ COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 +DOT_UML_DETAILS = NO +DOT_WRAP_THRESHOLD = 17 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES @@ -331,6 +354,7 @@ CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES +DIR_GRAPH_MAX_DEPTH = 1 DOT_IMAGE_FORMAT = png INTERACTIVE_SVG = NO DOT_PATH = diff --git a/bin/ai/CMakeLists.txt b/bin/ai/CMakeLists.txt index 7fb23cada3..8b54f74973 100644 --- a/bin/ai/CMakeLists.txt +++ b/bin/ai/CMakeLists.txt @@ -15,6 +15,7 @@ set(AI_COMPAT_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/compat_12.nut ${CMAKE_CURRENT_SOURCE_DIR}/compat_13.nut ${CMAKE_CURRENT_SOURCE_DIR}/compat_14.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_15.nut ) foreach(AI_COMPAT_SOURCE_FILE IN LISTS AI_COMPAT_SOURCE_FILES) diff --git a/bin/ai/compat_14.nut b/bin/ai/compat_14.nut index 3081fb58e8..0588a2b7fa 100644 --- a/bin/ai/compat_14.nut +++ b/bin/ai/compat_14.nut @@ -4,3 +4,5 @@ * 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 . */ + +AILog.Info("14 API compatibility in effect."); diff --git a/bin/ai/compat_15.nut b/bin/ai/compat_15.nut new file mode 100644 index 0000000000..3081fb58e8 --- /dev/null +++ b/bin/ai/compat_15.nut @@ -0,0 +1,6 @@ +/* + * 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 . + */ diff --git a/bin/game/CMakeLists.txt b/bin/game/CMakeLists.txt index 0b48e97857..d0b5448997 100644 --- a/bin/game/CMakeLists.txt +++ b/bin/game/CMakeLists.txt @@ -12,6 +12,7 @@ set(GS_COMPAT_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/compat_12.nut ${CMAKE_CURRENT_SOURCE_DIR}/compat_13.nut ${CMAKE_CURRENT_SOURCE_DIR}/compat_14.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_15.nut ) foreach(GS_COMPAT_SOURCE_FILE IN LISTS GS_COMPAT_SOURCE_FILES) diff --git a/bin/game/compat_14.nut b/bin/game/compat_14.nut index 3081fb58e8..9a5f872147 100644 --- a/bin/game/compat_14.nut +++ b/bin/game/compat_14.nut @@ -4,3 +4,5 @@ * 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 . */ + +GSLog.Info("14 API compatibility in effect."); diff --git a/bin/game/compat_15.nut b/bin/game/compat_15.nut new file mode 100644 index 0000000000..3081fb58e8 --- /dev/null +++ b/bin/game/compat_15.nut @@ -0,0 +1,6 @@ +/* + * 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 . + */ diff --git a/changelog.txt b/changelog.md similarity index 82% rename from changelog.txt rename to changelog.md index 2b0ca754f5..8ed27438a8 100644 --- a/changelog.txt +++ b/changelog.md @@ -1,1306 +1,1491 @@ -14.1 (2024-05-03) ------------------------------------------------------------------------- -Add: Check that towns can build roads before generating map (#12503) -Fix #12228: Ships could get lost due to pathfinder not considering reversing in some cases (#12474) -Fix #12433: Width of unit number display was too narrow (#12534) -Fix #12502: Desync related to vehicle replacement (#12512) -Fix #12506: Desync after new oil rig is constructed (#12511) -Fix #12584: Crash on some tar files during tar scan (#12586) -Fix: [SDL2] Keypad numbers did not function (#12596) -Fix: Houses and industry tiles could accept incorrect cargo (#12547) -Fix: Map generation stage strings were incorrect (#12549) -Fix: [Script] Allow only 255 league tables, as 255 itself is the invalid id sentinel (#12545) -Fix: Mark vehicle status bars dirty when a vehicle leaves unbunching depot (#12516) -Fix: Do not show train waiting for unbunching as waiting for free path (#12515) -Fix: Smooth outliers in unbunching round trip calculations (#12513) - - -14.0 (2024-04-13) ------------------------------------------------------------------------- -Update: New title game for 14.0 -Fix #12477: Crash when launching OpenTTD from within a Dropbox folder (#12478) -Fix #12233: Mini order list overlaps vehicle group name (#12423) -Fix #12114: Viewport coords of crashed aircraft not updated when falling (#12424) -Fix #12395: Ensure president name widget is tall enough (#12419) -Fix #12415: Incorrect payment for aircraft secondary cargo (#12416) -Fix #12387: [NewGRF] Wrong tile offset passed to rail station CB 149 (slope check) -Fix #12388: Autoreplacing train heads slowly made the unit number grow (#12389) -Fix #12368: Incorrect offset for click position within industry chain window (#12370) -Fix: Aircraft can float above the ground when crashed (#12425) -Fix: Segfault when using -q without providing a . character (#12418) -Fix: Wrong scrolling dropdown list position with RTL (#12412) -Fix: [Win32] Force font mapper to only use TrueType fonts (#12406) -Fix: "-q" displays NewGRF IDs in the wrong byte-order (#12397) -Fix: Do not send chat to clients that have not authorized yet (#12377) -Fix: [NewGRF] Label for fruit incorrectly changed to `FRUI` from `FRUT` (#12367) -Fix: [Script] ScriptSubsidy::GetExpireDate should return an economy-date (#12372) -Revert #11603: [Script] AI/GSTimeMode was not the best solution for economy/calendar support (#12362) - - -14.0-RC3 (2024-03-23) ------------------------------------------------------------------------- -Fix #12347: Crash attempting to find catchment tiles of a station with no catchment area (#12348) -Fix #12319: Some SSE blitters were broken due to ODR violations (#12322) -Fix #12302: Allow empty train engines to use an invalid cargo type (#12325) -Fix #12305: Crash with large positive sprite x offset in engine preview window (#12313) -Fix #12166: Crash when opening tram road stop build window (#12168) -Fix #12092: Incorrect x-axis in cargo payment graph window (#12359) -Fix: Crash when attempting to join a company while not joined (#12353) -Change: Show unbunching action in timetable window (#12351) -Change: [Windows] Switch to Microsoft Azure code signing certificate (#12292) - - -14.0-RC2 (2024-03-16) ------------------------------------------------------------------------- -Update: Bump bundled OpenTTD TTF fonts to version v0.6 (#12276) -Update: Developer credits (#12173, #12235) -Change: Use (at least) standard toolbar button size for signal selection buttons (#12265) -Change: [Script] Match FormatString behaviour more closely (#12205) -Fix #12236: Ship pathfinder causes crash when ship is already at destination (#12238) -Fix #12225: [Script] Missing AI::ResetConfig support for running AI config (#12226) -Fix #12203: When unbunching at a depot, don't overlook implicit orders (#12220) -Fix #12196: Always show selected content, even when filtering and disable "select upgrade" button when filtering (#12201) -Fix #12195: Reset cursor when no Object is selected (#12207) -Fix #12176: Ships are circling in one place (#12181) -Fix #12154: Incorrect calendar day lengths with minutes per year setting (#12158) -Fix #12148: Do not draw decimals when number of digits is 0 (#12150) -Fix #12147: Reset all saved settings to their default before loading a game (#12210) -Fix #12145: Incorrect date handling in date cheat in wallclock time-keeping mode (#12146) -Fix #12134: Use correct error messages if clearing drive-through road stops fails (#12139) -Fix #12133: [Script] Don't crash when emergency saving (#12138) -Fix #12127: Truncation ellipses rendered shadows even for black font without shadows (#12132) -Fix #12119: Remove red warning text when maximum loan is zero (#12141) -Fix #12118: When adding an unbunching order, properly check for unsafe conditions (#12136) -Fix #12076: Do not allow 'join' command on dedicated servers (#12208) -Fix #12010: Use economy timer for vehicle stats minimum age, not calendar (#12142) -Fix: Improved ship movement when no path to destination is found (#12285, #12286) -Fix: Initialize _switch_mode_time so crash-logs before first game have a realistic time (#12184) -Fix: [Script] Only show debug script window at the end of savegame loading (#12135) -Fix: [Script] Broken ScriptText circular reference detection (#12187) -Fix: Ordering of command per tick limit and pause mode filtering (#12126) -Fix: Only reset unbunching departure data in the correct depot (#12155) -Fix: Off by one in TimerGameEconomy::ConvertDateToYMD in wallclock mode (#12143) -Fix: Missing savegame conversion for current_order (#12188) -Fix: Helptext for timekeeping unit setting erroneously refers to vehicle movement (#12172) -Fix: Don't show "insert order" errors in the console (#12245) -Fix: Don't defer OnResize() after ReInit() (#12174) -Remove: [Script] random_deviation from setting description table (#12221) -Revert #11993: New number format system does not and cannot work for CJK languages (#12157) -Revert #11606: Don't auto-build past tunnelbridge ends (#12244) - - -14.0-RC1 (2024-02-18) ------------------------------------------------------------------------- -Feature: Fully user configurable number format and abbreviations (#11993) -Add: Show cargo icons on subsidy list window (#12079) -Add: [Script] GetAirportNumHelipads (#12085) -Change: Show 6 or 2 orientation buttons in NewGRF road stop picker as appropriate (#12090) -Change: Show cargo icons on Industry View window (#12071) -Change: Improve performance of finding free pool slots (#12055) -Change: Draw north-side farm fences/hedges/walls on tile edge, instead of 1/16th in (#12048) -Change: When adding orders, Ctrl+Click on a depot to unbunch, instead of service if required (#12023) -Change: Store running AI config inside Company (#12003) -Change: Show speed before destination in vehicle status bar (#11932) -Change: Replace long list of cargo filter buttons with a multi-select dropdown list (#11552) -Change: [Script] Use company randomizer when adding random deviation (#12065) -Fix #12074: Don't allow "part" command for dedicated servers (#12075) -Fix #12052: NewGRFs clearing industry cargo slots could fallback to default instead of empty (#12053) -Fix #12050: Add default size, shade and pin control buttons to company livery widget (#12080) -Fix #12041: Tarball extraction failing due to incorrect filename (#12044) -Fix #12037: Blurry OpenTTD font on Mac OS (#12047) -Fix #12029: Don't show Sandbox Options in multiplayer (#12032) -Fix #12024: Autoreplace failed news message for trains must go to lead engine (#12025) -Fix #12022: Adjust economy date when changing timekeeping units in Scenario Editor (#12042) -Fix #12020: Unbunch and service if needed should be mutually exclusive depot order types (#12021) -Fix #12019: Correctly highlight depot unbunch action in dropdown (#12021) -Fix #12014: Remove water when area clearing ship depot (#12030) -Fix #11840: Ship pathfinder always returns a valid trackdir if one is available (#12031) -Fix #10983: [AdminPort] Correct order of messages (#11140) -Fix #10405: [Script] Test engine and vehicle type validity for ScriptGroup::GetNumEngines (#11887) -Fix #10079: Don't render at 1000fps if HW acceleration + vsync is requested but not active (#12067) -Fix: Shadows of individual character glyphs could be drawn over other characters (#12115) -Fix: Don't invalidate station list on vehicle load/unload (#12112) -Fix: NewGRF roadstops were ignored if only in default class (#12089) -Fix: Visually also disable vsync when not using HW acceleration (#12066) -Fix: Industry tiles and houses could accept incorrect cargo types (#12062) -Fix: Redraw orders when a station feature is added/removed (#12061) -Fix: For content service, fallback to TCP downloads when HTTP stalls (#12056) -Fix: Don't issue autoreplace failed news message for command test mode (#12026) -Remove: Setting "no_http_content_downloads" (#12058) - - -14.0-beta3 (2024-02-06) ------------------------------------------------------------------------- -Add: [Script] ScriptTileList_StationCoverage to get station coverage area (#12015) -Change: Update OpenTTD TTF fonts to v0.5 (#11994) -Fix #12012: Crash when opening orders of another company (#12013) -Fix #12001: Use correct valid cargo check for old-style NewGRF town house 3rd cargo set up (#12006) -Fix #11997: Adjust economy date by 1920 when loading TTD/TTO savegames (#12007) -Fix: Focus hotkey in road/tram stop building window (#12008) -Fix: Signals were incorrectly shifted by 1 pixel when selected (#12005) -Fix: Missing default vehicles and industry acceptance/production (#12000) -Fix: [Script] Avoid overflow in scripts when infinite money is enabled (#12016) -Fix: [Script] Don't kill GS misusing GSText (#12009) - - -14.0-beta2 (2024-02-04) ------------------------------------------------------------------------- -Change: [NewGRF] Improved support for redefining default cargo types (#11719) -Fix #11982: Crash when trying to place signals on things other than plain rails (#11977) -Fix #11975: Inconsistent behaviour when changing first AI company settings (#11976) -Fix #11972: Year cut off in graph windows (#11974) -Fix #11968: Crash when opening orders window of new vehicles (#11973) -Fix #11966: Monospace text in windows may not have been fully scrollable (#11981) -Fix #11802: Made determining water region edge traversability more robust (#11986) -Fix: Second colour vehicle-type default liveries were not being updated (#11971) - - -14.0-beta1 (2024-02-03) ------------------------------------------------------------------------- -Feature: Order option to unbunch vehicles at depot (#11945) -Feature: Infinite money mode (#11902) -Feature: Setting to disable the loading speed penalty for trains longer than the station (#11682) -Feature: Plugin framework for Social Integration with Steam, Discord, GOG, etc (#11628) -Feature: Scalable OpenTTD TrueType font made by Zephyris (#11593) -Feature: Toyland-specific river graphics (#11523) -Feature: Add zoom level buttons to sprite aligner (#11518) -Feature: Add shading to river slopes (#11491) -Feature: Place cargo icon on cargo filter dropdowns (#11487) -Feature: Mode to display timetable in seconds (#11435) -Feature: Setting to influence how many minutes a calendar year takes (#11428) -Feature: Base graphics can offer parameters for additional settings (#11347) -Feature: Sandbox option to lock station ratings at 100% (#11346) -Feature: Setting to use real-time "wallclock" as timekeeping units (#11341) -Feature: Setting to automatically restart server based on hours played (#11142) -Feature: Add config option to set default company secondary colour for new games (#11068) -Feature: Transparency option for cost and income indicators (#11001) -Feature: Create group of vehicles from manage vehicle list button (#10890) -Feature: Show coverage highlight the same as stations when adding waypoints (#10875) -Feature: Show the number of industries already built in the Fund New Industry window (#10806) -Feature: Add search filter and name text to build waypoint window (#10786) -Feature: Setting to disallow level crossings with competitors (#10755) -Feature: Opt-in survey when leaving a game (#10719) -Feature: Replace buying/selling company shares with hostile takeovers of AI companies (#10709, #10914) -Feature: Settings to scale cargo production of towns and industries (#10606) -Feature: Separate rail/road and sea/air velocity units, and add knots (#10594) -Feature: Region-based pathfinder for ships (#10543) -Feature: Filter engine build menu by name and NewGRF extra text (#10519) -Feature: Industry directory text filter (#10518) -Feature: Ctrl+Click to reset late counter for the entire vehicle group (#10464) -Feature: Orientation of rail and road depots can be changed (#9642) -Feature: Display help and manuals in-game (#7786) -Feature: [NewGRF] Town production effect and multiplier (#11947) -Feature: [NewGRF] Randomize direction of rail vehicle on build based on probability callback (#11489) -Feature: [NewGRF] Related Act2 objects for airports and airport tiles (#11282) -Feature: [NewGRF] Allow higher max speeds for ships (#10734) -Feature: [NewGRF] Increase limit of objects/stations/roadstops per NewGRF (#10672) -Feature: [NewGRF] Road stops (#10144) -Feature: [Script] Goal destination can be updated (#10817) -Add: Argument for console command "restart" to use either current or newgame settings (#11962, #11963) -Add: {CURRENCY_SHORT} only did k / m suffix. Add bn / tn and make translatable (#11921) -Add: Show in multiplayer the amount of hours a game has been unpaused (#11886) -Add: Allow loading heightmaps from command-line (#11870) -Add: List_[scenario|heightmap] and load_[scenario|height] console commands (#11867) -Add: Latvian Lats currency (#11691) -Add: Horizontal scroll for script debug log (#11597) -Add: GUI options to select sprite font and AA mode for all fonts (#11593) -Add: Website button for basesets in Game Options window, the Game Script settings window and AI settings window (#11512) -Add: [Emscripten] Support for bootstrapping (#11109) -Add: Hotkey to focus town / industry directory filter box (#11030) -Add: Maximum number of companies allowed to the client list (#10523) -Add: Use specific error message when vehicle cannot go to station/waypoint (#10494) -Add: Show NewGRF name in NewGRF-created errors (#10457) -Add: Alternative setting for right-click close window option to exclude pinned windows (#10204) -Add: Allow autoreplace with same model vehicle (#7729) -Add: [NewGRF] Allow inspection of road tiles and airports (#11282, #11323) -Add: [NewGRF] Station variable 6B to get extended station id of nearby tiles (#10953) -Add: [NewGRF] String code "9A 21" to display force from textstack (#10782) -Add: [NewGRF] Station property 1C/1D to set name/classname (#10672) -Add: [Script] Optional filter parameter to ScriptXXXList constructors (#11698,#11663) -Add: [Script] AI/GS Time Mode to choose between economy (default) and calendar time (#11603) -Add: [Script] Allow to set max loan for each company separately (#11224) -Add: [Script] GSIndustry.GetConstructionDate() method (#11145) -Add: [Script] Game script control of industry production level and news messages (#11141) -Add: [Script] GSAsyncMode to set async mode of gamescript commands (#10913) -Add: [Script] GSCompanyMode::IsValid and IsDeity, and enforce valid company/deity mode where applicable (#10536, #10529) -Add: [Script] Allow GS to found town with random road layout (#10442) -Add: [Script] Create own Randomizer per instance (#10349) -Change: Better handle different GUI sizes for most windows, and squash inconsistencies between windows -Change: Allow configuring AI slots above the current maximum number of competitors (#11961) -Change: Forcefully enable prefixing logs with date (#11930) -Change: Position error window closer to cursor on large screens (#11923) -Change: Only open story-book in center when a GS does it (#11916) -Change: Rebrand Cheats as Sandbox Options (#11874) -Change: Make smooth-scrolling based on actual time (#11865) -Change: Set smooth-scrolling on by default (#11860) -Change: Disable building rail infrastructure if train build limit is zero (#11847) -Change: Invalidate music volume when restarting music playback on Windows (#11836) -Change: Make street lights transparent with houses (#11828) -Change: Redesign script debug window (#11782) -Change: Reorganize Settings menu items (#11683) -Change: Set amount of smoke/sparks to "realistic" by default (#11624) -Change: Show a message in livery window if vehicle type has no groups (#11617) -Change: Add distinct tooltips for vehicle group colour schemes (#11617) -Change: Move colour selection dropdowns to bottom of window (#11617) -Change: Support custom transparency remaps with 32bpp blitters (#11616) -Change: Make "middle" the default stopping location for trains in platforms (#11605) -Change: Scale sprites to requested highest resolution level (#11600) -Change: Allow opening multiple script debug windows by holding Ctrl (#11592) -Change: Don't show scoring year in high score table (#11546) -Change: Revert pressed-button content shifting introduced in r2161 (#11542) -Change: Show rating in station list even with no cargo waiting (#11540) -Change: Hide unused cargos from vehicle cargo filter (#11533) -Change: Don't restart playback when toggling playlist shuffle (#11504) -Change: Increase finance window lines (and underlines) with interface scale (#11459) -Change: Move baseset missing/corrupted files label to list item (#11455) -Change: Add horizontal scrollbar to Industry Directory window (#11434) -Change: Improve layout of airport, dock, object, road/tram stop, train station pickers (#11430) -Change: Display cargo lists in sorted cargo order (#11383) -Change: Link houses production on industry chain graph by TPE_PASSENGERS or TPE_MAIL cargo (#11378) -Change: Passenger subsidies are generated for any TPE_PASSENGER cargo type (#11378) -Change: Towns generate cargo based on town production effect (#11378) -Change: Always allow expanding towns in Scenario Editor to build new roads (#11377) -Change: Don't set vehicle on time if timetable not started (#11359) -Change: Store station blocked/wires/pylons flags in map (#11337) -Change: Recover when possible from crashes during a crash (#11238) -Change: Store crash logs in JSON format (#11232) -Change: Remove autosave from settings window; it is already in the Game Options (#11218) -Change: Enable "Forbid 90 degree turns" setting by default (#11160) -Change: Do not allow mixing road/tram types in powered road type list (#11148) -Change: Only show platform stopping location in orders when other than default (#11102) -Change: Autorail / autoroad tools can start dragging from invalid tiles (#11089) -Change: Only allow buying Exclusive Transport Rights when no one has them (#11076) -Change: Remove currency code/symbol suffix from language files (#11061) -Change: Add separate setting for server sent commands per frame limit (#11023) -Change: Cargo flow legend only shows defined cargo (#10872) -Change: Use "Via-Destination-Source" as default station cargodist display (#10851) -Change: Preserve orders and related settings where possible when moving engines around in a train (#10799) -Change: Standardise unit conversions and allow decimal places (#10795) -Change: Use separate names for default stations/roadstops (#10786) -Change: [MacOS] Require at least 10.15 to run the game (#10745) -Change: Hide all variants from UI when (display) parent is hidden (#10708) -Change: Split Game options into General, Graphics and Sound tabs (#10674) -Change: Extend entity override manager and station spec lists to support 16 bit IDs (#10672) -Change: Base autosaves intervals on real time (instead of game time) (#10655) -Change: Allow overbuilding station and waypoint tiles (#10618) -Change: Use realtime for Linkgraph update settings (#10610) -Change: Make tick length 27 milliseconds (#10607) -Change: Increase max cargo age and let min cargo payment approach zero (#10596) -Change: Show buy company dialog window even when playing in the AI company (#10459) -Change: Use HTTPS for content-service connections (#10448) -Change: Big UFO disaster targets current location of a random train (#10290) -Change: Remove land generator setting from World Generation GUI (#10093) -Change: Build signals to the next junction when dragging regardless of the Ctrl state (#9637) -Change: Allow dedicated server to use threaded saves (#10787) -Change: [NewGRF] Increase vehicle random data from 8 to 16 bits (#10701) -Change: [NewGRF] Read Action 3 IDs as extended-bytes for all features (#10672) -Change: [NewGRF] Make Action 3 debug messages more consistent (#10672) -Change: [NewGRF] Extend callback 161 (engine name) with bit 0x22 for context 'Autoreplace - Vehicles in use' (#10666) -Change: [Script] Replace easy/medium/hard values with default value (#11959) -Change: [Script] Limit total script ops that can be consumed by a list valuate (#11670) -Change: [Script] Allow GS access to ScriptGroup, ScriptGameSettings.IsDisabledVehicleType, more ScriptCompany and more ScriptOrder functions (#10642) -Change: [Script] Improve ScriptText validation error messages (#10545) -Change: [Script] Restore support of {RAW_STRING} in ScriptText (#10492) -Change: [Script] Validate ScriptText parameters type and amount (#10492) -Change: [Script] Automate the ScriptObject reference counting (#10492) -Change: [Script] Extract params info from GS strings (#10492) -Change: [Script] A ScriptText with too many parameters is now a fatal error (#10483) -Change: [Script] Log AI/GS Squirrel crashes in white text for readability (#10375) -Fix #11918: Houses should only build next to road stops, not any station type (#11919) -Fix #11827: Make text layouter aware of ligatures (#11831) -Fix #11752: Characters could be repeated when wrapping multi-line text (#11761) -Fix #11748: Decreasing service interval value sufficiently would result in it wrapping around (#11749) -Fix #11629: Crash when getting the nearest town for rotated airports (#11631) -Fix #11516: Adjust window size by interface scale during ReInit (#11517) -Fix #11515: Changing interface scale could have unintended effects on zoom level (#11615) -Fix #11442: "Default" colour in group colour window is not updated when changing master colour (#11614) -Fix #11437: Flipped shorter rail vehicles disappear in windows (#11446) -Fix #11413: Incorrect sorting by industry production (#11414) -Fix #11407: Don't steal focus from dropdown menus (#11484) -Fix #11402: Make string filter locale-aware (#11426) -Fix #11329: Don't assert vehicle list length is non-zero when only asked to set string parameter (#11330) -Fix #11315: Sort industries and cargoes by name in industry chain window (#11317) -Fix #11307: Incorrect GroupStatistics after selling leading wagon (#11311) -Fix #11261: Airport menu selectability after closing window on a class with no available airports (#11344) -Fix #11230: Sort by button in group list window could be misaligned (#11231) -Fix #11215: Assert in NewGRF parameters window (manual parameter mode) (#11217) -Fix #11203: [Linux] Crash when editing CJK characters in edit box (#11204) -Fix #11180: Aircraft crashes could point to the wrong tile (#11184) -Fix #11164: Don't create duplicate town names when using 'Many random towns' in the scenario editor (#11165) -Fix #11162: Second company colour was not consistently applied to articulated vehicles (#11163) -Fix #11115: Focus the abandon game/exit game windows (#11125) -Fix #11096: Increase priority of error and confirmation windows (#11104) -Fix #11087: Disable base graphics/sound dropdown outside main menu (#11091) -Fix #11054: Prevent translation of currency codes (#11061) -Fix #11026: Use real engine name instead of default name for filtering (#11033) -Fix #10982: No help text for gamelog command (#10984) -Fix #10880: Crash in object window due to incorrect parameter order (#10881) -Fix #10868: Crash when Script tries to load large savegame data (#11029) -Fix #10811: Allow dragging vehicle in depot to any free row (#11508) -Fix #10660: Sprite Font scale affected by viewport zoom level limits (#10668) -Fix #10619: Crash loading linkgraph for older savegames (#10620) -Fix #10600: 'Replace Vehicles' didn't show numbers >999 (#10680) -Fix #10578: Allow to select any version of AI/GS from GUI (#10604) -Fix #10522: Link graph tooltip vertical lines were not handled correctly (#10524) -Fix #10511: Don't search for depot every tick if one cannot be found (#11548) -Fix #10478: Clarify airport noise control setting texts (#11169) -Fix #10452: Prevent long stalls during river generation (#11544) -Fix #10430: Display chain window causing assert (#10431) -Fix #10343: Don't extend town-disallowed roadtypes (#10347) -Fix #10251: [MacOS] Screen looks blue-ish when using newer SDKs (#11207) -Fix #10222: Adjust line drawing algorithm (#10491) -Fix #10131: Actually cancel downloads when pressing cancel (#10485) -Fix #10118: Cycle through current signal group, not just path signals (#11798) -Fix #10439: [Script] Validate story page button colour, flags, cursor and vehicle type (#11892) -Fix #10438: [Script] Validate story page element type for ScriptStoryPage::NewElement (#11888) -Fix #9865: Removing files with the console always failed -Fix #9810: Rebuilding a through road stop costs money (#9852) -Fix #9722: Crash when pressing hotkeys early in world generation (#11858) -Fix #9697: Limit the default width of the Online Players window (#11936) -Fix #9642: Keep infrastructure totals when overbuilding road depots (#11229) -Fix #9545: Crash when all cargo types are disabled (#11432) -Fix #8846: When upgrading NewGRF presets, copy NewGRF parameters only if the NewGRF are compatible (#11348) -Fix #8253: Improve profit graph when having lots of money (#11915) -Fix #6377: Two tarballs with the same folder in them were considered as one (#11855) -Fix #5713: Ships could be sent to unreachable depots (#11768) -Fix #4575: Use Latin 'l' in English translation of zloty (#11090) -Fix #4415: Land info build date is also renovation date (#11759) -Fix: Display rank correctly with more than 15 companies in a league table (#11940) -Fix: Extra refit button when train/RV is in a depot (#11904) -Fix: Update server listing as offline when unexpected disconnect during refresh (#11891) -Fix: Horizontal scale of framerate window switched excessively (#11813) -Fix: [Linux] Various issues with resolutions and fullscreen in multi-display setups (#11778, #11779) -Fix: Build button text when train purchase window using "Engines" filter (#11755) -Fix: One-way state remained after removing road from road and tram tile (#11745) -Fix: Draw video driver info at the correct size and text wrap (#10716) -Fix: Language genders could not be applied to SCC_INDUSTRY_NAME (#11697) -Fix: Spurious cancellations of HTTP content downloads (#11668) -Fix: Calculation of initial engine age was inaccurate (#11660) -Fix: Prevent underflow if engine base life is less than 8 years (#11635) -Fix: Changing default livery did not propagate to group liveries (#11633) -Fix: Window width/height was doubly-scaled with automatic DPI switch (#11598) -Fix: Don't crash when saving a crashlog save with no main window open (#11586) -Fix: Prevent overflow when calculating max town noise (#11564) -Fix: Deleting towns did not check for waypoints referencing the town (#11513) -Fix: Invalidate playlist window when (un)shuffling playlist (#11504) -Fix: Restore original cargo legend 'blob' dimensions (#11480) -Fix: Extmidi did not move on to next song after playing ends (#11469) -Fix: Server password length in the UI was unnecessarily limited (#11408) -Fix: OpenTTD can fail to exit on an error due to mutex locks in threads (#11398) -Fix: Scale minimum width for server name by interface scale (#11381) -Fix: Server connection was not closed when relay window was closed (#11366) -Fix: Upgrading NewGRF presets could result in incomplete display of NewGRF parameters until restart (#11348) -Fix: Check for engine variant loops during NewGRF initialization (#11343) -Fix: Don't allow industries to produce invalid cargo (#11314) -Fix: Also apply cargo filters on shared groups in vehicle listing (#11294) -Fix: Only count distance traveled in vehicles for cargo payment (#11283) -Fix: Base cargo payment on load/unload tile, instead of station sign location (#11281) -Fix: Crash when opening a damaged base-graphics (#11275) -Fix: Trivial autoreplace of mixed cargo articulated engines (#11253) -Fix: [Emscripten] Config not saved on exit (#11248) -Fix: Inaccurate waiting cargo total in station window when using cargodist (#11213) -Fix: No fast forward in network was ensured only from GUI side (#11206) -Fix: Crash when not passing command-line parameter for -n (#11153) -Fix: [Bootstrap] Don't crash when failing to connect to content server (#11122) -Fix: Crash when failing to load a game into a dedicated server at startup (#11021) -Fix: Don't allow changing settings over the network that are marked as local settings (#11009) -Fix: Move no_http_content_downloads and use_relay_service to private settings (#10762) -Fix: Extra viewport could not be scrolled with right-click-close (#10644) -Fix: Specify units for value of share trading age setting (#10612) -Fix: Road type is not available before its introduction date (#10585) -Fix: Do not update a RV's Z-position when stationary while turning (#10570) -Fix: Don't (briefly) switch from title-only playlist on menu screen (#10553) -Fix: Reset content download progress to zero if falling back to TCP (#10485) -Fix: Make script goals work with the whole range of ClientIDs (#10435) -Fix: [NewGRF] Tile slope missing from road stops varact2 variable 0x42 (#11373) -Fix: [NewGRF] House class mappings were not reset between games (#11279) -Fix: [NewGRF] Profile didn't stop if there were no events yet (#10816) -Fix: [NewGRF] Support more than 256 stations/waypoints/roadstops per class (#10793) -Fix: [NewGRF] Var68 for station and roadstop was broken (#10784) -Fix: [NewGRF] Object and road stop ignore property handlers (#10525) -Fix: [Script] Apply random deviation to settings only at script start (#11944) -Fix: [Script] Improve ScriptText validation (#11721) -Fix: [Script] GSAdmin.Send() could generate invalid JSON (#11250) -Fix: [Script] Crash if squirrel compatibility scripts cannot be parsed (#11589) -Fix: [Script] Don't list unavailable road types for game scripts (#10585) -Fix: [Script] Game scripts were able to build with non-existing road types (#10539) -Fix: [Script] Inconsistent precondition failure return values (#10533) -Fix: [Script] Crash when companies disappear (#10529) -Fix: [Script] ScriptBase::Rand() return value could return negative values (#10443) -Fix: [Script] Incorrect value for GOAL_INVALID (#10436) -Fix: [Script] Extend Script::IsValidVehicle to check for primary vehicles (#10386) -Remove: "generation_seed" from config, as it was a write-only value (#11927) -Remove: Debug redirect over network (#11776) -Remove: Officially mark Vista as no longer supported (#11531) -Remove: OS/2 and SunOS ports (#11018, #11210) -Remove: Obsolete NewGRF text unprinting (#10884) -Remove: [Script] CONFIG_RANDOM from AddSetting flags (#11942) - - -13.4 (2023-07-29) ------------------------------------------------------------------------- -Fix: Setting tree lines drawn incorrectly for RTL languages (#11070) -Fix #11043: Don't choose toolbar dropdown option if focus is lost (#11044) -Fix #10917: Pay loan interest before generating statistics (#11040) -Fix #11016: Use after free in network invalid packet error path (#11022) -Fix #10987: Double-close of dropdown stopped land-info tool working as default (#11000) - - -13.3 (2023-06-11) ------------------------------------------------------------------------- -Fix: [Win32] use full monitor resolution for fullscreen (#10985) - - -13.2 (2023-06-10) ------------------------------------------------------------------------- -Change: [Win32] position window in center of workspace of primary display (#10942) -Change: Automatically disable hardware acceleration when GPU driver crashed the game last attempt (#10928) -Change: [Linux] Default scroll mode to non-mouse-lock (#10920) -Change: Include font style in font name for Freetype (#10879) -Fix: Don't restore backed up vehicle name if it's no longer unique (#10979) -Fix #10975: Train name wrongly marked as unique when joining trains (#10976) -Fix: Crash when not even a single row fits for dropdowns on low resolution screens (#10934) -Fix: Crash with tooltip on low resolution screens (#10933) -Fix: Crash when window can't be placed on low resolution screens (#10932) -Fix #10502: Apply engine refit before attaching free wagons (#10926) -Fix: Wayland crash on startup due to Pango also using FontConfig (#10916) -Fix: When syncing width of GUI items, take padding into account (#10915) -Fix: Make dropdowns self-close when losing focus (#10912) -Fix: Land info window maximum width was not scaled (#10894) -Fix: Check max member count in squirrel classes (#10883) -Fix: Ask FontConfig for the face index when opening fonts (#10878) -Fix #10831: Level crossing parts left barred after crossing tile removal (#10874) -Fix: Rail waypoint selection window not closed when parent windows closed (#10873) -Fix #10846: [Script] Crash on trying to allocate an excessively large array (#10848) -Fix: [Win32] Text line breaking did not properly handle punctuation characters (#10775) -Fix: [Emscripten] Crash when saving games (#10758) -Fix: [Win32] Wrong multi-line text layout due to incorrect whitespace handling (#10752) -Fix #10741: Rail platforms left partially reserved after train crash (#10751) -Fix: Shaded engines in purchase list incorrectly shaded (#10736) -Fix #10735: [NewGRF] {POP_COLOUR} fails if string is drawn with extra flags (#10736) -Fix #8177: Ships with max speed overflow to near-zero speed (#10695) -Fix #10289: Don't silently fail when setting timetable start dates (#10690) -Fix #8302: Improve "Maintenance intervals are in percents" helptext (#10686) -Fix #10665: "No vehicles are available yet" message did not appear correctly on non-temperate climates (#10673) -Fix #10630: Don't allow shifting service date earlier than year 0 (#10643) -Fix #10637, #10638: Incorrect water infrastructure totals when building certain object types (#10639, #10640) -Fix: Abort loading savegame if road vehicle is on invalid road type (#10622) - - -13.1 (2023-04-10) ------------------------------------------------------------------------- -Add: [NewGRF] Engine name callback for nested variants. (#10399) -Fix: Improve main toolbar tooltips (#10616) -Fix: [NewGRF] Additional validation for Action3 (+others) (#10601) -Fix: Clear button for editbox didn't take account of padding (#10583) -Fix: [Script] Access to enum/consts defined outside of main.nut (#10573) -Fix #10568: Bogus warning when loading a save with a NewGRFs on dedicated servers (#10572) -Fix #10554: Crash when scrolling in the autoreplace window with collapsed variants (#10555) -Fix: Network server highlight invisible with RTL languages. (#10551) -Fix: Client name was not being used as company manager name (#10535) -Fix: Prevent road vehicles on crossing from crashing into the side of a train (#10496) -Fix #10477: [macOS] Calculation for window sizes when using custom fonts was being rounded incorrectly (#10489) -Fix #10486: Crash in debug window when GS started before AIs (#10487) -Fix #10469: [Script] Negative numbers in League Table window were sorted incorrectly (#10471) -Fix #10465: Crash on timeout if user never enters a password for server (#10466) -Fix #10280, #10461: Crash on opening town windows as a spectator (#10462) -Fix #10059: Script config values stored in the config file could cause crashes (#10444) - - -13.0 (2023-02-05) ------------------------------------------------------------------------- -Change #10077: Make maximum loan a positive multiple of the loan interval (#10355) -Fix #10361: [Script] Don't try to give saved data to a dead script (#10433) -Fix #10419: Water infrastructure accounting when building ship depots and docks (#10432) - - -13.0-RC2 (2023-01-28) ------------------------------------------------------------------------- -Feature: Press Ctrl to build a diagonal area of trees (#10342) -Feature: Set a custom number of industries in map generation window (#10340) -Change: Display font status as aa/noaa instead of true/false (#10352) -Fix: [Script] Improved API documentation for scripts (#10413, #10412) -Fix #10255: Reduce basic thickness of linkgraph GUI lines (#10410) -Fix #10220: Don't select unselectable engine as default (#10404) -Fix #10395: When loading old saves, don't forcibly bar level crossings (#10400) -Fix #10377: Bad sorting of rail vehicles when primary variant is missing (#10378) -Fix #10368: Server restarting game caused clients to hit assertion (#10369) -Fix #10362: NewGRF bridges without speed limits (#10365) -Fix #10363: CargoDist setting helptext shouldn't suggest symmetric distribution for diamonds in subtropic (#10364) -Fix: [Script] Switch to OWNER_TOWN prevented OWNER_DEITY test during industry prospecting (#10360) -Fix #10009: Bad overflow protection when taking out loans (#10359) -Fix #9865: Removing files with the console always failed (#10357) -Fix #10057: FallbackParagraphLayout fails to properly wrap (#10356) -Fix #10177: Company list password padlock showed after switching to single player (#10354) -Fix: Various Wide River issues (#10348) -Fix: Link variants to parents when finalising engines (#10346) -Fix #10333: Only show industry prospecting errors to local company (#10338) -Fix #10335: Set initial scrollbar count for object GUI (#10336) -Fix #10331: Starting new company during load must happen after AI start (#10332) -Fix #10309: [SDL] Uninitialized width and height when turning off full screen (#10328) -Fix #10032: Capacities of articulated vehicles in build window (#10326) -Fix: Improve handling of corrupt NewGRF or image files (#10321, #10316) -Fix: [NewGRF] Don't assume engclass 2 should be elrail (#10315) -Fix: [Script] AIGroup.GetProfitLastYear could get values different than those displayed in GUI (#10227) -Fix #10304: [Scripts] Don't start GS in intro game (#10305) -Fix: [Script] Copy compat files for version 13 (#10303) - - -13.0-RC1 (2023-01-01) ------------------------------------------------------------------------- -Feature: 'font' console command to configure fonts within game (#10278) -Feature: Ctrl-click to bulk edit timetable speeds/waiting times (#10265) -Feature: [NewGRF] Vehicle variants in expandable purchase list (#10220) -Feature: Expand all towns in the scenario editor (#10215) -Add: [NewGRF] Slope-aware and roadtype-specific one-way sprites (#10282) -Change: Display text files in black (#10291) -Change: Make vehicle list dropdown buttons resize to fit strings (#10286) -Change: [NewGRF] Support flipping shorter engines without explicit support (#10262) -Change: Separate ground sprite from foundation sprite offsets (#10256) -Change: Vertically centre sprite font relative to TrueType font (#10254) -Change: [macOS] Set minimum macOS version to 10.13 (#10253) -Change: Use lowered not disabled widget for current vehicle details tab (#10252) -Change: Various improvements to NewGRF sprite aligner (#10249) -Change: reset_engines console command now rerandomises introduction dates and reliability (#10220) -Change: Show error message on failed industry prospecting (#10202) -Fix: Local authority window rating list height ignored icon sizes (#10285) -Fix #10150: Town signs could be truncated when using custom fonts (#10283) -Fix #8971: Resize QueryStrings with interface scale change (#10281) -Fix #10274: Crash when rescanning scripts with GS selected (#10276) -Fix #10151: Use smaller padding for signs (#10272) -Fix #10263: [Script] Restore tile validation for commands (#10269) -Fix: Missing scrollbar for rail/roadtype dropdowns (#10264) -Fix #10260: Incorrect rect height drawing image in vehicle details (#10261) -Fix #10257: Incorrect catenary position on sloped bridge heads (#10258) -Fix: Vertically centre chat prompt (#10250) -Fix #10214: League and graph buttons in toolbar did not have a default action (#10246) -Fix #10242: Allow a space for text shadow when clipping text (#10243) -Fix #10206: Fully disable scripts in intro game (#10241) -Fix #10218: Don't try to create river tiles along incorrect slopes (#10235) -Fix #10208: [NewGRF] Allow using a specific underlay for road/tram tunnels (#10233) -Fix #10224: Don't change fast-forward mode while saving (#10230) -Fix #10147: Sound effect volume slider no longer set volume (#10228) -Fix #10223: Crash when vehicle cloning fails on order cloning (#10225) -Fix: Maximum space for engine preview image was never scaled (#10219) -Fix #10216: Crash when upgrading savegame with crashed vehicles (#10217) -Fix #10212: [Script] Nested ScriptAccounting scopes not restored properly (#10213) -Fix #10114: Incorrect drag-highlight position with non-power-of-2 scaling (#10211) -Fix #10198: Rearrange Intro GUI to make button rows narrower (#10203) -Fix: Missing extra padding when drawing tooltip text (#10201) -Fix: Bad alignment of button icons when using the original baseset (#10200) -Fix: Signal icons incorrectly positioned in UI (#10199) -Fix #10021: Object GUI resized when switching between different objects (#10196) -Fix #9720: Delay start of GS/AI to after loading of savegame (#9745) - - -13.0-beta2 (2022-11-27) ------------------------------------------------------------------------- -Feature: Allow AI/GS to be fully modified in scenario editor (#10152) -Feature: Display power-to-weight ratio in ground vehicle details GUI (#10123) -Feature: Variable interface scaling (with chunky bevels!) (#10114) -Feature: Hotkey to honk a vehicle's horn (#10110) -Feature: Split AI/Game Script configuration windows and add them to world gen window (#10058) -Feature: [GS] Scriptable league tables (#10001) -Feature: Multi-track level crossings (#9931) -Feature: Improved local authority action window (#9928) -Feature: Automatic console command screenshot numbering with a filename ending in '#' (#9781) -Feature: Add buttons to toggle music in the Game Options menu (#9727) -Feature: Contextual actions for vehicles grouped by shared orders (#8425) -Feature: Add cargo filter support to vehicle list (#8308) -Feature: Show the cargoes the vehicles can carry in the vehicle list window (#8304) -Change: Allow building canal by area outside editor (#10173) -Change: Minor improvements to the new Finance GUI (#10168) -Change: Let AI developers edit non-editable AI/Game Script Parameters (#8895) -Change: Allow building docks on clearable watered object tiles (#8514) -Fix #8770: Center vehicle status bar icon (#10178) -Fix: Crash if error message window is too wide for screen. (#10172) -Fix #10155: Network games not syncing company settings properly (#10158) -Fix #10154: Network game desync related to setting a random company face (#10157) -Fix #10011: Incorrect infrastructure totals when overbuilding bay road stop (#10143) -Fix #10117: Object burst limit allowed one fewer object than the setting (#10120) -Fix #10023: Allow negative input in text fields when needed (#10112) -Fix #9908: Fix crash which could occur when a company was deleted when a depot window was open (#9912) - - -13.0-beta1 (2022-10-31) ------------------------------------------------------------------------- -Feature: Airport construction GUI displays infrastructure cost (#10094) -Feature: Purchase land multiple tiles at a time (#10027) -Feature: Add sticky pin & shade widgets to Object Selection UI panel (#10019, #10020) -Feature: Improved handling of HiDPI and mixed-DPI screens (#9994, #9996, #9997, #10064) -Feature: Alternative linkgraph colour schemes (#9866) -Feature: Allow Shift+Insert as paste in edit box (#9836) -Feature: Setting to make the local town authority rubber-stamp all actions (#9833) -Feature: Add/extend console commands to enable screenshot automation (#9771) -Feature: [Linkgraph] Show a tooltip with statistics when hovering a link (#9760) -Feature: Build objects by area (#9709) -Feature: Add setting to hide news about competitors vehicle crash (#9653) -Feature: Ctrl-click to remove fully autoreplaced vehicles from list (#9639) -Feature: Wide rivers on map generation (#9628) -Add: [Script] ScriptCargo::GetWeight to get cargo weights (#9930) -Add: Command line option to skip NewGRF scanning (#9879) -Add: Show video driver name in Game Options window (#9872) -Add: [NewGRF] Map seed as global variable (#9834) -Add: [Script] IndustryType::ResolveNewGRFID to resolve industry id from grf_local_id and grfid (#9798) -Add: [Script] ObjectType::ResolveNewGRFID to resolve object id from grfid and grf_local_id (#9795) -Update: To all the friends we have lost and those we have gained (#10000) -Change: Use the Simulation subcategory to openttd.desktop (#10015) -Change: Constantly update destination of 'any depot' orders (#9959) -Change: Use an indent, not a dash, to list train capacity (#9887) -Change: [NewGRF] Increase vehicle sprite stack from 4 layers to 8 (#9863) -Change: Don't pay Property Maintenance on stations when Infrastructure Maintenance is disabled (#9828) -Change: Improved layout of the finance window (#9827) -Change: [Admin] Bump admin port protocol due to command changes (#9754) -Change: Suppress vehicle age warnings for stopped vehicles (#9718) -Change: Make pf.yapf.rail_firstred_twoway_eol on by default (#9544) -Change: Deliver cargo to the closest industry first (#9536) -Fix: Lots of fixes to how windows handle resizing (#10040, #10042, #10046, #10051, #10056, #10068, #10070, #10098) -Fix: Console commands list_ai output was truncated with a suitably large number of AIs (#10075) -Fix #9876: MacBook Touch Bar crash / render issues w/ 32bpp graphics (#10060) -Fix: Reduce framerate overhead in Train::Tick (#10055) -Fix: Only open scenario editor date query once (#10050) -Fix #10048: Don't relocate company HQ on the same exact location (#10049) -Fix #10038: Missing upper bounds check when loading custom playlists (#10039) -Fix: Wrong string used to determine size of zoomed out station sign (#10036) -Fix: Disable "turn around" button for other companies' road vehicles (#10033) -Fix: Online Players list mouse hover behaviour (#10031) -Fix: [NewGRF] Weirdness of new stations (#10017) -Fix #9854: DrawStringMultiLine() could draw beyond its bounding box (#10014) -Fix: Incorrect player name in online players window (#10013) -Fix #8099: News window zoom level fixes (#10005) -Fix: [NewGRF] Upper 16 random bits should be the same for all station tiles in callback 140 (#9992) -Fix #9989: £0 Net Profit is neither negative nor positive (#9991) -Fix #9804: Only apply sprite_zoom_min setting when sprites available (#9988) -Fix #9972: Add missing fill/resize flags on Framerate window widgets (#9982) -Fix #9935: Use more selectivity when building SSE specific code (#9980) -Fix #9940: Print debuglevel parse errors to console when changed from console (#9979) -Fix #9977: Clearing the console with a large number of lines could cause a crash (#9978) -Fix #9974: Console command getsysdate did not work due to off-by-one error (#9975) -Fix: [NewGRF] Default value of RailVehicleInfo::railveh_type was inconsistent with other default properties (#9967) -Fix #8584: Vehicles with shared orders getting invalid or unexpected start dates (#9955) -Fix #9951: [NewGRF] Scenario editor random industries button broke NewGRF persistent storage (#9952) -Fix: Validation of various internal command parameters that could have allowed a rogue client to crash servers (#9942, #9943, #9944, #9945, #9946, #9947, #9948, #9950) -Fix #9937: Station industries_near incorrect after removing part moved sign (#9938) -Fix: [Script] ScriptRoad::HasRoadType really check for RoadType (#9934) -Fix #9363: Rebuild client list on reinit event (#9929) -Fix #9925: Industry tile layout validation for layouts of only one tile (#9926) -Fix #9918: Reset industy last production year on scenario start (#9920) -Fix #9914: Prevent more useless pathfinder run for blocked vehicles (#9917) -Fix: List a max of four share owners instead of three (#9905) -Fix: [NewGRF] Industry layouts with zero regular tiles should be invalid (#9902) -Fix #9869: Remove docking tile when doing a clear square (#9898) -Fix: New player companies use favorite manager face, if saved (#9895) -Fix: Towns don't build parallel, redundant bridges (#9891) -Fix #9712: Cap town bridge length at original 11-tile limit (#9890) -Fix #9883: Show cost/income float over end tile of rail or road construction (#9889) -Fix #9870: Don't update infrastructure totals when overbuilding object on canal (#9888) -Fix #9877: GS could trigger 'Cost: £0' cost message (#9878) -Fix 44f2ef1: [strgen] Allow gender for {CARGO_SHORT} (#9873) -Fix #9867: Industry::stations_near not filled at industry creation (#9868) -Fix #9853: Incorrect merge of guiflags and flags for osk_activation (#9855) -Fix #6544: Don't join AI company when loading network game in singleplayer (#9794) -Fix: Company values do not properly account for shares (#9770) -Fix #9546: Crash when no industries are present in game (#9726) -Fix #9708: [Linkgraph] Don't assume vehicles have a non-zero max speed (#9693) -Fix #9665: [Linkgraph] Fix travel times of non-direct journeys (#9693) -Fix #8797: Use logical rail length when placing signals (#9652) -Cleanup: [NewGRF] Remove unused flag sprites (#10052) - - -12.2 (2022-04-02) ------------------------------------------------------------------------- -Feature: Remember the last-used signal between games (#9792) -Change: [MacOS] Allow touchbar usage on all supported OS versions (#9776) -Change: Add a timestamp in name of crash files (#9761) -Fix #9736: Duplicate multiplayer window opens upon canceling password entry (#9842) -Fix: Removing long roads doesn't prioritise refusal of local authority over other errors (#9831) -Fix #9020: Glitchy station coverage highlight when changing selection (#9825) -Fix: Correct some Romanian town names (#9819) -Fix: Original music playback rate was slightly too fast (#9814) -Fix #9811: Use the NewGRF-defined vehicle center when dragging ships and aircraft (#9812) -Fix: Do not let shares in the company taking over another company disappear (#9808) -Fix #9802: Crash when using lots of NewGRF waypoint types (#9803) -Fix #9766: Don't write uninitialised data in config file (#9767) -Fix #9743: [MacOS] Don't try to render touchbar sprites with invalid zoom level (#9776) -Fix #9774: Building roadstop in estimation mode updates station acceptance (#9775) -Fix: If vehicles only refit to cargo-slots >= 32, the default cargo was wrong (#9744) -Fix #9735: Possible desync when replacing a depot on same tile (#9738) -Fix #9730: [Network] Connections can use an invalid socket due to a race condition (#9731) -Fix: Don't show sign edit window for GS-owned signs (#9716) -Fix #9702: Display order window for vehicle group on ctrl-click only when using shared orders (#9704) -Fix #9680: Crash when loading really old savegames with aircraft in certain places (#9699) -Fix: Update last servicing dates when using the date cheat (#9694) -Fix: Error message shows about missing glyphs while suitable fallback font is found (#9692) - - -12.1 (2021-11-08) ------------------------------------------------------------------------- -Feature: Button to toggle showing advanced signal types (#9617) -Change: Don't show screenshot GUI in screenshots (#9674) -Change: Suppress panning in intro game, while user is interacting with the GUI (#9645) -Change: Draw rotor in cursor when dragging helicopters in depots (#9612) -Fix: Invalid memory access when loading a currency NewGRF (#9675) -Fix #9579: Object and HQ construction is Construction cost, not Property Maintenance (#9673) -Fix #9669: Ships exiting a blocked depot/lock could exit in the wrong direction (#9672) -Fix: Every 16th client never reconnects after server restart (#9666) -Fix #9643: Screenshots were always written as BMP files (#9644) -Fix #9630: Intro game could zoom in/out more than allowed by settings (#9633) -Fix #9626: Incorrect loading of script saved data (#9629) -Fix: Emergency crash save had the wrong NewGRF list saved in it (#9627) -Fix #9595: Always use plural forms of cargo for subsidy strings (#9619) -Fix #9614: Refresh rate dropdown was still active when vsync was enabled (#9618) -Fix: Don't use 'server address' string in server list when displaying an invite code (#9615) - - -12.0 (2021-10-17) ------------------------------------------------------------------------- -Add: [Network] Keep the refresh button in lowered state while refreshing (#9600) -Add: Console command to list search directories for various things (#9583) -Fix: Try all possible reverse directions when a ship reaches a dead end (#9610) -Fix: Incorrect Romanian own name (#9598) -Fix #9591: Update station docking tiles upon placing a water object on a docking tile (#9594) -Fix #9548: [Squirrel] Crash during engine cleanup after reaching memory limit on realloc (#9592) -Fix #9588: [Squirrel] Reaching memory limit during script registration could prevent further script detections (#9589) -Fix: Make ships more likely to find their destination at the cost of slightly worse paths (#9576) -Change: Reverse ship when leaving docks if a better path exists (#9610) -Change: Allow all tiles around docks to be docking tiles (#9578) - - -12.0-RC1 (2021-09-25) ------------------------------------------------------------------------- -Feature: Display icon/text whether vehicle is lost in vehicle (list) window (#9543) -Feature: [MacOS] Add selected toolbar buttons to MacBook Pro Touch Bar (#9511) -Feature: Button to open order window from vehicle shared orders window (#9325) -Feature: Ctrl-Clicking shared order vehicle list opens order window (#9325) -Feature: Multiple rotating views on title screen (#8980) -Feature: Hide block signals in GUI by default (#8688) -Add: [Script] Allow GameScripts to build neutral objects (#9568) -Add: [Network] Allow sending chat messages via admin port (#9563) -Add: [AI/GS] Missing water related functions and objects (#8390) -Fix: Industry funding window did not update when changing funding method (#9572) -Fix #9562: [NewGRF] Handle case of invalid Action2 with zero results (#9564) -Fix: Incorrect error messages when placing water in scenario editor (#9560) -Fix #9484: Update locale currencies settings config map (#9559) -Fix: Prevent train reversing when entirely inside a train depot (#9557) -Fix: [Network] Add back 'Spectate' option to company toolbar menu (#9556) -Fix #9463: [Win32] Work around XAudio2 crashes (#9549) -Fix #8603: Don't give focus to text filter when opening Object GUI (#9547) -Fix #9241: Grove and forest tree brushes did not also create rainforest terrain (#9542) -Fix: [Network] Several crashes in our network code (#9534, #9456) -Fix #9527: Crash when trying to place multi-tile objects at map edge (#9529) -Fix: [Network] SendCmdNames only sent one name per packet (#9528) -Fix #9407: Desync when founding a town nearby a station (#9526) -Fix #9521: Don't load at just removed docks that were part of a multi-dock station (#9524) -Fix: Ships always tried to avoid docking tiles when pathfinding (even if nothing was on them) (#9522) -Fix: [Network] Convert server_advertise to server_game_type in config file (#9515) -Fix #9490: [Network] A full server couldn't be queried (#9508) -Fix: [Network] Don't show GameScript " (v0)" for old servers (#9507) -Fix: [Network] Show query errors in the server listing instead of error popup (#9506) -Fix: [Network] Crash when last-joined server was no longer available (#9503) -Fix #9501: [Network] Crash when more than one game-info query was pending (#9502) -Fix: Wrong error message when building canals over ship depots / locks (#9410) -Fix: Reduce cost of building canals over objects on sea (#9410) -Change: [Linkgraph] Delete links only served by vehicles stopped in depot (#9499) - - -12.0-beta2 (2021-08-19) ------------------------------------------------------------------------- -Feature: [Linkgraph] Prioritize faster routes for passengers, mail and express cargo (#9457) -Fix: Wrong town window refreshed when building an airport with noise levels enabled (#9497) -Fix: Improve wording of network-related messages (#9494, #9495, #9500) -Fix: [Network] Report reuse of invite-code (#9487) -Fix: [Network] Connecting with the same client name thrice hangs the server (#9485) - - -12.0-beta1 (2021-08-15) ------------------------------------------------------------------------- -Feature: [Network] Remove lobby window; pressing "Join Game" now immediately joins a server (#9467) -Feature: [Network] Synchronize server name to clients and display in Online Players window (#9472) -Feature: [Network] Mention you are a spectator in the status bar (#9471) -Feature: [Network] No longer require port-forwarding to host a server (#9443, #9447) -Feature: [Network] Allow setting your server visibility to "invite-only" (#9434) -Feature: [Network] Join servers based on their invite code (#9432) -Feature: Raise the maximum NewGRF limit to 255 (#9428) -Feature: Persistent rotation of numbered auto/netsave after restart (#9395, #9397) -Feature: [NewGRF] Maximum curve speed modifier for rail vehicles (#9346) -Feature: Move sensitive information to secrets.cfg and private information to private.cfg (#9298) -Feature: Signed Windows builds (#9294) -Feature: [NewGRF] Define refittability of default vehicles using cargo classes (#9148) -Feature: Configurable subsidy duration, up to 5000 years (#9081) -Feature: [Network] Rework in-game Online Players window (#9067) -Feature: [Network] Show previous chat history when the chat message box is open (#9025) -Feature: Button to reset game settings to their default values (#8958) -Feature: Press Ctrl to build diagonal rivers in Scenario Editor (#8880) -Feature: Set wagon replacement per group when using autoreplace (#7441) -Add: [Network] Open Online Players window on starting/joining a server (#9479) -Add: [Script] Basic information about loaded NewGRFs for scripts (#9464) -Add: [AI] Get the number of vehicles in a given group (#9462) -Add: [Network] Inform network clients what game-script a server is running (#9441) -Add: Hindi translation (#9086) -Add: [Network] Ensure players fill in a name instead of defaulting to "Player" (#9080) -Change: Allow pause/unpause console command in single player (#9342) -Change: Make savegame format self-descriptive and consistent across all objects (#9322, #9335, #9338, #9339) -Change: By default, make "unload all" leave stations empty (#9301) -Change: Reworked the debug levels and messages for network logs (#9230, #9251) -Change: [Emscripten] Set default scrolling mode to non-pointer-locking (#9191) -Change: Use neutral pronouns for various strings (#9189, #9203, #9228) -Change: Make the town directory horizontally resizable (#9157) -Change: Allow non-ASCII currency separators (#9121) -Change: [NewGRF] Display a pop-up window for Errors with severity ERROR (#9119) -Change: Treat languages as finished, if translations are 75% completed (#9019, #9086) -Change: Disable NewGRF window apply button if no change was made (#8934) -Fix: [Script] Crash when iterating lists of which the key is larger than 32bit (#9465) -Fix: [Network] Desync due to use of unstable sort when distributing cargo production (#9460) -Fix #9440: Negative cargo payments not being handled correctly (#9455) -Fix: [Network] Crash when joining a server again after a TCP disconnect (#9453) -Fix: Don't enable rename button for network clients in build vehicle window (#9452) -Fix: Money could underflow and wrap around (#9451) -Fix: Parse the console settings the same way as config settings (#9438) -Fix: Ensure no more than the allowed number of NewGRFs are loaded from the configuration (#9430) -Fix: [NewGRF] Overflow when determining cargo mask for string code 9A 1E (#9423) -Fix: Integers for scripts are 64bit, but saved as 32bit (#9415) -Fix #9392: [Script] Return a valid value with GetBuildWithRefitCapacity even when AIs are maxed out in vehicles (#9393) -Fix #8169: Crash when autoreplacing vehicle with no orders (#9387) -Fix: Wrong cargo line position in IndustryCargo window (#9383) -Fix: Race-condition during startup of NewGRF scan (#9382) -Fix: Don't propagate Shift/Ctrl state till next game-tick (#9381) -Fix: Prevent palette updates during copying to the video driver (#9379) -Fix: [Network] Determining GetNetworkRevisionString could overflow and underflow its buffer (#9372) -Fix #9358: Don't skip empty files in tar archives (#9367) -Fix: For old savegames, station bus/truck station cache was not updated (#9366) -Fix #9353: [Script] Garbage collecting on priority queues could crash the game (#9356) -Fix: Respect the autosave_on_exit setting for Null video driver (#9343) -Fix: Compatible NewGRFs in crash-log reported wrong MD5 hash (#9340) -Fix: [Script] Ensure the saved script strings are properly validated and terminated (#9336) -Fix #9316: Town bridge length limit check incorrect above 250k inhabitants (#9318) -Fix: Limit heightmap sizes to 8192x8192 (#9307) -Fix #9281: Money generating exploit when buying out a company (#9300) -Fix: Part of a tile might not be redrawn when terraforming (#9296) -Fix: [OpenGL] Increase timeout when waiting for the GPU to be done with the drawing buffer (#9282) -Fix: Vehicles sent in the wrong direction if there is no path to the destination (#9280) -Fix #9264: Do not attach temporary wagons to free wagon chains when autoreplacing (#9278) -Fix #9267: [Script] Crash during garbage collection (#9275) -Fix: Encountering two-way red signals could prune unrelated Pathfinder branches (#9271) -Fix #9255: [Network] Crash when hostname is not found (#9259) -Fix #9256: Invalid read after free when replacing train chains (#9258) -Fix: [Emscripten] Force secure WebSockets over HTTPS (#9248) -Fix #9242: Tree tick handler did not scale by map size (#9246) -Fix: [Network] Mark server as offline when no longer reachable (#9244) -Fix: [Network] Don't rebuild the host-list during iterating the list (#9240) -Fix: [Network] Don't mark the last-joined server as a manually added server (#9239) -Fix: [Network] Clients leaving because of broken connections was not broadcasted (#9238) -Fix: [Network] Check on CIDR for netmask check considered everything valid (#9235) -Fix: Creating screenshots on dedicated servers failed (#9232) -Fix: Leaking file descriptors for downloaded content (#9229) -Fix: Spelling of several town names (#9222) -Fix #9209: Game hangs when resizing highscore/news window if the screen is too small (#9210) -Fix: [Network] Optimize creating network connections for clients using IPv4 and IPv6 (#9199) -Fix #9186: Fix incorrect bounding box height causing station sprite glitch (#9187) -Fix: Truncating strings in settings could leave invalid UTF-8 characters (#9121) -Fix: Many issues related to window scaling (#9087, #9219) -Fix: Invalidate cached vehicle colourmaps when changing liveries setting (#9006) -Fix #8981: Don't attempt to re-reserve path if already entering/entered depot (#9000) -Fix: Missing 'Town names:' colon in map gen GUI (#8986) -Fix: Sorting and filtering industries that produce/accept many cargoes (#8468) -Remove: [Network] COMPANY_INFO packets (#9475) -Remove: [Network] A server can no longer set a limit to the amount of spectators allowed (#9466) -Remove: Arbitrary limit on number of statically loaded NewGRFs (#9431) -Remove: [Network] Language and map-name from server information (#9070) - - -1.11.2 (2021-05-03) ------------------------------------------------------------------------- -Change: [Win32] Limit hardware accelerated video driver to OpenGL 3.2 or higher (#9077) -Change: More improvements to the GUI at different scales (#9075, #9102, #9107, #9133, #9174, #9183) -Fix: Query windows could be partially drawn (#9184) -Fix #9113: Crash when removing an airport that exists in an aircraft's orders (#9182) -Fix #9117: [Fluidsynth] Hang when changing song (#9181) -Fix: String validation could leave invalid UTF-8 encoded strings (#9096) -Fix: [Network] Out-of-bounds memory access with modified servers sending too short password salts (#9176) -Fix: Crash when extra viewport with zero height has sign in view (#9175) -Fix #9147: Crash when taking screenshots (#9169) -Fix #6598: [Network] Prevent crashes when (re)joining network game by falling back to main menu first (#9163) -Fix #9152: Screenshot success popup window was treated as an error (#9159) -Fix: Fast-forward stuttering when vsync is enabled (#9140) -Fix: [Network, Win32] Network errors were handled badly (#9116) -Fix: [Network] Savegame transfer could stall in rare cases (#9106) -Fix #9097: [NewGRF] Cargo initial payment variable was being truncated (#9098) -Fix: [NewGRF] Industry variable 66 and object variable 46 erroneously truncated the distance (#9088) -Fix: [NewGRF] Industry variables 65 and 66 ignored the parameter, and always used the north tile (#9088) -Fix: Do not include regression test AI in bundle (#9068, #9164) -Fix #9062: [Win32] Version in executable was not set to current release version (#9066, #9154) - - -1.11.1 (2021-04-18) ------------------------------------------------------------------------- -Feature: Toggle to enable/disable vsync (#8997) -Feature: Volume controls in the Game Options window, and better defaults (#8943) -Add: Hotkey to focus object and rail filters (#8908) -Add: Better plural support for Romanian (#8936) -Change: Improve layout and spacing of several windows at different GUI scales (#9041, #9042, #9044, #9050) -Change: [Win32] Use user UI language setting for initial language selection (#8974) -Change: Make effect volume scale more intuitively (#8945, #8950) -Change: Improve padding of Object & Rail station windows (#8929) -Fix #6322: [Script] Crash when script allocates too much memory, now kills script instead (#9047) -Fix #7513: [Script] Crash on garbage collection with misbehaving script (#9040) -Fix #9028: [OpenGL] Crash when changing max sprite zoom level (#9032) -Fix #8874: show a warning when a NewGRF scan is requested multiple times (#9022) -Fix: Desync when GS unlocks railtype with wagon unlock (#9021) -Fix #9015: [Win32] Crash on running "pwd" command in the console (#9016) -Fix #9008: Validate starting year given on the command line (-t) (#9014) -Fix #8878: [Network] Slow DNS queries could block the server and disconnect clients (#9013) -Fix: Improve validation of OpenGL video driver to avoid crashes (#9007) -Fix: Credits scrolled too slowly with larger font sizes (#8994) -Fix #8977: Crash when altering max sprite resolution (#8993) -Fix #8956: Industry disaster news messages showed the wrong location (#8992) -Fix: [Win32] Font glyphs of certain widths had broken rendering (#8990) -Fix #8930: [Win32] Duplicate text input issue for systems using IME (#8976) -Fix: [Network] Potential stale client entries in client list (#8959) -Fix: Graphical issues when dragging measurement tooltips (#8951) -Fix: [Fluidsynth] Use provided default soundfont if available (#8948, #8953) -Fix #8935: [macOS] Crash on save (#8944) -Fix #8922: Crash when selling shared vehicles with shared vehicle window open (#8926) -Fix: Compiling on armhf (Raspberry Pi) (#8924) - - -1.11.0 (2021-04-01) ------------------------------------------------------------------------- -Feature: Allow setting a custom terrain type to define highest peak (#8891) -Feature: Auto-detect map height limit based on generated map (#8891) -Feature: Setting to indicate desert coverage for tropic climate and snow coverage for arctic climate (replaces snow line height) (#8891) -Add: Allow setting the highest mountain for heightmaps (#8891) -Change: Scale exported heightmaps to highest peak and inform the user of this value (#8891) -Change: Remove "maximum map height" from the New Game GUI (#8891) -Fix #8803: Only auto-remove signals when rail can be built (#8904) -Fix #8565: Stopped road vehicle displays a speed different than 0 (#8901) -Fix #8886: Don't try to resolve folders within tars named '.' (#8893) -Fix: Placing random trees in SE crashes the game (#8892) -Fix #8875: Filter string in station window breaks flow in user interface (#8885) -Fix #8871: [OpenGL] Initialize all buffers after resize and clear back buffer (#8877) -Fix: OpenGL performance with some AMD GPUs (#8876) -Fix: Recompute road/railtype availability after disabling the engine (#8872) -Fix: OSK layout not scaled for 2x or 4x GUI scale (#8868) - - -1.11.0-RC1 (2021-03-14) ------------------------------------------------------------------------- -Feature: Option to (dis-)allow hardware accelerated video drivers (#8819) -Feature: Option to set display refresh rate (#8813) -Feature: Allow custom width/height of screenshot and making heightmap screenshots via console (#8804) -Feature: Allow filtering on name in rail station window (#8706) -Feature: Setting for highest resolution of sprites to use (#8604) -Add: Make NewGRF Scanner / World Generation update smoother and make aborting it react faster (#8830) -Add: Malaysia Ringgit as Currency (#8783) -Add: "Engines only" filter in build train window (#8733) -Change: De-limit framerate window's framerate (#8772) -Change: Clarify what effect town interactions have (#8744) -Change: Don't show global goals in company goal windows (#8709) -Change: Recolour graph windows to brown (#8700) -Fix #8855: Bootstrap could result in an empty screen when bootstrap fails (#8856) -Fix #8851: Don't allow infinite "exec" depth in script, but limit to 10 deep (#8852) -Fix #8647: Incorrect drawing order of tram catenary sprites (#8843) -Fix #8711: Having gui_zoom lower than zoom_min causes a crash (#8835) -Fix #8810: "aircraft out of fuel" news shows the wrong place (#8832) -Fix #8833: Don't reload NewGRFs when we are shutting down (#8830) -Fix: Scale padding between elements the same as other padding (#8829) -Fix #8808: [OSX, OpenGL] Crash on switching blitters due to double-mapping the video buffer (#8822) -Fix #8784: Using Alt+Enter doesn't update the fullscreen toggle visibly (#8820) -Fix #8817: Keep NewGRF order for object class sorting (#8818) -Fix #8809: Crash when removing airport when hangar window open (#8815) -Fix #8799: Crash when Search Internet in Multiplayer (#8801) -Fix #8775: [Win32] Don't create the main window when Alt-Tabbing back into fullscreen (#8792) -Fix #8774: Black screenshots when using 40bpp-blitter (#8791) -Fix: [OSX] Hide dock when entering fullscreen (#8789) -Fix: Bootstrap fails to start on clean install (#8788) -Fix: Terraform limit acts random when maxing out per_64k_frames setting (#8782) -Fix: Max-value of fast-forward-speed-limit can be outside its storage size (#8769) - - -1.11.0-beta2 (2021-02-28) ------------------------------------------------------------------------- -Feature: Add setting to limit fast-forward speed (#8766) -Feature: Significant performance improvements to all video drivers (#8605, #8652, #8660, #8685, #8702, #8703, #8707, #8726, #8740) -Feature: Configurable display refresh-rate, default to 60fps (#8680) -Feature: Automatically upload releases to Steam (#8644) -Feature: Generic Linux builds (#8641) -Feature: [GS] Allow non-question type windows to have no buttons (#8638) -Feature: [macOS] ZIP build (#8614) -Feature: Object class selection string filtering (#8603) -Feature: 'Remove all industries' button in scenario editor (#8550) -Feature: Automatic UI and font zoom levels when supported by the OS (#8537) -Feature: [macOS] Render screen at native resolution by default for HiDPI screens (#8519) -Feature: OpenGL video driver (#7744) -Add: Indonesia Rupiah currency (#8616) -Change: Improve graph period markings (#8732) -Change: Make pathfinder account for maximum order speed, if set (#8722) -Change: Darken graph grid lines for legibility (#8690) -Change: Make order window hotkeys toggle for load & unload variants (#8669) -Change: Use a more specific error message when attempting to bulldoze your own HQ (#8667) -Change: Convert .md to .rtf for Windows/Mac packages (#8617) -Change: Move the 'tree placer algorithm' & 'road drive side' settings to the Settings window (#8566) -Change: Move town name generator selection to mapgen GUI (#8566) -Change: [macOS] Native font rendering (#8518) -Fix: Display of network lobby windows for different GUI sizes (#8765) -Fix: Don't desync if client leaves before you finish downloading map (#8755) -Fix: Allow estimating vehicle clone cost even if short on money (#8748) -Fix: Don't notify twice that a client left because of a timeout (#8746) -Fix: Vehicle cursor size did not account for the interface zoom level (#8739) -Fix #8123: Trams on half-tiles couldn't find depots (#8738) -Fix #8276: [NewGRF] Crash when an object's size was not set (#8719) -Fix #8349: Close depot vehicle list windows when closing the depot window (#8717) -Fix #8594: [NRT] Road pathfinder did not account for roadtype speed limits or lengths of tunnels/bridges (#8710) -Fix: Whole status bar instead of money widget refreshed on money change (#8692) -Fix: Unnecessary status bar redraws when there is no news to show (#8691) -Fix: New orders are non-stop by default (#8689) -Fix: Framerate window showed a slightly higher rate than actually measured (#8682) -Fix: Autorenew failure advice due to bad refit being shown to all companies (#8681) -Fix #8625: Wrong ending year was displayed in highscore table (#8672) -Fix #8620: Scale spacing between date & news in history window according to font scaling (#8671) -Fix: [Win32] Set minimum resolution for timers to 1ms (#8660) -Fix: Mention our websites with https:// (instead of http://) (#8657) -Fix: [Emscripten] Open links in browser (#8655) -Fix: Don't crash when towns upgrade road tiles during expansion (#8651) -Fix #8029: [SDL2] Blank display when under Wayland (#8648) -Fix: Default Network Server List sorter put compatible servers in wrong order (#8626) -Fix: Use non-pulsating red highlight for coverage (#8622) -Fix: Center text and image in vehicle statusbar vertically (#8602) -Fix: Don't walk out of the map when trying to build tunnels (#8600) -Fix: Off-by-one error in desert/rainforest positioning at world gen (#8588) -Fix #8037: Crash when restarting AI that is controlling the same company as the player (#8587) -Fix: Stopped ships shouldn't block depots (#8578) - - -1.11.0-beta1 (2021-01-22) ------------------------------------------------------------------------- -Feature: [GS] Ability to set some extra text in the industry window (#8576) -Feature: Show rainforest under vegetation on smallmap (#8562) -Feature: Automatically determine window size on new install (#8536) -Feature: Towns can build tunnels (#8473) -Feature: Make maximum length of town bridges depend on population (with a minimum limit of 4) (#8439) -Feature: New icons for renaming and go-to-location on GUI windows, and improve consistency of usage (#8455) -Feature: Support for ARM64 on Apple Silicon and Windows (#8340, #8577, #8583) -Feature: Add an option to disable tree growth completely (#8415) -Feature: Support for Emscripten (play-OpenTTD-in-the-browser!) (#8355) -Feature: Show group name as part of the default vehicle name (#8307) -Feature: "Frozen" economy setting that stops production changes and industry closures (#8282) -Feature: New velocity unit "tiles/day" (#8278) -Feature: Option to automatically remove signals when placing rail (#8274) -Feature: Increase max possible distance from border for oil refineries and rigs (#8237) -Feature: Improve tree planting window, and allow planting 'clumps' of trees by dragging in the scenario editor (#8234) -Feature: Indian Rupee (INR) currency (#8136) -Feature: [GS] Ability to give a company exclusive access to an industry (#8115) -Feature: Hotkeys for Land Info window, News window & close error window (#8053, #8266) -Feature: Improve rendering of large viewports (#7962) -Feature: [GS] Influence industry production changes from GameScript (#7912) -Feature: [GS] Push-buttons on storybook pages (#7896) -Feature: Option to group vehicle lists by shared orders (#7028) -Feature: Drag-and-drop vehicles in group GUI for shared order groups (#7028) -Add: [GS] A tile parameter to GSCompany::ChangeBankBalance for showing changes more visually (#8573) -Add: [NewGRF] Allow NewGRF vehicles to query the current rail/road/tram type (#8554) -Add: [Script] ScriptCargo::GetName for the human readable name of cargoes (#8544) -Add: "reload" console command to reload the current scenario or heightmap (#8527) -Add: [NewGRF] Flag to test if inflation is on or off (#8427) -Add: [Script] Native priority queue (useful for things like pathfinders) (#8091) -Add: [NewGRF] Industry behaviour flag to override second cargo production clamping for water industries when using smooth economy (#8079) -Change: [SDL2] Start game on the screen where the cursor is (#8572) -Change: Use a dark background for all profit graphs to increase contrast (#8557) -Change: Reword warning in cheat window (#8538) -Change: Enable the toolbar for road/rail/dock/airport, regardless of vehicle availability (#8521) -Change: For arctic and tropical climates, make sure at least a few hills are generated (#8513) -Change: Destroying a tunnel/bridge now sells the tracks before destroying the tunnel/bridge (#8508) -Change: Move "give money" from client-list to company window (#8500) -Change: [MacOS] Hide Dock and menu when in fullscreen mode (#8487) -Change: Improve performance for complex vehicle chains by resolving sprites less frequently (#8485) -Change: Make engine reliability independent of introduction date (#8470) -Change: Some default settings to improve gameplay for new players - default non-stop orders on, disable inflation, quick goto orders, show track reservations, and more (#8463) -Change: Converting town-owned road types now requires a positive town rating (#8457) -Change: Rework server list buttons for searching LAN/internet servers (#8426) -Change: Add some styling to GS question windows depending on the type (#8422) -Change: [Linkgraph] Speed up game exit by allowing job threads to be aborted early (#8416) -Change: Prevent towns from building dead-end road bridges (#8401) -Change: Send network error to the server before making an emergency save (#8387) -Change: Extend the allowed range for max loan setting up to £2 billion (#8386) -Change: Don't display OS name when exiting the game (#8366) -Change: Save openttd.cfg immediately on changing a setting (#8358) -Change: Autorenew now defaults to on (#8352) -Change: [NewGRF] Also use aircraft property 12 for helicopters (#8347) -Change: Service at depot also resets breakdown chance (#8317) -Change: Use key names instead of characters in hotkey.cfg (#8291) -Change: Allow command cost-estimation while paused (#8222) -Change: Always apply inflation from 1920 to 2090, no matter the game start year (#7589) -Change: Use CMake for build system (#7270) -Change: [Linkgraph] Pause the game when linkgraph jobs lag (#7081) -Change: Place "Group by" above "Sort by" in station window for consistency (#7028) -Fix #8589: Prevent desyncs with vehicle motion counters and NewGRFs (#8591) -Fix #7670: Improve pathfinder performance when lost vehicles are blocked from moving (#8568) -Fix: Inform user if a custom font failed to load due to missing glyphs (#8559) -Fix: Don't allow wagon chains (without an engine) to exceed maximum train length (#8533) -Fix #7619: Super fast NewGRF aircraft could be unable to land (#8531) -Fix: Improve connection retries for the content server in cases of broken networking (#8530) -Fix #7972: Show invalid orders to stations that don't accept the vehicle (#8516) -Fix: Error when trying to clone a vehicle with invalid orders (#8515) -Fix #8050: Various off-by-one errors in how the end-year of the game was used (#8512) -Fix #8332: Aborting vehicle group drag & drop could cause crashes (#8511) -Fix #8168: Allow relocating HQ partially over an existing HQ (#8510) -Fix #8068: Allow selling tram track regardless of bank balance (#8509) -Fix #7604: Prevent houses from wandering away from roads (#8507) -Fix: Make the "password" button the same size as the other buttons in the Company window (#8500) -Fix #7611: Keep news about vehicle accidents around after the vehicle is cleaned up (#8497) -Fix: [MacOS] Full animation in fullscreen mode was reducing the height of the window (#8491) -Fix: [MacOS] Loading custom fonts (#8484) -Fix: Network client makes emergency saves twice if the server is disconnected (#8477) -Fix #8462: Stop towns from trying to build roads on water (#8471) -Fix: [NewGRF] GetCurveSpeedLimit should use the railtype from the current tile (#8466) -Fix #8437: Crash when using certain heliports with certain rotated airports (#8458) -Fix #8437: Planes would land at the wrong height if the top corner of the airport was lowered (#8458) -Fix #8297: Infrastructure counters for road tunnels, bridges & depots (#8454) -Fix #6468: Don't store the version of AIs that are started via console (#8430) -Fix: Don't lower tree density if spreading is not enabled (#8413) -Fix: Prevent savegame version conflicts with certain old patchpacks (#8411) -Fix: [NewGRF] Variable 0x44 was always HZB_TOWN_EDGE for road stops (#8400) -Fix #8313: Use correct capitalization for TTO / DOS music files in the baseset metadata (#8385) -Fix: [NewGRF] Action 7/9 conditions 0x0F to 0x12 failed, if 'param' was 0x88 (#8382) -Fix: Change the working-dir searchpath when using '-c' (#8367) -Fix: Useless warning with -snull and no BaseSounds available (#8361) -Fix: Crash trying to load TTO/TTD savegames. (#8356) -Fix: [Script] Don't echo script exceptions to console (#8331) -Fix: Slovak ownname was using the wrong form (#8326) -Fix #8311: [NewGRF] Industry probability at map generation was scaled differently when set via property or callback (#8312) -Fix: Only check houses for cargo when generating subsidies with towns (#8305) -Fix: Sprite preview in sprite aligner was too small with scaled UI (#8288) -Fix: Spell 'Viewport' consistently (#8260) -Fix #7772: Show vehicle destination on mouseover when vehicle stopped (#8236, #8543) -Fix #8232: Huge screenshot warning was shown incorrectly (#8224) -Fix #8153: Report incompatible cargo/order when autoreplace fails (#8169) -Fix: [Script] ScriptMarine::AreWaterTilesConnected did not work for aqueducts (#8074) -Fix #7645: Add cost of clearing the sloped tile to the price of a dock (#7947) -Fix #6452: Reset only editable and visible settings from GUI (#7890) -Fix: Original terrain generator did not keep a single gap of water at the borders (#7883) -Remove: In-game console command "content select all" (#8363) -Remove: [OSX] Support for OSX older than 10.7, including QuickTime music driver (#8078) - - -1.10.3 (2020-08-09) ------------------------------------------------------------------------- -Change: Also make roadside trees match the tree transparency option (#8245) -Fix: Center text and icons in the status bar vertically (#8273) -Fix: [NRT] Set invalid road and tram types for rail tunnel ends (#8269) -Fix #7980: Properly invalidate mouse-over station coverage highlight (#8263) -Fix #8250: [NRT] Company infrastructure window always omits last road/tramtype (#8251) -Fix #8162: [NRT] Improve error message when converting town owned road (#8247) -Fix #8216: Don't show floating text on autoreplace if cost is 0 (#8244) -Fix #8129: Crash if a news message expires while viewing the endgame screen (#8243) -Fix #8221: Use more specific error message when a bridge is too long (#8240) -Fix #8230: Resolve ".." when opening files in .tar (#8231) -Fix: A few race conditions in netcode (#8227, #8228, #8229) -Fix #7838: Crash relating to group creation and renaming (#8223) -Fix #8104: [SDL2] Fix window resizability when going from fullscreen to windowed mode (#8211) -Fix: Display banlist's indexes correctly (#8209) -Fix: Possible desync with subsidy creation (#8159) -Fix #8131: Draw small bridges pillars in more places (#8149) - - -1.10.2 (2020-06-01) ------------------------------------------------------------------------- -Add: Ubuntu 20.04 packages (#8127) -Fix: [OSX] Possible crash on failure to set colourspace (#8181) -Fix #8166: Prevent crash from a NewGRF with an invalid RoadType (#8180) -Fix #8024: Make Online Content GUI more responsive while loading (#8179) -Fix #7970: Disable event loop when generating crash dump (#8176) -Fix: [Build] Compatibility with modern Visual Studio (#8170) -Fix: Trees would disappear completely after a few years if tree spread was disabled (#8160) -Fix #8155: Roadtype speed limit in scenario editor toolbar dropdown was doubled (#8156) -Fix: Desync after house replacement (#8151) -Fix #8137: New clients can't join (desync) after funding an industry (#8140) -Fix #8132: Corrupted savegame with station with multiple owners caused a crash (#8134, #8142) -Fix: Stop gamelog when recovering from a savegame load error (#8133) -Fix: Exceptionally unlikely issue when reading MIDI files (#8125) -Fix #8119: Docking areas were not properly updated when clearing, causing desyncs (#8124, #8130) -Fix #8117: Memory leak for incoming admin port packets (#8122) -Fix: Non-roadbridges potentially had roadtype information set (#8111) -Fix #8108: Possible crash on loading TTD savegames with phantom oil rigs (#8109, #8110) -Fix #8093: Build & refit test run changed game state and could cause desyncs (#8103) -Fix: [Script] AreWaterTilesConnected did not handle aqueducts properly (#8074) - - -1.10.1 (2020-04-13) ------------------------------------------------------------------------- -Fix #8081: Crash when placing a ship depot next to a dock (#8082) -Fix: [GS] A Goal's QuestionID was getting truncated (#8072) -Fix #8064: Refit capacity could be displayed incorrectly in extreme edgecases (#8065) -Fix #8060: Restore admin network API compatibility (#8061) -Fix #8055: Crash when roadtype availability changes with the road construction toolbar open (#8058) - - -1.10.0 (2020-04-01) ------------------------------------------------------------------------- -Change: Open company window when clicking on a company goal (#8033) -Change: [SDL2] Support pasting from clipboard on Linux (#8004) -Fix: [Script] Random deviation upper bound range should be inclusive (#8052) -Fix #8043: Incorrect handling of global road/tram hotkeys caused a crash (#8044) -Fix #8039: [Script] SetOrderFlags and GetOrderDestination didn't work for oil rigs (#8040) -Fix: [Script] CanBuildConnectedRoadPartsHere neighbours tiles were incorrect if you started a new game with a different world size (#8036) -Fix: Ignore clicks on non-applicable global goals (#8035) -Fix #7613: Limit News Window to 1024 messages to keep it usable and avoid overflowing scrollbars (#8026) -Fix #7644: [OSX] Hopefully improve performance by manually set colorspace to sRGB (#8023) -Fix #8020: Add missing docking tiles around industry neutral stations (#8021) -Fix: GUI tramway icon only contained a single set of tram tracks (#8015) -Fix: Station with multiple docks had the wrong tile area (#8014) -Fix #8011: Crash when loading TTD scenario containing a dock (#8012) -Fix #7998: Crash when scripts tried to access companies with invalid IDs (#8010) -Fix: Crash when attempting to draw a string containing nonprintable characters (#8005) -Fix #6399: Directory ~/.local/share not created if it didn't already exist (#8003) -Fix #7958: Custom catenary missing on road bridges (#7991) -Fix #7944: Demolishing locks built on rivers didn't always restore the river (#7946) - - -1.10.0-RC1 (2020-02-09) ------------------------------------------------------------------------- -Feature: Allow server to supply a reason to kicked/banned clients (#7859) -Feature: [NewGRF] Station variable 6A, querying GRFID of nearby station tiles (#7956) -Feature: Improved logic of sharing industry production between 3 or more stations (#7922) -Feature: Highlight the item under mouse cursor in file browser (#7900) -Feature: [GS] Methods to change town rating of companies (#7898) -Feature: [NewGRF] Callback profiling command (#7868) -Feature: Add a setting to show the name of the NewGRF of a vehicle in the build window (#7852) -Feature: Ability to filter industry directory window by cargo (#7843) -Feature: Minimap screenshot type (#7817) -Feature: [GS] Methods to control engine availability of a specific company (#7791) -Feature: Configurable game ending year (#7747) -Feature: Separate window for taking screenshots (#7550) -Change: Move autorenew setting to basic category (#7984) -Change: Improved algorithm for transfer feeder payments (#7935) -Change: Show volume sliders with wedges instead of boxy slider (#7902) -Change: Auto-restart loads the original resources (e.g. save or scenario) again (#7688) -Change: Improve readability of integer lists saved to config files (#7396) -Fix #7976: Crash when attempting to kick the host via rcon (#7985) -Fix #7592: Road vehicles no longer balanced between multiple road stop stations (#7979) -Fix: Station rating effects affecting too large area (#7977) -Fix #7974: Crash when Ctrl+click to show a collapsed vehicle group (#7975) -Fix #7969: Crash when executing a recursive console alias (#7973) -Fix #6566: Very long loading of the maximum "zoom out" level at high resolutions (#7968) -Fix #7952: Crash when switching input languages (#7953) -Fix: [OSX] Don't show a crash/assertion message box for a GUI-less video driver (#7934) -Fix #7925: Corrupt savegames could lead to corruption of the titlegame (#7932) -Fix: [Fluidsynth] Music notes from previous song were not properly reset (#7930) -Fix: Invalid string usage within music window (#7928) -Fix: Non-deterministic name sorting in industry directory window (#7915) -Fix #7899: Various issues with town list window sorting (#7906, #7916) -Fix #7587: Fix possible crashes when loading old save games with invalid waypoint positions (#7894) -Fix: Avoid a crash by properly resetting timetable duration when loading old savegames (#7894) -Fix: Possible crash when post road-works cleanup removes all road pieces (#7903) -Fix #7891: Fix crash when loading save from 1.7.2 (#7892) -Fix #7887: Missing sound effects for some main toolbar buttons (#7888) -Fix #6667: Avoid confusion by also recalculating bridge costs for 'spectated' AI companies (#7884) -Fix: Allow old NewGRF industries to hide in/out cargo slots (#7882) -Fix: [Windows] Fix bootstrap GUI with Uniscribe but no Freetype (#7878) -Fix: Missing keycodes for hotkeys.cfg (#7850) -Fix #7625: Ensure road infrastructure cost is correctly updated when upgrading your own roads (#7628) -Fix #7088: Avoid crash by closing AI/GS textfile windows when changing their in-game slot (#7094) - - -1.10.0-beta2 (2019-12-25) ------------------------------------------------------------------------- +## 15.x + +### 15.0-beta1 (2024-12-24) + +- Feature: Town, industry and vehicle window zoom with mouse wheel (#12810, #12809, #12797) +- Feature: Show count of towns and industries in their directory window captions (#12800) +- Feature: Toggle for showing industry names in small map (#12770) +- Feature: Manually place town buildings in scenario editor (#12661, #12741) +- Feature: Filter new picker window by all classes, by used types, or by favourites (#12595) +- Feature: New picker window for rail and road stations, rail and road waypoints, and objects (#12595) +- Feature: Road waypoints (#12572) +- Feature: Allow base sounds set to be changed mid-game (#12399) +- Feature: Authorize specific clients to join network, company and remote console without password (#12337, #12326, #12329) +- Feature: Authenticate to the server without sending the password (#12323, #11878) +- Feature: Encrypt the connection between game server and client, and admin (#12300, #11878) +- Feature: Industry production/transported graph (#10541, #13083) +- Feature: Import town data from JSON file (#10409) +- Add: Implement 2D map scrolling under SDL2 (#13169) +- Add: [NewGRF] Callback for custom refit mask for engines/vehicles (#13090) +- Add: [NewGRF] Vehicle prop that allows refittability based on cargo class intersection (#13090) +- Add: Ability to show and filter different datasets on the same graph (#13083) +- Add: Show and toggle follow vehicle state in vehicle view window (#13076) +- Add: Set application name for SDL2 (#13061) +- Add: Support sound effects in Ogg Opus format (#13055) +- Add: Improving town-owned bridges increases company rating (#13036) +- Add: Apply rail/road type conversion when NewGRFs are updated mid-game (#13021) +- Add: Implement missing road type label conversion (#13021) +- Add: [NewGRF] Cargo class bits 13 and 14 now define potable and non-potable (#12979) +- Add: Overlay cargo icon in vehicle/depot list when holding shift+ctrl (#12938) +- Add: [Script] Include number of victims in ScriptEventVehicleCrashed (#12861) +- Add: [Console] Schedule command to execute a script file next in-game month (#12761) +- Add: Setting to disable warning for old vehicles (#12714) +- Add: Use macOS Game Mode identification (#12700) +- Add: Shade button to waypoint picker (#12682) +- Add: [NewGRF] Extended custom waypoint classes (#12653) +- Add: 'Get Content' buttons next to base set dropdowns in Game Options (#12627) +- Add: [NewGRF] Custom road waypoint support (#12572) +- Add: [NewGRF] Allow fixed layout up to 256 tiles per NewGRF rail station (#12554) +- Add: [NewGRF] Station property 1E, extended station tile flags (#12554) +- Add: [SDL2] Driver parameter 'no_mouse_capture' to ease interactive debugging (#12336) +- Add: Basic autocompletion on tab for console commands (#12163) +- Add: Portuguese Escudo currency (#12108) +- Change: Hide buttons in Found Town window that can't be used (#13182) +- Change: Include new cargo classes in dump cargo types console command (#13174) +- Change: Use floats for tracking 2D scrolling to improve smoothness (#13169) +- Change: Improve news window layouts (#13136, #13134) +- Change: Show company finances column if it has any values in it (#13112) +- Change: Treat recolour sprites as regular sprites in the SpriteCache (#13107) +- Change: Determine industry directory width only on visible rows (#13097) +- Change: Add sound memory usage to framerate window (#13055) +- Change: Invalidate build toolbars when NewGRFs are changed (#13046) +- Change: Don't try to flood buoys or dock tiles (#13013) +- Change: Store water tile flooding state in the map (#13013) +- Change: Exclude parent item from directory sort in FiosGetFileList (#12947) +- Change: [NewGRF] Place all 8 bits of station tile layout in var 40/41 (#12890) +- Change: [NewGRF] Install translation tables into overridden NewGRF (#12879) +- Change: [UI] Use scaled sprite sizes and correct matrix padding for content list (#12875) +- Change: [UI] Improved Network server list icon sizing and text positioning (#12874) +- Change: Path signals now show green on junction-less tracks by default (#12857) +- Change: Don't use house construction states in Scenario Editor (#12822) +- Change: Changing zoom no longer stops following vehicle (#12808) +- Change: Allow bribing local authority when other company has exclusive rights (#12763) +- Change: Position caret on left or right of glyph depending on language direction (#12760) +- Change: Do not automatically connect double depots with track (#12752) +- Change: Call custom house name callback in House Picker (#12741) +- Change: [NewGRF] Treat house max year 0xFFFF as MAX_YEAR (#12740) +- Change: [UI] Remove substitution of missing glyph with '?' glyph (#12736) +- Change: [UI] Use SetMinimalTextLines to set label height correctly (#12734) +- Change: Don't use house construction states in Scenario Editor (#12730) +- Change: Ignore min/max years for manual house placer (#12687) +- Change: Use default NewGRF cargo translation table if no custom table is provided (#12646) +- Change: Don't include midi file processing in dedicated server build (#12632) +- Change: [Linkgraph] Improve distance scaling algorithm in demand scaler (#12542) +- Change: Draw group hierarchy tree lines (#12522) +- Change: Display more useful information in sprite aligner than sprite ID (#12439) +- Change: Disallow using Action A to load sprites above the baseset unless reserved (#12435) +- Change: Use aspect ratios for some common widgets and buttons (#12386, #12636) +- Change: Show correct default value and unit for vehicle service interval setting (#12376) +- Change: Use per-company group numbers (#12297) +- Change: [NewGRF] Increase house type limit from 512 to 4096 (#12288) +- Change: Add dividers in vehicle group action dropdown (#12284) +- Change: Use same audio buffer size (and config parameter) for all sound drivers (#12227) +- Change: Allow rail and road depot overbuilding in current orientation in order to connect to rail or road (#12219) +- Change: Show tile index as decimal number in land info window (#12104) +- Change: Increase object/station/roadstop class limit (#12094) +- Change: Disable the insecure admin login by default; replaced by allow list (#11878) +- Fix: [NewGRF] New engines did not have a default cargo type set (#13146) +- Fix: String parameter not set when determining width of smallmap contour labels (#13145) +- Fix #13022: Ensure minimum size of scrollbar slider (#13119) +- Fix #13110: [Script] Convert table keys to string when generating JSON (#13113) +- Fix: Error message not set when unable load an old save (#13106) +- Fix: Network clients incorrectly truncated all strings to NETWORK_COMPANY_NAME_LENGTH (#13102) +- Fix: Don't allow right-click to close world generation progress window (#13084) +- Fix: VF_LOADING_FINISHED and VF_STOP_LOADING flags tested on wrong vehicle part (#13063) +- Fix: X-axis direction of industry production graph (#13062) +- Fix #13053: Payment transfers incorrect for non-passenger cargos (#13054) +- Fix: Invalid data used for height map curves after first run (#13039) +- Fix: SkipGarbage() skipped all multi-byte utf-8 characters (#13032) +- Fix: Don't invalidate water regions on the other side of the map (#13012) +- Fix: ScriptTile::PlantTreeRectangle does not check that the end tile is in bounds (#13004) +- Fix #12973: Don't exclude high score after using sandbox (#12999) +- Fix #12976: Incorrect widget rect scrolling for RTL languages (#12978) +- Fix: Crash when exiting game with end game window open (#12962) +- Fix #12957: 1-in-X proportion of towns being cities is unnecessarily random (#12960) +- Fix: ICUParagraphLayout line wrapping (#12956) +- Fix: Highscore/Endgame window can disappear offscreen if game window is resized (#12951) +- Fix #12940: Use specific error when overbuilding station on signals (#12943) +- Fix #12917: Write to negative array index for some string patterns (#12932) +- Fix: Incorrect truncation in string setting validation (#12924) +- Fix #12914: Fix use of invalidated pointer in viewport drawer (#12918) +- Fix: Train curve detection did not take shortened parts into account (#12910) +- Fix: DepotWindow::GetVehicleFromDepotWndPt not refreshing vehicle list (#12907) +- Fix: DupSprite did not copy the control_flags field (#12906) +- Fix: Excessively fast ships on aqueducts fail to move at correct speed (#12895) +- Fix: Timetable does not show vehicles as early (#12873) +- Fix #12856: Updating network settings does not invalidate data (#12858) +- Fix #12839: Truncated help text dialog on Windows (#12844) +- Fix #12832: Sanitise strings from NewGRF before logging them in Debug output (#12835) +- Fix #12825: Crash when opening road toolbar in scenario editor (#12826) +- Fix #12818: During Hostile Takeover, profit was calculated incorrectly (#12819) +- Fix: Apply widget's internal padding to scrollbar capacity/position (#12801) +- Fix #12365: Company Window now displays proper inauguration year and period while in wallclock mode (#12798) +- Fix #12787: Boostrap doesn't have main vindow (#12788) +- Fix #10239: Use elevated snow height for roads on flat foundations (#12776) +- Fix #12764: Crash when opening Detailed performance rating window with no companies (#12765) +- Fix: Water infrastructure total when changing owner of object on water or using DC_FORCE_CLEAR_TILE (#12757, #12729) +- Fix: Editbox behaved improperly with RTL languages (#12746) +- Fix #12283: Vehicle group dropdown shouldn't have default action (#12727) +- Fix: Reverse left/right keypress when editing RTL text (#12711, #12710) +- Fix: Crash if interface scale or font size changes with chat box present (#12705) +- Fix: Make progress bars obey language direction (#12704) +- Fix #12681: Abstract filetype not set for network client join savegames (#12701) +- Fix: Implement support for secure state coding on macOS (#12698) +- Fix: Add entitlements needed for plugins on macOS (#12697) +- Fix: Allow resolving house parent scope (town) of unbuilt houses (#12695) +- Fix #12685: Nullptr dereference when checking for equal loaded/loading groups (#12686) +- Fix: Unable to choose a font containing hyphen (#12684) +- Fix: [Timetable] Use days as precision in day mode for accurate timetable syncing (#12683) +- Fix #12550: Files were not saved in the right location when binary and configuration are in the same folder (#12679) +- Fix #12591: Give descriptive error when station construction fails due to wrong layout (#12678) +- Fix #12594: Give descriptive error when company takeover fails due to vehicle limit (#12676) +- Fix: Ensure revision mismatch also considers random debugging status (#12673) +- Fix #12411: [Admin] Send Network Welcome Packet to admin port after game creation completed WelcomeAll moved into NetworkOnGameStart (#12672) +- Fix: [MacOS] Memory leak in CoreTextFontCache (#12662) +- Fix: Use widget resize step instead of window resize step (#12659) +- Fix #12563: Race condition setting finish flag in WinHTTP (#12658) +- Fix #12648: Ensure all uses of std::filesystem::path use native encoding (#12650) +- Fix #12643: _is_water_region_valid is not cleared/reset in AllocateWaterRegions (#12649) +- Fix: Console command scrolling did not take account of padding and cursor width (#12642) +- Fix: Allow changing size of default OpenTTD font (#12641) +- Fix: Always allow setting company settings, company/president name/face (#12635) +- Fix: For GUI network servers, name the first company the same as any other company (#12629) +- Fix: Properly test for presence of waypoint in NewGRF resolver (#12579) +- Fix: Inconsistent space between console history and current line (#12528) +- Fix: Build industry window did not take width of count into account (#12476) +- Fix: Draw continuation lines for engine variant hierarchy tree (#12434) +- Fix: Viewport signs assume small font is smaller than normal font (#12422) +- Fix #11345: Use correct default button value for vehicle service interval setting (#12376) +- Fix #7982: Show existing coverage with unambiguous adjacent station (#12346) +- Fix: Off-by-one in EnsureVisibleCaption (#12261) +- Fix #11055: Make saveload failure error messages consistent with others (#12247) +- Fix: Train running sounds should only be produced for engine parts (#12229) +- Fix: Maximum permitted sound volume from NewGRF is 128 (#12222) +- Fix: Relocate main toolbar and statusbar before other windows (#12218) +- Fix: YAPF doesn't check destination for start nodes (#12217) +- Fix: Make link graph node borders scale with GUI (#12167) +- Fix #10490: Allow ships to exit depots if another is not moving at the exit point (#12161) +- Fix: Incorrect padding for text purchase list (#12160) +- Fix: GetNextDecisionNode ignored go to nearest depot when combined with stop at depot (#12130) +- Fix: Aircraft could route to hangars outside their range when stopping at them (#12130) +- Remove: Hidden setting for adjacent stations (#12862) +- Remove: Custom opendir implementation for Windows no longer needed (#12524) +- Remove: Company passwords; replaced by client allow lists (#12337) +- Remove: Autoclean_unprotected settings; all companies will be protected (#12337) +- Remove: NPF and pathfinder change settings (#12209) + + +## 14.x + +### 14.1 (2024-05-03) + +- Add: Check that towns can build roads before generating map (#12503) +- Fix #12228: Ships could get lost due to pathfinder not considering reversing in some cases (#12474) +- Fix #12433: Width of unit number display was too narrow (#12534) +- Fix #12502: Desync related to vehicle replacement (#12512) +- Fix #12506: Desync after new oil rig is constructed (#12511) +- Fix #12584: Crash on some tar files during tar scan (#12586) +- Fix: [SDL2] Keypad numbers did not function (#12596) +- Fix: Houses and industry tiles could accept incorrect cargo (#12547) +- Fix: Map generation stage strings were incorrect (#12549) +- Fix: [Script] Allow only 255 league tables, as 255 itself is the invalid id sentinel (#12545) +- Fix: Mark vehicle status bars dirty when a vehicle leaves unbunching depot (#12516) +- Fix: Do not show train waiting for unbunching as waiting for free path (#12515) +- Fix: Smooth outliers in unbunching round trip calculations (#12513) + + +### 14.0 (2024-04-13) + +- Update: New title game for 14.0 +- Fix #12477: Crash when launching OpenTTD from within a Dropbox folder (#12478) +- Fix #12233: Mini order list overlaps vehicle group name (#12423) +- Fix #12114: Viewport coords of crashed aircraft not updated when falling (#12424) +- Fix #12395: Ensure president name widget is tall enough (#12419) +- Fix #12415: Incorrect payment for aircraft secondary cargo (#12416) +- Fix #12387: [NewGRF] Wrong tile offset passed to rail station CB 149 (slope check) +- Fix #12388: Autoreplacing train heads slowly made the unit number grow (#12389) +- Fix #12368: Incorrect offset for click position within industry chain window (#12370) +- Fix: Aircraft can float above the ground when crashed (#12425) +- Fix: Segfault when using -q without providing a . character (#12418) +- Fix: Wrong scrolling dropdown list position with RTL (#12412) +- Fix: [Win32] Force font mapper to only use TrueType fonts (#12406) +- Fix: "-q" displays NewGRF IDs in the wrong byte-order (#12397) +- Fix: Do not send chat to clients that have not authorized yet (#12377) +- Fix: [NewGRF] Label for fruit incorrectly changed to `FRUI` from `FRUT` (#12367) +- Fix: [Script] ScriptSubsidy::GetExpireDate should return an economy-date (#12372) +- Revert #11603: [Script] AI/GSTimeMode was not the best solution for economy/calendar support (#12362) + + +### 14.0-RC3 (2024-03-23) + +- Fix #12347: Crash attempting to find catchment tiles of a station with no catchment area (#12348) +- Fix #12319: Some SSE blitters were broken due to ODR violations (#12322) +- Fix #12302: Allow empty train engines to use an invalid cargo type (#12325) +- Fix #12305: Crash with large positive sprite x offset in engine preview window (#12313) +- Fix #12166: Crash when opening tram road stop build window (#12168) +- Fix #12092: Incorrect x-axis in cargo payment graph window (#12359) +- Fix: Crash when attempting to join a company while not joined (#12353) +- Change: Show unbunching action in timetable window (#12351) +- Change: [Windows] Switch to Microsoft Azure code signing certificate (#12292) + + +### 14.0-RC2 (2024-03-16) + +- Update: Bump bundled OpenTTD TTF fonts to version v0.6 (#12276) +- Update: Developer credits (#12173, #12235) +- Change: Use (at least) standard toolbar button size for signal selection buttons (#12265) +- Change: [Script] Match FormatString behaviour more closely (#12205) +- Fix #12236: Ship pathfinder causes crash when ship is already at destination (#12238) +- Fix #12225: [Script] Missing AI::ResetConfig support for running AI config (#12226) +- Fix #12203: When unbunching at a depot, don't overlook implicit orders (#12220) +- Fix #12196: Always show selected content, even when filtering and disable "select upgrade" button when filtering (#12201) +- Fix #12195: Reset cursor when no Object is selected (#12207) +- Fix #12176: Ships are circling in one place (#12181) +- Fix #12154: Incorrect calendar day lengths with minutes per year setting (#12158) +- Fix #12148: Do not draw decimals when number of digits is 0 (#12150) +- Fix #12147: Reset all saved settings to their default before loading a game (#12210) +- Fix #12145: Incorrect date handling in date cheat in wallclock time-keeping mode (#12146) +- Fix #12134: Use correct error messages if clearing drive-through road stops fails (#12139) +- Fix #12133: [Script] Don't crash when emergency saving (#12138) +- Fix #12127: Truncation ellipses rendered shadows even for black font without shadows (#12132) +- Fix #12119: Remove red warning text when maximum loan is zero (#12141) +- Fix #12118: When adding an unbunching order, properly check for unsafe conditions (#12136) +- Fix #12076: Do not allow 'join' command on dedicated servers (#12208) +- Fix #12010: Use economy timer for vehicle stats minimum age, not calendar (#12142) +- Fix: Improved ship movement when no path to destination is found (#12285, #12286) +- Fix: Initialize _switch_mode_time so crash-logs before first game have a realistic time (#12184) +- Fix: [Script] Only show debug script window at the end of savegame loading (#12135) +- Fix: [Script] Broken ScriptText circular reference detection (#12187) +- Fix: Ordering of command per tick limit and pause mode filtering (#12126) +- Fix: Only reset unbunching departure data in the correct depot (#12155) +- Fix: Off by one in TimerGameEconomy::ConvertDateToYMD in wallclock mode (#12143) +- Fix: Missing savegame conversion for current_order (#12188) +- Fix: Helptext for timekeeping unit setting erroneously refers to vehicle movement (#12172) +- Fix: Don't show "insert order" errors in the console (#12245) +- Fix: Don't defer OnResize() after ReInit() (#12174) +- Remove: [Script] random_deviation from setting description table (#12221) +- Revert #11993: New number format system does not and cannot work for CJK languages (#12157) +- Revert #11606: Don't auto-build past tunnelbridge ends (#12244) + + +### 14.0-RC1 (2024-02-18) + +- Feature: Fully user configurable number format and abbreviations (#11993) +- Add: Show cargo icons on subsidy list window (#12079) +- Add: [Script] GetAirportNumHelipads (#12085) +- Change: Show 6 or 2 orientation buttons in NewGRF road stop picker as appropriate (#12090) +- Change: Show cargo icons on Industry View window (#12071) +- Change: Improve performance of finding free pool slots (#12055) +- Change: Draw north-side farm fences/hedges/walls on tile edge, instead of 1/16th in (#12048) +- Change: When adding orders, Ctrl+Click on a depot to unbunch, instead of service if required (#12023) +- Change: Store running AI config inside Company (#12003) +- Change: Show speed before destination in vehicle status bar (#11932) +- Change: Replace long list of cargo filter buttons with a multi-select dropdown list (#11552) +- Change: [Script] Use company randomizer when adding random deviation (#12065) +- Fix #12074: Don't allow "part" command for dedicated servers (#12075) +- Fix #12052: NewGRFs clearing industry cargo slots could fallback to default instead of empty (#12053) +- Fix #12050: Add default size, shade and pin control buttons to company livery widget (#12080) +- Fix #12041: Tarball extraction failing due to incorrect filename (#12044) +- Fix #12037: Blurry OpenTTD font on Mac OS (#12047) +- Fix #12029: Don't show Sandbox Options in multiplayer (#12032) +- Fix #12024: Autoreplace failed news message for trains must go to lead engine (#12025) +- Fix #12022: Adjust economy date when changing timekeeping units in Scenario Editor (#12042) +- Fix #12020: Unbunch and service if needed should be mutually exclusive depot order types (#12021) +- Fix #12019: Correctly highlight depot unbunch action in dropdown (#12021) +- Fix #12014: Remove water when area clearing ship depot (#12030) +- Fix #11840: Ship pathfinder always returns a valid trackdir if one is available (#12031) +- Fix #10983: [AdminPort] Correct order of messages (#11140) +- Fix #10405: [Script] Test engine and vehicle type validity for ScriptGroup::GetNumEngines (#11887) +- Fix #10079: Don't render at 1000fps if HW acceleration + vsync is requested but not active (#12067) +- Fix: Shadows of individual character glyphs could be drawn over other characters (#12115) +- Fix: Don't invalidate station list on vehicle load/unload (#12112) +- Fix: NewGRF roadstops were ignored if only in default class (#12089) +- Fix: Visually also disable vsync when not using HW acceleration (#12066) +- Fix: Industry tiles and houses could accept incorrect cargo types (#12062) +- Fix: Redraw orders when a station feature is added/removed (#12061) +- Fix: For content service, fallback to TCP downloads when HTTP stalls (#12056) +- Fix: Don't issue autoreplace failed news message for command test mode (#12026) +- Remove: Setting "no_http_content_downloads" (#12058) + + +### 14.0-beta3 (2024-02-06) + +- Add: [Script] ScriptTileList_StationCoverage to get station coverage area (#12015) +- Change: Update OpenTTD TTF fonts to v0.5 (#11994) +- Fix #12012: Crash when opening orders of another company (#12013) +- Fix #12001: Use correct valid cargo check for old-style NewGRF town house 3rd cargo set up (#12006) +- Fix #11997: Adjust economy date by 1920 when loading TTD/TTO savegames (#12007) +- Fix: Focus hotkey in road/tram stop building window (#12008) +- Fix: Signals were incorrectly shifted by 1 pixel when selected (#12005) +- Fix: Missing default vehicles and industry acceptance/production (#12000) +- Fix: [Script] Avoid overflow in scripts when infinite money is enabled (#12016) +- Fix: [Script] Don't kill GS misusing GSText (#12009) + + +### 14.0-beta2 (2024-02-04) + +- Change: [NewGRF] Improved support for redefining default cargo types (#11719) +- Fix #11982: Crash when trying to place signals on things other than plain rails (#11977) +- Fix #11975: Inconsistent behaviour when changing first AI company settings (#11976) +- Fix #11972: Year cut off in graph windows (#11974) +- Fix #11968: Crash when opening orders window of new vehicles (#11973) +- Fix #11966: Monospace text in windows may not have been fully scrollable (#11981) +- Fix #11802: Made determining water region edge traversability more robust (#11986) +- Fix: Second colour vehicle-type default liveries were not being updated (#11971) + + +### 14.0-beta1 (2024-02-03) + +- Feature: Order option to unbunch vehicles at depot (#11945) +- Feature: Infinite money mode (#11902) +- Feature: Setting to disable the loading speed penalty for trains longer than the station (#11682) +- Feature: Plugin framework for Social Integration with Steam, Discord, GOG, etc (#11628) +- Feature: Scalable OpenTTD TrueType font made by Zephyris (#11593) +- Feature: Toyland-specific river graphics (#11523) +- Feature: Add zoom level buttons to sprite aligner (#11518) +- Feature: Add shading to river slopes (#11491) +- Feature: Place cargo icon on cargo filter dropdowns (#11487) +- Feature: Mode to display timetable in seconds (#11435) +- Feature: Setting to influence how many minutes a calendar year takes (#11428) +- Feature: Base graphics can offer parameters for additional settings (#11347) +- Feature: Sandbox option to lock station ratings at 100% (#11346) +- Feature: Setting to use real-time "wallclock" as timekeeping units (#11341) +- Feature: Setting to automatically restart server based on hours played (#11142) +- Feature: Add config option to set default company secondary colour for new games (#11068) +- Feature: Transparency option for cost and income indicators (#11001) +- Feature: Create group of vehicles from manage vehicle list button (#10890) +- Feature: Show coverage highlight the same as stations when adding waypoints (#10875) +- Feature: Show the number of industries already built in the Fund New Industry window (#10806) +- Feature: Add search filter and name text to build waypoint window (#10786) +- Feature: Setting to disallow level crossings with competitors (#10755) +- Feature: Opt-in survey when leaving a game (#10719) +- Feature: Replace buying/selling company shares with hostile takeovers of AI companies (#10709, #10914) +- Feature: Settings to scale cargo production of towns and industries (#10606) +- Feature: Separate rail/road and sea/air velocity units, and add knots (#10594) +- Feature: Region-based pathfinder for ships (#10543) +- Feature: Filter engine build menu by name and NewGRF extra text (#10519) +- Feature: Industry directory text filter (#10518) +- Feature: Ctrl+Click to reset late counter for the entire vehicle group (#10464) +- Feature: Orientation of rail and road depots can be changed (#9642) +- Feature: Display help and manuals in-game (#7786) +- Feature: [NewGRF] Town production effect and multiplier (#11947) +- Feature: [NewGRF] Randomize direction of rail vehicle on build based on probability callback (#11489) +- Feature: [NewGRF] Related Act2 objects for airports and airport tiles (#11282) +- Feature: [NewGRF] Allow higher max speeds for ships (#10734) +- Feature: [NewGRF] Increase limit of objects/stations/roadstops per NewGRF (#10672) +- Feature: [NewGRF] Road stops (#10144) +- Feature: [Script] Goal destination can be updated (#10817) +- Add: Argument for console command "restart" to use either current or newgame settings (#11962, #11963) +- Add: {CURRENCY_SHORT} only did k / m suffix. Add bn / tn and make translatable (#11921) +- Add: Show in multiplayer the amount of hours a game has been unpaused (#11886) +- Add: Allow loading heightmaps from command-line (#11870) +- Add: List_[scenario|heightmap] and load_[scenario|height] console commands (#11867) +- Add: Latvian Lats currency (#11691) +- Add: Horizontal scroll for script debug log (#11597) +- Add: GUI options to select sprite font and AA mode for all fonts (#11593) +- Add: Website button for basesets in Game Options window, the Game Script settings window and AI settings window (#11512) +- Add: [Emscripten] Support for bootstrapping (#11109) +- Add: Hotkey to focus town / industry directory filter box (#11030) +- Add: Maximum number of companies allowed to the client list (#10523) +- Add: Use specific error message when vehicle cannot go to station/waypoint (#10494) +- Add: Show NewGRF name in NewGRF-created errors (#10457) +- Add: Alternative setting for right-click close window option to exclude pinned windows (#10204) +- Add: Allow autoreplace with same model vehicle (#7729) +- Add: [NewGRF] Allow inspection of road tiles and airports (#11282, #11323) +- Add: [NewGRF] Station variable 6B to get extended station id of nearby tiles (#10953) +- Add: [NewGRF] String code "9A 21" to display force from textstack (#10782) +- Add: [NewGRF] Station property 1C/1D to set name/classname (#10672) +- Add: [Script] Optional filter parameter to ScriptXXXList constructors (#11698,#11663) +- Add: [Script] AI/GS Time Mode to choose between economy (default) and calendar time (#11603) +- Add: [Script] Allow to set max loan for each company separately (#11224) +- Add: [Script] GSIndustry.GetConstructionDate() method (#11145) +- Add: [Script] Game script control of industry production level and news messages (#11141) +- Add: [Script] GSAsyncMode to set async mode of gamescript commands (#10913) +- Add: [Script] GSCompanyMode::IsValid and IsDeity, and enforce valid company/deity mode where applicable (#10536, #10529) +- Add: [Script] Allow GS to found town with random road layout (#10442) +- Add: [Script] Create own Randomizer per instance (#10349) +- Change: Better handle different GUI sizes for most windows, and squash inconsistencies between windows +- Change: Allow configuring AI slots above the current maximum number of competitors (#11961) +- Change: Forcefully enable prefixing logs with date (#11930) +- Change: Position error window closer to cursor on large screens (#11923) +- Change: Only open story-book in center when a GS does it (#11916) +- Change: Rebrand Cheats as Sandbox Options (#11874) +- Change: Make smooth-scrolling based on actual time (#11865) +- Change: Set smooth-scrolling on by default (#11860) +- Change: Disable building rail infrastructure if train build limit is zero (#11847) +- Change: Invalidate music volume when restarting music playback on Windows (#11836) +- Change: Make street lights transparent with houses (#11828) +- Change: Redesign script debug window (#11782) +- Change: Reorganize Settings menu items (#11683) +- Change: Set amount of smoke/sparks to "realistic" by default (#11624) +- Change: Show a message in livery window if vehicle type has no groups (#11617) +- Change: Add distinct tooltips for vehicle group colour schemes (#11617) +- Change: Move colour selection dropdowns to bottom of window (#11617) +- Change: Support custom transparency remaps with 32bpp blitters (#11616) +- Change: Make "middle" the default stopping location for trains in platforms (#11605) +- Change: Scale sprites to requested highest resolution level (#11600) +- Change: Allow opening multiple script debug windows by holding Ctrl (#11592) +- Change: Don't show scoring year in high score table (#11546) +- Change: Revert pressed-button content shifting introduced in r2161 (#11542) +- Change: Show rating in station list even with no cargo waiting (#11540) +- Change: Hide unused cargos from vehicle cargo filter (#11533) +- Change: Don't restart playback when toggling playlist shuffle (#11504) +- Change: Increase finance window lines (and underlines) with interface scale (#11459) +- Change: Move baseset missing/corrupted files label to list item (#11455) +- Change: Add horizontal scrollbar to Industry Directory window (#11434) +- Change: Improve layout of airport, dock, object, road/tram stop, train station pickers (#11430) +- Change: Display cargo lists in sorted cargo order (#11383) +- Change: Link houses production on industry chain graph by TPE_PASSENGERS or TPE_MAIL cargo (#11378) +- Change: Passenger subsidies are generated for any TPE_PASSENGER cargo type (#11378) +- Change: Towns generate cargo based on town production effect (#11378) +- Change: Always allow expanding towns in Scenario Editor to build new roads (#11377) +- Change: Don't set vehicle on time if timetable not started (#11359) +- Change: Store station blocked/wires/pylons flags in map (#11337) +- Change: Recover when possible from crashes during a crash (#11238) +- Change: Store crash logs in JSON format (#11232) +- Change: Remove autosave from settings window; it is already in the Game Options (#11218) +- Change: Enable "Forbid 90 degree turns" setting by default (#11160) +- Change: Do not allow mixing road/tram types in powered road type list (#11148) +- Change: Only show platform stopping location in orders when other than default (#11102) +- Change: Autorail / autoroad tools can start dragging from invalid tiles (#11089) +- Change: Only allow buying Exclusive Transport Rights when no one has them (#11076) +- Change: Remove currency code/symbol suffix from language files (#11061) +- Change: Add separate setting for server sent commands per frame limit (#11023) +- Change: Cargo flow legend only shows defined cargo (#10872) +- Change: Use "Via-Destination-Source" as default station cargodist display (#10851) +- Change: Preserve orders and related settings where possible when moving engines around in a train (#10799) +- Change: Standardise unit conversions and allow decimal places (#10795) +- Change: Use separate names for default stations/roadstops (#10786) +- Change: [MacOS] Require at least 10.15 to run the game (#10745) +- Change: Hide all variants from UI when (display) parent is hidden (#10708) +- Change: Split Game options into General, Graphics and Sound tabs (#10674) +- Change: Extend entity override manager and station spec lists to support 16 bit IDs (#10672) +- Change: Base autosaves intervals on real time (instead of game time) (#10655) +- Change: Allow overbuilding station and waypoint tiles (#10618) +- Change: Use realtime for Linkgraph update settings (#10610) +- Change: Make tick length 27 milliseconds (#10607) +- Change: Increase max cargo age and let min cargo payment approach zero (#10596) +- Change: Show buy company dialog window even when playing in the AI company (#10459) +- Change: Use HTTPS for content-service connections (#10448) +- Change: Big UFO disaster targets current location of a random train (#10290) +- Change: Remove land generator setting from World Generation GUI (#10093) +- Change: Build signals to the next junction when dragging regardless of the Ctrl state (#9637) +- Change: Allow dedicated server to use threaded saves (#10787) +- Change: [NewGRF] Increase vehicle random data from 8 to 16 bits (#10701) +- Change: [NewGRF] Read Action 3 IDs as extended-bytes for all features (#10672) +- Change: [NewGRF] Make Action 3 debug messages more consistent (#10672) +- Change: [NewGRF] Extend callback 161 (engine name) with bit 0x22 for context 'Autoreplace - Vehicles in use' (#10666) +- Change: [Script] Replace easy/medium/hard values with default value (#11959) +- Change: [Script] Limit total script ops that can be consumed by a list valuate (#11670) +- Change: [Script] Allow GS access to ScriptGroup, ScriptGameSettings.IsDisabledVehicleType, more ScriptCompany and more ScriptOrder functions (#10642) +- Change: [Script] Improve ScriptText validation error messages (#10545) +- Change: [Script] Restore support of {RAW_STRING} in ScriptText (#10492) +- Change: [Script] Validate ScriptText parameters type and amount (#10492) +- Change: [Script] Automate the ScriptObject reference counting (#10492) +- Change: [Script] Extract params info from GS strings (#10492) +- Change: [Script] A ScriptText with too many parameters is now a fatal error (#10483) +- Change: [Script] Log AI/GS Squirrel crashes in white text for readability (#10375) +- Fix #11918: Houses should only build next to road stops, not any station type (#11919) +- Fix #11827: Make text layouter aware of ligatures (#11831) +- Fix #11752: Characters could be repeated when wrapping multi-line text (#11761) +- Fix #11748: Decreasing service interval value sufficiently would result in it wrapping around (#11749) +- Fix #11629: Crash when getting the nearest town for rotated airports (#11631) +- Fix #11516: Adjust window size by interface scale during ReInit (#11517) +- Fix #11515: Changing interface scale could have unintended effects on zoom level (#11615) +- Fix #11442: "Default" colour in group colour window is not updated when changing master colour (#11614) +- Fix #11437: Flipped shorter rail vehicles disappear in windows (#11446) +- Fix #11413: Incorrect sorting by industry production (#11414) +- Fix #11407: Don't steal focus from dropdown menus (#11484) +- Fix #11402: Make string filter locale-aware (#11426) +- Fix #11329: Don't assert vehicle list length is non-zero when only asked to set string parameter (#11330) +- Fix #11315: Sort industries and cargoes by name in industry chain window (#11317) +- Fix #11307: Incorrect GroupStatistics after selling leading wagon (#11311) +- Fix #11261: Airport menu selectability after closing window on a class with no available airports (#11344) +- Fix #11230: Sort by button in group list window could be misaligned (#11231) +- Fix #11215: Assert in NewGRF parameters window (manual parameter mode) (#11217) +- Fix #11203: [Linux] Crash when editing CJK characters in edit box (#11204) +- Fix #11180: Aircraft crashes could point to the wrong tile (#11184) +- Fix #11164: Don't create duplicate town names when using 'Many random towns' in the scenario editor (#11165) +- Fix #11162: Second company colour was not consistently applied to articulated vehicles (#11163) +- Fix #11115: Focus the abandon game/exit game windows (#11125) +- Fix #11096: Increase priority of error and confirmation windows (#11104) +- Fix #11087: Disable base graphics/sound dropdown outside main menu (#11091) +- Fix #11054: Prevent translation of currency codes (#11061) +- Fix #11026: Use real engine name instead of default name for filtering (#11033) +- Fix #10982: No help text for gamelog command (#10984) +- Fix #10880: Crash in object window due to incorrect parameter order (#10881) +- Fix #10868: Crash when Script tries to load large savegame data (#11029) +- Fix #10811: Allow dragging vehicle in depot to any free row (#11508) +- Fix #10660: Sprite Font scale affected by viewport zoom level limits (#10668) +- Fix #10619: Crash loading linkgraph for older savegames (#10620) +- Fix #10600: 'Replace Vehicles' didn't show numbers >999 (#10680) +- Fix #10578: Allow to select any version of AI/GS from GUI (#10604) +- Fix #10522: Link graph tooltip vertical lines were not handled correctly (#10524) +- Fix #10511: Don't search for depot every tick if one cannot be found (#11548) +- Fix #10478: Clarify airport noise control setting texts (#11169) +- Fix #10452: Prevent long stalls during river generation (#11544) +- Fix #10430: Display chain window causing assert (#10431) +- Fix #10343: Don't extend town-disallowed roadtypes (#10347) +- Fix #10251: [MacOS] Screen looks blue-ish when using newer SDKs (#11207) +- Fix #10222: Adjust line drawing algorithm (#10491) +- Fix #10131: Actually cancel downloads when pressing cancel (#10485) +- Fix #10118: Cycle through current signal group, not just path signals (#11798) +- Fix #10439: [Script] Validate story page button colour, flags, cursor and vehicle type (#11892) +- Fix #10438: [Script] Validate story page element type for ScriptStoryPage::NewElement (#11888) +- Fix #9865: Removing files with the console always failed +- Fix #9810: Rebuilding a through road stop costs money (#9852) +- Fix #9722: Crash when pressing hotkeys early in world generation (#11858) +- Fix #9697: Limit the default width of the Online Players window (#11936) +- Fix #9642: Keep infrastructure totals when overbuilding road depots (#11229) +- Fix #9545: Crash when all cargo types are disabled (#11432) +- Fix #8846: When upgrading NewGRF presets, copy NewGRF parameters only if the NewGRF are compatible (#11348) +- Fix #8253: Improve profit graph when having lots of money (#11915) +- Fix #6377: Two tarballs with the same folder in them were considered as one (#11855) +- Fix #5713: Ships could be sent to unreachable depots (#11768) +- Fix #4575: Use Latin 'l' in English translation of zloty (#11090) +- Fix #4415: Land info build date is also renovation date (#11759) +- Fix: Display rank correctly with more than 15 companies in a league table (#11940) +- Fix: Extra refit button when train/RV is in a depot (#11904) +- Fix: Update server listing as offline when unexpected disconnect during refresh (#11891) +- Fix: Horizontal scale of framerate window switched excessively (#11813) +- Fix: [Linux] Various issues with resolutions and fullscreen in multi-display setups (#11778, #11779) +- Fix: Build button text when train purchase window using "Engines" filter (#11755) +- Fix: One-way state remained after removing road from road and tram tile (#11745) +- Fix: Draw video driver info at the correct size and text wrap (#10716) +- Fix: Language genders could not be applied to SCC_INDUSTRY_NAME (#11697) +- Fix: Spurious cancellations of HTTP content downloads (#11668) +- Fix: Calculation of initial engine age was inaccurate (#11660) +- Fix: Prevent underflow if engine base life is less than 8 years (#11635) +- Fix: Changing default livery did not propagate to group liveries (#11633) +- Fix: Window width/height was doubly-scaled with automatic DPI switch (#11598) +- Fix: Don't crash when saving a crashlog save with no main window open (#11586) +- Fix: Prevent overflow when calculating max town noise (#11564) +- Fix: Deleting towns did not check for waypoints referencing the town (#11513) +- Fix: Invalidate playlist window when (un)shuffling playlist (#11504) +- Fix: Restore original cargo legend 'blob' dimensions (#11480) +- Fix: Extmidi did not move on to next song after playing ends (#11469) +- Fix: Server password length in the UI was unnecessarily limited (#11408) +- Fix: OpenTTD can fail to exit on an error due to mutex locks in threads (#11398) +- Fix: Scale minimum width for server name by interface scale (#11381) +- Fix: Server connection was not closed when relay window was closed (#11366) +- Fix: Upgrading NewGRF presets could result in incomplete display of NewGRF parameters until restart (#11348) +- Fix: Check for engine variant loops during NewGRF initialization (#11343) +- Fix: Don't allow industries to produce invalid cargo (#11314) +- Fix: Also apply cargo filters on shared groups in vehicle listing (#11294) +- Fix: Only count distance traveled in vehicles for cargo payment (#11283) +- Fix: Base cargo payment on load/unload tile, instead of station sign location (#11281) +- Fix: Crash when opening a damaged base-graphics (#11275) +- Fix: Trivial autoreplace of mixed cargo articulated engines (#11253) +- Fix: [Emscripten] Config not saved on exit (#11248) +- Fix: Inaccurate waiting cargo total in station window when using cargodist (#11213) +- Fix: No fast forward in network was ensured only from GUI side (#11206) +- Fix: Crash when not passing command-line parameter for -n (#11153) +- Fix: [Bootstrap] Don't crash when failing to connect to content server (#11122) +- Fix: Crash when failing to load a game into a dedicated server at startup (#11021) +- Fix: Don't allow changing settings over the network that are marked as local settings (#11009) +- Fix: Move no_http_content_downloads and use_relay_service to private settings (#10762) +- Fix: Extra viewport could not be scrolled with right-click-close (#10644) +- Fix: Specify units for value of share trading age setting (#10612) +- Fix: Road type is not available before its introduction date (#10585) +- Fix: Do not update a RV's Z-position when stationary while turning (#10570) +- Fix: Don't (briefly) switch from title-only playlist on menu screen (#10553) +- Fix: Reset content download progress to zero if falling back to TCP (#10485) +- Fix: Make script goals work with the whole range of ClientIDs (#10435) +- Fix: [NewGRF] Tile slope missing from road stops varact2 variable 0x42 (#11373) +- Fix: [NewGRF] House class mappings were not reset between games (#11279) +- Fix: [NewGRF] Profile didn't stop if there were no events yet (#10816) +- Fix: [NewGRF] Support more than 256 stations/waypoints/roadstops per class (#10793) +- Fix: [NewGRF] Var68 for station and roadstop was broken (#10784) +- Fix: [NewGRF] Object and road stop ignore property handlers (#10525) +- Fix: [Script] Apply random deviation to settings only at script start (#11944) +- Fix: [Script] Improve ScriptText validation (#11721) +- Fix: [Script] GSAdmin.Send() could generate invalid JSON (#11250) +- Fix: [Script] Crash if squirrel compatibility scripts cannot be parsed (#11589) +- Fix: [Script] Don't list unavailable road types for game scripts (#10585) +- Fix: [Script] Game scripts were able to build with non-existing road types (#10539) +- Fix: [Script] Inconsistent precondition failure return values (#10533) +- Fix: [Script] Crash when companies disappear (#10529) +- Fix: [Script] ScriptBase::Rand() return value could return negative values (#10443) +- Fix: [Script] Incorrect value for GOAL_INVALID (#10436) +- Fix: [Script] Extend Script::IsValidVehicle to check for primary vehicles (#10386) +- Remove: "generation_seed" from config, as it was a write-only value (#11927) +- Remove: Debug redirect over network (#11776) +- Remove: Officially mark Vista as no longer supported (#11531) +- Remove: OS/2 and SunOS ports (#11018, #11210) +- Remove: Obsolete NewGRF text unprinting (#10884) +- Remove: [Script] CONFIG_RANDOM from AddSetting flags (#11942) + +## 13.x + +### 13.4 (2023-07-29) + +- Fix: Setting tree lines drawn incorrectly for RTL languages (#11070) +- Fix #11043: Don't choose toolbar dropdown option if focus is lost (#11044) +- Fix #10917: Pay loan interest before generating statistics (#11040) +- Fix #11016: Use after free in network invalid packet error path (#11022) +- Fix #10987: Double-close of dropdown stopped land-info tool working as default (#11000) + + +### 13.3 (2023-06-11) + +- Fix: [Win32] use full monitor resolution for fullscreen (#10985) + + +### 13.2 (2023-06-10) + +- Change: [Win32] position window in center of workspace of primary display (#10942) +- Change: Automatically disable hardware acceleration when GPU driver crashed the game last attempt (#10928) +- Change: [Linux] Default scroll mode to non-mouse-lock (#10920) +- Change: Include font style in font name for Freetype (#10879) +- Fix: Don't restore backed up vehicle name if it's no longer unique (#10979) +- Fix #10975: Train name wrongly marked as unique when joining trains (#10976) +- Fix: Crash when not even a single row fits for dropdowns on low resolution screens (#10934) +- Fix: Crash with tooltip on low resolution screens (#10933) +- Fix: Crash when window can't be placed on low resolution screens (#10932) +- Fix #10502: Apply engine refit before attaching free wagons (#10926) +- Fix: Wayland crash on startup due to Pango also using FontConfig (#10916) +- Fix: When syncing width of GUI items, take padding into account (#10915) +- Fix: Make dropdowns self-close when losing focus (#10912) +- Fix: Land info window maximum width was not scaled (#10894) +- Fix: Check max member count in squirrel classes (#10883) +- Fix: Ask FontConfig for the face index when opening fonts (#10878) +- Fix #10831: Level crossing parts left barred after crossing tile removal (#10874) +- Fix: Rail waypoint selection window not closed when parent windows closed (#10873) +- Fix #10846: [Script] Crash on trying to allocate an excessively large array (#10848) +- Fix: [Win32] Text line breaking did not properly handle punctuation characters (#10775) +- Fix: [Emscripten] Crash when saving games (#10758) +- Fix: [Win32] Wrong multi-line text layout due to incorrect whitespace handling (#10752) +- Fix #10741: Rail platforms left partially reserved after train crash (#10751) +- Fix: Shaded engines in purchase list incorrectly shaded (#10736) +- Fix #10735: [NewGRF] {POP_COLOUR} fails if string is drawn with extra flags (#10736) +- Fix #8177: Ships with max speed overflow to near-zero speed (#10695) +- Fix #10289: Don't silently fail when setting timetable start dates (#10690) +- Fix #8302: Improve "Maintenance intervals are in percents" helptext (#10686) +- Fix #10665: "No vehicles are available yet" message did not appear correctly on non-temperate climates (#10673) +- Fix #10630: Don't allow shifting service date earlier than year 0 (#10643) +- Fix #10637, #10638: Incorrect water infrastructure totals when building certain object types (#10639, #10640) +- Fix: Abort loading savegame if road vehicle is on invalid road type (#10622) + + +### 13.1 (2023-04-10) + +- Add: [NewGRF] Engine name callback for nested variants. (#10399) +- Fix: Improve main toolbar tooltips (#10616) +- Fix: [NewGRF] Additional validation for Action3 (+others) (#10601) +- Fix: Clear button for editbox didn't take account of padding (#10583) +- Fix: [Script] Access to enum/consts defined outside of main.nut (#10573) +- Fix #10568: Bogus warning when loading a save with a NewGRFs on dedicated servers (#10572) +- Fix #10554: Crash when scrolling in the autoreplace window with collapsed variants (#10555) +- Fix: Network server highlight invisible with RTL languages. (#10551) +- Fix: Client name was not being used as company manager name (#10535) +- Fix: Prevent road vehicles on crossing from crashing into the side of a train (#10496) +- Fix #10477: [macOS] Calculation for window sizes when using custom fonts was being rounded incorrectly (#10489) +- Fix #10486: Crash in debug window when GS started before AIs (#10487) +- Fix #10469: [Script] Negative numbers in League Table window were sorted incorrectly (#10471) +- Fix #10465: Crash on timeout if user never enters a password for server (#10466) +- Fix #10280, #10461: Crash on opening town windows as a spectator (#10462) +- Fix #10059: Script config values stored in the config file could cause crashes (#10444) + + +### 13.0 (2023-02-05) + +- Change #10077: Make maximum loan a positive multiple of the loan interval (#10355) +- Fix #10361: [Script] Don't try to give saved data to a dead script (#10433) +- Fix #10419: Water infrastructure accounting when building ship depots and docks (#10432) + + +### 13.0-RC2 (2023-01-28) + +- Feature: Press Ctrl to build a diagonal area of trees (#10342) +- Feature: Set a custom number of industries in map generation window (#10340) +- Change: Display font status as aa/noaa instead of true/false (#10352) +- Fix: [Script] Improved API documentation for scripts (#10413, #10412) +- Fix #10255: Reduce basic thickness of linkgraph GUI lines (#10410) +- Fix #10220: Don't select unselectable engine as default (#10404) +- Fix #10395: When loading old saves, don't forcibly bar level crossings (#10400) +- Fix #10377: Bad sorting of rail vehicles when primary variant is missing (#10378) +- Fix #10368: Server restarting game caused clients to hit assertion (#10369) +- Fix #10362: NewGRF bridges without speed limits (#10365) +- Fix #10363: CargoDist setting helptext shouldn't suggest symmetric distribution for diamonds in subtropic (#10364) +- Fix: [Script] Switch to OWNER_TOWN prevented OWNER_DEITY test during industry prospecting (#10360) +- Fix #10009: Bad overflow protection when taking out loans (#10359) +- Fix #9865: Removing files with the console always failed (#10357) +- Fix #10057: FallbackParagraphLayout fails to properly wrap (#10356) +- Fix #10177: Company list password padlock showed after switching to single player (#10354) +- Fix: Various Wide River issues (#10348) +- Fix: Link variants to parents when finalising engines (#10346) +- Fix #10333: Only show industry prospecting errors to local company (#10338) +- Fix #10335: Set initial scrollbar count for object GUI (#10336) +- Fix #10331: Starting new company during load must happen after AI start (#10332) +- Fix #10309: [SDL] Uninitialized width and height when turning off full screen (#10328) +- Fix #10032: Capacities of articulated vehicles in build window (#10326) +- Fix: Improve handling of corrupt NewGRF or image files (#10321, #10316) +- Fix: [NewGRF] Don't assume engclass 2 should be elrail (#10315) +- Fix: [Script] AIGroup.GetProfitLastYear could get values different than those displayed in GUI (#10227) +- Fix #10304: [Scripts] Don't start GS in intro game (#10305) +- Fix: [Script] Copy compat files for version 13 (#10303) + + +### 13.0-RC1 (2023-01-01) + +- Feature: 'font' console command to configure fonts within game (#10278) +- Feature: Ctrl-click to bulk edit timetable speeds/waiting times (#10265) +- Feature: [NewGRF] Vehicle variants in expandable purchase list (#10220) +- Feature: Expand all towns in the scenario editor (#10215) +- Add: [NewGRF] Slope-aware and roadtype-specific one-way sprites (#10282) +- Change: Display text files in black (#10291) +- Change: Make vehicle list dropdown buttons resize to fit strings (#10286) +- Change: [NewGRF] Support flipping shorter engines without explicit support (#10262) +- Change: Separate ground sprite from foundation sprite offsets (#10256) +- Change: Vertically centre sprite font relative to TrueType font (#10254) +- Change: [macOS] Set minimum macOS version to 10.13 (#10253) +- Change: Use lowered not disabled widget for current vehicle details tab (#10252) +- Change: Various improvements to NewGRF sprite aligner (#10249) +- Change: reset_engines console command now rerandomises introduction dates and reliability (#10220) +- Change: Show error message on failed industry prospecting (#10202) +- Fix: Local authority window rating list height ignored icon sizes (#10285) +- Fix #10150: Town signs could be truncated when using custom fonts (#10283) +- Fix #8971: Resize QueryStrings with interface scale change (#10281) +- Fix #10274: Crash when rescanning scripts with GS selected (#10276) +- Fix #10151: Use smaller padding for signs (#10272) +- Fix #10263: [Script] Restore tile validation for commands (#10269) +- Fix: Missing scrollbar for rail/roadtype dropdowns (#10264) +- Fix #10260: Incorrect rect height drawing image in vehicle details (#10261) +- Fix #10257: Incorrect catenary position on sloped bridge heads (#10258) +- Fix: Vertically centre chat prompt (#10250) +- Fix #10214: League and graph buttons in toolbar did not have a default action (#10246) +- Fix #10242: Allow a space for text shadow when clipping text (#10243) +- Fix #10206: Fully disable scripts in intro game (#10241) +- Fix #10218: Don't try to create river tiles along incorrect slopes (#10235) +- Fix #10208: [NewGRF] Allow using a specific underlay for road/tram tunnels (#10233) +- Fix #10224: Don't change fast-forward mode while saving (#10230) +- Fix #10147: Sound effect volume slider no longer set volume (#10228) +- Fix #10223: Crash when vehicle cloning fails on order cloning (#10225) +- Fix: Maximum space for engine preview image was never scaled (#10219) +- Fix #10216: Crash when upgrading savegame with crashed vehicles (#10217) +- Fix #10212: [Script] Nested ScriptAccounting scopes not restored properly (#10213) +- Fix #10114: Incorrect drag-highlight position with non-power-of-2 scaling (#10211) +- Fix #10198: Rearrange Intro GUI to make button rows narrower (#10203) +- Fix: Missing extra padding when drawing tooltip text (#10201) +- Fix: Bad alignment of button icons when using the original baseset (#10200) +- Fix: Signal icons incorrectly positioned in UI (#10199) +- Fix #10021: Object GUI resized when switching between different objects (#10196) +- Fix #9720: Delay start of GS/AI to after loading of savegame (#9745) + + +### 13.0-beta2 (2022-11-27) + +- Feature: Allow AI/GS to be fully modified in scenario editor (#10152) +- Feature: Display power-to-weight ratio in ground vehicle details GUI (#10123) +- Feature: Variable interface scaling (with chunky bevels!) (#10114) +- Feature: Hotkey to honk a vehicle's horn (#10110) +- Feature: Split AI/Game Script configuration windows and add them to world gen window (#10058) +- Feature: [GS] Scriptable league tables (#10001) +- Feature: Multi-track level crossings (#9931) +- Feature: Improved local authority action window (#9928) +- Feature: Automatic console command screenshot numbering with a filename ending in '#' (#9781) +- Feature: Add buttons to toggle music in the Game Options menu (#9727) +- Feature: Contextual actions for vehicles grouped by shared orders (#8425) +- Feature: Add cargo filter support to vehicle list (#8308) +- Feature: Show the cargoes the vehicles can carry in the vehicle list window (#8304) +- Change: Allow building canal by area outside editor (#10173) +- Change: Minor improvements to the new Finance GUI (#10168) +- Change: Let AI developers edit non-editable AI/Game Script Parameters (#8895) +- Change: Allow building docks on clearable watered object tiles (#8514) +- Fix #8770: Center vehicle status bar icon (#10178) +- Fix: Crash if error message window is too wide for screen. (#10172) +- Fix #10155: Network games not syncing company settings properly (#10158) +- Fix #10154: Network game desync related to setting a random company face (#10157) +- Fix #10011: Incorrect infrastructure totals when overbuilding bay road stop (#10143) +- Fix #10117: Object burst limit allowed one fewer object than the setting (#10120) +- Fix #10023: Allow negative input in text fields when needed (#10112) +- Fix #9908: Fix crash which could occur when a company was deleted when a depot window was open (#9912) + + +### 13.0-beta1 (2022-10-31) + +- Feature: Airport construction GUI displays infrastructure cost (#10094) +- Feature: Purchase land multiple tiles at a time (#10027) +- Feature: Add sticky pin & shade widgets to Object Selection UI panel (#10019, #10020) +- Feature: Improved handling of HiDPI and mixed-DPI screens (#9994, #9996, #9997, #10064) +- Feature: Alternative linkgraph colour schemes (#9866) +- Feature: Allow Shift+Insert as paste in edit box (#9836) +- Feature: Setting to make the local town authority rubber-stamp all actions (#9833) +- Feature: Add/extend console commands to enable screenshot automation (#9771) +- Feature: [Linkgraph] Show a tooltip with statistics when hovering a link (#9760) +- Feature: Build objects by area (#9709) +- Feature: Add setting to hide news about competitors vehicle crash (#9653) +- Feature: Ctrl-click to remove fully autoreplaced vehicles from list (#9639) +- Feature: Wide rivers on map generation (#9628) +- Add: [Script] ScriptCargo::GetWeight to get cargo weights (#9930) +- Add: Command line option to skip NewGRF scanning (#9879) +- Add: Show video driver name in Game Options window (#9872) +- Add: [NewGRF] Map seed as global variable (#9834) +- Add: [Script] IndustryType::ResolveNewGRFID to resolve industry id from grf_local_id and grfid (#9798) +- Add: [Script] ObjectType::ResolveNewGRFID to resolve object id from grfid and grf_local_id (#9795) +- Update: To all the friends we have lost and those we have gained (#10000) +- Change: Use the Simulation subcategory to openttd.desktop (#10015) +- Change: Constantly update destination of 'any depot' orders (#9959) +- Change: Use an indent, not a dash, to list train capacity (#9887) +- Change: [NewGRF] Increase vehicle sprite stack from 4 layers to 8 (#9863) +- Change: Don't pay Property Maintenance on stations when Infrastructure Maintenance is disabled (#9828) +- Change: Improved layout of the finance window (#9827) +- Change: [Admin] Bump admin port protocol due to command changes (#9754) +- Change: Suppress vehicle age warnings for stopped vehicles (#9718) +- Change: Make pf.yapf.rail_firstred_twoway_eol on by default (#9544) +- Change: Deliver cargo to the closest industry first (#9536) +- Fix: Lots of fixes to how windows handle resizing (#10040, #10042, #10046, #10051, #10056, #10068, #10070, #10098) +- Fix: Console commands list_ai output was truncated with a suitably large number of AIs (#10075) +- Fix #9876: MacBook Touch Bar crash / render issues w/ 32bpp graphics (#10060) +- Fix: Reduce framerate overhead in Train::Tick (#10055) +- Fix: Only open scenario editor date query once (#10050) +- Fix #10048: Don't relocate company HQ on the same exact location (#10049) +- Fix #10038: Missing upper bounds check when loading custom playlists (#10039) +- Fix: Wrong string used to determine size of zoomed out station sign (#10036) +- Fix: Disable "turn around" button for other companies' road vehicles (#10033) +- Fix: Online Players list mouse hover behaviour (#10031) +- Fix: [NewGRF] Weirdness of new stations (#10017) +- Fix #9854: DrawStringMultiLine() could draw beyond its bounding box (#10014) +- Fix: Incorrect player name in online players window (#10013) +- Fix #8099: News window zoom level fixes (#10005) +- Fix: [NewGRF] Upper 16 random bits should be the same for all station tiles in callback 140 (#9992) +- Fix #9989: £0 Net Profit is neither negative nor positive (#9991) +- Fix #9804: Only apply sprite_zoom_min setting when sprites available (#9988) +- Fix #9972: Add missing fill/resize flags on Framerate window widgets (#9982) +- Fix #9935: Use more selectivity when building SSE specific code (#9980) +- Fix #9940: Print debuglevel parse errors to console when changed from console (#9979) +- Fix #9977: Clearing the console with a large number of lines could cause a crash (#9978) +- Fix #9974: Console command getsysdate did not work due to off-by-one error (#9975) +- Fix: [NewGRF] Default value of RailVehicleInfo::railveh_type was inconsistent with other default properties (#9967) +- Fix #8584: Vehicles with shared orders getting invalid or unexpected start dates (#9955) +- Fix #9951: [NewGRF] Scenario editor random industries button broke NewGRF persistent storage (#9952) +- Fix: Validation of various internal command parameters that could have allowed a rogue client to crash servers (#9942, #9943, #9944, #9945, #9946, #9947, #9948, #9950) +- Fix #9937: Station industries_near incorrect after removing part moved sign (#9938) +- Fix: [Script] ScriptRoad::HasRoadType really check for RoadType (#9934) +- Fix #9363: Rebuild client list on reinit event (#9929) +- Fix #9925: Industry tile layout validation for layouts of only one tile (#9926) +- Fix #9918: Reset industy last production year on scenario start (#9920) +- Fix #9914: Prevent more useless pathfinder run for blocked vehicles (#9917) +- Fix: List a max of four share owners instead of three (#9905) +- Fix: [NewGRF] Industry layouts with zero regular tiles should be invalid (#9902) +- Fix #9869: Remove docking tile when doing a clear square (#9898) +- Fix: New player companies use favorite manager face, if saved (#9895) +- Fix: Towns don't build parallel, redundant bridges (#9891) +- Fix #9712: Cap town bridge length at original 11-tile limit (#9890) +- Fix #9883: Show cost/income float over end tile of rail or road construction (#9889) +- Fix #9870: Don't update infrastructure totals when overbuilding object on canal (#9888) +- Fix #9877: GS could trigger 'Cost: £0' cost message (#9878) +- Fix 44f2ef1: [strgen] Allow gender for {CARGO_SHORT} (#9873) +- Fix #9867: Industry::stations_near not filled at industry creation (#9868) +- Fix #9853: Incorrect merge of guiflags and flags for osk_activation (#9855) +- Fix #6544: Don't join AI company when loading network game in singleplayer (#9794) +- Fix: Company values do not properly account for shares (#9770) +- Fix #9546: Crash when no industries are present in game (#9726) +- Fix #9708: [Linkgraph] Don't assume vehicles have a non-zero max speed (#9693) +- Fix #9665: [Linkgraph] Fix travel times of non-direct journeys (#9693) +- Fix #8797: Use logical rail length when placing signals (#9652) +- Cleanup: [NewGRF] Remove unused flag sprites (#10052) + +## 12.x + +### 12.2 (2022-04-02) + +- Feature: Remember the last-used signal between games (#9792) +- Change: [MacOS] Allow touchbar usage on all supported OS versions (#9776) +- Change: Add a timestamp in name of crash files (#9761) +- Fix #9736: Duplicate multiplayer window opens upon canceling password entry (#9842) +- Fix: Removing long roads doesn't prioritise refusal of local authority over other errors (#9831) +- Fix #9020: Glitchy station coverage highlight when changing selection (#9825) +- Fix: Correct some Romanian town names (#9819) +- Fix: Original music playback rate was slightly too fast (#9814) +- Fix #9811: Use the NewGRF-defined vehicle center when dragging ships and aircraft (#9812) +- Fix: Do not let shares in the company taking over another company disappear (#9808) +- Fix #9802: Crash when using lots of NewGRF waypoint types (#9803) +- Fix #9766: Don't write uninitialised data in config file (#9767) +- Fix #9743: [MacOS] Don't try to render touchbar sprites with invalid zoom level (#9776) +- Fix #9774: Building roadstop in estimation mode updates station acceptance (#9775) +- Fix: If vehicles only refit to cargo-slots >= 32, the default cargo was wrong (#9744) +- Fix #9735: Possible desync when replacing a depot on same tile (#9738) +- Fix #9730: [Network] Connections can use an invalid socket due to a race condition (#9731) +- Fix: Don't show sign edit window for GS-owned signs (#9716) +- Fix #9702: Display order window for vehicle group on ctrl-click only when using shared orders (#9704) +- Fix #9680: Crash when loading really old savegames with aircraft in certain places (#9699) +- Fix: Update last servicing dates when using the date cheat (#9694) +- Fix: Error message shows about missing glyphs while suitable fallback font is found (#9692) + + +### 12.1 (2021-11-08) + +- Feature: Button to toggle showing advanced signal types (#9617) +- Change: Don't show screenshot GUI in screenshots (#9674) +- Change: Suppress panning in intro game, while user is interacting with the GUI (#9645) +- Change: Draw rotor in cursor when dragging helicopters in depots (#9612) +- Fix: Invalid memory access when loading a currency NewGRF (#9675) +- Fix #9579: Object and HQ construction is Construction cost, not Property Maintenance (#9673) +- Fix #9669: Ships exiting a blocked depot/lock could exit in the wrong direction (#9672) +- Fix: Every 16th client never reconnects after server restart (#9666) +- Fix #9643: Screenshots were always written as BMP files (#9644) +- Fix #9630: Intro game could zoom in/out more than allowed by settings (#9633) +- Fix #9626: Incorrect loading of script saved data (#9629) +- Fix: Emergency crash save had the wrong NewGRF list saved in it (#9627) +- Fix #9595: Always use plural forms of cargo for subsidy strings (#9619) +- Fix #9614: Refresh rate dropdown was still active when vsync was enabled (#9618) +- Fix: Don't use 'server address' string in server list when displaying an invite code (#9615) + + +### 12.0 (2021-10-17) + +- Add: [Network] Keep the refresh button in lowered state while refreshing (#9600) +- Add: Console command to list search directories for various things (#9583) +- Fix: Try all possible reverse directions when a ship reaches a dead end (#9610) +- Fix: Incorrect Romanian own name (#9598) +- Fix #9591: Update station docking tiles upon placing a water object on a docking tile (#9594) +- Fix #9548: [Squirrel] Crash during engine cleanup after reaching memory limit on realloc (#9592) +- Fix #9588: [Squirrel] Reaching memory limit during script registration could prevent further script detections (#9589) +- Fix: Make ships more likely to find their destination at the cost of slightly worse paths (#9576) +- Change: Reverse ship when leaving docks if a better path exists (#9610) +- Change: Allow all tiles around docks to be docking tiles (#9578) + + +### 12.0-RC1 (2021-09-25) + +- Feature: Display icon/text whether vehicle is lost in vehicle (list) window (#9543) +- Feature: [MacOS] Add selected toolbar buttons to MacBook Pro Touch Bar (#9511) +- Feature: Button to open order window from vehicle shared orders window (#9325) +- Feature: Ctrl-Clicking shared order vehicle list opens order window (#9325) +- Feature: Multiple rotating views on title screen (#8980) +- Feature: Hide block signals in GUI by default (#8688) +- Add: [Script] Allow GameScripts to build neutral objects (#9568) +- Add: [Network] Allow sending chat messages via admin port (#9563) +- Add: [AI/GS] Missing water related functions and objects (#8390) +- Fix: Industry funding window did not update when changing funding method (#9572) +- Fix #9562: [NewGRF] Handle case of invalid Action2 with zero results (#9564) +- Fix: Incorrect error messages when placing water in scenario editor (#9560) +- Fix #9484: Update locale currencies settings config map (#9559) +- Fix: Prevent train reversing when entirely inside a train depot (#9557) +- Fix: [Network] Add back 'Spectate' option to company toolbar menu (#9556) +- Fix #9463: [Win32] Work around XAudio2 crashes (#9549) +- Fix #8603: Don't give focus to text filter when opening Object GUI (#9547) +- Fix #9241: Grove and forest tree brushes did not also create rainforest terrain (#9542) +- Fix: [Network] Several crashes in our network code (#9534, #9456) +- Fix #9527: Crash when trying to place multi-tile objects at map edge (#9529) +- Fix: [Network] SendCmdNames only sent one name per packet (#9528) +- Fix #9407: Desync when founding a town nearby a station (#9526) +- Fix #9521: Don't load at just removed docks that were part of a multi-dock station (#9524) +- Fix: Ships always tried to avoid docking tiles when pathfinding (even if nothing was on them) (#9522) +- Fix: [Network] Convert server_advertise to server_game_type in config file (#9515) +- Fix #9490: [Network] A full server couldn't be queried (#9508) +- Fix: [Network] Don't show GameScript " (v0)" for old servers (#9507) +- Fix: [Network] Show query errors in the server listing instead of error popup (#9506) +- Fix: [Network] Crash when last-joined server was no longer available (#9503) +- Fix #9501: [Network] Crash when more than one game-info query was pending (#9502) +- Fix: Wrong error message when building canals over ship depots / locks (#9410) +- Fix: Reduce cost of building canals over objects on sea (#9410) +- Change: [Linkgraph] Delete links only served by vehicles stopped in depot (#9499) + + +### 12.0-beta2 (2021-08-19) + +- Feature: [Linkgraph] Prioritize faster routes for passengers, mail and express cargo (#9457) +- Fix: Wrong town window refreshed when building an airport with noise levels enabled (#9497) +- Fix: Improve wording of network-related messages (#9494, #9495, #9500) +- Fix: [Network] Report reuse of invite-code (#9487) +- Fix: [Network] Connecting with the same client name thrice hangs the server (#9485) + + +### 12.0-beta1 (2021-08-15) + +- Feature: [Network] Remove lobby window; pressing "Join Game" now immediately joins a server (#9467) +- Feature: [Network] Synchronize server name to clients and display in Online Players window (#9472) +- Feature: [Network] Mention you are a spectator in the status bar (#9471) +- Feature: [Network] No longer require port-forwarding to host a server (#9443, #9447) +- Feature: [Network] Allow setting your server visibility to "invite-only" (#9434) +- Feature: [Network] Join servers based on their invite code (#9432) +- Feature: Raise the maximum NewGRF limit to 255 (#9428) +- Feature: Persistent rotation of numbered auto/netsave after restart (#9395, #9397) +- Feature: [NewGRF] Maximum curve speed modifier for rail vehicles (#9346) +- Feature: Move sensitive information to secrets.cfg and private information to private.cfg (#9298) +- Feature: Signed Windows builds (#9294) +- Feature: [NewGRF] Define refittability of default vehicles using cargo classes (#9148) +- Feature: Configurable subsidy duration, up to 5000 years (#9081) +- Feature: [Network] Rework in-game Online Players window (#9067) +- Feature: [Network] Show previous chat history when the chat message box is open (#9025) +- Feature: Button to reset game settings to their default values (#8958) +- Feature: Press Ctrl to build diagonal rivers in Scenario Editor (#8880) +- Feature: Set wagon replacement per group when using autoreplace (#7441) +- Add: [Network] Open Online Players window on starting/joining a server (#9479) +- Add: [Script] Basic information about loaded NewGRFs for scripts (#9464) +- Add: [AI] Get the number of vehicles in a given group (#9462) +- Add: [Network] Inform network clients what game-script a server is running (#9441) +- Add: Hindi translation (#9086) +- Add: [Network] Ensure players fill in a name instead of defaulting to "Player" (#9080) +- Change: Allow pause/unpause console command in single player (#9342) +- Change: Make savegame format self-descriptive and consistent across all objects (#9322, #9335, #9338, #9339) +- Change: By default, make "unload all" leave stations empty (#9301) +- Change: Reworked the debug levels and messages for network logs (#9230, #9251) +- Change: [Emscripten] Set default scrolling mode to non-pointer-locking (#9191) +- Change: Use neutral pronouns for various strings (#9189, #9203, #9228) +- Change: Make the town directory horizontally resizable (#9157) +- Change: Allow non-ASCII currency separators (#9121) +- Change: [NewGRF] Display a pop-up window for Errors with severity ERROR (#9119) +- Change: Treat languages as finished, if translations are 75% completed (#9019, #9086) +- Change: Disable NewGRF window apply button if no change was made (#8934) +- Fix: [Script] Crash when iterating lists of which the key is larger than 32bit (#9465) +- Fix: [Network] Desync due to use of unstable sort when distributing cargo production (#9460) +- Fix #9440: Negative cargo payments not being handled correctly (#9455) +- Fix: [Network] Crash when joining a server again after a TCP disconnect (#9453) +- Fix: Don't enable rename button for network clients in build vehicle window (#9452) +- Fix: Money could underflow and wrap around (#9451) +- Fix: Parse the console settings the same way as config settings (#9438) +- Fix: Ensure no more than the allowed number of NewGRFs are loaded from the configuration (#9430) +- Fix: [NewGRF] Overflow when determining cargo mask for string code 9A 1E (#9423) +- Fix: Integers for scripts are 64bit, but saved as 32bit (#9415) +- Fix #9392: [Script] Return a valid value with GetBuildWithRefitCapacity even when AIs are maxed out in vehicles (#9393) +- Fix #8169: Crash when autoreplacing vehicle with no orders (#9387) +- Fix: Wrong cargo line position in IndustryCargo window (#9383) +- Fix: Race-condition during startup of NewGRF scan (#9382) +- Fix: Don't propagate Shift/Ctrl state till next game-tick (#9381) +- Fix: Prevent palette updates during copying to the video driver (#9379) +- Fix: [Network] Determining GetNetworkRevisionString could overflow and underflow its buffer (#9372) +- Fix #9358: Don't skip empty files in tar archives (#9367) +- Fix: For old savegames, station bus/truck station cache was not updated (#9366) +- Fix #9353: [Script] Garbage collecting on priority queues could crash the game (#9356) +- Fix: Respect the autosave_on_exit setting for Null video driver (#9343) +- Fix: Compatible NewGRFs in crash-log reported wrong MD5 hash (#9340) +- Fix: [Script] Ensure the saved script strings are properly validated and terminated (#9336) +- Fix #9316: Town bridge length limit check incorrect above 250k inhabitants (#9318) +- Fix: Limit heightmap sizes to 8192x8192 (#9307) +- Fix #9281: Money generating exploit when buying out a company (#9300) +- Fix: Part of a tile might not be redrawn when terraforming (#9296) +- Fix: [OpenGL] Increase timeout when waiting for the GPU to be done with the drawing buffer (#9282) +- Fix: Vehicles sent in the wrong direction if there is no path to the destination (#9280) +- Fix #9264: Do not attach temporary wagons to free wagon chains when autoreplacing (#9278) +- Fix #9267: [Script] Crash during garbage collection (#9275) +- Fix: Encountering two-way red signals could prune unrelated Pathfinder branches (#9271) +- Fix #9255: [Network] Crash when hostname is not found (#9259) +- Fix #9256: Invalid read after free when replacing train chains (#9258) +- Fix: [Emscripten] Force secure WebSockets over HTTPS (#9248) +- Fix #9242: Tree tick handler did not scale by map size (#9246) +- Fix: [Network] Mark server as offline when no longer reachable (#9244) +- Fix: [Network] Don't rebuild the host-list during iterating the list (#9240) +- Fix: [Network] Don't mark the last-joined server as a manually added server (#9239) +- Fix: [Network] Clients leaving because of broken connections was not broadcasted (#9238) +- Fix: [Network] Check on CIDR for netmask check considered everything valid (#9235) +- Fix: Creating screenshots on dedicated servers failed (#9232) +- Fix: Leaking file descriptors for downloaded content (#9229) +- Fix: Spelling of several town names (#9222) +- Fix #9209: Game hangs when resizing highscore/news window if the screen is too small (#9210) +- Fix: [Network] Optimize creating network connections for clients using IPv4 and IPv6 (#9199) +- Fix #9186: Fix incorrect bounding box height causing station sprite glitch (#9187) +- Fix: Truncating strings in settings could leave invalid UTF-8 characters (#9121) +- Fix: Many issues related to window scaling (#9087, #9219) +- Fix: Invalidate cached vehicle colourmaps when changing liveries setting (#9006) +- Fix #8981: Don't attempt to re-reserve path if already entering/entered depot (#9000) +- Fix: Missing 'Town names:' colon in map gen GUI (#8986) +- Fix: Sorting and filtering industries that produce/accept many cargoes (#8468) +- Remove: [Network] COMPANY_INFO packets (#9475) +- Remove: [Network] A server can no longer set a limit to the amount of spectators allowed (#9466) +- Remove: Arbitrary limit on number of statically loaded NewGRFs (#9431) +- Remove: [Network] Language and map-name from server information (#9070) + + +## 1.11.x + +### 1.11.2 (2021-05-03) + +- Change: [Win32] Limit hardware accelerated video driver to OpenGL 3.2 or higher (#9077) +- Change: More improvements to the GUI at different scales (#9075, #9102, #9107, #9133, #9174, #9183) +- Fix: Query windows could be partially drawn (#9184) +- Fix #9113: Crash when removing an airport that exists in an aircraft's orders (#9182) +- Fix #9117: [Fluidsynth] Hang when changing song (#9181) +- Fix: String validation could leave invalid UTF-8 encoded strings (#9096) +- Fix: [Network] Out-of-bounds memory access with modified servers sending too short password salts (#9176) +- Fix: Crash when extra viewport with zero height has sign in view (#9175) +- Fix #9147: Crash when taking screenshots (#9169) +- Fix #6598: [Network] Prevent crashes when (re)joining network game by falling back to main menu first (#9163) +- Fix #9152: Screenshot success popup window was treated as an error (#9159) +- Fix: Fast-forward stuttering when vsync is enabled (#9140) +- Fix: [Network, Win32] Network errors were handled badly (#9116) +- Fix: [Network] Savegame transfer could stall in rare cases (#9106) +- Fix #9097: [NewGRF] Cargo initial payment variable was being truncated (#9098) +- Fix: [NewGRF] Industry variable 66 and object variable 46 erroneously truncated the distance (#9088) +- Fix: [NewGRF] Industry variables 65 and 66 ignored the parameter, and always used the north tile (#9088) +- Fix: Do not include regression test AI in bundle (#9068, #9164) +- Fix #9062: [Win32] Version in executable was not set to current release version (#9066, #9154) + + +### 1.11.1 (2021-04-18) + +- Feature: Toggle to enable/disable vsync (#8997) +- Feature: Volume controls in the Game Options window, and better defaults (#8943) +- Add: Hotkey to focus object and rail filters (#8908) +- Add: Better plural support for Romanian (#8936) +- Change: Improve layout and spacing of several windows at different GUI scales (#9041, #9042, #9044, #9050) +- Change: [Win32] Use user UI language setting for initial language selection (#8974) +- Change: Make effect volume scale more intuitively (#8945, #8950) +- Change: Improve padding of Object & Rail station windows (#8929) +- Fix #6322: [Script] Crash when script allocates too much memory, now kills script instead (#9047) +- Fix #7513: [Script] Crash on garbage collection with misbehaving script (#9040) +- Fix #9028: [OpenGL] Crash when changing max sprite zoom level (#9032) +- Fix #8874: show a warning when a NewGRF scan is requested multiple times (#9022) +- Fix: Desync when GS unlocks railtype with wagon unlock (#9021) +- Fix #9015: [Win32] Crash on running "pwd" command in the console (#9016) +- Fix #9008: Validate starting year given on the command line (-t) (#9014) +- Fix #8878: [Network] Slow DNS queries could block the server and disconnect clients (#9013) +- Fix: Improve validation of OpenGL video driver to avoid crashes (#9007) +- Fix: Credits scrolled too slowly with larger font sizes (#8994) +- Fix #8977: Crash when altering max sprite resolution (#8993) +- Fix #8956: Industry disaster news messages showed the wrong location (#8992) +- Fix: [Win32] Font glyphs of certain widths had broken rendering (#8990) +- Fix #8930: [Win32] Duplicate text input issue for systems using IME (#8976) +- Fix: [Network] Potential stale client entries in client list (#8959) +- Fix: Graphical issues when dragging measurement tooltips (#8951) +- Fix: [Fluidsynth] Use provided default soundfont if available (#8948, #8953) +- Fix #8935: [macOS] Crash on save (#8944) +- Fix #8922: Crash when selling shared vehicles with shared vehicle window open (#8926) +- Fix: Compiling on armhf (Raspberry Pi) (#8924) + + +### 1.11.0 (2021-04-01) + +- Feature: Allow setting a custom terrain type to define highest peak (#8891) +- Feature: Auto-detect map height limit based on generated map (#8891) +- Feature: Setting to indicate desert coverage for tropic climate and snow coverage for arctic climate (replaces snow line height) (#8891) +- Add: Allow setting the highest mountain for heightmaps (#8891) +- Change: Scale exported heightmaps to highest peak and inform the user of this value (#8891) +- Change: Remove "maximum map height" from the New Game GUI (#8891) +- Fix #8803: Only auto-remove signals when rail can be built (#8904) +- Fix #8565: Stopped road vehicle displays a speed different than 0 (#8901) +- Fix #8886: Don't try to resolve folders within tars named '.' (#8893) +- Fix: Placing random trees in SE crashes the game (#8892) +- Fix #8875: Filter string in station window breaks flow in user interface (#8885) +- Fix #8871: [OpenGL] Initialize all buffers after resize and clear back buffer (#8877) +- Fix: OpenGL performance with some AMD GPUs (#8876) +- Fix: Recompute road/railtype availability after disabling the engine (#8872) +- Fix: OSK layout not scaled for 2x or 4x GUI scale (#8868) + + +### 1.11.0-RC1 (2021-03-14) + +- Feature: Option to (dis-)allow hardware accelerated video drivers (#8819) +- Feature: Option to set display refresh rate (#8813) +- Feature: Allow custom width/height of screenshot and making heightmap screenshots via console (#8804) +- Feature: Allow filtering on name in rail station window (#8706) +- Feature: Setting for highest resolution of sprites to use (#8604) +- Add: Make NewGRF Scanner / World Generation update smoother and make aborting it react faster (#8830) +- Add: Malaysia Ringgit as Currency (#8783) +- Add: "Engines only" filter in build train window (#8733) +- Change: De-limit framerate window's framerate (#8772) +- Change: Clarify what effect town interactions have (#8744) +- Change: Don't show global goals in company goal windows (#8709) +- Change: Recolour graph windows to brown (#8700) +- Fix #8855: Bootstrap could result in an empty screen when bootstrap fails (#8856) +- Fix #8851: Don't allow infinite "exec" depth in script, but limit to 10 deep (#8852) +- Fix #8647: Incorrect drawing order of tram catenary sprites (#8843) +- Fix #8711: Having gui_zoom lower than zoom_min causes a crash (#8835) +- Fix #8810: "aircraft out of fuel" news shows the wrong place (#8832) +- Fix #8833: Don't reload NewGRFs when we are shutting down (#8830) +- Fix: Scale padding between elements the same as other padding (#8829) +- Fix #8808: [OSX, OpenGL] Crash on switching blitters due to double-mapping the video buffer (#8822) +- Fix #8784: Using Alt+Enter doesn't update the fullscreen toggle visibly (#8820) +- Fix #8817: Keep NewGRF order for object class sorting (#8818) +- Fix #8809: Crash when removing airport when hangar window open (#8815) +- Fix #8799: Crash when Search Internet in Multiplayer (#8801) +- Fix #8775: [Win32] Don't create the main window when Alt-Tabbing back into fullscreen (#8792) +- Fix #8774: Black screenshots when using 40bpp-blitter (#8791) +- Fix: [OSX] Hide dock when entering fullscreen (#8789) +- Fix: Bootstrap fails to start on clean install (#8788) +- Fix: Terraform limit acts random when maxing out per_64k_frames setting (#8782) +- Fix: Max-value of fast-forward-speed-limit can be outside its storage size (#8769) + + +### 1.11.0-beta2 (2021-02-28) + +- Feature: Add setting to limit fast-forward speed (#8766) +- Feature: Significant performance improvements to all video drivers (#8605, #8652, #8660, #8685, #8702, #8703, #8707, #8726, #8740) +- Feature: Configurable display refresh-rate, default to 60fps (#8680) +- Feature: Automatically upload releases to Steam (#8644) +- Feature: Generic Linux builds (#8641) +- Feature: [GS] Allow non-question type windows to have no buttons (#8638) +- Feature: [macOS] ZIP build (#8614) +- Feature: Object class selection string filtering (#8603) +- Feature: 'Remove all industries' button in scenario editor (#8550) +- Feature: Automatic UI and font zoom levels when supported by the OS (#8537) +- Feature: [macOS] Render screen at native resolution by default for HiDPI screens (#8519) +- Feature: OpenGL video driver (#7744) +- Add: Indonesia Rupiah currency (#8616) +- Change: Improve graph period markings (#8732) +- Change: Make pathfinder account for maximum order speed, if set (#8722) +- Change: Darken graph grid lines for legibility (#8690) +- Change: Make order window hotkeys toggle for load & unload variants (#8669) +- Change: Use a more specific error message when attempting to bulldoze your own HQ (#8667) +- Change: Convert .md to .rtf for Windows/Mac packages (#8617) +- Change: Move the 'tree placer algorithm' & 'road drive side' settings to the Settings window (#8566) +- Change: Move town name generator selection to mapgen GUI (#8566) +- Change: [macOS] Native font rendering (#8518) +- Fix: Display of network lobby windows for different GUI sizes (#8765) +- Fix: Don't desync if client leaves before you finish downloading map (#8755) +- Fix: Allow estimating vehicle clone cost even if short on money (#8748) +- Fix: Don't notify twice that a client left because of a timeout (#8746) +- Fix: Vehicle cursor size did not account for the interface zoom level (#8739) +- Fix #8123: Trams on half-tiles couldn't find depots (#8738) +- Fix #8276: [NewGRF] Crash when an object's size was not set (#8719) +- Fix #8349: Close depot vehicle list windows when closing the depot window (#8717) +- Fix #8594: [NRT] Road pathfinder did not account for roadtype speed limits or lengths of tunnels/bridges (#8710) +- Fix: Whole status bar instead of money widget refreshed on money change (#8692) +- Fix: Unnecessary status bar redraws when there is no news to show (#8691) +- Fix: New orders are non-stop by default (#8689) +- Fix: Framerate window showed a slightly higher rate than actually measured (#8682) +- Fix: Autorenew failure advice due to bad refit being shown to all companies (#8681) +- Fix #8625: Wrong ending year was displayed in highscore table (#8672) +- Fix #8620: Scale spacing between date & news in history window according to font scaling (#8671) +- Fix: [Win32] Set minimum resolution for timers to 1ms (#8660) +- Fix: Mention our websites with https:// (instead of http://) (#8657) +- Fix: [Emscripten] Open links in browser (#8655) +- Fix: Don't crash when towns upgrade road tiles during expansion (#8651) +- Fix #8029: [SDL2] Blank display when under Wayland (#8648) +- Fix: Default Network Server List sorter put compatible servers in wrong order (#8626) +- Fix: Use non-pulsating red highlight for coverage (#8622) +- Fix: Center text and image in vehicle statusbar vertically (#8602) +- Fix: Don't walk out of the map when trying to build tunnels (#8600) +- Fix: Off-by-one error in desert/rainforest positioning at world gen (#8588) +- Fix #8037: Crash when restarting AI that is controlling the same company as the player (#8587) +- Fix: Stopped ships shouldn't block depots (#8578) + + +### 1.11.0-beta1 (2021-01-22) + +- Feature: [GS] Ability to set some extra text in the industry window (#8576) +- Feature: Show rainforest under vegetation on smallmap (#8562) +- Feature: Automatically determine window size on new install (#8536) +- Feature: Towns can build tunnels (#8473) +- Feature: Make maximum length of town bridges depend on population (with a minimum limit of 4) (#8439) +- Feature: New icons for renaming and go-to-location on GUI windows, and improve consistency of usage (#8455) +- Feature: Support for ARM64 on Apple Silicon and Windows (#8340, #8577, #8583) +- Feature: Add an option to disable tree growth completely (#8415) +- Feature: Support for Emscripten (play-OpenTTD-in-the-browser!) (#8355) +- Feature: Show group name as part of the default vehicle name (#8307) +- Feature: "Frozen" economy setting that stops production changes and industry closures (#8282) +- Feature: New velocity unit "tiles/day" (#8278) +- Feature: Option to automatically remove signals when placing rail (#8274) +- Feature: Increase max possible distance from border for oil refineries and rigs (#8237) +- Feature: Improve tree planting window, and allow planting 'clumps' of trees by dragging in the scenario editor (#8234) +- Feature: Indian Rupee (INR) currency (#8136) +- Feature: [GS] Ability to give a company exclusive access to an industry (#8115) +- Feature: Hotkeys for Land Info window, News window & close error window (#8053, #8266) +- Feature: Improve rendering of large viewports (#7962) +- Feature: [GS] Influence industry production changes from GameScript (#7912) +- Feature: [GS] Push-buttons on storybook pages (#7896) +- Feature: Option to group vehicle lists by shared orders (#7028) +- Feature: Drag-and-drop vehicles in group GUI for shared order groups (#7028) +- Add: [GS] A tile parameter to GSCompany::ChangeBankBalance for showing changes more visually (#8573) +- Add: [NewGRF] Allow NewGRF vehicles to query the current rail/road/tram type (#8554) +- Add: [Script] ScriptCargo::GetName for the human readable name of cargoes (#8544) +- Add: "reload" console command to reload the current scenario or heightmap (#8527) +- Add: [NewGRF] Flag to test if inflation is on or off (#8427) +- Add: [Script] Native priority queue (useful for things like pathfinders) (#8091) +- Add: [NewGRF] Industry behaviour flag to override second cargo production clamping for water industries when using smooth economy (#8079) +- Change: [SDL2] Start game on the screen where the cursor is (#8572) +- Change: Use a dark background for all profit graphs to increase contrast (#8557) +- Change: Reword warning in cheat window (#8538) +- Change: Enable the toolbar for road/rail/dock/airport, regardless of vehicle availability (#8521) +- Change: For arctic and tropical climates, make sure at least a few hills are generated (#8513) +- Change: Destroying a tunnel/bridge now sells the tracks before destroying the tunnel/bridge (#8508) +- Change: Move "give money" from client-list to company window (#8500) +- Change: [MacOS] Hide Dock and menu when in fullscreen mode (#8487) +- Change: Improve performance for complex vehicle chains by resolving sprites less frequently (#8485) +- Change: Make engine reliability independent of introduction date (#8470) +- Change: Some default settings to improve gameplay for new players - default non-stop orders on, disable inflation, quick goto orders, show track reservations, and more (#8463) +- Change: Converting town-owned road types now requires a positive town rating (#8457) +- Change: Rework server list buttons for searching LAN/internet servers (#8426) +- Change: Add some styling to GS question windows depending on the type (#8422) +- Change: [Linkgraph] Speed up game exit by allowing job threads to be aborted early (#8416) +- Change: Prevent towns from building dead-end road bridges (#8401) +- Change: Send network error to the server before making an emergency save (#8387) +- Change: Extend the allowed range for max loan setting up to £2 billion (#8386) +- Change: Don't display OS name when exiting the game (#8366) +- Change: Save openttd.cfg immediately on changing a setting (#8358) +- Change: Autorenew now defaults to on (#8352) +- Change: [NewGRF] Also use aircraft property 12 for helicopters (#8347) +- Change: Service at depot also resets breakdown chance (#8317) +- Change: Use key names instead of characters in hotkey.cfg (#8291) +- Change: Allow command cost-estimation while paused (#8222) +- Change: Always apply inflation from 1920 to 2090, no matter the game start year (#7589) +- Change: Use CMake for build system (#7270) +- Change: [Linkgraph] Pause the game when linkgraph jobs lag (#7081) +- Change: Place "Group by" above "Sort by" in station window for consistency (#7028) +- Fix #8589: Prevent desyncs with vehicle motion counters and NewGRFs (#8591) +- Fix #7670: Improve pathfinder performance when lost vehicles are blocked from moving (#8568) +- Fix: Inform user if a custom font failed to load due to missing glyphs (#8559) +- Fix: Don't allow wagon chains (without an engine) to exceed maximum train length (#8533) +- Fix #7619: Super fast NewGRF aircraft could be unable to land (#8531) +- Fix: Improve connection retries for the content server in cases of broken networking (#8530) +- Fix #7972: Show invalid orders to stations that don't accept the vehicle (#8516) +- Fix: Error when trying to clone a vehicle with invalid orders (#8515) +- Fix #8050: Various off-by-one errors in how the end-year of the game was used (#8512) +- Fix #8332: Aborting vehicle group drag & drop could cause crashes (#8511) +- Fix #8168: Allow relocating HQ partially over an existing HQ (#8510) +- Fix #8068: Allow selling tram track regardless of bank balance (#8509) +- Fix #7604: Prevent houses from wandering away from roads (#8507) +- Fix: Make the "password" button the same size as the other buttons in the Company window (#8500) +- Fix #7611: Keep news about vehicle accidents around after the vehicle is cleaned up (#8497) +- Fix: [MacOS] Full animation in fullscreen mode was reducing the height of the window (#8491) +- Fix: [MacOS] Loading custom fonts (#8484) +- Fix: Network client makes emergency saves twice if the server is disconnected (#8477) +- Fix #8462: Stop towns from trying to build roads on water (#8471) +- Fix: [NewGRF] GetCurveSpeedLimit should use the railtype from the current tile (#8466) +- Fix #8437: Crash when using certain heliports with certain rotated airports (#8458) +- Fix #8437: Planes would land at the wrong height if the top corner of the airport was lowered (#8458) +- Fix #8297: Infrastructure counters for road tunnels, bridges & depots (#8454) +- Fix #6468: Don't store the version of AIs that are started via console (#8430) +- Fix: Don't lower tree density if spreading is not enabled (#8413) +- Fix: Prevent savegame version conflicts with certain old patchpacks (#8411) +- Fix: [NewGRF] Variable 0x44 was always HZB_TOWN_EDGE for road stops (#8400) +- Fix #8313: Use correct capitalization for TTO / DOS music files in the baseset metadata (#8385) +- Fix: [NewGRF] Action 7/9 conditions 0x0F to 0x12 failed, if 'param' was 0x88 (#8382) +- Fix: Change the working-dir searchpath when using '-c' (#8367) +- Fix: Useless warning with -snull and no BaseSounds available (#8361) +- Fix: Crash trying to load TTO/TTD savegames. (#8356) +- Fix: [Script] Don't echo script exceptions to console (#8331) +- Fix: Slovak ownname was using the wrong form (#8326) +- Fix #8311: [NewGRF] Industry probability at map generation was scaled differently when set via property or callback (#8312) +- Fix: Only check houses for cargo when generating subsidies with towns (#8305) +- Fix: Sprite preview in sprite aligner was too small with scaled UI (#8288) +- Fix: Spell 'Viewport' consistently (#8260) +- Fix #7772: Show vehicle destination on mouseover when vehicle stopped (#8236, #8543) +- Fix #8232: Huge screenshot warning was shown incorrectly (#8224) +- Fix #8153: Report incompatible cargo/order when autoreplace fails (#8169) +- Fix: [Script] ScriptMarine::AreWaterTilesConnected did not work for aqueducts (#8074) +- Fix #7645: Add cost of clearing the sloped tile to the price of a dock (#7947) +- Fix #6452: Reset only editable and visible settings from GUI (#7890) +- Fix: Original terrain generator did not keep a single gap of water at the borders (#7883) +- Remove: In-game console command "content select all" (#8363) +- Remove: [OSX] Support for OSX older than 10.7, including QuickTime music driver (#8078) + + +## 1.10.x + +### 1.10.3 (2020-08-09) + +- Change: Also make roadside trees match the tree transparency option (#8245) +- Fix: Center text and icons in the status bar vertically (#8273) +- Fix: [NRT] Set invalid road and tram types for rail tunnel ends (#8269) +- Fix #7980: Properly invalidate mouse-over station coverage highlight (#8263) +- Fix #8250: [NRT] Company infrastructure window always omits last road/tramtype (#8251) +- Fix #8162: [NRT] Improve error message when converting town owned road (#8247) +- Fix #8216: Don't show floating text on autoreplace if cost is 0 (#8244) +- Fix #8129: Crash if a news message expires while viewing the endgame screen (#8243) +- Fix #8221: Use more specific error message when a bridge is too long (#8240) +- Fix #8230: Resolve ".." when opening files in .tar (#8231) +- Fix: A few race conditions in netcode (#8227, #8228, #8229) +- Fix #7838: Crash relating to group creation and renaming (#8223) +- Fix #8104: [SDL2] Fix window resizability when going from fullscreen to windowed mode (#8211) +- Fix: Display banlist's indexes correctly (#8209) +- Fix: Possible desync with subsidy creation (#8159) +- Fix #8131: Draw small bridges pillars in more places (#8149) + + +### 1.10.2 (2020-06-01) + +- Add: Ubuntu 20.04 packages (#8127) +- Fix: [OSX] Possible crash on failure to set colourspace (#8181) +- Fix #8166: Prevent crash from a NewGRF with an invalid RoadType (#8180) +- Fix #8024: Make Online Content GUI more responsive while loading (#8179) +- Fix #7970: Disable event loop when generating crash dump (#8176) +- Fix: [Build] Compatibility with modern Visual Studio (#8170) +- Fix: Trees would disappear completely after a few years if tree spread was disabled (#8160) +- Fix #8155: Roadtype speed limit in scenario editor toolbar dropdown was doubled (#8156) +- Fix: Desync after house replacement (#8151) +- Fix #8137: New clients can't join (desync) after funding an industry (#8140) +- Fix #8132: Corrupted savegame with station with multiple owners caused a crash (#8134, #8142) +- Fix: Stop gamelog when recovering from a savegame load error (#8133) +- Fix: Exceptionally unlikely issue when reading MIDI files (#8125) +- Fix #8119: Docking areas were not properly updated when clearing, causing desyncs (#8124, #8130) +- Fix #8117: Memory leak for incoming admin port packets (#8122) +- Fix: Non-roadbridges potentially had roadtype information set (#8111) +- Fix #8108: Possible crash on loading TTD savegames with phantom oil rigs (#8109, #8110) +- Fix #8093: Build & refit test run changed game state and could cause desyncs (#8103) +- Fix: [Script] AreWaterTilesConnected did not handle aqueducts properly (#8074) + + +### 1.10.1 (2020-04-13) + +- Fix #8081: Crash when placing a ship depot next to a dock (#8082) +- Fix: [GS] A Goal's QuestionID was getting truncated (#8072) +- Fix #8064: Refit capacity could be displayed incorrectly in extreme edgecases (#8065) +- Fix #8060: Restore admin network API compatibility (#8061) +- Fix #8055: Crash when roadtype availability changes with the road construction toolbar open (#8058) + + +### 1.10.0 (2020-04-01) + +- Change: Open company window when clicking on a company goal (#8033) +- Change: [SDL2] Support pasting from clipboard on Linux (#8004) +- Fix: [Script] Random deviation upper bound range should be inclusive (#8052) +- Fix #8043: Incorrect handling of global road/tram hotkeys caused a crash (#8044) +- Fix #8039: [Script] SetOrderFlags and GetOrderDestination didn't work for oil rigs (#8040) +- Fix: [Script] CanBuildConnectedRoadPartsHere neighbours tiles were incorrect if you started a new game with a different world size (#8036) +- Fix: Ignore clicks on non-applicable global goals (#8035) +- Fix #7613: Limit News Window to 1024 messages to keep it usable and avoid overflowing scrollbars (#8026) +- Fix #7644: [OSX] Hopefully improve performance by manually set colorspace to sRGB (#8023) +- Fix #8020: Add missing docking tiles around industry neutral stations (#8021) +- Fix: GUI tramway icon only contained a single set of tram tracks (#8015) +- Fix: Station with multiple docks had the wrong tile area (#8014) +- Fix #8011: Crash when loading TTD scenario containing a dock (#8012) +- Fix #7998: Crash when scripts tried to access companies with invalid IDs (#8010) +- Fix: Crash when attempting to draw a string containing nonprintable characters (#8005) +- Fix #6399: Directory ~/.local/share not created if it didn't already exist (#8003) +- Fix #7958: Custom catenary missing on road bridges (#7991) +- Fix #7944: Demolishing locks built on rivers didn't always restore the river (#7946) + + +### 1.10.0-RC1 (2020-02-09) + +- Feature: Allow server to supply a reason to kicked/banned clients (#7859) +- Feature: [NewGRF] Station variable 6A, querying GRFID of nearby station tiles (#7956) +- Feature: Improved logic of sharing industry production between 3 or more stations (#7922) +- Feature: Highlight the item under mouse cursor in file browser (#7900) +- Feature: [GS] Methods to change town rating of companies (#7898) +- Feature: [NewGRF] Callback profiling command (#7868) +- Feature: Add a setting to show the name of the NewGRF of a vehicle in the build window (#7852) +- Feature: Ability to filter industry directory window by cargo (#7843) +- Feature: Minimap screenshot type (#7817) +- Feature: [GS] Methods to control engine availability of a specific company (#7791) +- Feature: Configurable game ending year (#7747) +- Feature: Separate window for taking screenshots (#7550) +- Change: Move autorenew setting to basic category (#7984) +- Change: Improved algorithm for transfer feeder payments (#7935) +- Change: Show volume sliders with wedges instead of boxy slider (#7902) +- Change: Auto-restart loads the original resources (e.g. save or scenario) again (#7688) +- Change: Improve readability of integer lists saved to config files (#7396) +- Fix #7976: Crash when attempting to kick the host via rcon (#7985) +- Fix #7592: Road vehicles no longer balanced between multiple road stop stations (#7979) +- Fix: Station rating effects affecting too large area (#7977) +- Fix #7974: Crash when Ctrl+click to show a collapsed vehicle group (#7975) +- Fix #7969: Crash when executing a recursive console alias (#7973) +- Fix #6566: Very long loading of the maximum "zoom out" level at high resolutions (#7968) +- Fix #7952: Crash when switching input languages (#7953) +- Fix: [OSX] Don't show a crash/assertion message box for a GUI-less video driver (#7934) +- Fix #7925: Corrupt savegames could lead to corruption of the titlegame (#7932) +- Fix: [Fluidsynth] Music notes from previous song were not properly reset (#7930) +- Fix: Invalid string usage within music window (#7928) +- Fix: Non-deterministic name sorting in industry directory window (#7915) +- Fix #7899: Various issues with town list window sorting (#7906, #7916) +- Fix #7587: Fix possible crashes when loading old save games with invalid waypoint positions (#7894) +- Fix: Avoid a crash by properly resetting timetable duration when loading old savegames (#7894) +- Fix: Possible crash when post road-works cleanup removes all road pieces (#7903) +- Fix #7891: Fix crash when loading save from 1.7.2 (#7892) +- Fix #7887: Missing sound effects for some main toolbar buttons (#7888) +- Fix #6667: Avoid confusion by also recalculating bridge costs for 'spectated' AI companies (#7884) +- Fix: Allow old NewGRF industries to hide in/out cargo slots (#7882) +- Fix: [Windows] Fix bootstrap GUI with Uniscribe but no Freetype (#7878) +- Fix: Missing keycodes for hotkeys.cfg (#7850) +- Fix #7625: Ensure road infrastructure cost is correctly updated when upgrading your own roads (#7628) +- Fix #7088: Avoid crash by closing AI/GS textfile windows when changing their in-game slot (#7094) + + +### 1.10.0-beta2 (2019-12-25) + - Feature: [Script] More error mappings (#7857) - Feature: Ctrl+Click on a vehicle in the vehicle group window selects and scrolls to the vehicle's group (#7800) - Feature: Ctrl+Click on the vehicle details button in the vehicle view window opens the vehicle group window focused on the vehicle (#7800) @@ -1324,8 +1509,8 @@ Fix #7088: Avoid crash by closing AI/GS textfile windows when changing their in- - Fix #5405: Aircraft could route to depots outside their range (#7104) -1.10.0-beta1 (2019-10-29) ------------------------------------------------------------------------- +### 1.10.0-beta1 (2019-10-29) + - Feature: Configurable minimum age for companies before allowing share trading (#7780) - Feature: Filter on town list window (#7621) - Feature: Ability to show Newspaper and Ticker messages in parallel (#7612) @@ -1371,16 +1556,18 @@ Fix #7088: Avoid crash by closing AI/GS textfile windows when changing their in- - Remove: Original Path Finder (#7245) -1.9.3 (2019-09-16) ------------------------------------------------------------------------- +## 1.9.x + +### 1.9.3 (2019-09-16) + - Change: Use natural sort when sorting the file list (#7727) - Fix #7479: Don't close construction windows when changing client name (#7728) - Fix #7731: Files sorting by modification time on Windows XP (#7731) - Fix #7644: [OSX] Better solution for colourspace/performance issues (#7741) -1.9.3-RC1 (2019-09-07) ------------------------------------------------------------------------- +### 1.9.3-RC1 (2019-09-07) + - Add: Can now click industries to make orders to their neutral station (e.g. oil rig) (#7709) - Fix #7644: [OSX] Poor framerate on certain systems (#7721) - Fix #7702: Highscore screen UI scaling (#7714) @@ -1393,8 +1580,8 @@ Fix #7088: Avoid crash by closing AI/GS textfile windows when changing their in- - Fix #7626: Allow building road stops over town-owned one-way roads, instead of crashing (#7627) -1.9.2 (2019-07-07) ------------------------------------------------------------------------- +### 1.9.2 (2019-07-07) + - Change: Set default setting in server browser of "Advertised" to "Yes" (#7568) - Change: Allow building road stops over self-owned one-way/blocked road (#7547) - Fix #7463: Promote scroll mode setting to basic category (#7586) @@ -1419,8 +1606,8 @@ Fix #7088: Avoid crash by closing AI/GS textfile windows when changing their in- - Fix: [Windows] Various reliability and correctness improvements to MIDI on Windows (#7620) -1.9.1 (2019-04-08) ------------------------------------------------------------------------- +### 1.9.1 (2019-04-08) + - Fix #6564: Enforce types of arguments for station name strings (#7419) - Fix #7433: Don't use AirportSpec substitute if it's not enabled (#7435) - Fix #7447, #7466, #7476: Missing NewGRF strings due to Action 4 feature check skipping pseudo-feature 48 (#7449) @@ -1430,13 +1617,13 @@ Fix #7088: Avoid crash by closing AI/GS textfile windows when changing their in- - Fix #7478: Don't remove NewGRF objects on company take-over. (#7483) -1.9.0 (2019-04-01) ------------------------------------------------------------------------- +### 1.9.0 (2019-04-01) + - Fix #7411: Use industry production callback (if used) on initial industry cargo generation (#7412) -1.9.0-RC2 (2019-03-24) ------------------------------------------------------------------------- +### 1.9.0-RC2 (2019-03-24) + - Fix #7400: WaterClass for tree tiles was not converted for old saves preventing industry creation (#7405) - Fix: Filtered file list did not scroll properly (#7402) - Fix #7391: Don't invalidate go to depot orders of non-aircraft when invalidating hangar orders that happen to share IDs (#7392) @@ -1448,8 +1635,8 @@ Fix #7088: Avoid crash by closing AI/GS textfile windows when changing their in- - Fix #7334: Ship lost after crossing bridge due to path cache not being consumed while on final bridge end (#7335) -1.9.0-RC1 (2019-03-03) ------------------------------------------------------------------------- +### 1.9.0-RC1 (2019-03-03) + - Add: Various AI/GS functions for vehicle groups (#7225) - Change: Synchronise introduction date and reliability randomness across vehicles with the same base introduction date (#7147) - Change: Allow towns to build bridges over rails and one-way roads (#7291) @@ -1460,8 +1647,8 @@ Fix #7088: Avoid crash by closing AI/GS textfile windows when changing their in- - Fix: Goto hangar orders were not invalidated when rebuilding airports (#7100) -1.9.0-beta3 (2019-02-24) ------------------------------------------------------------------------- +### 1.9.0-beta3 (2019-02-24) + - Feature: Option to adjust font size separately from GUI size (#7003) - Feature: Increase maximum number of orders from 64000 to ~16.7m (#7220) - Add: Show performance of AI and GS in framerate window @@ -1487,13 +1674,13 @@ Fix #7088: Avoid crash by closing AI/GS textfile windows when changing their in- - Doc: [AI] UnshareOrders empties the orders list of the vehicle -1.9.0-beta2 (2019-02-09) ------------------------------------------------------------------------- +### 1.9.0-beta2 (2019-02-09) + - Fix: Non-Windows builds did not get correct git hash -1.9.0-beta1 (2019-02-09) ------------------------------------------------------------------------- +### 1.9.0-beta1 (2019-02-09) + Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbers have been replaced with Pull Requests and Issue numbers - Feature: Group liveries, and livery window usability enhancements (#7108) - Feature: Overhaul of music system, support MPSMIDI music files (TTD DOS/TTO) (#6839) @@ -1565,13 +1752,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Remove: PSP, WinCE support -1.8.0 (2018-04-01) ------------------------------------------------------------------------- +## 1.8.x + +### 1.8.0 (2018-04-01) + (None) -1.8.0-RC1 (2018-03-21) ------------------------------------------------------------------------- +### 1.8.0-RC1 (2018-03-21) + - Feature: [GFX] Climate-specific Action5 extra airport sprites [FS#6664] (r27976) - Feature: Draw vertical separators at tile distance in the train depot GUI (r27938, r27899) - Feature: [Build] MSVC 2017 project file generator. Most noticeable, std:c++latest is enabled (r27920, r27919, r27918, r27917) @@ -1606,13 +1795,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Road tunnel/bridge heads have no trackbits wrt. catenary drawing (r27812) -1.7.2 (2017-12-24) ------------------------------------------------------------------------- +## 1.7.x + +### 1.7.2 (2017-12-24) + (None) -1.7.2-RC1 (2017-12-11) ------------------------------------------------------------------------- +### 1.7.2-RC1 (2017-12-11) + - Change: When train depots have a horizontal scrollbar, allow scrolling 1 tile beyond the longest train, so you can actually attach a wagon at the end (r27937) - Fix: When moving wagons in the depot, the drag highlight did not exactly match the length of the dragged wagon chain (r27936) - Fix: [Win32] Right mouse scrolling didn't work properly with the Windows 10 Fall Creators Update [FS#6629] (r27935) @@ -1625,13 +1816,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Console command parser passed invalid strings to the debug output, if command lines had many parameters [FS#6576] (r27884, r27883) -1.7.1 (2017-06-13) ------------------------------------------------------------------------- +### 1.7.1 (2017-06-13) + (None) -1.7.1-RC1 (2017-05-04) ------------------------------------------------------------------------- +### 1.7.1-RC1 (2017-05-04) + - Fix: Add missing game script event for ships arriving at a station [FS#6560] (r27859, r27858) - Fix: StringID truncation to 16 bits broke string remapping test [FS#6555] (r27851) - Fix: Infinite loop in pathfinder when checking safe waiting position from a waypoint [FS#5926] (r27846) @@ -1653,13 +1844,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Allow rail conversion even if ship is on tile [FS#6505] (r27784) -1.7.0 (2017-04-01) ------------------------------------------------------------------------- +### 1.7.0 (2017-04-01) + (None) -1.7.0-RC1 (2017-03-11) ------------------------------------------------------------------------- +### 1.7.0-RC1 (2017-03-11) + - Feature: [NewGRF] Extend the DCxx range to D800-DFFF (r27769) - Feature: [NewGRF, script] Increase the maximum number of GameScript texts to 64k, and NewGRF texts to 512k (r27758) - Feature: [NewGRF] CB 37 results 0x0401 and 0x0800-0BFF for improved control of display of input cargos in the industry GUI (r27751) @@ -1697,16 +1888,18 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [Script] Kill scripts, when a non-suspendable valuator call takes way too long [FS#6473] (r27594) -1.6.1 (2016-07-01) ------------------------------------------------------------------------- +## 1.6.x + +### 1.6.1 (2016-07-01) + - Fix: Compilation and optimisation issues with GCC6 (r27606, r27605, r27595) - Fix: Compilation with --disable-network [FS#6481] (r27602) - Fix: [NewGRF] shift-and-add-divide/modulo varadjusts use signed division/modulo (r27600) - Fix: Company 0 could accept engine previews before they were offered (r27598) -1.6.1-RC1 (2016-06-01) ------------------------------------------------------------------------- +### 1.6.1-RC1 (2016-06-01) + - Feature: Mexican Spanish (r27564, r27553, r27552) - Change: Performance improvement for dedicated servers by skipping drawing calls earlier in the process [FS#6402] (r27579) - Fix: Automatic servicing of road vehicles compared path finder costs with tile distances, thus vehicles went to depots which were factor 100 too far away [FS#6410] (r27586) @@ -1731,13 +1924,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] Station spritelayouts did not accept the var10 flag for the palette [FS#6435] (r27534) -1.6.0 (2016-04-01) ------------------------------------------------------------------------- +### 1.6.0 (2016-04-01) + (None) -1.6.0-RC1 (2016-03-01) ------------------------------------------------------------------------- +### 1.6.0-RC1 (2016-03-01) + - Feature: [NewGRF] Allow custom sound IDs in RV property 0x12, ship property 0x10 and aircraft property 0x12 (r27507) - Feature: When viewing online content of a particular type, hide content of other types unless they have been (auto)selected for download (r27469, r27468, r27444) - Feature: [NewGRF] Move sprite 8 positions in sprite aligner with ctrl+click [FS#6241] (r27451) @@ -1762,13 +1955,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [Haiku] On Haiku use the appropriate system variable to obtain the include dir [FS#6401] (r27472) -1.5.3 (2015-12-01) ------------------------------------------------------------------------- +## 1.5.x + +### 1.5.3 (2015-12-01) + (None) -1.5.3-RC1 (2015-11-01) ------------------------------------------------------------------------- +### 1.5.3-RC1 (2015-11-01) + - Fix: When selecting a refit cargo for orders, do not check whether the vehicle is in a depot or station, and do not ask whether the vehicle currently allows station-refitting. Also hide the refit cost for orders, it is not predictable (r27428) - Fix: Use the NewGRF railtype sorting order in the infrastructure window (r27427) - Fix: Crash when switching to or taking over companies, when an order window of a vehicle of the new company was opened. Now close those windows [FS#5842] (r27425) @@ -1787,13 +1982,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Center settings filter warning also vertically, and also in case of multiple lines (r27365) -1.5.2 (2015-09-01) ------------------------------------------------------------------------- +### 1.5.2 (2015-09-01) + (None) -1.5.2-RC1 (2015-08-01) ------------------------------------------------------------------------- +### 1.5.2-RC1 (2015-08-01) + - Change: Auto-complete partial roads when building level-crossings [FS#6283] (r27309) - Fix: Do not rerandomise the town name when only cost-estimating the founding [FS#6332] (r27341) - Fix: Make variety distribution not assume that sea level is at height 0.2 / 3 * TGPGetMaxHeight() [FS#6335] (r27331, r27330, r27329, r27328) @@ -1810,13 +2005,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Cloning/autoreplace/autorenew did not copy custom service intervals (r27280) -1.5.1 (2015-06-01) ------------------------------------------------------------------------- +### 1.5.1 (2015-06-01) + (None) -1.5.1-RC1 (2015-05-08) ------------------------------------------------------------------------- +### 1.5.1-RC1 (2015-05-08) + - Fix: Do not consider road junctions with trivial dead ends as branch points during town growth [FS#6245] (r27260, r27259, r27244) - Fix: ScriptList::RemoveList failed to remove a list from itself [FS#6287] (r27258) - Fix: Combined button+dropdown widgets in order and autoreplace GUI had incorrect hitbox when using GUI zoom [FS#6270] (r27255) @@ -1836,13 +2031,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Draw correct overlay sprites for path reservations on bridges and tunnels (r27208) -1.5.0 (2015-04-01) ------------------------------------------------------------------------- +### 1.5.0 (2015-04-01) + - Fix: [NewGRF] Add Misc. GRF Feature Flag 6 to enable the second rocky tile set [FS#6260] (r27200) -1.5.0-RC1 (2015-03-18) ------------------------------------------------------------------------- +### 1.5.0-RC1 (2015-03-18) + - Feature: [NewGRF] Display relative offset changes in the sprite aligner [FS#6236] (r27174) - Fix: Original road vehicle acceleration crashed for vehicles taking over [FS#6255] (r27190) - Fix: GCC 5 compilation (r27185, r27183) @@ -1852,8 +2047,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Windows randomly drops SetCursorPos calls, breaking the RMB-scrolling [FS#6238] (r27172) -1.5.0-beta2 (2015-02-24) ------------------------------------------------------------------------- +### 1.5.0-beta2 (2015-02-24) + - Feature: [NoGo] Game scripts can point to a location, station, industry, or town when publishing news (r27164) - Feature: Allow changing max heightlevel in scenario editor (r27151) - Feature: Make use of both rocky tile sets from the base graphics (r27117) @@ -1881,8 +2076,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [Script] Money values would end up wrong in strings when outside the bounds of a 32 bits integer [FS#6194] (r27102) -1.5.0-beta1 (2014-12-24) ------------------------------------------------------------------------- +### 1.5.0-beta1 (2014-12-24) + - Feature: Support .txt.gz and -txt.xz changelog, readme and license files in basesets, NewGRFs, etc (r27035, r27034) - Feature: More height levels [FS#4126] (r27010) - Feature: Latin translation (r26993) @@ -1934,13 +2129,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Remove: A bunch of archaic settings from the GUI (r26528, r26526, r26525) -1.4.4 (2014-10-21) ------------------------------------------------------------------------- +## 1.4.x + +### 1.4.4 (2014-10-21) + (None) -1.4.4-RC1 (2014-10-08) ------------------------------------------------------------------------- +### 1.4.4-RC1 (2014-10-08) + - Fix: Image widgets stored 32bit SpriteID in uint16 (r26971) - Fix: Owner of road depot road types were not properly changed upon bankruptcy [FS#6126] (r26955) - Fix: Compilation on HAIKU (r26922) @@ -1953,19 +2150,19 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: The ok-button in the OSK for the signs list should just close the OSK [FS#6116] (r26827) -1.4.3 (2014-09-23) ------------------------------------------------------------------------- +### 1.4.3 (2014-09-23) + (None) -1.4.3-RC2 (2014-09-14) ------------------------------------------------------------------------- +### 1.4.3-RC2 (2014-09-14) + - Fix: Crashes on joining a server with pending order backups [FS#6112] (r26819) - Fix: Crashes on start due to dereferencing the -1 index of the file names array of music files (r26809) -1.4.3-RC1 (2014-09-07) ------------------------------------------------------------------------- +### 1.4.3-RC1 (2014-09-07) + - Fix: TC_NO_SHADE did not work for 32bpp text rendering (r26792) - Fix: Loading a game with order backups leaked Orders and left unreachable items in the pool (r26787) - Fix: Buffer overrun in SQCompiler::Error (r26764) @@ -1977,13 +2174,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Properly check for cargo acceptance of houses [FS#5997] (r26723) -1.4.2 (2014-08-16) ------------------------------------------------------------------------- +### 1.4.2 (2014-08-16) + (None) -1.4.2-RC2 (2014-08-03) ------------------------------------------------------------------------- +### 1.4.2-RC2 (2014-08-03) + - Change: Use awk instead of trying to convince cpp to preprocess nfo files (r26708) - Fix: CMD_CLEAR_ORDER_BACKUP should not be suppressed by pause modes (r26716) - Fix: [NewGRF] Parameters to SCC_NEWGRF_PUSH_WORD and SCC_NEWGRF_UNPRINT were not skipped during drawing (r26713) @@ -1996,8 +2193,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Tighten parameter bound checks on GSCargoMonitor functions, and return -1 on out-of-bound parameters (r26685) -1.4.2-RC1 (2014-07-03) ------------------------------------------------------------------------- +### 1.4.2-RC1 (2014-07-03) + - Fix: CargoPacket::SourceStation() returns a StationID (r26660) - Fix: Days in dates are not represented by ordinal numbers in all languages [FS#6047] (r26657) - Fix: Production cheat cannot be allowed to be active in multiplayer for desync reasons, even when activated in singleplayer previously [FS#6044] (r26656) @@ -2009,13 +2206,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: If the video driver fails to supply a list of resolutions, display an error message [FS#6012] (r26629) -1.4.1 (2014-06-02) ------------------------------------------------------------------------- +### 1.4.1 (2014-06-02) + - Fix: First send packages about new company, then clients joining it to admin port [FS#6025] (r26616) -1.4.1-RC2 (2014-05-18) ------------------------------------------------------------------------- +### 1.4.1-RC2 (2014-05-18) + - Fix: Save/load issues on big endian machines (r26593, r26590, r26589) - Fix: Consider multiheaded trains in station refits [FS#5995] (r26586) - Fix: Game script could be changed in game by double clicking [FS#5974] (r26583) @@ -2024,8 +2221,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [Network] Client of non-dedicated server was not correctly put into the first company for all state variables [FS#6001] (r26577) -1.4.1-RC1 (2014-05-04) ------------------------------------------------------------------------- +### 1.4.1-RC1 (2014-05-04) + - Change: Remove demand calculation based on tiles (r26484) - Change: Use pkg-config for libpng as well (r26435, r26433, r26432) - Change: Use better distance metric for link graph [FS#5941] (r26411) @@ -2051,13 +2248,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Shares button state was not appropriately updated when switching setting or company [FS#5947] (r26416) -1.4.0 (2014-04-01) ------------------------------------------------------------------------- +### 1.4.0 (2014-04-01) + (None) -1.4.0-RC1 (2014-03-18) ------------------------------------------------------------------------- +### 1.4.0-RC1 (2014-03-18) + - Feature: [Script] APIs to get cargo waiting from/via other station (r26396) - Fix: Do not explain "symmetric" cargodist mode when the setting does not allow it [FS#5939] (r26394) - Fix: Update distances between link graph nodes when station sign is moved (r26393) @@ -2067,8 +2264,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: All goal commands invalidated the goal list of company 0 [FS#5932] (r26382) -1.4.0-beta5 (2014-02-25) ------------------------------------------------------------------------- +### 1.4.0-beta5 (2014-02-25) + - Feature: Warn the user about empty setting search results, and about missing setting search results due to filtering (r26322, r26321) - Feature: [NewGRF] Extend object variable 0x60 to also return the view [FS#5696] (r26316) - Feature: Allow map sizes up to 4096x4096 (r26319) @@ -2101,8 +2298,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: The giant-screenshot confirmation window only triggered for ridiculously big screenshots, not for ludicrously big ones [FS#5899] (r26314) -1.4.0-beta4 (2014-02-06) ------------------------------------------------------------------------- +### 1.4.0-beta4 (2014-02-06) + - Change: [NewGRF] Make vehicle variable 61 return 'not available' instead of zero when using it in invalid callback contexts (r26294) - Feature: Display speed limit also for road bridges in the TileInfo window [FS#5849] (r26277) - Fix: [NoGo] Invalid DoCommand return callback for method returning bool (r26298) @@ -2113,8 +2310,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] If NewGRF provided the same station name for different industry types, stations would end up with same name. So also consider the provided name, not only the industry type (r26275) -1.4.0-beta3 (2014-01-21) ------------------------------------------------------------------------- +### 1.4.0-beta3 (2014-01-21) + - Feature: Several small performance improvements with the SSE blitters (r26260, r26259, r26256, r26255, r26254) - Feature: [NewGRF] Add StringCodes 9A 1B, 9A 1C and 9A 1D to display amounts of cargo (r26244) - Fix: Do not run into infinite recursion when getting next stopping station [FS#5865] (r26267, r26263) @@ -2126,8 +2323,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Decimal and digit separators were swapped for Korean language (r26235) -1.4.0-beta2 (2014-01-07) ------------------------------------------------------------------------- +### 1.4.0-beta2 (2014-01-07) + - Feature: Blitter autoselection is now based on full animation state, so a non-animated specialised blitter will generally be chosen when animation is turned off (r26217) - Feature: Specialised animated SSE4 blitter, and non-animated SSE4.1, SSSE3 and SSE2 blitters, improving the blitting significantly in many situations (r26214, r26213, r26212, r26211) - Feature: Specialised SSE 4.1 sprite sorter, improving the sorting performance significantly (r26205) @@ -2136,8 +2333,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Visual effects did not work for articulated RV parts (r26180) -1.4.0-beta1 (2013-12-24) ------------------------------------------------------------------------- +### 1.4.0-beta1 (2013-12-24) + - Feature: [NewGRF] Vehicle variable 4D for determining the position within an articulated vehicle (r26157) - Feature: [NewGRF] Invalidate vehicle colour palette when leaving a station [FS#5669] (r26027) - Feature: [NoGo] New goal type that show a story page when clicked (r26012) @@ -2228,20 +2425,22 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Remove: Ordered refit with subtypes, since the cases where it worked were corner cases rather than the general case [FS#3764] (r25041) -1.3.3 (2013-11-29) ------------------------------------------------------------------------- +## 1.3.x + +### 1.3.3 (2013-11-29) + - Fix: Aircraft crashing near the map's border due to a lack of airports could trigger a crash [CVE-2013-6411] [FS#5820] (r26134) -1.3.3-RC2 (2013-11-24) ------------------------------------------------------------------------- +### 1.3.3-RC2 (2013-11-24) + - Fix: [Script] Prevent scripts from crashing OpenTTD when they send text with command codes to user editable texts such as sign and station names [FS#5818] (r26093, r26092) - Fix: Occasional hanging when client joins [FS#5811] (r26043) - Fix: Multi line text was handled incorrectly causing glitches [FS#5809] (r26037, r26036) -1.3.3-RC1 (2013-11-17) ------------------------------------------------------------------------- +### 1.3.3-RC1 (2013-11-17) + - Fix: Crash when the ICU layouter thinks a font is corrupted [FS#5711] (r26029, r26018, r26017, r26016, r26015) - Fix: Make the installer warning about Windows XP SP3 not trigger on the 64 bit Windows XP which is not really Windows XP to start with [FS#5773] (r26028) - Fix: Only forward key presses to the IME system if an edit box has the input focus (r26023, r25693, r25691, r25689, r25686, r25684, r25682, r25681, r25667) @@ -2314,13 +2513,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Only the front engine's date of last service was updated [FS#5550] (r25604) -1.3.2 (2013-07-27) ------------------------------------------------------------------------- +### 1.3.2 (2013-07-27) + (None) -1.3.2-RC2 (2013-07-13) ------------------------------------------------------------------------- +### 1.3.2-RC2 (2013-07-13) + - Fix: [Admin] End-of-rcon data could not be determined reliably for any rcon command [FS#5643] (r25598, r25588, r25587) - Fix: [Content] When the server closed the connection, the client would for eternity try to read a packet and never timeout making it impossible to reconnect [FS#5635] (r25597) - Fix: [Script] Changing the script difficulty level in-game would also change the settings using the default even though they were not allowed to change in-game [FS#5644] (r25592) @@ -2342,8 +2541,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [GS] The checks and validations for setting the extra text in the town window became too stringent [FS#5625] (r25544) -1.3.2-RC1 (2013-06-30) ------------------------------------------------------------------------- +### 1.3.2-RC1 (2013-06-30) + - Remove: SETX(Y) does not work at all with other than default fonts, so get rid of it (r25454) - Fix: strndup should not examine strings beyond its upper limit [FS#5621] (r25527) - Fix: Proper support for Brahmic scripts (e.g. Tamil and Thai) [FS#5481] (r25526, r25525, r25524, r25514, r25513, r25512, r25511, r25501, r25493, r25485, r25483, r25482, r25481, r25478, r25477, r25476, r25474, r25473, r25472, r25471, r25470, r25469, r25468, r25467, r25466, r25465, r25463, r25462, r25455, r25452, r25451, r25450, r25447, r25446, r25445, r25444, r25443, r25442, r25441, r25440, r25439, r25438, r25437, r25436, r25343, r25157) @@ -2370,13 +2569,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: gcc4.6 removed -mno-cygwin option (r25266) -1.3.1 (2013-06-01) ------------------------------------------------------------------------- +### 1.3.1 (2013-06-01) + - Fix: When overbuilding a reserved track with a non-track station tile, that tile would remain reserved and eventually trigger a crash upon removal [FS#5540] (r25251) -1.3.1-RC1 (2013-05-17) ------------------------------------------------------------------------- +### 1.3.1-RC1 (2013-05-17) + - Feature: Translations of baseset descriptions via language files (r25209, r25205) - Feature: Faroese and Scottish Gaelic translations (r25198, r25176) - Feature: Plural form to be used by Scottish Gaelic (1,11; 2,12; 3..10, 13..19; other) (r25078) @@ -2408,22 +2607,22 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Pass $LDFLAGS_BUILD to all endian_check compilations (r25108) -1.3.0 (2013-04-01) ------------------------------------------------------------------------- +### 1.3.0 (2013-04-01) + - Fix: Station rebuilding could leave reserved tiles which caused crashes later on [FS#5510, FS#5516] (r25132) - Fix: When the count for a scrollbar was 0, the inter distance was subtracted too much causing a scrollbar with a negative size (r25123) -1.3.0-RC3 (2013-03-18) ------------------------------------------------------------------------- +### 1.3.0-RC3 (2013-03-18) + - Fix: Limit aircraft property 0D to 19, since the conversion result to km-ish/h needs to fit into a byte [FS#5492] (r25099) - Fix: Clicking the statusbar crashed, when news were pending but no news were shown yet [FS#5486] (r25093) - Fix: Make editbox character filters also apply to pasted content from clipboard (r25090, r25089) - Fix: Catch exception anonymously, if the exception content is not of interest [FS#5500] (r25081) -1.3.0-RC2 (2013-03-05) ------------------------------------------------------------------------- +### 1.3.0-RC2 (2013-03-05) + - Fix: Make sizes of the station preview list and direction selection identical in the station build window [FS#5472] (r25064) - Fix: When allocation of the sprite cache fails, try to allocate less memory and display an error message later on (r25061) - Fix: Refactor Script Debug GUI to only set widget states in OnInvalidateData [FS#5490] (r25052) @@ -2432,8 +2631,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Green path signals would be shown when building them 'under' a train, and they would keep showing green until they were passed again [FS#5480] (r25037) -1.3.0-RC1 (2013-02-19) ------------------------------------------------------------------------- +### 1.3.0-RC1 (2013-02-19) + - Feature: Searching of (missing) content via GrfCrawler (r25024, r25023) - Change: Cleanup goals and cargo monitors of companies when they go bankrupt or are taken over (r24986) - Change: Apply the same name sorting rules to content and NewGRF list as for the server list (r24983) @@ -2444,8 +2643,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [Script] Crash when passing too many parameters [FS#5465] (r24982, r24981, r24980) -1.3.0-beta2 (2013-02-07) ------------------------------------------------------------------------- +### 1.3.0-beta2 (2013-02-07) + - Feature: [NewGRF] Station randomisation triggers (r24906, r24905) - Feature: Settings type filter included in the advanced settings GUI (r24862, r24863) - Change: Revert to opening the vehicle GUI again when cloning vehicles using the clone-button from the depot GUI [FS#4458] (r24955) @@ -2494,8 +2693,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Extend widget data member to 32 bits so that sprite IDs >= 2^16 can be used (r24853) -1.3.0-beta1 (2012-12-24) ------------------------------------------------------------------------- +### 1.3.0-beta1 (2012-12-24) + - Feature: Advanced settings to disable certain sound effects (r24846) - Feature: [NewGRF] Support oversized purchase list sprites [FS#5271] (r24839) - Feature: Improve pylon placement around station tiles that display neither pylons nor catenary (r24836) @@ -2620,13 +2819,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Unify the spacing in 'AI/Game Script' and never just say 'Game' when 'Game Script' is meant [FS#4898] (r24020) -1.2.3 (2012-11-01) ------------------------------------------------------------------------- +## 1.2.x + +### 1.2.3 (2012-11-01) + (None) -1.2.3-RC1 (2012-10-17) ------------------------------------------------------------------------- +### 1.2.3-RC1 (2012-10-17) + - Change: [NewGRF] Set the reference brightness of 32bpp mask recolouring to 128 (r24610) - Fix: Configure script did not properly handle _BUILD flags during reconfigure (r24601) - Fix: Configure script failed to detect libfontconfig 2.10 as newer than 2.3 (r24598) @@ -2659,13 +2860,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Changing auto-refit for a 'goto station' order was inadvertently modifying the full load state [FS#5264] (r24457) -1.2.2 (2012-08-16) ------------------------------------------------------------------------- +### 1.2.2 (2012-08-16) + (None) -1.2.2-RC1 (2012-08-01) ------------------------------------------------------------------------- +### 1.2.2-RC1 (2012-08-01) + - Fix: In some cases ships could be covered with land [CVE-2012-3436] [FS#5254] (r24449, r24439) - Fix: Copy constructor and assignment operator cannot be implicit template specialisations [FS#5255] (r24448) - Fix: Make (non-refittable) vehicles with invalid default cargo unavailable [FS#5256] (r24438) @@ -2701,13 +2902,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Make the oilrig-vehicle list accessible to spectators and colour its caption neutrally grey [FS#5126] (r24260) -1.2.1 (2012-06-01) ------------------------------------------------------------------------- +### 1.2.1 (2012-06-01) + - Fix: [Script] ScriptTown::GetGrowthRate() returned wrong values after usage of SetGrowthRate() (r24302) -1.2.1-RC1 (2012-05-16) ------------------------------------------------------------------------- +### 1.2.1-RC1 (2012-05-16) + - Fix: Change the unit of the sprite-cache size setting from megabytes to megapixels, so it depends on the blitter being used. Also increase it from 64 to 128, and change the name in the cfg file, so everyone gets the new default [FS#5162] (r24252) - Fix: Do not immediately display error messages from parsing the cfg file, but schedule them for displaying after the GUI is prepared for it [FS#5154] (r24250, r24249, r24248, r24247) - Fix: Dereferencing uninitialised pointer causing a crash [FS#5159] (r24224) @@ -2742,15 +2943,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Do not redraw the text effect when nothing changed (r24066) -1.2.0 (2012-04-15) ------------------------------------------------------------------------- +### 1.2.0 (2012-04-15) + - Fix: When starting GS or AI, always use the settings of the game, not the new-game settings [FS#5142] (r24108) - Fix: Provide translated comments in the desktop file without language name postfix (r24100) - Fix: Cloning orders of aircraft with limited range failed [FS#5131] (r24086) -1.2.0-RC4 (2012-04-01) ------------------------------------------------------------------------- +### 1.2.0-RC4 (2012-04-01) + - Fix: Reversing trains while they were entering or leaving a depot could lead to stuck trains [FS#5093] (r24078, r24071) - Fix: The 'last joined' server was not properly selected anymore [FS#5098] (r24070) - Fix: Immediately start querying the last joined server instead of waiting for the requery loop [FS#5097] (r24069, r24062) @@ -2764,8 +2965,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Improve error messages for the placement restrictions of banks, water towers and toy shops [FS#5095] (r24040) -1.2.0-RC3 (2012-03-18) ------------------------------------------------------------------------- +### 1.2.0-RC3 (2012-03-18) + - Feature: Allow display of baseset textfiles (r24037) - Feature: Increase the station class limit from 32 to 256 (r24031) - Fix: After opening a text window with the monospaced font, all other text started glitching (r24038) @@ -2774,8 +2975,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Pass cases down into the list of cargoes [FS#5090] (r24024, r24023, r24022) -1.2.0-RC2 (2012-03-04) ------------------------------------------------------------------------- +### 1.2.0-RC2 (2012-03-04) + - Fix: [Script] AI used in names in API for GSOrder [FS#5088] (r24006) - Fix: Improve rounding when converting display speeds to internal speeds [FS#5079] (r23995) - Fix: Also reset the font glyph cache when switching blitters (r23992, r23987) @@ -2791,8 +2992,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Also save the maximum travel speed for the current vehicle order (r23973) -1.2.0-RC1 (2012-02-19) ------------------------------------------------------------------------- +### 1.2.0-RC1 (2012-02-19) + - Feature: [NewGRF] Customisable tunnel portals for rail types (r23952) - Feature: Timetabled maximum travel speeds for non-flying vehicles (r23947) - Feature: Readme/licence/changelog viewer for AI and game scripts [FS#5047] (r23936) @@ -2829,8 +3030,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Remove: PNG sprite loader; use 32bpp sprites in a NewGRF (r23898) -1.2.0-beta4 (2012-02-04) ------------------------------------------------------------------------- +### 1.2.0-beta4 (2012-02-04) + - Feature: [NewGRF] Give NewGRF defined level crossings and rail depots access to the townzone (r23866) - Feature: [NewGRF] New algorithm (activated via an engine flag) to determine the capacity of vehicles. This allows vehicles to better control the capacity for cargotypes which they know; and let cargo NewGRFs influence the capacity for cargoes the vehicle NewGRF does not know, but which the vehicle is refittable to due to cargo classes (r23861) - Feature: [NewGRF] Add cargo property 1D to set the capacity multipliers when refitting vehicles, which do not use callback 15 (r23860) @@ -2853,8 +3054,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: If a vehicle is not refittable to any cargo in the CTT, then pick the first refittable cargoslot (r23836) -1.2.0-beta3 (2012-01-21) ------------------------------------------------------------------------- +### 1.2.0-beta3 (2012-01-21) + - Feature: [NoGo] Allow to chose the goal question window's title from a (small) set of options [FS#4992] (r23827) - Feature: [NewGRF] Enhance some fatal NewGRF errors with the sprite number that caused the problem (r23809) - Fix: Loading empty GS strings/translations failed [FS#4996] (r23829) @@ -2877,8 +3078,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] Update all cached train properties if a train vehicle enters a new railtype (r23773) -1.2.0-beta2 (2012-01-07) ------------------------------------------------------------------------- +### 1.2.0-beta2 (2012-01-07) + - Feature: [NewGRF] Allow read-only display of NewGRF parameters, if GRF list may not be edited (r23760) - Feature: [NewGRF] Alternate rail type label list (r23758) - Feature: Make the default secondary sort method for the server list the number of clients instead of the name (r23710) @@ -2916,8 +3117,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [Script] The 'Configure' button in the 'AI / Game Configuration' window did not get enabled when activating a GameScript (r23668) -1.2.0-beta1 (2011-12-24) ------------------------------------------------------------------------- +### 1.2.0-beta1 (2011-12-24) + - Feature: Ability to run a game script; a script that controls some of the logic of the game, e.g. to implement goals or tutorials (r23637) - Feature: Allow to place locks also on river rapids and restore rivers, if locks are deleted [FS#4872] (r23512) - Feature: Aircraft ranges (r23504) @@ -3066,8 +3267,10 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Change the centre of train vehicles to depend on the vehicle length instead of being fixed at 4/8th of the original vehicle length to make sure shortened vehicles do not block tiles they should not block [FS#2379,FS#3569] (r23290) -1.1.5 (2012-01-14) ------------------------------------------------------------------------- +## 1.1.x + +### 1.1.5 (2012-01-14) + - Fix: Make default timeouts for certain network states lower and configurable [CVE-2012-0049] [FS#4955] (r23764) - Fix: Check whether a water tile is really empty when overbuilding it with an object [FS#4956] (r23763) - Fix: Missing locking causing crash in extreme case when being in the MP lobby [FS#4938] (r23752) @@ -3075,8 +3278,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Do not assume all industries that cut trees have tile (0,0) and wait until all tiles of an industry are completed before starting to cut trees (r23458) -1.1.4 (2011-12-05) ------------------------------------------------------------------------- +### 1.1.4 (2011-12-05) + - Fix: Savegames made with the Catalan town name generator would trigger a 'savegame corrupt' exception [FS#4866] (r23418) - Fix: [Network] Do not send chat messages to clients that have not joined yet [FS#4826] (r23337) - Fix: Assertion could be triggered in case a station was removed just after a vehicle delivered cargo to it [FS#4849] (r23312) @@ -3084,8 +3287,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] Prevent against writing data for unknown fonts (r23283) -1.1.4-RC1 (2011-11-20) ------------------------------------------------------------------------- +### 1.1.4-RC1 (2011-11-20) + - Fix: 3-column view of NewGRF GUI had too much space for certain font sizes (r23251) - Fix: Ignore special characters, such as the train 'character', when determining a fallback font (r23237) - Fix: [NewGRF] Make train var 0xF3 consistent with TTDPatch (r23231) @@ -3133,15 +3336,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Locks would be incorrectly assembled (r22108) -1.1.3 (2011-09-15) ------------------------------------------------------------------------- +### 1.1.3 (2011-09-15) + - Fix: Prevent authentication bypass for the admin port when a new game is started [FS#4771] (r22934) - Fix: TTO savegames with any aircraft not in an hangar caused crashes during load (r22915) - Fix: Windows 2000 and XP without service pack 3 must use the win9x binary/installer; the newer MSVC compiler of the compile farm does not support those versions of Windows anymore [FS#4749] (r22909) -1.1.3-RC1 (2011-09-04) ------------------------------------------------------------------------- +### 1.1.3-RC1 (2011-09-04) + - Add: River graphics for the original base set (r22766) - Fix: [NewGRF] DCxx text references via the text stack are not allowed, but caused crash [FS#4758] (r22882) - Fix: Harden memory allocation (r22881, r22880, r22875) @@ -3172,14 +3375,14 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NoAI] Allow AIAirport::GetNoiseLevelIncrease() also for expired airports [FS#4704] (r22710) -1.1.2 (2011-08-14) ------------------------------------------------------------------------- +### 1.1.2 (2011-08-14) + - Fix: Some corrupted savegames could crash OpenTTD instead of showing the 'savegame corrupted' message [CVE-2011-3342] [FS#4717] (r22737, r22736) - Fix: [NewGRF] Triggering NOT_REACHED when playing with a NewGRF that supplies genders/cases for a language that was not installed [FS#4718] (r22735) -1.1.2-RC2 (2011-07-30) ------------------------------------------------------------------------- +### 1.1.2-RC2 (2011-07-30) + - Fix: Cost of adding an extra road type to a bridge or tunnel was undercalculated [FS#4680, FS#4681] (r22700, r22699) - Fix: Only insert cleared object tiles into _cleared_object_areas if clearing actually succeeds, else subsequent tests of the same tile will be skipped and considered successful [FS#4694] (r22698) - Fix: When building a house it could be built at the wrong place if multitile houses failed some tests (r22697) @@ -3188,8 +3391,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] Aircraft defined with IDs above the default aircraft's always defaulted to passenger cargo (r22690) -1.1.2-RC1 (2011-07-24) ------------------------------------------------------------------------- +### 1.1.2-RC1 (2011-07-24) + - Change: [NewGRF] Only allow access (via hotkey and menu) to the bounding box visualisation when NewGRF developer tools are enabled (r22675) - Fix: [NewGRF] Disallow accessing variable 1B in network games due to desync reasons (r22682) - Fix: Switching from a red to a white highlight (by switching to another tool) without switching the highlight mode (HT_RECT etc.) did not mark the selection dirty [FS#4670] (r22649) @@ -3234,14 +3437,14 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: No client error packet was sent to the admin bots [FS#4585] (r22384) -1.1.1 (2011-06-01) ------------------------------------------------------------------------- +### 1.1.1 (2011-06-01) + - Change: Automatic orders are better called implicit orders as no real order influencing path finding is added (r22474, r22473) - Fix: Only try to insert implicit orders for ground vehicles. Aircraft may reach unscheduled terminals when skipping orders [FS#4624] (r22492) -1.1.1-RC1 (2011-05-15) ------------------------------------------------------------------------- +### 1.1.1-RC1 (2011-05-15) + - Feature: [NewGRF] Allow to filter by town of the current industry when using industry variable 0x68 [FS#4591] (r22434) - Change: Improve the speed of YAPF by tweaking hash tables size (r22351, r22350, r22348) - Change: Show one digit of the fractional train length in the depot (r22336, r22305, r22304, r22303) @@ -3279,13 +3482,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Reset Window::scrolling_scrollbar when raising scrollbar buttons [FS#4571] (r22294) -1.1.0 (2011-04-01) ------------------------------------------------------------------------- +### 1.1.0 (2011-04-01) + - Fix: In the scenario editor you could build a ship depot using the appropriate hotkey. Removing that depot causes an assertion to trigger [FS#4558] (r22266) -1.1.0-RC3 (2011-03-18) ------------------------------------------------------------------------- +### 1.1.0-RC3 (2011-03-18) + - Fix: New game settings were applied too early when starting a game via a heightmap [FS#4557] (r22259) - Fix: Do not resort town, industry and signs list directly in OnInvalidateData(). There might be a scheduled rebuild which needs execution first. So, only set a trigger for resorting [FS#4546] (r22249, r22248, r22247, r22246, r22245, r22244, r22243, r22242, r22241, r22236, r22228, r22227, r22226) - Fix: [NewGRF] Object variable 0x48 was not available in callback 0x15C (r22231) @@ -3297,8 +3500,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Invalidate the object build window when using the date cheat (r22193) -1.1.0-RC2 (2011-03-04) ------------------------------------------------------------------------- +### 1.1.0-RC2 (2011-03-04) + - Fix: Following a vehicle with a very high VehicleID was impossible (r22181) - Fix: [NewGRF] Memory leak if an industry NewGRF had more than one prop A or 15, or a station NewGRF had more than one prop 09 (r22175, r22165) - Fix: [NewGRF] Disable a station NewGRF when it contains an unterminated spritelayout in action0 prop 08 instead of crashing (r22164) @@ -3324,8 +3527,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: CanRemoveRoadWithStop() failed for _current_company = OWNER_TOWN, and for OWNER_NONE-owned road (r22117) -1.1.0-RC1 (2011-02-18) ------------------------------------------------------------------------- +### 1.1.0-RC1 (2011-02-18) + - Feature: [NewGRF] Test all possible industry layouts during construction and prospecting [FS#4131] (r22012, r22010) - Feature: Wheel scrolling in the console (r21982) - Feature: Console command to reset the engine pool. It removes the traces of engines which are no longer associated to a NewGRF, and can be used to e.g. 'fix' scenarios which were screwed up by the author. You can only use it when there are no vehicles in the game though (r21975) @@ -3377,8 +3580,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Crash when a multiplayer company goes bankrupt with 'you' in it [FS#4464] (r21970) -1.1.0-beta5 (2011-02-04) ------------------------------------------------------------------------- +### 1.1.0-beta5 (2011-02-04) + - Feature: GUI setting to disable reversing at signals (r21962) - Feature: Not loading and not unloading is now possible (r21961) - Change: [NewGRF] Disable the flipping of train engines/wagons in the depot by default for NewGRFs [FS#4462] (r21966) @@ -3412,8 +3615,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Remove: The non-uniform stations setting; it has been broken for over a year, and thus not used [FS#4456] (r21953) -1.1.0-beta4 (2011-01-21) ------------------------------------------------------------------------- +### 1.1.0-beta4 (2011-01-21) + - Feature: [NewGRF] Rail type property to influence sorting of rail types in the drop down list [FS#4394] (r21866) - Feature: [Network] Console command to change the password of other companies for servers [FS#4368] (r21855) - Feature: [NewGRF] Introduction dates/required types for rail types; e.g. introduce a particular rail type in 1960 (or when a vehicle using it is introduced), but also allow limiting its introduction to only happen when the required railtypes are available [FS#4393] (r21842) @@ -3452,8 +3655,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Remove: The 'stopall' console command, as its functionality was broken. Group start/stop commands can be used instead [FS#4409] (r21804) -1.1.0-beta3 (2011-01-09) ------------------------------------------------------------------------- +### 1.1.0-beta3 (2011-01-09) + - Feature: Configurable limit amount of tiles that can be cleared/terraformed by a company [FS#4331] (r21728) - Feature: Show a list of companies in the owner legend and allow them to be toggled for visibility (r21720, r21718) - Feature: Console command 'list_ai_libs' to get a list of recognised AI libraries [FS#4372] (r21703) @@ -3477,8 +3680,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Due to an error in the Debian changelog building of Debian/Ubuntu packages failed (r21682) -1.1.0-beta2 (2010-12-31) ------------------------------------------------------------------------- +### 1.1.0-beta2 (2010-12-31) + - Feature: Command logging using the admin interface (r21668) - Feature: Concept of automatic station orders; add stub orders for intermediate stations and remove them when not visiting them anymore. This allows you to see what trains visit a station without actually having to order a vehicle to stop at all stations (r21642) - Add: [NoAI] AIEventTownFounded (r21664) @@ -3498,8 +3701,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Crash under certain circumstances when using autorail [FS#4327] (r21619) -1.1.0-beta1 (2010-12-24) ------------------------------------------------------------------------- +### 1.1.0-beta1 (2010-12-24) + - Feature: [NewGRF] Variable 7B for accessing 60+x variables while taking the parameter from the accumulator (r21604) - Feature: Allow to refit only the selected part of a train consist (r21567) - Feature: Store the used OpenTTD version, base graphics set, NewGRFs and AIs in the PNG screenshots (r21558, r21553) @@ -3676,13 +3879,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: When building a lock, do not add the cost of building canals if they are already built, pay for clearing the other tiles and do not add the first bridge type's cost to aqueducts (r19719, r19718, r19717) -1.0.5 (2010-11-20) ------------------------------------------------------------------------- +## 1.0.x + +### 1.0.5 (2010-11-20) + (None) -1.0.5-RC2 (2010-11-14) ------------------------------------------------------------------------- +### 1.0.5-RC2 (2010-11-14) + - Fix: Reading (very) recently freed memory [CVE-2010-4168] (r21182) - Fix: Default service interval for ships/aircraft got switched [FS#4222] (r21155) - Fix: Size of sort buttons for save/load and build vehicle list gui could be too small [FS#4221] (r21151) @@ -3694,8 +3899,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] Crash when getting an industry ID at an offset that uses some 'old' style industry tile [FS#4157] (r20912) -1.0.5-RC1 (2010-10-31) ------------------------------------------------------------------------- +### 1.0.5-RC1 (2010-10-31) + - Change: Make OpenTTD aware of XZ/LZMA compressed savegames so loading those gives a proper error message (r21047) - Change: Make it possible to make .tar.xz bundles (r21042) - Fix: Missing default values for the custom town number in the world generation options (r21034) @@ -3731,14 +3936,14 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Vehicle lists of non-trains could not resize horizontally causing truncation of texts [FS#4123, FS#3955] (r20174) -1.0.4 (2010-09-14) ------------------------------------------------------------------------- +### 1.0.4 (2010-09-14) + - Change: Move removal of bin/data/opentt[dw].grf from distclean to maintainer-clean (r20752) - Fix: Recent NFORenum does not know '-?' (r20715) -1.0.4-RC1 (2010-08-30) ------------------------------------------------------------------------- +### 1.0.4-RC1 (2010-08-30) + - Change: Merge the extra GRF's sources and make it possible to rebuild them easily (r20490) - Fix: Empty NewGRF presets were not selectable [FS#4087] (r20694) - Fix: Desync checker checked the wrong variable (r20677) @@ -3793,16 +3998,16 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Inconsistencies w.r.t. to km/h vs km-ish/h as 'base' unit for aircraft speeds [FS#3870] (r20164) -1.0.3 (2010-08-01) ------------------------------------------------------------------------- +### 1.0.3 (2010-08-01) + - Fix: Make it possible to properly assess the length of the rail toolbar caption, do not require '{WHITE}' control codes (r20242) - Fix: Check for disallowed level crossings also when converting rail (r20237) - Fix: Haiku uses a 'special' location for headers (r20219) - Fix: Desync due to (temporary) wrong railtype; when loading a savegame the railtype of some (high ID) trains could be wrong [FS#3945] (r20137) -1.0.3-RC1 (2010-07-17) ------------------------------------------------------------------------- +### 1.0.3-RC1 (2010-07-17) + - Feature: [NewGRF] Textstack support for CB 38 (r20086) - Feature: [NewGRF] Add a railtype flag to disallow level crossings per railtype (r20049) - Change: Improve desync debugging, crash log data and the Debian packaging (by making a debug symbols package) (r20138, r20136, r20129) @@ -3837,8 +4042,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Under some (unlucky) circumstances the wrong company would be 'current company' when changing company colour or orders [FS#3903] -1.0.2 (2010-06-19) ------------------------------------------------------------------------- +### 1.0.2 (2010-06-19) + - Fix: Owner of the Waypoint View window was not properly set (r19990) - Fix: Close list of vehicles with given oil rig in orders when the oil rig is deleted (r19956) - Fix: Close list of vehicles with given buoy/oil rig in orders when switching company (r19955) @@ -3846,8 +4051,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Close buoy's vehicle list when the buoy is deleted [FS#3869] (r19951) -1.0.2-RC1 (2010-06-05) ------------------------------------------------------------------------- +### 1.0.2-RC1 (2010-06-05) + - Feature: Translated desktop shortcut comments (r19884) - Change: Name invalid engines, cargoes and industries 'invalid', if the player removed the supplying NewGRFs, hide invalid engines from the purchase list (r19879, r19877) - Fix: When 'pause on new game' is set, pause the game before CleanupGeneration() to avoid conflicts with concurrent GUI code [FS#3857] (r19934) @@ -3891,15 +4096,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Do not display an error message when double clicking on a vehicle in the 'available vehicles'-window (r19669) -1.0.1 (2010-05-01) ------------------------------------------------------------------------- +### 1.0.1 (2010-05-01) + - Fix: Crash when using restart via rcon (r19722) - Fix: Leaking a file descriptor [CVE-2010-0406] [FS#3785] (r19695) - Fix: Crash when the music/graphics metadata files were unreadable [FS#3774] (r19674) -1.0.1-RC2 (2010-04-22) ------------------------------------------------------------------------- +### 1.0.1-RC2 (2010-04-22) + - Fix: Desync when joining the game because of using the wrong variable (r19687) - Fix: Truncated archives were not detected when using zlib 1.2.3. This also fixes zlib 1.2.4 compatibility, zlib 1.2.5 is bug free (r19686) - Fix: Towns with 3x3 and 2x2 road layouts could not expand (r19683) @@ -3907,8 +4112,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Client status was shown incorrect in the console (r19678) -1.0.1-RC1 (2010-04-17) ------------------------------------------------------------------------- +### 1.0.1-RC1 (2010-04-17) + - Feature: [NewGRF] Support for extended text code 0x9A 11, print qword (r19570) - Feature: Give more detailed error message when trying to build a too long bridge (r19561) - Feature: Add rail speed limit to land area information window (r19556, r19434) @@ -3954,14 +4159,14 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Update: Plural type of Slovak (r19452) -1.0.0 (2010-04-01) ------------------------------------------------------------------------- +### 1.0.0 (2010-04-01) + - Fix: Network clients would crash while connecting to a server with AIs (r19526) - Fix: [NPF] Crash when finding a waypoint before finding the closest depot [FS#3703] (r19460) -1.0.0-RC3 (2010-03-18) ------------------------------------------------------------------------- +### 1.0.0-RC3 (2010-03-18) + - Feature: Append rail type speed limit (if set) to rail type selection list, and toolbar title (r19431) - Feature: [NewGRF] Smallmap colours for railtypes (r19307) - Change: Make the drive through and cargo list consistency checks only run when 'desync' debugging is enabled (r19403, r19398) @@ -3987,8 +4192,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Close error messages about missing ownership when the company closes or is taken over [FS#3663] (r19358, r19357) -1.0.0-RC2 (2010-03-04) ------------------------------------------------------------------------- +### 1.0.0-RC2 (2010-03-04) + - Feature: [YAPF] Consider the railtype imposed speed limit for pathfinding (r19301) - Feature: BaNaNaS support for music sets (r19262) - Feature: [NewGRF] Add 2 bits of pseudo-random data for rail types, based on tile location (r19235) @@ -4025,8 +4230,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Vehicle running costs should not be changed in a running game [FS#3629] (r19165) -1.0.0-RC1 (2010-02-18) ------------------------------------------------------------------------- +### 1.0.0-RC1 (2010-02-18) + - Feature: Allow to select different land colours for the smallmap (r19064) - Feature: [NewGRF] Action 3/2/1 (i.e. new graphics) support for rail types (r19056) - Feature: Add zoom-out to smallmap (r19039) @@ -4057,8 +4262,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: When removing roads, the player was also charged for removing the foundations [FS#3591] (r19016) -1.0.0-beta4 (2010-02-04) ------------------------------------------------------------------------- +### 1.0.0-beta4 (2010-02-04) + - Feature: Content mirroring support (r18994) - Feature: Show empty query after creating new group (instead of 'Group nnn') (r18981) - Feature: [NewGRF] NewGRF-settable rail type properties, increase number of possible rail types, per rail type speed limits (r18970, r18969) @@ -4096,8 +4301,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] Crash when a NewGRF used var62 in an industry tile chain when the industry tile was part of an original industry (r18878) -1.0.0-beta3 (2010-01-21) ------------------------------------------------------------------------- +### 1.0.0-beta3 (2010-01-21) + - Feature: Make building (long) roads work like building rail; build upon the first obstruction instead of failing totally [FS#3318] (r18803) - Feature: Allow user customisable compression levels for the zlib compression (r18772) - Feature: [NoAI] Rerandomise AIs on reloading (via the debug window) when they were randomly chosen [FS#3095] (r18763) @@ -4134,8 +4339,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] VarAction2Station variable 67 was not swapped properly for orientation (r18737) -1.0.0-beta2 (2010-01-05) ------------------------------------------------------------------------- +### 1.0.0-beta2 (2010-01-05) + - Feature: Do not delete the rough/rocky status of a tile when it is covered by snow, this allows rocky tiles under snow if you have a variable snowline (r18719) - Feature: [NewGRF] Add support for custom station foundation graphics (r18708) - Feature: Allow virtually paying a percentage of the leg profit in feeder chains. This to give the user a better chance to get a feeder system without 'losses' (r18703) @@ -4182,8 +4387,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: {CARGO} takes 2 parameters, not 1. This made {N:XYZ} commands after CARGO mess up their indices and that then triggered an assertion [FS#3425] (r18626) -1.0.0-beta1 (2009-12-24) ------------------------------------------------------------------------- +### 1.0.0-beta1 (2009-12-24) + - Feature: Music replacement sets, like graphics and sound replacement sets (r18608) - Feature: Add shading and unshading of windows [FS#2943] (r18588) - Feature: Initially select the last joined server when going to the server list [FS#3311] (r18578) @@ -4323,13 +4528,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Remove: Support for gcc2. It has not been able to compile OpenTTD for months. All attempts to do another workaround failed (r16492) -0.7.5 (2009-12-23) ------------------------------------------------------------------------- +## 0.7.x + +### 0.7.5 (2009-12-23) + (None) -0.7.5-RC1 (2009-12-14) ------------------------------------------------------------------------- +### 0.7.5-RC1 (2009-12-14) + - Add: Some missing latin-ish characters from the OpenGFX set (r18431) - Change: Recolour the bubble generator just like any other industry [FS#3349] (r18409) - Fix: Read after free in case no network connection could be made with the content server (r18493) @@ -4349,14 +4556,14 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Off-by-one in the preconfigured music lists [FS#3339] (r18369) -0.7.4 (2009-12-01) ------------------------------------------------------------------------- +### 0.7.4 (2009-12-01) + - Fix: Endianness issue with saving the zoom level [FS#3333] (r18351) - Fix: [NewGRF] When starting a new game the values of action D variable 13 were incorrect [FS#3324] (r18207) -0.7.4-RC1 (2009-11-15) ------------------------------------------------------------------------- +### 0.7.4-RC1 (2009-11-15) + - Change: Prefer extmidi over allegro midi and allegro over null driver [FS#3272] (r17875) - Change: [NewGRF] Apply default refitmasks only when the NewGRF did not set any of the three refittability properties (xor mask, positive classes, negative classes) (r17663) - Fix: Crash when an articulated RV is turning on a drive through road station that gets forcefully (bankrupt) removed [FS#3310] (r18049) @@ -4381,13 +4588,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Count only active clients (not those waiting for map download) when checking min_active_clients limit (r16506) -0.7.3 (2009-10-01) ------------------------------------------------------------------------- +### 0.7.3 (2009-10-01) + - Fix: [NewGRF] Crash when trying to build an industry that has no industry layout defined [FS#3233] (r17638, r17633) -0.7.3-RC2 (2009-09-24) ------------------------------------------------------------------------- +### 0.7.3-RC2 (2009-09-24) + - Update: Documentation about bug reporting and known bugs (r17554) - Fix: When a command did not fail in test run and failed in execution run, error message was not set. Affects only few commands (r17607) - Fix: [NewGRF] Crash when defining the same tile in a tile layout twice [FS#3218] (r17605) @@ -4405,8 +4612,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NoAI] Crash when doing commands in the 'global' scope [FS#3202] (r17544) -0.7.3-RC1 (2009-09-13) ------------------------------------------------------------------------- +### 0.7.3-RC1 (2009-09-13) + - Add: [NoAI] AITown::GetLastMonthTransportedPercentage and AIIndustry::GetLastMonthTransportedPercentage (r17294) - Add: [NoAI] AICompany::Get/Set PresidentGender (r17016) - Add: [NoAI] AIEngine::GetDesignDate (r17014) @@ -4478,21 +4685,21 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: The last manually added server would not be saved [FS#3062] (r16981) -0.7.2 (2009-08-01) ------------------------------------------------------------------------- +### 0.7.2 (2009-08-01) + - Fix: Vehicles would wait 'very long' when they had nothing to unload and gradual loading was disabled [FS#3054] (r16933) -0.7.2-RC2 (2009-07-21) ------------------------------------------------------------------------- +### 0.7.2-RC2 (2009-07-21) + - Fix: When marking trains stuck do not reset the unload/stuck counter when the vehicle is unloading. It will be automatically reset once the vehicle wants to leave the station [FS#3038] (r16901) - Fix: [NoAI] Small errors in the API documentation [FS#3037] (r16865) - Fix: Savegames from before 0.4 would get their waypoint 'index' messed up (r16854) - Fix: Cargo payments were not destroyed when a vehicle was destructed. This only happened when you crashed a vehicle while it was unloading [FS#3032, FS#3046] (r16801) -0.7.2-RC1 (2009-07-15) ------------------------------------------------------------------------- +### 0.7.2-RC1 (2009-07-15) + - Add: Plural 'rule' for Korean (r16811) - Add: [NoAI] AIVehicle::GetReliability to get the current reliability of vehicles (r16790) - Fix: Call the AI Save() function only once so AIs can not crash OpenTTD [FS#3034] (r16834) @@ -4530,8 +4737,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Removing of duplicates of base graphics set could behave randomly (r16548) -0.7.1 (2009-06-09) ------------------------------------------------------------------------- +### 0.7.1 (2009-06-09) + - Fix: When finding duplicate graphics sets favour the more complete one (r16538) - Fix: [Squirrel] Crash that occurred when an AI was halted while one or more generators were still in a 'running' state [FS#2942] (r16534) - Fix: [Squirrel] Do not copy an object when we just checked that the pointer to it is NULL (r16532) @@ -4543,8 +4750,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NoAI] Make sure AIBridge::BuildBridge returns what the documentation says it does (r16520) -0.7.1-RC3 (2009-06-03) ------------------------------------------------------------------------- +### 0.7.1-RC3 (2009-06-03) + - Add: [NoAI] AISignList that can be used to get a list of valid signs (r16400) - Change: [NoAI] Stop an AI when it takes too long to initialise or load [FS#2869] (r16425) - Fix: Base graphics names must be unique, so do not add duplicates (r16503) @@ -4565,8 +4772,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] Use a valid StringID as fall-back when undefined generic NewGRF strings of vehicles are requested (r16366) -0.7.1-RC2 (2009-05-21) ------------------------------------------------------------------------- +### 0.7.1-RC2 (2009-05-21) + - Fix: The previously selected NewGRF station type was still remembered after switching to a different game without newstations enabled, preventing stations from being built (r16363) - Fix: Pointer incremented with wrong count (r16361) - Fix: Delete invalid depots in TTD savegames caused by improper SVXConverter conversions (r16357) @@ -4591,8 +4798,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] Disable multitile houses for which the NewGRF does not define proper additional tiles (r16274) -0.7.1-RC1 (2009-05-11) ------------------------------------------------------------------------- +### 0.7.1-RC1 (2009-05-11) + - Add: [NoAI] AIController::GetVersion, this returns the version of OpenTTD in the same way as for NewGRFs (r16253) - Add: [NoAI] AIAirport::GetPrice, returning the building cost of an airport (r16252) - Add: [NoAI] Two new error codes to AITile: ERR_AREA_ALREADY_FLAT and ERR_EXCAVATION_WOULD_DAMAGE (r16171) @@ -4652,8 +4859,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Tooltip of detailed ratings window button showed wrong message (r15943) -0.7.0 (2009-04-01) ------------------------------------------------------------------------- +### 0.7.0 (2009-04-01) + - Feature: Watermark crash.sav and do not generate crash information if a loaded crash.sav causes a crash so the real crash report does not get overwritten (r15893) - Feature: Add autoclean_novehicles setting which will, when autoclean_companies is true, remove any company with no vehicles and no active client after autoclean_novehicles-months (r15848) - Add: [NoAI] AIIndustryType::IsBuiltOnWater(), HasHeliport() and HasDock(). Just like AIIndustry (r15901) @@ -4668,8 +4875,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Make sure house class/ID counters do not overflow (r15831) -0.7.0-RC2 (2009-03-23) ------------------------------------------------------------------------- +### 0.7.0-RC2 (2009-03-23) + - Change: [NewGRF] Expose GRF ID of engines in var action property 0x25 (r15739) - Fix: Some (newer) GCCs have trouble compiling the Windows specific part of fontcache.cpp; jumps across variable declarations [FS#2752] (r15818) - Fix: When sorting on cost do not sort on the running cost [FS#2749] (r15778) @@ -4691,8 +4898,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: When town generator failed to create requested number of towns, there were too many cities (r15744) -0.7.0-RC1 (2009-03-16) ------------------------------------------------------------------------- +### 0.7.0-RC1 (2009-03-16) + - Feature: Pop up the AI Debug Window if one of the AIs crashed and show a message that the user should report the crash [FS#2728] (r15708) - Feature: Allow the number of towns that will be generated in the generate world window to be customised [FS#2672] (r15695) - Fix: Enabling freeform edges could cause submarines to get stuck on land tiles (r15733) @@ -4710,8 +4917,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Do not mark a company as having ratings in a town when querying the cost of a command (r15662) -0.7.0-beta2 (2009-03-10) ------------------------------------------------------------------------- +### 0.7.0-beta2 (2009-03-10) + - Feature: Allow downloading scenarios and heightmaps via the in game content download (r15632) - Feature: When cloning a vehicle with a custom name, add and/or increment a number at the end of name and assign it to the new vehicle (r15621) - Feature: Show scenarios/heightmaps from both your home directory and installation directory (r15615) @@ -4767,8 +4974,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Crash when opening the game options when the currently loaded base graphics pack has less than 2 valid graphics files. For example when someone replaces all their original base graphics with custom work (but keeps the name) or renames the dos ones to windows or vice versa [FS#2630] (r15476) -0.7.0-beta1 (2009-02-16) ------------------------------------------------------------------------- +### 0.7.0-beta1 (2009-02-16) + - Feature: Make it possible to have multiple windows with edit box open simultaneously (r15424) - Feature: Add ability to select which base graphics set is used from the Game Options window. The change takes effect when the window is closed. This option can only be used from the intro menu, as reloading graphics during a game may cause issues (r15389) - Feature: Do not draw superfluous catenary wires [FS#1761] (r15347) @@ -4904,16 +5111,18 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Automatically recalculate inflation if NewGRFs are changed and cargo types are added, so that cargo payment rates are correct [FS#2074] (r13836) -0.6.3 (2008-10-01) ------------------------------------------------------------------------- +## 0.6.x + +### 0.6.3 (2008-10-01) + - Fix: NewGRF VarAction 2 variable 43 for industries saw MP_VOID tiles as land tiles and was inefficient (r14417, r14416, r14415) - Fix: Possible buffer overrun/wrong parameter type passed to printf (r14414, r14397) - Fix: Generation seed set using -G was always overwritten by -g (r14408) - Fix: Do not allow extending signals by dragging in any direction other than the track direction [FS#2202] (r14013) -0.6.3-RC1 (2008-09-22) ------------------------------------------------------------------------- +### 0.6.3-RC1 (2008-09-22) + - Fix: Invalid v->u.air.targetairport could cause crashes at several places [FS#2300] (r14383, r14344, r14343) - Fix: Moving the first vehicle of a train elsewhere might require a new unitnumber for the remaining chain which might not be available (r14384) - Fix: Trams jumping when reversing on a single trambit (like caused during road construction reworks) or when (manually) reversing in a corner [FS#1852] (r14371) @@ -4970,8 +5179,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Wrong tooltip for the industry directory's list [FS#2178] (r13917) -0.6.2 (2008-08-01) ------------------------------------------------------------------------- +### 0.6.2 (2008-08-01) + - Fix: Custom vehicle names from TTD(Patch) games were lost (r13884) - Fix: NewGRF Callback 10 (visual effect and powered wagons setting) and powered wagons operation were not performed for articulated wagons [FS#2167] (r13870) - Fix: In some cases the sprite cache could be filled with unremovable items [FS#2153] (r13869) @@ -4984,8 +5193,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: FreeType may return a bitmap glyph even if a grey-scale glyph was requested [FS#2152] (r13832) -0.6.2-RC2 (2008-07-25) ------------------------------------------------------------------------- +### 0.6.2-RC2 (2008-07-25) + - Fix: Building through the wrong side of a drive through station was allowed [FS#2166] (r13822) - Fix: Check for vehicle length changes outside a depot (callback 0x11) and give a warning about that [FS#2150] (r13816) - Fix: Several minor memory leaks. They only happened once per game (r13809, 13810) @@ -5002,8 +5211,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Enforce the length restrictions of company and president name in the commands too (r13712) -0.6.2-RC1 (2008-07-16) ------------------------------------------------------------------------- +### 0.6.2-RC1 (2008-07-16) + - Fix: Possible buffer overflow in string truncation code [CVE-2008-3576] (r13700) - Fix: Handle SETX(Y) properly when truncating a string instead of ignoring it and returning a too long string (r13699) - Fix: In some cases the (sound) mixer could overflow causing artifacts in the sound [FS#2120] (r13695) @@ -5037,14 +5246,14 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Loading of very old OpenTTD savegames was broken (r13373) -0.6.1 (2008-06-01) ------------------------------------------------------------------------- +### 0.6.1 (2008-06-01) + - Fix: Industry tiles would sometimes tell they need a 'level' slope when they do not want the slope (r13348) - Fix: Attempts to make the old AI perform better (r13217, r13221, r13222) -0.6.1-RC2 (2008-05-21) ------------------------------------------------------------------------- +### 0.6.1-RC2 (2008-05-21) + - Fix: Do not send rcon commands of the server to the first client but do directly execute those on the server (r13137) - Fix: For multiheaded engines, halve power and running cost when used instead of when loading, to allow callback values to work properly (r13074) - Fix: Loading of TTDP savegames with rivers in them [FS#2005] (r13066) @@ -5065,8 +5274,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Vehicles going twice to a depot when the automatic service interfered with the current order [FS#1985] (r12629) -0.6.1-RC1 (2008-04-26) ------------------------------------------------------------------------- +### 0.6.1-RC1 (2008-04-26) + - Fix: Vehicle groups, engine replacement rules and player/company names were not properly reset/freed after bankrupt (r12906) - Fix: Remove trams from savegames saved in OpenTTD without tram support, it is better than to simply crash [FS#1953] (r12904) - Fix: GCC on FreeBSD does not support -dumpmachine causing configure to fail. Use g++ instead [FS#1928] (r12876) @@ -5102,8 +5311,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Do not install scenarios into the current user's homedir when running 'make install', that is silly. Simply always install scenarios system wide instead (r12542) -0.6.0 (2008-04-01) ------------------------------------------------------------------------- +### 0.6.0 (2008-04-01) + - Fix: Final formatting of some string codes from NewGRFs was not done correctly [FS#1889] (r12488) - Fix: Timetable times for aircraft were always doubled [FS#1883] (r12477) - Fix: Remove broken endian-dependent code and unnecessary rgb to bgr swapping [FS#1880] (r12453) @@ -5111,8 +5320,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Plural rule for Icelandic was wrong (r12417) -0.6.0-RC1 (2008-03-26) ------------------------------------------------------------------------- +### 0.6.0-RC1 (2008-03-26) + - Feature: Show whether a town is a 'city' in the town description title bar (r12391) - Feature: Increase house animation frame number from 32 to 128 (r12347) - Fix: Loading of TTD savegames (r12399, r12401) @@ -5145,8 +5354,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Recalculate cached train data after clearing reversing flag when entering depot (r12339) -0.6.0-beta5 (2008-03-04) ------------------------------------------------------------------------- +### 0.6.0-beta5 (2008-03-04) + - Feature: Vehicle variable FE bit 5, 6 and 8 [FS#1812] (r12331, r12330) - Feature: Support loading full range of 0xD0xx NewGRF strings which includes 0xD000 to 0xD3FF (r12316) - Feature: Ability to change aircraft speed factor, from so called 'realistic' (matching other vehicles) (1/1) to original TTD speed (1/4) (r12293, r12294) @@ -5180,8 +5389,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Take into account possible loan when AI is deciding which bridge to build, so it will not build wooden bridges every time (r12184) -0.6.0-beta4 (2008-02-18) ------------------------------------------------------------------------- +### 0.6.0-beta4 (2008-02-18) + - Feature: Allow buttons to resize in NewGRF settings window (r12172) - Feature: Change colour of autorail and autoroad selection when Ctrl is pressed (r12167) - Feature: Separate catenary transparency settings from building transparency settings (r12103) @@ -5303,8 +5512,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Do not make crossing red when we cannot enter it in any case (r11870) -0.6.0-beta3 (2008-01-16) ------------------------------------------------------------------------- +### 0.6.0-beta3 (2008-01-16) + - Feature: Replaced fixed size custom name array. Names are now attached to their object directly and there is no limit to the amount of names (r11822) - Feature: Add drag-n-drop support to the raise/lower land tools. Land is raised/lowered at the start and the rest of the area levelled to match (r11759) - Feature: Add support for NewGRF's train 'tilt' flag. Trains with tilt capability (specific details are per NewGRF set) will be given a 20% speed limit bonus on curves (r11741) @@ -5350,8 +5559,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Temperate banks can only be built in towns (over a house) (r11615) -0.6.0-beta2 (2007-12-09) ------------------------------------------------------------------------- +### 0.6.0-beta2 (2007-12-09) + - Feature: Allow setting a default password for new companies in network games (r11556) - Feature: Signal selection GUI for the ones that really like to use that over CTRL (r11547) - Feature: Make the bridge selection window resizable (r11539) @@ -5394,8 +5603,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Operator priority problem resulting in problematic autoroad placement in some cases (r11466) -0.6.0-beta1 (2007-11-18) ------------------------------------------------------------------------- +### 0.6.0-beta1 (2007-11-18) + - Feature: Make news messages related to the industry (production) changes better configurable; you can now disable news messages popping up for industries you are not servicing (r11442) - Feature: When sorting stations by cargo sum, only sum the cargoes that are selected in the filter (r11437) - Feature: Show all players who have shares, not just the first two (r11435) @@ -5578,8 +5787,10 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [OPF] Signal update was incorrectly propagated (r7620) -0.5.3 (2007-09-15) ------------------------------------------------------------------------- +## 0.5.x + +### 0.5.3 (2007-09-15) + - Fix: Possible NULL pointer dereference that could be triggered remotely (r11074) - Fix: Removing CMD_AUTO from some commands could remotely trigger an assertion [FS#1179] (r11040) - Fix: Underflow that caused overflows in the performance rating [FS#1179] (r11039) @@ -5592,8 +5803,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Crash when quitting the game in one of the end score windows [FS#1218] (r11071) -0.5.3-RC3 (2007-08-30) ------------------------------------------------------------------------- +### 0.5.3-RC3 (2007-08-30) + - Fix: Spectators are not allowed to issue commands (r11006) - Fix: Make the AI not crash when it has ships as the AI does not support them [FS#1133] (r10942) - Fix: Trains would not get flooded when they are at the lower part of a tile that would become a coast tile after flooding [FS#1127] (r10892) @@ -5611,14 +5822,14 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [YAPF] 'target_seen' flag that is set prematurely in some cases (1 tile long cached segment followed by target station) which caused asserts to trigger [FS#884] (r10199) -0.5.3-RC2 (2007-07-07) ------------------------------------------------------------------------- +### 0.5.3-RC2 (2007-07-07) + - Fix: Visual glitches when a window is resized in the WE_CREATE callback (r10465) - Fix: [Windows] _wnd.has_focus was not properly set after using ALT-TAB [FS#962] (r10399) -0.5.3-RC1 (2007-06-28) ------------------------------------------------------------------------- +### 0.5.3-RC1 (2007-06-28) + - Feature: Make the client list window (for network games) stickyable (r10293) - Feature: Console command to get the current game date (r10137) - Fix: Waypoints could be renamed when you are not the owner (r10368) @@ -5663,15 +5874,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: When selling trains, if there were no wagons between multiheaded engines the rear part could be checked despite having already been deleted (10023) -0.5.2 (2007-05-29) ------------------------------------------------------------------------- +### 0.5.2 (2007-05-29) + - Feature: Add threading support for MorphOS (r9759) - Fix: Bridges and tunnels were not always removed on bankruptcy, thus leaving tunnels/bridges with an invalid owner that would crash the game when clicking with the query tool on them (r9966) - Fix: Null pointer dereference under MorphOS and AmigaOS (r9861) -0.5.2-RC1 (2007-05-16) ------------------------------------------------------------------------- +### 0.5.2-RC1 (2007-05-16) + - Feature: Windows 95/98/ME check in Windows 2000/XP/2003/Vista builds (r9834) - Feature: Add password protected status to 'players' (network server) console command (r9771) - Feature: Add server_lang in [network] section of openttd.cfg (r9716) @@ -5693,13 +5904,13 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [YAPF] The guessed path was ignored for ships [FS#736] (r9694) -0.5.1 (2007-04-20) ------------------------------------------------------------------------- +### 0.5.1 (2007-04-20) + (None) -0.5.1-RC3 (2007-04-17) ------------------------------------------------------------------------- +### 0.5.1-RC3 (2007-04-17) + - Feature: Add list_patches to console commands; shows all patches and values (r9565) - Fix: Select 'Custom' in the difficulty settings gui when changing a setting [FS#733] (r9647) - Fix: Building rail on steep slopes ignored build_on_slopes patch setting (r9602) @@ -5713,14 +5924,14 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: When deleting a vehicle which has shared orders with one more vehicle and no orders, segfaulted (r9429) -0.5.1-RC2 (2007-03-23) ------------------------------------------------------------------------- +### 0.5.1-RC2 (2007-03-23) + - Fix: Crashes when the chatbox would be drawn outside of the main window [FS#701] (r9420) - Fix: Reading out of an array caused a segmentation fault [FS#694] (r9394) -0.5.1-RC1 (2007-03-20) ------------------------------------------------------------------------- +### 0.5.1-RC1 (2007-03-20) + - Feature: Translation dependent formatting of dates (r8906) - Feature: Kick inactive initial network connections after some time [FS#115] (r9038, r9061) - Feature: Add an extra news group for opening and closing of industries (r9097) @@ -5756,8 +5967,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [NewGRF] Support for vehicle variable 48 was wrong (r8943) -0.5.0 (2007-02-27) ------------------------------------------------------------------------- +### 0.5.0 (2007-02-27) + - Feature: Add the ability to load newer TTDP games (the tile information for coasts has changed) (r8738) - Feature: Selecting 'end of orders' and deleting will delete all the vehicle's orders (shared mode unchanged) (r8685) - Codechange: Call GetFirstVehicleInChain only for trains thus increasing performance in large games (r8744) @@ -5769,8 +5980,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Loading times for overhanging trains are miscomputed (r8709) -0.5.0-RC5 (2007-02-08) ------------------------------------------------------------------------- +### 0.5.0-RC5 (2007-02-08) + - Feature: Requery gameservers that did not respond to their first query (r8520, r8542) - Feature: Logging of the IP address and port of invalid/illegal UDP packets (r8490) - Codechange: Replace missing sprites with a red question mark (r8634) @@ -5788,8 +5999,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Deleting a vehicle with shared orders, but no orders would fail to reset prev_shared and next_shared (r8294) -0.5.0-RC4 (2007-01-18) ------------------------------------------------------------------------- +### 0.5.0-RC4 (2007-01-18) + - Feature: Increase spritecache size to 2MB, will increase performance in games using NewGRF files (r8218) - Feature: OS/2 support with GCC (Watcom is dropped) (r8042) - Codechange: Add Japanese, Slovenian language as an official translation and split Norwegian into Bokmal and Nynorsk (r7987, r8084, r8069) @@ -5808,8 +6019,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Picking up en-route cargo will also have virtual profit deducted for trains as well (r8026) - Fix: Out-of-bounds read access on _clients array (harmless) (r7984) -0.5.0-RC3 (2007-01-07) ------------------------------------------------------------------------- +### 0.5.0-RC3 (2007-01-07) + - Codechange: Add Lithuanian language as an official translation (r7806) - Fix: The configure script did not work work for dash, a sh compatible shell [FS#485] (r7893) - Fix: [OSX] Control + enter no longer fullscreens, interfered with team-chat (r7886) @@ -5818,8 +6029,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [SDL] Sometimes ALT-TAB could trigger the fast forward (r7727) -0.5.0-RC2 (2006-12-31) ------------------------------------------------------------------------- +### 0.5.0-RC2 (2006-12-31) + - General Removed support for OSX older than 10.3.9. Either upgrade, or use 0.4.8 (compatible with OSX 10.2) - Codechange: Drastically reduce the CPU usage in certain cases (AI using CheckStationSpreadOut()) (r7585) - Fix: Internal bug in updating the animated_tiles table caused desyncs between (different endian) machines in MP (r7631) @@ -5835,8 +6046,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Removing rail station cost was calculated on occupied area not on number of tiles with an actual station on (r7547) -0.5.0-RC1 (2006-12-21) ------------------------------------------------------------------------- +### 0.5.0-RC1 (2006-12-21) + - General fixes and improvements to TTDPatch's NewGRF format, most noticeable are newstations, newsounds, more callbacks and I18n - Added languages: Bulgarian, Esperanto, Russian, Ukrainian, Languages with proper diacretics: Czech, Hungarian, Turkish - Feature: Show NewGRF compatibility of network games; green for full compatibility, yellow for missing NewGRFs and red for invalid revision (r7505) @@ -5994,14 +6205,16 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Remove the restriction that the 'patch' console command can only be run from network games [SF1366446] (r3723) -0.4.8 (2006-08-12) ------------------------------------------------------------------------- +## 0.4.x + +### 0.4.8 (2006-08-12) + - Fix: A ship in a depot must be stopped before it can be cloned - Fix: After changing directory in 'Play Scenario', the default scenarios did not show up in 'New Game' -0.4.8-RC2 (2006-07-31) ------------------------------------------------------------------------- +### 0.4.8-RC2 (2006-07-31) + - Feature: Add Italian town names as we have an official Italian translation - Codechange: Verify the presence of music files in the gm/ folder. This should also solve some 100% CPU buildup for some users - Fix: Certain combinations of trains crash when moved around inside the depot @@ -6013,8 +6226,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Crash when trying to build a vehicle type that is set to a max of zero -0.4.8-RC1 (2006-06-28) ------------------------------------------------------------------------- +### 0.4.8-RC1 (2006-06-28) + - Feature: Add Turkish town names as we have an official Turkish translation - Feature: Add a fully optional configure script that is a wrapper around the cumbersome makefile.config - Codechange: [NPF] Disable NPF totally for ships as it wholly kills performance. Only for 0.4/ branch and 0.4.8 @@ -6073,16 +6286,16 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Several fixes to chatbox code, mainly plug a buffer overflow -0.4.7 (2006-03-26) ------------------------------------------------------------------------- +### 0.4.7 (2006-03-26) + - Feature: [OSX] Add support for triple-binaries (PPC, PPC970, i386) (r4102) - Fix: [OSX] Crash when going to fullscreen (r4100) - Fix: Allow unused wagons to have their first cache set. Fixes faulty cache-warning message and noticeably speeds up depot operations (r4094) - Fix: [NPF] Trains & buses were unable to find a route when leaving a depot or bus stop (r4072) -0.4.6 (2006-03-22) ------------------------------------------------------------------------- +### 0.4.6 (2006-03-22) + - Codechange: [Windows] Show the revision in crash.txt and enable the button to show the crash text in the crash-window (r3965) - Codechange: Add additional linker information to release builds to help figure out crashes more easily (r3526) @@ -6117,8 +6330,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Crash in string code with openbsd/zaurus; alignment issues [SF#1415782] (r3948) -0.4.5 (2006-01-31) ------------------------------------------------------------------------- +### 0.4.5 (2006-01-31) + - Feature: [NewGRF] Implement varaction2 property 0x41 and 0xDA (r2361) - Feature: Giving server_ip a value of 'all' will make the server listen on any interface (r2374) - Feature: Shortcut CTRL + U that clears the current input-box (r2385) @@ -6269,8 +6482,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Price for demolishing a bridge was dependent on orientation and map size (r3487) -0.4.0.1 (2005-05-21) ------------------------------------------------------------------------- +### 0.4.0.1 (2005-05-21) + - Feature: Add 'clear' command and CTRL+L to empty console window - Feature: Add the possibility to print out the current debug-level @@ -6292,8 +6505,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: It was possible to open more than one tree window -0.4.0 (2005-05-15) ------------------------------------------------------------------------- +### 0.4.0 (2005-05-15) + - Feature: Bigger maps. Enjoy playing up to 2028x2048 (64 times as big as you were used to!) - Feature: New realistic acceleration; should be much better. Includes bigger penalty on narrow curves and speedlimits in depots/stations - Feature: It is now possible to build multiple road stations (up to 8) on a single station @@ -6372,8 +6585,10 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Scrolling with the arrow keys is now smooth and it now also scrolls exactly in tile direction if e.g. up and left are pressed -0.3.6 (2005-01-24) ------------------------------------------------------------------------- +## 0.3.x + +### 0.3.6 (2005-01-24) + - Feature: Resizeable windows. All useful windows are already made resizeable - Feature: Highscore chart (accessible from the difficulty window) with top 5 companies for a given difficulty (select the difficulty in the menu) - Feature: Endgame score on 1 Jan 2051 where you are added to the highscore if sufficiently large points have been accumulated. Game is paused while @@ -6498,8 +6713,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: You should no longer be able to delete bridges on any type of underground when there is a vehicle on it -0.3.5 (2004-12-24) ------------------------------------------------------------------------- +### 0.3.5 (2004-12-24) + - Feature: [Network] New network, very stable, a lot of new features - Feature: [Network] Ingame Serverlist (with online game-servers to join) - Feature: [Network] Webbased Serverlist: http://servers.openttd.org/ @@ -6582,8 +6797,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Ctrl + d bug. Longest outstanding bug has been fixed [SF#926105] -0.3.4 (2004-09-14) ------------------------------------------------------------------------- +### 0.3.4 (2004-09-14) + - Add: Dutch translation - Add: Generalised A* Algorithm - Add: Generalised queues (Fifo, Stack, InsSort, BinaryHeap) @@ -6731,8 +6946,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Vehicle depots not transparent with transparent buildings [SF#1026271] -0.3.3 (2004-07-13) ------------------------------------------------------------------------- +### 0.3.3 (2004-07-13) + - Feature: MorphOS/AmigaOS network support - Feature: Improved German town name generator - Feature: Transparent station signs @@ -6839,8 +7054,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Signal bug [SF#949929] -0.3.2.1 (2004-05-23) ------------------------------------------------------------------------- +### 0.3.2.1 (2004-05-23) + - Feature: Now builds on FreeBSD - Feature: Now builds on MorphOS - Fix: Use english.lng by default @@ -6848,8 +7063,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Czech file was missing -0.3.2 (2004-05-22) ------------------------------------------------------------------------- +### 0.3.2 (2004-05-22) + - Feature: HP for trains limited to 16bit int - Feature: Added Czech translation - Feature: Train refitting @@ -6907,8 +7122,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Fixed alignment issue in station drawing -0.3.1 (2004-04-26) ------------------------------------------------------------------------- +### 0.3.1 (2004-04-26) + - Feature: Bridge pillars for higher bridges - Feature: Remember cargo payment rates selection, default to all - Feature: Fast forward button @@ -6950,8 +7165,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Destroying things with no money -0.3.0 (2004-04-14) ------------------------------------------------------------------------- +### 0.3.0 (2004-04-14) + - Feature: Cost estimation with Shift - Feature: Added patch for starting_date, takes a value on the form yyyy, yyyymm or yyyymmdd - Feature: Support for multiheaded trains @@ -7027,8 +7242,10 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: [OSX] Altered compiler settings to make a completely stable app -0.2.1 (2004-04-04) ------------------------------------------------------------------------- +## 0.2.x + +### 0.2.1 (2004-04-04) + - Feature: 'A' hotkey now always opens autorail - Feature: X can be used to toggle transparent buildings - Feature: Hotkeys 1-9 can be used to build a bridge in the bridge window @@ -7039,8 +7256,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Installer does not delete savegames -0.2 (2004-04-03) ------------------------------------------------------------------------- +### 0.2 (2004-04-03) + - Feature: Autoscroll (only works to left/right) - Feature: Train checkpoints, instead of TTDPatch's nonstop handling - Feature: TTDPatch compatible nonstop handling @@ -7099,8 +7316,10 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Incorrect train running cost in newspaper -0.1.4 (2004-03-25) ------------------------------------------------------------------------- +## 0.1.x + +### 0.1.4 (2004-03-25) + - Feature: Crash submit system on Windows - Feature: Autosave - Feature: In-game resolution selection via settings window @@ -7139,8 +7358,8 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Screen went black when resizing -0.1.3 (2004-03-18) ------------------------------------------------------------------------- +### 0.1.3 (2004-03-18) + - Feature: Swedish town names - Feature: More currencies - Feature: Better window resizing/zooming @@ -7162,15 +7381,15 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Train slowness on hills -0.1.2 (2004-03-15) ------------------------------------------------------------------------- +### 0.1.2 (2004-03-15) + - Feature: Mouse wheel can be used to zoom in out on Windows - Feature: Implemented some support for resizing the window dynamically in Windows - Fix: Tunnel mouse icon for maglev and monorail -0.1.1 (2004-03-14) ------------------------------------------------------------------------- +### 0.1.1 (2004-03-14) + - Feature: Preliminary presignal support - Feature: Centre windows properly in higher resolutions - Feature: Command line -g flag now optionally takes a game to load @@ -7181,8 +7400,3 @@ Note: OpenTTD was migrated to GitHub for 1.9, so SVN revision and FlySpray numbe - Fix: Colours in map window for routes - Fix: Road drive side - Fix: 'Fund road construction' not clickable when unavailable - - -0.1.0 (2004-03-06) ------------------------------------------------------------------------- -Initial release diff --git a/cmake/CompileFlags.cmake b/cmake/CompileFlags.cmake index a85a915c3b..2f62a7b8b5 100644 --- a/cmake/CompileFlags.cmake +++ b/cmake/CompileFlags.cmake @@ -10,6 +10,7 @@ macro(compile_flags) if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") add_compile_options( + /Zc:preprocessor # Needed for __VA_OPT__() in macros. /MP # Enable multi-threaded compilation. /FC # Display the full path of source code files passed to the compiler in diagnostics. ) @@ -55,6 +56,11 @@ macro(compile_flags) # This flag disables the broken optimisation to work around the bug add_compile_options(/d2ssa-rse-) endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_compile_options( + -Wno-multichar + ) + endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") add_compile_options( -W @@ -74,12 +80,6 @@ macro(compile_flags) # We use 'ABCD' multichar for SaveLoad chunks identifiers -Wno-multichar - - # Compilers complains about that we break strict-aliasing. - # On most places we don't see how to fix it, and it doesn't - # break anything. So disable strict-aliasing to make the - # compiler all happy. - -fno-strict-aliasing ) # Ninja processes the output so the output from the compiler diff --git a/cmake/FindOgg.cmake b/cmake/FindOgg.cmake new file mode 100644 index 0000000000..a6487fe083 --- /dev/null +++ b/cmake/FindOgg.cmake @@ -0,0 +1,37 @@ +include(FindPackageHandleStandardArgs) + +find_library(Ogg_LIBRARY + NAMES ogg +) + +set(Ogg_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of ogg") + +set(Ogg_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of ogg") + +set(Ogg_LINK_FLAGS "" CACHE STRING "Extra link flags of ogg") + +find_path(Ogg_INCLUDE_PATH + NAMES ogg.h + PATH_SUFFIXES ogg +) + +find_package_handle_standard_args(Ogg + REQUIRED_VARS Ogg_LIBRARY Ogg_INCLUDE_PATH +) + +if (Ogg_FOUND) + set(Ogg_dirs ${Ogg_INCLUDE_PATH}) + if(EXISTS "${Ogg_INCLUDE_PATH}/ogg") + list(APPEND Ogg_dirs "${Ogg_INCLUDE_PATH}/ogg") + endif() + if (NOT TARGET Ogg::ogg) + add_library(Ogg::ogg UNKNOWN IMPORTED) + set_target_properties(Ogg::ogg PROPERTIES + IMPORTED_LOCATION "${Ogg_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${Ogg_dirs}" + INTERFACE_COMPILE_OPTIONS "${Ogg_COMPILE_OPTIONS}" + INTERFACE_LINK_LIBRARIES "${Ogg_LINK_LIBRARIES}" + INTERFACE_LINK_FLAGS "${Ogg_LINK_FLAGS}" + ) + endif() +endif() diff --git a/cmake/FindOpus.cmake b/cmake/FindOpus.cmake new file mode 100644 index 0000000000..c8ad1b48a5 --- /dev/null +++ b/cmake/FindOpus.cmake @@ -0,0 +1,37 @@ +include(FindPackageHandleStandardArgs) + +find_library(Opus_LIBRARY + NAMES opus +) + +set(Opus_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of opus") + +set(Opus_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of opus") + +set(Opus_LINK_FLAGS "" CACHE STRING "Extra link flags of opus") + +find_path(Opus_INCLUDE_PATH + NAMES opus.h + PATH_SUFFIXES opus +) + +find_package_handle_standard_args(Opus + REQUIRED_VARS Opus_LIBRARY Opus_INCLUDE_PATH +) + +if (Opus_FOUND) + set(Opus_dirs ${Opus_INCLUDE_PATH}) + if(EXISTS "${Opus_INCLUDE_PATH}/opus") + list(APPEND Opus_dirs "${Opus_INCLUDE_PATH}/opus") + endif() + if (NOT TARGET Opus::opus) + add_library(Opus::opus UNKNOWN IMPORTED) + set_target_properties(Opus::opus PROPERTIES + IMPORTED_LOCATION "${Opus_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${Opus_dirs}" + INTERFACE_COMPILE_OPTIONS "${Opus_COMPILE_OPTIONS}" + INTERFACE_LINK_LIBRARIES "${Opus_LINK_LIBRARIES}" + INTERFACE_LINK_FLAGS "${Opus_LINK_FLAGS}" + ) + endif() +endif() diff --git a/cmake/FindOpusFile.cmake b/cmake/FindOpusFile.cmake new file mode 100644 index 0000000000..8cc4a3b263 --- /dev/null +++ b/cmake/FindOpusFile.cmake @@ -0,0 +1,40 @@ +include(FindPackageHandleStandardArgs) + +find_library(OpusFile_LIBRARY + NAMES opusfile +) + +set(OpusFile_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of opusfile") + +set(OpusFile_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of opusfile") + +set(OpusFile_LINK_FLAGS "" CACHE STRING "Extra link flags of opusfile") + +find_path(OpusFile_INCLUDE_PATH + NAMES opusfile.h + PATH_SUFFIXES opus +) + +find_package_handle_standard_args(OpusFile + REQUIRED_VARS OpusFile_LIBRARY OpusFile_INCLUDE_PATH +) + +find_package(Ogg) +find_package(Opus) + +if (OpusFile_FOUND) + set(OpusFile_dirs ${OpusFile_INCLUDE_PATH}) + if(EXISTS "${OpusFile_INCLUDE_PATH}/opus") + list(APPEND OpusFile_dirs "${OpusFile_INCLUDE_PATH}/opus") + endif() + if (NOT TARGET OpusFile::opusfile) + add_library(OpusFile::opusfile UNKNOWN IMPORTED) + set_target_properties(OpusFile::opusfile PROPERTIES + IMPORTED_LOCATION "${OpusFile_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${OpusFile_dirs}" + INTERFACE_COMPILE_OPTIONS "${OpusFile_COMPILE_OPTIONS}" + INTERFACE_LINK_LIBRARIES "Ogg::ogg;Opus::opus;${OpusFile_LINK_LIBRARIES}" + INTERFACE_LINK_FLAGS "${OpusFile_LINK_FLAGS}" + ) + endif() +endif() diff --git a/cmake/InstallAndPackage.cmake b/cmake/InstallAndPackage.cmake index feb0004d85..de01597132 100644 --- a/cmake/InstallAndPackage.cmake +++ b/cmake/InstallAndPackage.cmake @@ -51,8 +51,8 @@ install(FILES ${CMAKE_SOURCE_DIR}/README.md ${CMAKE_SOURCE_DIR}/CREDITS.md ${CMAKE_SOURCE_DIR}/CONTRIBUTING.md - ${CMAKE_SOURCE_DIR}/changelog.txt - ${CMAKE_SOURCE_DIR}/known-bugs.txt + ${CMAKE_SOURCE_DIR}/changelog.md + ${CMAKE_SOURCE_DIR}/known-bugs.md DESTINATION ${DOCS_DESTINATION_DIR} COMPONENT docs) diff --git a/cmake/Options.cmake b/cmake/Options.cmake index da2d172808..38d708c717 100644 --- a/cmake/Options.cmake +++ b/cmake/Options.cmake @@ -56,7 +56,7 @@ 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" OFF) + option(OPTION_USE_ASSERTS "Use assertions; leave enabled for nightlies, betas, and RCs" ON) 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) diff --git a/cmake/PackageBundle.cmake b/cmake/PackageBundle.cmake index 737f4809aa..e737c02005 100644 --- a/cmake/PackageBundle.cmake +++ b/cmake/PackageBundle.cmake @@ -4,6 +4,7 @@ set(CPACK_BUNDLE_NAME "OpenTTD") set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/os/macosx/openttd.icns") set(CPACK_BUNDLE_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Info.plist") set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/os/macosx/splash.png") +set(CPACK_BUNDLE_APPLE_ENTITLEMENTS "${CMAKE_SOURCE_DIR}/os/macosx/openttd.entitlements") set(CPACK_DMG_FORMAT "UDBZ") # Create a temporary Info.plist.in, where we will fill in the version via diff --git a/cmake/scripts/Baseset.cmake b/cmake/scripts/Baseset.cmake index c298aea484..d822a5720c 100644 --- a/cmake/scripts/Baseset.cmake +++ b/cmake/scripts/Baseset.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) # # Create a single baseset meta file with the correct translations. diff --git a/cmake/scripts/CreateGRF.cmake b/cmake/scripts/CreateGRF.cmake index eff9a7b959..832c475799 100644 --- a/cmake/scripts/CreateGRF.cmake +++ b/cmake/scripts/CreateGRF.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) # # Create a single GRF file based on sprites/.nfo and sprites/*.png diff --git a/cmake/scripts/Desktop.cmake b/cmake/scripts/Desktop.cmake index 7cec0b9afe..7f64a60c49 100644 --- a/cmake/scripts/Desktop.cmake +++ b/cmake/scripts/Desktop.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) # # Create a desktop file with the correct translations. diff --git a/cmake/scripts/FindVersion.cmake b/cmake/scripts/FindVersion.cmake index c4ff99a7fd..f931926592 100644 --- a/cmake/scripts/FindVersion.cmake +++ b/cmake/scripts/FindVersion.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) if(NOT REV_MAJOR) set(REV_MAJOR 0) @@ -49,14 +49,14 @@ 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} ) string(REGEX REPLACE "([0-9]+)-([0-9]+)-([0-9]+).*" "\\1\\2\\3" COMMITDATE "${COMMITDATE}") set(REV_ISODATE "${COMMITDATE}") - string(SUBSTRING "${REV_ISODATE}" 0 4 REV_YEAR) # Get the branch execute_process(COMMAND ${GIT_EXECUTABLE} symbolic-ref -q HEAD @@ -113,7 +113,6 @@ elseif(EXISTS "${CMAKE_SOURCE_DIR}/.ottdrev") list(GET OTTDREV 3 REV_HASH) list(GET OTTDREV 4 REV_ISTAG) list(GET OTTDREV 5 REV_ISSTABLETAG) - list(GET OTTDREV 6 REV_YEAR) else() message(WARNING "No version detected; this build will NOT be network compatible") set(REV_VERSION "norev0000") @@ -122,14 +121,19 @@ else() set(REV_HASH "unknown") set(REV_ISTAG 0) set(REV_ISSTABLETAG 0) - set(REV_YEAR "1970") endif() +# Extract REV_YEAR and REV_DATE from REV_ISODATE +string(SUBSTRING "${REV_ISODATE}" 0 4 REV_YEAR) +string(SUBSTRING "${REV_ISODATE}" 4 4 REV_DATE) +# Drop leading 0 in REV_DATE if any +string(REGEX REPLACE "^0?([0-9]+)" "\\1" REV_DATE "${REV_DATE}") + message(STATUS "Version string: ${REV_VERSION}") if(GENERATE_OTTDREV) message(STATUS "Generating .ottdrev") - file(WRITE ${CMAKE_SOURCE_DIR}/.ottdrev "${REV_VERSION}\t${REV_ISODATE}\t${REV_MODIFIED}\t${REV_HASH}\t${REV_ISTAG}\t${REV_ISSTABLETAG}\t${REV_YEAR}\n") + file(WRITE ${CMAKE_SOURCE_DIR}/.ottdrev "${REV_VERSION}\t${REV_ISODATE}\t${REV_MODIFIED}\t${REV_HASH}\t${REV_ISTAG}\t${REV_ISSTABLETAG}\n") else() message(STATUS "Generating rev.cpp") configure_file("${CMAKE_SOURCE_DIR}/src/rev.cpp.in" diff --git a/cmake/scripts/GenerateWidget.cmake b/cmake/scripts/GenerateWidget.cmake index ffa5e6edea..4387202037 100644 --- a/cmake/scripts/GenerateWidget.cmake +++ b/cmake/scripts/GenerateWidget.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) # # CMake script to automatically generate the enums in script_window.hpp diff --git a/cmake/scripts/Regression.cmake b/cmake/scripts/Regression.cmake index e8c3c0b5a2..bfc5ee4ff4 100644 --- a/cmake/scripts/Regression.cmake +++ b/cmake/scripts/Regression.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) # # Runs a single regressoion test diff --git a/cmake/scripts/SquirrelExport.cmake b/cmake/scripts/SquirrelExport.cmake index 5116a4c9d5..2ed2130a1e 100644 --- a/cmake/scripts/SquirrelExport.cmake +++ b/cmake/scripts/SquirrelExport.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) if(NOT SCRIPT_API_SOURCE_FILE) message(FATAL_ERROR "Script needs SCRIPT_API_SOURCE_FILE defined") diff --git a/cmake/scripts/SquirrelIncludes.cmake b/cmake/scripts/SquirrelIncludes.cmake index ef81ddf041..80a77775fb 100644 --- a/cmake/scripts/SquirrelIncludes.cmake +++ b/cmake/scripts/SquirrelIncludes.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) if(NOT INCLUDES_SOURCE_FILE) message(FATAL_ERROR "Script needs INCLUDES_SOURCE_FILE defined") diff --git a/docs/admin_network.md b/docs/admin_network.md index 4703df322a..7dae8a0de8 100644 --- a/docs/admin_network.md +++ b/docs/admin_network.md @@ -1,6 +1,6 @@ # OpenTTD's admin network -Last updated: 2011-01-20 +Last updated: 2024-03-26 ## Table of contents @@ -49,10 +49,29 @@ Last updated: 2011-01-20 Create a TCP connection to the server on port 3977. The application is expected to authenticate within 10 seconds. - To authenticate send a `ADMIN_PACKET_ADMIN_JOIN` packet. + To authenticate send either an `ADMIN_PACKET_ADMIN_JOIN` or an + `ADMIN_PACKET_ADMIN_JOIN_SECURE` packet. - The server will reply with `ADMIN_PACKET_SERVER_PROTOCOL` followed directly by - `ADMIN_PACKET_SERVER_WELCOME`. + The `ADMIN_PACKET_ADMIN_JOIN` packet sends the password without any + encryption or safeguards over the connection, and as such has been disabled + by default. + + The `ADMIN_PACKET_ADMIN_JOIN_SECURE` packet initiates a key exchange + authentication schema which tells te server which methods the client + supports and the server makes a choice. The server will then send an + `ADMIN_PACKET_SERVER_AUTH_REQUEST` packet to which the client has to respond + with an `ADMIN_PACKET_ADMIN_AUTH_RESPONSE` packet. + + The current choices for secure authentication are authorized keys, where + the client has a private key and the server a list of authorized public + keys, and a so-called password-authenticated key exchange which allows to + authenticate using a password without actually sending the password. + The server falls back to password authentication when the client's key is + not in the list of authorized keys. + + When authentication has succeeded for either of the `JOIN` schemas, the + server will reply with `ADMIN_PACKET_SERVER_PROTOCOL` followed directly + by `ADMIN_PACKET_SERVER_WELCOME`. `ADMIN_PACKET_SERVER_PROTOCOL` contains details about the protocol version. It is the job of your application to check this number and decide whether diff --git a/docs/importing_town_data.md b/docs/importing_town_data.md new file mode 100644 index 0000000000..5f79d1596e --- /dev/null +++ b/docs/importing_town_data.md @@ -0,0 +1,97 @@ +# Importing Town Data into OpenTTD + +To aid players in scenario creation, OpenTTD's Scenario Editor can import town data from external JSON files. This enables players to use an image editing program to align town coordinates with a real-world heightmap using a map underlay, instead of guessing at the correct locations in Scenario Editor itself. + +This town data consists of a JSON file storing an array of town data objects, each containing a name, location, target OpenTTD population, and whether it is marked as a city in the game. + +This document describes the standard format for this JSON file and outlines a workflow for creating this data effectively. + +## Table of contents + +- Why load external data? +- How to use this feature + - Creating geodata + - Town data format standards + - Town data values + - Loading geodata into OpenTTD +- Tutorial: Creating town data + +## Why load external data? + +There are three benefits to using an image editing program to create towns instead of the OpenTTD Scenario Editor. + +1. Placing towns accurately is much easier using a map underlay such as OpenStreetMap to match town locations with the corresponding heightmap. +2. Storing town data in a JSON file instead of as an OpenTTD Scenario (.scn) doesn't require choosing your NewGRF house set before placing towns. +3. Town coordinates are scaled by the map size, so you can load the data onto whatever size map you like. + +## How to use this feature + +### Creating geodata + +Town data is a text file in the JSON format, with a list of towns, each containing a coordinate location and properties: name, population, and whether or not it should be a city in OpenTTD. + +The format of this file is standardized for importing into OpenTTD and must be followed for OpenTTD to properly parse the data. + +For use in OpenTTD, you will also need a matching heightmap of the terrain features, as a PNG. + +#### Town data format standards + +The following code sample is complete and can be used in OpenTTD. +- The list of towns is enclosed in an array marked with square brackets `[]` +- Each town is enclosed in curly braces `{}`, with a comma after each town except for the last in the list. +- The properties separated by commas except for the last. +- Property names are enclosed in double quotes `""` with a colon `:` separating it from the property value. +- The name property value is enclosed in double quotes `"London"`, while all other property values `44910`, `true`, etc., are not. + +``` +[ + { + "name": "London", + "population": 44910, + "city": true, + "x": 0.7998046875, + "y": 0.708984375 + }, + { + "name": "Canterbury", + "population": 217.16, + "city": false, + "x": 0.83251953125, + "y": 0.828125 + } +] +``` + +#### Town data values + +- Population is scaled down for use in OpenTTD. It is possible to generate huge cities by using a large number, but there is a practical limit to town size. The larger the town, the longer it will take to import town data, since towns are placed at a relatively small size and then expanded until the population is greater than the player-defined target. +- X and Y coordinates are a proportion of the total map dimension, between 0 and 1. Just take the pixel coordinates of the town's location in the corresponding heightmap (more detail in the tutorial below) and divide each by the maximum value. + - For example, London is at `726, 1638` in a 1024 px by 2048 px heightmap, so `726 / 1024 = 0.7998046875` and `1638 / 2048 = 0.708984375` gives the correct coordinates for OpenTTD. + - The reason for these proportional coordinates is so the data can be used for any map size. +- 0,0 is (approximately) the very top tile in OpenTTD. You can see tile coordinates in-game with the Land Info Tool. +- In most image editing programs, 0,0 is the top-left corner of the image. You can rotate the image however you want relative to compass north to orient the map to your liking. Make sure you crop and resize the image before recording town locations. +- In OpenTTD, X and Y axis are swapped compared to most image editing programs and the standard Cartesian coordinate system. From the 0,0 origin at top left, X is the axis along the left side and Y is the axis along the right side. You can still measure X and Y coordinates in your image editing program, just swap them before importing into OpenTTD or towns won't line up with your heightmap. + +### Loading geodata into OpenTTD +Using geodata to create a real-world location in OpenTTD is done in the Scenario Editor. + +1. Choose the NewGRFs you want to use in the game. +2. Load the heightmap which you created in the geodata workflow. Either rotation will work, but the clockwise rotation is considered "correct" and the coordinates in the Land Info Tool will match your data; counter clockwise maps will align properly but the coordinates won't match your data. +3. In the Town Generation window, click `Load from file` and choose the .json file containing town data. The default directory to search for town data is `OpenTTD\scenario\heightmap`. +4. (Optional) Manually add industries, rivers, trees, and objects. +5. Save the game as a Scenario and exit to the main menu. +6. Load the game with Play Scenario and enjoy. + +Sometimes it's not possible to place a town, such as when the heightmap is very rough and a flat tile can't be found with a 16-tile radius of the target tile. In such cases, a sign will be placed on the target tile with the name of the town. The player can then place the town manually or change the heightmap settings and try again. This fallback also helps debug errors with data creation, such as if towns end up in the ocean. + +## Tutorial: Creating town data + +1. Load both your heightmap and a labeled map like OpenStreetMap as layers in an image editing program. You can use a free/open-source program like QGIS to acquire, align, and export these map images, if you like. +2. Crop the image to your desired bounds, ensuring the aspect ratio is supported in OpenTTD (1:1, 1:2, 1:4, etc.). +3. Resize the image to one of OpenTTD's supported map sizes, such as 512 px by 1024 px. Some image editors let you do this part of step 2. You can always load heightmaps and town data at a reduced size, so you may want to make this larger than your intended use in case you want it later. +4. Use the labeled map layer to find the pixel coordinates of each town you'd like to include in your map. In GIMP this is displayed in the bottom left corner of the image window, and in Photoshop you need to enable the Info panel (F8) and switch to pixel units of measurement if not already. +5. Some spreadsheets including Google Sheets can export data as JSON, so you may want to record it there, to export after step 8. Or you can build the JSON file manually. +6. Adjust population numbers for OpenTTD. +7. Change coordinates from pixels to proportion (0-1) of the total dimension: `x / maximum_x` and `y / maximum_y`, as described in "Town data values" above. +8. Swap X and Y coordinates before importing to OpenTTD, since OpenTTD uses a reverse X and Y system than most image editors. +9. Save the heightmap and town data files in your `OpenTTD\scenario\heightmap` folder. diff --git a/docs/landscape.html b/docs/landscape.html index f592cd0f6a..ab918a77ff 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -721,10 +721,9 @@ -
  • m3 bit 6 : bit 8 of house type (m4), allowing 512 different types.
  • -
  • m3 bit 5 : free
  • +
  • m3 bits 6..5 : free
  • m3 bits 4..0 : triggers activated (newhouses)
  • -
  • m4 : town building type (with m3[6] bit)
  • +
  • m4 : free
  • m5 : see m3 bit 7
  • m6 :
      @@ -740,6 +739,7 @@
  • +
  • m6: bits 1..0: animated tile state
  • m7 :
    • If newhouses is activated @@ -759,10 +759,12 @@
  • +
  • m8 bits 15..12 : free
  • +
  • m8 bits 11..0 : town building type
  • Newhouses is the name englobing a newGRF feature developed by TTDPatch devs (mainly Csaboka).
    It allows the replacement of the properties as well as the graphics of houses in the game.
    - To distinguish between the standard behaviour and the newGRF one, HouseID (m4 + m3[6]) is tested for anything above 110.
    + To distinguish between the standard behaviour and the newGRF one, HouseID is tested for anything above 110.
    110 is the count of standard houses. So above 110 means there is a new definition of at least one house
    @@ -876,6 +878,25 @@
  • m2: index into the array of stations
  • m3 bits 7..4: persistent random data for railway stations/waypoints and airports)
  • m3 bits 7..4: owner of tram tracks (road stop)
  • +
  • m3 bits 3..2: ground type (road waypoints) + + + + + + + + + + + + + +
    0  on bare land
    1  on grass
    2  paved
    +
  • +
  • m3 bit 2: rail station / waypoint may have catenary pylons
  • +
  • m3 bit 1: rail station / waypoint may have catenary wires
  • +
  • m3 bit 0: rail station / waypoint is blocked
  • m4: custom station id; 0 means standard graphics
  • m4: Roadtype for road stops
  • m5: graphics index (range from 0..255 for each station type): @@ -956,6 +977,22 @@ + + 04..05  + road waypoints + + + + + + + + + +
    04  drive through X
    05  drive through Y
    + + + 00..05  ship dock @@ -989,17 +1026,16 @@
  • -
  • m6 bit 7: rail station / waypoint may have catenary pylons
  • -
  • m6 bit 6: rail station / waypoint may have catenary wires
  • -
  • m6 bits 5..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint)
  • +
  • m6 bits 6..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy, waypoint, road waypoint)
  • m6 bit 2: pbs reservation state for railway stations/waypoints
  • -
  • m6 bit 0: rail station / waypoint is blocked
  • +
  • m6 bits 1..0: animated tile state
  • m7 bits 4..0: owner of road (road stops)
  • m7: animation frame (railway stations/waypoints, airports)
  • +
  • m8 bit 15: Snow or desert present (road waypoints)
  • m8 bits 11..6: Tramtype
  • m8 bits 5..0: track type for railway stations/waypoints
  • -
  • m8 bits 5..0: custom road stop id; 0 means standard graphics
  • +
  • m8 bits 5..0: custom road stop/waypoint id; 0 means standard graphics
  • @@ -1016,99 +1052,94 @@
  • m1 bits 6..5 : Water class (sea, canal or river)
  • m1 bits 4..0: owner (for sea, rivers, and coasts normally 11)
  • m2: Depot index (for depots only)
  • +
  • m3 bit 0: Non-flooding state
  • m4: Random data for canal or river tiles
  • -
  • m5: tile type: +
  • m5 bits 7..4: Water tile type: - - - - - - - - - - - - - - - - - - - -
    00  water, canal or river
    01  coast or riverbank
    10..1B  canal locks - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    10  middle part, (SW-NE direction)
    11  middle part, (NW-SE direction)
    12  middle part, (NE-SW direction)
    13  middle part, (SE-NW direction)
    14  lower part, (SW-NE direction)
    15  lower part, (NW-SE direction)
    16  lower part, (NE-SW direction)
    17  lower part, (SE-NW direction)
    18  upper part, (SW-NE direction)
    19  upper part, (NW-SE direction)
    1A  upper part, (NE-SW direction)
    1B  upper part, (SE-NW direction)
    -
    80..83  ship depots - - - - - - - - - - - - - - - - - -
    80  ship depot, NE part (X direction)
    81  ship depot, SW part (X direction)
    82  ship depot, NW part (Y direction)
    83  ship depot, SE part (Y direction)
    -
    + + 0  + water, canal or river + + + 1  + coast or riverbank + + + 2  + canal lock
    +
      +
    • m5 bits 3..2: Lock part + + + + + + + + + + + + + +
      0  Middle part
      1  Lower part
      2  Upper part
      +
    • +
    • m5 bits 1..0: Lock direction + + + + + + + + + + + + + + + + + +
      0  NE raised
      1  SE raised
      2  SW raised
      3  NW raised
      +
    • +
    + + + + 3  + depot
    +
      +
    • m5 bit 1: Depot axis + + + + + + + + + +
      0  X direction (NE-SW)
      1  Y direction (NW-SE)
      +
    • +
    • m5 bit 0: Depot part + + + + + + + + + +
      0  North part
      1  South part
      +
    • +
    + + + +
  • @@ -1451,6 +1482,7 @@
  • m6 bits 5..3: random triggers (NewGRF)
  • m6 bit 2: bit 8 of type (see m5)
  • +
  • m6 bits 1..0: animated tile state
  • m7: animation frame
  • @@ -1623,6 +1655,7 @@
  • m2: index into the array of objects, bits 0 to 15 (upper bits in m5)
  • m3: random bits
  • m5: index into the array of objects, bits 16 to 23 (lower bits in m2)
  • +
  • m6 bits 1..0: animated tile state
  • m7: animation counter
  • diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index a0fa527a6b..b2ee8293e2 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -79,8 +79,8 @@ the array so you can quickly see what is used and what is not. 0 ground - XXXX XX XX - XXXX XXXX + XXXX XX XX + XXXX XXXX OOO1 OOOO OOOO OOOO OOOO OOOO XXX XOOOO @@ -130,7 +130,7 @@ the array so you can quickly see what is used and what is not. OO XX XXXX OO XXXOOO OOX OXXXX - OOOO XXXX XXOO OOOO + OOOO XXXX XXOO OOOO level crossing @@ -156,17 +156,17 @@ the array so you can quickly see what is used and what is not. finished house XXXX XXXX XXXX XXXX XXXX XXXX - 1 XOXXXXX - XXXX XXXX + 1OOX XXXX + OOOO OOOO XXXX XXXX - XXXX XXOO + XXXX XXXX XXXX XXXX - OOOO OOOO OOOO OOOO + OOOO XXXX XXXX XXXX house under construction - O XOXXXXX - OOOXX XXX + OXOX XXXX + OOOX XXXX 4 @@ -181,14 +181,14 @@ the array so you can quickly see what is used and what is not. OOOO OOOO OOOO OOOO - 5 + 5 rail station - OXX XXXXX - XXXX XXXX XXXX XXXX - XXXX OOOO + OXX XXXXX + XXXX XXXX XXXX XXXX + XXXX OXXX XXXX XXXX XXXX XXXX - XXXXX XOX + OXXX XXXX XXXX XXXX OOOO OOOO OOXX XXXX @@ -199,12 +199,17 @@ the array so you can quickly see what is used and what is not. road stop XXXX OOOO - OOXX XXXX - OOOO OXXX - OOXX XOOO - OOOX XXXX + OOXX XXXX + OOOO OXXX + OXXX XOXX + OOOX XXXX OOOO XXXX XX XXXXXX + + road waypoint + XXXX XXOO + XOOO XXXX XX XXXXXX + airport XXXX OOOO @@ -235,32 +240,37 @@ the array so you can quickly see what is used and what is not. OOOO OOOO - 6 - sea, shore - X XX XXXXX - OOOO OOOO OOOO OOOO - OOOO OOOO - OOOO OOOO - OOOO OOOX - OOOO OOOO - OOOO OOOO + 6 + sea + X XX XXXXX OOOO OOOO OOOO OOOO + OOOO OOOX + OOOO OOOO + 0000 OOO0 + OOOO OOOO + OOOO OOOO + OOOO OOOO OOOO OOOO canal, river XXXX XXXX - OOOO OOOO + 0000 OOOO + + + shore + OOOO OOOO + 0001 OOOO lock OOOO OOOO - OOO1 XX XX + 0010 XXXX shipdepot XXXX XXXX XXXX XXXX OOOO OOOO - 1OOO OOX X + 0011 OOXX 8 @@ -270,7 +280,7 @@ the array so you can quickly see what is used and what is not. XXXX XXXX XXXX XXXX XXXX XXXX - OOXXX XOO + OOXXX XXX XXXX XXXX OOOO OOOO OOOO OOOO @@ -303,7 +313,7 @@ the array so you can quickly see what is used and what is not. XXXX XXXX OOOO OOOO XXXX XXXX - OOOO OOOO + OOOO OOXX XXXX XXXX OOOO OOOO OOOO OOOO diff --git a/docs/openttd.6 b/docs/openttd.6 index 0319f98cc8..9e8ff63b34 100644 --- a/docs/openttd.6 +++ b/docs/openttd.6 @@ -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 diff --git a/docs/releasing_openttd.md b/docs/releasing_openttd.md index acb3ad9bc7..d0cc591b65 100644 --- a/docs/releasing_openttd.md +++ b/docs/releasing_openttd.md @@ -7,18 +7,20 @@ 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. +1. Update the [changelog](../changelog.md) with new changes since the last release. * Changelog entries are typically PR titles, but can be edited to be more helpful without context. * Don't include fixes to things which haven't previously been released (like fixes to features which are in the same changelog). * Order the entries by importance: `Feature > Add > Change > Fix`, then numerically by PR number. @@ -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. diff --git a/known-bugs.md b/known-bugs.md new file mode 100644 index 0000000000..b50fff766d --- /dev/null +++ b/known-bugs.md @@ -0,0 +1,426 @@ +# OpenTTD's known bugs + +## Table of contents + +- 1.0) About +- 2.0) Known bugs + +## 1.0) About + +All bugs listed below are marked as known. Please do not submit any bugs +that are the same as these. If you do, do not act surprised, because +we WILL flame you! + +The current list of known bugs that we intend to fix can be found in our +bug tracking system at https://github.com/OpenTTD/OpenTTD/issues +Also check the closed bugs when searching for your bug in this system as we +might have fixed the bug in the mean time. + +## 2.0) Known bugs + +This section lists all known bugs that we do not intend to fix and the +reasons why we think that fixing them is infeasible. We might make some +minor improvements that reduce the scope of these bugs, but we will not +be able to completely fix them. + +### No suitable AI can be found: + +If you have no AIs and an AI is started the so-called 'dummy' AI will +be loaded. This AI does nothing but writing a message on the AI debug +window and showing a red warning. There are basically two solutions +for this problem: Either you set the number of AI players to 0 so that +no AI is started. You find that setting at the top of the window in the +"AI / Game Scripts Settings" window. + +The other solution is acquiring (downloading) some AI. The easiest way +to do this is via the "Check Online Content" button in the main (intro) +menu or directly in the "AI / Game Scripts Settings" dialogue via the +"Check Online Content" button. + +### After a while of playing, colours get corrupted: + +In Windows 7 the background slideshow corrupts the colour mapping +of OpenTTD's 8bpp screen modes. Workarounds for this are: + +* Switching to windowed mode, instead of fullscreen +* Switching off background slideshow +* Setting up the `32bpp-anim` or `32bpp-optimized` blitter + +### Custom vehicle type name is incorrectly aligned: + +Some NewGRFs use sprites that are bigger than normal in the "buy +vehicle" window. Due to this they have to encode an offset for +the vehicle type name. Upon renaming the vehicle type this encoded +offset is stripped from the name because the "edit box" cannot show +this encoding. As a result the custom vehicle type names will get +the default alignment. The only way to (partially) fix this is by +adding spaces to the custom name. + +### Clipping problems [#119]: + +In some cases sprites are not drawn as one would expect. Examples of +this are aircraft that might be hidden below the runway or trees that +in some cases are rendered over vehicles. + +The primary cause of this problem is that OpenTTD does not have enough +data (like a 3D model) to properly determine what needs to be drawn in +front of what. OpenTTD has bounding boxes but in lots of cases they +are either too big or too small and then cause problems with what +needs to be drawn in front of what. Also some visual tricks are used. + +For example trains at 8 pixels high, the catenary needs to be drawn +above that. When you want to draw bridges on top of that, which are +only one height level (= 8 pixels) higher, you are getting into some +big problems. + +We can not change the height levels; it would require us to either +redraw all vehicle or all landscape graphics. Doing so would mean we +leave the Transport Tycoon graphics, which in effect means OpenTTD +will not be a Transport Tycoon clone anymore. + +### Mouse scrolling not possible at the edges of the screen [#383] [#3966]: + +Scrolling the viewport with the mouse cursor at the edges of the screen +in the same direction of the edge will fail. If the cursor is near the +edge the scrolling will be very slow. + +OpenTTD only receives cursor position updates when the cursor is inside +OpenTTD's window. It is not told how far you have moved the cursor +outside of OpenTTD's window. + +### Lost trains ignore (block) exit signals [#1473]: + +If trains are lost they ignore block exit signals, blocking junctions +with presignals. This is caused because the path finders cannot tell +where the train needs to go. As such a random direction is chosen at +each junction. This causes the trains to occasionally to make choices +that are unwanted from a player's point of view. + +This will not be fixed because lost trains are in almost all cases a +network problem, e.g. a train can never reach a specific place. This +makes the impact of fixing the bug enormously small against the amount +of work needed to write a system that prevents the lost trains from +taking the wrong direction. + +### Vehicle owner of last transfer leg gets paid for all [#2427]: + +When you make a transfer system that switches vehicle owners. This +is only possible with 'industry stations', e.g. the oil rig station +the owner of the vehicle that does the final delivery gets paid for +the whole trip. It is not shared amongst the different vehicle +owners that have participated in transporting the cargo. + +This sharing is not done because it would enormously increase the +memory and CPU usage in big games for something that is happening +in only one corner case. We think it is not worth the effort until +sharing of stations is an official feature. + +### Forbid 90 degree turns does not work for crossing PBS paths [#2737]: + +When you run a train through itself on a X junction with PBS turned on +the train will not obey the 'forbid 90 degree turns' setting. This is +due to the fact that we can not be sure that the setting was turned +off when the track was reserved, which means that we assume it was +turned on and that the setting does not hold at the time. We made it +this way to allow one to change the setting in-game, but it breaks +slightly when you are running your train through itself. Running a +train through means that your network is broken and is thus a user +error which OpenTTD tries to graciously handle. + +Fixing this bug means that we need to record whether this particular +setting was turned on or off at the time the reservation was made. This +means adding quite a bit of data to the savegame for solving an issue +that is basically an user error. We think it is not worth the effort. + +### Duplicate (station) names after renaming [#3204]: + +After renaming stations one can create duplicate station names. This +is done giving a station the same custom name as another station with +an automatically generated name. + +The major part of this problem is that station names are translatable. +Meaning that a station is called e.g. ' Central' in English and +' Centraal' in Dutch. This means that in network games the +renaming of a town could cause the rename to succeed on some clients +and fail at others. This creates an inconsistent game state that will +be seen as a 'desync'. Secondly the custom names are intended to fall +completely outside of the ' ' naming of stations, so when +you rename a town all station names are updated accordingly. + +As a result the decision has been made that all custom names are only +compared to the other custom names in the same class and not compared +to the automatically generated names. + +### Extreme CPU usage/hangs when using SDL and PulseAudio [#3294], OpenTTD hangs/freezes when closing, OpenTTD is slow, OpenTTD uses a lot of CPU: + +OpenTTD can be extremely slow/use a lot of CPU when the sound is +played via SDL and then through PulseAudio's ALSA wrapper. Under the +same configuration OpenTTD, or rather SDL, might hang when exiting +the game. This problem is seen most in Ubuntu 9.04 and higher. + +This is because recent versions of the PulseAudio sound server +are configured to use timer-based audio scheduling rather than +interrupt-based audio scheduling. Configuring PulseAudio to force +use of interrupt-based scheduling may resolve sound problems for +some users. Under recent versions of Ubuntu Linux (9.04 and higher) +this can be accomplished by changing the following line in the +`/etc/pulse/default.pa` file: + `load-module module-udev-detect` +to + `load-module module-udev-detect tsched=0` + +Note that PulseAudio must be restarted for changes to take effect. Older +versions of PulseAudio may use the module-hal-detect module instead. +Adding tsched=0 to the end of that line will have a similar effect. + +Another possible solution is selecting the "pulse" backend of SDL +by either using `SDL_AUDIODRIVER=pulse openttd` at the command +prompt or installing the `libsdl1.2debian-pulseaudio` package from +Ubuntu's Universe repository. For other distributions a similar +package needs to be installed. + +### OpenTTD not properly resizing with SDL on X [#3305]: + +Under some X window managers OpenTTD's window does not properly +resize. You will either end up with a black bar at the right/bottom +side of the window or you cannot see the right/bottom of the window, +e.g. you cannot see the status bar. The problem is that OpenTTD does +not always receive a resize event from SDL making it impossible for +OpenTTD to know that the window was resized; sometimes moving the +window will solve the problem. + +Window managers that are known to exhibit this behaviour are GNOME's +and KDE's. With the XFCE's and LXDE's window managers the resize +event is sent when the user releases the mouse. + +### Incorrect colours, crashes upon exit, debug warnings and smears upon window resizing with SDL on macOS [#3447]: + +Video handling with (lib)SDL under macOS is known to fail on some +versions of macOS with some hardware configurations. Some of +the problems happen only under some circumstances whereas others +are always present. + +We suggest that the SDL video/sound backend is not used for OpenTTD +in combinations with macOS. + +### Train crashes entering same junction from block and path signals [#3928]: + +When a train has reserved a path from a path signal to a two way +block signal and the reservation passes a path signal through the +back another train can enter the reserved path (only) via that +same two way block signal. + +The reason for this has to do with optimisation; to fix this issue +the signal update has to pass all path signals until it finds either +a train or a backwards facing signal. This is a very expensive task. + +The (signal) setups that allow these crashes can furthermore be +considered incorrectly signalled; one extra safe waiting point for +the train entering from path signal just after the backwards facing +signal (from the path signal train) resolves the issue. + +### Crashes when run in a VM using Parallels Desktop [#4003]: + +When the Windows version of OpenTTD is executed in a VM under +Parallels Desktop a privileged instruction exception may be thrown. + +As OpenTTD works natively on macOS as well as natively on Windows and +these native builds both don't exhibit this behaviour this crash is +most likely due to a bug in the virtual machine, something out of +the scope of OpenTTD. Most likely this is due to Parallels Desktop +lacking support for RDTSC calls. The problem can be avoided by using +other VM-software, Wine, or running natively on macOS. + +### Entry- and exit signals are not dragged [#4378]: + +Unlike all other signal types, the entry- and exit signals are not +dragged but instead normal signals are placed on subsequent track +sections. This is done on purpose as this is the usually more +convenient solution. There are little to no occasions where more +than one entry or exit signal in a row are useful. This is different +for all other signal types where several in a row can serve one +purpose or another. + +### (Temporary) wrong colours when switching to full screen [#4511]: + +On Windows it can happen that you temporarily see wrong colours +when switching to full screen OpenTTD, either by starting +OpenTTD in full screen mode, changing to full screen mode or by +ALT-TAB-ing into a full screen OpenTTD. This is caused by the +fact that OpenTTD, by default, uses 8bpp paletted output. The +wrong colours you are seeing is a temporary effect of the video +driver switching to 8bpp palette mode. + +This issue can be worked around in two ways: +* Setting fullscreen_bpp to 32 +* Setting up the 32bpp-anim or 32bpp-optimized blitter + +### Can't run OpenTTD with the -d option from a MSYS console [#4587]: + +The MSYS console does not allow OpenTTD to open an extra console for +debugging output. Compiling OpenTTD with the --enable-console +configure option prevents this issue and allows the -d option to use +the MSYS console for its output. + +### Unreadable characters for non-latin locales [#4607]: + +OpenTTD does not ship a non-latin font in its graphics files. As a +result OpenTTD needs to acquire the font from somewhere else. What +OpenTTD does is ask the operating system, or a system library, for +the best font for a given language if the currently loaded font +does not provide all characters of the chosen translation. This +means that OpenTTD has no influence over the quality of the chosen +font; it just does the best it can do. + +If the text is unreadable there are several steps that you can take +to improve this. The first step is finding a good font and configure +this in the configuration file. See section 9.0 of README.md for +more information. You can also increase the font size to make the +characters bigger and possible better readable. + +If the problem is with the clarity of the font you might want to +enable anti-aliasing by setting the small_aa/medium_aa/large_aa +settings to "true". However, anti-aliasing only works when a 32-bit +blitter has been selected, e.g. `blitter = "32bpp-anim"`, as with the +8 bits blitter there are not enough colours to properly perform the +anti-aliasing. + +### Train does not crash with itself [#4635]: + +When a train drives in a circle the front engine passes through +wagons of the same train without crashing. This is intentional. +Signals are only aware of tracks, they do not consider the train +length and whether there would be enough room for a train in some +circle it might drive on. Also the path a train might take is not +necessarily known when passing a signal. + +Checking all circumstances would take a lot of additional +computational power for signals, which is not considered worth +the effort, as it does not add anything to gameplay. + +Nevertheless trains shall not crash in normal operation, so making +a train not crash with itself is the best solution for everyone. + +### Aircraft coming through wall in rotated airports [#4705]: + +With rotated airports, specifically hangars, you will see that the +aircraft will show a part through the back wall of the hangar. + +This can be solved by only drawing a part of the plane when being +at the back of the hangar, however then with transparency turned on +the aircraft would be shown partially which would be even weirder. + +As such the current behaviour is deemed the least bad. +The same applies to overly long ships and their depots. + +### Vehicles not keeping their "maximum" speed [#4815]: + +Vehicles that have not enough power to reach and maintain their +advertised maximum speed might be constantly jumping between two +speeds. This is due to the fact that speed and its calculations +are done with integral numbers instead of floating point numbers. + +As a result of this a vehicle will never reach its equilibrium +between the drag/friction and propulsion. So in effect it will be +in a vicious circle of speeding up and slowing down due to being +just at the other side of the equilibrium. + +Not speeding up when near the equilibrium will cause the vehicle to +never come in the neighbourhood of the equilibrium and not slowing +down when near the equilibrium will cause the vehicle to never slow +down towards the equilibrium once it has come down a hill. + +It is possible to calculate whether the equilibrium will be passed, +but then all acceleration calculations need to be done twice. + +### Settings not saved when OpenTTD crashes [#4846]: + +The settings are not saved when OpenTTD crashes for several reasons. +The most important is that the game state is broken and as such the +settings might contain invalid values, or the settings have not even +been loaded yet. This would cause invalid or totally wrong settings +to be written to the configuration file. + +A solution to that would be saving the settings whenever one changes, +however due to the way the configuration file is saved this requires +a flush of the file to the disk and OpenTTD needs to wait till that +is finished. On some file system implementations this causes the +flush of all 'write-dirty' caches, which can be a significant amount +of data to be written. This can further be aggravated by spinning +down disks to conserve power, in which case this disk needs to be +spun up first. This means that many seconds may pass before the +configuration file is actually written, and all that time OpenTTD +will not be able to show any progress. Changing the way the +configuration file is saved is not an option as that leaves us more +vulnerable to corrupt configuration files. + +Finally, crashes should not be happening. If they happen they should +be reported and fixed, so essentially fixing this is fixing the wrong +thing. If you really need the configuration changes to be saved, +and you need to run a version that crashes regularly, then you can +use the `saveconfig` command in the console to save the settings. + +### Not all NewGRFs, AIs, game scripts are found [#4887]: + +Under certain situations, where the path for the content within a +tar file is the same as other content on the file system or in another +tar file, it is possible that content is not found. A more thorough +explanation and solutions are described in section 4.4 of README.md. + +### Mouse cursor going missing with SDL [#4997]: + +Under certain circumstances SDL does not notify OpenTTD of changes with +respect to the mouse pointer, specifically whether the mouse pointer +is within the bounds of OpenTTD or not. For example, if you "Alt-Tab" +to another application the mouse cursor will still be shown in OpenTTD, +and when you move the mouse outside of the OpenTTD window so the cursor +gets hidden, open/move another application on top of the OpenTTD window +and then Alt-tab back into OpenTTD the cursor will not be shown. + +We cannot fix this problem as SDL simply does not provide the required +information in these corner cases. This is a bug in SDL and as such +there is little that we can do about it. + +### Trains might not stop at platforms that are currently being changed [#5553]: + +If you add tiles to or remove tiles from a platform while a train is +approaching to stop at the same platform, that train can miss the place +where it's supposed to stop and pass the station without stopping. + +This is caused by the fact that the train is considered to already +have stopped if it's beyond its assigned stopping location. We can't +let the train stop just anywhere in the station because then it would +never leave the station if you have the same station in the order +list multiple times in a row or if there is only one station +in theorder list (see #5684). + +### Some houses and industries are not affected by transparency [#5817]: + +Some of the default houses and industries (f.e. the iron ore mine) are +not affected by the transparency options. This is because the graphics +do not (completely) separate the ground from the building. + +This is a bug of the original graphics, and unfortunately cannot be +fixed with OpenGFX for the sake of maintaining compatibility with +the original graphics. + +### Involuntary cargo exchange with cargodist via neutral station [#6114]: + +When two players serve a neutral station at an industry, a cross-company +chain for cargo flow can and will be established which can only be +interrupted if one of the players stops competing for the resources of +that industry. There is an easy fix for this: If you are loading at the +shared station make the order "no unload" and if you're unloading make +it "no load". Cargodist will then figure out that it should not create +such a route. + +### Incorrect ending year displayed in end of game newspaper [#8625] + +The ending year of the game is configurable, but the date displayed in +the newspaper at the end of the game is part of the graphics, not text. + +So to fix this would involve fixing the graphics in every baseset, +including the original. Additionally, basesets are free to put this +text in different positions (which they do), making a proper solution +to this infinitely more complex for a part of the game that fewer than +1% of players ever see. diff --git a/known-bugs.txt b/known-bugs.txt deleted file mode 100644 index 1b9a465af7..0000000000 --- a/known-bugs.txt +++ /dev/null @@ -1,389 +0,0 @@ -OpenTTD's known bugs ------------------------------------------------------------------------- - - -Table of contents ------------------ -1.0) About -2.0) Known bugs - - -1.0) About ----- ----- -All bugs listed below are marked as known. Please do not submit any bugs -that are the same as these. If you do, do not act surprised, because -we WILL flame you! - -The current list of known bugs that we intend to fix can be found in our -bug tracking system at https://github.com/OpenTTD/OpenTTD/issues -Also check the closed bugs when searching for your bug in this system as we -might have fixed the bug in the mean time. - - -2.0) Known bugs ----- ---------------------------------- -This section lists all known bugs that we do not intend to fix and the -reasons why we think that fixing them is infeasible. We might make some -minor improvements that reduce the scope of these bugs, but we will not -be able to completely fix them. - -No suitable AI can be found: - If you have no AIs and an AI is started the so-called 'dummy' AI will - be loaded. This AI does nothing but writing a message on the AI debug - window and showing a red warning. There are basically two solutions - for this problem: Either you set the number of AI players to 0 so that - no AI is started. You find that setting at the top of the window in the - "AI / Game Scripts Settings" window. - The other solution is acquiring (downloading) some AI. The easiest way - to do this is via the "Check Online Content" button in the main (intro) - menu or directly in the "AI / Game Scripts Settings" dialogue via the - "Check Online Content" button. - -After a while of playing, colours get corrupted: - In Windows 7 the background slideshow corrupts the colour mapping - of OpenTTD's 8bpp screen modes. Workarounds for this are: - a) Switching to windowed mode, instead of fullscreen - b) Switching off background slideshow - c) Setting up the 32bpp-anim or 32bpp-optimized blitter - -Custom vehicle type name is incorrectly aligned: - Some NewGRFs use sprites that are bigger than normal in the "buy - vehicle" window. Due to this they have to encode an offset for - the vehicle type name. Upon renaming the vehicle type this encoded - offset is stripped from the name because the "edit box" cannot show - this encoding. As a result the custom vehicle type names will get - the default alignment. The only way to (partially) fix this is by - adding spaces to the custom name. - -Clipping problems [#119]: - In some cases sprites are not drawn as one would expect. Examples of - this are aircraft that might be hidden below the runway or trees that - in some cases are rendered over vehicles. - The primary cause of this problem is that OpenTTD does not have enough - data (like a 3D model) to properly determine what needs to be drawn in - front of what. OpenTTD has bounding boxes but in lots of cases they - are either too big or too small and then cause problems with what - needs to be drawn in front of what. Also some visual tricks are used. - For example trains at 8 pixels high, the catenary needs to be drawn - above that. When you want to draw bridges on top of that, which are - only one height level (= 8 pixels) higher, you are getting into some - big problems. - We can not change the height levels; it would require us to either - redraw all vehicle or all landscape graphics. Doing so would mean we - leave the Transport Tycoon graphics, which in effect means OpenTTD - will not be a Transport Tycoon clone anymore. - -Mouse scrolling not possible at the edges of the screen [#383] [#3966]: - Scrolling the viewport with the mouse cursor at the edges of the screen - in the same direction of the edge will fail. If the cursor is near the - edge the scrolling will be very slow. - OpenTTD only receives cursor position updates when the cursor is inside - OpenTTD's window. It is not told how far you have moved the cursor - outside of OpenTTD's window. - -Lost trains ignore (block) exit signals [#1473]: - If trains are lost they ignore block exit signals, blocking junctions - with presignals. This is caused because the path finders cannot tell - where the train needs to go. As such a random direction is chosen at - each junction. This causes the trains to occasionally to make choices - that are unwanted from a player's point of view. - This will not be fixed because lost trains are in almost all cases a - network problem, e.g. a train can never reach a specific place. This - makes the impact of fixing the bug enormously small against the amount - of work needed to write a system that prevents the lost trains from - taking the wrong direction. - -Vehicle owner of last transfer leg gets paid for all [#2427]: - When you make a transfer system that switches vehicle owners. This - is only possible with 'industry stations', e.g. the oil rig station - the owner of the vehicle that does the final delivery gets paid for - the whole trip. It is not shared amongst the different vehicle - owners that have participated in transporting the cargo. - This sharing is not done because it would enormously increase the - memory and CPU usage in big games for something that is happening - in only one corner case. We think it is not worth the effort until - sharing of stations is an official feature. - -Forbid 90 degree turns does not work for crossing PBS paths [#2737]: - When you run a train through itself on a X junction with PBS turned on - the train will not obey the 'forbid 90 degree turns' setting. This is - due to the fact that we can not be sure that the setting was turned - off when the track was reserved, which means that we assume it was - turned on and that the setting does not hold at the time. We made it - this way to allow one to change the setting in-game, but it breaks - slightly when you are running your train through itself. Running a - train through means that your network is broken and is thus a user - error which OpenTTD tries to graciously handle. - Fixing this bug means that we need to record whether this particular - setting was turned on or off at the time the reservation was made. This - means adding quite a bit of data to the savegame for solving an issue - that is basically an user error. We think it is not worth the effort. - -Duplicate (station) names after renaming [#3204]: - After renaming stations one can create duplicate station names. This - is done giving a station the same custom name as another station with - an automatically generated name. - The major part of this problem is that station names are translatable. - Meaning that a station is called e.g. ' Central' in English and - ' Centraal' in Dutch. This means that in network games the - renaming of a town could cause the rename to succeed on some clients - and fail at others. This creates an inconsistent game state that will - be seen as a 'desync'. Secondly the custom names are intended to fall - completely outside of the ' ' naming of stations, so when - you rename a town all station names are updated accordingly. - As a result the decision has been made that all custom names are only - compared to the other custom names in the same class and not compared - to the automatically generated names. - -Extreme CPU usage/hangs when using SDL and PulseAudio [#3294], -OpenTTD hangs/freezes when closing, OpenTTD is slow, OpenTTD uses a lot of CPU: - OpenTTD can be extremely slow/use a lot of CPU when the sound is - played via SDL and then through PulseAudio's ALSA wrapper. Under the - same configuration OpenTTD, or rather SDL, might hang when exiting - the game. This problem is seen most in Ubuntu 9.04 and higher. - - This is because recent versions of the PulseAudio sound server - are configured to use timer-based audio scheduling rather than - interrupt-based audio scheduling. Configuring PulseAudio to force - use of interrupt-based scheduling may resolve sound problems for - some users. Under recent versions of Ubuntu Linux (9.04 and higher) - this can be accomplished by changing the following line in the - /etc/pulse/default.pa file: - load-module module-udev-detect - to - load-module module-udev-detect tsched=0 - Note that PulseAudio must be restarted for changes to take effect. Older - versions of PulseAudio may use the module-hal-detect module instead. - Adding tsched=0 to the end of that line will have a similar effect. - - Another possible solution is selecting the "pulse" backend of SDL - by either using "SDL_AUDIODRIVER=pulse openttd" at the command - prompt or installing the 'libsdl1.2debian-pulseaudio' package from - Ubuntu's Universe repository. For other distributions a similar - package needs to be installed. - -OpenTTD not properly resizing with SDL on X [#3305]: - Under some X window managers OpenTTD's window does not properly - resize. You will either end up with a black bar at the right/bottom - side of the window or you cannot see the right/bottom of the window, - e.g. you cannot see the status bar. The problem is that OpenTTD does - not always receive a resize event from SDL making it impossible for - OpenTTD to know that the window was resized; sometimes moving the - window will solve the problem. - Window managers that are known to exhibit this behaviour are GNOME's - and KDE's. With the XFCE's and LXDE's window managers the resize - event is sent when the user releases the mouse. - -Incorrect colours, crashes upon exit, debug warnings and smears upon -window resizing with SDL on macOS [#3447]: - Video handling with (lib)SDL under macOS is known to fail on some - versions of macOS with some hardware configurations. Some of - the problems happen only under some circumstances whereas others - are always present. - We suggest that the SDL video/sound backend is not used for OpenTTD - in combinations with macOS. - -Train crashes entering same junction from block and path signals [#3928]: - When a train has reserved a path from a path signal to a two way - block signal and the reservation passes a path signal through the - back another train can enter the reserved path (only) via that - same two way block signal. - The reason for this has to do with optimisation; to fix this issue - the signal update has to pass all path signals until it finds either - a train or a backwards facing signal. This is a very expensive task. - The (signal) setups that allow these crashes can furthermore be - considered incorrectly signalled; one extra safe waiting point for - the train entering from path signal just after the backwards facing - signal (from the path signal train) resolves the issue. - -Crashes when run in a VM using Parallels Desktop [#4003]: - When the Windows version of OpenTTD is executed in a VM under - Parallels Desktop a privileged instruction exception may be thrown. - As OpenTTD works natively on macOS as well as natively on Windows and - these native builds both don't exhibit this behaviour this crash is - most likely due to a bug in the virtual machine, something out of - the scope of OpenTTD. Most likely this is due to Parallels Desktop - lacking support for RDTSC calls. The problem can be avoided by using - other VM-software, Wine, or running natively on macOS. - -Entry- and exit signals are not dragged [#4378]: - Unlike all other signal types, the entry- and exit signals are not - dragged but instead normal signals are placed on subsequent track - sections. This is done on purpose as this is the usually more - convenient solution. There are little to no occasions where more - than one entry or exit signal in a row are useful. This is different - for all other signal types where several in a row can serve one - purpose or another. - -Station build date is incorrect [#4415]: - The tile query tool will show the date of the last (re)construction - at the station and not the date of the first construction. This is - due to compatibility reasons with NewGRFs and the fact that it is - wrong to say that the station is built in a particular year when it - was completely destroyed/rebuilt later on. - The tile query tool can be fixed by changing the "Build date" text - to "Date at which the last (re)construction took place" but this is - deemed too specific and long for that window. - -(Temporary) wrong colours when switching to full screen [#4511]: - On Windows it can happen that you temporarily see wrong colours - when switching to full screen OpenTTD, either by starting - OpenTTD in full screen mode, changing to full screen mode or by - ALT-TAB-ing into a full screen OpenTTD. This is caused by the - fact that OpenTTD, by default, uses 8bpp paletted output. The - wrong colours you are seeing is a temporary effect of the video - driver switching to 8bpp palette mode. - - This issue can be worked around in two ways: - a) Setting fullscreen_bpp to 32 - b) Setting up the 32bpp-anim or 32bpp-optimized blitter - -Can't run OpenTTD with the -d option from a MSYS console [#4587]: - The MSYS console does not allow OpenTTD to open an extra console for - debugging output. Compiling OpenTTD with the --enable-console - configure option prevents this issue and allows the -d option to use - the MSYS console for its output. - -Unreadable characters for non-latin locales [#4607]: - OpenTTD does not ship a non-latin font in its graphics files. As a - result OpenTTD needs to acquire the font from somewhere else. What - OpenTTD does is ask the operating system, or a system library, for - the best font for a given language if the currently loaded font - does not provide all characters of the chosen translation. This - means that OpenTTD has no influence over the quality of the chosen - font; it just does the best it can do. - - If the text is unreadable there are several steps that you can take - to improve this. The first step is finding a good font and configure - this in the configuration file. See section 9.0 of README.md for - more information. You can also increase the font size to make the - characters bigger and possible better readable. - - If the problem is with the clarity of the font you might want to - enable anti-aliasing by setting the small_aa/medium_aa/large_aa - settings to "true". However, anti-aliasing only works when a 32-bit - blitter has been selected, e.g. blitter = "32bpp-anim", as with the - 8 bits blitter there are not enough colours to properly perform the - anti-aliasing. - -Train does not crash with itself [#4635]: - When a train drives in a circle the front engine passes through - wagons of the same train without crashing. This is intentional. - Signals are only aware of tracks, they do not consider the train - length and whether there would be enough room for a train in some - circle it might drive on. Also the path a train might take is not - necessarily known when passing a signal. - Checking all circumstances would take a lot of additional - computational power for signals, which is not considered worth - the effort, as it does not add anything to gameplay. - Nevertheless trains shall not crash in normal operation, so making - a train not crash with itself is the best solution for everyone. - -Aircraft coming through wall in rotated airports [#4705]: - With rotated airports, specifically hangars, you will see that the - aircraft will show a part through the back wall of the hangar. - This can be solved by only drawing a part of the plane when being - at the back of the hangar, however then with transparency turned on - the aircraft would be shown partially which would be even weirder. - As such the current behaviour is deemed the least bad. - The same applies to overly long ships and their depots. - -Vehicles not keeping their "maximum" speed [#4815]: - Vehicles that have not enough power to reach and maintain their - advertised maximum speed might be constantly jumping between two - speeds. This is due to the fact that speed and its calculations - are done with integral numbers instead of floating point numbers. - As a result of this a vehicle will never reach its equilibrium - between the drag/friction and propulsion. So in effect it will be - in a vicious circle of speeding up and slowing down due to being - just at the other side of the equilibrium. - - Not speeding up when near the equilibrium will cause the vehicle to - never come in the neighbourhood of the equilibrium and not slowing - down when near the equilibrium will cause the vehicle to never slow - down towards the equilibrium once it has come down a hill. - - It is possible to calculate whether the equilibrium will be passed, - but then all acceleration calculations need to be done twice. - -Settings not saved when OpenTTD crashes [#4846]: - The settings are not saved when OpenTTD crashes for several reasons. - The most important is that the game state is broken and as such the - settings might contain invalid values, or the settings have not even - been loaded yet. This would cause invalid or totally wrong settings - to be written to the configuration file. - - A solution to that would be saving the settings whenever one changes, - however due to the way the configuration file is saved this requires - a flush of the file to the disk and OpenTTD needs to wait till that - is finished. On some file system implementations this causes the - flush of all 'write-dirty' caches, which can be a significant amount - of data to be written. This can further be aggravated by spinning - down disks to conserve power, in which case this disk needs to be - spun up first. This means that many seconds may pass before the - configuration file is actually written, and all that time OpenTTD - will not be able to show any progress. Changing the way the - configuration file is saved is not an option as that leaves us more - vulnerable to corrupt configuration files. - - Finally, crashes should not be happening. If they happen they should - be reported and fixed, so essentially fixing this is fixing the wrong - thing. If you really need the configuration changes to be saved, - and you need to run a version that crashes regularly, then you can - use the 'saveconfig' command in the console to save the settings. - -Not all NewGRFs, AIs, game scripts are found [#4887]: - Under certain situations, where the path for the content within a - tar file is the same as other content on the file system or in another - tar file, it is possible that content is not found. A more thorough - explanation and solutions are described in section 4.4 of README.md. - -Mouse cursor going missing with SDL [#4997]: - Under certain circumstances SDL does not notify OpenTTD of changes with - respect to the mouse pointer, specifically whether the mouse pointer - is within the bounds of OpenTTD or not. For example, if you "Alt-Tab" - to another application the mouse cursor will still be shown in OpenTTD, - and when you move the mouse outside of the OpenTTD window so the cursor - gets hidden, open/move another application on top of the OpenTTD window - and then Alt-tab back into OpenTTD the cursor will not be shown. - - We cannot fix this problem as SDL simply does not provide the required - information in these corner cases. This is a bug in SDL and as such - there is little that we can do about it. - -Trains might not stop at platforms that are currently being changed [#5553]: - If you add tiles to or remove tiles from a platform while a train is - approaching to stop at the same platform, that train can miss the place - where it's supposed to stop and pass the station without stopping. - This is caused by the fact that the train is considered to already - have stopped if it's beyond its assigned stopping location. We can't - let the train stop just anywhere in the station because then it would - never leave the station if you have the same station in the order - list multiple times in a row or if there is only one station - in theorder list (see #5684). - -Some houses and industries are not affected by transparency [#5817]: - Some of the default houses and industries (f.e. the iron ore mine) are - not affected by the transparency options. This is because the graphics - do not (completely) separate the ground from the building. - This is a bug of the original graphics, and unfortunately cannot be - fixed with OpenGFX for the sake of maintaining compatibility with - the original graphics. - -Involuntary cargo exchange with cargodist via neutral station [#6114]: - When two players serve a neutral station at an industry, a cross-company - chain for cargo flow can and will be established which can only be - interrupted if one of the players stops competing for the resources of - that industry. There is an easy fix for this: If you are loading at the - shared station make the order "no unload" and if you're unloading make - it "no load". Cargodist will then figure out that it should not create - such a route. - -Incorrect ending year displayed in end of game newspaper [#8625] - The ending year of the game is configurable, but the date displayed in - the newspaper at the end of the game is part of the graphics, not text. - So to fix this would involve fixing the graphics in every baseset, - including the original. Additionally, basesets are free to put this - text in different positions (which they do), making a proper solution - to this infinitely more complex for a part of the game that fewer than - 1% of players ever see. diff --git a/media/baseset/openttd.grf b/media/baseset/openttd.grf index 7f4f6cbc499876021243cd11e7bc33fac4132c52..6cee79959ece163e16291971c1397b1bb5f231b3 100644 GIT binary patch delta 677 zcmeBrrnvF7q9_A{dyrcbFP9Y8{1iq81{MYehX1DB>zyH6Ofq}tux}!U@LINaY zs!&1(6k(|00uB(6Y5YxEoSZFMCr^R`=ETWUFW9(Fo;=CZq;ra=QTGM&3pPfG^+FxK Ij0_+E06comO8@`> delta 74 zcmdn^TCwYyq9_A{dyrcbFP9Wod. +// + + -1 * 0 0C "Road waypoints" +//@@LINT OFF + -1 * 3 05 19 04 +//@@LINT ON + -1 sprites/road_waypoints.png 8bpp 10 10 64 40 -5 -22 normal + -1 sprites/road_waypoints.png 8bpp 90 10 64 40 -31 -9 normal + -1 sprites/road_waypoints.png 8bpp 170 10 64 35 -31 -4 normal + -1 sprites/road_waypoints.png 8bpp 240 10 64 35 -57 -17 normal diff --git a/media/baseset/openttd/road_waypoints.png b/media/baseset/openttd/road_waypoints.png new file mode 100644 index 0000000000000000000000000000000000000000..2934cb5b222cab6080b0239016640da9ca3308c3 GIT binary patch literal 8580 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%0AW(A5mRI`K#>0oLR6rC#<<~7E8cCF)i~K z``&(B%v}7u;38+_hEJ;!cTAYBv`+u4QTzGV?O|v69z2uzcFnElA?xo~k9orcUdg;W zS~&YeH2+iOlH9@Y*Y0of6`OEdN84G}Cv(EFLPn{>D?i3OGcovhBc*4e@SfRg?yXWO+|IrF=*tUC ze`b9=C0yZRv*z*a&nrt^WhX{I{vf$nHOFb=x(V4UEc74j{>uHaaEYg>BP*;t|1 zhF-b1<+v-$T%6}b=e9Q}+SmwLrz)m>5Y*A*Xj~`nG4qZ-&mV_&gD~ahJCi=NyuG8! z-Q1O)F8#*r%KVE4q5^UP7rz~0P>j65WbM+m_mt8uzCCa1IFxSRd#!SG^4EgWMZu?B zl$-QEiUcKbJl?kJgzu?|5my-g`E;#%QS&g}&og85hyJhr&2h%lCIww)?|C@kYx(Nn z4#SnY8@hV=_;bX)Z=~IOUS+=LU(k9sudaJd=T}dis`RF1C1<;Mu(S82?+cljEAGW| z|36W5MXA6|Xu{OR)dqS`=8D!W>a2QXAH)5LVs~+K#u>hMk7_Y89J(F9!{m3;B>&gf6R$4pEPlUmVF7#E+ov~W zt~^K+O`Bm|67~1(oknH5reBJ-FB!8Qh<$(fCex6!IP&i!36q6S3$E8M=1*i0b?sit zd%EH7!A}-eM=jg?1FUCO7(aH^U0=6GYp=<(Is6MWAACB)u3ljJGTH9M>qG6K*J^%d zJ0w+XK9(`?VQtpAx3eyRl< zdidO}qyONps`>qM*j>MV6a>rXM2YVBLi7tFllZ|WtdUs0>uK4`OU zNaAHboj$LJZ}lcu<@Yf^#e96;Kk#e!%RjtvMlHYUF{c>Q>+c1&$w|*s`{6CbpzQKb zU{r zj@w4JXBGaPD-u1+C)@Ol{dJqWD%+Z;m8p~n^0gm-sb<}>ab1|EvH6Y_XT%Qte{`(k z*_K$pD@r~UFV{DSp1Kq9;9kpIuMbI!uCKlscKyWFZ3f1H*Cs6Y+_=6@o(GsA^uIov!!#g*(+Y3UAHxU-qb4rZ@3RfPrddhD(K}(sh2PQ1+^zI zy4K8k>}cjvowDHgy9w$OUwcgV)Z*Ih<{I%zqcMAL{*6C>58eKKUE834^XwH%KlmtD z&Ha0#d`p9Zrb(jLiyW>t4jJ!t)?YdL&;QcD`|8NVHCk66uAVjHw(glu^`nvk*^BS; z@iu5SUq5j1s^7df!m~EbH*u&4bi2mEc1JT<=Jdnd{cktytEhgiwWG0sVaY>XuLhTA zOxLFIunB~z?yb!%>t+bO!d!lKVb8307bJxvUoSLOKYn3B!QU?oiJv~kDJ;J_;r+aj ziEH9+OL#Sle-wCr&?;r8_Kx;hkCyCh_554%U;56C&<7^pVoyF_T+8(3zI&KWSo_rs zj=b+BA#JSuWet(dbJlEo`LMC)`%Mp9hW{#dN#CpsytNv6-*AZ>Jt4ElGF$FgNR5Mx zxX$$?mOaK_H);Dat>$Ta!?MiOd#W^Nn+>OX!VIN1k!lxytPa+g;p7vSxT27;L8i?` zjXmX{OeTwfeO`OZ@@EYU)l5MLe<$9#(UAAR`Ak|s*`de%zw6sqowZruxN&M&X!wB_ z>G~zER=P3Y%2Ie0TM|F(*mLknoS5y!Yu0LVQcvycxW0D!5#BEhlE0$ZP8LrOS+lLV@%vMr{B2X#)m+hCpT79ugg0|JUsWHd zJibufAi1{qupBbtwEmsCP|NddpZg?%W$HzYCE7eHU-mAw z=WVUp`KY2|ZK#Q;jeyY}mE*krR>vF`y%0Iue?Tg{Am@puh@rpZ*_gdN&62|T=e^_` z9N$gu?PSiY|MukMw4R&}MLpNkD|y2CTwA4W9}8xh+OPdzB&Zp*LYF&zy~ahMY0GCF z-rOZFf9Wy%j+^G}Gc9Ky+u&2Mvr*4OG-YSrfmUbDK<017lN~Rg-tbFro3y1?=8u~O z)zi+b*<0V4r6sdz(Y|X_o@!=mw|~EVG;U#bN+R#kywZn9Pbhca{m2sdf6@FsiJP5s z6n+P7V69#&HM#atsqmiPX=-O{W(!=q?7wf;3+3hWuggoFy8q_->?z-09hH*qzj$bh zM$Ye~W8V_~vwvr4`?l@z!5tjjH4i_md3jQx)7E;{=VLC}42RE}Y)n42I_n$Tg@+oo zHd9mOr?pOR*O^{7gU=~do_)2A?}=@bnV+%i6@_e`yop)NurPY9UEsxKAK2gUeOrIs zwWwgdh!uZL$6UU1X64hrOyU2#)oA;+0+!cb?iG8T{r-_ZV|wI`o+43RcGc(|5_=e3 zihL^9d}$T354-&C)VZlYcAWTDCs4hPrDn0&aYv4CH$VFJvMr4%HHh^$Vc%jb%FMDm zqw%D=zs9UT_H`!@*PM79-Q-oYMp}VcV#daj_u0RE&Ky>qR{G_@WyvtI~Z@f@t!D4o$mNyb7k7QE9TE8|81U^(!5n=`eE}8 zIyVm&&Xib?ajVy{H^J)R-Td2p$LG%Ri+d$~qiAnu>B*_)n^y_9y?v1~$Aec%>-KAx zb}9b|tCE2pn=}gn)Chm$pnS}>SHZJBDaqjH3Z5FzF!RKK45BVDr zz5xft{EvB8uUNSD>HEUWze$a~4AN`0m!_I-V81tc-90u9jWVCiSK=R5+?8MEGtHU# z_5542*06p&Y+{%w?-082ju!u<2N~cF0OgF#N$TYZiib7f2>}22D~Zx10NL*)8Kw0%|q?!&l5Uvsq%(8J#<6 zLl+yCl%95L`gL~&Ge2bm?N`(D%*v}=2dk!#{oH+9S#Y1-+*{_k$=6RHa@60H- zO8WBqvpJVzT;&lJ`6u;_o$t-LKR#X3{wgJL^7R#~*3>dM%uleLy}XTpvlcQ~Z!~ksY4HXJzl@ZWeitczk%qR1GuRfF@AubNhzaBU@VU;T-e=TTB@3&V!WUP84OvhU5uDvRohe*c7^X|#7 zkKC`8$Z_jcd%SpVskPp{Yeia3JOTVmWt@*MN?_eQr-!+wWi6-KiwRfTw3FYMT;Hsn zbaNqJjPQh@rH#LD&YhNOuX|ci(nee8txsu`;eq%RzKdF2XOD2|xm~)Wd^OK3Rn*$! zW-GUHiv($F$)6s_}txa5}bHd>1|8L@&p4M&L z^WOhH{3G#B<)>{E^e3D<-u!LBj!1*_!Y9rv=JE%)3jVC|+?e>gROaN0!#sK}{U%!+ zYFgGmw(|*kJc%*jKt{HXx$Nn*9u=*h5^pxO`Yr#nIdA2DlcQ@(eJbwE*zM}Iqw&To z*Q<^~IftWWul_aXKj-GG_qMa^U$}hEa*XqyziiWu-Mmq=oYvInMXt{E4PQKosoiStjVZjIS~^$O_%s~a z;iu?QmhhFIQO{OoufY=D6(=%^4hlC&9DLhZ9Id+TcIY>cIA?1$1OJMjdSx59i#>EE zd~{{3W#(4DWIXGToWS#YF0bdwIIl|*p4YzduwrWbW>itY6lX zwBgbDEHC@C#Kj9cs-JnxRuPj|iEei0H+>j<_08-M(;ESvrw;ydZ=8N~M(bAYr}JKV z_&#EYHtgdoUZ6Om@h+2IT!f!#*7p^QJC^3iCke8MC=1&k-otQHZq|zyCYe%{;Iptc5|I{LFhC4EBCAaevsVNrX04?AoWhQ_(8_6Yt|kp)tRx&!fHnJEt_Bb z{!jYf6gZ{t-0IMC_EmJMe>P9hms~X2132 zI{~><_g_tHIr`3YQ+1Z*{m>N4 zm2cAT1fDrs6FQ56I2ia?FEjk$JSr2gT|k}r)$Ik>()wClx79px*YCLffJqIBSP}d$n22jbPbT z3Hk!_TjtrzD)dge{WRv}k(mb)4zcc-*|w6SQs&Ozxo(F%4?Nf&`E5$^)lYql3)d-c z?YgbUU-N9vxl@mFc=kEU@xAE(A^)zE+fPG$mLJ>0^U>Ao-$yn`-f_JzE+=Ezu(D$k z+XT0~IqV6>r+-BCCYMXUzRh>~!FtKL+IJqlub#_vY~KTuhavoj-8}9jGt|7h(|7!y z%7v0uSNAXrd_EWP^Upf|=aOMh)C6OuExc3{9=c2L!{!^Q>6<1fM#yefi~RO#imC9n z6-qBXb|oFI_;|Ze-Zv~&bWK2cS>f->zkQKS1xJ~h&Nz#1JpV%~Qtisclk1j+E$cmh z;S@+k)YH|Jc$ zkFVJn(zh^B?yTSk)vd3mY42DWZ8O*M*ODmR`?D_jr>%X&(H`*T6=P|l#81U*H#NML zIdw(6YMFX{R?zy5%xl*j@aAhfxB1uSUy}ceR=qoMYvs)sruUQPhZh`d_;ThgUwQ8n z2XDo;+fLuvEEdL3?qG??;tMn5@O|fXgZWY9?f?emGO>i!Tq#?-10R+@5kHW{v$w`Z zGEuyR>)E3XYnvadiSw#ex)ytHPulgfda2mPQ->bttPYN_X}EiJh1-0!n>DMuyes}S zv&fin)=yt9Z?(&LqGIKRUj;d{?SI`xk#DhGu5(SJS*`xF)(`d6M<Utry{mfFml6^`)^)|$;gjd!G}|$h7EuYs;(JC%;@j=i%1RYk2mizkPls^X_g3-koYj-}!of zZ)tkr(s?!L+QaW`j;3p~-gQoTlMuFio0*b|hxp~|M_Lp!9d_0oI69em+NAGmWG-jy zowPVxYUYB|3)&Z+NS;3P`_KAw^%6}RZ}yy5JEk+I**V_wPQ>d&a}#*HeAeBNo1oEm zG{LmyHFL2~K~H1#zw1xx4>uiekds?))zbFN@#wQ>dIv41g;}>tDde0^{IDiFvTb6; z!SKU(jQq~|2`%&El=#(ZcmBmRx33DR^4l)u>$NUSx@!1kr6reJketWj?ejOrp1-AD zZ#1*u*IFqSudbPj=UCs$yZv~7{ro4-2X!%1wUa-q_~gvw2}ym+Rar7iIAR9>##av= z#3%Q*oLieH#_|8>qtYUlH&1pk9zRlhLr5yjq0@$^S)03g<5jH_6V#^JJ@R@~Uvalh zTF#&+d!Ueg13mKS{9- za2!=u+Wt~u$NjJSlmBY3slPURs-y9V@Q334#cwpyzg2WGcs?r-_$u|S{AvNia=%ik z?|ph^{}tW{T2z^aO_}-aK(F~Nod*)9kLA8OJ2|g$dq}rm)YRO|HQcSbNB6&0|LtOw zVUg0kUU~&b?uvw@xRata3tmr`h%cG$YBW2!@nMRvgY z;}aRq?2&8NdUJNoY2SM~_rKa&C-n(h2NwD#n%wn0CnGb_V5(brQ8UkkHifhJ9AHM2H?nzo- z$|`^Pzuimw6Q#Ef=IlD=k{)ohoaxA}OrG>@%nLlFS1P4N>g38jHF&o4?GE2<&$_G{ z#I3WHADmKp5b}8W!F8$Ugahg>S1~B9)G%eN47qb*v$|!UO|Ht{SJTY$O{ZNHnWWaj zy`G)>Xw)U069;d~{&-ydH{`}*_s z(rt^_l&5q1?O*-ivzpZlff=SZo4K_`U&RYa+|8RF)>~;>^{L{6)1x;162^sX5(RB7 zvkF@mao>8nkd^IL^X+;@mGj#_Kd$;+B{XS8j7WVPclF)#d$(;ZT7PlQZ^2uMWzqS& z!-CtxG{5wGtFH5j*?h|}$oxaZq0BQM-qzl|{3Si&8~cCxB@a3j1aH1p^!S_iH(-|H z^VW^lExA*Tb+o3$f2zOM<>DrE$I;=v`;qXj1}mc-yo&n&IXCj;P19*WGbr=iRmKX(UrM>&@wLF}1!fNB*Cx>%V>WT5FF==Mv68 zd4JFCc~*ArtLw|Vyu2kHG_4GlXoccohUg| zsG00uXBGC0_r?`h#}iGxD^EX?a%%hGVw7Z4Uby0nKL6XjpZb<`nt!(UcJ&$|@%evNnCh6u?&(!5d$FhP>A5dEa$d%7`S*sSQGRdLoE_!v zw|-o9^>rzZDNnh_Bsc5Rj3uXZ<}Xa0YRlapKX2*(U#tJ$)i6x@cKLkhuQTjF^8QIb zUV7-*+pdpukF^T+BvT;Jo=&XN!we$Ewi`E+hsZ5oV?cY z!S3W8K8lCb&N|&cd*-&V$rkUI+$XxSwjNn|J>}u`f&)4>>|y_JXWtiM|FcE7f^#1C zkGP;{)(IbS6-unmT$WXAEx5JhT(yPsbXFCspHCO=4gdDr{Z;+1uH=6&&rZzUxAuN) zlA3N-gWJ7Jk%w+-UKEpk*PMNe^K|aDq9uoeMRK-0TxcxI&T@9Sl6;Ip(Lo39O^Sy~ z-DdKq&CW_NoAb3TU5D*`oL75{^IXKWy%#k39wfi3o9V;7 zI9so(rSFQ>t6sa*GTm&y9;4*VUtTW$7W)3+dH$EXi}u?u`LN_@{dx8uKPUgWUNc>L z!@oxJSig9YqdupeFHn8G|5lx^-G+tT-Pt#v=N5dQ#gf%I=Rjr8^!R2)XM_14_MHt| zty(PZ_}6oKyP`=&{~wly;1l-i8P1%Skj)GEbm!NH#1m|DRIVhxvSoPK!OgJNKa0nz z!=HOyh`ZC5w>LA6oO%26OqDR7xZ>)Nw7P#s{{O!`Tg<+@;>-LO{Zsx+AJ8~fT(V88 z+eTj^QTLJ3vJ|aHwi(}M>}qG&^se^apOY~kwm7rYNdB1obYEIZnG#Fdg#}xag$j*r zW5gFY%()~cW0GLAjn|IjxOU+z=Ca=3R;%Y9DPj8au{f*?N-~8KeZDZuNeIWes_PIv?S5rSR|IeAUL-yM|)`|D7>aH&L{K?1e zS$RlC{B&5jL;3AOlWQ8L=kz+f?KyAid9p5DSIok(&i0}ov!ZYeXHNWr(xe+JW&0gX zIG0aY$1%_7!B3VqEQ>GJ{kRl4pGCz$lmD8&{r^cR`48J4{!V^(`}x<$hi__Wmj`~S zjJoy8#@#cMt0(y_cVJuUeYWc5O%p%HSG-zMwt#nE&6L1h`3Yu;i?}X`@^3UY*Ni=H zBsgsf+qaaS5RYm4vmBcqult`Hk(iz?UwxKobBx35yc4FcqJMRKF?9ZR|6p@qvtwv_ zM8t{A*(q;xW_pLEi(fCwz4_z!hlKw!57cV@E;#!Ap?%c;&!q=SmS`sQ+^mo+Dwv#| ze{j0h&E1|!oBZ-u#`KDbs?*BNMjhZ;^XU=~U!SeQI>y>Z& z?o9JIXUXr&(sDsl?7;e&hbPYzeQ@!!*o^u;|MG&)?sR^7eN9I74ms0@sWO6Tn~Poa z7hA1S`1W<>gZfp7HV$18( z-%K_zHa=P5bkR#f)$if2sl58lJI@}nU%B7?UvmBZWIMwLp-%Ugw`W&fjSLlv*D111 zzqX?E_ZdHqNg{z4WP7&nEog9Fq}AQWY{PL$`bxmuD8q=u>o`|G+*IMwX%PN`e;eDc zY`xyTUfZDEvQxU$^_h&b#3zNE`LHlDVuiq;Bb7SSUp81hn#Vb@wK-Gz9@FVX7QqJJ zde=+&dP_Zx*sS2)->ROk3{X7@ZMcjl24N+ zuQparF?{o4i}pj|Zn1(?(>+VK={|j-8OQzV^Mv5{YoA@26F*7$;vc~~K8rN@3^qT{ zT&%G0+Pl9WcYOZ7l)>wYaF5W@d+h7ZKU95cVi!NJpUbL8`YNd5PcJJyDuaSETjd9t6dYv}3C z_s;HMK4PX`W_k0Gdi?8n!hqXa9 zrSCVN3CYe~cE9q=dA^Fzf1Vxxnw>oL=HVdCcW;fCw@r24@ucVBVVjvVeCDb8ElsqV z5q!`oX7Up8vftXzAD)V7SA6fb!>ReKk08UcmME#N?85DT3e1>T?UL3?#_i9#Ci0-H zG^a5)HF-yeAm5!;lRGCJWm>TDe|$6Tomm6V3xPXFaPh` zbu909>}cE8n{=14^v;*yU#TBvTh^>Pbk;q!ry;qah3~iUeugUkjI9gQRo6dw5xP6Q zoyk7&$&bZblds6lQ&<<7k^MqXLp`Y5`lamM)YGvxlm6dL`&*SbThhed(>?Rkskfqb zou-p`48NzXY7w8Oa)9B`u|H*|&$i2diT@G8yLr{Sopnd#qN;g*i&-uGTRumMIV1Ym zldrZ>zWYBnBv)80DL!!expdwalW9*wjVlAV4o}tFR(nChqLDNB{xjc3`(mT4bCT5s z%xzrj&I)Xt_#vsvkJ+uDBf3Jlk^lR?(#w}FDdhUAc)j>~SaSC}ovp#8PVOfxCNDSN z`CIMzl8iUZuNj>WZ$8d9WBvZ055AxIlfc;dKYhIl)AHa2{&V+CF0fp-`@KYY!;*8h zHna8r+hthIDJ&2cf6&^)=@=n7UGVznFb4}W3xUGGDW_yEt(vkUIboLAhF4s7PWWw< zS#`QcM)u&Wou4@;Z`^F5w6rDH`PxM3y2HA!q%3FrWpvL!pO(AgOHfj0+tS0z8zPTS zyFX(GgZum|KZ9#}wf|fH|8AXf;P?Id33ajc$MX7iCs{7tbZg=cPQK2Zz4a|lf*St6 z=TF)%aGJLwv?i-Y{<_GygnM_E-F|WSErWev@8lKRCn=n~*~;)p!ItA+rn70$z7xW# zb;)xRSv7C(cw4hEIb&|Z@k*Onv-MM*wyt*jdUP7EAFD#Aozr1SP1We688a4sf8J*x zTU_yO$(4)q6q=X*UuVtm(|+Tx`Ym#k4De#>#DbMXv6|1SS6cbfAa9I^5eSBW~~ zrOGqIMeft*1^L=c?{8lDexT&T+shJ098cXovCCxda$LxkS~9W5K=*W6rP?&-!+b7G z7H3-iEij9-@eQqx%1*ek^S1&k_nQ-a{#xAkCr;fzg_+^5dw|U0X~iu+9{-wgWPe=W z+ifK&l6s5&KIU5?`NNi{?gqoU`KiMHq&yyX3-6Cy23Go_y?{?)!;o-zMI z__ESP?ftF$%N727y}rM`pTY9~v4Ro-o50Kmu@hc!eV_8z_dxiRUxHB<4?pl1&YIun zBWl`vOijMjY|^8oUZGK-e!?ETYedBZMEz=wYlh9rrM(S9HD=k zUhLHESZv&8{q^#B`@e@u+<#1K+&@|Ega2#u=Jz+017$8)UJdMJa~Jy*;W<6j3d;C?ot9FU)-Da0|zr`YgQQ~Y_;VmcOiT{dAcAjRB`5WBX zW|W_T=|Yxb^(Y^DUDYn5M5} zyP$3^^IdWOA6E}yZuf(Kzi>bOSI%0$f0FuF|JU*Jt2o>E6mAOq{H&2S5`)Xa+kR^?KRjrPIUo>;BfE_JRxXwwAaa`(q6%E40JvL}U(3$R{( z$G!J(a@w!|ozpjHKm7A_`TV+{Ghf#)K5+kppe)<`$M-+fZ*7pW+xs!yIZS-H>fGGp zI%+Z$2_=&>@11zDV$)NrnoX0I zPL#C18C~(Sv?%fN@(VMKqdwNVFIler&-O51%l^HE9}Z8F{rByyn9WE1h|^O2yU#WE zpX=b{o+T`{+Gn1vWU!Aw!X3GQi?a*EJg&q{+{R^K{y^GF>+mG@mZ06;3wR!ty|fCO zB+la-s`Syv%V=*@q`B4Ku!%Bd%NxWrlJqT0j!#i(jphrkSnxjVme>W0^`HH`Z{Fyi zbuHM}e{b4D`F-ou=U!Z}`SEQwr;WuYmR0}wzmb{eZ~YnO3N}T}z%S3gK7MG#H?3XN z#`*jfmM3)yeA|)?SE)trjbA_UEZYSEy~2+karV11-K3Xj78>kc+6 zLKbr$-o4-Z8d;iM4=lV}|IJTmTWR`h&yG)Z_vV{BTD?5>`|i!TbLZN=eVZ7$XcG6* zMWsx!EduUWZaiN+`NHjEEUTuJSlde}ytukXkJWv5hO5Zz6$h$5bohQ`xOTXCfq3AH zKcV|{?tY#eSIfO5yP5fAPw)@k`zunW{wvY{wqnW=lV4uF79Dz zWKH@DC3CX_9^1JVgsZf2e~>6$Qs{Ui{z~JaW4={$4~ersnldG0>xTg6i=h{nN<31P zf1}PWbXDe>O{~|O<8l`ygPB7udV^owaFIQy+A3}Ig86*Vg^8OAU;KUPRUNvZi(5pG zos<7~N>I|Job6(m0DdaIe#`f#7y2P*5h3gi31WJc`e!0cNw?}hh=f8a?56#Q{lF2TS`Qqr+ zm$zK1_?zrGvVTZS`^Vu@u5qw4${|6gF6AnFQqa{E>ZwavT~|hSTurK*b5ERm=CW_b zo2GUC=PYSZUvoL3uuw*KcIjVpVGsST%eF?3BT93)+~+&K5R`6rUG9I|!%*$#!wbIu zy`I-QOchyv*^qI~hDOOSA-3Kxj^}4wGGO`Iu9B$aDt~sOTXkFCI+w-4rx|{&3cGB_ zlocR(If?0ltIKA`&XW9SnKyreTT0JpOz7eh+*0zJdGWN=ouY@`&9e)f5cl%g z3(<8J57z}L@6GeoU$J~+)!H!W36q2bJn|2iZJKjvQ6ZZw(*~bIukv{|6*PPZzF@!Y z)qJIO?ZPkqd9}uGarf$xi&dz#YjqNLp1;|zUYYC5@xqzT#Y^_{C%%yEXs!(^VTkSW zw`aeY`|IV@7Ki;RqHj$~lolENy7aa4OLCFW)|y`rqU%!=HqEMgI$uiE{kz73Y}*&h zK4ePl)cEq^`qnQ}(swy7tndq7)?jsUM%I$+%PwwWetGnrOBHA9zokm7zACfsshmHi zc4=#U-Pzgf8?&nNCs@2ay1`x6S6}~P_VYDOFIw5vFU6m{|5{(hFl*XBvBtLxyL~S% zzm#say2bm1+Cg2@8Wztz9sg3l`L9@D;DpUl;z;^;`BNq(NmT6 z>S1tZ?T*76M_T_pAa`|y9-FcYs!33$_{@4f$86lO15^|moi9f+@RTV zjz5#bd49{HCH*D=&o9Y;HaMonZlr0k{lK0V#%s4GMYO0c)jst?>U4;Qu~ev|Vcki` zFZ14tELJjbSenlF;)Pjvi89x&?EO=Y|H_JRcFCPn z6iP+5X4fC=H!R^!+54Z7FWtaR)&2Qm&Bh<@Dz3fTyuL49zOJ>3B`RL4){7r^k z_dKULoo?eTR^a&Wl*;$+!bb-JKQPyf8gX?OUdcWp$9)uCle6K;4XYwb0PjCpk;%FH~y#jx$)y7%(Bcckb0_-b={ z*E)FCt8HXyxOr>7RZ5Q>)8rtIS|v?c_CGK0nU`p_ex3KK-r+~=&FNJYi-Tgy`k0-R z_MX2Z9zW$pT|m0kwd0!{k2zcnR(+Tt=qmo`=g-$0H(%=hoVB?qW7KutsuSv7K6En`cn>I{45MR;M8|VK#cg+WpLNngSO(Jb}yLB%7Ns&KuXZ|{_ z(7@z$QJ!;#K^uCsZt?bPymtIQ$1me&|JIAXwVjZ)I`sEN*%dFGlV9-eEWD`cHnnNt zcZ*dU&vG7j3tanR+d-Z%!;i-U=G=?)jxC;WLEw?!;s|5Sret5yYpF|LCjSkL?CzCf zF5Ksl@9v+NTQ|eF`vUhHlMTs_|GdoK?sP-;0LSX-#Y-f9B?ufeIs0q%%jQ=>S8gmU zZ1ecf@O)l|xo6fv-jwV2FD0IN70laGp1Nj|U4)bG?-;L!9qW|%mdh4anL2RYT@hNq zmm?GvHbs7}?U755HflCWUcPY7oOA!t6|LT#7mr=6TM`zm?X&S)pujb`4VLnrlbrv{ zc%!#N2bvbz60>z#a4<<0ChmB*T=KAOKG?1!OS^xxuzON99N;kh?|@7Z9YN$ zy*SJ$;j?&8!Sm#I_ngzg)BJuAJ z&+en)OA}@^7w9d#BHhopS8H8)XomHxnSQ!V){8~Hdo7&Hnad-4>v6kOLgkDy-hcDPGht?0xr838+-81J%~%PhNf?(L^%TV`6B zuC6-WtiGn@*FDp1Srrdj?IS;_F4kDzcx;wxs{8w&GLQ248JO$d+DJA$H-7*5kLVQc zy_!pnCNIvJp(Gi`8250F@s?cP1!o>~Srq2Ei*T_X|6IW7bU=04A-`T@)pFK|i<1Hk z`~vQlx_;bSUFCS>SBQ#y`#E8apPCPU7Jp`x%G>tT%G%oc=`XDyMy|Mk6}J@_u1YyR zPnXkK{b=Pmle27plsBK7@3sBmlQ;59O4WAm??~BrHRRzOryoo54sE^5n)iLqo;@qh z$?*do4R{wH1^4jH!rLbPlVypcUr#^pQ$5gR>>(j8H z-Jz#^!yb7z7qD{sW?H{b`u{0ytAFX5sH6KuZ|%H%N=njiT3KHBxjX0HZkYSq;m_G_ zC+qN&7tiHddA?lZ&RRUl(mS`_Oyd08q{$Qh+&S#DEYmDQXY1W_^KMO3(=MtPT$wa= z!M2)1xAKByvZY13-n2ysdb2<1`(JxNWTjZnOWU1q?Fz03Z~7UX|7ZD@7k?a2N^LP% zUf_2p%WZG#?^N-}W%j#UO~2e(6&R`WM!7Al&2PhoFoBQH)o+|T{@{0wt`DQ!tUD>{ zYjV?9pNrnHHSR&#eZ{SB=Pce9n7-z1THlJd8>3jmLUv4Q-XOtz@x@Mk$A@NfLRW_5 z-#_HJgVUE`Ugnf3a*Q#bBEnAYaHw7Wglqr1y3;&Ao~mqPJaE)=%Qj!B<9EZyZQOYBnE-rUg%c+Yv| z`QnOyQM*L0ZZqGxXnz^^bBhe&uGU*?YhJPBJ)g2@|32rxUpeMql~Rv7eV8H4y}7Bd zZPBi)FC4v`kNjJn5xIKd{DjDQmrsB4|1nJUz7@9eP~^gMzGcZ%-`}1-$<$-o#x?x2 zl?pI?ifPRpm7c)XQtiO!932+1f~9dx zYL3sYZqC> zzO&7$eagxS3j>+gyT0yvk$0f%np0uycQ4a#D-<3bDY^2~tv4i&fPY{ zE9$o4s(`Y&6KzgO-{JFovF7gsxg`d#-bkM7wDYZ64Ot=+7fWXbW=^D~1Zxv#pM^R(CGpHOL?Q>ovVnHsO|2wIg1CQl67rDqXj(%2!Zx#Dw) zgxa>xmh=W60-*BMXZ-raax&~xrw`!%KRVuz=%C}8<2b0d32mgV<= z#h2>BV|xC)GM?`s?7VXRnv3sRGjZ@~ z9bTaLw8u($Z_y8fo-)%*R`oyr#mL;=>0+_GHSc4@s)cvX?|hVVT}V{v-tn7DC0w@% zhb$`m_HMsI(yiDlKfRY#>?_=M&U5;1Z?|bD9v+_X#$c}ZqH{|RS&BatHlH7y8?r;z zol7_eD&Svu%6A<+8i01pS4#;=2YJg`EpyP=E+9>+}-9! zD>0pe>Q%-uS%vrcn~63^lRG*qiG!bKTFt%AM150TC{zpPRED$%71E& z1pnQ5BcF0|XNg|7zU(1Uy$iK2dkwRCi{?#_n*Ad#{n@|EUpf8e%-oYM`7K@b)gJcJ zcYGh)*Kt^F-{_oP`_4e;((OgX-vw7}cB^DPSzY`xXRht8)2y6%EQ zM!J}DtIXRwf?@kM{MumaR%{(S_qXGpi@i``pCcXSHf{Th82^m)ZOoItS9ag~ z?EK0nr(|8j$BdsbPad6h*`&&?o-nb8`HlX!2{T>4P29NNsr7$L4ChJF|H4J7D&fEU zPlQg0v6SAaJ;~ctIQO>GwHk&^Yt=tG7Kv7c#H)Ni*}dnr^Q6^EH`S#pCmcR;bwbQk zIa7g?%Ab|qZ|L50!>*`QrTVh+=I=Jm_ckwD@9{2Etca;k{MkF{w@T-yD80@-@ueyA zYtEC!e9G5dih6UNh_p|%cPcuY^Td`roHgh#~AY- z&v%=YB6$-Ts$^e3&5~PPWuGkKc4l`)zKrT_r$2YA4r$z6BKV@>-%;Ut^&8llcX9=_ zZr=Jf>PKvD)fGW?caOaHN3HVp)F!gc21N;0Zg|Evwx%K02t1l~> zJ^0Rc-}&r3>2-t>v+>RQflo9`C98CMQWuezoG3&yQD{XkTY&;NoXIReqK?G4`YDs;|qh2*2bq z=y9nk?faY-bFT5n>ysH0jJb?v%Cg7|FBjkvrO>>EcQPKC% zj{p0nJJ@Soh~4LUB;Ue#W2Tg z;LH6$_7F*|6@F&O57LAI_>Cxs`W7VgcLFCEG3TYw#5; znzmqb@k8N5`C%XTzZWU#6E3MMoBmv1j!mO1wrkCXpHE*HcZa{Ul-cXWttVT0FCgem zEH8`T>6+$AZ!bp9TC@G)PR?s(!Hqq4o&Waie!ujR8J|%0?fW`Q7}F=#+*x<)Ur<+0 zajyL)%}KL4pJ;m2omeXVa7%BK%UpYvo6j_PC!U?KX8WZ4!k2P=O0Orr(Vnm|c$Gfq z+cy%M^e6pgG1|Hzcv8x8eurzfC;Xl8CSO&)OyQ>dNyf0ENz5l1J@~dvsxMTq6gcS^ z_DPI=QuBm6cf9`D<$ubTpL9p&a75B!Wh+CyS;$*Rm2cKinim5ht z8=nNIY~M0zv5ZGv=aaCoPb}<{q7`>;o%*Mh`Q+M(caC|KX!R*_!wD2i68PgC7qs<2ablKnFGXWHV@vSm*f*-pBhs;q9&bBFQd+szZFKXbkJLGh&Q zL>--1uhjlEZ8_V0waQfVxa zt>fg``LZ}HM%#?1&6ZR6S{JLv{3)_~ms`60S$4Q0%`#-ptXkg(Yj1ChS-CZ-wrI`l ztdo5g8*i(&uMvISZROrlyzY>CXmR4*D^GK*rmNVAHg8Rv`~O6e$i*yHDA+o?#u&kH#wKQJI?fl|GCSARbso-r{8+}{mtsG(s|q0 zzP@JvKP*Cfna;P$m9EuRjxJ&xuOCmVnN}OR(K|ibC$;ly;onHrn!Kw!#eUDV-tqU{ zgwUh&)8j*ZO3Q9Yy<^NsWIk+Z{=&R7Zed`lmb>nozh2SdA1rgF?z~}|X$M9#|+hmn)d^`QnW`5sgq3P9=QdC9GdCIjfmGtkQ#c8#9nOoLROSirC z3yR;#zf?M|TGiTcC4Yxa1*ezADQHL9yH8Y}Xk#cHDg8v_lZ%S~BKLhtI}17% zpO`$s#!`A`O1Aq%_2^%l=XLqsoNrj z4Yf}8l{uEQ);Da6%ruzjqI^qZ0lUJlY(4M8nX}CVo!{|yZsXjkE~LCDdCdz8_eIG9 zx_92*;9fp;mF9mrtMX)jm5m*J!WaGu+&wul>bPRntcGQGcWqU^7_U^dtKnVui)DQ- z{?1!IRQE3^ouGX2vf$sX4a=ggm#h5Jb~!I_;mV=oMUh_Zkw17`xb1&_dDZ_SF-Gi< z_lrC4a+GFvzK9Gf`Csp!9sNbe*DdE_z?cA}a{JxlX z%*%BR7rW4^a^cW|pNSVY3);Fne0M96bYJvbp=zCI?3IZxc6Q8DUvOCP?K6)rHZJG4 zFAD8*Iq&|3iSaIX^5*hB7k!74J)(aPO?*+|qAzm6R`BnUi7#3^;!<4im#b6>E)wT{ zA=k0*oyV6X7yXTk&RYxZWx43hT2k&J|Nr}iZOYe!Tt6MIRVRt~5MM!L)(8bj(CReMZ#TN%>tbVeq@}ctrRkfX~ z1@_L8D-+YM5enPkU%c^h;!z*4*VjQcVd6Y!=2`hPG6*4 z){9=avUEuSvrKwgw%}erhkn;Dfu3L5ceuOdcgMBAc)#X_ai2?l`-`t(Uqsy(U0429 zt^KP{`Qm-$s%Fu@HzvN|?YNiWI$NgmUdIdhH7~CBx%_Wu?Hu<(CxUFH_+oCyznfio?>)Yp@3@!hT3)PDCBCSB?|Sh! z?2C>2qVLL97rWwKd6Xt%)!%4K3{Q|9F-$PccsSk+l)mvAaF{nLfIo z=CCe^5#93q5W^PRr5}2Bulcr8DUMfm#W!w?{X%PAx90o^Xh@9sdCuvn`gX%`yGP%@ zG*nz(-}Fr~>FInnjV(?>>n9wQ7x*X@`1|OJy|;X4^Y&l0|5N*Pd&PR&*y}&`EN}4N zlJ&kSmP=nJW6H7RofYZ^9k-g*mVYyUX?EgWR_}_v@n&zeo&M-yH`GFdzcQ!W4_W#Hen13zc zzKKmo` zarzJZpEh6L;km%|Pom&^riE{rIj^5^`yJ8XCbjxo`=*D=0_z38-#y8%fB$`dM@9I? zPk)za?Z5i_l>fOl{`Mcuk6HgzRBOdY{a&ne&-cLUxl2AA7kIDo=zH4X^0psWbQaz( z{`O$$R(rKacNMp!dT6K4^1Z#C@rQ`^eDROglBtzzCI1T^dH+dpi~P*^r>j#w?(eLS zZRz9ZecGp4{zBqe`dZ{ivD@rbq_m>FE4NC{vf_9 z1?#qNdYH}dXH8*ok?4o!_gxi76OPpPEmIG%t8hy_Zayb(@!mV{)&!sA6pZI!@>IBJ zp=q6UB~R2&=q}&U$0vR+K6dF_^ZL$Q{Rh^Gx4UK6*V+A8)tajPe@TbFPT0SMm8af! zP1WCG%d;!tTAje7-41^mT^Bul-h6)XC;!e0f5!EE6A!H~-4ya~!Q`7C_7)!aD)gcD zpWeN6DUGQaF{h{Io2~qx^uqMPBD3p%SS{Tq++Mh6yZc|a3EY#WCA037pIZNCO`YzE ztJ9|ia}{Y%U2isfr|QzDAx(G8o;Kfo_NQj)(<@DP(w;7kP1||wQjwI_@>@saGIFjh z-n(VaWQFPHTDJu67kZNAaoUpm&ev0C-lpw*b*U)IF>mgtpJtn5QZp^Z9rIRB`tLbc z^+ct|>7BDqU%!)ZZvCPyb7m`CpRsn1|L<2X_NBb~7_&P|FUoP<)-7||C&cSsGncp4 zNST&B#lKp_?DW<1$K!0y{@V6t!uA-!h5yX7jLT-dUOH_{OrYZaSvhZOo^){*&EFvZ z{PMmyCUeK1MXPttJMDQ|RQyx+iLi6W-+6NsoxO4D=M6{A;=Nlkvi0IkGk0D}+nLb4 zH)P^8ZS@x$T`P;%@2quR`t%g{&#P(K&yvrJp7|O3`|6zd=**p6Y1SE&{O#APoVc2* z{XF=*-LpSUpC5*aKGEBtt^4nQf^PWPIq?qjzB1+BV!g0d?V9$f-IqM32R_ygj|;u` zNvm#gQijZ%GZvBYTMvD|8Snb@jnO0R_JiyUr!yo}A0K*Rp|$>?G1tP9Z9hBLTg8{p zn!JA7o@h()DSR9zB?%VJ;u^<)N-Z?Io+7iaM4Clv-8~77qphV!h4;2SDz|O=xZY+ZvA7Y}q5ex_JBI81J+VI#-ffigq^d+^2JL&%@Jg zhZ4eTMa!IIckU?-`5OG{j!OQ-^E1O|sz-c(S9!fw*1UY~slKAj`lEYJJ&9c>ye8+|{7@A1u;uJyjCH2d+o;2VMKl)E>)<9-_Z{j?qP?h>1tn1fAGXS|hf9r=CR`CY*>1Rt%K2*0kShV%j8}+&sFH*wqzmr_`WP!~$_wWP%Tlc)Edy$dK zd^EdxKEsZ$X>T`it{0AoU#he1l}hI7#O+tCmV5quKDE;6q?4t3aa`8PJwoSqzq~Rz zcmMgh(~Bjhc~36idCflHbx!%R?Ps-Dh)#PwS?1}YYQM({TbWya3t2yMSvGIBU-Fj7 zl}VGs-z$7+?b`GD-eUdi>AodjO`nH4De3P@pJndwQu5o+tN!JI^CusEci;ZQk(}@H z_QltCKAkVV<^7B6^B!I~Ao^@hBrkL0zqK#qT-F`f^{R1h_{^UXD^r-G&GXNO=*RTm zG}+E1v2RxOHG@yAQ&PJQhLxX;S`$|-o+4+~kXU&k(fPsg2PZQg>|SKDXv5CCYpb3v z^V!%w_27rouiB@o^ReVV)0O!;<=ob1#s;#UtcSDyd1Os*4w$0Jbkb;19rJ-N1v(OD z_B9q}OVoB4zxxp5X7WW~#-+svHzY?^ZTaV|v)VdlvS6+>>$2(T2Mqo{IBgsp=9GGj zweNJtwD(7@_{TG}9ew9-)%TVsRf2zpcE_!24e8QpcPI2lG<2*s{kT`-G0WsGkLOWO z#M3fuxZ6rUvezz4Td-e?O*)k;AX{t3J3EHMm)iCB=iHOwIxIOO!Y68rU%r9uOCGkO zpPtVh*$t0rv%S>2wCL{dhL;cO4E~t>eDH6b@d6=XhUk=>1sB5%=H{@pO||wnn-#?R z_P6RAT@~G14_Ibw%4*(?FXle*i^CH zKO#o?M;jjg&fA{U6rIGVcR_7}iIctfi9KIfc(R|zRT(W|tuUOo!P(8kgu^I)%F%05 z{}<(`%j>LWUo+kC?Jvyu~?HO zxa-oz6Jc8-HBy$vu^pbZZ{>9Nv&RkZ@yz+vw}7 zY=+A#H)pJ0xBOh0HoIR&i=ol8ORH>W#I4+LJg#D?)8%gozYpt3UX*K0@Y3dKdl8n| z>VE&=xwky8yS%&W;#hswn>Qw2TXB8%uHVgW3tNfMV204|bnKScEc4&!wm>RQgZN)zctFxu8b=|W!aGla( z_;&i~tTUhMP1z2cyuH2oY`3%Onl*QecKSW~SsjM}nuQuyG6 zfG?5jr!8K<)OBu}p?InIy#04%cRvj8e7DaqfJ-~OZH~sOsZyPXkL|e6_eHa7*D-VcWs9Ana%C|DZw>Cai!gc8eC_wyvQ@p{*ST5BksG_w-nj7NGP4| zjh?mH=KrH;o$RY$dLWyhHtL9|L>bwrfSv#-2 z_ipV%gK1y)#W2>nNncnXnCE}bFiXAcUHJwFzY_EF!Tvi;Y)e-sa@|@TwQbWC{|dXG z{yMKWIj>jtTCi6uVOq?(e>VSRUVm+0?fG6U&Q<+KOBfzf1hj zZnhww^q;NXxzC+l->~jK-I)zZ0MHn;e;N=5Ju(pAv8}XQ%PO&L>-iNz zC9-oiu6g{_<^PkPpZ~pxpFQ`b{nzL%`yexPzni!4KNr=Bdc_hn8AwRL{#%GVx=Jkpo4 z^5|N(lhynD`r|Rr=f=zb5$j51e0I8H-G7#(e%+(jvg&ePK7VK-rja;Hy^N9bqUAb^ zyM@OaQjQCMU%1X9uU8~le71Di1~$8=Ki&5?K0IB;zO7_c`&?^po_Q)op|dv>Cr9l2 zbL`>0$#)m1tP<5%_FCK{oPEH+xp=|6`_XHzhi{1S+rsf{O};@%LjK!VRbsl{oc*aT zb1!fh8wIQt-R{k6%~SGiL-e{evn%Y3Ie0dzuRr`@lf?;RgHXQ`gQ`QE5$iu*_xin0 zP|4SF>lM+;f9J|2-u(Yseey&81;LFa9h1fOHY6XEePggJ+E{H~Ly?A+_cG2a(v|;C z758)uB>d!zQxw;fXhWX?XF6_c7-`R|KW&po;Qxwh-{ zMGi{+2wlgi_UTXU9JgazeE;k^UM92i>I9ADVLDc4g)*3D>!0K9a0pagGbNZ?TU++= zno#@r&UdEV(XY2w=sJ7pZfUf)Kc=zx&BWWaw-)U!N-Lfy@V0rasaWjC@9oLeddHn*Zm@S)IM^zfU@neR0qBBo`IcQ;%f0(%m*| z$4i-{n4Rl6qd0TQiOjEC=RTK3Pmxz*!yPYk1hhV2K@wv^rHU41Ni zvKnu)sT3V$_~jP6rt#AIrrSA(HIGE!P@SG_IOp)64o1(_-uKzSraUa|XTBI$GXO?aVKdsj8jMCH!G z%qK2y=LSCBbYNyId;N(k4il%Jc{KU`i#fla?AD9Apz$YRx(}n()aFvpml=AimVD`u zwlL3O2v6ZM(B={1TlnJF%Jxh9E!dadc>i!_4C6LKZ~b=;(gnMBDF0BOGsmpC*=Xuj zMLwVN%bF9v{!72yu9}p3wf(WtqRnbeFO%$bUgwMd{%Lw#ErR#=(^(I=({9H9zL?JY zJG;I>j8B^P0sBm;3{ysTg%K{rmr(?W#fsUjP*wkZn&hsy5jimfO)a} z`%hgxkdk&Y_|2W$=WRW00+H;}t*tI3wcMqaTg84Qp~(vQxPX%I#Ea ztooh>y}kIctntCYbqyztST&^19kD;Ak(_??_`8R@3wK>^Fb-wJBv%J~b zw;cJc`OO^NpHy$~%$JLP9qch#)1jo);|KHI+x;8suY6@c$dP~Q>H(p&f89SL8eZ!^ zEAKm6mCLmM^wk4~JF1_l=iKN^ubXv)<8gTXkGN$F#?6WkpSma9h<*6f{U7f_f!$(Z zr>`E!PAk;f)f2yL`8>%|dF}xDc_(gU@76F-P5!d2*6q{N^Uojn{0sgO=w`A@Z^pXE zIn5!v|2aRg)i|Wr-fFbSRoZ~t-|mRuvp*->8TVOqX0dgrxLov~DYqc=eBQ?e6%iue zn%Fk&^jpQYY|ST$DP^a$?tfyfNHpK0r5tr;^31vS;HsO~mcWucM+?cW6seR&^(nB#G7R`*@!ad>Ff~Chn{3HC}vl|9-iTNBJf8zRcYFOibA2b{*d`@8%6#|Lro#coeG9 znfmwC-7+bE*Twf6U#c8qwq8G{hjSNm>IHtCiGsR^jJwyDEbn{a_5I}Bi(6(NG}{?> zLEdM7a<;DJKWk|Vo|p@ne$!80{UPEberTh3hF;6}zq~d#r<%x35Yd%7tmAR`qJh|@ zdtdz>!eg%Iv}{}SHvOT(e+ljj2fplht~FF3(l6$dTC>yyW-t0Lk5n zYkD#^?mKgRX5NdYFGUubfr zA@S^Tanr^78y3AOH*jCaw#(q-bLRsFuP^?ba4zcLUfW5U4y&aStS$+)Kh|`QYU?|! zmcg?;)us1j!Y;|i!fP963nqs*nBO`eYw7-_WoGqu`&rxG<<34e#o>wZn2dJ2;ppEw*Tn-2XG9`$1;}GjB%!RFT~OhGp+2TJ*22ElOGbFQcXOp}FM6 z%)v2reWv}qb$GaU2zeQW9nM>?6ES3vrI(*5rU)%QzEgQQ2Os{HA`igPGjYI8Q%43zx*RR1#6sN#j3b2{@w8M zg60?IX#TlUV*IKXg#RgpH&~weW54ckgu(0n*MIAeJlwG_Cv|Hz!?G>2A{x9hO{3Mg z1SiJmcl?u``z&cr{@*Vph0hmCOj$QM^mOR?I~LO!=53Ci=l$WgOmXPJHAa^XZ~U)% z!bgr_Uhp(^{vSyizphJfSlIJL;`N)8DSuz`%?oD#|I#i&EGjJdhnL#*32hU8R+v4D z`uFh-W0uCbdrfI*| z!Iv7gIXcS0>oackUvGSVC#wJY*CoX>q&Gynh1{N5S#+D%=DGg9w_=ri)s1pWsaLW> z#plJ&W|`;xcfIc`Lzz<=e-peVU*1@H>Ag?qmXr^FszRfR`dtc=zs)M%s_Ss0)L`zx zs=xa#OMX}EKG3?C{r>5WU0YJ$y|#XwVe!vgcwhAarJJngQ{Nl3?$d3VySwVw#s%{i zD(N)rjk}a}Rg!V0bRpNqZ#lO<=e=H_qQe%f_)?9dr18v}l@jS|&(FG5W@=%~Hbeb# z)V$dS7r`q%lSI z6{qc1-pZ2^b6}pSy2RBwr+w}7Oc8cDbXg$lt{~O0rqsH=Ma`(LZ;3!R@7Q zRWy!?w7yRXTlQrqAE#vQ0fXf+ZnN!59ggofR5VlS#~YKBcdQ0)W=wvPmC^RKN`~+B z8pX^>j5CkN29`XUx}a|3g2v(v?rP!xvnO~aACkO&S)VPRQGEwPc4`r?1lP>>@0t$& z_`7>|+pZPUjQ31AX?jLfXx@VB|L#3KxyPfy%f}$)R7Y7JH>Y?((XMSzT6fj0KG1ye zVARYi*%CG(xpPM*`1~wLo4xTylY6>nUS$rj}l zxqod|KMvpjvbVohnyW2kA4VB$l?fI2#+BdsUF(wq8iWrnhak(O0X({ON@f`;UFK z6&5Vyp0hzZZi?Y_OJ2o?xmJFPW&3>JNAd32!1?TChn~s7OWbM(KK^?Xvc!2^XXr%e zDeqdRs$0aMCY2UwFQ#FgX%KMc&t^4-+2k(@`TZfG$!(-H$e9r$hP`mTtPW}Pw0@sVdMcP{C z4KMF9_jLWQa3y@kIgu@;&17 z?!C5_x0#ZAG+t4~<+Y{rv)fZ|Z1{SA-J9J~r;IKpOj+5OIB~*+R^>BI=Qne)_P9)c z=r-NeqwCsk&Slb#GhSbkZcL7M6ioG=p%BZ!k#l#CEr+zLP@4va_mmc8twm~5k6h+- zPTL_6{>bf4XJ5qxrzHj-Ra`v+gL*0^K3c+}>FG7`(h?p`?>(NDLGwZsL_Jl5W`-!* zEzw%(wraA}61ATlH9mHVTRn1v+OMd7^{BnnV4^aAlG_r)pFJjuzMjU5)b}0f|1p_C z$fL=lSjZ=8f`E`$)TFQ_rkTz>OLQ~cZ%yV~qWsduYO=wl))Ljb9_5#szo_i>uz2bF zYr;O2rZ38WPd-m!kn~`icrb-S(yMLa!xWaEO+6}$Ph=Ri^{75RQDM~RGO05~Xr|Ay z2`gJBXbAcCsI(oKWbsLLrrWZ~>re0)O+KR(KRN$Ij#2v=)z2qtHZ>fX+?pa7=`(G@ z(kIcIdUVvaPk3$WTBF99!W-#*ZQ}bARiA8cI+#t8Khgh0eN$(STJ(vqO}#nl)+hWv z8Q*lRQ`~)0ZqxKP%KIn%Rhg(VF=did&`HKgZhVuRpBPv=^-W5CVqP?7j)MA0&7xUz z6w^;y7R`%M5I?C}G&4pq{G@5o+&c>9Cv}Tv-%%_-X@r)hhX_tbau#m;Cff04`0+xXcpC%~*@-PPoI z&;RJpetP5I0%eu-3f06(J1-qLXZNVFd{O#-=9eYiM}s#h%uJDg@q)2tE^F5NBi^;r zTSA^LoXDMZ#OtY`@Rmz_7sBHotX9|9BP{*?{s#N?tCCf&N&VrM64h>8FPte|`}1Z| zmG?ZIj=VSPCN@c)zPb3~MzJ4KTw3ps@I}h5d%9FmJ!B5c3A)YC&Cbec z54L~deEKxwu4|2{_tfd88c%iZ*k!+HI(qP@aMK_DD*HvQ|8)7YA1wKl5q6btDqCJ3 zLshiMw;RWoCcijlzDeq2#P+*V3!9sdZJ9Xj?OWOD;a5Fxnfd1BzPXqodsk}XWvQ2^ zc;@jN?P-$rGGzX4&T=bz>*S6Ues-wcrY2Yc)gMW7|Zjm0`CodtA?JtXvQqv|0RTv{tRvaXF#V zO2d-q=BSXh7ycfbSb1;?)0=$u$>uL7&(#iCdnj(F=-!lBXc-*3FQFHEF^R>H^{_e3=w`M!F zK80yKY2yV7&! zooiOH-|R0oeu9S%a6GLRcz^gq;=gZV8vDyP?F$uuD3)h9^|(}MW6(kWQ(+f=eG~bj zxj15R!m>2&7wxs`ubJ0;U;bDy_gB(_=asGJs(%0f&EEfP^1rD}`}8xeOK@}a&)KlR z_2BG>w-5fV<=XOXN|EXTL)GHo+>;Ui8@YbmbF=33zabXCu+K^Ko!j;Wf7ND(zs>V^ zEH0m9-(Rp%=EJ&2>8E$I{`t50FKh0fogPB>yrZjXi@n$Xzd!3=?RxY2Z7;vpf1kT% z{oU{*9PhU*Xw7t7-Q*@?`{jSIqjOQ|vI+bV7anwq3CLYM7c!}A*~0(#U;E5`jNh|C2A@?g^1Qla_vze;j7D%K~jym(ejnm4^*$2R4^krUr870nKj zaq`}zy;X?S_s{KjCk>7sI<(kok$mXK+nav|?$lK)Ub6gvY;oY;?~9e~cF4a~c%!7c zuSt0E0Y#C&8J#(GTl#ESO>G;ut@DZKzSD5$!Q}eg3mRD({%xM38N)8M>z7O7+;j4q zmoNCg<(ba=97SK}^te-Q(FyL0{WCfvA^lM zEBfA5ZZ*06bmEix%?nPIUhr4HcslnUukP+G$w__}0&0&e7UaK{SNr+nCC`P2VqzJ0 z?`ajr`#8{6n3b){Ok+ozK5sKJwYP#l2s9Py3pq*FV2MZ$4Xp-u&NT7V&lBe05#T z??3ae)SV}uzO1Y3kKLL1aoHx@Lp~IJ{1c>T?>e>eM){4RguRolY-q2jn`@A#Y4>j@ z)4ofaYSia_f2T3;e_+Ln6^q37^Sy3=$GhFl=Kh10in{$9isvM`P4&37r(%Y}JpCk* z^Yg`Tc^AG}J?qE!qaO<9RqVUL@$pY-qTG?ZSd%kChkd(CyDfZQ_~v&mLaik?%QMW z&)g)UDrEJX=i1UO{JmKw)9vDYD+Dxh^g^w||A`*`Y?q|6edx;A(E$29QbFR+~xW=N*6?r_dVSy z;&nBL&EU4Y$mtc#YkQwR?|N_GFlA{aV`-M-55M(sm(N=H&;I|n@Bf7c`|Gpz=V#U} z|G$szg0Il$mCc)5`ooz*>KQLc&TYJ1eyH5;mudQO`@3Hwgk`^RoRayaQTY7LW`E;7 zKfb2&rBZRui-bt%<`_Q_EXMMkraTE9JQ$JPCyfa;Ny+dku zS*z*nxdzqY5ls8a>?0RFY+ds;ZnwYMy)~0UL$}|1-LifE`bNI4gDJC$Tk-VQRo-ua_@8FWe2S?)&dmYxwU-+gp2=y))##f4jO=bLlVfDl3t? RwY!6F{P^^ragp^+Jpc%4;iCWm literal 134652 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ͱ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�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

    {d$qmtM3ctD71#HDPWAlR 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 @@ -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() diff --git a/os/emscripten/emsdk-liblzma.patch b/os/emscripten/emsdk-liblzma.patch deleted file mode 100644 index 7bfdd47de2..0000000000 --- a/os/emscripten/emsdk-liblzma.patch +++ /dev/null @@ -1,198 +0,0 @@ -From 84d0e9112d5c87a714abd21ec8547921f46f37b5 Mon Sep 17 00:00:00 2001 -From: milek7 -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 diff --git a/os/emscripten/ports/liblzma.py b/os/emscripten/ports/liblzma.py new file mode 100644 index 0000000000..0adf0e5697 --- /dev/null +++ b/os/emscripten/ports/liblzma.py @@ -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" +''' diff --git a/os/macosx/Info.plist.in b/os/macosx/Info.plist.in index 71619d8936..9ed3579a09 100644 --- a/os/macosx/Info.plist.in +++ b/os/macosx/Info.plist.in @@ -33,5 +33,7 @@ True LSMinimumSystemVersion 10.13.0 + LSApplicationCategoryType + public.app-category.simulation-games diff --git a/os/macosx/openttd.entitlements b/os/macosx/openttd.entitlements new file mode 100644 index 0000000000..47c24e0756 --- /dev/null +++ b/os/macosx/openttd.entitlements @@ -0,0 +1,11 @@ + + + + + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.disable-library-validation + + + diff --git a/regression/regression/info.nut b/regression/regression/info.nut index 9c775eede5..383f219cd7 100644 --- a/regression/regression/info.nut +++ b/regression/regression/info.nut @@ -4,7 +4,7 @@ class Regression extends AIInfo { function GetShortName() { return "REGR"; } function GetDescription() { return "This runs regression-tests on some commands. On the same map the result should always be the same."; } function GetVersion() { return 1; } - function GetAPIVersion() { return "14"; } + function GetAPIVersion() { return "15"; } function GetDate() { return "2007-03-18"; } function CreateInstance() { return "Regression"; } function UseAsRandomAI() { return false; } diff --git a/regression/regression/result.txt b/regression/regression/result.txt index 8eedd0d71e..27a81675a9 100644 --- a/regression/regression/result.txt +++ b/regression/regression/result.txt @@ -6093,7 +6093,7 @@ ERROR: IsEnd() is invalid as Begin() is never called GetNumEngines(): 1 GetNumEngines(): 1 GetNumEngines(): 0 - GetName(): Group 0 + GetName(): Group 1 GetName(): (null : 0x00000000) AIVehicle.SellVehicle(): true AITile.DemolishTile(): true @@ -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 diff --git a/regression/stationlist/info.nut b/regression/stationlist/info.nut index f3a1684876..a1a10466dd 100644 --- a/regression/stationlist/info.nut +++ b/regression/stationlist/info.nut @@ -4,7 +4,7 @@ class StationList extends AIInfo { function GetShortName() { return "REGS"; } function GetDescription() { return "This runs stationlist-tests on some commands. On the same map the result should always be the same."; } function GetVersion() { return 1; } - function GetAPIVersion() { return "14"; } + function GetAPIVersion() { return "15"; } function GetDate() { return "2007-03-18"; } function CreateInstance() { return "StationList"; } function UseAsRandomAI() { return false; } diff --git a/src/3rdparty/icu/CMakeLists.txt b/src/3rdparty/icu/CMakeLists.txt index 09afc44926..d8a085cc4e 100644 --- a/src/3rdparty/icu/CMakeLists.txt +++ b/src/3rdparty/icu/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(tests) + add_files( scriptrun.cpp scriptrun.h diff --git a/src/3rdparty/icu/scriptrun.cpp b/src/3rdparty/icu/scriptrun.cpp index d0ed37dcb9..22bcb7e8db 100644 --- a/src/3rdparty/icu/scriptrun.cpp +++ b/src/3rdparty/icu/scriptrun.cpp @@ -184,13 +184,6 @@ UBool ScriptRun::next() parenStack[++startSP].scriptCode = scriptCode; } } - - // if this character is a close paired character, - // pop it from the stack - if (pairIndex >= 0 && (pairIndex & 1) != 0 && parenSP >= 0) { - parenSP -= 1; - startSP -= 1; - } } else { // if the run broke on a surrogate pair, // end it before the high surrogate diff --git a/src/3rdparty/icu/tests/CMakeLists.txt b/src/3rdparty/icu/tests/CMakeLists.txt new file mode 100644 index 0000000000..db2b9150a3 --- /dev/null +++ b/src/3rdparty/icu/tests/CMakeLists.txt @@ -0,0 +1,4 @@ +add_test_files( + test_srtest.cpp + CONDITION ICU_i18n_FOUND +) diff --git a/src/3rdparty/icu/tests/test_srtest.cpp b/src/3rdparty/icu/tests/test_srtest.cpp new file mode 100644 index 0000000000..6922fffdd3 --- /dev/null +++ b/src/3rdparty/icu/tests/test_srtest.cpp @@ -0,0 +1,66 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + * %W% %E% + * + * (C) Copyright IBM Corp. 2001-2016 - All Rights Reserved + * + */ +/** @file test_srtest.cpp Test icu::ScriptRun result. */ + +#include "../../../stdafx.h" + +#include "../../../3rdparty/catch2/catch.hpp" +#include "../../../3rdparty/fmt/core.h" + +#include "../scriptrun.h" + +#include +#include + +static void TestScriptRun(std::span testChars, std::span testResults) +{ + icu::ScriptRun scriptRun(testChars.data(), 0, std::size(testChars)); + size_t i = 0; + + while (scriptRun.next()) { + int32_t start = scriptRun.getScriptStart(); + int32_t end = scriptRun.getScriptEnd(); + UScriptCode code = scriptRun.getScriptCode(); + + REQUIRE(i < std::size(testResults)); + CHECK(fmt::format("Script '{}' from {} to {}.", uscript_getName(code), start, end) == testResults[i]); + + ++i; + } + + REQUIRE(i == std::size(testResults)); +} + +TEST_CASE("ICU ScriptRun") +{ + /** Example string sequence as in srtest.cpp. */ + static const char16_t testChars[] = { + 0x0020, 0x0946, 0x0939, 0x093F, 0x0928, 0x094D, 0x0926, 0x0940, 0x0020, + 0x0627, 0x0644, 0x0639, 0x0631, 0x0628, 0x064A, 0x0629, 0x0020, + 0x0420, 0x0443, 0x0441, 0x0441, 0x043A, 0x0438, 0x0439, 0x0020, + 'E', 'n', 'g', 'l', 'i', 's', 'h', 0x0020, + 0x6F22, 0x5B75, 0x3068, 0x3072, 0x3089, 0x304C, 0x306A, 0x3068, + 0x30AB, 0x30BF, 0x30AB, 0x30CA, + 0xD801, 0xDC00, 0xD801, 0xDC01, 0xD801, 0xDC02, 0xD801, 0xDC03 + }; + + /** Expected results from script run. */ + static const std::string_view testResults[] = { + "Script 'Devanagari' from 0 to 9.", + "Script 'Arabic' from 9 to 17.", + "Script 'Cyrillic' from 17 to 25.", + "Script 'Latin' from 25 to 33.", + "Script 'Han' from 33 to 35.", + "Script 'Hiragana' from 35 to 41.", + "Script 'Katakana' from 41 to 45.", + "Script 'Deseret' from 45 to 53.", + }; + + TestScriptRun(testChars, testResults); +} diff --git a/src/3rdparty/md5/md5.h b/src/3rdparty/md5/md5.h index 29c7167944..a19f159168 100644 --- a/src/3rdparty/md5/md5.h +++ b/src/3rdparty/md5/md5.h @@ -57,8 +57,8 @@ static const size_t MD5_HASH_BYTES = 16; /** Container for storing a MD5 hash/checksum/digest. */ -struct MD5Hash : std::array { - MD5Hash() : std::array{} {} +struct MD5Hash : std::array { + MD5Hash() : std::array{} {} /** * Exclusively-or the given hash into this hash. diff --git a/src/3rdparty/squirrel/squirrel/sqcompiler.cpp b/src/3rdparty/squirrel/squirrel/sqcompiler.cpp index 6f67796839..560edfc66b 100644 --- a/src/3rdparty/squirrel/squirrel/sqcompiler.cpp +++ b/src/3rdparty/squirrel/squirrel/sqcompiler.cpp @@ -650,8 +650,7 @@ public: _fs->AddInstruction(_OP_LOADINT, _exst._deref,_integer(constval)); } else if(ctype == OT_FLOAT && sizeof(SQFloat) == sizeof(SQInt32)) { - SQFloat f = _float(constval); - _fs->AddInstruction(_OP_LOADFLOAT, _exst._deref,*((SQInt32 *)&f)); + _fs->AddInstruction(_OP_LOADFLOAT, _exst._deref, std::bit_cast(_float(constval))); } else { _fs->AddInstruction(_OP_LOAD, _exst._deref, _fs->GetConstant(constval)); @@ -697,7 +696,7 @@ public: break; case TK_FLOAT: if(sizeof(SQFloat) == sizeof(SQInt32)) { - _fs->AddInstruction(_OP_LOADFLOAT, _fs->PushTarget(),*((SQInt32 *)&_lex._fvalue)); + _fs->AddInstruction(_OP_LOADFLOAT, _fs->PushTarget(), std::bit_cast(_lex._fvalue)); } else { _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._fvalue)); diff --git a/src/3rdparty/squirrel/squirrel/sqfuncstate.cpp b/src/3rdparty/squirrel/squirrel/sqfuncstate.cpp index e6e64ba81a..54744292a9 100644 --- a/src/3rdparty/squirrel/squirrel/sqfuncstate.cpp +++ b/src/3rdparty/squirrel/squirrel/sqfuncstate.cpp @@ -199,7 +199,7 @@ void SQFuncState::Dump(SQFunctionProto *func) } } else if(inst.op==_OP_LOADFLOAT) { - printf("[%03d] %15s %d %f %d %d\n",n,g_InstrDesc[inst.op].name,inst._arg0,*((SQFloat*)&inst._arg1),inst._arg2,inst._arg3); + printf("[%03d] %15s %d %f %d %d\n",n,g_InstrDesc[inst.op].name,inst._arg0,std::bit_cast(inst._arg1),inst._arg2,inst._arg3); } else if(inst.op==_OP_ARITH){ printf("[%03d] %15s %d %d %d %c\n",n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); @@ -242,19 +242,20 @@ SQInteger SQFuncState::GetConstant(const SQObject &cons) void SQFuncState::SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3) { - _instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&arg0); - _instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&arg1); - _instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&arg2); - _instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&arg3); + _instructions[pos]._arg0 = (unsigned char)std::bit_cast(arg0); + _instructions[pos]._arg1 = (SQInt32)std::bit_cast(arg1); + _instructions[pos]._arg2 = (unsigned char)std::bit_cast(arg2); + _instructions[pos]._arg3 = (unsigned char)std::bit_cast(arg3); } void SQFuncState::SetIntructionParam(SQInteger pos,SQInteger arg,SQInteger val) { switch(arg){ - case 0:_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&val);break; - case 1:case 4:_instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&val);break; - case 2:_instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&val);break; - case 3:_instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&val);break; + case 0: _instructions[pos]._arg0 = (unsigned char)std::bit_cast(val); break; + case 1: + case 4: _instructions[pos]._arg1 = (SQInt32)std::bit_cast(val); break; + case 2: _instructions[pos]._arg2 = (unsigned char)std::bit_cast(val); break; + case 3: _instructions[pos]._arg3 = (unsigned char)std::bit_cast(val); break; }; } diff --git a/src/3rdparty/squirrel/squirrel/sqvm.cpp b/src/3rdparty/squirrel/squirrel/sqvm.cpp index 02dbc1625d..52a4dfe85c 100644 --- a/src/3rdparty/squirrel/squirrel/sqvm.cpp +++ b/src/3rdparty/squirrel/squirrel/sqvm.cpp @@ -51,7 +51,7 @@ bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,con case BW_XOR: res = i1 ^ i2; break; case BW_SHIFTL: res = i1 << i2; break; case BW_SHIFTR: res = i1 >> i2; break; - case BW_USHIFTR:res = (SQInteger)(*((SQUnsignedInteger*)&i1) >> i2); break; + case BW_USHIFTR:res = (SQInteger)(std::bit_cast(i1) >> i2); break; default: { Raise_Error("internal vm error bitwise op failed"); return false; } } } @@ -472,10 +472,10 @@ bool SQVM::DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjec #define arg0 (_i_._arg0) #define arg1 (_i_._arg1) -#define sarg1 (*(const_cast(&_i_._arg1))) +#define sarg1 (std::bit_cast(_i_._arg1)) #define arg2 (_i_._arg2) #define arg3 (_i_._arg3) -#define sarg3 ((SQInteger)*((const signed char *)&_i_._arg3)) +#define sarg3 ((SQInteger)std::bit_cast(_i_._arg3)) SQRESULT SQVM::Suspend() { @@ -764,7 +764,7 @@ exception_restore: continue; case _OP_LOAD: TARGET = ci->_literals[arg1]; continue; case _OP_LOADINT: TARGET = (SQInteger)arg1; continue; - case _OP_LOADFLOAT: TARGET = *((const SQFloat *)&arg1); continue; + case _OP_LOADFLOAT: TARGET = std::bit_cast(arg1); continue; case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue; case _OP_TAILCALL: temp_reg = STK(arg1); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ebb946ae4e..599d45aa5e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,6 +39,11 @@ add_files( CONDITION ICU_i18n_FOUND AND HARFBUZZ_FOUND ) +add_files( + soundloader_opus.cpp + CONDITION OpusFile_FOUND +) + add_files( aircraft.h aircraft_cmd.cpp @@ -50,8 +55,11 @@ add_files( airport_gui.cpp animated_tile.cpp animated_tile_func.h + animated_tile_map.h articulated_vehicles.cpp articulated_vehicles.h + autocompletion.cpp + autocompletion.h autoreplace.cpp autoreplace_base.h autoreplace_cmd.cpp @@ -75,6 +83,7 @@ add_files( bridge_map.cpp bridge_map.h build_vehicle_gui.cpp + cachecheck.cpp cargo_type.h cargoaction.cpp cargoaction.h @@ -136,6 +145,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 @@ -248,6 +261,7 @@ add_files( music_gui.cpp newgrf.cpp newgrf.h + newgrf_act5.h newgrf_airport.cpp newgrf_airport.h newgrf_airporttiles.cpp @@ -299,6 +313,7 @@ add_files( newgrf_storage.h newgrf_text.cpp newgrf_text.h + newgrf_text_type.h newgrf_town.cpp newgrf_town.h newgrf_townname.cpp @@ -330,8 +345,12 @@ add_files( palette_func.h pbs.cpp pbs.h + picker_func.h + picker_gui.cpp + picker_gui.h progress.cpp progress.h + provider_manager.h querystring_gui.h rail.cpp rail.h @@ -391,6 +410,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 @@ -401,6 +422,11 @@ add_files( sound.cpp sound_func.h sound_type.h + soundloader.cpp + soundloader_func.h + soundloader_type.h + soundloader_raw.cpp + soundloader_wav.cpp sprite.cpp sprite.h spritecache.cpp @@ -466,7 +492,6 @@ add_files( tilearea_type.h tilehighlight_func.h tilehighlight_type.h - tilematrix_type.hpp timetable.h timetable_cmd.cpp timetable_cmd.h diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp index d1cc955395..358ab4f834 100644 --- a/src/ai/ai_core.cpp +++ b/src/ai/ai_core.cpp @@ -40,7 +40,7 @@ /* Clients shouldn't start AIs */ if (_networking && !_network_server) return; - Backup cur_company(_current_company, company, FILE_LINE); + Backup cur_company(_current_company, company); Company *c = Company::Get(company); AIConfig *config = c->ai_config.get(); @@ -60,7 +60,7 @@ c->ai_info = info; assert(c->ai_instance == nullptr); - c->ai_instance = new AIInstance(); + c->ai_instance = std::make_unique(); c->ai_instance->Initialize(info); c->ai_instance->LoadOnStack(config->GetToLoadData()); config->SetToLoadData(nullptr); @@ -81,7 +81,7 @@ assert(_settings_game.difficulty.competitor_speed <= 4); if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return; - Backup cur_company(_current_company, FILE_LINE); + Backup cur_company(_current_company); for (const Company *c : Company::Iterate()) { if (c->is_ai) { PerformanceMeasurer framerate((PerformanceElement)(PFE_AI0 + c->index)); @@ -109,11 +109,10 @@ if (_networking && !_network_server) return; PerformanceMeasurer::SetInactive((PerformanceElement)(PFE_AI0 + company)); - Backup cur_company(_current_company, company, FILE_LINE); + Backup 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(); @@ -129,7 +128,7 @@ * for the server owner to unpause the script again. */ if (_network_dedicated) return; - Backup cur_company(_current_company, company, FILE_LINE); + Backup cur_company(_current_company, company); Company::Get(company)->ai_instance->Pause(); cur_company.Restore(); @@ -137,7 +136,7 @@ /* static */ void AI::Unpause(CompanyID company) { - Backup cur_company(_current_company, company, FILE_LINE); + Backup cur_company(_current_company, company); Company::Get(company)->ai_instance->Unpause(); cur_company.Restore(); @@ -145,7 +144,7 @@ /* static */ bool AI::IsPaused(CompanyID company) { - Backup cur_company(_current_company, company, FILE_LINE); + Backup cur_company(_current_company, company); bool paused = Company::Get(company)->ai_instance->IsPaused(); cur_company.Restore(); @@ -243,37 +242,30 @@ /* static */ void AI::NewEvent(CompanyID company, ScriptEvent *event) { - /* AddRef() and Release() need to be called at least once, so do it here */ - event->AddRef(); + ScriptObjectRef counter(event); /* Clients should ignore events */ if (_networking && !_network_server) { - event->Release(); return; } /* Only AIs can have an event-queue */ if (!Company::IsValidAiID(company)) { - event->Release(); return; } /* Queue the event */ - Backup cur_company(_current_company, company, FILE_LINE); + Backup cur_company(_current_company, company); Company::Get(_current_company)->ai_instance->InsertEvent(event); cur_company.Restore(); - - event->Release(); } /* static */ void AI::BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company) { - /* AddRef() and Release() need to be called at least once, so do it here */ - event->AddRef(); + ScriptObjectRef counter(event); /* Clients should ignore events */ if (_networking && !_network_server) { - event->Release(); return; } @@ -281,8 +273,6 @@ for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { if (c != skip_company) AI::NewEvent(c, event); } - - event->Release(); } /* static */ void AI::Save(CompanyID company) @@ -293,7 +283,7 @@ /* When doing emergency saving, an AI can be not fully initialised. */ if (c->ai_instance != nullptr) { - Backup cur_company(_current_company, company, FILE_LINE); + Backup cur_company(_current_company, company); c->ai_instance->Save(); cur_company.Restore(); return; diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index 0cd83ea8b3..387e1dde8d 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -82,11 +82,11 @@ static constexpr NWidgetPart _nested_ai_config_widgets[] = { }; /** Window definition for the configure AI window. */ -static WindowDesc _ai_config_desc(__FILE__, __LINE__, +static WindowDesc _ai_config_desc( WDP_CENTER, nullptr, 0, 0, WC_GAME_OPTIONS, WC_NONE, 0, - std::begin(_nested_ai_config_widgets), std::end(_nested_ai_config_widgets) + _nested_ai_config_widgets ); /** @@ -97,7 +97,7 @@ struct AIConfigWindow : public Window { int line_height; ///< Height of a single AI-name line. Scrollbar *vscroll; ///< Cache of the vertical scrollbar. - AIConfigWindow() : Window(&_ai_config_desc) + AIConfigWindow() : Window(_ai_config_desc) { this->InitNested(WN_GAME_OPTIONS_AI); // Initializes 'this->line_height' as a side effect. this->vscroll = this->GetScrollbar(WID_AIC_SCROLLBAR); @@ -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; } } diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp index eb3400b244..fccfd94871 100644 --- a/src/ai/ai_info.cpp +++ b/src/ai/ai_info.cpp @@ -24,7 +24,7 @@ */ static bool CheckAPIVersion(const std::string &api_version) { - static const std::set versions = { "0.7", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14" }; + static const std::set versions = { "0.7", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14", "15" }; return versions.find(api_version) != versions.end(); } diff --git a/src/aircraft.h b/src/aircraft.h index c05d21a1ca..a6b747d5cf 100644 --- a/src/aircraft.h +++ b/src/aircraft.h @@ -17,12 +17,10 @@ * Base values for flight levels above ground level for 'normal' flight and holding patterns. * Due to speed and direction, the actual flight level may be higher. */ -enum AircraftFlyingAltitude { - AIRCRAFT_MIN_FLYING_ALTITUDE = 120, ///< Minimum flying altitude above tile. - AIRCRAFT_MAX_FLYING_ALTITUDE = 360, ///< Maximum flying altitude above tile. - PLANE_HOLD_MAX_FLYING_ALTITUDE = 150, ///< holding flying altitude above tile of planes. - HELICOPTER_HOLD_MAX_FLYING_ALTITUDE = 184 ///< holding flying altitude above tile of helicopters. -}; +static constexpr int AIRCRAFT_MIN_FLYING_ALTITUDE = 120; ///< Minimum flying altitude above tile. +static constexpr int AIRCRAFT_MAX_FLYING_ALTITUDE = 360; ///< Maximum flying altitude above tile. +static constexpr int PLANE_HOLD_MAX_FLYING_ALTITUDE = 150; ///< holding flying altitude above tile of planes. +static constexpr int HELICOPTER_HOLD_MAX_FLYING_ALTITUDE = 184; ///< holding flying altitude above tile of helicopters. struct Aircraft; @@ -73,14 +71,14 @@ struct AircraftCache { */ struct Aircraft final : public SpecializedVehicle { uint16_t crashed_counter; ///< Timer for handling crash animations. - byte pos; ///< Next desired position of the aircraft. - byte previous_pos; ///< Previous desired position of the aircraft. + uint8_t pos; ///< Next desired position of the aircraft. + uint8_t previous_pos; ///< Previous desired position of the aircraft. StationID targetairport; ///< Airport to go to next. - byte state; ///< State of the airport. @see AirportMovementStates + uint8_t state; ///< State of the airport. @see AirportMovementStates Direction last_direction; - byte number_consecutive_turns; ///< Protection to prevent the aircraft of making a lot of turns in order to reach a specific point. - byte turn_counter; ///< Ticks between each turn to prevent > 45 degree turns. - byte flags; ///< Aircraft flags. @see AirVehicleFlags + uint8_t number_consecutive_turns; ///< Protection to prevent the aircraft of making a lot of turns in order to reach a specific point. + uint8_t turn_counter; ///< Ticks between each turn to prevent > 45 degree turns. + uint8_t flags; ///< Aircraft flags. @see AirVehicleFlags AircraftCache acache; diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index 41208608d0..1b8ed4e7af 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -134,7 +134,7 @@ static StationID FindNearestHangar(const Aircraft *v) const Station *next_dest = nullptr; if (max_range != 0) { if (v->current_order.IsType(OT_GOTO_STATION) || - (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDepotActionType() != ODATFB_NEAREST_DEPOT)) { + (v->current_order.IsType(OT_GOTO_DEPOT) && (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0)) { last_dest = Station::GetIfValid(v->last_station_visited); next_dest = Station::GetIfValid(v->current_order.GetDestination()); } else { @@ -630,13 +630,11 @@ void UpdateAircraftCache(Aircraft *v, bool update_range) /** * Special velocities for aircraft */ -enum AircraftSpeedLimits { - SPEED_LIMIT_TAXI = 50, ///< Maximum speed of an aircraft while taxiing - SPEED_LIMIT_APPROACH = 230, ///< Maximum speed of an aircraft on finals - SPEED_LIMIT_BROKEN = 320, ///< Maximum speed of an aircraft that is broken - SPEED_LIMIT_HOLD = 425, ///< Maximum speed of an aircraft that flies the holding pattern - SPEED_LIMIT_NONE = 0xFFFF, ///< No environmental speed limit. Speed limit is type dependent -}; +static constexpr uint16_t SPEED_LIMIT_TAXI = 50; ///< Maximum speed of an aircraft while taxiing +static constexpr uint16_t SPEED_LIMIT_APPROACH = 230; ///< Maximum speed of an aircraft on finals +static constexpr uint16_t SPEED_LIMIT_BROKEN = 320; ///< Maximum speed of an aircraft that is broken +static constexpr uint16_t SPEED_LIMIT_HOLD = 425; ///< Maximum speed of an aircraft that flies the holding pattern +static constexpr uint16_t SPEED_LIMIT_NONE = UINT16_MAX; ///< No environmental speed limit. Speed limit is type dependent /** * Sets the new speed for an aircraft @@ -655,7 +653,7 @@ static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, * ~ acceleration * 77 (km-ish/h / 256) */ uint spd = v->acceleration * 77; - byte t; + uint8_t t; /* Adjust speed limits by plane speed factor to prevent taxiing * and take-off speeds being too low. */ @@ -672,7 +670,7 @@ static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, speed_limit = v->vcache.cached_max_speed; } - v->subspeed = (t = v->subspeed) + (byte)spd; + v->subspeed = (t = v->subspeed) + (uint8_t)spd; /* Aircraft's current speed is used twice so that very fast planes are * forced to slow down rapidly in the short distance needed. The magic @@ -699,7 +697,7 @@ static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, spd = v->GetOldAdvanceSpeed(spd); spd += v->progress; - v->progress = (byte)spd; + v->progress = (uint8_t)spd; return spd >> 8; } @@ -825,7 +823,7 @@ template int GetAircraftFlightLevel(Aircraft *v, bool takeoff); * @param rotation The rotation of the airport. * @return The index of the entry point */ -static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation) +static uint8_t AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation) { assert(v != nullptr); assert(apc != nullptr); @@ -1288,7 +1286,7 @@ void HandleMissingAircraftOrders(Aircraft *v) */ const Station *st = GetTargetAirportIfValid(v); if (st == nullptr) { - Backup cur_company(_current_company, v->owner, FILE_LINE); + Backup cur_company(_current_company, v->owner); CommandCost ret = Command::Do(DC_EXEC, v->index, DepotCommand::None, {}); cur_company.Restore(); @@ -1322,10 +1320,10 @@ void Aircraft::MarkDirty() uint Aircraft::Crash(bool flooded) { - uint pass = Vehicle::Crash(flooded) + 2; // pilots + uint victims = Vehicle::Crash(flooded) + 2; // pilots this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded - return pass; + return victims; } /** @@ -1336,8 +1334,8 @@ static void CrashAirplane(Aircraft *v) { CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); - uint pass = v->Crash(); - SetDParam(0, pass); + uint victims = v->Crash(); + SetDParam(0, victims); v->cargo.Truncate(); v->Next()->cargo.Truncate(); @@ -1351,8 +1349,8 @@ static void CrashAirplane(Aircraft *v) newsitem = STR_NEWS_AIRCRAFT_CRASH; } - AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING)); - Game::NewEvent(new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING)); + AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims)); + Game::NewEvent(new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims)); NewsType newstype = NT_ACCIDENT; if (v->owner != _local_company) { @@ -1654,7 +1652,7 @@ static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass /* Send the helicopter to a hangar if needed for replacement */ if (v->NeedsAutomaticServicing()) { - Backup cur_company(_current_company, v->owner, FILE_LINE); + Backup cur_company(_current_company, v->owner); Command::Do(DC_EXEC, v->index, DepotCommand::Service | DepotCommand::LocateHangar, {}); cur_company.Restore(); } @@ -1669,7 +1667,7 @@ static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc) /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41}, * if it is an airplane, look for LANDING, for helicopter HELILANDING * it is possible to choose from multiple landing runways, so loop until a free one is found */ - byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING; + uint8_t landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING; const AirportFTA *current = apc->layout[v->pos].next; while (current != nullptr) { if (current->heading == landingtype) { @@ -1705,7 +1703,7 @@ static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *) /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */ if (v->NeedsAutomaticServicing()) { - Backup cur_company(_current_company, v->owner, FILE_LINE); + Backup cur_company(_current_company, v->owner); Command::Do(DC_EXEC, v->index, DepotCommand::Service, {}); cur_company.Restore(); } @@ -1816,8 +1814,8 @@ static bool AirportMove(Aircraft *v, const AirportFTAClass *apc) const AirportFTA *current = &apc->layout[v->pos]; /* we have arrived in an important state (eg terminal, hangar, etc.) */ if (current->heading == v->state) { - byte prev_pos = v->pos; // location could be changed in state, so save it before-hand - byte prev_state = v->state; + uint8_t prev_pos = v->pos; // location could be changed in state, so save it before-hand + uint8_t prev_state = v->state; _aircraft_state_handlers[v->state](v, apc); if (v->state != FLYING) v->previous_pos = prev_pos; if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v); @@ -1953,7 +1951,7 @@ static const MovementTerminalMapping _airport_terminal_mapping[] = { * @param last_terminal Terminal number to stop examining. * @return A terminal or helipad has been found, and has been assigned to the aircraft. */ -static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal) +static bool FreeTerminal(Aircraft *v, uint8_t i, uint8_t last_terminal) { assert(last_terminal <= lengthof(_airport_terminal_mapping)); Station *st = Station::Get(v->targetairport); diff --git a/src/aircraft_gui.cpp b/src/aircraft_gui.cpp index cb7eae122a..ee2bf15ea3 100644 --- a/src/aircraft_gui.cpp +++ b/src/aircraft_gui.cpp @@ -97,6 +97,17 @@ void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, Eng PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); seq.Draw(x, y, pal, (v->vehstatus & VS_CRASHED) != 0); + + /* Aircraft can store cargo in their shadow, show this if present. */ + const Vehicle *u = v->Next(); + assert(u != nullptr); + int dx = 0; + if (u->cargo_cap > 0 && u->cargo_type != v->cargo_type) { + dx = GetLargestCargoIconSize().width / 2; + DrawCargoIconOverlay(x + dx, y, u->cargo_type); + } + if (v->cargo_cap > 0) DrawCargoIconOverlay(x - dx, y, v->cargo_type); + if (helicopter) { const Aircraft *a = Aircraft::From(v); VehicleSpriteSeq rotor_seq; diff --git a/src/airport.cpp b/src/airport.cpp index 936087f3c0..a1e68b0629 100644 --- a/src/airport.cpp +++ b/src/airport.cpp @@ -110,12 +110,12 @@ AirportMovingData RotateAirportMovingData(const AirportMovingData *orig, Directi AirportFTAClass::AirportFTAClass( const AirportMovingData *moving_data_, - const byte *terminals_, - const byte num_helipads_, - const byte *entry_points_, + const uint8_t *terminals_, + const uint8_t num_helipads_, + const uint8_t *entry_points_, Flags flags_, const AirportFTAbuildup *apFA, - byte delta_z_ + uint8_t delta_z_ ) : moving_data(moving_data_), terminals(terminals_), @@ -204,7 +204,7 @@ static AirportFTA *AirportBuildAutomata(uint nofelements, const AirportFTAbuildu * @param airport_type %Airport type to query FTA from. @see AirportTypes * @return Finite state machine of the airport. */ -const AirportFTAClass *GetAirport(const byte airport_type) +const AirportFTAClass *GetAirport(const uint8_t airport_type) { if (airport_type == AT_DUMMY) return &_airportfta_dummy; return AirportSpec::Get(airport_type)->fsm; @@ -215,7 +215,7 @@ const AirportFTAClass *GetAirport(const byte airport_type) * @param hangar_tile The tile on which the vehicle is build * @return The position (index in airport node array) where the aircraft ends up */ -byte GetVehiclePosOnBuild(TileIndex hangar_tile) +uint8_t GetVehiclePosOnBuild(TileIndex hangar_tile) { const Station *st = Station::GetByTile(hangar_tile); const AirportFTAClass *apc = st->airport.GetFTA(); diff --git a/src/airport.h b/src/airport.h index ac831c0971..d5df10e275 100644 --- a/src/airport.h +++ b/src/airport.h @@ -152,12 +152,12 @@ public: AirportFTAClass( const AirportMovingData *moving_data, - const byte *terminals, - const byte num_helipads, - const byte *entry_points, + const uint8_t *terminals, + const uint8_t num_helipads, + const uint8_t *entry_points, Flags flags, const AirportFTAbuildup *apFA, - byte delta_z + uint8_t delta_z ); ~AirportFTAClass(); @@ -167,7 +167,7 @@ public: * @param position Element number to get movement data about. * @return Pointer to the movement data. */ - const AirportMovingData *MovingData(byte position) const + const AirportMovingData *MovingData(uint8_t position) const { assert(position < nofelements); return &moving_data[position]; @@ -175,12 +175,12 @@ public: const AirportMovingData *moving_data; ///< Movement data. struct AirportFTA *layout; ///< state machine for airport - const byte *terminals; ///< %Array with the number of terminal groups, followed by the number of terminals in each group. - const byte num_helipads; ///< Number of helipads on this airport. When 0 helicopters will go to normal terminals. + const uint8_t *terminals; ///< %Array with the number of terminal groups, followed by the number of terminals in each group. + const uint8_t num_helipads; ///< Number of helipads on this airport. When 0 helicopters will go to normal terminals. Flags flags; ///< Flags for this airport type. - byte nofelements; ///< number of positions the airport consists of - const byte *entry_points; ///< when an airplane arrives at this airport, enter it at position entry_point, index depends on direction - byte delta_z; ///< Z adjustment for helicopter pads + uint8_t nofelements; ///< number of positions the airport consists of + const uint8_t *entry_points; ///< when an airplane arrives at this airport, enter it at position entry_point, index depends on direction + uint8_t delta_z; ///< Z adjustment for helicopter pads }; DECLARE_ENUM_AS_BIT_SET(AirportFTAClass::Flags) @@ -190,12 +190,12 @@ DECLARE_ENUM_AS_BIT_SET(AirportFTAClass::Flags) struct AirportFTA { AirportFTA *next; ///< possible extra movement choices from this position uint64_t block; ///< 64 bit blocks (st->airport.flags), should be enough for the most complex airports - byte position; ///< the position that an airplane is at - byte next_position; ///< next position from this position - byte heading; ///< heading (current orders), guiding an airplane to its target on an airport + uint8_t position; ///< the position that an airplane is at + uint8_t next_position; ///< next position from this position + uint8_t heading; ///< heading (current orders), guiding an airplane to its target on an airport }; -const AirportFTAClass *GetAirport(const byte airport_type); -byte GetVehiclePosOnBuild(TileIndex hangar_tile); +const AirportFTAClass *GetAirport(const uint8_t airport_type); +uint8_t GetVehiclePosOnBuild(TileIndex hangar_tile); #endif /* AIRPORT_H */ diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index aa2069c425..68326f8df2 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -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" @@ -41,11 +42,11 @@ static AirportClassID _selected_airport_class; ///< the currently visible airport class static int _selected_airport_index; ///< the index of the selected airport in the current class or -1 -static byte _selected_airport_layout; ///< selected airport layout number. +static uint8_t _selected_airport_layout; ///< selected airport layout number. static void ShowBuildAirportPicker(Window *parent); -SpriteID GetCustomAirportSprite(const AirportSpec *as, byte layout); +SpriteID GetCustomAirportSprite(const AirportSpec *as, uint8_t layout); void CcBuildAirport(Commands, const CommandCost &result, TileIndex tile) { @@ -63,8 +64,8 @@ static void PlaceAirport(TileIndex tile) { if (_selected_airport_index == -1) return; - byte airport_type = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex(); - byte layout = _selected_airport_layout; + uint8_t airport_type = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex(); + uint8_t layout = _selected_airport_layout; bool adjacent = _ctrl_pressed; auto proc = [=](bool test, StationID to_join) -> bool { @@ -82,7 +83,7 @@ static void PlaceAirport(TileIndex tile) struct BuildAirToolbarWindow : Window { int last_user_action; // Last started user action. - BuildAirToolbarWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) + BuildAirToolbarWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc) { this->InitNested(window_number); this->OnInvalidateData(); @@ -207,11 +208,11 @@ static constexpr NWidgetPart _nested_air_toolbar_widgets[] = { EndContainer(), }; -static WindowDesc _air_toolbar_desc(__FILE__, __LINE__, +static WindowDesc _air_toolbar_desc( WDP_ALIGN_TOOLBAR, "toolbar_air", 0, 0, WC_BUILD_TOOLBAR, WC_NONE, WDF_CONSTRUCTION, - std::begin(_nested_air_toolbar_widgets), std::end(_nested_air_toolbar_widgets), + _nested_air_toolbar_widgets, &BuildAirToolbarWindow::hotkeys ); @@ -227,7 +228,7 @@ Window *ShowBuildAirToolbar() if (!Company::IsValidID(_local_company)) return nullptr; CloseWindowByClass(WC_BUILD_TOOLBAR); - return AllocateWindowDescFront(&_air_toolbar_desc, TRANSPORT_AIR); + return AllocateWindowDescFront(_air_toolbar_desc, TRANSPORT_AIR); } class BuildAirportWindow : public PickerWindowBase { @@ -240,15 +241,15 @@ class BuildAirportWindow : public PickerWindowBase { { DropDownList list; - for (uint i = 0; i < AirportClass::GetClassCount(); i++) { - list.push_back(std::make_unique(AirportClass::Get((AirportClassID)i)->name, i, false)); + for (const auto &cls : AirportClass::Classes()) { + list.push_back(MakeDropDownListStringItem(cls.name, cls.Index())); } return list; } public: - BuildAirportWindow(WindowDesc *desc, Window *parent) : PickerWindowBase(desc, parent) + BuildAirportWindow(WindowDesc &desc, Window *parent) : PickerWindowBase(desc, parent) { this->CreateNestedTree(); @@ -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(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 (byte layout = 0; layout < as->num_table; layout++) { + for (uint8_t layout = 0; layout < static_cast(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 (byte layout = 0; layout < as->num_table; layout++) { + for (uint8_t layout = 0; layout < static_cast(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; @@ -383,13 +384,14 @@ public: case WID_AP_AIRPORT_LIST: { Rect row = r.WithHeight(this->line_height).Shrink(WidgetDimensions::scaled.bevel); Rect text = r.WithHeight(this->line_height).Shrink(WidgetDimensions::scaled.matrix); - AirportClass *apclass = AirportClass::Get(_selected_airport_class); - for (uint i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < apclass->GetSpecCount(); i++) { - const AirportSpec *as = apclass->GetSpec(i); + const auto specs = AirportClass::Get(_selected_airport_class)->Specs(); + auto [first, last] = this->vscroll->GetVisibleRangeIterators(specs); + for (auto it = first; it != last; ++it) { + const AirportSpec *as = *it; if (!as->IsAvailable()) { GfxFillRect(row, PC_BLACK, FILLRECT_CHECKER); } - DrawString(text, as->name, ((int)i == _selected_airport_index) ? TC_WHITE : TC_BLACK); + DrawString(text, as->name, (static_cast(as->index) == _selected_airport_index) ? TC_WHITE : TC_BLACK); row = row.Translate(0, this->line_height); text = text.Translate(0, this->line_height); } @@ -473,14 +475,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 +497,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; @@ -535,24 +537,21 @@ public: { /* First try to select an airport in the selected class. */ AirportClass *sel_apclass = AirportClass::Get(_selected_airport_class); - for (uint i = 0; i < sel_apclass->GetSpecCount(); i++) { - const AirportSpec *as = sel_apclass->GetSpec(i); + for (const AirportSpec *as : sel_apclass->Specs()) { if (as->IsAvailable()) { - this->SelectOtherAirport(i); + this->SelectOtherAirport(as->index); return; } } 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->index); return; } } @@ -618,16 +617,16 @@ static constexpr NWidgetPart _nested_build_airport_widgets[] = { EndContainer(), }; -static WindowDesc _build_airport_desc(__FILE__, __LINE__, +static WindowDesc _build_airport_desc( WDP_AUTO, nullptr, 0, 0, WC_BUILD_STATION, WC_BUILD_TOOLBAR, WDF_CONSTRUCTION, - std::begin(_nested_build_airport_widgets), std::end(_nested_build_airport_widgets) + _nested_build_airport_widgets ); static void ShowBuildAirportPicker(Window *parent) { - new BuildAirportWindow(&_build_airport_desc, parent); + new BuildAirportWindow(_build_airport_desc, parent); } void InitializeAirportGui() diff --git a/src/animated_tile.cpp b/src/animated_tile.cpp index 0b5401564c..6705e83dca 100644 --- a/src/animated_tile.cpp +++ b/src/animated_tile.cpp @@ -8,7 +8,8 @@ /** @file animated_tile.cpp Everything related to animated tiles. */ #include "stdafx.h" -#include "core/container_func.hpp" +#include "animated_tile_func.h" +#include "animated_tile_map.h" #include "tile_cmd.h" #include "viewport_func.h" #include "framerate_type.h" @@ -19,28 +20,53 @@ std::vector _animated_tiles; /** - * Removes the given tile from the animated tile table. + * Stops animation on the given tile. * @param tile the tile to remove + * @param immediate immediately delete the tile from the animated tile list instead of waiting for the next tick. */ -void DeleteAnimatedTile(TileIndex tile) +void DeleteAnimatedTile(TileIndex tile, bool immediate) { - auto to_remove = std::find(_animated_tiles.begin(), _animated_tiles.end(), tile); - if (to_remove != _animated_tiles.end()) { - /* The order of the remaining elements must stay the same, otherwise the animation loop may miss a tile. */ - _animated_tiles.erase(to_remove); - MarkTileDirtyByTile(tile); + if (immediate) { + if (GetAnimatedTileState(tile) == AnimatedTileState::None) return; + + /* The tile may be switched to a non-animatable tile soon, so we should remove it from the + * animated tile list early. */ + SetAnimatedTileState(tile, AnimatedTileState::None); + + /* To avoid having to move everything after this tile in the animated tile list, look for this tile + * in the animated tile list and replace with last entry if not last. */ + auto it = std::ranges::find(_animated_tiles, tile); + if (it == std::end(_animated_tiles)) return; + + if (std::next(it) != std::end(_animated_tiles)) *it = _animated_tiles.back(); + _animated_tiles.pop_back(); + + return; } + + /* If the tile was animated, mark it for deletion from the tile list on the next animation loop. */ + if (GetAnimatedTileState(tile) == AnimatedTileState::Animated) SetAnimatedTileState(tile, AnimatedTileState::Deleted); } /** - * Add the given tile to the animated tile table (if it does not exist - * on that table yet). Also increases the size of the table if necessary. + * Add the given tile to the animated tile table (if it does not exist yet). * @param tile the tile to make animated + * @param mark_dirty whether to also mark the tile dirty. */ -void AddAnimatedTile(TileIndex tile) +void AddAnimatedTile(TileIndex tile, bool mark_dirty) { - MarkTileDirtyByTile(tile); - include(_animated_tiles, tile); + if (mark_dirty) MarkTileDirtyByTile(tile); + + const AnimatedTileState state = GetAnimatedTileState(tile); + + /* Tile is already animated so nothing needs to happen. */ + if (state == AnimatedTileState::Animated) return; + + /* Tile has no previous animation state, so add to the tile list. If the state is anything + * other than None (e.g. Deleted) then the tile will still be in the list and does not need to be added again. */ + if (state == AnimatedTileState::None) _animated_tiles.push_back(tile); + + SetAnimatedTileState(tile, AnimatedTileState::Animated); } /** @@ -48,22 +74,29 @@ void AddAnimatedTile(TileIndex tile) */ void AnimateAnimatedTiles() { - PerformanceAccumulator framerate(PFE_GL_LANDSCAPE); + PerformanceAccumulator landscape_framerate(PFE_GL_LANDSCAPE); - const TileIndex *ti = _animated_tiles.data(); - while (ti < _animated_tiles.data() + _animated_tiles.size()) { - const TileIndex curr = *ti; - AnimateTile(curr); - /* During the AnimateTile call, DeleteAnimatedTile could have been called, - * deleting an element we've already processed and pushing the rest one - * slot to the left. We can detect this by checking whether the index - * in the current slot has changed - if it has, an element has been deleted, - * and we should process the current slot again instead of going forward. - * NOTE: this will still break if more than one animated tile is being - * deleted during the same AnimateTile call, but no code seems to - * be doing this anyway. - */ - if (*ti == curr) ++ti; + for (auto it = std::begin(_animated_tiles); it != std::end(_animated_tiles); /* nothing */) { + TileIndex &tile = *it; + + if (GetAnimatedTileState(tile) != AnimatedTileState::Animated) { + /* Tile should not be animated any more, mark it as not animated and erase it from the list. */ + SetAnimatedTileState(tile, AnimatedTileState::None); + + /* Removing the last entry, no need to swap and continue. */ + if (std::next(it) == std::end(_animated_tiles)) { + _animated_tiles.pop_back(); + break; + } + + /* Replace the current list entry with the back of the list to avoid moving elements. */ + *it = _animated_tiles.back(); + _animated_tiles.pop_back(); + continue; + } + + AnimateTile(tile); + ++it; } } diff --git a/src/animated_tile_func.h b/src/animated_tile_func.h index 6a871f1bc5..95fedbb038 100644 --- a/src/animated_tile_func.h +++ b/src/animated_tile_func.h @@ -12,8 +12,8 @@ #include "tile_type.h" -void AddAnimatedTile(TileIndex tile); -void DeleteAnimatedTile(TileIndex tile); +void AddAnimatedTile(TileIndex tile, bool mark_dirty = true); +void DeleteAnimatedTile(TileIndex tile, bool immediate = false); void AnimateAnimatedTiles(); void InitializeAnimatedTiles(); diff --git a/src/animated_tile_map.h b/src/animated_tile_map.h new file mode 100644 index 0000000000..4103a948e4 --- /dev/null +++ b/src/animated_tile_map.h @@ -0,0 +1,44 @@ +/* + * 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 . + */ + +/** @file animated_tile_map.h Maps accessors for animated tiles. */ + +#ifndef ANIMATED_TILE_MAP_H +#define ANIMATED_TILE_MAP_H + +#include "core/bitmath_func.hpp" +#include "map_func.h" + +/** + * Animation state of a possibly-animated tile. + */ +enum class AnimatedTileState : uint8_t { + None = 0, ///< Tile is not animated. + Deleted = 1, ///< Tile was animated but should be removed. + Animated = 3, ///< Tile is animated. +}; + +/** + * Get the animated state of a tile. + * @param t The tile. + * @returns true iff the tile is animated. + */ +inline AnimatedTileState GetAnimatedTileState(Tile t) +{ + return static_cast(GB(t.m6(), 0, 2)); +} + +/** + * Set the animated state of a tile. + * @param t The tile. + */ +inline void SetAnimatedTileState(Tile t, AnimatedTileState state) +{ + SB(t.m6(), 0, 2, to_underlying(state)); +} + +#endif /* ANIMATED_TILE_MAP_H */ diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index 110acb3371..c63394a8c6 100644 --- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -99,18 +99,15 @@ uint CountArticulatedParts(EngineID engine_type, bool purchase_window) /** - * Returns the default (non-refitted) capacity of a specific EngineID. + * Returns the default (non-refitted) cargo and capacity of a specific EngineID. * @param engine the EngineID of interest - * @param cargo_type returns the default cargo type, if needed - * @return capacity + * @return cargo and capacity */ -static inline uint16_t GetVehicleDefaultCapacity(EngineID engine, CargoID *cargo_type) +static inline std::pair GetVehicleDefaultCapacity(EngineID engine) { const Engine *e = Engine::Get(engine); - CargoID cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : INVALID_CARGO); - if (cargo_type != nullptr) *cargo_type = cargo; - if (!IsValidCargoID(cargo)) return 0; - return e->GetDisplayDefaultCapacity(); + CargoID cargo = e->CanCarryCargo() ? e->GetDefaultCargoType() : INVALID_CARGO; + return {cargo, IsValidCargoID(cargo) ? e->GetDisplayDefaultCapacity() : 0}; } /** @@ -143,9 +140,9 @@ CargoArray GetCapacityOfArticulatedParts(EngineID engine) CargoArray capacity{}; const Engine *e = Engine::Get(engine); - CargoID cargo_type; - uint16_t cargo_capacity = GetVehicleDefaultCapacity(engine, &cargo_type); - if (cargo_type < NUM_CARGO) capacity[cargo_type] = cargo_capacity; + if (auto [cargo, cap] = GetVehicleDefaultCapacity(engine); IsValidCargoID(cargo)) { + capacity[cargo] = cap; + } if (!e->IsGroundVehicle()) return capacity; @@ -155,8 +152,9 @@ CargoArray GetCapacityOfArticulatedParts(EngineID engine) EngineID artic_engine = GetNextArticulatedPart(i, engine); if (artic_engine == INVALID_ENGINE) break; - cargo_capacity = GetVehicleDefaultCapacity(artic_engine, &cargo_type); - if (cargo_type < NUM_CARGO) capacity[cargo_type] += cargo_capacity; + if (auto [cargo, cap] = GetVehicleDefaultCapacity(artic_engine); IsValidCargoID(cargo)) { + capacity[cargo] += cap; + } } return capacity; @@ -172,9 +170,9 @@ CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine) CargoTypes cargoes = 0; const Engine *e = Engine::Get(engine); - CargoID cargo_type; - uint16_t cargo_capacity = GetVehicleDefaultCapacity(engine, &cargo_type); - if (cargo_type < NUM_CARGO && cargo_capacity > 0) SetBit(cargoes, cargo_type); + if (auto [cargo, cap] = GetVehicleDefaultCapacity(engine); IsValidCargoID(cargo) && cap > 0) { + SetBit(cargoes, cargo); + } if (!e->IsGroundVehicle()) return cargoes; @@ -184,8 +182,9 @@ CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine) EngineID artic_engine = GetNextArticulatedPart(i, engine); if (artic_engine == INVALID_ENGINE) break; - cargo_capacity = GetVehicleDefaultCapacity(artic_engine, &cargo_type); - if (cargo_type < NUM_CARGO && cargo_capacity > 0) SetBit(cargoes, cargo_type); + if (auto [cargo, cap] = GetVehicleDefaultCapacity(artic_engine); IsValidCargoID(cargo) && cap > 0) { + SetBit(cargoes, cargo); + } } return cargoes; diff --git a/src/autocompletion.cpp b/src/autocompletion.cpp new file mode 100644 index 0000000000..e88150c752 --- /dev/null +++ b/src/autocompletion.cpp @@ -0,0 +1,66 @@ +/* + * 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 . + */ + +/** @file autocompletion.cpp Generic auto-completion engine. */ + +#include "stdafx.h" + +#include "autocompletion.h" + +#include "console_internal.h" +#include "town.h" +#include "network/network_base.h" + +#include "safeguards.h" + +bool AutoCompletion::AutoComplete() +{ + // We are pressing TAB for the first time after reset. + if (this->suggestions.empty()) { + this->InitSuggestions(this->textbuf->buf); + if (this->suggestions.empty()) { + return false; + } + this->ApplySuggestion(prefix, suggestions[0]); + return true; + } + + // We are pressing TAB again on the same text. + if (this->current_suggestion_index + 1 < this->suggestions.size()) { + this->ApplySuggestion(prefix, this->suggestions[++this->current_suggestion_index]); + } else { + // We are out of options, restore original text. + this->textbuf->Assign(initial_buf); + this->Reset(); + } + return true; +} + +void AutoCompletion::Reset() +{ + this->prefix = ""; + this->query = ""; + this->initial_buf.clear(); + this->suggestions.clear(); + this->current_suggestion_index = 0; +} + +void AutoCompletion::InitSuggestions(std::string_view text) +{ + this->initial_buf = text; + size_t space_pos = this->initial_buf.find_last_of(' '); + this->query = this->initial_buf; + if (space_pos == std::string::npos) { + this->prefix = ""; + } else { + this->prefix = this->query.substr(0, space_pos + 1); + this->query.remove_prefix(space_pos + 1); + } + + this->suggestions = this->GetSuggestions(prefix, query); + this->current_suggestion_index = 0; +} diff --git a/src/autocompletion.h b/src/autocompletion.h new file mode 100644 index 0000000000..452c23e87a --- /dev/null +++ b/src/autocompletion.h @@ -0,0 +1,46 @@ +/* + * 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 . + */ + +/** @file autocompletion.h Generic auto-completion engine. */ + +#ifndef AUTOCOMPLETION_H +#define AUTOCOMPLETION_H + +#include "textbuf_type.h" + +class AutoCompletion { +protected: + Textbuf *textbuf; + +private: + std::string initial_buf; ///< Value of text buffer when we started current suggestion session. + + std::string_view prefix; ///< Prefix of the text before the last space. + std::string_view query; ///< Last token of the text. This is used to based the suggestions on. + + std::vector suggestions; + size_t current_suggestion_index; + +public: + AutoCompletion(Textbuf *textbuf) : textbuf(textbuf) + { + this->Reset(); + } + virtual ~AutoCompletion() = default; + + // Returns true the textbuf was updated. + bool AutoComplete(); + void Reset(); + +private: + void InitSuggestions(std::string_view text); + + virtual std::vector GetSuggestions(std::string_view prefix, std::string_view query) = 0; + virtual void ApplySuggestion(std::string_view prefix, std::string_view suggestion) = 0; +}; + +#endif /* AUTOCOMPLETION_H */ diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 010e773e30..757f18d371 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -362,7 +362,7 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic /* Refit the vehicle if needed */ if (refit_cargo != CARGO_NO_REFIT) { - byte subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo); + uint8_t subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo); cost.AddCost(std::get<0>(Command::Do(DC_EXEC, new_veh->index, refit_cargo, subtype, false, false, 0))); assert(cost.Succeeded()); // This should be ensured by GetNewCargoTypeForReplace() diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp index 64707259d3..b2512edec0 100644 --- a/src/autoreplace_gui.cpp +++ b/src/autoreplace_gui.cpp @@ -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, uint16_t min, uint16_t max, 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 }; /** @@ -85,7 +83,7 @@ class ReplaceVehicleWindow : public Window { bool reset_sel_engine; ///< Also reset #sel_engine while updating left and/or right and no valid engine selected. GroupID sel_group; ///< Group selected to replace. int details_height; ///< Minimal needed height of the details panels, in text lines (found so far). - byte sort_criteria; ///< Criteria of sorting vehicles. + uint8_t sort_criteria; ///< Criteria of sorting vehicles. bool descending_sort_order; ///< Order of sorting vehicles. bool show_hidden_engines; ///< Whether to show the hidden engines. RailType sel_railtype; ///< Type of rail tracks selected. #INVALID_RAILTYPE to show all. @@ -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 @@ -143,7 +120,7 @@ class ReplaceVehicleWindow : public Window { std::vector variants; EngineID selected_engine = INVALID_ENGINE; VehicleType type = (VehicleType)this->window_number; - byte side = draw_left ? 0 : 1; + uint8_t side = draw_left ? 0 : 1; GUIEngineList list; @@ -190,7 +167,7 @@ class ReplaceVehicleWindow : public Window { if (side == 1) { /* ensure primary engine of variant group is in list */ for (const auto &variant : variants) { - if (std::find(list.begin(), list.end(), variant) == list.end()) { + if (std::ranges::find(list, variant, &GUIEngineListItem::engine_id) == list.end()) { const Engine *e = Engine::Get(variant); list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0); } @@ -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); } @@ -286,7 +263,7 @@ class ReplaceVehicleWindow : public Window { } public: - ReplaceVehicleWindow(WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc) + ReplaceVehicleWindow(WindowDesc &desc, VehicleType vehicletype, GroupID id_g) : Window(desc) { this->sel_railtype = INVALID_RAILTYPE; this->sel_roadtype = INVALID_ROADTYPE; @@ -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(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: { @@ -487,11 +462,9 @@ public: case WID_RV_LEFT_MATRIX: case WID_RV_RIGHT_MATRIX: { int side = (widget == WID_RV_LEFT_MATRIX) ? 0 : 1; - EngineID start = static_cast(this->vscroll[side]->GetPosition()); // what is the offset for the start (scrolling) - EngineID end = static_cast(std::min(this->vscroll[side]->GetCapacity() + start, this->engines[side].size())); /* Do the actual drawing */ - DrawEngineList((VehicleType)this->window_number, r, this->engines[side], start, end, this->sel_engine[side], side == 0, this->sel_group); + DrawEngineList((VehicleType)this->window_number, r, this->engines[side], *this->vscroll[side], this->sel_engine[side], side == 0, this->sel_group); break; } } @@ -565,8 +538,8 @@ public: case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN: { DropDownList list; - list.push_back(std::make_unique(STR_REPLACE_ENGINES, 1, false)); - list.push_back(std::make_unique(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; } @@ -609,7 +582,7 @@ public: case WID_RV_LEFT_MATRIX: case WID_RV_RIGHT_MATRIX: { - byte click_side; + uint8_t click_side; if (widget == WID_RV_LEFT_MATRIX) { click_side = 0; } else { @@ -621,7 +594,7 @@ public: if (it != this->engines[click_side].end()) { const auto &item = *it; const Rect r = this->GetWidget(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL); - if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) { + if (HasFlag(item.flags, EngineDisplayFlags::HasVariants) && IsInsideMM(r.left, r.right, pt.x)) { /* toggle folded flag on engine */ assert(item.variant_id != INVALID_ENGINE); Engine *engine = Engine::Get(item.variant_id); @@ -631,7 +604,7 @@ public: InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well return; } - if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) e = item.engine_id; + if (!HasFlag(item.flags, EngineDisplayFlags::Shaded)) e = item.engine_id; } /* If Ctrl is pressed on the left side and we don't have any engines of the selected type, stop autoreplacing. @@ -742,10 +715,10 @@ static constexpr NWidgetPart _nested_replace_rail_vehicle_widgets[] = { EndContainer(), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0), + NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0), + NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), @@ -789,11 +762,11 @@ static constexpr NWidgetPart _nested_replace_rail_vehicle_widgets[] = { EndContainer(), }; -static WindowDesc _replace_rail_vehicle_desc(__FILE__, __LINE__, +static WindowDesc _replace_rail_vehicle_desc( WDP_AUTO, "replace_vehicle_train", 500, 140, WC_REPLACE_VEHICLE, WC_NONE, WDF_CONSTRUCTION, - std::begin(_nested_replace_rail_vehicle_widgets), std::end(_nested_replace_rail_vehicle_widgets) + _nested_replace_rail_vehicle_widgets ); static constexpr NWidgetPart _nested_replace_road_vehicle_widgets[] = { @@ -806,10 +779,10 @@ static constexpr NWidgetPart _nested_replace_road_vehicle_widgets[] = { EndContainer(), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0), + NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0), + NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), @@ -847,11 +820,11 @@ static constexpr NWidgetPart _nested_replace_road_vehicle_widgets[] = { EndContainer(), }; -static WindowDesc _replace_road_vehicle_desc(__FILE__, __LINE__, +static WindowDesc _replace_road_vehicle_desc( WDP_AUTO, "replace_vehicle_road", 500, 140, WC_REPLACE_VEHICLE, WC_NONE, WDF_CONSTRUCTION, - std::begin(_nested_replace_road_vehicle_widgets), std::end(_nested_replace_road_vehicle_widgets) + _nested_replace_road_vehicle_widgets ); static constexpr NWidgetPart _nested_replace_vehicle_widgets[] = { @@ -864,10 +837,10 @@ static constexpr NWidgetPart _nested_replace_vehicle_widgets[] = { EndContainer(), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0), + NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0), + NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), @@ -901,11 +874,11 @@ static constexpr NWidgetPart _nested_replace_vehicle_widgets[] = { EndContainer(), }; -static WindowDesc _replace_vehicle_desc(__FILE__, __LINE__, +static WindowDesc _replace_vehicle_desc( WDP_AUTO, "replace_vehicle", 456, 118, WC_REPLACE_VEHICLE, WC_NONE, WDF_CONSTRUCTION, - std::begin(_nested_replace_vehicle_widgets), std::end(_nested_replace_vehicle_widgets) + _nested_replace_vehicle_widgets ); /** @@ -916,11 +889,9 @@ static WindowDesc _replace_vehicle_desc(__FILE__, __LINE__, void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype) { CloseWindowById(WC_REPLACE_VEHICLE, vehicletype); - WindowDesc *desc; switch (vehicletype) { - case VEH_TRAIN: desc = &_replace_rail_vehicle_desc; break; - case VEH_ROAD: desc = &_replace_road_vehicle_desc; break; - default: desc = &_replace_vehicle_desc; break; + case VEH_TRAIN: new ReplaceVehicleWindow(_replace_rail_vehicle_desc, vehicletype, id_g); break; + case VEH_ROAD: new ReplaceVehicleWindow(_replace_road_vehicle_desc, vehicletype, id_g); break; + default: new ReplaceVehicleWindow(_replace_vehicle_desc, vehicletype, id_g); break; } - new ReplaceVehicleWindow(desc, vehicletype, id_g); } diff --git a/src/autoslope.h b/src/autoslope.h index 4e054d85c4..1c66604425 100644 --- a/src/autoslope.h +++ b/src/autoslope.h @@ -16,7 +16,7 @@ /** * Autoslope check for tiles with an entrance on an edge. - * E.g. depots and non-drive-through-road-stops. + * E.g. depots and bay road-stops. * * The test succeeds if the slope is not steep and at least one corner of the entrance edge is on the TileMaxZ() level. * @@ -34,6 +34,27 @@ inline bool AutoslopeCheckForEntranceEdge(TileIndex tile, int z_new, Slope tileh return ((tileh_new == SLOPE_FLAT) || CanBuildDepotByTileh(entrance, tileh_new)); } +/** + * Autoslope check for tiles with something built along an axis. + * E.g. railway stations and drive through road stops. + * + * The test succeeds if the slope is not steep and at least one corner at either of the entrance edges is on the TileMaxZ() level. + * + * @note The test does not check if autoslope is enabled at all. + * + * @param tile The tile. + * @param z_new New TileZ. + * @param tileh_new New TileSlope. + * @param axis The axis. + * @return true iff terraforming is allowed. + */ +inline bool AutoslopeCheckForAxis(TileIndex tile, int z_new, Slope tileh_new, Axis axis) +{ + DiagDirection direction = AxisToDiagDir(axis); + return AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction) && + AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction)); +} + /** * Tests if autoslope is enabled for _current_company. * diff --git a/src/base_media_base.h b/src/base_media_base.h index 5d74cb1a0d..0164a1176a 100644 --- a/src/base_media_base.h +++ b/src/base_media_base.h @@ -158,8 +158,8 @@ struct BaseSet { */ std::optional GetTextfile(TextfileType type) const { - for (uint i = 0; i < NUM_FILES; i++) { - auto textfile = ::GetTextfile(type, BASESET_DIR, this->files[i].filename); + for (const auto &file : this->files) { + auto textfile = ::GetTextfile(type, BASESET_DIR, file.filename); if (textfile.has_value()) { return textfile; } @@ -313,8 +313,8 @@ static const uint NUM_SONGS_AVAILABLE = 1 + NUM_SONG_CLASSES * NUM_SONGS_CLASS; static const uint NUM_SONGS_PLAYLIST = 32; /* Functions to read DOS music CAT files, similar to but not quite the same as sound effect CAT files */ -char *GetMusicCatEntryName(const std::string &filename, size_t entrynum); -byte *GetMusicCatEntryData(const std::string &filename, size_t entrynum, size_t &entrylen); +std::optional GetMusicCatEntryName(const std::string &filename, size_t entrynum); +std::optional> GetMusicCatEntryData(const std::string &filename, size_t entrynum); enum MusicTrackType { MTT_STANDARDMIDI, ///< Standard MIDI file @@ -324,7 +324,7 @@ enum MusicTrackType { /** Metadata about a music track. */ struct MusicSongInfo { std::string songname; ///< name of song displayed in UI - byte tracknr; ///< track number of song displayed in UI + uint8_t tracknr; ///< track number of song displayed in UI std::string filename; ///< file on disk containing song (when used in MusicSet class) MusicTrackType filetype; ///< decoder required for song file int cat_index; ///< entry index in CAT file, for filetype==MTT_MPSMIDI @@ -338,7 +338,7 @@ struct MusicSet : BaseSet { /** Data about individual songs in set. */ MusicSongInfo songinfo[NUM_SONGS_AVAILABLE]; /** Number of valid songs in set. */ - byte num_available; + uint8_t num_available; bool FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename); }; diff --git a/src/base_media_func.h b/src/base_media_func.h index d04485579c..2d68caaa6b 100644 --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -325,8 +325,8 @@ template const char *TryGetBaseSetFile(const ContentInfo *ci, if (!md5sum) return s->files[0].filename.c_str(); MD5Hash md5; - for (uint i = 0; i < Tbase_set::NUM_FILES; i++) { - md5 ^= s->files[i].hash; + for (const auto &file : s->files) { + md5 ^= file.hash; } if (md5 == ci->md5sum) return s->files[0].filename.c_str(); } diff --git a/src/base_station_base.h b/src/base_station_base.h index 82b6c967b2..72a5bb85ce 100644 --- a/src/base_station_base.h +++ b/src/base_station_base.h @@ -19,16 +19,11 @@ typedef Pool StationPool; extern StationPool _station_pool; -struct StationSpecList { - const StationSpec *spec; - uint32_t grfid; ///< GRF ID of this custom station - uint16_t localidx; ///< Station ID within GRF of station -}; - -struct RoadStopSpecList { - const RoadStopSpec *spec; - uint32_t grfid; ///< GRF ID of this custom road stop - uint16_t localidx; ///< Station ID within GRF of road stop +template +struct SpecMapping { + const T *spec; ///< Custom spec. + uint32_t grfid; ///< GRF ID of this custom spec. + uint16_t localidx; ///< Local ID within GRF of this custom spec. }; struct RoadStopTileData { @@ -64,7 +59,7 @@ struct StationRect : public Rect { struct BaseStation : StationPool::PoolItem<&_station_pool> { TileIndex xy; ///< Base tile of the station TrackedViewportSign sign; ///< NOSAVE: Dimensions of sign - byte delete_ctr; ///< Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is deleted. + uint8_t delete_ctr; ///< Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is deleted. std::string name; ///< Custom name StringID string_id; ///< Default name (town area) of station @@ -74,13 +69,13 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> { Owner owner; ///< The owner of this station StationFacility facilities; ///< The facilities that this station has - std::vector speclist; ///< List of rail station specs of this station. - std::vector roadstop_speclist; ///< List of road stop specs of this station + std::vector> speclist; ///< List of rail station specs of this station. + std::vector> roadstop_speclist; ///< List of road stop specs of this station TimerGameCalendar::Date build_date; ///< Date of construction uint16_t random_bits; ///< Random bits assigned to this station - byte waiting_triggers; ///< Waiting triggers (NewGRF) for this station + uint8_t waiting_triggers; ///< Waiting triggers (NewGRF) for this station uint8_t cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen. uint8_t cached_roadstop_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing should happen. CargoTypes cached_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask @@ -118,7 +113,7 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> { * @param available will return false if ever the variable asked for does not exist * @return the value stored in the corresponding variable */ - virtual uint32_t GetNewGRFVariable(const struct ResolverObject &object, byte variable, byte parameter, bool *available) const = 0; + virtual uint32_t GetNewGRFVariable(const struct ResolverObject &object, uint8_t variable, uint8_t parameter, bool &available) const = 0; /** * Update the coordinated of the sign (as shown in the viewport). @@ -184,7 +179,7 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> { return (this->facilities & ~FACIL_WAYPOINT) != 0; } - inline byte GetRoadStopRandomBits(TileIndex tile) const + inline uint8_t GetRoadStopRandomBits(TileIndex tile) const { for (const RoadStopTileData &tile_data : this->custom_roadstop_tile_data) { if (tile_data.tile == tile) return tile_data.random_bits; @@ -192,7 +187,7 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> { return 0; } - inline byte GetRoadStopAnimationFrame(TileIndex tile) const + inline uint8_t GetRoadStopAnimationFrame(TileIndex tile) const { for (const RoadStopTileData &tile_data : this->custom_roadstop_tile_data) { if (tile_data.tile == tile) return tile_data.animation_frame; @@ -201,11 +196,11 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> { } private: - void SetRoadStopTileData(TileIndex tile, byte data, bool animation); + bool SetRoadStopTileData(TileIndex tile, uint8_t data, bool animation); public: - inline void SetRoadStopRandomBits(TileIndex tile, byte random_bits) { this->SetRoadStopTileData(tile, random_bits, false); } - inline void SetRoadStopAnimationFrame(TileIndex tile, byte frame) { this->SetRoadStopTileData(tile, frame, true); } + inline void SetRoadStopRandomBits(TileIndex tile, uint8_t random_bits) { this->SetRoadStopTileData(tile, random_bits, false); } + inline bool SetRoadStopAnimationFrame(TileIndex tile, uint8_t frame) { return this->SetRoadStopTileData(tile, frame, true); } void RemoveRoadStopTileData(TileIndex tile); static void PostDestructor(size_t index); @@ -226,7 +221,7 @@ struct SpecializedStation : public BaseStation { * Set station type correctly * @param tile The base tile of the station. */ - inline SpecializedStation(TileIndex tile) : + inline SpecializedStation(TileIndex tile) : BaseStation(tile) { this->facilities = EXPECTED_FACIL; @@ -310,4 +305,14 @@ struct SpecializedStation : public BaseStation { static Pool::IterateWrapper Iterate(size_t from = 0) { return Pool::IterateWrapper(from); } }; +/** + * Get spec mapping list for each supported custom spec type. + * @tparam T Spec type. + * @param bst Station of custom spec list. + * @return Speclist of custom spec type. + */ +template std::vector> &GetStationSpecList(BaseStation *bst); +template <> inline std::vector> &GetStationSpecList(BaseStation *bst) { return bst->speclist; } +template <> inline std::vector> &GetStationSpecList(BaseStation *bst) { return bst->roadstop_speclist; } + #endif /* BASE_STATION_BASE_H */ diff --git a/src/blitter/32bpp_anim.cpp b/src/blitter/32bpp_anim.cpp index 0c2612fcde..d118d75f7a 100644 --- a/src/blitter/32bpp_anim.cpp +++ b/src/blitter/32bpp_anim.cpp @@ -34,23 +34,23 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel const uint16_t *src_n = (const uint16_t *)(src->data + src->offset[zoom][1]); for (uint i = bp->skip_top; i != 0; i--) { - src_px = (const Colour *)((const byte *)src_px + *(const uint32_t *)src_px); - src_n = (const uint16_t *)((const byte *)src_n + *(const uint32_t *)src_n); + src_px = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px); + src_n = (const uint16_t *)((const uint8_t *)src_n + *(const uint32_t *)src_n); } Colour *dst = (Colour *)bp->dst + bp->top * bp->pitch + bp->left; uint16_t *anim = this->anim_buf + this->ScreenToAnimOffset((uint32_t *)bp->dst) + bp->top * this->anim_buf_pitch + bp->left; - const byte *remap = bp->remap; // store so we don't have to access it via bp every time + const uint8_t *remap = bp->remap; // store so we don't have to access it via bp every time for (int y = 0; y < bp->height; y++) { Colour *dst_ln = dst + bp->pitch; uint16_t *anim_ln = anim + this->anim_buf_pitch; - const Colour *src_px_ln = (const Colour *)((const byte *)src_px + *(const uint32_t *)src_px); + const Colour *src_px_ln = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px); src_px++; - const uint16_t *src_n_ln = (const uint16_t *)((const byte *)src_n + *(const uint32_t *)src_n); + const uint16_t *src_n_ln = (const uint16_t *)((const uint8_t *)src_n + *(const uint32_t *)src_n); src_n += 2; Colour *dst_end = dst + bp->skip_left; diff --git a/src/blitter/32bpp_anim.hpp b/src/blitter/32bpp_anim.hpp index b5b19d784e..975a9bfd62 100644 --- a/src/blitter/32bpp_anim.hpp +++ b/src/blitter/32bpp_anim.hpp @@ -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; /** diff --git a/src/blitter/32bpp_anim_sse2.hpp b/src/blitter/32bpp_anim_sse2.hpp index 669cef80a7..56262e387f 100644 --- a/src/blitter/32bpp_anim_sse2.hpp +++ b/src/blitter/32bpp_anim_sse2.hpp @@ -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. */ diff --git a/src/blitter/32bpp_anim_sse4.cpp b/src/blitter/32bpp_anim_sse4.cpp index 031f0cc985..c0007f1e29 100644 --- a/src/blitter/32bpp_anim_sse4.cpp +++ b/src/blitter/32bpp_anim_sse4.cpp @@ -33,7 +33,7 @@ template remap; + const uint8_t * const remap = bp->remap; Colour *dst_line = (Colour *) bp->dst + bp->top * bp->pitch + bp->left; uint16_t *anim_line = this->anim_buf + this->ScreenToAnimOffset((uint32_t *)bp->dst) + bp->top * this->anim_buf_pitch + bp->left; int effective_width = bp->width; @@ -42,7 +42,7 @@ inline void Blitter_32bppSSE4_Anim::Draw(const BlitterParams *bp, ZoomLevel zoom const Blitter_32bppSSE_Base::SpriteData * const sd = (const Blitter_32bppSSE_Base::SpriteData *) bp->sprite; const SpriteInfo * const si = &sd->infos[zoom]; const MapValue *src_mv_line = (const MapValue *) &sd->data[si->mv_offset] + bp->skip_top * si->sprite_width; - const Colour *src_rgba_line = (const Colour *) ((const byte *) &sd->data[si->sprite_offset] + bp->skip_top * si->sprite_line_size); + const Colour *src_rgba_line = (const Colour *) ((const uint8_t *) &sd->data[si->sprite_offset] + bp->skip_top * si->sprite_line_size); if (read_mode != RM_WITH_MARGIN) { src_rgba_line += bp->skip_left; @@ -104,20 +104,20 @@ inline void Blitter_32bppSSE4_Anim::Draw(const BlitterParams *bp, ZoomLevel zoom if (animated) { /* Remap colours. */ - const byte m0 = mvX2; + const uint8_t m0 = mvX2; if (m0 >= PALETTE_ANIM_START) { const Colour c0 = (this->LookupColourInPalette(m0).data & 0x00FFFFFF) | (src[0].data & 0xFF000000); - InsertFirstUint32(AdjustBrightneSSE(c0, (byte) (mvX2 >> 8)).data, srcABCD); + InsertFirstUint32(AdjustBrightneSSE(c0, (uint8_t) (mvX2 >> 8)).data, srcABCD); } - const byte m1 = mvX2 >> 16; + const uint8_t m1 = mvX2 >> 16; if (m1 >= PALETTE_ANIM_START) { const Colour c1 = (this->LookupColourInPalette(m1).data & 0x00FFFFFF) | (src[1].data & 0xFF000000); - InsertSecondUint32(AdjustBrightneSSE(c1, (byte) (mvX2 >> 24)).data, srcABCD); + InsertSecondUint32(AdjustBrightneSSE(c1, (uint8_t) (mvX2 >> 24)).data, srcABCD); } /* Update anim buffer. */ - const byte a0 = src[0].a; - const byte a1 = src[1].a; + const uint8_t a0 = src[0].a; + const uint8_t a1 = src[1].a; uint32_t anim01 = 0; if (a0 == 255) { if (a1 == 255) { @@ -185,9 +185,9 @@ bmno_full_transparency: __m128i dstABCD = _mm_loadl_epi64((__m128i*) dst); /* Remap colours. */ - const uint m0 = (byte) mvX2; + const uint m0 = (uint8_t) mvX2; const uint r0 = remap[m0]; - const uint m1 = (byte) (mvX2 >> 16); + const uint m1 = (uint8_t) (mvX2 >> 16); const uint r1 = remap[m1]; if (mvX2 & 0x00FF00FF) { #define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \ @@ -195,7 +195,7 @@ bmno_full_transparency: Colour m_colour = m_colour_init; \ { \ const Colour srcm = (Colour) (m_src); \ - const uint m = (byte) (m_m); \ + const uint m = (uint8_t) (m_m); \ const uint r = remap[m]; \ const Colour cmap = (this->LookupColourInPalette(r).data & 0x00FFFFFF) | (srcm.data & 0xFF000000); \ m_colour = r == 0 ? m_colour : cmap; \ @@ -225,8 +225,8 @@ bmno_full_transparency: /* Update anim buffer. */ if (animated) { - const byte a0 = src[0].a; - const byte a1 = src[1].a; + const uint8_t a0 = src[0].a; + const uint8_t a1 = src[1].a; uint32_t anim01 = mvX2 & 0xFF00FF00; if (a0 == 255) { anim01 |= r0; @@ -368,7 +368,7 @@ bmcr_alpha_blend_single: next_line: if (mode != BM_TRANSPARENT && mode != BM_TRANSPARENT_REMAP) src_mv_line += si->sprite_width; - src_rgba_line = (const Colour*) ((const byte*) src_rgba_line + si->sprite_line_size); + src_rgba_line = (const Colour*) ((const uint8_t*) src_rgba_line + si->sprite_line_size); dst_line += bp->pitch; anim_line += this->anim_buf_pitch; } diff --git a/src/blitter/32bpp_anim_sse4.hpp b/src/blitter/32bpp_anim_sse4.hpp index d11c90d341..8217e7d805 100644 --- a/src/blitter/32bpp_anim_sse4.hpp +++ b/src/blitter/32bpp_anim_sse4.hpp @@ -39,10 +39,10 @@ public: template void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; - Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override { + Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &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; }; diff --git a/src/blitter/32bpp_optimized.cpp b/src/blitter/32bpp_optimized.cpp index 15b607a9a7..848bb7e9c7 100644 --- a/src/blitter/32bpp_optimized.cpp +++ b/src/blitter/32bpp_optimized.cpp @@ -40,26 +40,26 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL /* skip upper lines in src_px and src_n */ for (uint i = bp->skip_top; i != 0; i--) { - src_px = (const Colour *)((const byte *)src_px + *(const uint32_t *)src_px); - src_n = (const uint16_t *)((const byte *)src_n + *(const uint32_t *)src_n); + src_px = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px); + src_n = (const uint16_t *)((const uint8_t *)src_n + *(const uint32_t *)src_n); } /* skip lines in dst */ Colour *dst = (Colour *)bp->dst + bp->top * bp->pitch + bp->left; /* store so we don't have to access it via bp every time (compiler assumes pointer aliasing) */ - const byte *remap = bp->remap; + const uint8_t *remap = bp->remap; for (int y = 0; y < bp->height; y++) { /* next dst line begins here */ Colour *dst_ln = dst + bp->pitch; /* next src line begins here */ - const Colour *src_px_ln = (const Colour *)((const byte *)src_px + *(const uint32_t *)src_px); + const Colour *src_px_ln = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px); src_px++; /* next src_n line begins here */ - const uint16_t *src_n_ln = (const uint16_t *)((const byte *)src_n + *(const uint32_t *)src_n); + const uint16_t *src_n_ln = (const uint16_t *)((const uint8_t *)src_n + *(const uint32_t *)src_n); src_n += 2; /* we will end this line when we reach this point */ @@ -285,7 +285,7 @@ void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, this->Draw(bp, mode, zoom); } -template Sprite *Blitter_32bppOptimized::EncodeInternal(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) +template Sprite *Blitter_32bppOptimized::EncodeInternal(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) { /* streams of pixels (a, r, g, b channels) * @@ -306,9 +306,9 @@ template 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; @@ -405,8 +405,8 @@ template Sprite *Blitter_32bppOptimized::EncodeInternal(const dst_n_ln = (uint32_t *)dst_n; } - lengths[z][0] = (byte *)dst_px_ln - (byte *)dst_px_orig[z]; // all are aligned to 4B boundary - lengths[z][1] = (byte *)dst_n_ln - (byte *)dst_n_orig[z]; + lengths[z][0] = (uint8_t *)dst_px_ln - (uint8_t *)dst_px_orig[z]; // all are aligned to 4B boundary + lengths[z][1] = (uint8_t *)dst_n_ln - (uint8_t *)dst_n_orig[z]; } uint len = 0; // total length of data @@ -414,12 +414,12 @@ template Sprite *Blitter_32bppOptimized::EncodeInternal(const len += lengths[z][0] + lengths[z][1]; } - Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sizeof(SpriteData) + len); + Sprite *dest_sprite = allocator.Allocate(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)); @@ -438,10 +438,10 @@ template Sprite *Blitter_32bppOptimized::EncodeInternal(const return dest_sprite; } -template Sprite *Blitter_32bppOptimized::EncodeInternal(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator); -template Sprite *Blitter_32bppOptimized::EncodeInternal(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator); +template Sprite *Blitter_32bppOptimized::EncodeInternal(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); +template Sprite *Blitter_32bppOptimized::EncodeInternal(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); -Sprite *Blitter_32bppOptimized::Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) +Sprite *Blitter_32bppOptimized::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) { return this->EncodeInternal(sprite, allocator); } diff --git a/src/blitter/32bpp_optimized.hpp b/src/blitter/32bpp_optimized.hpp index e40a0ec667..862c1d9e7e 100644 --- a/src/blitter/32bpp_optimized.hpp +++ b/src/blitter/32bpp_optimized.hpp @@ -18,19 +18,19 @@ public: /** Data stored about a (single) sprite. */ struct SpriteData { uint32_t offset[ZOOM_LVL_END][2]; ///< Offsets (from .data) to streams for different zoom levels, and the normal and remap image information. - byte data[]; ///< Data, all zoomlevels. + uint8_t data[]; ///< Data, all zoomlevels. }; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; - Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override; + Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; - const char *GetName() override { return "32bpp-optimized"; } + std::string_view GetName() override { return "32bpp-optimized"; } template void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); protected: template void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom); - template Sprite *EncodeInternal(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator); + template Sprite *EncodeInternal(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); }; /** Factory for the optimised 32 bpp blitter (without palette animation). */ diff --git a/src/blitter/32bpp_simple.cpp b/src/blitter/32bpp_simple.cpp index 00c0497109..ae660e372f 100644 --- a/src/blitter/32bpp_simple.cpp +++ b/src/blitter/32bpp_simple.cpp @@ -115,20 +115,20 @@ void Blitter_32bppSimple::DrawColourMappingRect(void *dst, int width, int height Debug(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('{}')", pal); } -Sprite *Blitter_32bppSimple::Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) +Sprite *Blitter_32bppSimple::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &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 = allocator.Allocate(sizeof(*dest_sprite) + static_cast(sprite[ZOOM_LVL_MIN].height) * static_cast(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; diff --git a/src/blitter/32bpp_simple.hpp b/src/blitter/32bpp_simple.hpp index df154ce57d..902af2905a 100644 --- a/src/blitter/32bpp_simple.hpp +++ b/src/blitter/32bpp_simple.hpp @@ -26,9 +26,9 @@ class Blitter_32bppSimple : public Blitter_32bppBase { public: void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override; - Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override; + Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; - const char *GetName() override { return "32bpp-simple"; } + std::string_view GetName() override { return "32bpp-simple"; } }; /** Factory for the simple 32 bpp blitter. */ diff --git a/src/blitter/32bpp_sse2.cpp b/src/blitter/32bpp_sse2.cpp index 1638127b56..2b4505253b 100644 --- a/src/blitter/32bpp_sse2.cpp +++ b/src/blitter/32bpp_sse2.cpp @@ -20,15 +20,15 @@ /** Instantiation of the SSE2 32bpp blitter factory. */ static FBlitter_32bppSSE2 iFBlitter_32bppSSE2; -Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) +Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) { /* First uint32_t of a line = the number of transparent pixels from the left. * 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; @@ -51,11 +51,11 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri all_sprites_size += rgba_size + mv_size; } - 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; + Sprite *dst_sprite = allocator.Allocate(sizeof(Sprite) + sizeof(SpriteData) + all_sprites_size); + 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. */ @@ -114,7 +114,7 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri (*dst_rgba_line).data = nb_pix_transp; Colour *nb_right = dst_rgba_line + 1; - dst_rgba_line = (Colour*) ((byte*) dst_rgba_line + sd.infos[z].sprite_line_size); + dst_rgba_line = (Colour*) ((uint8_t*) dst_rgba_line + sd.infos[z].sprite_line_size); /* Count the number of transparent pixels from the right. */ dst_rgba = dst_rgba_line - 1; diff --git a/src/blitter/32bpp_sse2.hpp b/src/blitter/32bpp_sse2.hpp index fa643dd3cd..ebbb31e059 100644 --- a/src/blitter/32bpp_sse2.hpp +++ b/src/blitter/32bpp_sse2.hpp @@ -73,10 +73,10 @@ public: struct SpriteData { SpriteFlags flags; SpriteInfo infos[ZOOM_LVL_END]; - byte data[]; ///< Data, all zoomlevels. + uint8_t data[]; ///< Data, all zoomlevels. }; - Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator); + Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); }; DECLARE_ENUM_AS_BIT_SET(Blitter_32bppSSE_Base::SpriteFlags); @@ -88,11 +88,11 @@ public: template void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); - Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override { + Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override { 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). */ diff --git a/src/blitter/32bpp_sse4.hpp b/src/blitter/32bpp_sse4.hpp index deb4fbed92..7d8798c697 100644 --- a/src/blitter/32bpp_sse4.hpp +++ b/src/blitter/32bpp_sse4.hpp @@ -32,7 +32,7 @@ public: void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; template 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). */ diff --git a/src/blitter/32bpp_sse_func.hpp b/src/blitter/32bpp_sse_func.hpp index 4f319f1476..77c2bec954 100644 --- a/src/blitter/32bpp_sse_func.hpp +++ b/src/blitter/32bpp_sse_func.hpp @@ -221,7 +221,7 @@ inline void Blitter_32bppSSSE3::Draw(const Blitter::BlitterParams *bp, ZoomLevel inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom) #endif { - const byte * const remap = bp->remap; + const uint8_t * const remap = bp->remap; Colour *dst_line = (Colour *) bp->dst + bp->top * bp->pitch + bp->left; int effective_width = bp->width; @@ -229,7 +229,7 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel const SpriteData * const sd = (const SpriteData *) bp->sprite; const SpriteInfo * const si = &sd->infos[zoom]; const MapValue *src_mv_line = (const MapValue *) &sd->data[si->mv_offset] + bp->skip_top * si->sprite_width; - const Colour *src_rgba_line = (const Colour *) ((const byte *) &sd->data[si->sprite_offset] + bp->skip_top * si->sprite_line_size); + const Colour *src_rgba_line = (const Colour *) ((const uint8_t *) &sd->data[si->sprite_offset] + bp->skip_top * si->sprite_line_size); if (read_mode != RM_WITH_MARGIN) { src_rgba_line += bp->skip_left; @@ -314,7 +314,7 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel Colour m_colour = m_colour_init; \ { \ const Colour srcm = (Colour) (m_src); \ - const uint m = (byte) (m_m); \ + const uint m = (uint8_t) (m_m); \ const uint r = remap[m]; \ const Colour cmap = (this->LookupColourInPalette(r).data & 0x00FFFFFF) | (srcm.data & 0xFF000000); \ m_colour = r == 0 ? m_colour : cmap; \ @@ -442,7 +442,7 @@ bmcr_alpha_blend_single: next_line: if (mode == BM_COLOUR_REMAP || mode == BM_CRASH_REMAP) src_mv_line += si->sprite_width; - src_rgba_line = (const Colour*) ((const byte*) src_rgba_line + si->sprite_line_size); + src_rgba_line = (const Colour*) ((const uint8_t*) src_rgba_line + si->sprite_line_size); dst_line += bp->pitch; } } diff --git a/src/blitter/32bpp_sse_type.h b/src/blitter/32bpp_sse_type.h index e8662e4949..57de1d6d58 100644 --- a/src/blitter/32bpp_sse_type.h +++ b/src/blitter/32bpp_sse_type.h @@ -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 diff --git a/src/blitter/32bpp_ssse3.hpp b/src/blitter/32bpp_ssse3.hpp index c95095d4df..4ee475d45f 100644 --- a/src/blitter/32bpp_ssse3.hpp +++ b/src/blitter/32bpp_ssse3.hpp @@ -32,7 +32,7 @@ public: void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; template 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). */ diff --git a/src/blitter/40bpp_anim.cpp b/src/blitter/40bpp_anim.cpp index e65a1d7151..3a9b89e4bd 100644 --- a/src/blitter/40bpp_anim.cpp +++ b/src/blitter/40bpp_anim.cpp @@ -104,8 +104,8 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel /* skip upper lines in src_px and src_n */ for (uint i = bp->skip_top; i != 0; i--) { - src_px = (const Colour *)((const byte *)src_px + *(const uint32_t *)src_px); - src_n = (const uint16_t *)((const byte *)src_n + *(const uint32_t *)src_n); + src_px = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px); + src_n = (const uint16_t *)((const uint8_t *)src_n + *(const uint32_t *)src_n); } /* skip lines in dst */ @@ -114,7 +114,7 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel uint8_t *anim = VideoDriver::GetInstance()->GetAnimBuffer() + ((uint32_t *)bp->dst - (uint32_t *)_screen.dst_ptr) + bp->top * bp->pitch + bp->left; /* store so we don't have to access it via bp everytime (compiler assumes pointer aliasing) */ - const byte *remap = bp->remap; + const uint8_t *remap = bp->remap; for (int y = 0; y < bp->height; y++) { /* next dst line begins here */ @@ -122,11 +122,11 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel uint8_t *anim_ln = anim + bp->pitch; /* next src line begins here */ - const Colour *src_px_ln = (const Colour *)((const byte *)src_px + *(const uint32_t *)src_px); + const Colour *src_px_ln = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px); src_px++; /* next src_n line begins here */ - const uint16_t *src_n_ln = (const uint16_t *)((const byte *)src_n + *(const uint32_t *)src_n); + const uint16_t *src_n_ln = (const uint16_t *)((const uint8_t *)src_n + *(const uint32_t *)src_n); src_n += 2; /* we will end this line when we reach this point */ @@ -397,7 +397,7 @@ void Blitter_40bppAnim::DrawColourMappingRect(void *dst, int width, int height, } } -Sprite *Blitter_40bppAnim::Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) +Sprite *Blitter_40bppAnim::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) { return this->EncodeInternal(sprite, allocator); } diff --git a/src/blitter/40bpp_anim.hpp b/src/blitter/40bpp_anim.hpp index 4371100914..0174735c3a 100644 --- a/src/blitter/40bpp_anim.hpp +++ b/src/blitter/40bpp_anim.hpp @@ -27,12 +27,12 @@ public: void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override; - Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override; + Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; size_t BufferSize(uint width, uint height) override; Blitter::PaletteAnimation UsePaletteAnimation() override; bool NeedsAnimationBuffer() override; - const char *GetName() override { return "40bpp-anim"; } + std::string_view GetName() override { return "40bpp-anim"; } template void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); diff --git a/src/blitter/8bpp_optimized.cpp b/src/blitter/8bpp_optimized.cpp index 3758e3a80a..c4bef59b53 100644 --- a/src/blitter/8bpp_optimized.cpp +++ b/src/blitter/8bpp_optimized.cpp @@ -120,7 +120,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z } } -Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) +Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) { /* Make memory for all zoom-levels */ uint memory = sizeof(SpriteData); @@ -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; @@ -147,10 +147,10 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri /* Don't allocate memory each time, but just keep some * memory around as this function is called quite often * and the memory usage is quite low. */ - static ReusableBuffer temp_buffer; + static ReusableBuffer temp_buffer; SpriteData *temp_dst = (SpriteData *)temp_buffer.Allocate(memory); memset(temp_dst, 0, sizeof(*temp_dst)); - byte *dst = temp_dst->data; + uint8_t *dst = temp_dst->data; /* Make the sprites per zoom-level */ for (ZoomLevel i = zoom_min; i <= zoom_max; i++) { @@ -166,7 +166,7 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri uint trans = 0; uint pixels = 0; uint last_colour = 0; - byte *count_dst = nullptr; + uint8_t *count_dst = nullptr; /* Store the scaled image */ const SpriteLoader::CommonPixel *src = &sprite[i].data[y * sprite[i].width]; @@ -213,18 +213,18 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri } } - uint size = dst - (byte *)temp_dst; + uint size = dst - (uint8_t *)temp_dst; /* Safety check, to make sure we guessed the size correctly */ assert(size < memory); /* Allocate the exact amount of memory we need */ - Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + size); + Sprite *dest_sprite = allocator.Allocate(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; diff --git a/src/blitter/8bpp_optimized.hpp b/src/blitter/8bpp_optimized.hpp index 39a7d91d22..c3c8e381d0 100644 --- a/src/blitter/8bpp_optimized.hpp +++ b/src/blitter/8bpp_optimized.hpp @@ -19,13 +19,13 @@ public: /** Data stored about a (single) sprite. */ struct SpriteData { uint32_t offset[ZOOM_LVL_END]; ///< Offsets (from .data) to streams for different zoom levels. - byte data[]; ///< Data, all zoomlevels. + uint8_t data[]; ///< Data, all zoomlevels. }; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; - Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override; + Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &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. */ diff --git a/src/blitter/8bpp_simple.cpp b/src/blitter/8bpp_simple.cpp index a80ceba115..6686ec0e2a 100644 --- a/src/blitter/8bpp_simple.cpp +++ b/src/blitter/8bpp_simple.cpp @@ -61,19 +61,19 @@ void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoom } } -Sprite *Blitter_8bppSimple::Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) +Sprite *Blitter_8bppSimple::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &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 = allocator.Allocate(sizeof(*dest_sprite) + static_cast(sprite[ZOOM_LVL_MIN].height) * static_cast(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; diff --git a/src/blitter/8bpp_simple.hpp b/src/blitter/8bpp_simple.hpp index 23d49d7f91..12e7f0fd8e 100644 --- a/src/blitter/8bpp_simple.hpp +++ b/src/blitter/8bpp_simple.hpp @@ -17,9 +17,9 @@ class Blitter_8bppSimple final : public Blitter_8bppBase { public: void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; - Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override; + Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; - const char *GetName() override { return "8bpp-simple"; } + std::string_view GetName() override { return "8bpp-simple"; } }; /** Factory for the most trivial 8bpp blitter. */ diff --git a/src/blitter/base.hpp b/src/blitter/base.hpp index 26ed39eb6d..24ccc3ac13 100644 --- a/src/blitter/base.hpp +++ b/src/blitter/base.hpp @@ -31,7 +31,7 @@ public: /** Parameters related to blitting. */ struct BlitterParams { const void *sprite; ///< Pointer to the sprite how ever the encoder stored it - const byte *remap; ///< XXX -- Temporary storage for remap array + const uint8_t *remap; ///< XXX -- Temporary storage for remap array int skip_left; ///< How much pixels of the source to skip on the left (based on zoom of dst) int skip_top; ///< How much pixels of the source to skip on the top (based on zoom of dst) @@ -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 diff --git a/src/blitter/factory.hpp b/src/blitter/factory.hpp index 5889cca4f2..5071a168ae 100644 --- a/src/blitter/factory.hpp +++ b/src/blitter/factory.hpp @@ -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; } diff --git a/src/blitter/null.cpp b/src/blitter/null.cpp index f73b5ae331..dc8b1c0ea2 100644 --- a/src/blitter/null.cpp +++ b/src/blitter/null.cpp @@ -15,15 +15,15 @@ /** Instantiation of the null blitter factory. */ static FBlitter_Null iFBlitter_Null; -Sprite *Blitter_Null::Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) +Sprite *Blitter_Null::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) { Sprite *dest_sprite; - dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite)); + dest_sprite = allocator.Allocate(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; } diff --git a/src/blitter/null.hpp b/src/blitter/null.hpp index 61c5cab9de..a0ecfbe8db 100644 --- a/src/blitter/null.hpp +++ b/src/blitter/null.hpp @@ -18,7 +18,7 @@ public: uint8_t GetScreenDepth() override { return 0; } void Draw(Blitter::BlitterParams *, BlitterMode, ZoomLevel) override {}; void DrawColourMappingRect(void *, int, int, PaletteID) override {}; - Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override; + Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; void *MoveTo(void *, int, int) override { return nullptr; }; void SetPixel(void *, int, int, uint8_t) override {}; void DrawRect(void *, int, int, uint8_t) override {}; @@ -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. */ diff --git a/src/bmp.cpp b/src/bmp.cpp index eca61335dd..202dce7a39 100644 --- a/src/bmp.cpp +++ b/src/bmp.cpp @@ -8,97 +8,32 @@ /** @file bmp.cpp Read and write support for bmps. */ #include "stdafx.h" +#include "random_access_file_type.h" #include "bmp.h" #include "core/bitmath_func.hpp" -#include "core/alloc_func.hpp" -#include "core/mem_func.hpp" #include "safeguards.h" -void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file) -{ - buffer->pos = -1; - buffer->file = file; - buffer->read = 0; - buffer->real_pos = ftell(file); -} - -static inline void AdvanceBuffer(BmpBuffer *buffer) -{ - if (buffer->read < 0) return; - - buffer->read = (int)fread(buffer->data, 1, BMP_BUFFER_SIZE, buffer->file); - buffer->pos = 0; -} - -static inline bool EndOfBuffer(BmpBuffer *buffer) -{ - if (buffer->read < 0) return false; - - if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer); - return buffer->pos == buffer->read; -} - -static inline byte ReadByte(BmpBuffer *buffer) -{ - if (buffer->read < 0) return 0; - - if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer); - buffer->real_pos++; - return buffer->data[buffer->pos++]; -} - -static inline uint16_t ReadWord(BmpBuffer *buffer) -{ - uint16_t var = ReadByte(buffer); - return var | (ReadByte(buffer) << 8); -} - -static inline uint32_t ReadDword(BmpBuffer *buffer) -{ - uint32_t var = ReadWord(buffer); - return var | (ReadWord(buffer) << 16); -} - -static inline void SkipBytes(BmpBuffer *buffer, int bytes) -{ - int i; - for (i = 0; i < bytes; i++) ReadByte(buffer); -} - -static inline void SetStreamOffset(BmpBuffer *buffer, int offset) -{ - if (fseek(buffer->file, offset, SEEK_SET) < 0) { - buffer->read = -1; - } - buffer->pos = -1; - buffer->real_pos = offset; - AdvanceBuffer(buffer); -} - /** * Reads a 1 bpp uncompressed bitmap * The bitmap is converted to a 8 bpp bitmap */ -static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data) +static inline bool BmpRead1(RandomAccessFile &file, BmpInfo &info, BmpData &data) { - uint x, y, i; - byte pad = GB(4 - info->width / 8, 0, 2); - byte *pixel_row; - byte b; - for (y = info->height; y > 0; y--) { - x = 0; - pixel_row = &data->bitmap[(y - 1) * info->width]; - while (x < info->width) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected - b = ReadByte(buffer); - for (i = 8; i > 0; i--) { - if (x < info->width) *pixel_row++ = GB(b, i - 1, 1); + uint8_t pad = GB(4 - info.width / 8, 0, 2); + for (uint y = info.height; y > 0; y--) { + uint x = 0; + uint8_t *pixel_row = &data.bitmap[(y - 1) * static_cast(info.width)]; + while (x < info.width) { + if (file.AtEndOfFile()) return false; // the file is shorter than expected + uint8_t b = file.ReadByte(); + for (uint i = 8; i > 0; i--) { + if (x < info.width) *pixel_row++ = GB(b, i - 1, 1); x++; } } /* Padding for 32 bit align */ - SkipBytes(buffer, pad); + file.SkipBytes(pad); } return true; } @@ -107,27 +42,24 @@ static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data) * Reads a 4 bpp uncompressed bitmap * The bitmap is converted to a 8 bpp bitmap */ -static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data) +static inline bool BmpRead4(RandomAccessFile &file, BmpInfo &info, BmpData &data) { - uint x, y; - byte pad = GB(4 - info->width / 2, 0, 2); - byte *pixel_row; - byte b; - for (y = info->height; y > 0; y--) { - x = 0; - pixel_row = &data->bitmap[(y - 1) * info->width]; - while (x < info->width) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected - b = ReadByte(buffer); + uint8_t pad = GB(4 - info.width / 2, 0, 2); + for (uint y = info.height; y > 0; y--) { + uint x = 0; + uint8_t *pixel_row = &data.bitmap[(y - 1) * static_cast(info.width)]; + while (x < info.width) { + if (file.AtEndOfFile()) return false; // the file is shorter than expected + uint8_t b = file.ReadByte(); *pixel_row++ = GB(b, 4, 4); x++; - if (x < info->width) { + if (x < info.width) { *pixel_row++ = GB(b, 0, 4); x++; } } /* Padding for 32 bit align */ - SkipBytes(buffer, pad); + file.SkipBytes(pad); } return true; } @@ -136,56 +68,56 @@ static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data) * Reads a 4-bit RLE compressed bitmap * The bitmap is converted to a 8 bpp bitmap */ -static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data) +static inline bool BmpRead4Rle(RandomAccessFile &file, BmpInfo &info, BmpData &data) { uint x = 0; - uint y = info->height - 1; - byte *pixel = &data->bitmap[y * info->width]; - while (y != 0 || x < info->width) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected + uint y = info.height - 1; + uint8_t *pixel = &data.bitmap[y * static_cast(info.width)]; + while (y != 0 || x < info.width) { + if (file.AtEndOfFile()) return false; // the file is shorter than expected - byte n = ReadByte(buffer); - byte c = ReadByte(buffer); + uint8_t n = file.ReadByte(); + uint8_t c = file.ReadByte(); if (n == 0) { switch (c) { case 0: // end of line x = 0; if (y == 0) return false; - pixel = &data->bitmap[--y * info->width]; + pixel = &data.bitmap[--y * static_cast(info.width)]; break; case 1: // end of bitmap return true; case 2: { // delta - if (EndOfBuffer(buffer)) return false; - byte dx = ReadByte(buffer); - byte dy = ReadByte(buffer); + if (file.AtEndOfFile()) return false; + uint8_t dx = file.ReadByte(); + uint8_t dy = file.ReadByte(); /* Check for over- and underflow. */ - if (x + dx >= info->width || x + dx < x || dy > y) return false; + if (x + dx >= info.width || x + dx < x || dy > y) return false; x += dx; y -= dy; - pixel = &data->bitmap[y * info->width + x]; + pixel = &data.bitmap[y * info.width + x]; break; } default: { // uncompressed uint i = 0; while (i++ < c) { - if (EndOfBuffer(buffer) || x >= info->width) return false; - byte b = ReadByte(buffer); + if (file.AtEndOfFile() || x >= info.width) return false; + uint8_t b = file.ReadByte(); *pixel++ = GB(b, 4, 4); x++; if (i++ < c) { - if (x >= info->width) return false; + if (x >= info.width) return false; *pixel++ = GB(b, 0, 4); x++; } } /* Padding for 16 bit align */ - SkipBytes(buffer, ((c + 1) / 2) % 2); + file.SkipBytes(((c + 1) / 2) % 2); break; } } @@ -194,10 +126,10 @@ static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data) * pixels to be written is higher than the remaining line width. * Ignore the superfluous pixels instead of reporting an error. */ uint i = 0; - while (x < info->width && i++ < n) { + while (x < info.width && i++ < n) { *pixel++ = GB(c, 4, 4); x++; - if (x < info->width && i++ < n) { + if (x < info.width && i++ < n) { *pixel++ = GB(c, 0, 4); x++; } @@ -210,18 +142,15 @@ static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data) /** * Reads a 8 bpp bitmap */ -static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data) +static inline bool BmpRead8(RandomAccessFile &file, BmpInfo &info, BmpData &data) { - uint i; - uint y; - byte pad = GB(4 - info->width, 0, 2); - byte *pixel; - for (y = info->height; y > 0; y--) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected - pixel = &data->bitmap[(y - 1) * info->width]; - for (i = 0; i < info->width; i++) *pixel++ = ReadByte(buffer); + uint8_t pad = GB(4 - info.width, 0, 2); + for (uint y = info.height; y > 0; y--) { + if (file.AtEndOfFile()) return false; // the file is shorter than expected + uint8_t *pixel = &data.bitmap[(y - 1) * static_cast(info.width)]; + for (uint i = 0; i < info.width; i++) *pixel++ = file.ReadByte(); /* Padding for 32 bit align */ - SkipBytes(buffer, pad); + file.SkipBytes(pad); } return true; } @@ -229,49 +158,49 @@ static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data) /** * Reads a 8-bit RLE compressed bpp bitmap */ -static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data) +static inline bool BmpRead8Rle(RandomAccessFile &file, BmpInfo &info, BmpData &data) { uint x = 0; - uint y = info->height - 1; - byte *pixel = &data->bitmap[y * info->width]; - while (y != 0 || x < info->width) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected + uint y = info.height - 1; + uint8_t *pixel = &data.bitmap[y * static_cast(info.width)]; + while (y != 0 || x < info.width) { + if (file.AtEndOfFile()) return false; // the file is shorter than expected - byte n = ReadByte(buffer); - byte c = ReadByte(buffer); + uint8_t n = file.ReadByte(); + uint8_t c = file.ReadByte(); if (n == 0) { switch (c) { case 0: // end of line x = 0; if (y == 0) return false; - pixel = &data->bitmap[--y * info->width]; + pixel = &data.bitmap[--y * static_cast(info.width)]; break; case 1: // end of bitmap return true; case 2: { // delta - if (EndOfBuffer(buffer)) return false; - byte dx = ReadByte(buffer); - byte dy = ReadByte(buffer); + if (file.AtEndOfFile()) return false; + uint8_t dx = file.ReadByte(); + uint8_t dy = file.ReadByte(); /* Check for over- and underflow. */ - if (x + dx >= info->width || x + dx < x || dy > y) return false; + if (x + dx >= info.width || x + dx < x || dy > y) return false; x += dx; y -= dy; - pixel = &data->bitmap[y * info->width + x]; + pixel = &data.bitmap[y * static_cast(info.width) + x]; break; } default: { // uncompressed for (uint i = 0; i < c; i++) { - if (EndOfBuffer(buffer) || x >= info->width) return false; - *pixel++ = ReadByte(buffer); + if (file.AtEndOfFile() || x >= info.width) return false; + *pixel++ = file.ReadByte(); x++; } /* Padding for 16 bit align */ - SkipBytes(buffer, c % 2); + file.SkipBytes(c % 2); break; } } @@ -279,7 +208,7 @@ static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data) /* Apparently it is common to encounter BMPs where the count of * pixels to be written is higher than the remaining line width. * Ignore the superfluous pixels instead of reporting an error. */ - for (uint i = 0; x < info->width && i < n; i++) { + for (uint i = 0; x < info.width && i < n; i++) { *pixel++ = c; x++; } @@ -291,22 +220,20 @@ static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data) /** * Reads a 24 bpp uncompressed bitmap */ -static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data) +static inline bool BmpRead24(RandomAccessFile &file, BmpInfo &info, BmpData &data) { - uint x, y; - byte pad = GB(4 - info->width * 3, 0, 2); - byte *pixel_row; - for (y = info->height; y > 0; y--) { - pixel_row = &data->bitmap[(y - 1) * info->width * 3]; - for (x = 0; x < info->width; x++) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected - *(pixel_row + 2) = ReadByte(buffer); // green - *(pixel_row + 1) = ReadByte(buffer); // blue - *pixel_row = ReadByte(buffer); // red + uint8_t pad = GB(4 - info.width * 3, 0, 2); + for (uint y = info.height; y > 0; --y) { + uint8_t *pixel_row = &data.bitmap[(y - 1) * static_cast(info.width) * 3]; + for (uint x = 0; x < info.width; ++x) { + if (file.AtEndOfFile()) return false; // the file is shorter than expected + *(pixel_row + 2) = file.ReadByte(); // green + *(pixel_row + 1) = file.ReadByte(); // blue + *pixel_row = file.ReadByte(); // red pixel_row += 3; } /* Padding for 32 bit align */ - SkipBytes(buffer, pad); + file.SkipBytes(pad); } return true; } @@ -314,109 +241,98 @@ static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data) /* * Reads bitmap headers, and palette (if any) */ -bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data) +bool BmpReadHeader(RandomAccessFile &file, BmpInfo &info, BmpData &data) { - uint32_t header_size; - assert(info != nullptr); - MemSetT(info, 0); + info = {}; /* Reading BMP header */ - if (ReadWord(buffer) != 0x4D42) return false; // signature should be 'BM' - SkipBytes(buffer, 8); // skip file size and reserved - info->offset = ReadDword(buffer); + if (file.ReadWord() != 0x4D42) return false; // signature should be 'BM' + file.SkipBytes(8); // skip file size and reserved + info.offset = file.ReadDword() + file.GetStartPos(); /* Reading info header */ - header_size = ReadDword(buffer); + uint32_t header_size = file.ReadDword(); if (header_size < 12) return false; // info header should be at least 12 bytes long - info->os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long + info.os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long - if (info->os2_bmp) { - info->width = ReadWord(buffer); - info->height = ReadWord(buffer); + if (info.os2_bmp) { + info.width = file.ReadWord(); + info.height = file.ReadWord(); header_size -= 8; } else { - info->width = ReadDword(buffer); - info->height = ReadDword(buffer); + info.width = file.ReadDword(); + info.height = file.ReadDword(); header_size -= 12; } - if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane + if (file.ReadWord() != 1) return false; // BMP can have only 1 plane - info->bpp = ReadWord(buffer); - if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) { + info.bpp = file.ReadWord(); + if (info.bpp != 1 && info.bpp != 4 && info.bpp != 8 && info.bpp != 24) { /* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */ return false; } /* Reads compression method if available in info header*/ if ((header_size -= 4) >= 4) { - info->compression = ReadDword(buffer); + info.compression = file.ReadDword(); header_size -= 4; } /* Only 4-bit and 8-bit rle compression is supported */ - if (info->compression > 2 || (info->compression > 0 && !(info->bpp == 4 || info->bpp == 8))) return false; - - if (info->bpp <= 8) { - uint i; + if (info.compression > 2 || (info.compression > 0 && !(info.bpp == 4 || info.bpp == 8))) return false; + if (info.bpp <= 8) { /* Reads number of colours if available in info header */ if (header_size >= 16) { - SkipBytes(buffer, 12); // skip image size and resolution - info->palette_size = ReadDword(buffer); // number of colours in palette - SkipBytes(buffer, header_size - 16); // skip the end of info header + file.SkipBytes(12); // skip image size and resolution + info.palette_size = file.ReadDword(); // number of colours in palette + file.SkipBytes(header_size - 16); // skip the end of info header } - uint maximum_palette_size = 1U << info->bpp; - if (info->palette_size == 0) info->palette_size = maximum_palette_size; + uint maximum_palette_size = 1U << info.bpp; + if (info.palette_size == 0) info.palette_size = maximum_palette_size; /* More palette colours than palette indices is not supported. */ - if (info->palette_size > maximum_palette_size) return false; + if (info.palette_size > maximum_palette_size) return false; - data->palette = CallocT(info->palette_size); + data.palette.resize(info.palette_size); - for (i = 0; i < info->palette_size; i++) { - data->palette[i].b = ReadByte(buffer); - data->palette[i].g = ReadByte(buffer); - data->palette[i].r = ReadByte(buffer); - if (!info->os2_bmp) SkipBytes(buffer, 1); // unused + for (auto &colour : data.palette) { + colour.b = file.ReadByte(); + colour.g = file.ReadByte(); + colour.r = file.ReadByte(); + if (!info.os2_bmp) file.SkipBytes(1); // unused } } - return buffer->real_pos <= info->offset; + return file.GetPos() <= info.offset; } /* * Reads the bitmap * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps */ -bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data) +bool BmpReadBitmap(RandomAccessFile &file, BmpInfo &info, BmpData &data) { - assert(info != nullptr && data != nullptr); - - data->bitmap = CallocT(static_cast(info->width) * info->height * ((info->bpp == 24) ? 3 : 1)); + data.bitmap.resize(static_cast(info.width) * info.height * ((info.bpp == 24) ? 3 : 1)); /* Load image */ - SetStreamOffset(buffer, info->offset); - switch (info->compression) { - case 0: // no compression - switch (info->bpp) { - case 1: return BmpRead1(buffer, info, data); - case 4: return BmpRead4(buffer, info, data); - case 8: return BmpRead8(buffer, info, data); - case 24: return BmpRead24(buffer, info, data); + file.SeekTo(info.offset, SEEK_SET); + switch (info.compression) { + case 0: // no compression + switch (info.bpp) { + case 1: return BmpRead1(file, info, data); + case 4: return BmpRead4(file, info, data); + case 8: return BmpRead8(file, info, data); + case 24: return BmpRead24(file, info, data); + default: NOT_REACHED(); + } + break; + + case 1: return BmpRead8Rle(file, info, data); // 8-bit RLE compression + case 2: return BmpRead4Rle(file, info, data); // 4-bit RLE compression default: NOT_REACHED(); - } - case 1: return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression - case 2: return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression - default: NOT_REACHED(); } } - -void BmpDestroyData(BmpData *data) -{ - assert(data != nullptr); - free(data->palette); - free(data->bitmap); -} diff --git a/src/bmp.h b/src/bmp.h index 9b7289e4d1..0e82aff004 100644 --- a/src/bmp.h +++ b/src/bmp.h @@ -11,9 +11,10 @@ #define BMP_H #include "gfx_type.h" +#include "random_access_file_type.h" struct BmpInfo { - uint32_t offset; ///< offset of bitmap data from .bmp file beginning + size_t offset; ///< offset of bitmap data from .bmp file beginning uint32_t width; ///< bitmap width uint32_t height; ///< bitmap height bool os2_bmp; ///< true if OS/2 1.x or windows 2.x bitmap @@ -23,23 +24,11 @@ struct BmpInfo { }; struct BmpData { - Colour *palette; - byte *bitmap; + std::vector palette; + std::vector bitmap; }; -#define BMP_BUFFER_SIZE 1024 - -struct BmpBuffer { - byte data[BMP_BUFFER_SIZE]; - int pos; - int read; - FILE *file; - uint real_pos; -}; - -void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file); -bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data); -bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data); -void BmpDestroyData(BmpData *data); +bool BmpReadHeader(RandomAccessFile &file, BmpInfo &info, BmpData &data); +bool BmpReadBitmap(RandomAccessFile &file, BmpInfo &info, BmpData &data); #endif /* BMP_H */ diff --git a/src/bootstrap_gui.cpp b/src/bootstrap_gui.cpp index 4a1b72d9c5..a9abfb9303 100644 --- a/src/bootstrap_gui.cpp +++ b/src/bootstrap_gui.cpp @@ -40,17 +40,17 @@ static constexpr NWidgetPart _background_widgets[] = { /** * Window description for the background window to prevent smearing. */ -static WindowDesc _background_desc(__FILE__, __LINE__, +static WindowDesc _background_desc( WDP_MANUAL, nullptr, 0, 0, WC_BOOTSTRAP, WC_NONE, WDF_NO_CLOSE, - std::begin(_background_widgets), std::end(_background_widgets) + _background_widgets ); /** The background for the game. */ class BootstrapBackground : public Window { public: - BootstrapBackground() : Window(&_background_desc) + BootstrapBackground() : Window(_background_desc) { this->InitNested(0); CLRBITS(this->flags, WF_WHITE_BORDER); @@ -76,17 +76,17 @@ static constexpr NWidgetPart _nested_bootstrap_errmsg_widgets[] = { }; /** Window description for the error window. */ -static WindowDesc _bootstrap_errmsg_desc(__FILE__, __LINE__, +static WindowDesc _bootstrap_errmsg_desc( WDP_CENTER, nullptr, 0, 0, WC_BOOTSTRAP, WC_NONE, WDF_MODAL | WDF_NO_CLOSE, - std::begin(_nested_bootstrap_errmsg_widgets), std::end(_nested_bootstrap_errmsg_widgets) + _nested_bootstrap_errmsg_widgets ); /** The window for a failed bootstrap. */ class BootstrapErrorWindow : public Window { public: - BootstrapErrorWindow() : Window(&_bootstrap_errmsg_desc) + BootstrapErrorWindow() : Window(_bootstrap_errmsg_desc) { this->InitNested(1); } @@ -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(); } } @@ -133,11 +133,11 @@ static constexpr NWidgetPart _nested_bootstrap_download_status_window_widgets[] }; /** Window description for the download window */ -static WindowDesc _bootstrap_download_status_window_desc(__FILE__, __LINE__, +static WindowDesc _bootstrap_download_status_window_desc( WDP_CENTER, nullptr, 0, 0, WC_NETWORK_STATUS_WINDOW, WC_NONE, WDF_MODAL | WDF_NO_CLOSE, - std::begin(_nested_bootstrap_download_status_window_widgets), std::end(_nested_bootstrap_download_status_window_widgets) + _nested_bootstrap_download_status_window_widgets ); @@ -145,7 +145,7 @@ static WindowDesc _bootstrap_download_status_window_desc(__FILE__, __LINE__, struct BootstrapContentDownloadStatusWindow : public BaseNetworkContentDownloadStatusWindow { public: /** Simple call the constructor of the superclass. */ - BootstrapContentDownloadStatusWindow() : BaseNetworkContentDownloadStatusWindow(&_bootstrap_download_status_window_desc) + BootstrapContentDownloadStatusWindow() : BaseNetworkContentDownloadStatusWindow(_bootstrap_download_status_window_desc) { } @@ -185,11 +185,11 @@ static constexpr NWidgetPart _bootstrap_query_widgets[] = { }; /** The window description for the query. */ -static WindowDesc _bootstrap_query_desc(__FILE__, __LINE__, +static WindowDesc _bootstrap_query_desc( WDP_CENTER, nullptr, 0, 0, WC_CONFIRM_POPUP_QUERY, WC_NONE, WDF_NO_CLOSE, - std::begin(_bootstrap_query_widgets), std::end(_bootstrap_query_widgets) + _bootstrap_query_widgets ); /** The window for the query. It can't use the generic query window as that uses sprites that don't exist yet. */ @@ -198,7 +198,7 @@ class BootstrapAskForDownloadWindow : public Window, ContentCallback { public: /** Start listening to the content client events. */ - BootstrapAskForDownloadWindow() : Window(&_bootstrap_query_desc) + BootstrapAskForDownloadWindow() : Window(_bootstrap_query_desc) { this->InitNested(WN_CONFIRM_POPUP_QUERY_BOOTSTRAP); _network_content_client.AddCallback(this); @@ -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() @@ -385,9 +385,9 @@ bool HandleBootstrap() * This way the mauve and gray colours work and we can show the user interface. */ GfxInitPalettes(); static const int offsets[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0x04, 0x08 }; - for (uint i = 0; i != 16; i++) { - for (int j = 0; j < 8; j++) { - _colour_gradient[i][j] = offsets[i] + j; + for (Colours i = COLOUR_BEGIN; i != COLOUR_END; i++) { + for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) { + SetColourGradient(i, j, offsets[i] + j); } } diff --git a/src/bridge.h b/src/bridge.h index b9d08de46f..4f849baa52 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -27,12 +27,13 @@ enum BridgePieces { BRIDGE_PIECE_MIDDLE_ODD, BRIDGE_PIECE_MIDDLE_EVEN, BRIDGE_PIECE_HEAD, - BRIDGE_PIECE_INVALID, + NUM_BRIDGE_PIECES, }; DECLARE_POSTFIX_INCREMENT(BridgePieces) static const uint MAX_BRIDGES = 13; ///< Maximal number of available bridge specs. +constexpr uint SPRITES_PER_BRIDGE_PIECE = 32; ///< Number of sprites there are per bridge piece. typedef uint BridgeType; ///< Bridge spec number. @@ -41,7 +42,7 @@ typedef uint BridgeType; ///< Bridge spec number. */ struct BridgeSpec { TimerGameCalendar::Year avail_year; ///< the year where it becomes available - byte min_length; ///< the minimum length (not counting start and end tile) + uint8_t min_length; ///< the minimum length (not counting start and end tile) uint16_t max_length; ///< the maximum length (not counting start and end tile) uint16_t price; ///< the price multiplier uint16_t speed; ///< maximum travel speed (1 unit = 1/1.6 mph = 1 km-ish/h) @@ -50,7 +51,7 @@ struct BridgeSpec { StringID material; ///< the string that contains the bridge description StringID transport_name[2]; ///< description of the bridge, when built for road or rail PalSpriteID **sprite_table; ///< table of sprites for drawing the bridge - byte flags; ///< bit 0 set: disable drawing of far pillars. + uint8_t flags; ///< bit 0 set: disable drawing of far pillars. }; extern BridgeSpec _bridge[MAX_BRIDGES]; diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp index fec724bb4c..51b4107fa5 100644 --- a/src/bridge_gui.cpp +++ b/src/bridge_gui.cpp @@ -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" @@ -54,7 +54,7 @@ typedef GUIList GUIBridgeList; ///< List of bridges, used in #B * @param tile_start start tile * @param transport_type transport type. */ -void CcBuildBridge(Commands, const CommandCost &result, TileIndex end_tile, TileIndex tile_start, TransportType transport_type, BridgeType, byte) +void CcBuildBridge(Commands, const CommandCost &result, TileIndex end_tile, TileIndex tile_start, TransportType transport_type, BridgeType, uint8_t) { if (result.Failed()) return; if (_settings_client.sound.confirm) SndPlayTileFx(SND_27_CONSTRUCTION_BRIDGE, end_tile); @@ -75,14 +75,18 @@ 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 sorter_funcs; /* Internal variables */ TileIndex start_tile; TileIndex end_tile; TransportType transport_type; - byte road_rail_type; + uint8_t road_rail_type; GUIBridgeList bridges; int icon_width; ///< Scaled width of the the bridge icon sprite. Scrollbar *vscroll; @@ -122,7 +126,7 @@ private: this->bridges.Sort(); /* Display the current sort variant */ - this->GetWidget(WID_BBS_DROPDOWN_CRITERIA)->widget_data = this->sorter_names[this->bridges.SortType()]; + this->GetWidget(WID_BBS_DROPDOWN_CRITERIA)->widget_data = BuildBridgeWindow::sorter_names[this->bridges.SortType()]; /* Set the modified widgets dirty */ this->SetWidgetDirty(WID_BBS_DROPDOWN_CRITERIA); @@ -147,7 +151,7 @@ private: } public: - BuildBridgeWindow(WindowDesc *desc, TileIndex start, TileIndex end, TransportType transport_type, byte road_rail_type, GUIBridgeList &&bl) : Window(desc), + BuildBridgeWindow(WindowDesc &desc, TileIndex start, TileIndex end, TransportType transport_type, uint8_t road_rail_type, GUIBridgeList &&bl) : Window(desc), start_tile(start), end_tile(end), transport_type(transport_type), @@ -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(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; } } @@ -231,11 +232,11 @@ public: case WID_BBS_BRIDGE_LIST: { Rect tr = r.WithHeight(this->resize.step_height).Shrink(WidgetDimensions::scaled.matrix); bool rtl = _current_text_dir == TD_RTL; - for (int i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < (int)this->bridges.size(); i++) { - const BuildBridgeData &bridge_data = this->bridges.at(i); - const BridgeSpec *b = bridge_data.spec; + auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->bridges); + for (auto it = first; it != last; ++it) { + const BridgeSpec *b = it->spec; DrawSpriteIgnorePadding(b->sprite, b->pal, tr.WithWidth(this->icon_width, rtl), SA_HOR_CENTER | SA_BOTTOM); - DrawStringMultiLine(tr.Indent(this->icon_width + WidgetDimensions::scaled.hsep_normal, rtl), GetBridgeSelectString(bridge_data)); + DrawStringMultiLine(tr.Indent(this->icon_width + WidgetDimensions::scaled.hsep_normal, rtl), GetBridgeSelectString(*it)); tr = tr.Translate(0, this->resize.step_height); } 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 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 */ @@ -341,11 +334,11 @@ static constexpr NWidgetPart _nested_build_bridge_widgets[] = { }; /** Window definition for the rail bridge selection window. */ -static WindowDesc _build_bridge_desc(__FILE__, __LINE__, +static WindowDesc _build_bridge_desc( WDP_AUTO, "build_bridge", 200, 114, WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR, WDF_CONSTRUCTION, - std::begin(_nested_build_bridge_widgets), std::end(_nested_build_bridge_widgets) + _nested_build_bridge_widgets ); /** @@ -358,7 +351,7 @@ static WindowDesc _build_bridge_desc(__FILE__, __LINE__, * @param transport_type The transport type * @param road_rail_type The road/rail type */ -void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transport_type, byte road_rail_type) +void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transport_type, uint8_t road_rail_type) { CloseWindowByClass(WC_BUILD_BRIDGE); @@ -442,7 +435,7 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo } if (!bl.empty()) { - new BuildBridgeWindow(&_build_bridge_desc, start, end, transport_type, road_rail_type, std::move(bl)); + new BuildBridgeWindow(_build_bridge_desc, start, end, transport_type, road_rail_type, std::move(bl)); } else { ShowErrorMessage(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE, errmsg, WL_INFO, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE); } diff --git a/src/bridge_map.cpp b/src/bridge_map.cpp index eb28673867..f5825bf504 100644 --- a/src/bridge_map.cpp +++ b/src/bridge_map.cpp @@ -69,10 +69,9 @@ TileIndex GetOtherBridgeEnd(TileIndex tile) */ int GetBridgeHeight(TileIndex t) { - int h; - Slope tileh = GetTileSlope(t, &h); + auto [tileh, h] = GetTileSlopeZ(t); Foundation f = GetBridgeFoundation(tileh, DiagDirToAxis(GetTunnelBridgeDirection(t))); /* one height level extra for the ramp */ - return h + 1 + ApplyFoundationToSlope(f, &tileh); + return h + 1 + ApplyFoundationToSlope(f, tileh); } diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 467b9c0bed..22c69a5215 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -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" @@ -95,7 +96,7 @@ static constexpr NWidgetPart _nested_build_vehicle_widgets[] = { bool _engine_sort_direction; ///< \c false = descending, \c true = ascending. -byte _engine_sort_last_criteria[] = {0, 0, 0, 0}; ///< Last set sort criteria, for each vehicle type. +uint8_t _engine_sort_last_criteria[] = {0, 0, 0, 0}; ///< Last set sort criteria, for each vehicle type. bool _engine_sort_last_order[] = {false, false, false, false}; ///< Last set direction of the sort order, for each vehicle type. bool _engine_sort_show_hidden_engines[] = {false, false, false, false}; ///< Last set 'show hidden engines' setting for each vehicle type. static CargoID _engine_sort_last_cargo_criteria[] = {CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY}; ///< Last set filter criteria, for each vehicle type. @@ -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 _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, }; @@ -998,18 +995,16 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number, * @param type Type of vehicle (VEH_*) * @param r The Rect of the list * @param eng_list What engines to draw - * @param min where to start in the list - * @param max where in the list to end + * @param sb Scrollbar of list. * @param selected_id what engine to highlight as selected, if any * @param show_count Whether to show the amount of engines or not * @param selected_group the group to list the engines of */ -void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, uint16_t min, uint16_t max, EngineID selected_id, bool show_count, GroupID selected_group) +void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group) { static const int sprite_y_offsets[] = { -1, -1, -2, -2 }; - /* Obligatory sanity checks! */ - assert(max <= eng_list.size()); + auto [first, last] = sb.GetVisibleRangeIterators(eng_list); bool rtl = _current_text_dir == TD_RTL; int step_size = GetEngineListHeight(type); @@ -1017,7 +1012,7 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right; int sprite_width = sprite_left + sprite_right; int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width); - int linecolour = _colour_gradient[COLOUR_ORANGE][4]; + int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL); Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix); int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2; @@ -1028,9 +1023,8 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li replace_icon = GetSpriteSize(SPR_GROUP_REPLACE_ACTIVE); uint biggest_num_engines = 0; - for (auto i = min; i < max; i++) { - const auto &item = eng_list[i]; - const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id); + for (auto it = first; it != last; ++it) { + const uint num_engines = GetGroupNumEngines(_local_company, selected_group, it->engine_id); biggest_num_engines = std::max(biggest_num_engines, num_engines); } @@ -1047,13 +1041,31 @@ 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 (; min < max; min++, y += step_size) { - const auto &item = eng_list[min]; + for (auto it = first; it != last; ++it) { + const auto &item = *it; uint indent = item.indent * WidgetDimensions::scaled.hsep_indent; - 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; + bool has_variants = HasFlag(item.flags, EngineDisplayFlags::HasVariants); + bool is_folded = HasFlag(item.flags, EngineDisplayFlags::IsFolded); + bool shaded = HasFlag(item.flags, EngineDisplayFlags::Shaded); + + 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); @@ -1081,14 +1093,7 @@ 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 = (min + 1U) < eng_list.size() && eng_list[min + 1].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; } } @@ -1115,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 (HasFlag(item.flags, EngineDisplayFlags::HasVariants) && !HasFlag(item.flags, EngineDisplayFlags::IsFolded)) { + /* Add this engine again as a child */ + if (!HasFlag(item.flags, EngineDisplayFlags::Shaded)) { + 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 @@ -1128,7 +1171,7 @@ struct BuildVehicleWindow : Window { RoadType roadtype; ///< Road type to show, or #INVALID_ROADTYPE. } filter; ///< Filter to apply. bool descending_sort_order; ///< Sort direction, @see _engine_sort_direction - byte sort_criteria; ///< Current sort criterium. + uint8_t sort_criteria; ///< Current sort criterium. bool show_hidden_engines; ///< State of the 'show hidden engines' button. bool listview_mode; ///< If set, only display the available vehicles and do not show a 'build' button. EngineID sel_engine; ///< Currently selected engine, or #INVALID_ENGINE @@ -1158,28 +1201,7 @@ 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) + 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; this->listview_mode = tile == INVALID_TILE; @@ -1234,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::ranges::find_if(this->eng_list, [&](GUIEngineListItem &item) { return !HasFlag(item.flags, EngineDisplayFlags::Shaded); }); if (it != this->eng_list.end()) engine = it->engine_id; this->SelectEngine(engine); } @@ -1286,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); } @@ -1331,7 +1353,7 @@ struct BuildVehicleWindow : Window { this->eng_list.Filter(this->cargo_filter_criteria); if (0 == this->eng_list.size()) { // no engine passed through the filter, invalidate the previously selected engine this->SelectEngine(INVALID_ENGINE); - } else if (std::find(this->eng_list.begin(), this->eng_list.end(), this->sel_engine) == this->eng_list.end()) { // previously selected engine didn't pass the filter, select the first engine of the list + } else if (std::ranges::find(this->eng_list, this->sel_engine, &GUIEngineListItem::engine_id) == this->eng_list.end()) { // previously selected engine didn't pass the filter, select the first engine of the list this->SelectEngine(this->eng_list[0].engine_id); } } @@ -1404,7 +1426,7 @@ struct BuildVehicleWindow : Window { /* ensure primary engine of variant group is in list */ for (const auto &variant : variants) { - if (std::find(list.begin(), list.end(), variant) == list.end()) { + if (std::ranges::find(list, variant, &GUIEngineListItem::engine_id) == list.end()) { const Engine *e = Engine::Get(variant); list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0); if (e->u.rail.railveh_type != RAILVEH_WAGON) num_engines++; @@ -1519,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: @@ -1547,7 +1568,7 @@ struct BuildVehicleWindow : Window { } for (const auto &variant : variants) { - if (std::find(this->eng_list.begin(), this->eng_list.end(), variant) == this->eng_list.end()) { + if (std::ranges::find(this->eng_list, variant, &GUIEngineListItem::engine_id) == this->eng_list.end()) { const Engine *e = Engine::Get(variant); this->eng_list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0); } @@ -1557,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(); } @@ -1567,25 +1587,54 @@ struct BuildVehicleWindow : Window { DropDownList list; /* Add item for disabling filtering. */ - list.push_back(std::make_unique(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(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(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(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; } + void BuildVehicle() + { + EngineID sel_eng = this->sel_engine; + if (sel_eng == INVALID_ENGINE) return; + + CargoID cargo = this->cargo_filter_criteria; + if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO; + if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) { + Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); + } else { + Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); + } + + /* Update last used variant in hierarchy and refresh if necessary. */ + bool refresh = false; + EngineID parent = sel_eng; + while (parent != INVALID_ENGINE) { + Engine *e = Engine::Get(parent); + refresh |= (e->display_last_variant != sel_eng); + e->display_last_variant = sel_eng; + parent = e->info.variant_id; + } + + if (refresh) { + InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window + InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well + } + } + void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { switch (widget) { @@ -1610,7 +1659,7 @@ struct BuildVehicleWindow : Window { if (it != this->eng_list.end()) { const auto &item = *it; const Rect r = this->GetWidget(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL); - if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) { + if (HasFlag(item.flags, EngineDisplayFlags::HasVariants) && IsInsideMM(r.left, r.right, pt.x)) { /* toggle folded flag on engine */ assert(item.variant_id != INVALID_ENGINE); Engine *engine = Engine::Get(item.variant_id); @@ -1620,7 +1669,7 @@ struct BuildVehicleWindow : Window { InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well return; } - if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) e = item.engine_id; + if (!HasFlag(item.flags, EngineDisplayFlags::Shaded)) e = item.engine_id; } this->SelectEngine(e); this->SetDirty(); @@ -1648,34 +1697,9 @@ struct BuildVehicleWindow : Window { break; } - case WID_BV_BUILD: { - EngineID sel_eng = this->sel_engine; - if (sel_eng != INVALID_ENGINE) { - CargoID cargo = this->cargo_filter_criteria; - if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO; - if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) { - Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); - } else { - Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); - } - - /* Update last used variant in hierarchy and refresh if necessary. */ - bool refresh = false; - EngineID parent = sel_eng; - while (parent != INVALID_ENGINE) { - Engine *e = Engine::Get(parent); - refresh |= (e->display_last_variant != sel_eng); - e->display_last_variant = sel_eng; - parent = e->info.variant_id; - } - if (refresh) { - InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window - InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well - return; - } - } + case WID_BV_BUILD: + this->BuildVehicle(); break; - } case WID_BV_RENAME: { EngineID sel_eng = this->sel_engine; @@ -1723,7 +1747,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: @@ -1742,43 +1766,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(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; } } @@ -1791,8 +1815,7 @@ struct BuildVehicleWindow : Window { this->vehicle_type, r, this->eng_list, - this->vscroll->GetPosition(), - static_cast(std::min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->eng_list.size())), + *this->vscroll, this->sel_engine, false, DEFAULT_GROUP @@ -1821,7 +1844,7 @@ struct BuildVehicleWindow : Window { int needed_height = this->details_height; /* Draw details panels. */ if (this->sel_engine != INVALID_ENGINE) { - const Rect r = this->GetWidget(WID_BV_PANEL)->GetCurrentRect().Shrink(WidgetDimensions::scaled.frametext, WidgetDimensions::scaled.framerect); + const Rect r = this->GetWidget(WID_BV_PANEL)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect); int text_end = DrawVehiclePurchaseInfo(r.left, r.right, r.top, this->sel_engine, this->te); needed_height = std::max(needed_height, (text_end - r.top) / GetCharacterHeight(FS_NORMAL)); } @@ -1834,11 +1857,11 @@ struct BuildVehicleWindow : Window { } } - void OnQueryTextFinished(char *str) override + void OnQueryTextFinished(std::optional str) override { - if (str == nullptr) return; + if (!str.has_value()) return; - Command::Post(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type, this->rename_engine, str); + Command::Post(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type, this->rename_engine, *str); } void OnDropdownSelect(WidgetID widget, int index) override @@ -1899,11 +1922,11 @@ struct BuildVehicleWindow : Window { }}; }; -static WindowDesc _build_vehicle_desc(__FILE__, __LINE__, +static WindowDesc _build_vehicle_desc( WDP_AUTO, "build_vehicle", 240, 268, WC_BUILD_VEHICLE, WC_NONE, WDF_CONSTRUCTION, - std::begin(_nested_build_vehicle_widgets), std::end(_nested_build_vehicle_widgets), + _nested_build_vehicle_widgets, &BuildVehicleWindow::hotkeys ); @@ -1919,5 +1942,5 @@ void ShowBuildVehicleWindow(TileIndex tile, VehicleType type) CloseWindowById(WC_BUILD_VEHICLE, num); - new BuildVehicleWindow(&_build_vehicle_desc, tile, type); + new BuildVehicleWindow(_build_vehicle_desc, tile, type); } diff --git a/src/cachecheck.cpp b/src/cachecheck.cpp new file mode 100644 index 0000000000..a68fa7871c --- /dev/null +++ b/src/cachecheck.cpp @@ -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 . + */ + +/** @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 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 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 grf_cache; + std::vector veh_cache; + std::vector gro_cache; + std::vector 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 old_town_stations_near; + for (Town *t : Town::Iterate()) old_town_stations_near.push_back(t->stations_near); + + std::vector old_industry_stations_near; + for (Industry *ind : Industry::Iterate()) old_industry_stations_near.push_back(ind->stations_near); + + std::vector 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 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++; + } +} diff --git a/src/cargo_type.h b/src/cargo_type.h index 021f7f364d..7dc765b752 100644 --- a/src/cargo_type.h +++ b/src/cargo_type.h @@ -19,7 +19,7 @@ using CargoLabel = StrongType::Typedef { */ inline uint GetCount() const { - return std::count_if(this->begin(), this->end(), [](uint amount) { return amount != 0; }); + return std::ranges::count_if(*this, [](uint amount) { return amount != 0; }); } }; /** Types of cargo source and destination */ -enum class SourceType : byte { +enum class SourceType : uint8_t { Industry, ///< Source/destination is an industry Town, ///< Source/destination is a town Headquarters, ///< Source/destination are company headquarters diff --git a/src/cargoaction.cpp b/src/cargoaction.cpp index c70eee9bfb..e6d32aac89 100644 --- a/src/cargoaction.cpp +++ b/src/cargoaction.cpp @@ -107,7 +107,7 @@ bool CargoDelivery::operator()(CargoPacket *cp) { uint remove = this->Preprocess(cp); this->source->RemoveFromMeta(cp, VehicleCargoList::MTA_DELIVER, remove); - this->payment->PayFinalDelivery(cp, remove, this->current_tile); + this->payment->PayFinalDelivery(this->cargo, cp, remove, this->current_tile); return this->Postprocess(cp, remove); } diff --git a/src/cargoaction.h b/src/cargoaction.h index c0c58b12cd..c9bb7351b3 100644 --- a/src/cargoaction.h +++ b/src/cargoaction.h @@ -40,9 +40,10 @@ class CargoDelivery : public CargoRemoval { protected: TileIndex current_tile; ///< Current tile cargo delivery is happening. CargoPayment *payment; ///< Payment object where payments will be registered. + CargoID cargo; ///< The cargo type of the cargo. public: - CargoDelivery(VehicleCargoList *source, uint max_move, CargoPayment *payment, TileIndex current_tile) : - CargoRemoval(source, max_move), current_tile(current_tile), payment(payment) {} + CargoDelivery(VehicleCargoList *source, uint max_move, CargoID cargo, CargoPayment *payment, TileIndex current_tile) : + CargoRemoval(source, max_move), current_tile(current_tile), payment(payment), cargo(cargo) {} bool operator()(CargoPacket *cp); }; diff --git a/src/cargomonitor.cpp b/src/cargomonitor.cpp index dc5ab7643f..ab8dc4a06f 100644 --- a/src/cargomonitor.cpp +++ b/src/cargomonitor.cpp @@ -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 p(monitor, 0); - monitor_map.insert(p); + monitor_map.emplace(monitor, 0); } return 0; } else { diff --git a/src/cargomonitor.h b/src/cargomonitor.h index 614f10002e..7360fa2c66 100644 --- a/src/cargomonitor.h +++ b/src/cargomonitor.h @@ -35,17 +35,14 @@ extern CargoMonitorMap _cargo_pickups; extern CargoMonitorMap _cargo_deliveries; -/** Constants for encoding and extracting cargo monitors. */ -enum CargoCompanyBits { - CCB_TOWN_IND_NUMBER_START = 0, ///< Start bit of the town or industry number. - CCB_TOWN_IND_NUMBER_LENGTH = 16, ///< Number of bits of the town or industry number. - CCB_IS_INDUSTRY_BIT = 16, ///< Bit indicating the town/industry number is an industry. - CCB_IS_INDUSTRY_BIT_VALUE = 1ul << CCB_IS_INDUSTRY_BIT, ///< Value of the #CCB_IS_INDUSTRY_BIT bit. - CCB_CARGO_TYPE_START = 19, ///< Start bit of the cargo type field. - CCB_CARGO_TYPE_LENGTH = 6, ///< Number of bits of the cargo type field. - CCB_COMPANY_START = 25, ///< Start bit of the company field. - CCB_COMPANY_LENGTH = 4, ///< Number of bits of the company field. -}; +/* Constants for encoding and extracting cargo monitors. */ +constexpr uint8_t CCB_TOWN_IND_NUMBER_START = 0; ///< Start bit of the town or industry number. +constexpr uint8_t CCB_TOWN_IND_NUMBER_LENGTH = 16; ///< Number of bits of the town or industry number. +constexpr uint8_t CCB_IS_INDUSTRY_BIT = 16; ///< Bit indicating the town/industry number is an industry. +constexpr uint8_t CCB_CARGO_TYPE_START = 19; ///< Start bit of the cargo type field. +constexpr uint8_t CCB_CARGO_TYPE_LENGTH = 6; ///< Number of bits of the cargo type field. +constexpr uint8_t CCB_COMPANY_START = 25; ///< Start bit of the company field. +constexpr uint8_t CCB_COMPANY_LENGTH = 4; ///< Number of bits of the company field. static_assert(NUM_CARGO <= (1 << CCB_CARGO_TYPE_LENGTH)); static_assert(MAX_COMPANIES <= (1 << CCB_COMPANY_LENGTH)); diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index fc5031d7eb..02145491fd 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -432,11 +432,12 @@ void VehicleCargoList::AgeCargo() * @param next_station ID of the station the vehicle will go to next. * @param order_flags OrderUnloadFlags that will apply to the unload operation. * @param ge GoodsEntry for getting the flows. + * @param cargo The cargo type of the cargo. * @param payment Payment object for registering transfers. * @param current_tile Current tile the cargo handling is happening on. * return If any cargo will be unloaded. */ -bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8_t order_flags, const GoodsEntry *ge, CargoPayment *payment, TileIndex current_tile) +bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8_t order_flags, const GoodsEntry *ge, CargoID cargo, CargoPayment *payment, TileIndex current_tile) { this->AssertCountConsistency(); assert(this->action_counts[MTA_LOAD] == 0); @@ -512,7 +513,7 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID case MTA_TRANSFER: this->packets.push_front(cp); /* Add feeder share here to allow reusing field for next station. */ - share = payment->PayTransfer(cp, cp->count, current_tile); + share = payment->PayTransfer(cargo, cp, cp->count, current_tile); cp->AddFeederShare(share); this->feeder_share += share; cp->next_hop = cargo_next; @@ -619,11 +620,12 @@ uint VehicleCargoList::Shift(uint max_move, VehicleCargoList *dest) * ranges defined by designation_counts. * @param dest StationCargoList to add transferred cargo to. * @param max_move Maximum amount of cargo to move. + * @param cargo The cargo type of the cargo. * @param payment Payment object to register payments in. * @param current_tile Current tile the cargo handling is happening on. * @return Amount of cargo actually unloaded. */ -uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPayment *payment, TileIndex current_tile) +uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoID cargo, CargoPayment *payment, TileIndex current_tile) { uint moved = 0; if (this->action_counts[MTA_TRANSFER] > 0) { @@ -633,7 +635,7 @@ uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPaymen } if (this->action_counts[MTA_TRANSFER] == 0 && this->action_counts[MTA_DELIVER] > 0 && moved < max_move) { uint move = std::min(this->action_counts[MTA_DELIVER], max_move - moved); - this->ShiftCargo(CargoDelivery(this, move, payment, current_tile)); + this->ShiftCargo(CargoDelivery(this, move, cargo, payment, current_tile)); moved += move; } return moved; diff --git a/src/cargopacket.h b/src/cargopacket.h index 6498760822..b97a521a10 100644 --- a/src/cargopacket.h +++ b/src/cargopacket.h @@ -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 friend class CargoList; @@ -478,7 +478,7 @@ public: void InvalidateCache(); - bool Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8_t order_flags, const GoodsEntry *ge, CargoPayment *payment, TileIndex current_tile); + bool Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8_t order_flags, const GoodsEntry *ge, CargoID cargo, CargoPayment *payment, TileIndex current_tile); /** * Marks all cargo in the vehicle as to be kept. This is mostly useful for @@ -498,7 +498,7 @@ public: template uint Reassign(uint max_move); uint Return(uint max_move, StationCargoList *dest, StationID next_station, TileIndex current_tile); - uint Unload(uint max_move, StationCargoList *dest, CargoPayment *payment, TileIndex current_tile); + uint Unload(uint max_move, StationCargoList *dest, CargoID cargo, CargoPayment *payment, TileIndex current_tile); uint Shift(uint max_move, VehicleCargoList *dest); uint Truncate(uint max_move = UINT_MAX); uint Reroute(uint max_move, VehicleCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge); diff --git a/src/cargotype.cpp b/src/cargotype.cpp index 98d53a9d1c..454895c9b7 100644 --- a/src/cargotype.cpp +++ b/src/cargotype.cpp @@ -19,6 +19,8 @@ #include "table/strings.h" #include "table/cargo_const.h" +#include + #include "safeguards.h" CargoSpec CargoSpec::array[NUM_CARGO]; @@ -41,6 +43,20 @@ CargoTypes _standard_cargo_mask; */ static std::vector _default_cargo_labels; +/** + * Default cargo translation for upto version 7 NewGRFs. + * This maps the original 12 cargo slots to their original label. If a climate dependent cargo is not present it will + * map to CT_INVALID. For default cargoes this ends up as a 1:1 mapping via climate slot -> label -> cargo ID. + */ +static std::array _climate_dependent_cargo_labels; + +/** + * Default cargo translation for version 8+ NewGRFs. + * This maps the 32 "bitnum" cargo slots to their original label. If a bitnum is not present it will + * map to CT_INVALID. + */ +static std::array _climate_independent_cargo_labels; + /** * Set up the default cargo types for the given landscape type. * @param l Landscape @@ -51,30 +67,37 @@ void SetupCargoForClimate(LandscapeID l) _cargo_mask = 0; _default_cargo_labels.clear(); + _climate_dependent_cargo_labels.fill(CT_INVALID); + _climate_independent_cargo_labels.fill(CT_INVALID); /* Copy from default cargo by label or index. */ auto insert = std::begin(CargoSpec::array); for (const auto &cl : _default_climate_cargo[l]) { - /* Check if value is an index into the cargo table */ - if (std::holds_alternative(cl)) { - /* Copy the default cargo by index. */ - *insert = _default_cargo[std::get(cl)]; - } else { - /* Search for label in default cargo types and copy if found. */ - CargoLabel label = std::get(cl); - auto found = std::find_if(std::begin(_default_cargo), std::end(_default_cargo), [&label](const CargoSpec &cs) { return cs.label == label; }); - if (found != std::end(_default_cargo)) { - *insert = *found; - } else { + struct visitor { + const CargoSpec &operator()(const int &index) + { + /* Copy the default cargo by index. */ + return _default_cargo[index]; + } + const CargoSpec &operator()(const CargoLabel &label) + { + /* Search for label in default cargo types and copy if found. */ + auto found = std::ranges::find(_default_cargo, label, &CargoSpec::label); + if (found != std::end(_default_cargo)) return *found; + /* Index or label is invalid, this should not happen. */ NOT_REACHED(); } - } + }; + + *insert = std::visit(visitor{}, cl); if (insert->IsValid()) { SetBit(_cargo_mask, insert->Index()); _default_cargo_labels.push_back(insert->label); + _climate_dependent_cargo_labels[insert->Index()] = insert->label; + _climate_independent_cargo_labels[insert->bitnum] = insert->label; } ++insert; } @@ -85,6 +108,24 @@ void SetupCargoForClimate(LandscapeID l) BuildCargoLabelMap(); } +/** + * Get default climate-dependent cargo translation table for a NewGRF, used if the NewGRF does not provide its own. + * @return Default translation table for GRF version. + */ +std::span GetClimateDependentCargoTranslationTable() +{ + return _climate_dependent_cargo_labels; +} + +/** + * Get default climate-independent cargo translation table for a NewGRF, used if the NewGRF does not provide its own. + * @return Default translation table for GRF version. + */ +std::span GetClimateIndependentCargoTranslationTable() +{ + return _climate_independent_cargo_labels; +} + /** * Build cargo label map. * This is called multiple times during NewGRF initialization as cargos are defined, so that TranslateRefitMask() and @@ -99,7 +140,7 @@ void BuildCargoLabelMap() /* 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()); } } @@ -130,23 +171,6 @@ Dimension GetLargestCargoIconSize() return size; } -/** - * Find the CargoID of a 'bitnum' value. - * @param bitnum 'bitnum' to find. - * @return First CargoID with the given bitnum, or #INVALID_CARGO if not found or if the provided \a bitnum is invalid. - */ -CargoID GetCargoIDByBitnum(uint8_t bitnum) -{ - if (bitnum == INVALID_CARGO_BITNUM) return INVALID_CARGO; - - for (const CargoSpec *cs : CargoSpec::Iterate()) { - if (cs->bitnum == bitnum) return cs->Index(); - } - - /* No matching label was found, so it is invalid */ - return INVALID_CARGO; -} - /** * Get sprite for showing cargo of this type. * @return Sprite number to use. @@ -235,3 +259,41 @@ uint64_t CargoSpec::WeightOfNUnitsInTrain(uint32_t n) const if (this->is_freight) n *= _settings_game.vehicle.freight_trains; return this->WeightOfNUnits(n); } + +/** + * Build comma-separated cargo acceptance string. + * @param acceptance CargoArray filled with accepted cargo. + * @param label Label to prefix cargo acceptance list. + * @return String of accepted cargo, or nullopt if no cargo is accepted. + */ +std::optional BuildCargoAcceptanceString(const CargoArray &acceptance, StringID label) +{ + std::string_view list_separator = GetListSeparator(); + + /* Cargo acceptance is displayed in a extra multiline */ + std::stringstream line; + line << GetString(label); + + bool found = false; + for (const CargoSpec *cs : _sorted_cargo_specs) { + CargoID cid = cs->Index(); + if (acceptance[cid] > 0) { + /* Add a comma between each item. */ + if (found) line << list_separator; + found = true; + + /* If the accepted value is less than 8, show it in 1/8:ths */ + if (acceptance[cid] < 8) { + SetDParam(0, acceptance[cid]); + SetDParam(1, cs->name); + line << GetString(STR_LAND_AREA_INFORMATION_CARGO_EIGHTS); + } else { + line << GetString(cs->name); + } + } + } + + if (found) return line.str(); + + return std::nullopt; +} diff --git a/src/cargotype.h b/src/cargotype.h index 81758c8b42..335d531798 100644 --- a/src/cargotype.h +++ b/src/cargotype.h @@ -18,7 +18,7 @@ #include "core/bitmath_func.hpp" /** Town growth effect when delivering cargo. */ -enum TownAcceptanceEffect : byte { +enum TownAcceptanceEffect : uint8_t { TAE_BEGIN = 0, TAE_NONE = TAE_BEGIN, ///< Cargo has no effect. TAE_PASSENGERS, ///< Cargo behaves passenger-like. @@ -31,7 +31,7 @@ enum TownAcceptanceEffect : byte { }; /** Town effect when producing cargo. */ -enum TownProductionEffect : byte { +enum TownProductionEffect : uint8_t { TPE_NONE, ///< Town will not produce this cargo type. TPE_PASSENGERS, ///< Cargo behaves passenger-like for production. TPE_MAIL, ///< Cargo behaves mail-like for production. @@ -45,7 +45,7 @@ enum TownProductionEffect : byte { }; /** Cargo classes. */ -enum CargoClass { +enum CargoClass : uint16_t { CC_NOAVAILABLE = 0, ///< No cargo class has been specified CC_PASSENGERS = 1 << 0, ///< Passengers CC_MAIL = 1 << 1, ///< Mail @@ -57,29 +57,37 @@ enum CargoClass { CC_REFRIGERATED = 1 << 7, ///< Refrigerated cargo (Food, Fruit) CC_HAZARDOUS = 1 << 8, ///< Hazardous cargo (Nuclear Fuel, Explosives, etc.) CC_COVERED = 1 << 9, ///< Covered/Sheltered Freight (Transportation in Box Vans, Silo Wagons, etc.) + CC_OVERSIZED = 1 << 10, ///< Oversized (stake/flatbed wagon) + CC_POWDERIZED = 1 << 11, ///< Powderized, moist protected (powder/silo wagon) + CC_NOT_POURABLE = 1 << 12, ///< Not Pourable (open wagon, but not hopper wagon) + CC_POTABLE = 1 << 13, ///< Potable / food / clean. + CC_NON_POTABLE = 1 << 14, ///< Non-potable / non-food / dirty. CC_SPECIAL = 1 << 15, ///< Special bit used for livery refit tricks instead of normal cargoes. }; -static const byte INVALID_CARGO_BITNUM = 0xFF; ///< Constant representing invalid cargo +/** Bitmask of cargo classes. */ +using CargoClasses = uint16_t; + +static const uint8_t INVALID_CARGO_BITNUM = 0xFF; ///< Constant representing invalid cargo 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 classes; ///< Classes of this cargo type. @see CargoClass + uint16_t multiplier = 0x100; ///< Capacity multiplier for vehicles. (8 fractional bits) + CargoClasses 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. @@ -205,7 +213,8 @@ extern CargoTypes _standard_cargo_mask; void SetupCargoForClimate(LandscapeID l); bool IsDefaultCargo(CargoID cid); void BuildCargoLabelMap(); -CargoID GetCargoIDByBitnum(uint8_t bitnum); + +std::optional BuildCargoAcceptanceString(const CargoArray &acceptance, StringID label); inline CargoID GetCargoIDByLabel(CargoLabel label) { diff --git a/src/cheat.cpp b/src/cheat.cpp index 9fba889da0..30adc36fbe 100644 --- a/src/cheat.cpp +++ b/src/cheat.cpp @@ -20,20 +20,3 @@ void InitializeCheats() { memset(&_cheats, 0, sizeof(Cheats)); } - -/** - * Return true if any cheat has been used, false otherwise - * @return has a cheat been used? - */ -bool CheatHasBeenUsed() -{ - /* Cannot use lengthof because _cheats is of type Cheats, not Cheat */ - const Cheat *cht = (Cheat*)&_cheats; - const Cheat *cht_last = &cht[sizeof(_cheats) / sizeof(Cheat)]; - - for (; cht != cht_last; cht++) { - if (cht->been_used) return true; - } - - return false; -} diff --git a/src/cheat_func.h b/src/cheat_func.h index 6d26e71c4a..5c205b04bd 100644 --- a/src/cheat_func.h +++ b/src/cheat_func.h @@ -16,6 +16,5 @@ extern Cheats _cheats; void ShowCheatWindow(); -bool CheatHasBeenUsed(); #endif /* CHEAT_FUNC_H */ diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp index 0a1f7fbeee..d226591add 100644 --- a/src/cheat_gui.cpp +++ b/src/cheat_gui.cpp @@ -129,6 +129,7 @@ static int32_t ClickChangeDateCheat(int32_t new_value, int32_t) InvalidateWindowClassesData(WC_BUS_STATION, 0); InvalidateWindowClassesData(WC_TRUCK_STATION, 0); InvalidateWindowClassesData(WC_BUILD_OBJECT, 0); + InvalidateWindowClassesData(WC_FINANCES, 0); ResetSignalVariant(); return TimerGameCalendar::year.base(); } @@ -145,7 +146,7 @@ static int32_t ClickChangeMaxHlCheat(int32_t new_value, int32_t) /* Check if at least one mountain on the map is higher than the new value. * If yes, disallow the change. */ - for (TileIndex t = 0; t < Map::Size(); t++) { + for (const auto t : Map::Iterate()) { if ((int32_t)TileHeight(t) > new_value) { ShowErrorMessage(STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN, INVALID_STRING_ID, WL_ERROR); /* Return old, unchanged value */ @@ -230,7 +231,7 @@ struct CheatWindow : Window { uint line_height; Dimension icon; ///< Dimension of company icon sprite - CheatWindow(WindowDesc *desc) : Window(desc) + CheatWindow(WindowDesc &desc) : Window(desc) { this->InitNested(); } @@ -298,38 +299,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 +339,8 @@ struct CheatWindow : Window { this->line_height = std::max(this->icon.height, SETTING_BUTTON_HEIGHT); this->line_height = std::max(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 @@ -405,14 +405,14 @@ struct CheatWindow : Window { this->SetDirty(); } - void OnQueryTextFinished(char *str) override + void OnQueryTextFinished(std::optional str) override { /* Was 'cancel' pressed or nothing entered? */ - if (str == nullptr || StrEmpty(str)) return; + if (!str.has_value() || str->empty()) return; const CheatEntry *ce = &_cheats_ui[clicked_widget]; int oldvalue = (int32_t)ReadValue(ce->variable, ce->type); - int value = atoi(str); + int value = atoi(str->c_str()); *ce->been_used = true; value = ce->proc(value, value - oldvalue); @@ -426,16 +426,16 @@ struct CheatWindow : Window { }; /** Window description of the cheats GUI. */ -static WindowDesc _cheats_desc(__FILE__, __LINE__, +static WindowDesc _cheats_desc( WDP_AUTO, "cheats", 0, 0, WC_CHEATS, WC_NONE, 0, - std::begin(_nested_cheat_widgets), std::end(_nested_cheat_widgets) + _nested_cheat_widgets ); /** Open cheat window. */ void ShowCheatWindow() { CloseWindowById(WC_CHEATS, 0); - new CheatWindow(&_cheats_desc); + new CheatWindow(_cheats_desc); } diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp index 9831764b31..c40904e0fd 100644 --- a/src/clear_cmd.cpp +++ b/src/clear_cmd.cpp @@ -44,7 +44,7 @@ static CommandCost ClearTile_Clear(TileIndex tile, DoCommandFlag flags) return price; } -void DrawClearLandTile(const TileInfo *ti, byte set) +void DrawClearLandTile(const TileInfo *ti, uint8_t set) { DrawGroundSprite(SPR_FLAT_BARE_LAND + SlopeToSpriteOffset(ti->tileh) + set * 19, PAL_NONE); } @@ -129,8 +129,7 @@ static void DrawTile_Clear(TileInfo *ti) static int GetSlopePixelZ_Clear(TileIndex tile, uint x, uint y, bool) { - int z; - Slope tileh = GetTilePixelSlope(tile, &z); + auto [tileh, z] = GetTilePixelSlope(tile); return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh); } @@ -145,27 +144,11 @@ static void UpdateFences(TileIndex tile) assert(IsTileType(tile, MP_CLEAR) && IsClearGround(tile, CLEAR_FIELDS)); bool dirty = false; - bool neighbour = (IsTileType(TILE_ADDXY(tile, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 1, 0), CLEAR_FIELDS)); - if (!neighbour && GetFence(tile, DIAGDIR_SW) == 0) { - SetFence(tile, DIAGDIR_SW, 3); - dirty = true; - } - - neighbour = (IsTileType(TILE_ADDXY(tile, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, 1), CLEAR_FIELDS)); - if (!neighbour && GetFence(tile, DIAGDIR_SE) == 0) { - SetFence(tile, DIAGDIR_SE, 3); - dirty = true; - } - - neighbour = (IsTileType(TILE_ADDXY(tile, -1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, -1, 0), CLEAR_FIELDS)); - if (!neighbour && GetFence(tile, DIAGDIR_NE) == 0) { - SetFence(tile, DIAGDIR_NE, 3); - dirty = true; - } - - neighbour = (IsTileType(TILE_ADDXY(tile, 0, -1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, -1), CLEAR_FIELDS)); - if (!neighbour && GetFence(tile, DIAGDIR_NW) == 0) { - SetFence(tile, DIAGDIR_NW, 3); + for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { + if (GetFence(tile, dir) != 0) continue; + TileIndex neighbour = tile + TileOffsByDiagDir(dir); + if (IsTileType(neighbour, MP_CLEAR) && IsClearGround(neighbour, CLEAR_FIELDS)) continue; + SetFence(tile, dir, 3); dirty = true; } @@ -178,30 +161,28 @@ static void TileLoopClearAlps(TileIndex tile) { int k = GetTileZ(tile) - GetSnowLine() + 1; - if (k < 0) { + if (!IsSnowTile(tile)) { /* Below the snow line, do nothing if no snow. */ - if (!IsSnowTile(tile)) return; - } else { /* At or above the snow line, make snow tile if needed. */ - if (!IsSnowTile(tile)) { + if (k >= 0) { MakeSnow(tile); MarkTileDirtyByTile(tile); - return; } + return; } + /* Update snow density. */ uint current_density = GetClearDensity(tile); uint req_density = (k < 0) ? 0u : std::min(k, 3u); - if (current_density < req_density) { - AddClearDensity(tile, 1); - } else if (current_density > req_density) { - AddClearDensity(tile, -1); - } else { + if (current_density == req_density) { /* Density at the required level. */ if (k >= 0) return; ClearSnow(tile); + } else { + AddClearDensity(tile, current_density < req_density ? 1 : -1); } + MarkTileDirtyByTile(tile); } diff --git a/src/clear_func.h b/src/clear_func.h index 2232b56747..28fd7d4a85 100644 --- a/src/clear_func.h +++ b/src/clear_func.h @@ -13,6 +13,6 @@ #include "tile_cmd.h" void DrawHillyLandTile(const TileInfo *ti); -void DrawClearLandTile(const TileInfo *ti, byte set); +void DrawClearLandTile(const TileInfo *ti, uint8_t set); #endif /* CLEAR_FUNC_H */ diff --git a/src/command.cpp b/src/command.cpp index 1d6a530b8c..b975c51fbb 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -151,7 +151,7 @@ bool IsCommandAllowedWhilePaused(Commands cmd) CMDPL_NO_CONSTRUCTION, ///< CMDT_VEHICLE_MANAGEMENT CMDPL_NO_CONSTRUCTION, ///< CMDT_ROUTE_MANAGEMENT CMDPL_NO_CONSTRUCTION, ///< CMDT_OTHER_MANAGEMENT - CMDPL_NO_CONSTRUCTION, ///< CMDT_COMPANY_SETTING + CMDPL_NO_ACTIONS, ///< CMDT_COMPANY_SETTING CMDPL_NO_ACTIONS, ///< CMDT_SERVER_SETTING CMDPL_NO_ACTIONS, ///< CMDT_CHEAT }; diff --git a/src/command_func.h b/src/command_func.h index f0da57c65e..7a65950c6a 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -27,16 +27,6 @@ */ static const CommandCost CMD_ERROR = CommandCost(INVALID_STRING_ID); -/** - * Returns from a function with a specific StringID as error. - * - * This macro is used to return from a function. The parameter contains the - * StringID which will be returned. - * - * @param errcode The StringID to return - */ -#define return_cmd_error(errcode) return CommandCost(errcode); - void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, const CommandDataBuffer &cmd_data); bool IsValidCommand(Commands cmd); @@ -250,7 +240,7 @@ public: template static Tret Unsafe(StringID err_message, Tcallback *callback, bool my_cmd, bool estimate_only, TileIndex location, std::tuple args) { - return Execute(err_message, reinterpret_cast(callback), my_cmd, estimate_only, false, location, std::move(args)); + return Execute(err_message, reinterpret_cast(reinterpret_cast(callback)), my_cmd, estimate_only, false, location, std::move(args)); } protected: @@ -301,7 +291,7 @@ protected: /* Only set client IDs when the command does not come from the network. */ if (!network_command && GetCommandFlags() & CMD_CLIENT_ID) SetClientIds(args, std::index_sequence_for{}); - Tret res = Execute(err_message, reinterpret_cast(callback), my_cmd, estimate_only, network_command, tile, args); + Tret res = Execute(err_message, reinterpret_cast(reinterpret_cast(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) { @@ -372,7 +362,7 @@ protected: assert(AllClientIdsSet(args, std::index_sequence_for{})); } - Backup cur_company(_current_company, FILE_LINE); + Backup cur_company(_current_company); if (!InternalExecutePrepTest(cmd_flags, tile, cur_company)) { cur_company.Trash(); return MakeResult(CMD_ERROR); diff --git a/src/command_type.h b/src/command_type.h index b3ca60d901..a6186c640f 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -21,12 +21,12 @@ struct GRFFile; * a possible error message/state together. */ class CommandCost { - ExpensesType expense_type; ///< the type of expence as shown on the finances view Money cost; ///< The cost of this action StringID message; ///< Warning message for when success is unset + ExpensesType expense_type; ///< the type of expence as shown on the finances view bool success; ///< Whether the command went fine up to this moment - const GRFFile *textref_stack_grffile; ///< NewGRF providing the #TextRefStack content. - uint textref_stack_size; ///< Number of uint32_t values to put on the #TextRefStack for the error message. + const GRFFile *textref_stack_grffile = nullptr; ///< NewGRF providing the #TextRefStack content. + uint textref_stack_size = 0; ///< Number of uint32_t values to put on the #TextRefStack for the error message. StringID extra_message = INVALID_STRING_ID; ///< Additional warning message for when success is unset static uint32_t textref_stack[16]; @@ -35,25 +35,25 @@ public: /** * Creates a command cost return with no cost and no error */ - CommandCost() : expense_type(INVALID_EXPENSES), cost(0), message(INVALID_STRING_ID), success(true), textref_stack_grffile(nullptr), textref_stack_size(0) {} + CommandCost() : cost(0), message(INVALID_STRING_ID), expense_type(INVALID_EXPENSES), success(true) {} /** * Creates a command return value with one, or optionally two, error message strings. */ - explicit CommandCost(StringID msg, StringID extra_msg = INVALID_STRING_ID) : expense_type(INVALID_EXPENSES), cost(0), message(msg), success(false), textref_stack_grffile(nullptr), textref_stack_size(0), extra_message(extra_msg) {} + explicit CommandCost(StringID msg, StringID extra_msg = INVALID_STRING_ID) : cost(0), message(msg), expense_type(INVALID_EXPENSES), success(false), extra_message(extra_msg) {} /** * Creates a command cost with given expense type and start cost of 0 * @param ex_t the expense type */ - explicit CommandCost(ExpensesType ex_t) : expense_type(ex_t), cost(0), message(INVALID_STRING_ID), success(true), textref_stack_grffile(nullptr), textref_stack_size(0) {} + explicit CommandCost(ExpensesType ex_t) : cost(0), message(INVALID_STRING_ID), expense_type(ex_t), success(true) {} /** * Creates a command return value with the given start cost and expense type * @param ex_t the expense type * @param cst the initial cost of this command */ - CommandCost(ExpensesType ex_t, const Money &cst) : expense_type(ex_t), cost(cst), message(INVALID_STRING_ID), success(true), textref_stack_grffile(nullptr), textref_stack_size(0) {} + CommandCost(ExpensesType ex_t, const Money &cst) : cost(cst), message(INVALID_STRING_ID), expense_type(ex_t), success(true) {} /** @@ -207,6 +207,9 @@ enum Commands : uint16_t { CMD_RENAME_WAYPOINT, ///< rename a waypoint CMD_REMOVE_FROM_RAIL_WAYPOINT, ///< remove a (rectangle of) tiles from a rail waypoint + CMD_BUILD_ROAD_WAYPOINT, ///< build a road waypoint + CMD_REMOVE_FROM_ROAD_WAYPOINT, ///< remove a (rectangle of) tiles from a road waypoint + CMD_BUILD_ROAD_STOP, ///< build a road stop CMD_REMOVE_ROAD_STOP, ///< remove a road stop CMD_BUILD_LONG_ROAD, ///< build a complete road (not a "half" one) @@ -283,6 +286,7 @@ enum Commands : uint16_t { CMD_TOWN_SET_TEXT, ///< set the custom text of a town CMD_EXPAND_TOWN, ///< expand a town CMD_DELETE_TOWN, ///< delete a town + CMD_PLACE_HOUSE, ///< place a house CMD_ORDER_REFIT, ///< change the refit information of an order (for "goto depot" ) CMD_CLONE_ORDER, ///< clone (and share) an order @@ -294,6 +298,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_ALLOW_LIST_CTRL, ///< Used in multiplayer to add/remove a client's public key to/from 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 @@ -462,7 +467,7 @@ template struct CommandTraits; }; /** Storage buffer for serialized command data. */ -typedef std::vector CommandDataBuffer; +typedef std::vector CommandDataBuffer; /** * Define a callback function for the client, after the command is finished. diff --git a/src/company_base.h b/src/company_base.h index b29d0ddc1c..24f9051fc2 100644 --- a/src/company_base.h +++ b/src/company_base.h @@ -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 rail{}; ///< Count of company owned track bits for each rail type. + std::array 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,23 +75,27 @@ 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. - byte money_fraction; ///< Fraction of money of the company, too small to represent in #money. + uint8_t money_fraction; ///< Fraction of money of the company, too small to represent in #money. Money current_loan; ///< Amount of money borrowed from the bank. Money max_loan; ///< Max allowed amount of the loan or COMPANY_MAX_LOAN_DEFAULT. Colours colour; ///< Company colour. - byte block_preview; ///< Number of quarters that the company is not allowed to get new exclusive engine previews (see CompaniesGenStatistics). + uint8_t block_preview; ///< Number of quarters that the company is not allowed to get new exclusive engine previews (see CompaniesGenStatistics). TileIndex location_of_HQ; ///< Northern tile of HQ; #INVALID_TILE when there is none. TileIndex last_build_coordinate; ///< Coordinate of the last build thing by this company. TimerGameEconomy::Year inaugurated_year; ///< Economy year of starting the company. + TimerGameCalendar::Year inaugurated_year_calendar; ///< Calendar year of starting the company. Used to display proper Inauguration year while in wallclock mode. - byte months_of_bankruptcy; ///< Number of months that the company is unable to pay its debts + 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. Money bankrupt_value; @@ -110,7 +114,7 @@ struct CompanyProperties { std::array yearly_expenses{}; ///< Expenses of the company for the last three years. CompanyEconomyEntry cur_economy; ///< Economic data of the company of this quarter. CompanyEconomyEntry old_economy[MAX_HISTORY_QUARTERS]; ///< Economic data of the company of the last #MAX_HISTORY_QUARTERS quarters. - byte num_valid_stat_ent; ///< Number of valid statistical entries in #old_economy. + uint8_t num_valid_stat_ent; ///< Number of valid statistical entries in #old_economy. Livery livery[LS_END]; @@ -133,7 +137,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 ai_instance; class AIInfo *ai_info; std::unique_ptr ai_config; @@ -143,6 +147,7 @@ struct Company : CompanyProperties, CompanyPool::PoolItem<&_company_pool> { CompanyInfrastructure infrastructure; ///< NOSAVE: Counts of company owned infrastructure. FreeUnitIDGenerator freeunits[VEH_COMPANY_END]; + FreeUnitIDGenerator freegroups; Money GetMaxLoan() const; diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index 3ffd4e430f..7f37cc585e 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -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" @@ -146,8 +148,8 @@ void SetLocalCompany(CompanyID new_company) */ TextColour GetDrawStringCompanyColour(CompanyID company) { - if (!Company::IsValidID(company)) return (TextColour)_colour_gradient[COLOUR_WHITE][4] | TC_IS_PALETTE_COLOUR; - return (TextColour)_colour_gradient[_company_colours[company]][4] | TC_IS_PALETTE_COLOUR; + if (!Company::IsValidID(company)) return (TextColour)GetColourGradient(COLOUR_WHITE, SHADE_NORMAL) | TC_IS_PALETTE_COLOUR; + return (TextColour)GetColourGradient(_company_colours[company], SHADE_NORMAL) | TC_IS_PALETTE_COLOUR; } /** @@ -298,10 +300,10 @@ void SubtractMoneyFromCompany(const CommandCost &cost) void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost &cst) { Company *c = Company::Get(company); - byte m = c->money_fraction; + uint8_t m = c->money_fraction; Money cost = cst.GetCost(); - c->money_fraction = m - (byte)cost; + c->money_fraction = m - (uint8_t)cost; cost >>= 8; if (c->money_fraction > m) cost++; if (cost != 0) SubtractMoneyFromAnyCompany(c, CommandCost(cst.GetExpensesType(), cost)); @@ -366,7 +368,7 @@ CommandCost CheckOwnership(Owner owner, TileIndex tile) if (owner == _current_company) return CommandCost(); SetDParamsForOwnedBy(owner, tile); - return_cmd_error(STR_ERROR_OWNED_BY); + return CommandCost(STR_ERROR_OWNED_BY); } /** @@ -386,7 +388,7 @@ CommandCost CheckTileOwnership(TileIndex tile) /* no need to get the name of the owner unless we're the local company (saves some time) */ if (IsLocalCompany()) SetDParamsForOwnedBy(owner, tile); - return_cmd_error(STR_ERROR_OWNED_BY); + return CommandCost(STR_ERROR_OWNED_BY); } /** @@ -424,12 +426,12 @@ set_name:; MarkWholeScreenDirty(); if (c->is_ai) { - CompanyNewsInformation *cni = new CompanyNewsInformation(c); + auto cni = std::make_unique(c); SetDParam(0, STR_NEWS_COMPANY_LAUNCH_TITLE); SetDParam(1, STR_NEWS_COMPANY_LAUNCH_DESCRIPTION); SetDParamStr(2, cni->company_name); SetDParam(3, t->index); - AddNewsItem(STR_MESSAGE_NEWS_FORMAT, NT_COMPANY_INFO, NF_COMPANY, NR_TILE, c->last_build_coordinate.base(), NR_NONE, UINT32_MAX, cni); + AddNewsItem(STR_MESSAGE_NEWS_FORMAT, NT_COMPANY_INFO, NF_COMPANY, NR_TILE, c->last_build_coordinate.base(), NR_NONE, UINT32_MAX, std::move(cni)); } return; } @@ -447,7 +449,7 @@ bad_town_name:; } /** Sorting weights for the company colours. */ -static const byte _colour_sort[COLOUR_END] = {2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 2, 2, 3, 1, 1, 1}; +static const uint8_t _colour_sort[COLOUR_END] = {2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 2, 2, 3, 1, 1, 1}; /** Similar colours, so we can try to prevent same coloured companies. */ static const Colours _similar_colour[COLOUR_END][2] = { { COLOUR_BLUE, COLOUR_LIGHT_BLUE }, // COLOUR_DARK_BLUE @@ -477,7 +479,7 @@ static Colours GenerateCompanyColour() Colours colours[COLOUR_END]; /* Initialize array */ - for (uint i = 0; i < COLOUR_END; i++) colours[i] = (Colours)i; + for (uint i = 0; i < COLOUR_END; i++) colours[i] = static_cast(i); /* And randomize it */ for (uint i = 0; i < 100; i++) { @@ -606,6 +608,7 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY) c->avail_railtypes = GetCompanyRailTypes(c->index); c->avail_roadtypes = GetCompanyRoadTypes(c->index); c->inaugurated_year = TimerGameEconomy::year; + c->inaugurated_year_calendar = TimerGameCalendar::year; /* If starting a player company in singleplayer and a favorite company manager face is selected, choose it. Otherwise, use a random face. * In a network game, we'll choose the favorite face later in CmdCompanyCtrl to sync it to all clients. */ @@ -666,12 +669,12 @@ void InitializeCompanies() } /** - * May company \a cbig buy company \a csmall? + * Can company \a cbig buy company \a csmall without exceeding vehicle limits? * @param cbig Company buying \a csmall. * @param csmall Company getting bought. * @return Return \c true if it is allowed. */ -bool MayCompanyTakeOver(CompanyID cbig, CompanyID csmall) +bool CheckTakeoverVehicleLimit(CompanyID cbig, CompanyID csmall) { const Company *c1 = Company::Get(cbig); const Company *c2 = Company::Get(csmall); @@ -723,7 +726,7 @@ static void HandleBankruptcyTakeover(Company *c) if (c2->bankrupt_asked == 0 && // Don't ask companies going bankrupt themselves !HasBit(c->bankrupt_asked, c2->index) && best_performance < c2->old_economy[1].performance_history && - MayCompanyTakeOver(c2->index, c->index)) { + CheckTakeoverVehicleLimit(c2->index, c->index)) { best_performance = c2->old_economy[1].performance_history; best = c2; } @@ -793,7 +796,7 @@ static IntervalTimer _economy_companies_yearly({TimerGameEcono /* Move expenses to previous years. */ std::rotate(std::rbegin(c->yearly_expenses), std::rbegin(c->yearly_expenses) + 1, std::rend(c->yearly_expenses)); c->yearly_expenses[0] = {}; - SetWindowDirty(WC_FINANCES, c->index); + InvalidateWindowData(WC_FINANCES, c->index); } if (_settings_client.gui.show_finances && _local_company != COMPANY_SPECTATOR) { @@ -888,8 +891,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,12 +898,13 @@ 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. */ + /* + * If a favorite company manager face is selected, choose it. Otherwise, use a random face. + * Because this needs to be synchronised over the network, only the client knows + * its configuration and we are currently in the execution of a command, we have + * to circumvent the normal ::Post logic for commands and just send the command. + */ if (_company_manager_face != 0) Command::SendNet(STR_NULL, c->index, _company_manager_face); /* Now that we have a new company, broadcast our company settings to @@ -944,13 +946,13 @@ CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID if (!(flags & DC_EXEC)) return CommandCost(); - CompanyNewsInformation *cni = new CompanyNewsInformation(c); + auto cni = std::make_unique(c); /* Show the bankrupt news */ SetDParam(0, STR_NEWS_COMPANY_BANKRUPT_TITLE); SetDParam(1, STR_NEWS_COMPANY_BANKRUPT_DESCRIPTION); SetDParamStr(2, cni->company_name); - AddCompanyNewsItem(STR_MESSAGE_NEWS_FORMAT, cni); + AddCompanyNewsItem(STR_MESSAGE_NEWS_FORMAT, std::move(cni)); /* Remove the company */ ChangeOwnershipOfCompanyItems(c->index, INVALID_OWNER); @@ -978,6 +980,54 @@ CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID return CommandCost(); } +static bool ExecuteAllowListCtrlAction(CompanyAllowListCtrlAction action, Company *c, const std::string &public_key) +{ + switch (action) { + case CALCA_ADD: + return c->allow_list.Add(public_key); + + case CALCA_REMOVE: + return c->allow_list.Remove(public_key); + + default: + NOT_REACHED(); + } +} + +/** + * Add or remove the given public key to the allow list of this company. + * @param flags Operation to perform. + * @param action The action to perform. + * @param public_key The public key of the client to add or remove. + * @return The cost of this operation or an error. + */ +CommandCost CmdCompanyAllowListCtrl(DoCommandFlag flags, CompanyAllowListCtrlAction action, const std::string &public_key) +{ + Company *c = Company::GetIfValid(_current_company); + if (c == nullptr) return CMD_ERROR; + + /* The public key length includes the '\0'. */ + if (public_key.size() != NETWORK_PUBLIC_KEY_LENGTH - 1) return CMD_ERROR; + + switch (action) { + case CALCA_ADD: + case CALCA_REMOVE: + break; + + default: + return CMD_ERROR; + } + + if (flags & DC_EXEC) { + if (ExecuteAllowListCtrlAction(action, c, 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 @@ -1035,7 +1085,7 @@ CommandCost CmdSetCompanyColour(DoCommandFlag flags, LiveryScheme scheme, bool p if (flags & DC_EXEC) { if (primary) { - if (scheme != LS_DEFAULT) SB(c->livery[scheme].in_use, 0, 1, colour != INVALID_COLOUR); + if (scheme != LS_DEFAULT) AssignBit(c->livery[scheme].in_use, 0, colour != INVALID_COLOUR); if (colour == INVALID_COLOUR) colour = c->livery[LS_DEFAULT].colour1; c->livery[scheme].colour1 = colour; @@ -1048,7 +1098,7 @@ CommandCost CmdSetCompanyColour(DoCommandFlag flags, LiveryScheme scheme, bool p CompanyAdminUpdate(c); } } else { - if (scheme != LS_DEFAULT) SB(c->livery[scheme].in_use, 1, 1, colour != INVALID_COLOUR); + if (scheme != LS_DEFAULT) AssignBit(c->livery[scheme].in_use, 1, colour != INVALID_COLOUR); if (colour == INVALID_COLOUR) colour = c->livery[LS_DEFAULT].colour2; c->livery[scheme].colour2 = colour; @@ -1122,7 +1172,7 @@ CommandCost CmdRenameCompany(DoCommandFlag flags, const std::string &text) if (!reset) { if (Utf8StringLength(text) >= MAX_LENGTH_COMPANY_NAME_CHARS) return CMD_ERROR; - if (!IsUniqueCompanyName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE); + if (!IsUniqueCompanyName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE); } if (flags & DC_EXEC) { @@ -1165,7 +1215,7 @@ CommandCost CmdRenamePresident(DoCommandFlag flags, const std::string &text) if (!reset) { if (Utf8StringLength(text) >= MAX_LENGTH_PRESIDENT_NAME_CHARS) return CMD_ERROR; - if (!IsUniquePresidentName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE); + if (!IsUniquePresidentName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE); } if (flags & DC_EXEC) { @@ -1251,12 +1301,12 @@ CommandCost CmdGiveMoney(DoCommandFlag flags, Money money, CompanyID dest_compan CommandCost amount(EXPENSES_OTHER, std::min(money, 20000000LL)); /* You can only transfer funds that is in excess of your loan */ - if (c->money - c->current_loan < amount.GetCost() || amount.GetCost() < 0) return_cmd_error(STR_ERROR_INSUFFICIENT_FUNDS); + if (c->money - c->current_loan < amount.GetCost() || amount.GetCost() < 0) return CommandCost(STR_ERROR_INSUFFICIENT_FUNDS); if (!Company::IsValidID(dest_company)) return CMD_ERROR; if (flags & DC_EXEC) { /* Add money to company */ - Backup cur_company(_current_company, dest_company, FILE_LINE); + Backup cur_company(_current_company, dest_company); SubtractMoneyFromCompany(CommandCost(EXPENSES_OTHER, -amount.GetCost())); cur_company.Restore(); diff --git a/src/company_cmd.h b/src/company_cmd.h index b2be9a45bc..513293a995 100644 --- a/src/company_cmd.h +++ b/src/company_cmd.h @@ -15,9 +15,10 @@ #include "livery.h" enum ClientID : uint32_t; -enum Colours : byte; +enum Colours : uint8_t; CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID company_id, CompanyRemoveReason reason, ClientID client_id); +CommandCost CmdCompanyAllowListCtrl(DoCommandFlag flags, CompanyAllowListCtrlAction action, 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,10 +26,11 @@ 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_ALLOW_LIST_CTRL, CmdCompanyAllowListCtrl, 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) -DEF_CMD_TRAIT(CMD_SET_COMPANY_MANAGER_FACE, CmdSetCompanyManagerFace, 0, CMDT_OTHER_MANAGEMENT) -DEF_CMD_TRAIT(CMD_SET_COMPANY_COLOUR, CmdSetCompanyColour, 0, CMDT_OTHER_MANAGEMENT) +DEF_CMD_TRAIT(CMD_RENAME_COMPANY, CmdRenameCompany, 0, CMDT_COMPANY_SETTING) +DEF_CMD_TRAIT(CMD_RENAME_PRESIDENT, CmdRenamePresident, 0, CMDT_COMPANY_SETTING) +DEF_CMD_TRAIT(CMD_SET_COMPANY_MANAGER_FACE, CmdSetCompanyManagerFace, 0, CMDT_COMPANY_SETTING) +DEF_CMD_TRAIT(CMD_SET_COMPANY_COLOUR, CmdSetCompanyColour, 0, CMDT_COMPANY_SETTING) #endif /* COMPANY_CMD_H */ diff --git a/src/company_func.h b/src/company_func.h index 5d2d0df1dd..33885516c4 100644 --- a/src/company_func.h +++ b/src/company_func.h @@ -15,7 +15,7 @@ #include "gfx_type.h" #include "vehicle_type.h" -bool MayCompanyTakeOver(CompanyID cbig, CompanyID small); +bool CheckTakeoverVehicleLimit(CompanyID cbig, CompanyID small); void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner); static const int OWNED_BY_OWNER_IN_PARAMETERS_OFFSET = 2; ///< The index in the parameters for the owner information. void SetDParamsForOwnedBy(Owner owner, TileIndex tile); diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 8b62da9a84..46a8c00da0 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -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(), @@ -331,10 +333,13 @@ static constexpr NWidgetPart _nested_company_finances_widgets[] = { /** Window class displaying the company finances. */ struct CompanyFinancesWindow : Window { + static constexpr int NUM_PERIODS = WID_CF_EXPS_PRICE3 - WID_CF_EXPS_PRICE1 + 1; + static Money max_money; ///< The maximum amount of money a company has had this 'run' bool small; ///< Window is toggled to 'small'. + uint8_t first_visible = NUM_PERIODS - 1; ///< First visible expenses column. The last column (current) is always visible. - CompanyFinancesWindow(WindowDesc *desc, CompanyID company) : Window(desc) + CompanyFinancesWindow(WindowDesc &desc, CompanyID company) : Window(desc) { this->small = false; this->CreateNestedTree(); @@ -342,6 +347,7 @@ struct CompanyFinancesWindow : Window { this->FinishInitNested(company); this->owner = (Owner)this->window_number; + this->InvalidateData(); } void SetStringParameters(WidgetID widget) const override @@ -387,29 +393,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; } } @@ -424,12 +430,12 @@ struct CompanyFinancesWindow : Window { case WID_CF_EXPS_PRICE1: case WID_CF_EXPS_PRICE2: case WID_CF_EXPS_PRICE3: { + int period = widget - WID_CF_EXPS_PRICE1; + if (period < this->first_visible) break; + const Company *c = Company::Get((CompanyID)this->window_number); - auto age = std::min(TimerGameEconomy::year - c->inaugurated_year, TimerGameEconomy::Year(2)); - int wid_offset = widget - WID_CF_EXPS_PRICE1; - if (wid_offset <= age) { - DrawYearColumn(r, TimerGameEconomy::year - (age - wid_offset), c->yearly_expenses[(age - wid_offset).base()]); - } + const auto &expenses = c->yearly_expenses[NUM_PERIODS - period - 1]; + DrawYearColumn(r, TimerGameEconomy::year - (NUM_PERIODS - period - 1), expenses); break; } @@ -512,6 +518,24 @@ struct CompanyFinancesWindow : Window { } } + void RefreshVisibleColumns() + { + for (uint period = 0; period < this->first_visible; ++period) { + const Company *c = Company::Get((CompanyID)this->window_number); + const Expenses &expenses = c->yearly_expenses[NUM_PERIODS - period - 1]; + /* Show expenses column if it has any non-zero value in it. */ + if (std::ranges::any_of(expenses, [](const Money &value) { return value != 0; })) { + this->first_visible = period; + break; + } + } + } + + void OnInvalidateData(int, bool) override + { + this->RefreshVisibleColumns(); + } + /** * Check on a regular interval if the maximum amount of money has changed. * If it has, rescale the window to fit the new amount. @@ -529,11 +553,11 @@ struct CompanyFinancesWindow : Window { /** First conservative estimate of the maximum amount of money */ Money CompanyFinancesWindow::max_money = INT32_MAX; -static WindowDesc _company_finances_desc(__FILE__, __LINE__, +static WindowDesc _company_finances_desc( WDP_AUTO, "company_finances", 0, 0, WC_FINANCES, WC_NONE, 0, - std::begin(_nested_company_finances_widgets), std::end(_nested_company_finances_widgets) + _nested_company_finances_widgets ); /** @@ -546,29 +570,9 @@ void ShowCompanyFinances(CompanyID company) if (!Company::IsValidID(company)) return; if (BringWindowToFrontById(WC_FINANCES, company)) return; - new CompanyFinancesWindow(&_company_finances_desc, 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 +590,11 @@ static const LiveryClass _livery_class[LS_END] = { template class DropDownListColourItem : public DropDownIcon> { public: - DropDownListColourItem(int colour, bool masked) : DropDownIcon>(TSprite, GENERAL_SPRITE_COLOUR(colour % COLOUR_END), colour < COLOUR_END ? _colour_dropdown[colour] : STR_COLOUR_DEFAULT, colour, masked) + DropDownListColourItem(int colour, bool masked) : DropDownIcon>(TSprite, GENERAL_SPRITE_COLOUR(colour % COLOUR_END), colour < COLOUR_END ? (STR_COLOUR_DARK_BLUE + colour) : STR_COLOUR_DEFAULT, colour, masked) { } }; -typedef GUIList GUIGroupList; - /** Company livery colour scheme window. */ struct SelectCompanyLiveryWindow : public Window { private: @@ -602,7 +604,6 @@ private: uint rows; uint line_height; GUIGroupList groups; - std::vector indents; Scrollbar *vscroll; void ShowColourDropDownMenu(uint32_t widget) @@ -610,7 +611,7 @@ private: uint32_t used_colours = 0; const Livery *livery, *default_livery = nullptr; bool primary = widget == WID_SCL_PRI_COL_DROPDOWN; - byte default_col = 0; + uint8_t default_col = 0; /* Disallow other company colours for the primary colour */ if (this->livery_class < LC_GROUP_RAIL && HasBit(this->sel, LS_DEFAULT) && primary) { @@ -647,11 +648,11 @@ private: default_col = (primary ? default_livery->colour1 : default_livery->colour2) + COLOUR_END; list.push_back(std::make_unique>(default_col, false)); } - for (uint i = 0; i < lengthof(_colour_dropdown); i++) { - list.push_back(std::make_unique>(i, HasBit(used_colours, i))); + for (Colours colour = COLOUR_BEGIN; colour != COLOUR_END; colour++) { + list.push_back(std::make_unique>(colour, HasBit(used_colours, colour))); } - byte sel; + uint8_t sel; if (default_livery == nullptr || HasBit(livery->in_use, primary ? 0 : 1)) { sel = primary ? livery->colour1 : livery->colour2; } else { @@ -660,60 +661,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(); } @@ -734,7 +692,7 @@ private: } public: - SelectCompanyLiveryWindow(WindowDesc *desc, CompanyID company, GroupID group) : Window(desc) + SelectCompanyLiveryWindow(WindowDesc &desc, CompanyID company, GroupID group) : Window(desc) { this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_SCL_MATRIX_SCROLLBAR); @@ -774,14 +732,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 +757,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 +766,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 +782,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 +902,11 @@ public: } } } else { - uint max = static_cast(std::min(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 +949,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 +966,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 +983,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 +1039,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(); @@ -1139,18 +1100,18 @@ static constexpr NWidgetPart _nested_select_company_livery_widgets[] = { EndContainer(), }; -static WindowDesc _select_company_livery_desc(__FILE__, __LINE__, +static WindowDesc _select_company_livery_desc( WDP_AUTO, "company_color_scheme", 0, 0, WC_COMPANY_COLOUR, WC_NONE, 0, - std::begin(_nested_select_company_livery_widgets), std::end(_nested_select_company_livery_widgets) + _nested_select_company_livery_widgets ); void ShowCompanyLiveryWindow(CompanyID company, GroupID group) { SelectCompanyLiveryWindow *w = (SelectCompanyLiveryWindow *)BringWindowToFrontById(WC_COMPANY_COLOUR, company); if (w == nullptr) { - new SelectCompanyLiveryWindow(&_select_company_livery_desc, company, group); + new SelectCompanyLiveryWindow(_select_company_livery_desc, company, group); } else if (group != INVALID_GROUP) { w->SetSelectedGroup(company, group); } @@ -1209,7 +1170,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), @@ -1410,7 +1371,7 @@ class SelectCompanyManagerFaceWindow : public Window } public: - SelectCompanyManagerFaceWindow(WindowDesc *desc, Window *parent) : Window(desc) + SelectCompanyManagerFaceWindow(WindowDesc &desc, Window *parent) : Window(desc) { this->advanced = false; this->CreateNestedTree(); @@ -1465,31 +1426,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 +1463,7 @@ public: case WID_SCMF_COLLAR: case WID_SCMF_TIE_EARRING: case WID_SCMF_GLASSES: - *size = this->number_dim; + size = this->number_dim; break; } } @@ -1747,12 +1708,12 @@ public: } } - void OnQueryTextFinished(char *str) override + void OnQueryTextFinished(std::optional str) override { - if (str == nullptr) return; + if (!str.has_value()) return; /* Set a new company manager face number */ - if (!StrEmpty(str)) { - this->face = std::strtoul(str, nullptr, 10); + if (!str->empty()) { + this->face = std::strtoul(str->c_str(), nullptr, 10); ScaleAllCompanyManagerFaceBits(this->face); ShowErrorMessage(STR_FACE_FACECODE_SET, INVALID_STRING_ID, WL_INFO); this->UpdateData(); @@ -1764,11 +1725,11 @@ public: }; /** Company manager face selection window description */ -static WindowDesc _select_company_manager_face_desc(__FILE__, __LINE__, +static WindowDesc _select_company_manager_face_desc( WDP_AUTO, nullptr, 0, 0, WC_COMPANY_MANAGER_FACE, WC_NONE, WDF_CONSTRUCTION, - std::begin(_nested_select_company_manager_face_widgets), std::end(_nested_select_company_manager_face_widgets) + _nested_select_company_manager_face_widgets ); /** @@ -1781,7 +1742,7 @@ static void DoSelectCompanyManagerFace(Window *parent) if (!Company::IsValidID((CompanyID)parent->window_number)) return; if (BringWindowToFrontById(WC_COMPANY_MANAGER_FACE, parent->window_number)) return; - new SelectCompanyManagerFaceWindow(&_select_company_manager_face_desc, parent); + new SelectCompanyManagerFaceWindow(_select_company_manager_face_desc, parent); } static constexpr NWidgetPart _nested_company_infrastructure_widgets[] = { @@ -1831,7 +1792,7 @@ struct CompanyInfrastructureWindow : Window uint total_width; ///< String width of the total cost line. - CompanyInfrastructureWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) + CompanyInfrastructureWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc) { this->UpdateRailRoadTypes(); @@ -1900,7 +1861,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 +1869,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 +1890,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 +1951,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(size->height, WidgetDimensions::scaled.vsep_normal + GetCharacterHeight(FS_NORMAL)) : 0; + size.height = _settings_game.economy.infrastructure_maintenance ? std::max(size.height, WidgetDimensions::scaled.vsep_normal + GetCharacterHeight(FS_NORMAL)) : 0; } break; } @@ -2141,11 +2102,11 @@ struct CompanyInfrastructureWindow : Window } }; -static WindowDesc _company_infrastructure_desc(__FILE__, __LINE__, +static WindowDesc _company_infrastructure_desc( WDP_AUTO, "company_infrastructure", 0, 0, WC_COMPANY_INFRASTRUCTURE, WC_NONE, 0, - std::begin(_nested_company_infrastructure_widgets), std::end(_nested_company_infrastructure_widgets) + _nested_company_infrastructure_widgets ); /** @@ -2155,7 +2116,7 @@ static WindowDesc _company_infrastructure_desc(__FILE__, __LINE__, static void ShowCompanyInfrastructure(CompanyID company) { if (!Company::IsValidID(company)) return; - AllocateWindowDescFront(&_company_infrastructure_desc, company); + AllocateWindowDescFront(_company_infrastructure_desc, company); } static constexpr NWidgetPart _nested_company_widgets[] = { @@ -2174,7 +2135,7 @@ static constexpr NWidgetPart _nested_company_widgets[] = { NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), - NWidget(WWT_TEXT, COLOUR_GREY, WID_C_DESC_INAUGURATION), SetDataTip(STR_COMPANY_VIEW_INAUGURATED_TITLE, STR_NULL), SetFill(1, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_C_DESC_INAUGURATION), SetDataTip(STR_JUST_STRING2, STR_NULL), SetFill(1, 0), NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), NWidget(WWT_LABEL, COLOUR_GREY, WID_C_DESC_COLOUR_SCHEME), SetDataTip(STR_COMPANY_VIEW_COLOUR_SCHEME_TITLE, STR_NULL), NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_COLOUR_SCHEME_EXAMPLE), SetMinimalSize(30, 0), SetFill(1, 1), @@ -2208,9 +2169,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 +2177,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 +2209,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 @@ -2265,7 +2218,7 @@ struct CompanyWindow : Window CWP_RELOCATE_HIDE, ///< Hide the relocate HQ button. }; - CompanyWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) + CompanyWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc) { this->InitNested(window_number); this->owner = (Owner)this->window_number; @@ -2296,7 +2249,7 @@ struct CompanyWindow : Window reinit |= this->GetWidget(WID_C_SELECT_HOSTILE_TAKEOVER)->SetDisplayedPlane((local || _local_company == COMPANY_SPECTATOR || !c->is_ai || _networking) ? SZSP_NONE : 0); /* Multiplayer buttons. */ - reinit |= this->GetWidget(WID_C_SELECT_MULTIPLAYER)->SetDisplayedPlane((!_networking) ? (int)SZSP_NONE : (int)(local ? CWP_MP_C_PWD : CWP_MP_C_JOIN)); + reinit |= this->GetWidget(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 +2262,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 +2274,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 +2307,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 +2344,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 +2411,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; } } @@ -2483,7 +2423,14 @@ struct CompanyWindow : Window break; case WID_C_DESC_INAUGURATION: - SetDParam(0, Company::Get((CompanyID)this->window_number)->inaugurated_year); + if (TimerGameEconomy::UsingWallclockUnits()) { + SetDParam(0, STR_COMPANY_VIEW_INAUGURATED_TITLE_WALLCLOCK); + SetDParam(1, Company::Get(static_cast(this->window_number))->inaugurated_year_calendar); + SetDParam(2, Company::Get(static_cast(this->window_number))->inaugurated_year); + } else { + SetDParam(0, STR_COMPANY_VIEW_INAUGURATED_TITLE); + SetDParam(1, Company::Get(static_cast(this->window_number))->inaugurated_year); + } break; case WID_C_DESC_COMPANY_VALUE: @@ -2532,7 +2479,7 @@ struct CompanyWindow : Window } case WID_C_BUILD_HQ: - if ((byte)this->window_number != _local_company) return; + if ((uint8_t)this->window_number != _local_company) return; if (this->IsWidgetLowered(WID_C_BUILD_HQ)) { ResetObjectToPlace(); this->RaiseButtons(); @@ -2569,19 +2516,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,29 +2549,25 @@ struct CompanyWindow : Window this->RaiseButtons(); } - void OnQueryTextFinished(char *str) override + void OnQueryTextFinished(std::optional str) override { - if (str == nullptr) return; + if (!str.has_value()) return; switch (this->query_widget) { default: NOT_REACHED(); case WID_C_GIVE_MONEY: { - Money money = std::strtoull(str, nullptr, 10) / _currency->rate; + Money money = std::strtoull(str->c_str(), nullptr, 10) / GetCurrency().rate; Command::Post(STR_ERROR_CAN_T_GIVE_MONEY, money, (CompanyID)this->window_number); break; } case WID_C_PRESIDENT_NAME: - Command::Post(STR_ERROR_CAN_T_CHANGE_PRESIDENT, str); + Command::Post(STR_ERROR_CAN_T_CHANGE_PRESIDENT, *str); break; case WID_C_COMPANY_NAME: - Command::Post(STR_ERROR_CAN_T_CHANGE_COMPANY_NAME, str); - break; - - case WID_C_COMPANY_JOIN: - NetworkClientRequestMove((CompanyID)this->window_number, str); + Command::Post(STR_ERROR_CAN_T_CHANGE_COMPANY_NAME, *str); break; } } @@ -2645,11 +2581,11 @@ struct CompanyWindow : Window } }; -static WindowDesc _company_desc(__FILE__, __LINE__, +static WindowDesc _company_desc( WDP_AUTO, "company", 0, 0, WC_COMPANY, WC_NONE, 0, - std::begin(_nested_company_widgets), std::end(_nested_company_widgets) + _nested_company_widgets ); /** @@ -2660,7 +2596,7 @@ void ShowCompany(CompanyID company) { if (!Company::IsValidID(company)) return; - AllocateWindowDescFront(&_company_desc, company); + AllocateWindowDescFront(_company_desc, company); } /** @@ -2674,7 +2610,7 @@ void DirtyCompanyInfrastructureWindows(CompanyID company) } struct BuyCompanyWindow : Window { - BuyCompanyWindow(WindowDesc *desc, WindowNumber window_number, bool hostile_takeover) : Window(desc), hostile_takeover(hostile_takeover) + BuyCompanyWindow(WindowDesc &desc, WindowNumber window_number, bool hostile_takeover) : Window(desc), hostile_takeover(hostile_takeover) { this->InitNested(window_number); @@ -2682,18 +2618,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; } } @@ -2779,11 +2715,11 @@ static constexpr NWidgetPart _nested_buy_company_widgets[] = { EndContainer(), }; -static WindowDesc _buy_company_desc(__FILE__, __LINE__, +static WindowDesc _buy_company_desc( WDP_AUTO, nullptr, 0, 0, WC_BUY_COMPANY, WC_NONE, WDF_CONSTRUCTION, - std::begin(_nested_buy_company_widgets), std::end(_nested_buy_company_widgets) + _nested_buy_company_widgets ); /** @@ -2795,6 +2731,6 @@ void ShowBuyCompanyDialog(CompanyID company, bool hostile_takeover) { auto window = BringWindowToFrontById(WC_BUY_COMPANY, company); if (window == nullptr) { - new BuyCompanyWindow(&_buy_company_desc, company, hostile_takeover); + new BuyCompanyWindow(_buy_company_desc, company, hostile_takeover); } } diff --git a/src/company_manager_face.h b/src/company_manager_face.h index 113a4c728b..dbb11de5fd 100644 --- a/src/company_manager_face.h +++ b/src/company_manager_face.h @@ -54,9 +54,9 @@ DECLARE_POSTFIX_INCREMENT(CompanyManagerFaceVariable) /** Information about the valid values of CompanyManagerFace bitgroups as well as the sprites to draw */ struct CompanyManagerFaceBitsInfo { - byte offset; ///< Offset in bits into the CompanyManagerFace - byte length; ///< Number of bits used in the CompanyManagerFace - byte valid_values[GE_END]; ///< The number of valid values per gender/ethnicity + uint8_t offset; ///< Offset in bits into the CompanyManagerFace + uint8_t length; ///< Number of bits used in the CompanyManagerFace + uint8_t valid_values[GE_END]; ///< The number of valid values per gender/ethnicity SpriteID first_sprite[GE_END]; ///< The first sprite per gender/ethnicity }; diff --git a/src/company_type.h b/src/company_type.h index ff22a4d0dc..355bde8630 100644 --- a/src/company_type.h +++ b/src/company_type.h @@ -15,7 +15,7 @@ /** * Enum for all companies/owners. */ -enum Owner : byte { +enum Owner : uint8_t { /* All companies below MAX_COMPANIES are playable * companies, above, they are special, computer controlled 'companies' */ OWNER_BEGIN = 0x00, ///< First owner @@ -72,4 +72,12 @@ enum CompanyCtrlAction : uint8_t { CCA_END, ///< Sentinel for end. }; +/** The action to do with CMD_COMPANY_ALLOW_LIST_CTRL. */ +enum CompanyAllowListCtrlAction : uint8_t { + CALCA_ADD, ///< Create a public key. + CALCA_REMOVE, ///< Remove a public key. + + CALCA_END, ///< Sentinel for end. +}; + #endif /* COMPANY_TYPE_H */ diff --git a/src/console.cpp b/src/console.cpp index 9781283095..fa23eacf14 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -34,11 +34,11 @@ static const uint ICON_MAX_RECURSE = 10; ///< Maximum number of recursion return aliases; } -FILE *_iconsole_output_file; +std::optional _iconsole_output_file; void IConsoleInit() { - _iconsole_output_file = nullptr; + _iconsole_output_file = std::nullopt; _redirect_console_to_client = INVALID_CLIENT_ID; _redirect_console_to_admin = INVALID_ADMIN_ID; @@ -49,13 +49,12 @@ void IConsoleInit() static void IConsoleWriteToLogFile(const std::string &string) { - if (_iconsole_output_file != nullptr) { + if (_iconsole_output_file.has_value()) { /* if there is an console output file ... also print it there */ try { - fmt::print(_iconsole_output_file, "{}{}\n", GetLogPrefix(), string); + fmt::print(*_iconsole_output_file, "{}{}\n", GetLogPrefix(), string); } catch (const std::system_error &) { - fclose(_iconsole_output_file); - _iconsole_output_file = nullptr; + _iconsole_output_file.reset(); IConsolePrint(CC_ERROR, "Cannot write to console log file; closing the log file."); } } @@ -63,10 +62,9 @@ static void IConsoleWriteToLogFile(const std::string &string) bool CloseConsoleLogIfActive() { - if (_iconsole_output_file != nullptr) { + if (_iconsole_output_file.has_value()) { IConsolePrint(CC_INFO, "Console log file closed."); - fclose(_iconsole_output_file); - _iconsole_output_file = nullptr; + _iconsole_output_file.reset(); return true; } @@ -206,7 +204,7 @@ static std::string RemoveUnderscores(std::string name) * @param tokencount the number of parameters passed * @param *tokens are the parameters given to the original command (0 is the first param) */ -static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT], const uint recurse_count) +static void IConsoleAliasExec(const IConsoleAlias *alias, uint8_t tokencount, char *tokens[ICON_TOKEN_COUNT], const uint recurse_count) { std::string alias_buffer; diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 737cdd244a..296bbd019b 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -29,7 +29,7 @@ #include "strings_func.h" #include "viewport_func.h" #include "window_func.h" -#include "timer/timer_game_calendar.h" +#include "timer/timer.h" #include "company_func.h" #include "gamelog.h" #include "ai/ai.hpp" @@ -53,6 +53,24 @@ /* scriptfile handling */ static uint _script_current_depth; ///< Depth of scripts running (used to abort execution when #ConReturn is encountered). +/* Scheduled execution handling. */ +static std::string _scheduled_monthly_script; ///< Script scheduled to execute by the 'schedule' console command (empty if no script is scheduled). + +/** Timer that runs every month of game time for the 'schedule' console command. */ +static IntervalTimer _scheduled_monthly_timer = {{TimerGameCalendar::MONTH, TimerGameCalendar::Priority::NONE}, [](auto) { + if (_scheduled_monthly_script.empty()) { + return; + } + + /* Clear the schedule before rather than after the script to allow the script to itself call + * schedule without it getting immediately cleared. */ + const std::string filename = _scheduled_monthly_script; + _scheduled_monthly_script.clear(); + + IConsolePrint(CC_DEFAULT, "Executing scheduled script file '{}'...", filename); + IConsoleCmdExec(std::string("exec") + " " + filename); +}}; + /** File list storage for the console, for caching the last 'ls' command. */ class ConsoleFileList : public FileList { public: @@ -89,7 +107,7 @@ static ConsoleFileList _console_file_list_scenario{FT_SCENARIO, false}; ///< Fil static ConsoleFileList _console_file_list_heightmap{FT_HEIGHTMAP, false}; ///< File storage cache for heightmaps. /* console command defines */ -#define DEF_CONSOLE_CMD(function) static bool function([[maybe_unused]] byte argc, [[maybe_unused]] char *argv[]) +#define DEF_CONSOLE_CMD(function) static bool function([[maybe_unused]] uint8_t argc, [[maybe_unused]] char *argv[]) #define DEF_CONSOLE_HOOK(function) static ConsoleHookResult function(bool echo) @@ -523,7 +541,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 +854,7 @@ DEF_CONSOLE_CMD(ConRcon) if (argc == 0) { IConsolePrint(CC_HELP, "Remote control the server from another client. Usage: 'rcon '."); 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,7 +932,7 @@ DEF_CONSOLE_CMD(ConClientNickChange) DEF_CONSOLE_CMD(ConJoinCompany) { if (argc < 2) { - IConsolePrint(CC_HELP, "Request joining another company. Usage: 'join []'."); + IConsolePrint(CC_HELP, "Request joining another company. Usage: 'join '."); IConsolePrint(CC_HELP, "For valid company-id see company list, use 255 for spectator."); return true; } @@ -942,9 +961,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; } @@ -952,7 +970,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; @@ -1112,15 +1130,14 @@ DEF_CONSOLE_CMD(ConExec) if (argc < 2) return false; - FILE *script_file = FioFOpenFile(argv[1], "r", BASE_DIR); + auto script_file = FioFOpenFile(argv[1], "r", BASE_DIR); - if (script_file == nullptr) { + if (!script_file.has_value()) { if (argc == 2 || atoi(argv[2]) != 0) IConsolePrint(CC_ERROR, "Script file '{}' not found.", argv[1]); return true; } if (_script_current_depth == 11) { - FioFCloseFile(script_file); IConsolePrint(CC_ERROR, "Maximum 'exec' depth reached; script A is calling script B is calling script C ... more than 10 times."); return true; } @@ -1129,7 +1146,7 @@ DEF_CONSOLE_CMD(ConExec) uint script_depth = _script_current_depth; char cmdline[ICON_CMDLN_SIZE]; - while (fgets(cmdline, sizeof(cmdline), script_file) != nullptr) { + while (fgets(cmdline, sizeof(cmdline), *script_file) != nullptr) { /* Remove newline characters from the executing script */ for (char *cmdptr = cmdline; *cmdptr != '\0'; cmdptr++) { if (*cmdptr == '\n' || *cmdptr == '\r') { @@ -1145,12 +1162,40 @@ DEF_CONSOLE_CMD(ConExec) if (_script_current_depth == script_depth - 1) break; } - if (ferror(script_file)) { + if (ferror(*script_file) != 0) { IConsolePrint(CC_ERROR, "Encountered error while trying to read from script file '{}'.", argv[1]); } if (_script_current_depth == script_depth) _script_current_depth--; - FioFCloseFile(script_file); + return true; +} + +DEF_CONSOLE_CMD(ConSchedule) +{ + if (argc < 3 || std::string_view(argv[1]) != "on-next-calendar-month") { + IConsolePrint(CC_HELP, "Schedule a local script to execute later. Usage: 'schedule on-next-calendar-month