diff --git a/.clang-format b/.clang-format index c29e19d6a3..ebb3421b67 100644 --- a/.clang-format +++ b/.clang-format @@ -54,7 +54,6 @@ ForEachMacros: - FOR_ALL_PEEPS - FOR_ALL_GUESTS - FOR_ALL_STAFF - - FOR_ALL_RIDES IncludeBlocks: 'Regroup' IncludeCategories: - Regex: '^"' diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 1626ca3408..258c68dde8 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ Fill in the placeholders below. Delete any headings and placeholders that you do not fill in. --> **OS:** [e.g. Windows 10] -**Version:** [e.g. 0.2.2] +**Version:** [e.g. 0.2.3] **Commit/Build:** [e.g. 426e106] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..4b328a853e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: CI +on: [push] +jobs: + build-appimage: + name: Build AppImage + runs-on: ubuntu-latest + container: + image: openrct2/openrct2:ubuntu_amd64 + steps: + - uses: actions/checkout@v1 + - name: Get pre-reqs + run: | + git clone https://github.com/discordapp/discord-rpc -b v3.4.0 + git clone https://github.com/janisozaur/rapidjson discord-rpc/thirdparty/rapidjson -b patch-1 + mkdir bin && cd bin + apt-get update + apt-get install -y wget libcairo2 + wget https://github.com/TheAssassin/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + chmod +x linuxdeploy*.AppImage + - name: Build OpenRCT2 + working-directory: bin + run: | + cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr + ninja -v + DESTDIR=AppDir ninja install + - name: Build AppImage + working-directory: bin + run: | + ./linuxdeploy*.AppImage --appimage-extract-and-run --appdir AppDir/ --output appimage --desktop-file AppDir/usr/share/applications/openrct2.desktop + mkdir artifacts + mv OpenRCT2*.AppImage artifacts + - uses: actions/upload-artifact@master + with: + name: OpenRCT2-AppImage + path: bin/artifacts diff --git a/.travis.yml b/.travis.yml index 92b8ca0dab..009b814e1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ sudo: required dist: trusty env: global: - - OPENRCT2_VERSION="0.2.2" + - OPENRCT2_VERSION="0.2.3" # CURL timeouts in [seconds] (https://ec.haxx.se/usingcurl-timeouts.html) - CURL_MAX_TIME=300 - CURL_CONNECT_TIMEOUT=5 @@ -27,12 +27,12 @@ env: matrix: include: - os: linux - name: Ubuntu amd64 GCC RelWithDebInfo + name: Ubuntu amd64 GCC MinSizeRel if: type != cron services: - docker env: - - OPENRCT2_CMAKE_OPTS="-G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=OpenRCT2 -DPORTABLE=ON -DCMAKE_CXX_FLAGS=\"-gz\"" TARGET=ubuntu_amd64 + - OPENRCT2_CMAKE_OPTS="-G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=OpenRCT2 -DPORTABLE=ON -DCMAKE_CXX_FLAGS=\"-g -gz\"" TARGET=ubuntu_amd64 - secure: "S3u2VCE2Vy8KNXoeh+DhnzjCmgTX0r95uEZrXDU+IKANOOCKn7Dg4OFDZE3LY/i1y2/EUDpnR5yLC38Ks795EUP/sv/OoMl4tjQ20yERjqWh+gcIRrgx7SdVabuAh3t4aBdaLD4Pfnj5avxeCt6rL7yGnj0wdbrbJSBZPsgSnuQ=" after_success: - sudo chown -R $USER build @@ -41,13 +41,14 @@ matrix: - mv OpenRCT2/bin/openrct2 OpenRCT2/ && mv OpenRCT2/bin/openrct2-cli OpenRCT2/ && mv OpenRCT2/bin/libopenrct2.so OpenRCT2/ && mv OpenRCT2/share/openrct2 OpenRCT2/data && mv OpenRCT2/share/doc/openrct2 OpenRCT2/doc && mv OpenRCT2/bin/libdiscord-rpc.so OpenRCT2/ - rm -rf OpenRCT2/bin OpenRCT2/share # remove empty dirs - tar cvzf openrct2-linux.tar.gz OpenRCT2/ + - ls -lR - if [[ "z${TRAVIS_TAG}" != "z" ]] ; then export PUSH_BRANCH=master ; else export PUSH_BRANCH=$TRAVIS_BRANCH ; export FILENAME_PART=-${TRAVIS_BRANCH}-$(git rev-parse --short HEAD) ; fi + - curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT --upload-file openrct2-linux.tar.gz https://transfer.sh/openrct2-linux-x86_64.tar.gz -o link && cat link && echo || echo "Failed transfer.sh upload" - if [[ "z$OPENRCT2_ORG_TOKEN" != "z" && "$TRAVIS_PULL_REQUEST" == "false" && ("${TRAVIS_BRANCH}" =~ ^(develop|push/) || "z${TRAVIS_TAG}" != "z") ]] ; then curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT -o - -v --form "key=$OPENRCT2_ORG_TOKEN" --form "fileName=OpenRCT2-${OPENRCT2_VERSION}${FILENAME_PART}-linux-x86_64.tar.gz" --form "version=${OPENRCT2_VERSION}" --form "gitHash=$TRAVIS_COMMIT" --form "gitBranch=$PUSH_BRANCH" --form "flavourId=9" --form "file=@openrct2-linux.tar.gz" "https://openrct2.org/altapi/?command=push-build"; - else curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT --upload-file openrct2-linux.tar.gz https://transfer.sh/openrct2-linux-x86_64.tar.gz -o link && cat link || if [[ $? ]] ; then echo "Failed transfer.sh upload" ; fi; fi - os: linux name: Ubuntu i686 GCC Debug m32 no-build-shared-libs @@ -65,13 +66,14 @@ matrix: - mv OpenRCT2/bin/openrct2 OpenRCT2/ && mv OpenRCT2/share/openrct2 OpenRCT2/data && mv OpenRCT2/share/doc/openrct2 OpenRCT2/doc - rm -rf OpenRCT2/bin OpenRCT2/share # remove empty dirs - tar cvzf openrct2-linux.tar.gz OpenRCT2/ + - ls -lR - if [[ "z${TRAVIS_TAG}" != "z" ]] ; then export PUSH_BRANCH=master ; else export PUSH_BRANCH=$TRAVIS_BRANCH ; export FILENAME_PART=-${TRAVIS_BRANCH}-$(git rev-parse --short HEAD) ; fi + - curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT --upload-file openrct2-linux.tar.gz https://transfer.sh/openrct2-linux-i686.tar.gz -o link && cat link && echo || echo "Failed transfer.sh upload" - if [[ "z$OPENRCT2_ORG_TOKEN" != "z" && "$TRAVIS_PULL_REQUEST" == "false" && ("${TRAVIS_BRANCH}" =~ ^(develop|push/) || "z${TRAVIS_TAG}" != "z") ]] ; then curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT -o - -v --form "key=$OPENRCT2_ORG_TOKEN" --form "fileName=OpenRCT2-${OPENRCT2_VERSION}${FILENAME_PART}-linux-i686.tar.gz" --form "version=${OPENRCT2_VERSION}" --form "gitHash=$TRAVIS_COMMIT" --form "gitBranch=$PUSH_BRANCH" --form "flavourId=4" --form "file=@openrct2-linux.tar.gz" "https://openrct2.org/altapi/?command=push-build"; - else curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT --upload-file openrct2-linux.tar.gz https://transfer.sh/openrct2-linux-i686.tar.gz -o link && cat link || if [[ $? ]] ; then echo "Failed transfer.sh upload" ; fi; fi - os: linux name: Ubuntu amd64 Clang @@ -114,9 +116,9 @@ matrix: export PUSH_BRANCH=master ; else export PUSH_BRANCH=$TRAVIS_BRANCH ; export FILENAME_PART=-${TRAVIS_BRANCH}-$(git rev-parse --short HEAD) ; fi + - curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT --upload-file openrct2-macos.zip https://transfer.sh/openrct2-macos.zip -o link && cat link && echo || echo "Failed transfer.sh upload" - if [[ "z$OPENRCT2_ORG_TOKEN" != "z" && "$TRAVIS_PULL_REQUEST" == "false" && ("${TRAVIS_BRANCH}" =~ ^(develop|push/) || "z${TRAVIS_TAG}" != "z") ]] ; then curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT -o - -v --form "key=$OPENRCT2_ORG_TOKEN" --form "fileName=OpenRCT2-${OPENRCT2_VERSION}${FILENAME_PART}-macos.zip" --form "version=${OPENRCT2_VERSION}" --form "gitHash=$TRAVIS_COMMIT" --form "gitBranch=$PUSH_BRANCH" --form "flavourId=3" --form "file=@openrct2-macos.zip" "https://openrct2.org/altapi/?command=push-build"; - else curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT --upload-file openrct2-macos.zip https://transfer.sh/openrct2-macos.zip -o link && cat link || if [[ $? ]] ; then echo "Failed transfer.sh upload" ; fi; fi - os: linux name: Android @@ -162,8 +164,8 @@ matrix: after_success: # Only run Android jobs when triggered from cron or on tag, otherwise skip - if [[ "$OPENRCT2_ANDROID" != "true" ]] && [[ "z${TRAVIS_TAG}" == "z" ]] ; then exit 0 ; fi - - curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT --upload-file app/build/outputs/apk/arm/pr/app-arm-pr.apk https://transfer.sh/openrct2-android-arm.apk -o link && cat link || if [[ $? ]] ; then echo "Failed transfer.sh upload" ; fi; - - curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT --upload-file app/build/outputs/apk/x86/pr/app-x86-pr.apk https://transfer.sh/openrct2-android-x86.apk -o link && cat link || if [[ $? ]] ; then echo "Failed transfer.sh upload" ; fi; + - curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT --upload-file app/build/outputs/apk/arm/pr/app-arm-pr.apk https://transfer.sh/openrct2-android-arm.apk -o link && cat link && echo|| if [[ $? ]] ; then echo "Failed transfer.sh upload" ; fi; + - curl -m $CURL_MAX_TIME --connect-timeout $CURL_CONNECT_TIMEOUT --upload-file app/build/outputs/apk/x86/pr/app-x86-pr.apk https://transfer.sh/openrct2-android-x86.apk -o link && cat link && echo || if [[ $? ]] ; then echo "Failed transfer.sh upload" ; fi; - if [[ "z${TRAVIS_TAG}" != "z" ]] ; then export PUSH_BRANCH=master ; else export PUSH_BRANCH=$TRAVIS_BRANCH ; export FILENAME_PART=-${TRAVIS_BRANCH}-$(git rev-parse --short HEAD) ; diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 807a3b4217..b5a05960bb 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -32,7 +32,8 @@ "/usr/include", "/usr/local/include", "${workspaceRoot}", - "${workspaceRoot}/src" + "${workspaceRoot}/src", + "${workspaceRoot}/bin/googletest-distribution-prefix/src/googletest-distribution/googletest/include/gtest" ], "defines": [], "intelliSenseMode": "clang-x64", diff --git a/.vscode/launch.json b/.vscode/launch.json index d3df614e53..70b2a2c016 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,7 @@ "stopAtEntry": false, "cwd": "${workspaceFolder}/bin", "environment": [], - "externalConsole": true, + "externalConsole": false, "setupCommands": [ { "description": "Enable pretty-printing for gdb", diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ad80fb1b4..ea4bdfd822 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ endif() project(openrct2 CXX) include(cmake/platform.cmake) +include(CMakeDependentOption) if (NOT MSVC) include(FindPkgConfig) @@ -20,17 +21,22 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}") set(CMAKE_MACOSX_RPATH 1) -set(TITLE_SEQUENCE_URL "https://github.com/OpenRCT2/title-sequences/releases/download/v0.1.2/title-sequence-v0.1.2.zip") -set(TITLE_SEQUENCE_SHA1 "1136ef92bfb05cd1cba9831ba6dc4a653d87a246") +set(TITLE_SEQUENCE_URL "https://github.com/OpenRCT2/title-sequences/releases/download/v0.1.2c/title-sequences.zip") +set(TITLE_SEQUENCE_SHA1 "304d13a126c15bf2c86ff13b81a2f2cc1856ac8d") -set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v1.0.9/objects.zip") -set(OBJECTS_SHA1 "be0bcb454505e4f7c56d21d6804f81faf8a0a652") +set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v1.0.12/objects.zip") +set(OBJECTS_SHA1 "56b5d22ed7da0afa750b3dcb5ac22de61e3597c2") + +set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v0.0.4/replays.zip") +set(REPLAYS_SHA1 "6584368CD04EC42FDC2EB5DF26FECE9A964C27B7") option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.") option(WITH_TESTS "Build tests") option(PORTABLE "Create a portable build (-rpath=$ORIGIN)" OFF) option(DOWNLOAD_TITLE_SEQUENCES "Download title sequences during installation." ON) option(DOWNLOAD_OBJECTS "Download objects during installation." ON) +CMAKE_DEPENDENT_OPTION(DOWNLOAD_REPLAYS "Download replays during installation." ON + "WITH_TESTS" OFF) # Options option(STATIC "Create a static build.") @@ -175,8 +181,10 @@ if (MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_SCL_SECURE_NO_WARNINGS) - add_definitions(-D__SSE4_1__) - add_definitions(-D__AVX2__) + if ((CMAKE_VS_PLATFORM_NAME STREQUAL "Win32") OR (CMAKE_VS_PLATFORM_NAME STREQUAL "x64")) + add_definitions(-D__SSE4_1__) + add_definitions(-D__AVX2__) + endif () add_definitions(-DNOMINMAX) else () ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_NULL_DEREFERENCE -Wnull-dereference) @@ -287,6 +295,16 @@ if (DOWNLOAD_OBJECTS) file(REMOVE \$ENV{DESTDIR}/${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT}/object/objects.zip)\n\ endif ()") endif () +if (DOWNLOAD_REPLAYS) + install(CODE + "if (EXISTS \${CMAKE_CURRENT_BINARY_DIR}/testdata/replays/)\n\ + message(\"Using cached replays\")\n\ + else () \n\ + file(DOWNLOAD ${REPLAYS_URL} \${CMAKE_CURRENT_BINARY_DIR}/testdata/replays/replays.zip EXPECTED_HASH SHA1=${REPLAYS_SHA1} SHOW_PROGRESS)\n\ + execute_process(COMMAND \"${CMAKE_COMMAND}\" -E chdir \${CMAKE_CURRENT_BINARY_DIR}/testdata/replays/ \"${CMAKE_COMMAND}\" -E tar xf replays.zip)\n\ + file(REMOVE \${CMAKE_CURRENT_BINARY_DIR}/testdata/replays/replays.zip)\n\ + endif ()") +endif () install(TARGETS "libopenrct2" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") if(NOT DISABLE_GUI) diff --git a/OpenRCT2.entitlements b/OpenRCT2.entitlements new file mode 100644 index 0000000000..0c67376eba --- /dev/null +++ b/OpenRCT2.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 5da21932d7..ab503eb36d 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -21,30 +21,27 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 01C6F0C222FD519E0057E2F7 /* TrackImporter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 01C6F0C022FD519E0057E2F7 /* TrackImporter.cpp */; }; + 01C6F0C422FD51B70057E2F7 /* T4Importer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 01C6F0C322FD51B70057E2F7 /* T4Importer.cpp */; }; + 01C6F0C822FD51FC0057E2F7 /* T6Exporter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 01C6F0C522FD51FC0057E2F7 /* T6Exporter.cpp */; }; + 01C6F0C922FD51FC0057E2F7 /* T6Importer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 01C6F0C622FD51FC0057E2F7 /* T6Importer.cpp */; }; + 01DDFE6522FD608500221318 /* Window_internal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 01DDFE6422FD608500221318 /* Window_internal.cpp */; }; 2A1F4FE0221FF4B0003CA045 /* Twitch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C840F1EC4E7CC00FA49E2 /* Twitch.cpp */; }; 2A1F4FE1221FF4B0003CA045 /* Audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C83571EC4E7CC00FA49E2 /* Audio.cpp */; }; 2A1F4FE2221FF4B0003CA045 /* macos.mm in Sources */ = {isa = PBXBuildFile; fileRef = F76C845D1EC4E7CC00FA49E2 /* macos.mm */; }; - 2A43D2BA2225B8D900E8F73B /* RideSetVehiclesAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A43D2B72225B8D900E8F73B /* RideSetVehiclesAction.hpp */; }; - 2A43D2BB2225B8D900E8F73B /* SmallSceneryPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A43D2B82225B8D900E8F73B /* SmallSceneryPlaceAction.hpp */; }; - 2A43D2BC2225B8D900E8F73B /* LoadOrQuitAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A43D2B92225B8D900E8F73B /* LoadOrQuitAction.hpp */; }; - 2A43D2C02225B91A00E8F73B /* RideSetVehiclesAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A43D2BD2225B91A00E8F73B /* RideSetVehiclesAction.hpp */; }; - 2A43D2C12225B91A00E8F73B /* RideEntranceExitRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A43D2BE2225B91A00E8F73B /* RideEntranceExitRemoveAction.hpp */; }; - 2A43D2C22225B91A00E8F73B /* LoadOrQuitAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A43D2BF2225B91A00E8F73B /* LoadOrQuitAction.hpp */; }; 2A5354E922099C4F00A5440F /* Network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A5354E822099C4F00A5440F /* Network.cpp */; }; - 2A5C1368221E9F9000F8C245 /* TrackRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */; }; - 2A61CAFB2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */; }; - 2A61CAF92229E59F0095AD67 /* WaterSetHeightAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */; }; - 2A61CAF52229E5720095AD67 /* FootpathSceneryPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAF22229E5720095AD67 /* FootpathSceneryPlaceAction.hpp */; }; - 2A61CAF62229E5720095AD67 /* FootpathSceneryRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAF32229E5720095AD67 /* FootpathSceneryRemoveAction.hpp */; }; - 2A61CAF72229E5720095AD67 /* FootpathPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAF42229E5720095AD67 /* FootpathPlaceAction.hpp */; }; - 2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */; }; - 2AA050332209A8E300D3A922 /* StaffSetOrdersAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */; }; - 2AAFD7FA220DD2DC002461A4 /* TrackPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AAFD7F9220DD2DC002461A4 /* TrackPlaceAction.hpp */; }; - 2AAFD7FC220DD336002461A4 /* RideSetPriceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AAFD7FB220DD336002461A4 /* RideSetPriceAction.hpp */; }; - 2AAFD7FE220DD374002461A4 /* PauseToggleAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AAFD7FD220DD374002461A4 /* PauseToggleAction.hpp */; }; - 2AAFD800220DD3D2002461A4 /* LandSetHeightAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AAFD7FF220DD3D2002461A4 /* LandSetHeightAction.hpp */; }; - 2ACBAB172226850A0034FB91 /* RideSetSetting.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ACBAB162226850A0034FB91 /* RideSetSetting.hpp */; }; - 2AF7893D220B253E0072754A /* RideSetAppearanceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */; }; + 2ADE2F27224418B2002598AF /* Random.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F21224418B1002598AF /* Random.hpp */; }; + 2ADE2F28224418B2002598AF /* DataSerialiserTag.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F22224418B1002598AF /* DataSerialiserTag.h */; }; + 2ADE2F29224418B2002598AF /* Numerics.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F23224418B1002598AF /* Numerics.hpp */; }; + 2ADE2F2A224418B2002598AF /* Meta.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F24224418B2002598AF /* Meta.hpp */; }; + 2ADE2F2B224418B2002598AF /* JobPool.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F25224418B2002598AF /* JobPool.hpp */; }; + 2ADE2F2C224418B2002598AF /* FileIndex.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F26224418B2002598AF /* FileIndex.hpp */; }; + 2ADE2F2E224418E7002598AF /* ConversionTables.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F2D224418E7002598AF /* ConversionTables.h */; }; + 2ADE2F3122441905002598AF /* DiscordService.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F2F22441905002598AF /* DiscordService.h */; }; + 2ADE2F3222441905002598AF /* DiscordService.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2ADE2F3022441905002598AF /* DiscordService.cpp */; }; + 2ADE2F342244191E002598AF /* VirtualFloor.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F332244191E002598AF /* VirtualFloor.h */; }; + 2ADE2F3622441960002598AF /* RideTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F352244195F002598AF /* RideTypes.h */; }; + 2ADE2F382244198B002598AF /* SpriteBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F372244198A002598AF /* SpriteBase.h */; }; 4C29DEB3218C6AE500E8707F /* RCT12.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C29DEB2218C6AE500E8707F /* RCT12.cpp */; }; 4C358E5221C445F700ADE6BC /* ReplayManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C358E5021C445F700ADE6BC /* ReplayManager.cpp */; }; 4C3B4236205914F7000C5BB7 /* InGameConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C3B4234205914F7000C5BB7 /* InGameConsole.cpp */; }; @@ -61,6 +58,9 @@ 9308DA03209908090079EE96 /* Surface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9308D9FB209908080079EE96 /* Surface.cpp */; }; 9308DA04209908090079EE96 /* TileElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9308D9FC209908080079EE96 /* TileElement.h */; }; 9308DA05209908090079EE96 /* Surface.h in Headers */ = {isa = PBXBuildFile; fileRef = 9308D9FD209908090079EE96 /* Surface.h */; }; + 932A211E22D73CFA00C57EDB /* GameActionCompat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 932A20CF22D73CEE00C57EDB /* GameActionCompat.cpp */; }; + 932A211F22D73CFA00C57EDB /* GameActionRegistration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 932A20D322D73CEF00C57EDB /* GameActionRegistration.cpp */; }; + 932A212022D73CFA00C57EDB /* GameAction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 932A211C22D73CFA00C57EDB /* GameAction.cpp */; }; 933CBDB520CB1ACD00134678 /* Widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 933CBDB120CB1ACC00134678 /* Widget.cpp */; }; 933CBDB620CB1ACD00134678 /* Theme.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 933CBDB220CB1ACD00134678 /* Theme.cpp */; }; 933CBDBB20CB1B3F00134678 /* TitleSequencePlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 933CBDB920CB1B3F00134678 /* TitleSequencePlayer.cpp */; }; @@ -78,6 +78,8 @@ 9346F9DB208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; }; 9346F9DC208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; }; 9346F9DD208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; }; + 937A92132242CCB300B09278 /* LandBuyRightsAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 937A92122242CCB300B09278 /* LandBuyRightsAction.hpp */; }; + 937A92152242CDAA00B09278 /* LandSmoothAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 937A92142242CDAA00B09278 /* LandSmoothAction.hpp */; }; 939A359A20C12FC800630B3F /* Paint.Litter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 939A359720C12FC700630B3F /* Paint.Litter.cpp */; }; 939A359B20C12FC800630B3F /* Paint.Misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 939A359820C12FC700630B3F /* Paint.Misc.cpp */; }; 939A359C20C12FC800630B3F /* Paint.Sprite.h in Headers */ = {isa = PBXBuildFile; fileRef = 939A359920C12FC700630B3F /* Paint.Sprite.h */; }; @@ -134,14 +136,6 @@ C6352B841F477022006CCEE3 /* DataSerialiser.h in Headers */ = {isa = PBXBuildFile; fileRef = C6352B811F477022006CCEE3 /* DataSerialiser.h */; }; C6352B851F477022006CCEE3 /* DataSerialiserTraits.h in Headers */ = {isa = PBXBuildFile; fileRef = C6352B821F477022006CCEE3 /* DataSerialiserTraits.h */; }; C6352B861F477022006CCEE3 /* Endianness.h in Headers */ = {isa = PBXBuildFile; fileRef = C6352B831F477022006CCEE3 /* Endianness.h */; }; - C6352B901F477032006CCEE3 /* GameAction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6352B881F477032006CCEE3 /* GameAction.cpp */; }; - C6352B911F477032006CCEE3 /* GameAction.h in Headers */ = {isa = PBXBuildFile; fileRef = C6352B891F477032006CCEE3 /* GameAction.h */; }; - C6352B921F477032006CCEE3 /* GameActionCompat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6352B8A1F477032006CCEE3 /* GameActionCompat.cpp */; }; - C6352B931F477032006CCEE3 /* GameActionRegistration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6352B8B1F477032006CCEE3 /* GameActionRegistration.cpp */; }; - C6352B941F477032006CCEE3 /* PlaceParkEntranceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = C6352B8C1F477032006CCEE3 /* PlaceParkEntranceAction.hpp */; }; - C6352B951F477032006CCEE3 /* RideCreateAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = C6352B8D1F477032006CCEE3 /* RideCreateAction.hpp */; }; - C6352B961F477032006CCEE3 /* RideSetStatus.hpp in Headers */ = {isa = PBXBuildFile; fileRef = C6352B8E1F477032006CCEE3 /* RideSetStatus.hpp */; }; - C6352B971F477032006CCEE3 /* SetParkEntranceFeeAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = C6352B8F1F477032006CCEE3 /* SetParkEntranceFeeAction.hpp */; }; C64644F81F3FA4120026AC2D /* ClearScenery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C64644EE1F3FA4120026AC2D /* ClearScenery.cpp */; }; C64644F91F3FA4120026AC2D /* EditorInventionsList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C64644EF1F3FA4120026AC2D /* EditorInventionsList.cpp */; }; C64644FA1F3FA4120026AC2D /* EditorObjectiveOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C64644F01F3FA4120026AC2D /* EditorObjectiveOptions.cpp */; }; @@ -286,7 +280,6 @@ C68878A020289B200084B384 /* LanguagePack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C7B53B31FFF935B00A52E21 /* LanguagePack.cpp */; }; C68878A120289B200084B384 /* Localisation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C7B53B51FFF935B00A52E21 /* Localisation.cpp */; }; C68878A220289B200084B384 /* RealNames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C7B53B71FFF935B00A52E21 /* RealNames.cpp */; }; - C68878A320289B200084B384 /* User.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C7B53B91FFF935B00A52E21 /* User.cpp */; }; C68878A420289B200084B384 /* UTF8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C7B53BB1FFF935B00A52E21 /* UTF8.cpp */; }; C68878A520289B2A0084B384 /* Award.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C93F1B01F8E185600A9330D /* Award.cpp */; }; C68878A620289B2A0084B384 /* Finance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C93F1B21F8E185600A9330D /* Finance.cpp */; }; @@ -409,6 +402,7 @@ C6E415511FAFD6DC00D4A52A /* RideConstruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6E415501FAFD6DB00D4A52A /* RideConstruction.cpp */; }; C6E96E361E0408B40076A04F /* libzip.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C6E96E351E0408B40076A04F /* libzip.dylib */; }; C6E96E371E040E040076A04F /* libzip.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C6E96E351E0408B40076A04F /* libzip.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + C9C630B62235A22D009AD16E /* GameStateSnapshots.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C9C630B52235A22C009AD16E /* GameStateSnapshots.cpp */; }; D41B73EF1C2101890080A7B9 /* libcurl.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D41B73EE1C2101890080A7B9 /* libcurl.tbd */; }; D41B741D1C210A7A0080A7B9 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D41B741C1C210A7A0080A7B9 /* libiconv.tbd */; }; D41B74731C2125E50080A7B9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D41B74721C2125E50080A7B9 /* Assets.xcassets */; }; @@ -470,7 +464,7 @@ F76C86551EC4E88300FA49E2 /* NetworkServerAdvertiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C84061EC4E7CC00FA49E2 /* NetworkServerAdvertiser.cpp */; }; F76C86581EC4E88300FA49E2 /* NetworkUser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C84091EC4E7CC00FA49E2 /* NetworkUser.cpp */; }; F76C865A1EC4E88300FA49E2 /* ServerList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C840B1EC4E7CC00FA49E2 /* ServerList.cpp */; }; - F76C865C1EC4E88300FA49E2 /* TcpSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C840D1EC4E7CC00FA49E2 /* TcpSocket.cpp */; }; + F76C865C1EC4E88300FA49E2 /* Socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C840D1EC4E7CC00FA49E2 /* Socket.cpp */; }; F76C86601EC4E88300FA49E2 /* BannerObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C84121EC4E7CC00FA49E2 /* BannerObject.cpp */; }; F76C86621EC4E88300FA49E2 /* EntranceObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C84141EC4E7CC00FA49E2 /* EntranceObject.cpp */; }; F76C86641EC4E88300FA49E2 /* FootpathItemObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C84161EC4E7CC00FA49E2 /* FootpathItemObject.cpp */; }; @@ -633,6 +627,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 01C6F0C022FD519E0057E2F7 /* TrackImporter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackImporter.cpp; sourceTree = ""; }; + 01C6F0C122FD519E0057E2F7 /* TrackImporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackImporter.h; sourceTree = ""; }; + 01C6F0C322FD51B70057E2F7 /* T4Importer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = T4Importer.cpp; sourceTree = ""; }; + 01C6F0C522FD51FC0057E2F7 /* T6Exporter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = T6Exporter.cpp; sourceTree = ""; }; + 01C6F0C622FD51FC0057E2F7 /* T6Importer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = T6Importer.cpp; sourceTree = ""; }; + 01C6F0C722FD51FC0057E2F7 /* T6Exporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = T6Exporter.h; sourceTree = ""; }; + 01DDFE6422FD608500221318 /* Window_internal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Window_internal.cpp; sourceTree = ""; }; 2A43D2B72225B8D900E8F73B /* RideSetVehiclesAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetVehiclesAction.hpp; sourceTree = ""; }; 2A43D2B82225B8D900E8F73B /* SmallSceneryPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SmallSceneryPlaceAction.hpp; sourceTree = ""; }; 2A43D2B92225B8D900E8F73B /* LoadOrQuitAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LoadOrQuitAction.hpp; sourceTree = ""; }; @@ -641,21 +642,18 @@ 2A43D2BF2225B91A00E8F73B /* LoadOrQuitAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LoadOrQuitAction.hpp; sourceTree = ""; }; 2A5354E822099C4F00A5440F /* Network.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Network.cpp; sourceTree = ""; }; 2A5354EA22099C7200A5440F /* CircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircularBuffer.h; sourceTree = ""; }; - 2A5354EB22099D7700A5440F /* SignSetStyleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SignSetStyleAction.hpp; sourceTree = ""; }; - 2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackRemoveAction.hpp; sourceTree = ""; }; - 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideEntranceExitPlaceAction.hpp; sourceTree = ""; }; - 2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterSetHeightAction.hpp; sourceTree = ""; }; - 2A61CAF22229E5720095AD67 /* FootpathSceneryPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathSceneryPlaceAction.hpp; sourceTree = ""; }; - 2A61CAF32229E5720095AD67 /* FootpathSceneryRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathSceneryRemoveAction.hpp; sourceTree = ""; }; - 2A61CAF42229E5720095AD67 /* FootpathPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathPlaceAction.hpp; sourceTree = ""; }; - 2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetCostumeAction.hpp; sourceTree = ""; }; - 2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetOrdersAction.hpp; sourceTree = ""; }; - 2AAFD7F9220DD2DC002461A4 /* TrackPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackPlaceAction.hpp; sourceTree = ""; }; - 2AAFD7FB220DD336002461A4 /* RideSetPriceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetPriceAction.hpp; sourceTree = ""; }; - 2AAFD7FD220DD374002461A4 /* PauseToggleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PauseToggleAction.hpp; sourceTree = ""; }; - 2AAFD7FF220DD3D2002461A4 /* LandSetHeightAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandSetHeightAction.hpp; sourceTree = ""; }; - 2ACBAB162226850A0034FB91 /* RideSetSetting.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetSetting.hpp; sourceTree = ""; }; - 2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetAppearanceAction.hpp; sourceTree = ""; }; + 2ADE2F21224418B1002598AF /* Random.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Random.hpp; sourceTree = ""; }; + 2ADE2F22224418B1002598AF /* DataSerialiserTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSerialiserTag.h; sourceTree = ""; }; + 2ADE2F23224418B1002598AF /* Numerics.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Numerics.hpp; sourceTree = ""; }; + 2ADE2F24224418B2002598AF /* Meta.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Meta.hpp; sourceTree = ""; }; + 2ADE2F25224418B2002598AF /* JobPool.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = JobPool.hpp; sourceTree = ""; }; + 2ADE2F26224418B2002598AF /* FileIndex.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FileIndex.hpp; sourceTree = ""; }; + 2ADE2F2D224418E7002598AF /* ConversionTables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConversionTables.h; sourceTree = ""; }; + 2ADE2F2F22441905002598AF /* DiscordService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiscordService.h; sourceTree = ""; }; + 2ADE2F3022441905002598AF /* DiscordService.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DiscordService.cpp; sourceTree = ""; }; + 2ADE2F332244191E002598AF /* VirtualFloor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VirtualFloor.h; sourceTree = ""; }; + 2ADE2F352244195F002598AF /* RideTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RideTypes.h; sourceTree = ""; }; + 2ADE2F372244198A002598AF /* SpriteBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpriteBase.h; sourceTree = ""; }; 4C04D69F2056AA9600F82EBA /* linenoise.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = linenoise.hpp; sourceTree = ""; }; 4C1A53EC205FD19F000F8EF5 /* SceneryObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SceneryObject.cpp; sourceTree = ""; }; 4C29DEB2218C6AE500E8707F /* RCT12.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RCT12.cpp; sourceTree = ""; }; @@ -705,8 +703,6 @@ 4C7B53B61FFF935B00A52E21 /* Localisation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Localisation.h; sourceTree = ""; }; 4C7B53B71FFF935B00A52E21 /* RealNames.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RealNames.cpp; sourceTree = ""; }; 4C7B53B81FFF935B00A52E21 /* StringIds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringIds.h; sourceTree = ""; }; - 4C7B53B91FFF935B00A52E21 /* User.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = User.cpp; sourceTree = ""; }; - 4C7B53BA1FFF935B00A52E21 /* user.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = user.h; sourceTree = ""; }; 4C7B53BB1FFF935B00A52E21 /* UTF8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UTF8.cpp; sourceTree = ""; }; 4C7B53C61FFF94F900A52E21 /* ConversionTables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConversionTables.cpp; sourceTree = ""; }; 4C7B53C91FFF991000A52E21 /* Language.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Language.h; sourceTree = ""; }; @@ -920,6 +916,87 @@ 9308D9FB209908080079EE96 /* Surface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Surface.cpp; sourceTree = ""; }; 9308D9FC209908080079EE96 /* TileElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TileElement.h; sourceTree = ""; }; 9308D9FD209908090079EE96 /* Surface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Surface.h; sourceTree = ""; }; + 932A20CD22D73CEE00C57EDB /* GuestSetNameAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GuestSetNameAction.hpp; sourceTree = ""; }; + 932A20CE22D73CEE00C57EDB /* RideSetVehiclesAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetVehiclesAction.hpp; sourceTree = ""; }; + 932A20CF22D73CEE00C57EDB /* GameActionCompat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameActionCompat.cpp; sourceTree = ""; }; + 932A20D022D73CEE00C57EDB /* RideSetSetting.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetSetting.hpp; sourceTree = ""; }; + 932A20D122D73CEF00C57EDB /* WallPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WallPlaceAction.hpp; sourceTree = ""; }; + 932A20D222D73CEF00C57EDB /* SmallSceneryRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SmallSceneryRemoveAction.hpp; sourceTree = ""; }; + 932A20D322D73CEF00C57EDB /* GameActionRegistration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameActionRegistration.cpp; sourceTree = ""; }; + 932A20D422D73CEF00C57EDB /* RideSetName.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetName.hpp; sourceTree = ""; }; + 932A20D522D73CEF00C57EDB /* PlacePeepSpawnAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PlacePeepSpawnAction.hpp; sourceTree = ""; }; + 932A20D622D73CEF00C57EDB /* LandSetRightsAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandSetRightsAction.hpp; sourceTree = ""; }; + 932A20D722D73CEF00C57EDB /* RideCreateAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideCreateAction.hpp; sourceTree = ""; }; + 932A20D822D73CEF00C57EDB /* ParkEntranceRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkEntranceRemoveAction.hpp; sourceTree = ""; }; + 932A20D922D73CEF00C57EDB /* ParkSetLoanAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkSetLoanAction.hpp; sourceTree = ""; }; + 932A20DA22D73CF000C57EDB /* LandLowerAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandLowerAction.hpp; sourceTree = ""; }; + 932A20DB22D73CF000C57EDB /* FootpathPlaceFromTrackAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathPlaceFromTrackAction.hpp; sourceTree = ""; }; + 932A20DC22D73CF000C57EDB /* PlayerKickAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PlayerKickAction.hpp; sourceTree = ""; }; + 932A20DD22D73CF000C57EDB /* RideDemolishAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideDemolishAction.hpp; sourceTree = ""; }; + 932A20DE22D73CF000C57EDB /* ClimateSetAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ClimateSetAction.hpp; sourceTree = ""; }; + 932A20DF22D73CF000C57EDB /* PauseToggleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PauseToggleAction.hpp; sourceTree = ""; }; + 932A20E022D73CF000C57EDB /* LargeSceneryPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LargeSceneryPlaceAction.hpp; sourceTree = ""; }; + 932A20E122D73CF000C57EDB /* LandSetHeightAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandSetHeightAction.hpp; sourceTree = ""; }; + 932A20E222D73CF100C57EDB /* StaffSetColourAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetColourAction.hpp; sourceTree = ""; }; + 932A20E322D73CF100C57EDB /* StaffSetCostumeAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetCostumeAction.hpp; sourceTree = ""; }; + 932A20E422D73CF100C57EDB /* StaffSetNameAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetNameAction.hpp; sourceTree = ""; }; + 932A20E522D73CF100C57EDB /* BannerPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BannerPlaceAction.hpp; sourceTree = ""; }; + 932A20E622D73CF100C57EDB /* MazeSetTrackAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MazeSetTrackAction.hpp; sourceTree = ""; }; + 932A20E722D73CF100C57EDB /* StaffHireNewAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffHireNewAction.hpp; sourceTree = ""; }; + 932A20E822D73CF100C57EDB /* RideSetPriceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetPriceAction.hpp; sourceTree = ""; }; + 932A20E922D73CF200C57EDB /* WaterLowerAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterLowerAction.hpp; sourceTree = ""; }; + 932A20EA22D73CF200C57EDB /* ParkSetParameterAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkSetParameterAction.hpp; sourceTree = ""; }; + 932A20EB22D73CF200C57EDB /* SmallSceneryPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SmallSceneryPlaceAction.hpp; sourceTree = ""; }; + 932A20EC22D73CF200C57EDB /* LargeScenerySetColourAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LargeScenerySetColourAction.hpp; sourceTree = ""; }; + 932A20ED22D73CF200C57EDB /* ParkSetResearchFundingAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkSetResearchFundingAction.hpp; sourceTree = ""; }; + 932A20EE22D73CF200C57EDB /* ScenarioSetSettingAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ScenarioSetSettingAction.hpp; sourceTree = ""; }; + 932A20EF22D73CF200C57EDB /* TrackRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackRemoveAction.hpp; sourceTree = ""; }; + 932A20F022D73CF300C57EDB /* SignSetNameAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SignSetNameAction.hpp; sourceTree = ""; }; + 932A20F122D73CF300C57EDB /* RideEntranceExitRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideEntranceExitRemoveAction.hpp; sourceTree = ""; }; + 932A20F222D73CF300C57EDB /* SetParkEntranceFeeAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SetParkEntranceFeeAction.hpp; sourceTree = ""; }; + 932A20F322D73CF300C57EDB /* FootpathRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathRemoveAction.hpp; sourceTree = ""; }; + 932A20F422D73CF300C57EDB /* RideSetAppearanceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetAppearanceAction.hpp; sourceTree = ""; }; + 932A20F522D73CF300C57EDB /* GameAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameAction.h; sourceTree = ""; }; + 932A20F622D73CF300C57EDB /* RideEntranceExitPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideEntranceExitPlaceAction.hpp; sourceTree = ""; }; + 932A20F722D73CF300C57EDB /* FootpathPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathPlaceAction.hpp; sourceTree = ""; }; + 932A20F822D73CF400C57EDB /* SignSetStyleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SignSetStyleAction.hpp; sourceTree = ""; }; + 932A20F922D73CF400C57EDB /* SmallScenerySetColourAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SmallScenerySetColourAction.hpp; sourceTree = ""; }; + 932A20FA22D73CF400C57EDB /* SurfaceSetStyleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SurfaceSetStyleAction.hpp; sourceTree = ""; }; + 932A20FB22D73CF400C57EDB /* RideSetStatus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetStatus.hpp; sourceTree = ""; }; + 932A20FC22D73CF400C57EDB /* ParkSetNameAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkSetNameAction.hpp; sourceTree = ""; }; + 932A20FD22D73CF500C57EDB /* LandBuyRightsAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandBuyRightsAction.hpp; sourceTree = ""; }; + 932A20FE22D73CF500C57EDB /* StaffSetPatrolAreaAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetPatrolAreaAction.hpp; sourceTree = ""; }; + 932A20FF22D73CF500C57EDB /* PeepPickupAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PeepPickupAction.hpp; sourceTree = ""; }; + 932A210022D73CF500C57EDB /* BannerSetNameAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BannerSetNameAction.hpp; sourceTree = ""; }; + 932A210122D73CF500C57EDB /* SetCheatAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SetCheatAction.hpp; sourceTree = ""; }; + 932A210222D73CF600C57EDB /* ParkMarketingAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkMarketingAction.hpp; sourceTree = ""; }; + 932A210322D73CF600C57EDB /* StaffSetOrdersAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetOrdersAction.hpp; sourceTree = ""; }; + 932A210422D73CF600C57EDB /* BannerSetColourAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BannerSetColourAction.hpp; sourceTree = ""; }; + 932A210522D73CF600C57EDB /* FootpathSceneryRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathSceneryRemoveAction.hpp; sourceTree = ""; }; + 932A210622D73CF600C57EDB /* GuestSetFlagsAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GuestSetFlagsAction.hpp; sourceTree = ""; }; + 932A210722D73CF600C57EDB /* TrackPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackPlaceAction.hpp; sourceTree = ""; }; + 932A210822D73CF700C57EDB /* PlaceParkEntranceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PlaceParkEntranceAction.hpp; sourceTree = ""; }; + 932A210922D73CF700C57EDB /* FootpathSceneryPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathSceneryPlaceAction.hpp; sourceTree = ""; }; + 932A210A22D73CF700C57EDB /* BannerRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BannerRemoveAction.hpp; sourceTree = ""; }; + 932A210B22D73CF700C57EDB /* LandRaiseAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandRaiseAction.hpp; sourceTree = ""; }; + 932A210C22D73CF700C57EDB /* LoadOrQuitAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LoadOrQuitAction.hpp; sourceTree = ""; }; + 932A210D22D73CF700C57EDB /* StaffFireAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffFireAction.hpp; sourceTree = ""; }; + 932A210E22D73CF800C57EDB /* ParkSetDateAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkSetDateAction.hpp; sourceTree = ""; }; + 932A210F22D73CF800C57EDB /* WallSetColourAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WallSetColourAction.hpp; sourceTree = ""; }; + 932A211022D73CF800C57EDB /* WaterSetHeightAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterSetHeightAction.hpp; sourceTree = ""; }; + 932A211122D73CF800C57EDB /* RideSetColourScheme.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetColourScheme.hpp; sourceTree = ""; }; + 932A211222D73CF800C57EDB /* WaterRaiseAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterRaiseAction.hpp; sourceTree = ""; }; + 932A211322D73CF800C57EDB /* WallRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WallRemoveAction.hpp; sourceTree = ""; }; + 932A211422D73CF800C57EDB /* LandSmoothAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandSmoothAction.hpp; sourceTree = ""; }; + 932A211522D73CF900C57EDB /* BalloonPressAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BalloonPressAction.hpp; sourceTree = ""; }; + 932A211622D73CF900C57EDB /* ClearAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ClearAction.hpp; sourceTree = ""; }; + 932A211722D73CF900C57EDB /* BannerSetStyleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BannerSetStyleAction.hpp; sourceTree = ""; }; + 932A211822D73CF900C57EDB /* NetworkModifyGroupAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NetworkModifyGroupAction.hpp; sourceTree = ""; }; + 932A211922D73CF900C57EDB /* PlayerSetGroupAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PlayerSetGroupAction.hpp; sourceTree = ""; }; + 932A211A22D73CFA00C57EDB /* TileModifyAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TileModifyAction.hpp; sourceTree = ""; }; + 932A211B22D73CFA00C57EDB /* TrackSetBrakeSpeedAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackSetBrakeSpeedAction.hpp; sourceTree = ""; }; + 932A211C22D73CFA00C57EDB /* GameAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameAction.cpp; sourceTree = ""; }; + 932A211D22D73CFA00C57EDB /* LargeSceneryRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LargeSceneryRemoveAction.hpp; sourceTree = ""; }; 933CBDB120CB1ACC00134678 /* Widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Widget.cpp; sourceTree = ""; }; 933CBDB220CB1ACD00134678 /* Theme.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Theme.cpp; sourceTree = ""; }; 933CBDB320CB1ACD00134678 /* Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Theme.h; sourceTree = ""; }; @@ -1160,6 +1237,9 @@ 9350B52720B46E0900897BC5 /* ftbdf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ftbdf.h; sourceTree = ""; }; 9350B52820B46E0900897BC5 /* ftrender.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ftrender.h; sourceTree = ""; }; 9350B52920B46E0900897BC5 /* ft2build.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ft2build.h; sourceTree = ""; }; + 9391535A22D74359008E0780 /* OpenRCT2.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OpenRCT2.entitlements; sourceTree = ""; }; + 937A92122242CCB300B09278 /* LandBuyRightsAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandBuyRightsAction.hpp; sourceTree = ""; }; + 937A92142242CDAA00B09278 /* LandSmoothAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandSmoothAction.hpp; sourceTree = ""; }; 939A359720C12FC700630B3F /* Paint.Litter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Paint.Litter.cpp; sourceTree = ""; }; 939A359820C12FC700630B3F /* Paint.Misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Paint.Misc.cpp; sourceTree = ""; }; 939A359920C12FC700630B3F /* Paint.Sprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Paint.Sprite.h; sourceTree = ""; }; @@ -1208,14 +1288,6 @@ C6352B811F477022006CCEE3 /* DataSerialiser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSerialiser.h; sourceTree = ""; }; C6352B821F477022006CCEE3 /* DataSerialiserTraits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSerialiserTraits.h; sourceTree = ""; }; C6352B831F477022006CCEE3 /* Endianness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Endianness.h; sourceTree = ""; }; - C6352B881F477032006CCEE3 /* GameAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameAction.cpp; sourceTree = ""; }; - C6352B891F477032006CCEE3 /* GameAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameAction.h; sourceTree = ""; }; - C6352B8A1F477032006CCEE3 /* GameActionCompat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameActionCompat.cpp; sourceTree = ""; }; - C6352B8B1F477032006CCEE3 /* GameActionRegistration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameActionRegistration.cpp; sourceTree = ""; }; - C6352B8C1F477032006CCEE3 /* PlaceParkEntranceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PlaceParkEntranceAction.hpp; sourceTree = ""; }; - C6352B8D1F477032006CCEE3 /* RideCreateAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideCreateAction.hpp; sourceTree = ""; }; - C6352B8E1F477032006CCEE3 /* RideSetStatus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetStatus.hpp; sourceTree = ""; }; - C6352B8F1F477032006CCEE3 /* SetParkEntranceFeeAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SetParkEntranceFeeAction.hpp; sourceTree = ""; }; C64644EE1F3FA4120026AC2D /* ClearScenery.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClearScenery.cpp; sourceTree = ""; }; C64644EF1F3FA4120026AC2D /* EditorInventionsList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EditorInventionsList.cpp; sourceTree = ""; }; C64644F01F3FA4120026AC2D /* EditorObjectiveOptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EditorObjectiveOptions.cpp; sourceTree = ""; }; @@ -1303,6 +1375,13 @@ C6E96E331E0408A80076A04F /* zip.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = zip.h; sourceTree = ""; }; C6E96E341E0408A80076A04F /* zipconf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = zipconf.h; sourceTree = ""; }; C6E96E351E0408B40076A04F /* libzip.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libzip.dylib; sourceTree = ""; }; + C9C630B42235A22C009AD16E /* GameStateSnapshots.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameStateSnapshots.h; sourceTree = ""; }; + C9C630B52235A22C009AD16E /* GameStateSnapshots.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameStateSnapshots.cpp; sourceTree = ""; }; + C9C630B92235A7E7009AD16E /* LandRaiseAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandRaiseAction.hpp; sourceTree = ""; }; + C9C630BA2235A7E7009AD16E /* LandLowerAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandLowerAction.hpp; sourceTree = ""; }; + C9C630BB2235A7F9009AD16E /* WaterLowerAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterLowerAction.hpp; sourceTree = ""; }; + C9C630BC2235A7F9009AD16E /* WaterRaiseAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterRaiseAction.hpp; sourceTree = ""; }; + C9C630BD2235A9C6009AD16E /* FootpathPlaceFromTrackAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathPlaceFromTrackAction.hpp; sourceTree = ""; }; D41B73EE1C2101890080A7B9 /* libcurl.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcurl.tbd; path = usr/lib/libcurl.tbd; sourceTree = SDKROOT; }; D41B741C1C210A7A0080A7B9 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; D41B74721C2125E50080A7B9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = distribution/macos/Assets.xcassets; sourceTree = SOURCE_ROOT; }; @@ -1570,10 +1649,10 @@ F76C840A1EC4E7CC00FA49E2 /* NetworkUser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NetworkUser.h; sourceTree = ""; }; F76C840B1EC4E7CC00FA49E2 /* ServerList.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ServerList.cpp; sourceTree = ""; }; F76C840C1EC4E7CC00FA49E2 /* ServerList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ServerList.h; sourceTree = ""; }; - F76C840D1EC4E7CC00FA49E2 /* TcpSocket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TcpSocket.cpp; sourceTree = ""; }; - F76C840E1EC4E7CC00FA49E2 /* TcpSocket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TcpSocket.h; sourceTree = ""; }; + F76C840D1EC4E7CC00FA49E2 /* Socket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Socket.cpp; sourceTree = ""; }; + F76C840E1EC4E7CC00FA49E2 /* Socket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Socket.h; sourceTree = ""; }; F76C840F1EC4E7CC00FA49E2 /* Twitch.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Twitch.cpp; sourceTree = ""; }; - F76C84101EC4E7CC00FA49E2 /* twitch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = twitch.h; sourceTree = ""; }; + F76C84101EC4E7CC00FA49E2 /* Twitch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Twitch.h; sourceTree = ""; }; F76C84121EC4E7CC00FA49E2 /* BannerObject.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BannerObject.cpp; sourceTree = ""; }; F76C84131EC4E7CC00FA49E2 /* BannerObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BannerObject.h; sourceTree = ""; }; F76C84141EC4E7CC00FA49E2 /* EntranceObject.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = EntranceObject.cpp; sourceTree = ""; }; @@ -2034,35 +2113,87 @@ C6352B871F477032006CCEE3 /* actions */ = { isa = PBXGroup; children = ( - 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */, - 2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */, - 2A61CAF42229E5720095AD67 /* FootpathPlaceAction.hpp */, - 2A61CAF22229E5720095AD67 /* FootpathSceneryPlaceAction.hpp */, - 2A61CAF32229E5720095AD67 /* FootpathSceneryRemoveAction.hpp */, - 2ACBAB162226850A0034FB91 /* RideSetSetting.hpp */, - 2A43D2B92225B8D900E8F73B /* LoadOrQuitAction.hpp */, - 2A43D2B72225B8D900E8F73B /* RideSetVehiclesAction.hpp */, - 2A43D2B82225B8D900E8F73B /* SmallSceneryPlaceAction.hpp */, - 2A43D2BF2225B91A00E8F73B /* LoadOrQuitAction.hpp */, - 2A43D2BE2225B91A00E8F73B /* RideEntranceExitRemoveAction.hpp */, - 2A43D2BD2225B91A00E8F73B /* RideSetVehiclesAction.hpp */, - 2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */, - 2AAFD7FF220DD3D2002461A4 /* LandSetHeightAction.hpp */, - 2AAFD7FB220DD336002461A4 /* RideSetPriceAction.hpp */, - 2AAFD7FD220DD374002461A4 /* PauseToggleAction.hpp */, - 2AAFD7F9220DD2DC002461A4 /* TrackPlaceAction.hpp */, - 2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */, - 2A5354EB22099D7700A5440F /* SignSetStyleAction.hpp */, - 2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */, - 2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */, - C6352B881F477032006CCEE3 /* GameAction.cpp */, - C6352B891F477032006CCEE3 /* GameAction.h */, - C6352B8A1F477032006CCEE3 /* GameActionCompat.cpp */, - C6352B8B1F477032006CCEE3 /* GameActionRegistration.cpp */, - C6352B8C1F477032006CCEE3 /* PlaceParkEntranceAction.hpp */, - C6352B8D1F477032006CCEE3 /* RideCreateAction.hpp */, - C6352B8E1F477032006CCEE3 /* RideSetStatus.hpp */, - C6352B8F1F477032006CCEE3 /* SetParkEntranceFeeAction.hpp */, + 932A211522D73CF900C57EDB /* BalloonPressAction.hpp */, + 932A20E522D73CF100C57EDB /* BannerPlaceAction.hpp */, + 932A210A22D73CF700C57EDB /* BannerRemoveAction.hpp */, + 932A210422D73CF600C57EDB /* BannerSetColourAction.hpp */, + 932A210022D73CF500C57EDB /* BannerSetNameAction.hpp */, + 932A211722D73CF900C57EDB /* BannerSetStyleAction.hpp */, + 932A211622D73CF900C57EDB /* ClearAction.hpp */, + 932A20DE22D73CF000C57EDB /* ClimateSetAction.hpp */, + 932A20F722D73CF300C57EDB /* FootpathPlaceAction.hpp */, + 932A20DB22D73CF000C57EDB /* FootpathPlaceFromTrackAction.hpp */, + 932A20F322D73CF300C57EDB /* FootpathRemoveAction.hpp */, + 932A210922D73CF700C57EDB /* FootpathSceneryPlaceAction.hpp */, + 932A210522D73CF600C57EDB /* FootpathSceneryRemoveAction.hpp */, + 932A211C22D73CFA00C57EDB /* GameAction.cpp */, + 932A20F522D73CF300C57EDB /* GameAction.h */, + 932A20CF22D73CEE00C57EDB /* GameActionCompat.cpp */, + 932A20D322D73CEF00C57EDB /* GameActionRegistration.cpp */, + 932A210622D73CF600C57EDB /* GuestSetFlagsAction.hpp */, + 932A20CD22D73CEE00C57EDB /* GuestSetNameAction.hpp */, + 932A20FD22D73CF500C57EDB /* LandBuyRightsAction.hpp */, + 932A20DA22D73CF000C57EDB /* LandLowerAction.hpp */, + 932A210B22D73CF700C57EDB /* LandRaiseAction.hpp */, + 932A20E122D73CF000C57EDB /* LandSetHeightAction.hpp */, + 932A20D622D73CEF00C57EDB /* LandSetRightsAction.hpp */, + 932A211422D73CF800C57EDB /* LandSmoothAction.hpp */, + 932A20E022D73CF000C57EDB /* LargeSceneryPlaceAction.hpp */, + 932A211D22D73CFA00C57EDB /* LargeSceneryRemoveAction.hpp */, + 932A20EC22D73CF200C57EDB /* LargeScenerySetColourAction.hpp */, + 932A210C22D73CF700C57EDB /* LoadOrQuitAction.hpp */, + 932A20E622D73CF100C57EDB /* MazeSetTrackAction.hpp */, + 932A211822D73CF900C57EDB /* NetworkModifyGroupAction.hpp */, + 932A20D822D73CEF00C57EDB /* ParkEntranceRemoveAction.hpp */, + 932A210222D73CF600C57EDB /* ParkMarketingAction.hpp */, + 932A210E22D73CF800C57EDB /* ParkSetDateAction.hpp */, + 932A20D922D73CEF00C57EDB /* ParkSetLoanAction.hpp */, + 932A20FC22D73CF400C57EDB /* ParkSetNameAction.hpp */, + 932A20EA22D73CF200C57EDB /* ParkSetParameterAction.hpp */, + 932A20ED22D73CF200C57EDB /* ParkSetResearchFundingAction.hpp */, + 932A20DF22D73CF000C57EDB /* PauseToggleAction.hpp */, + 932A20FF22D73CF500C57EDB /* PeepPickupAction.hpp */, + 932A210822D73CF700C57EDB /* PlaceParkEntranceAction.hpp */, + 932A20D522D73CEF00C57EDB /* PlacePeepSpawnAction.hpp */, + 932A20DC22D73CF000C57EDB /* PlayerKickAction.hpp */, + 932A211922D73CF900C57EDB /* PlayerSetGroupAction.hpp */, + 932A20D722D73CEF00C57EDB /* RideCreateAction.hpp */, + 932A20DD22D73CF000C57EDB /* RideDemolishAction.hpp */, + 932A20F622D73CF300C57EDB /* RideEntranceExitPlaceAction.hpp */, + 932A20F122D73CF300C57EDB /* RideEntranceExitRemoveAction.hpp */, + 932A20F422D73CF300C57EDB /* RideSetAppearanceAction.hpp */, + 932A211122D73CF800C57EDB /* RideSetColourScheme.hpp */, + 932A20D422D73CEF00C57EDB /* RideSetName.hpp */, + 932A20E822D73CF100C57EDB /* RideSetPriceAction.hpp */, + 932A20D022D73CEE00C57EDB /* RideSetSetting.hpp */, + 932A20FB22D73CF400C57EDB /* RideSetStatus.hpp */, + 932A20CE22D73CEE00C57EDB /* RideSetVehiclesAction.hpp */, + 932A20EE22D73CF200C57EDB /* ScenarioSetSettingAction.hpp */, + 932A210122D73CF500C57EDB /* SetCheatAction.hpp */, + 932A20F222D73CF300C57EDB /* SetParkEntranceFeeAction.hpp */, + 932A20F022D73CF300C57EDB /* SignSetNameAction.hpp */, + 932A20F822D73CF400C57EDB /* SignSetStyleAction.hpp */, + 932A20EB22D73CF200C57EDB /* SmallSceneryPlaceAction.hpp */, + 932A20D222D73CEF00C57EDB /* SmallSceneryRemoveAction.hpp */, + 932A20F922D73CF400C57EDB /* SmallScenerySetColourAction.hpp */, + 932A210D22D73CF700C57EDB /* StaffFireAction.hpp */, + 932A20E722D73CF100C57EDB /* StaffHireNewAction.hpp */, + 932A20E222D73CF100C57EDB /* StaffSetColourAction.hpp */, + 932A20E322D73CF100C57EDB /* StaffSetCostumeAction.hpp */, + 932A20E422D73CF100C57EDB /* StaffSetNameAction.hpp */, + 932A210322D73CF600C57EDB /* StaffSetOrdersAction.hpp */, + 932A20FE22D73CF500C57EDB /* StaffSetPatrolAreaAction.hpp */, + 932A20FA22D73CF400C57EDB /* SurfaceSetStyleAction.hpp */, + 932A211A22D73CFA00C57EDB /* TileModifyAction.hpp */, + 932A210722D73CF600C57EDB /* TrackPlaceAction.hpp */, + 932A20EF22D73CF200C57EDB /* TrackRemoveAction.hpp */, + 932A211B22D73CFA00C57EDB /* TrackSetBrakeSpeedAction.hpp */, + 932A20D122D73CEF00C57EDB /* WallPlaceAction.hpp */, + 932A211322D73CF800C57EDB /* WallRemoveAction.hpp */, + 932A210F22D73CF800C57EDB /* WallSetColourAction.hpp */, + 932A20E922D73CF200C57EDB /* WaterLowerAction.hpp */, + 932A211222D73CF800C57EDB /* WaterRaiseAction.hpp */, + 932A211022D73CF800C57EDB /* WaterSetHeightAction.hpp */, ); path = actions; sourceTree = ""; @@ -2300,6 +2431,7 @@ D497D06F1C20FD52002BF46A = { isa = PBXGroup; children = ( + 9391535A22D74359008E0780 /* OpenRCT2.entitlements */, 4CF67196206B7E720034ADDD /* object */, D41B72431C21015A0080A7B9 /* Sources */, D497D07A1C20FD52002BF46A /* Resources */, @@ -2391,6 +2523,10 @@ F76C83551EC4E7CC00FA49E2 /* libopenrct2 */ = { isa = PBXGroup; children = ( + 01C6F0C022FD519E0057E2F7 /* TrackImporter.cpp */, + 01C6F0C122FD519E0057E2F7 /* TrackImporter.h */, + C9C630B52235A22C009AD16E /* GameStateSnapshots.cpp */, + C9C630B42235A22C009AD16E /* GameStateSnapshots.h */, 4C358E5021C445F700ADE6BC /* ReplayManager.cpp */, 4C358E5121C445F700ADE6BC /* ReplayManager.h */, C6352B871F477032006CCEE3 /* actions */, @@ -2508,6 +2644,12 @@ F76C83781EC4E7CC00FA49E2 /* core */ = { isa = PBXGroup; children = ( + 2ADE2F22224418B1002598AF /* DataSerialiserTag.h */, + 2ADE2F26224418B2002598AF /* FileIndex.hpp */, + 2ADE2F25224418B2002598AF /* JobPool.hpp */, + 2ADE2F24224418B2002598AF /* Meta.hpp */, + 2ADE2F23224418B1002598AF /* Numerics.hpp */, + 2ADE2F21224418B1002598AF /* Random.hpp */, 2A5354EA22099C7200A5440F /* CircularBuffer.h */, F76C83791EC4E7CC00FA49E2 /* Collections.hpp */, F76C837A1EC4E7CC00FA49E2 /* Console.cpp */, @@ -2590,6 +2732,7 @@ F76C83BB1EC4E7CC00FA49E2 /* interface */ = { isa = PBXGroup; children = ( + 01DDFE6422FD608500221318 /* Window_internal.cpp */, 4C7B53DD200143C200A52E21 /* Chat.cpp */, 4C7B53DE200143C200A52E21 /* Chat.h */, 4C7B53DF200143C200A52E21 /* Colour.cpp */, @@ -2617,6 +2760,7 @@ F76C83D71EC4E7CC00FA49E2 /* localisation */ = { isa = PBXGroup; children = ( + 2ADE2F2D224418E7002598AF /* ConversionTables.h */, 4C7B53C61FFF94F900A52E21 /* ConversionTables.cpp */, 4C7B53AA1FFF935B00A52E21 /* Convert.cpp */, 4C7B53AB1FFF935B00A52E21 /* Currency.cpp */, @@ -2635,8 +2779,6 @@ 4C7B53B61FFF935B00A52E21 /* Localisation.h */, 4C7B53B71FFF935B00A52E21 /* RealNames.cpp */, 4C7B53B81FFF935B00A52E21 /* StringIds.h */, - 4C7B53B91FFF935B00A52E21 /* User.cpp */, - 4C7B53BA1FFF935B00A52E21 /* user.h */, 4C7B53BB1FFF935B00A52E21 /* UTF8.cpp */, ); path = localisation; @@ -2662,6 +2804,8 @@ F76C83F51EC4E7CC00FA49E2 /* network */ = { isa = PBXGroup; children = ( + 2ADE2F3022441905002598AF /* DiscordService.cpp */, + 2ADE2F2F22441905002598AF /* DiscordService.h */, F76C83F61EC4E7CC00FA49E2 /* Http.cpp */, F76C83F71EC4E7CC00FA49E2 /* http.h */, F76C83F81EC4E7CC00FA49E2 /* Network.cpp */, @@ -2685,10 +2829,10 @@ F76C840A1EC4E7CC00FA49E2 /* NetworkUser.h */, F76C840B1EC4E7CC00FA49E2 /* ServerList.cpp */, F76C840C1EC4E7CC00FA49E2 /* ServerList.h */, - F76C840D1EC4E7CC00FA49E2 /* TcpSocket.cpp */, - F76C840E1EC4E7CC00FA49E2 /* TcpSocket.h */, + F76C840D1EC4E7CC00FA49E2 /* Socket.cpp */, + F76C840E1EC4E7CC00FA49E2 /* Socket.h */, F76C840F1EC4E7CC00FA49E2 /* Twitch.cpp */, - F76C84101EC4E7CC00FA49E2 /* twitch.h */, + F76C84101EC4E7CC00FA49E2 /* Twitch.h */, ); path = network; sourceTree = ""; @@ -2750,6 +2894,7 @@ F76C843A1EC4E7CC00FA49E2 /* paint */ = { isa = PBXGroup; children = ( + 2ADE2F332244191E002598AF /* VirtualFloor.h */, F76C84491EC4E7CC00FA49E2 /* sprite */, F76C843B1EC4E7CC00FA49E2 /* tile_element */, 4C6A66AE1FE278C900694CB6 /* Paint.cpp */, @@ -2832,6 +2977,7 @@ F76C84661EC4E7CC00FA49E2 /* rct1 */ = { isa = PBXGroup; children = ( + 01C6F0C322FD51B70057E2F7 /* T4Importer.cpp */, 4C7B54022004C57400A52E21 /* RCT1.h */, F76C84671EC4E7CC00FA49E2 /* S4Importer.cpp */, F76C84681EC4E7CC00FA49E2 /* Tables.cpp */, @@ -2860,6 +3006,9 @@ F76C84761EC4E7CC00FA49E2 /* rct2 */ = { isa = PBXGroup; children = ( + 01C6F0C522FD51FC0057E2F7 /* T6Exporter.cpp */, + 01C6F0C722FD51FC0057E2F7 /* T6Exporter.h */, + 01C6F0C622FD51FC0057E2F7 /* T6Importer.cpp */, 4C7B54042004C58200A52E21 /* RCT2.h */, F76C847D1EC4E7CC00FA49E2 /* S6Exporter.cpp */, F76C847E1EC4E7CC00FA49E2 /* S6Exporter.h */, @@ -2871,6 +3020,7 @@ F76C84831EC4E7CC00FA49E2 /* ride */ = { isa = PBXGroup; children = ( + 2ADE2F352244195F002598AF /* RideTypes.h */, F76C84861EC4E7CC00FA49E2 /* coaster */, F76C84A91EC4E7CC00FA49E2 /* gentle */, F76C84C01EC4E7CC00FA49E2 /* shops */, @@ -3095,6 +3245,7 @@ F76C855B1EC4E7CD00FA49E2 /* world */ = { isa = PBXGroup; children = ( + 2ADE2F372244198A002598AF /* SpriteBase.h */, 4C7B541D2007646A00A52E21 /* Balloon.cpp */, 4C7B541E2007646A00A52E21 /* Banner.cpp */, 4C7B541F2007646A00A52E21 /* Banner.h */, @@ -3347,50 +3498,36 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 2A43D2C12225B91A00E8F73B /* RideEntranceExitRemoveAction.hpp in Headers */, + 2ADE2F3122441905002598AF /* DiscordService.h in Headers */, C67B28172002D67A00109C93 /* Viewport.h in Headers */, 939A359C20C12FC800630B3F /* Paint.Sprite.h in Headers */, 9308DA04209908090079EE96 /* TileElement.h in Headers */, C67B28152002D67A00109C93 /* Widget.h in Headers */, - C6352B951F477032006CCEE3 /* RideCreateAction.hpp in Headers */, C6352B851F477022006CCEE3 /* DataSerialiserTraits.h in Headers */, 939A359F20C12FDE00630B3F /* Paint.Surface.h in Headers */, - 2A61CAF62229E5720095AD67 /* FootpathSceneryRemoveAction.hpp in Headers */, C67B28192002D7F200109C93 /* Window_internal.h in Headers */, - C6352B971F477032006CCEE3 /* SetParkEntranceFeeAction.hpp in Headers */, - 2AA050332209A8E300D3A922 /* StaffSetOrdersAction.hpp in Headers */, - 2AAFD7FA220DD2DC002461A4 /* TrackPlaceAction.hpp in Headers */, - 2A61CAF92229E59F0095AD67 /* WaterSetHeightAction.hpp in Headers */, + 2ADE2F28224418B2002598AF /* DataSerialiserTag.h in Headers */, + 2ADE2F2E224418E7002598AF /* ConversionTables.h in Headers */, 933F2CBB20935668001B33FD /* LocalisationService.h in Headers */, - 2A5C1368221E9F9000F8C245 /* TrackRemoveAction.hpp in Headers */, - 2A61CAFB2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp in Headers */, C6352B861F477022006CCEE3 /* Endianness.h in Headers */, + 2ADE2F2C224418B2002598AF /* FileIndex.hpp in Headers */, 93CBA4CC20A7504500867D56 /* ImageImporter.h in Headers */, - 2AAFD7FE220DD374002461A4 /* PauseToggleAction.hpp in Headers */, - C6352B941F477032006CCEE3 /* PlaceParkEntranceAction.hpp in Headers */, - C6352B911F477032006CCEE3 /* GameAction.h in Headers */, - 2A43D2BA2225B8D900E8F73B /* RideSetVehiclesAction.hpp in Headers */, - 2A43D2C02225B91A00E8F73B /* RideSetVehiclesAction.hpp in Headers */, - 2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */, - 2A61CAF72229E5720095AD67 /* FootpathPlaceAction.hpp in Headers */, - 2A43D2C22225B91A00E8F73B /* LoadOrQuitAction.hpp in Headers */, + 2ADE2F29224418B2002598AF /* Numerics.hpp in Headers */, + 2ADE2F382244198B002598AF /* SpriteBase.h in Headers */, C62D838B1FD36D6F008C04F1 /* EditorObjectSelectionSession.h in Headers */, - 2A43D2BC2225B8D900E8F73B /* LoadOrQuitAction.hpp in Headers */, + 2ADE2F27224418B2002598AF /* Random.hpp in Headers */, 9344BEF920C1E6180047D165 /* Crypt.h in Headers */, 939A35A220C12FFD00630B3F /* InteractiveConsole.h in Headers */, - 2ACBAB172226850A0034FB91 /* RideSetSetting.hpp in Headers */, 93CBA4C320A7502E00867D56 /* Imaging.h in Headers */, - 2A61CAF52229E5720095AD67 /* FootpathSceneryPlaceAction.hpp in Headers */, + 2ADE2F2B224418B2002598AF /* JobPool.hpp in Headers */, + 2ADE2F3622441960002598AF /* RideTypes.h in Headers */, 9308DA05209908090079EE96 /* Surface.h in Headers */, 93DE9753209C3C1000FB1CC8 /* GameState.h in Headers */, + 2ADE2F2A224418B2002598AF /* Meta.hpp in Headers */, C6352B841F477022006CCEE3 /* DataSerialiser.h in Headers */, 939A35A020C12FDE00630B3F /* Paint.TileElement.h in Headers */, - 2AF7893D220B253E0072754A /* RideSetAppearanceAction.hpp in Headers */, - 2AAFD7FC220DD336002461A4 /* RideSetPriceAction.hpp in Headers */, C67B28162002D67A00109C93 /* Window.h in Headers */, - C6352B961F477032006CCEE3 /* RideSetStatus.hpp in Headers */, - 2A43D2BB2225B8D900E8F73B /* SmallSceneryPlaceAction.hpp in Headers */, - 2AAFD800220DD3D2002461A4 /* LandSetHeightAction.hpp in Headers */, + 2ADE2F342244191E002598AF /* VirtualFloor.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3467,7 +3604,7 @@ D497D0701C20FD52002BF46A /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1010; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = OpenRCT2; TargetAttributes = { C68B2D431EC790690020651C = { @@ -3476,6 +3613,11 @@ }; D497D0771C20FD52002BF46A = { CreatedOnToolsVersion = 7.2; + SystemCapabilities = { + com.apple.HardenedRuntime = { + enabled = 1; + }; + }; }; F76C80991EC4D9FA00FA49E2 = { CreatedOnToolsVersion = 8.3.2; @@ -3489,9 +3631,10 @@ }; buildConfigurationList = D497D0731C20FD52002BF46A /* Build configuration list for PBXProject "OpenRCT2" */; compatibilityVersion = "Xcode 10.0"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -3545,7 +3688,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "version=\"1.0.9\"\nzipname=\"objects.zip\"\nliburl=\"https://github.com/OpenRCT2/objects/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/data/object\" || ! -e \"${SRCROOT}/objectsversion\" || $(head -n 1 \"${SRCROOT}/objectsversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/data/object\" ]]; then rm -r \"${SRCROOT}/data/object\"; fi\nmkdir -p \"${SRCROOT}/data/object\"\n\ncurl -L -o \"${SRCROOT}/data/object/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/data/object\" \"${SRCROOT}/data/object/$zipname\"\nrm \"${SRCROOT}/data/object/$zipname\"\n\necho $version > \"${SRCROOT}/objectsversion\"\nfi"; + shellScript = "version=\"1.0.12\"\nzipname=\"objects.zip\"\nliburl=\"https://github.com/OpenRCT2/objects/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/data/object\" || ! -e \"${SRCROOT}/objectsversion\" || $(head -n 1 \"${SRCROOT}/objectsversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/data/object\" ]]; then rm -r \"${SRCROOT}/data/object\"; fi\nmkdir -p \"${SRCROOT}/data/object\"\n\ncurl -L -o \"${SRCROOT}/data/object/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/data/object\" \"${SRCROOT}/data/object/$zipname\"\nrm \"${SRCROOT}/data/object/$zipname\"\n\necho $version > \"${SRCROOT}/objectsversion\"\nfi"; }; C68B2D471EC790710020651C /* Download Libraries */ = { isa = PBXShellScriptBuildPhase; @@ -3559,7 +3702,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "version=\"17\"\nzipname=\"openrct2-libs-macos.zip\"\nliburl=\"https://github.com/OpenRCT2/Dependencies/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/libxc\" || ! -e \"${SRCROOT}/libversion\" || $(head -n 1 \"${SRCROOT}/libversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/libxc\" ]]; then rm -r \"${SRCROOT}/libxc\"; fi\nmkdir \"${SRCROOT}/libxc\"\n\ncurl -L -o \"${SRCROOT}/libxc/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/libxc\" \"${SRCROOT}/libxc/$zipname\"\nrm \"${SRCROOT}/libxc/$zipname\"\n\necho $version > \"${SRCROOT}/libversion\"\nfi"; + shellScript = "version=\"19\"\nzipname=\"openrct2-libs-v19-x64-macos-dylibs.zip\"\nliburl=\"https://github.com/OpenRCT2/Dependencies/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/libxc\" || ! -e \"${SRCROOT}/libversion\" || $(head -n 1 \"${SRCROOT}/libversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/libxc\" ]]; then rm -r \"${SRCROOT}/libxc\"; fi\nmkdir \"${SRCROOT}/libxc\"\n\ncurl -L -o \"${SRCROOT}/libxc/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/libxc\" \"${SRCROOT}/libxc/$zipname\"\nrm \"${SRCROOT}/libxc/$zipname\"\n\necho $version > \"${SRCROOT}/libversion\"\nfi\n"; }; D42C09D21C254F4E00309751 /* Build g2.dat */ = { isa = PBXShellScriptBuildPhase; @@ -3606,7 +3749,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "version=\"0.1.2\"\nzipname=\"title-sequence-v$version.zip\"\nliburl=\"https://github.com/OpenRCT2/title-sequences/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/data/title\" || ! -e \"${SRCROOT}/sequencesversion\" || $(head -n 1 \"${SRCROOT}/sequencesversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/data/title\" ]]; then rm -r \"${SRCROOT}/data/title\"; fi\nmkdir -p \"${SRCROOT}/data/title\"\n\ncurl -L -o \"${SRCROOT}/data/title/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/data/title\" \"${SRCROOT}/data/title/$zipname\"\nrm \"${SRCROOT}/data/title/$zipname\"\n\necho $version > \"${SRCROOT}/sequencesversion\"\nfi"; + shellScript = "version=\"0.1.2c\"\nzipname=\"title-sequences.zip\"\nliburl=\"https://github.com/OpenRCT2/title-sequences/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/data/title\" || ! -e \"${SRCROOT}/sequencesversion\" || $(head -n 1 \"${SRCROOT}/sequencesversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/data/title\" ]]; then rm -r \"${SRCROOT}/data/title\"; fi\nmkdir -p \"${SRCROOT}/data/title\"\n\ncurl -L -o \"${SRCROOT}/data/title/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/data/title\" \"${SRCROOT}/data/title/$zipname\"\nrm \"${SRCROOT}/data/title/$zipname\"\n\necho $version > \"${SRCROOT}/sequencesversion\"\nfi"; }; D4EC012A1C25532B00DAFE69 /* Setup AppIcon */ = { isa = PBXShellScriptBuildPhase; @@ -3687,6 +3830,7 @@ C654DF2E1F69C0430040F43D /* DemolishRidePrompt.cpp in Sources */, C6D2BEE21F9BAA6C008B557C /* Ride.cpp in Sources */, C666EE7C1F37ACB10061AA04 /* TitleLogo.cpp in Sources */, + 932A212022D73CFA00C57EDB /* GameAction.cpp in Sources */, F76C88781EC5324E00FA49E2 /* AudioChannel.cpp in Sources */, C666EE6D1F37ACB10061AA04 /* Cheats.cpp in Sources */, C685E5191F8907850090598F /* NewRide.cpp in Sources */, @@ -3722,13 +3866,16 @@ 4C93F1AF1F8CD9F600A9330D /* KeyboardShortcut.cpp in Sources */, C654DF3D1F69C0430040F43D /* TrackDesignPlace.cpp in Sources */, C666EE721F37ACB10061AA04 /* Multiplayer.cpp in Sources */, + 01C6F0C822FD51FC0057E2F7 /* T6Exporter.cpp in Sources */, C654DF371F69C0430040F43D /* Sign.cpp in Sources */, 93CBA4C920A7504500867D56 /* ImageImporter.cpp in Sources */, 933CBDB620CB1ACD00134678 /* Theme.cpp in Sources */, 93CBA4C020A74FF200867D56 /* BitmapReader.cpp in Sources */, + 01DDFE6522FD608500221318 /* Window_internal.cpp in Sources */, C68878CC20289B710084B384 /* SoftwareDrawingEngine.cpp in Sources */, C67CCD681FBBD138004FAE4C /* EditorMain.cpp in Sources */, C6E415511FAFD6DC00D4A52A /* RideConstruction.cpp in Sources */, + 932A211F22D73CFA00C57EDB /* GameActionRegistration.cpp in Sources */, 933CBDBD20CB1BA900134678 /* ViewportInteraction.cpp in Sources */, C685E51B1F8907850090598F /* Guest.cpp in Sources */, C64644F91F3FA4120026AC2D /* EditorInventionsList.cpp in Sources */, @@ -3752,13 +3899,16 @@ C666ED761F33DBB20061AA04 /* ShortcutKeyChange.cpp in Sources */, C685E51C1F8907850090598F /* Map.cpp in Sources */, F7CB864A1EEDA1330030C877 /* KeyboardShortcuts.cpp in Sources */, + 01C6F0C922FD51FC0057E2F7 /* T6Importer.cpp in Sources */, C654DF381F69C0430040F43D /* StaffFirePrompt.cpp in Sources */, C68313D51FDB4F4C006DB3D8 /* Graph.cpp in Sources */, C685E51D1F8907850090598F /* Research.cpp in Sources */, + 01C6F0C422FD51B70057E2F7 /* T4Importer.cpp in Sources */, C64644FB1F3FA4120026AC2D /* EditorScenarioOptions.cpp in Sources */, 93F60050213DD7E400EEB83E /* StationObject.cpp in Sources */, C654DF321F69C0430040F43D /* InstallTrack.cpp in Sources */, C64644FF1F3FA4120026AC2D /* StaffList.cpp in Sources */, + 932A211E22D73CFA00C57EDB /* GameActionCompat.cpp in Sources */, 4C29DEB3218C6AE500E8707F /* RCT12.cpp in Sources */, C6D2BEE81F9BAACE008B557C /* MazeConstruction.cpp in Sources */, C666EE771F37ACB10061AA04 /* SavePrompt.cpp in Sources */, @@ -3780,6 +3930,7 @@ C68878C320289B710084B384 /* DrawRectShader.cpp in Sources */, C666EE751F37ACB10061AA04 /* NewsOptions.cpp in Sources */, C654DF311F69C0430040F43D /* GuestList.cpp in Sources */, + 01C6F0C222FD519E0057E2F7 /* TrackImporter.cpp in Sources */, 4C93F1AD1F8CD9F000A9330D /* Input.cpp in Sources */, C666EE761F37ACB10061AA04 /* Options.cpp in Sources */, 2A5354E922099C4F00A5440F /* Network.cpp in Sources */, @@ -3799,6 +3950,7 @@ C68878C220289B710084B384 /* DrawLineShader.cpp in Sources */, F76C888B1EC5324E00FA49E2 /* Ui.cpp in Sources */, C685E51A1F8907850090598F /* Staff.cpp in Sources */, + C9C630B62235A22D009AD16E /* GameStateSnapshots.cpp in Sources */, F76C888C1EC5324E00FA49E2 /* UiContext.cpp in Sources */, C666EE7D1F37ACB10061AA04 /* TitleMenu.cpp in Sources */, 93F6004C213DD7DD00EEB83E /* TerrainSurfaceObject.cpp in Sources */, @@ -3816,7 +3968,6 @@ buildActionMask = 2147483647; files = ( F7C44AF82030E8D3007E099F /* AVX2Drawing.cpp in Sources */, - C68878A320289B200084B384 /* User.cpp in Sources */, F70839931FFC0B61002DCEFA /* Scenario.cpp in Sources */, C688791C20289B9B0084B384 /* Facility.cpp in Sources */, C688790C20289B9B0084B384 /* CarRide.cpp in Sources */, @@ -3832,7 +3983,6 @@ C688788820289ADE0084B384 /* X8DrawingEngine.cpp in Sources */, F775F5381EE3725C001F00E7 /* DummyAudioContext.cpp in Sources */, F775F5351EE35A89001F00E7 /* DummyUiContext.cpp in Sources */, - C6352B931F477032006CCEE3 /* GameActionRegistration.cpp in Sources */, 2A1F4FE1221FF4B0003CA045 /* Audio.cpp in Sources */, C688790920289B9B0084B384 /* WildMouse.cpp in Sources */, C688791420289B9B0084B384 /* Maze.cpp in Sources */, @@ -3944,7 +4094,7 @@ 93F76EFF20BFF77B00D4512C /* Paint.Wall.cpp in Sources */, F76C86581EC4E88300FA49E2 /* NetworkUser.cpp in Sources */, F76C865A1EC4E88300FA49E2 /* ServerList.cpp in Sources */, - F76C865C1EC4E88300FA49E2 /* TcpSocket.cpp in Sources */, + F76C865C1EC4E88300FA49E2 /* Socket.cpp in Sources */, C688784B202899B90084B384 /* Intro.cpp in Sources */, C68878FD20289B9B0084B384 /* MiniRollerCoaster.cpp in Sources */, 2A1F4FE0221FF4B0003CA045 /* Twitch.cpp in Sources */, @@ -4050,7 +4200,6 @@ C688790A20289B9B0084B384 /* WoodenRollerCoaster.cpp in Sources */, C688787220289A780084B384 /* MusicList.cpp in Sources */, 93F76F0220BFF77B00D4512C /* Paint.Surface.cpp in Sources */, - C6352B921F477032006CCEE3 /* GameActionCompat.cpp in Sources */, F76C871C1EC4E88400FA49E2 /* TrackDesignRepository.cpp in Sources */, C68878FA20289B9B0084B384 /* LoopingRollerCoaster.cpp in Sources */, C68878A720289B2A0084B384 /* Marketing.cpp in Sources */, @@ -4071,6 +4220,7 @@ C688790E20289B9B0084B384 /* CrookedHouse.cpp in Sources */, C68878F520289B9B0084B384 /* InvertedImpulseCoaster.cpp in Sources */, C688793020289B9B0084B384 /* LogFlume.cpp in Sources */, + 2ADE2F3222441905002598AF /* DiscordService.cpp in Sources */, C688786620289A430084B384 /* Intent.cpp in Sources */, C68878E520289B9B0084B384 /* Platform.Android.cpp in Sources */, C68878EA20289B9B0084B384 /* Shared.cpp in Sources */, @@ -4088,7 +4238,6 @@ C688792120289B9B0084B384 /* LaunchedFreefall.cpp in Sources */, C688791920289B9B0084B384 /* ObservationTower.cpp in Sources */, 93F76EF020BFF71700D4512C /* InteractiveConsole.cpp in Sources */, - C6352B901F477032006CCEE3 /* GameAction.cpp in Sources */, C6887853202899F00084B384 /* Sprite.cpp in Sources */, C688786F20289A6F0084B384 /* VehicleData.cpp in Sources */, C688786520289A400084B384 /* _legacy.cpp in Sources */, @@ -4287,6 +4436,7 @@ CLANG_WARN_UNREACHABLE_CODE = NO; CLANG_X86_VECTOR_INSTRUCTIONS = default; COMBINE_HIDPI_IMAGES = YES; + ENABLE_HARDENED_RUNTIME = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", @@ -4329,6 +4479,7 @@ CLANG_WARN_UNREACHABLE_CODE = NO; CLANG_X86_VECTOR_INSTRUCTIONS = default; COMBINE_HIDPI_IMAGES = YES; + ENABLE_HARDENED_RUNTIME = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", diff --git a/OpenRCT2.xcodeproj/xcshareddata/xcschemes/OpenRCT2.xcscheme b/OpenRCT2.xcodeproj/xcshareddata/xcschemes/OpenRCT2.xcscheme index 05ddd44286..91c482ea49 100644 --- a/OpenRCT2.xcodeproj/xcshareddata/xcschemes/OpenRCT2.xcscheme +++ b/OpenRCT2.xcodeproj/xcshareddata/xcschemes/OpenRCT2.xcscheme @@ -1,6 +1,6 @@ scripts\ps\appveyor_install.ps1 environment: OPENRCT2_ORG_TOKEN: - secure: leQX3xCQpmBLGuMqrxjFlzexDt96ypNRMM5TTRVHbGE8PwVg9crgeykLc2BIZU6HDHveJCHqh2cGMdHtHYJYcw== + secure: esyy5+5PRKZNYZ1hx1w/JpJGVwEC/YsJXnPp3cH98Yu7sW6/a03z/oJ1m9jhM/nDv5HL0swVK7pi9qQsN0utRg== BUILD_SERVER: AppVeyor PATH: C:\ProgramData\chocolatey\bin;$(PATH);C:\Program Files (x86)\Windows Kits\10\bin\x86;C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin install: - ps: >- .\scripts\ps\appveyor_install.ps1 platform: - - Win32 - x64 + - Win32 configuration: Release +before_build: +- ps: Set-Content -Path '.\resources\version.h' -Value "#define OPENRCT2_FILE_VERSION $($env:APPVEYOR_BUILD_VERSION -replace "\.", ",")" +- ps: Add-Content -Path '.\resources\version.h' -Value "#define OPENRCT2_PRODUCT_VERSION `"$($env:APPVEYOR_BUILD_VERSION)-$($env:APPVEYOR_REPO_COMMIT.Substring(0,10))`"" build: parallel: true project: openrct2.proj diff --git a/contributors.md b/contributors.md index 837d679d88..b0d4753f72 100644 --- a/contributors.md +++ b/contributors.md @@ -77,6 +77,7 @@ The following people are not part of the development team, but have been contrib * Albert Morgese (Fusxfaranto) - Shop auto-rotation, unicode uppercasing. * Olivier Wervers (oli414) - Remove unused objects command, various bugfixes * Christian Schubert (Osmodium) - Ensuring custom user content folders, incl. open folder. +* (Xkeeper0) - Improved banner tooltips; multiplayer status in toolbar. ## Bug fixes * (halfbro) @@ -104,6 +105,7 @@ The following people are not part of the development team, but have been contrib * (telk5093) * Ethan Smith (ethanhs) - Refactor MAX_PATH * Robert Lewicki (rlewicki) +* Liam Parker (elInfidel) * Tyler Ruckinger (TyPR124) * Justin Gottula (jgottula) * Seongsik Park (pss9205) @@ -116,6 +118,24 @@ The following people are not part of the development team, but have been contrib * Øystein Dale (oystedal) * Christian Schubert (Osmodium) * James Lord (RCTMASTA) +* Brian Massino (Nazey) +* Lauren Watson (lwatson2016) +* Jason Myre (jmyre1999) +* Nicole Wright (nicolewright) +* Josh Tucker (joshtucker132) +* Hussein Okasha (Hokasha2016) +* Brandon Dupree (Bdupree5) +* Zetao Ye (ZbrettonYe) +* Jordan Arevalos (Jarevalos2017) +* Florian Will (w-flo) +* Trevor Harkness (tharkne) +* Steve Xu (stevexu-umich) +* (aw20368) +* Jim Armstrong (41northstudios) +* Kenny Castro-Monroy (kennycastro007) +* Joseph Atkins-Turkish (Spacerat) +* Tulio Paschoalin Leao (tupaschoal) +* Denis Khabenkov (kodmord) ## Toolchain * (Balletie) - macOS @@ -125,6 +145,7 @@ The following people are not part of the development team, but have been contrib * Ted John (IntelOrca) - Windows * Michał Janiszewski (janisozaur) - Linux, Travis CI * Lewis Fox (LRFLEW) - macOS +* Andrew Rimpici (Andy608) - macOS ## Documentation * (honzi) @@ -151,12 +172,12 @@ The following people are not part of the development team, but have been contrib * Finnish - (DJHasis), (Zode), (TheWing) * French - (fbourigault), Joël Troch (JoelTroch), Michael Steenbeek (Gymnasiast), Romain Vigier (rvgr), (AziasYur), Hugo Courtial (s0r00t), David Delobel (incyclum), Nicolas Hawrysh (xp4xbox) * German - (danidoedel), (atmaxinger), (Yepoleb), Daniel Kessel (dkessel), Leon (AllGoodNamesAreTaken), (raidcookie) -* Italian - Luca Andrea Rossi (LucaRed) +* Italian - Luca Andrea Rossi (LucaRed), Precious Ugo Abara (48cfu) * Japanese - Aaron van Geffen (AaronVanGeffen), Nick Hall (nickhall), (jhako), Harry Lam (daihakken) * Korean - (telk5093), (NeverDruid); small fixes: (kexplo) * Norwegian - Hugo Wallenburg (Goddesen) * Polish - Adrian Wielgosik (adrian17), (lopezloo), Michał Janiszewski (janisozaur) -* Portuguese (BR) - (kaudy), (renansimoes) +* Portuguese (BR) - (kaudy), (renansimoes), Tulio Paschoalin Leao (tupaschoal) * Russian - (Soosisya) * Spanish - (mdtrooper), Josué Acevedo (Wirlie), Daniel Trujillo Viedma (gDanix); small fixes: (teapartycthulu) * Swedish - (Jinxit), (mharrys), (Slimeyo), Matte Andersson (Nubbie) diff --git a/data/language/ar-EG.txt b/data/language/ar-EG.txt index 27bda0920c..3bdc6a3f80 100644 --- a/data/language/ar-EG.txt +++ b/data/language/ar-EG.txt @@ -552,8 +552,8 @@ STR_1167 :...لا يمكن زيادة مستوي الماء هنا STR_1168 :الإعدادات STR_1169 :(لا شئ) STR_1170 :{STRING} -STR_1171 :{RED} مغلق - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED} مغلق +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK} ابني ممشي وخطوط إنتظار STR_1174 :Banner sign in the way STR_1175 :Can't build this on sloped footpath @@ -1109,7 +1109,7 @@ STR_1726 :الأرض ليست للبيع! STR_1727 :حقوق الإنشاء ليست للبيع! STR_1728 :Can't buy construction rights here... STR_1729 :الأرض ليست مملوكة للحديقة! -STR_1730 :{RED}مغلقة - - +STR_1730 :{RED}مغلقة STR_1731 :{WHITE}{STRINGID} - - STR_1732 :بناء STR_1733 :الوضع @@ -2240,19 +2240,19 @@ STR_2977 :Staff member name STR_2978 :Enter new name for this member of staff: STR_2979 :Can't name staff member... STR_2980 :Too many banners in game -STR_2981 :{RED}No entry - - +STR_2981 :{RED}لا دخول STR_2982 :Banner text STR_2983 :Enter new text for this banner: STR_2984 :Can't set new text for banner... -STR_2985 :Banner -STR_2986 :{SMALLFONT}{BLACK}Change text on banner +STR_2985 :يافطة +STR_2986 :{SMALLFONT}{BLACK}غير الكلام علي اليافطة STR_2987 :{SMALLFONT}{BLACK}Set this banner as a 'no-entry' sign for guests -STR_2988 :{SMALLFONT}{BLACK}Demolish this banner +STR_2988 :{SMALLFONT}{BLACK}دمر هذه اليافطة STR_2989 :{SMALLFONT}{BLACK}حدد اللون الأساسي STR_2990 :{SMALLFONT}{BLACK}حدد لون الكتابة STR_2991 :يافطة STR_2992 :الكلام علي اليافطة -STR_2993 :أدخل كلام جديد لكتابته علي اليافطة: +STR_2993 ::أدخل كلام جديد لكتابته علي اليافطة STR_2994 :{SMALLFONT}{BLACK}تغيير االكلام علي اليافطة STR_2995 :{SMALLFONT}{BLACK}تدمير هذه اليافطة STR_2996 :{BLACK}ABC @@ -2269,7 +2269,7 @@ STR_3006 :{PALEGOLD}ABC STR_3007 :{LIGHTPINK}ABC STR_3008 :{PEARLAQUA}ABC STR_3009 :{PALESILVER}ABC -STR_3010 :غير قادر علي تحميل الملف... +STR_3010 :...غير قادر علي تحميل الملف STR_3011 :الملف يحتوي علي بيانات خاطئة STR_3012 :Dodgems beat style STR_3013 :Fairground organ style @@ -2419,7 +2419,6 @@ STR_3190 :Path Extras STR_3191 :Scenery Groups STR_3192 :Park Entrance STR_3193 :Water -STR_3194 :Scenario Description STR_3195 :Invention List STR_3196 :{WINDOW_COLOUR_2}Research Group: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Items pre-invented at start of game: @@ -2519,7 +2518,7 @@ STR_3290 :بارد ومبتل STR_3291 :دافئ STR_3292 :حار وجاف STR_3293 :بارد -STR_3294 :تغيير... +STR_3294 :...تغيير STR_3295 :{SMALLFONT}{BLACK}Change name of park STR_3296 :{SMALLFONT}{BLACK}Change name of scenario STR_3297 :{SMALLFONT}{BLACK}Change detail notes about park / scenario @@ -3587,8 +3586,8 @@ STR_6125 :نوع العنصر STR_6126 :نوع غير معروف STR_6127 :{STRING}:الملف STR_6128 :.الملف لا يمكن تحملية لأن هناك بعض العناصر المُشار إليها فيه مفقودة أو ربما تكون متضررة. قائمة بهذه العناصر معروضة في الأسفل -STR_6129 :إنسخ المحدد إلي الحافظة -STR_6130 :إنسخ القائمة كاملة إلي الحافظة +STR_6129 :إنسخ +STR_6130 :إنسخ الكل STR_6131 :مصدر العنصر STR_6132 :تجاهل حالة التطوير STR_6133 :{SMALLFONT}{BLACK}Access rides and scenery that have not yet been invented @@ -3728,12 +3727,60 @@ STR_6270 :أسطح التضاريس STR_6271 :حواف التضاريس STR_6272 :المحطات STR_6273 :الموسيقي -STR_6274 :لا يمكن تحديد مخطط اللون... +STR_6274 :...لا يمكن تحديد مخطط اللون STR_6275 :{WINDOW_COLOUR_2}نمط المحطة -STR_6276 :{RED}{STRINGID} هل علق الزوار؟ ربما يكون هذا بسبب أفعوانية غير صالحة أو وضع تشغيل غير صالح. -STR_6277 :{WINDOW_COLOUR_2}فهرسة المحطة: {BLACK}{COMMA16} +STR_6276 :{RED}{STRINGID} .هل علق الزوار؟ ربما يكون هذا بسبب أفعوانية غير صالحة أو وضع تشغيل غير صالح +STR_6277 :{WINDOW_COLOUR_2} {BLACK}{COMMA16} :فهرسة المحطة STR_6278 :عدد ملفات الحفظ التلقائي STR_6279 :{SMALLFONT}{BLACK}عدد ملفات الحفظ التلقائي التي يجب إبقائها في المرة الواحدة +STR_6280 :{SMALLFONT}{BLACK}الدردشة +STR_6281 :{SMALLFONT}{BLACK}أظهر زر منفصل لنافذة الدردشة في شريط الأدوات +STR_6282 :الدردشة +STR_6283 :الدردشة غير متوفرة حالياً. هل أنت متصل بالسرفر؟ +STR_6284 :الشبكة +STR_6285 :معلومات الشبكة +STR_6286 :يستقبل +STR_6287 :يُرسل +STR_6288 :إجمالي المُستقبل +STR_6289 :إجمالي المُرسل +STR_6290 :البرتكول الأساسي +STR_6291 :الأوامر +STR_6292 :الخريطة +STR_6293 :بايت +STR_6294 :كيلو بايت +STR_6295 :ميجا بايت +STR_6296 :جيجا بايت +STR_6297 :تيرا بايت +STR_6298 :{STRING}/ثانية +STR_6299 :حمل الكل +STR_6300 :{SMALLFONT}{BLACK}.حمل كل العناصر المفقودة إذا كانت متوفرة علي الإنترنت +STR_6301 :{SMALLFONT}{BLACK}.إنسخ أسم العنصر المُحدد إلي الحافظة +STR_6302 :{SMALLFONT}{BLACK}.إنسخ قائمة العناصر المفقودة كلها إلي الحافظة +STR_6303 :({COMMA16} / {COMMA16}): [{STRING}] يُحمل العنصر +STR_6304 :إفتح مُحدد المشهد +STR_6305 :المعالجة المتعددة +STR_6306 :{SMALLFONT}{BLACK}.اعداد إختباري لإستخدام عدة معالجات لكي ترسم المشهد، ربما يسبب عدم الإتزان +STR_6307 :{BLACK}{STRINGID} :نمط اللون +STR_6309 :إعادة الإتصال +STR_6310 :{WINDOW_COLOUR_2} {BLACK}{INT32} {INT32} {INT32} :الموقع +STR_6311 :{WINDOW_COLOUR_2} {BLACK}{INT32} {INT32} {INT32} :التالي +STR_6312 :(السطح) +STR_6313 :({INT32} الإنحدار) +STR_6314 :{WINDOW_COLOUR_2}مسافة: {BLACK}{INT32}, {INT32} تفاوت {INT32} +STR_6315 :{WINDOW_COLOUR_2}هدف مُحدد المسار: {BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6316 :{WINDOW_COLOUR_2}تاريخ مُحدد المسار: +STR_6317 :{BLACK}{INT32}، {INT32}، {INT32} مسار {INT32} +STR_6318 :{STRING} :ملف السجل {NEWLINE}.تم إكتشاف إنفصال عن الشبكة +STR_6319 :{WINDOW_COLOUR_2}مكابح البلوك مغلقة +STR_6320 :{WINDOW_COLOUR_2}غير قابل للتدمير +STR_6321 :{WINDOW_COLOUR_2}الإضافة متضررة +STR_6322 :{WINDOW_COLOUR_2} {BLACK}{INT32} :رقم النقش المتحرك +STR_6323 :يُحاكي +STR_6324 :حاكي +STR_6325 :{SMALLFONT}{BLACK}حاكي الرحلة/المزار +STR_6326 :...{POP16}{POP16}{POP16}{STRINGID} لا يمكن محاكاة +STR_6327 :خلفية شفافة للقطات الشاشة العملاقة +STR_6328 :{SMALLFONT}{BLACK}.مع هذا الإعداد، صور لقطة الشاشة العملاقة ستحصل علي خلفية شفافة بدلاً من الخلفية السوداء الإفتراضية ############# # Scenarios # diff --git a/data/language/ca-ES.txt b/data/language/ca-ES.txt index 122d7909fd..ceedafe45b 100644 --- a/data/language/ca-ES.txt +++ b/data/language/ca-ES.txt @@ -552,8 +552,8 @@ STR_1167 :No es pot pujar el nivell d'aigua... STR_1168 :Opcions STR_1169 :(Cap) STR_1170 :{STRING} -STR_1171 :{RED}Tancada - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Tancada +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Construir camins i cues STR_1174 :Hi ha un cartell pel mig. STR_1175 :Això no es pot construir a sobre d'un pendent. @@ -1109,7 +1109,7 @@ STR_1726 :El terreny no està en venda! STR_1727 :Els drets de construcció no estan a la venda! STR_1728 :No se'n poden comprar els drets de construcció... STR_1729 :El terreny no és propietat del parc! -STR_1730 :{RED}Tancat - - +STR_1730 :{RED}Tancat STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Construeix STR_1733 :Mode @@ -2078,7 +2078,7 @@ STR_2739 :Cap STR_2740 :RollerCoaster Tycoon 1 STR_2741 :RollerCoaster Tycoon 2 STR_2742 :El fitxer «css50.dat» no s'ha trobat. -STR_2743 :Copieu «data/css17.dat» de la vostra instal·lació del RCT1 en «data/css50.dat» a la instal·lació del RCT2, o establiu correctament el camí del RCT1 a la pestanya d'altres opcions. +STR_2743 :Copieu «data/css17.dat» de la vostra instal·lació del RCT1 en «data/css50.dat» a la instal·lació del RCT2, o establiu correctament el camí del RCT1 a la pestanya «Avançat» de les opcions de configuració. STR_2744 :[ STR_2745 :\ STR_2746 :] @@ -2095,8 +2095,8 @@ STR_2756 :Neteja escombraries STR_2763 :??? STR_2765 :Molts visitants STR_2766 :Objectiu -STR_2767 :Manté el clima -STR_2768 :Clima canviant +STR_2767 :Mantén l'actual estat meteorològic +STR_2768 :Estableix una meteorologia canviant STR_2769 :Obre el parc STR_2770 :Tanca el parc STR_2773 :Finestra @@ -2240,7 +2240,7 @@ STR_2977 :Posa nom a l'empleat STR_2978 :Escriviu un nom per aquest empleat: STR_2979 :No es pot posar el nom a l'empleat... STR_2980 :Hi ha massa cartells a la partida. -STR_2981 :{RED}No passeu - - +STR_2981 :{RED}No passeu STR_2982 :Text del cartell STR_2983 :Escriviu el nou text del cartell: STR_2984 :No es pot establir aquest text per al cartell... @@ -2333,18 +2333,6 @@ STR_3074 :{RED}Avís: La valoració del parc continua per sota de 700!{NEWLIN STR_3075 :{RED}Avís: La valoració del parc continua per sota de 700!{NEWLINE}Només us queden 2 setmanes per millorar-la o clausuraran el parc. STR_3076 :{RED}Últim avís: La valoració del parc continua per sota de 700!{NEWLINE}En set dies, es clausurarà el parc si la valoració no millora suficientment. STR_3077 :{RED}Avís de tancament: El parc tanca definitivament les seves portes! -STR_3078 :Estil senzill -STR_3079 :De fusta -STR_3080 :Carpa de circ -STR_3081 :Castell gris -STR_3082 :Castell marró -STR_3083 :Estil selvàtic -STR_3084 :Cabana de fusta -STR_3085 :Estil clàssic/romà -STR_3086 :Estil abstracte -STR_3087 :Amb gel i neu -STR_3088 :Estil pagoda -STR_3089 :Estil espacial STR_3090 :{SMALLFONT}{BLACK}Trieu el tipus d'entrada, de sortida i d'estació. STR_3091 :No teniu permís per treure aquesta secció! STR_3092 :No teniu permís per moure o modificar l'estació d'aquesta atracció! @@ -2431,7 +2419,6 @@ STR_3190 :Extres dels camins STR_3191 :Grups de decoracions STR_3192 :Entrada al parc STR_3193 :Aigua -STR_3194 :Descripció de l'escenari STR_3195 :Llista de recerques STR_3196 :{WINDOW_COLOUR_2}Grup de recerca: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Elements ja recercats a l'inici de la partida: @@ -2973,7 +2960,7 @@ STR_5461 :Estableix els paràmetres dels visitants STR_5462 :{CURRENCY} STR_5463 :Objectiu: Divertiu-vos! STR_5464 :General -STR_5465 :Clima +STR_5465 :Estat meteorològic STR_5466 :Empleats STR_5467 :ALT + STR_5468 :Missatges recents @@ -3126,7 +3113,7 @@ STR_5616 :{SMALLFONT}{BLACK}Últim element per bandera de casella. STR_5617 :{SMALLFONT}{BLACK}Mou l'element seleccionat amunt. STR_5618 :{SMALLFONT}{BLACK}Mou l'element seleccionat avall. STR_5619 :RollerCoaster Tycoon -STR_5620 :Atraccions afegides +STR_5620 :Added attractions STR_5621 :Loopy Landscapes STR_5622 :RollerCoaster Tycoon 2 STR_5623 :Wacky Worlds @@ -3198,7 +3185,7 @@ STR_5721 :Ennuvolat STR_5722 :Pluja STR_5723 :Pluja forta STR_5724 :Tempesta -STR_5725 :{BLACK}Força el clima: +STR_5725 :{BLACK}Força l'estat meteorològic: STR_5726 :{SMALLFONT}{BLACK}Escolliu el temps que farà al parc. STR_5727 :Qualitat d'escalat: STR_5728 :Requereix l'opció de renderitzat amb maquinari @@ -3599,8 +3586,8 @@ STR_6125 :Tipus d'objecte STR_6126 :Tipus desconegut STR_6127 :Arxiu: {STRING} STR_6128 :L'arxiu no s'ha pogut carregar ja que està danyat o bé els objectes referenciats són desconeguts. Se'n dóna una llista a continuació. -STR_6129 :Copia l'objecte seleccionat al porta-retalls -STR_6130 :Copia la llista sencera al porta-retalls +STR_6129 :Copia +STR_6130 :Copia'ls tots STR_6131 :Origen de l'objecte STR_6132 :Ignora recerques pendents STR_6133 :{SMALLFONT}{BLACK}Permet l'accés a atraccions i decoracions que encara no s'han recercat. @@ -3738,6 +3725,67 @@ STR_6264 :Empra sempre l'explorador d'arxius del sistema STR_6265 :{SMALLFONT}{BLACK}Quan està activat, s'emprarà l'explorador d'arxius del vostre sistema, en lloc del de l'OpenRCT2. STR_6266 :Obre la carpeta de contingut personalitzat STR_6267 :Obre l'inspecció de casella +STR_6268 :Avança un tic +STR_6269 :ID d'estat meteorològic no vàlid +STR_6270 :Superfícies del terreny +STR_6271 :Vores i separadors de caselles +STR_6272 :Estacions +STR_6273 :Música +STR_6274 :No es poden establir els colors... +STR_6275 :{WINDOW_COLOUR_2}Estil d'estació: +STR_6276 :{RED}Hi ha visitants bloquejats en {STRINGID}, probablement a causa d'un tipus d'atracció no vàlid o un mode d'operació incorrecte. +STR_6277 :{WINDOW_COLOUR_2}Índex d'estació: {BLACK}{COMMA16} +STR_6278 :Nombre de desades automàtiques: +STR_6279 :{SMALLFONT}{BLACK}Nombre de desades automàtiques que es mantindran. +STR_6280 :{SMALLFONT}{BLACK}Xat +STR_6281 :{SMALLFONT}{BLACK}Mostra un botó per la finestra del xat a la barra d'eines. +STR_6282 :Xat +STR_6283 :Ara mateix el xat no està disponible. Esteu connectats al servidor? +STR_6284 :Xarxa +STR_6285 :Informació de la xarxa +STR_6286 :Rebut +STR_6287 :Enviat +STR_6288 :Total rebut +STR_6289 :Total enviat +STR_6290 :Protocol base +STR_6291 :Ordres +STR_6292 :Mapa +STR_6293 :B +STR_6294 :KiB +STR_6295 :MiB +STR_6296 :GiB +STR_6297 :TiB +STR_6298 :{STRING}/seg +STR_6299 :Descarrega-ho tot +STR_6300 :{SMALLFONT}{BLACK}Descarrega tots els elements que falten si estan disponibles en línia. +STR_6301 :{SMALLFONT}{BLACK}Copia el nom de l'element seleccionat al porta-retalls. +STR_6302 :{SMALLFONT}{BLACK}Copia la llista completa d'elements que falten al porta-retalls. +STR_6303 :Descarregant element ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :Obre el seleccionador de decoracions +STR_6305 :Multifil +STR_6306 :{SMALLFONT}{BLACK}Opció experimental per usar diferents fils per renderitzar. Pot causar problemes durant les partides. +STR_6307 :Colors: {BLACK}{STRINGID} +STR_6308 :«{STRINGID}{OUTLINE}{TOPAZ}»{NEWLINE}{STRINGID} +STR_6309 :Reconnecta +STR_6310 :{WINDOW_COLOUR_2}Posició: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Propera: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(superfície) +STR_6313 :(pendent {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Destinació: {BLACK}{INT32}, {INT32} tolerància {INT32} +STR_6315 :{WINDOW_COLOUR_2}Objectiu de l'encaminador: {BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6316 :{WINDOW_COLOUR_2}Historial de l'encaminador: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6318 :S'ha detectat una dessincronització amb la xarxa.{NEWLINE}Fitxer de registre: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Frens de bloc tancats +STR_6320 :{WINDOW_COLOUR_2}Indestructible +STR_6321 :{WINDOW_COLOUR_2}L'adició està trencada +STR_6322 :{WINDOW_COLOUR_2}ID de l'sprite: {BLACK}{INT32} +STR_6323 :S'està simulant +STR_6324 :Simula +STR_6325 :{SMALLFONT}{BLACK}Simula l'atracció +STR_6326 :No es pot simular {POP16}{POP16}{POP16}{STRINGID}... +STR_6327 :Fons transparent per captures de pantalla gegants +STR_6328 :{SMALLFONT}{BLACK}Si s'activa, les captures de pantalla gegants tindran un fons transparent en lloc del color negre per defecte. ############# # Scenarios # diff --git a/data/language/cs-CZ.txt b/data/language/cs-CZ.txt index d45d00f869..1f5b106519 100644 --- a/data/language/cs-CZ.txt +++ b/data/language/cs-CZ.txt @@ -552,8 +552,8 @@ STR_1167 :Tady nelze zvýšit hladinu vody... STR_1168 :Nastavení STR_1169 :(None) STR_1170 :{STRING} -STR_1171 :{RED}Zavřeno - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Zavřeno +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Nástroj pro cesty a fronty STR_1174 :Banner v cestě STR_1175 :Toto nelze postavit na strmé cestě @@ -1111,8 +1111,8 @@ STR_1726 :Pozemek není na prodej! STR_1727 :Stavební práva nejsou na prodej! STR_1728 :Tyto stavební práva nelze zakoupit... STR_1729 :Pozemek není ve vlastnictví parku! -STR_1730 :{RED}Zavřeno - - -STR_1731 :{WHITE}{STRINGID} - - +STR_1730 :{RED}Zavřeno +STR_1731 :{WHITE}{STRINGID} STR_1732 :Stavět STR_1733 :Mód STR_1734 :{WINDOW_COLOUR_2}Počet kol: @@ -2242,7 +2242,7 @@ STR_2977 :Přejmenovat zaměstnance STR_2978 :Zadejte nové jméno tohoto zaměstnance: STR_2979 :Nelze přejmenovat zaměstnance... STR_2980 :Příliš mnoho bannerů ve hře -STR_2981 :{RED}Zákaz vstupu - - +STR_2981 :{RED}Zákaz vstupu STR_2982 :Text banneru STR_2983 :Zadejte nový text banneru: STR_2984 :Nelze změnit text banneru... @@ -2433,7 +2433,6 @@ STR_3190 :Doplňky cest STR_3191 :Skupiny kulis STR_3192 :Vstup do parku STR_3193 :Voda -STR_3194 :Popis scénáře STR_3195 :Seznam výzkumů STR_3196 :{WINDOW_COLOUR_2}Skupina výzkumu: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Položky vyzkoumané před začátkem hry: @@ -3236,7 +3235,7 @@ STR_5762 :Čínský jüan (CN¥) STR_5763 :Všechny soubory STR_5764 :Neplatný typ atrakce STR_5765 :Nelze upravovat atrakce neplatného typu -STR_5766 : +STR_5766 :Maďarský Forint (Ft) STR_5767 :Příjem STR_5768 :Celkem zákazníků STR_5769 :Celkový zisk @@ -3772,6 +3771,35 @@ STR_6300 :{SMALLFONT}{BLACK}Stáhnout všechny chybějící objekty dostupné STR_6301 :{SMALLFONT}{BLACK}Kopírovat název vybraného objektu do schránky. STR_6302 :{SMALLFONT}{BLACK}Kopírovat seznam objektů do schránky STR_6303 :Stahování objektu ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :Otevřít výběr kulis +STR_6305 :Multithreading +STR_6306 :{SMALLFONT}{BLACK}Experimentální podpora více vláken pro vykreslování obrazu, může způsobit nestabilitu. +STR_6307 :Barevné schéma: {BLACK}{STRINGID} +STR_6308 :“{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :Znovu připojit +STR_6310 :{WINDOW_COLOUR_2}Pozice: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Další: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(povrch) +STR_6313 :(sklon {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Cíl: {BLACK}{INT32}, {INT32} tolerance {INT32} +STR_6315 :{WINDOW_COLOUR_2}Cíl hledání cesty: {BLACK}{INT32}, {INT32}, {INT32} směr {INT32} +STR_6316 :{WINDOW_COLOUR_2}Historie hledání cest: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} směr {INT32} +STR_6318 :Detekována síťová desynchronizace.{NEWLINE}Log: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Uzavřený brzdný blok +STR_6320 :{WINDOW_COLOUR_2}Nezničitelné +STR_6321 :{WINDOW_COLOUR_2}Rozbitá část +STR_6322 :{WINDOW_COLOUR_2}Sprite Id: {BLACK}{INT32} +STR_6323 :Probíhá simulace +STR_6324 :Simulovat +STR_6325 :{SMALLFONT}{BLACK}Simulovat atrakci +STR_6326 :Nelze simulovat {POP16}{POP16}{POP16}{STRINGID}... +STR_6327 :Průhledné pozadí pro velké screenshoty +STR_6328 :{SMALLFONT}{BLACK}Velké snímky obrazovky budou mít průhledné pozadí namísto výchozí černé barvy. +STR_6329 :{STRING}{STRINGID} +STR_6330 :Stahování [{STRING}] od {STRING} ({COMMA16} / {COMMA16}) +STR_6331 :Přivolat kachny +STR_6332 :Zmizet kachny ############################################################################### ## RCT2 Scenarios diff --git a/data/language/da-DK.txt b/data/language/da-DK.txt index 62006072f7..1121e18500 100644 --- a/data/language/da-DK.txt +++ b/data/language/da-DK.txt @@ -552,8 +552,8 @@ STR_1167 :Kan ikke hæve vand her... STR_1168 :Indstillinger STR_1169 :(ingen) STR_1170 :{STRING} -STR_1171 :{RED}Lukket - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Lukket +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Byg stier og kø linjer STR_1174 :Banner skilt i vejen STR_1175 :Kan ikke bygge dette på en skrå sti @@ -1110,7 +1110,7 @@ STR_1726 :Land er ikke til salg! STR_1727 :Byggetilladelse er ikke til salg! STR_1728 :Kan ikke købe byggetilladelse her... STR_1729 :Land ikke ejet af parken! -STR_1730 :{RED}Lukket - - +STR_1730 :{RED}Lukket STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Byg STR_1733 :Tilstand @@ -2242,7 +2242,7 @@ STR_2977 :Navn på ansat STR_2978 :Angiv et navn for denne ansat: STR_2979 :Kan ikke navngive ansat... STR_2980 :For mange bannere i spillet -STR_2981 :{RED}Ingen indhold - - +STR_2981 :{RED}Ingen indhold STR_2982 :Banner tekst STR_2983 :Angiv ny tekst på dette banner: STR_2984 :Kan ikke sætte ny tekst på banner... @@ -3237,7 +3237,7 @@ STR_5762 :Chinese Yuan (CN¥) STR_5763 :Alle filer STR_5764 :Ugyldig forlystelses type STR_5765 :Kan ikke redigere ugyldige forlystelses typer -STR_5766 : +STR_5766 :Ungarsk Forint (Ft) STR_5767 :Indkomst STR_5768 :Total kunder STR_5769 :Total profit @@ -3774,6 +3774,35 @@ STR_6300 :{SMALLFONT}{BLACK}Hent alle manglende objekter hvis de er tilgænge STR_6301 :{SMALLFONT}{BLACK}Kopier det valgte objekt til udklipsholder. STR_6302 :{SMALLFONT}{BLACK}Kopier hele listen af manglende objekter til udklipsholderen. STR_6303 :Henter objekt ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :Åben scenarie vælger +STR_6305 :Multi-tråde +STR_6306 :{SMALLFONT}{BLACK}Eksperimentel indstilling, brug flere processor kerner til at gengive spillet, kan påvirke stabiliteten. +STR_6307 :Farve tema: {BLACK}{STRINGID} +STR_6308 :“{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :Forbind igen +STR_6310 :{WINDOW_COLOUR_2}Position: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Næste: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(overflade) +STR_6313 :(skråning {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Dest: {BLACK}{INT32}, {INT32} tolerance {INT32} +STR_6315 :{WINDOW_COLOUR_2}Stisøger Goal: {BLACK}{INT32}, {INT32}, {INT32} ret {INT32} +STR_6316 :{WINDOW_COLOUR_2}Stisøger historie: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} ret {INT32} +STR_6318 :Netværks synkroniseringsfejl.{NEWLINE}Log fil: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Blok Bremse tilkoblet +STR_6320 :{WINDOW_COLOUR_2}Uforgængelig +STR_6321 :{WINDOW_COLOUR_2}Tilføjelse defekt +STR_6322 :{WINDOW_COLOUR_2}Sprite Id: {BLACK}{INT32} +STR_6323 :Simulérer +STR_6324 :Simulér +STR_6325 :{SMALLFONT}{BLACK}Simulér forlystelse/attraktion +STR_6326 :Kan ikke simulére {POP16}{POP16}{POP16}{STRINGID}... +STR_6327 :Transparent baggrund for gigantisk Skærmbillede +STR_6328 :{SMALLFONT}{BLACK}Med denne indstilling aktiveret, vil gigantisk skærmbillede have transparent baggrund i stedet for en standard sort baggrund. +STR_6329 :{STRING}{STRINGID} +STR_6330 :Henter [{STRING}] fra {STRING} ({COMMA16} / {COMMA16}) +STR_6331 :Anbring ænder +STR_6332 :Fjern ænder ############# # Scenarios # @@ -3894,152 +3923,152 @@ STR_DTLS :Kun for sjov! STR_SCNR :Whispering Cliffs STR_PARK :Whispering Cliffs -STR_DTLS :Develop the seaside cliffs into a thriving amusement park +STR_DTLS :Udvikel denne klippe ud mod havet, til en blomstrende forlystelsespark STR_SCNR :Three Monkeys Park STR_PARK :Three Monkeys Park -STR_DTLS :Central to this large developing park is a giant triple-track racing/duelling steel coaster +STR_DTLS :Centralt i denne store udviklingspark, er en kæmpe tredobbelt-Track Racing/Duelling stål rutchebane STR_SCNR :Canary Mines STR_PARK :Canary Mines -STR_DTLS :This abandoned mine already has the makings of a tourist attraction with its miniature railway and a pair of vertical drop rutschebaner +STR_DTLS :Denne forladte mine er allerede en turistattraktion med sin miniature jernbane og et par lodrette fald rutschebaner STR_SCNR :Barony Bridge STR_PARK :Barony Bridge -STR_DTLS :An old redundant bridge is yours to develop into an amusement park +STR_DTLS :Få denne gamle udtjente bro, til at udvikle sig til en forlystelsespark STR_SCNR :Funtopia STR_PARK :Funtopia -STR_DTLS :Covering land both sides of a highway, this park has several forlystelser already operating +STR_DTLS :Med land begge sider af en motorvej, har denne park allerede flere fungerende forlystelser STR_SCNR :Haunted Harbour STR_PARK :Haunted Harbour -STR_DTLS :The local authority has agreed to sell nearby land cheaply to this small seaside park, on the condition that certain forlystelser are preserved +STR_DTLS :De lokale myndigheder har indvilliget i at sælge nærliggende land billigt, til denne lille kystpark, på betingelse af, at visse forlystelser bliver bevaret STR_SCNR :Fun Fortress STR_PARK :Fun Fortress -STR_DTLS :This castle is all yours to turn into a theme park +STR_DTLS :Denne borg, kan du forvandle til en forlystelspark STR_SCNR :Future World STR_PARK :Future World -STR_DTLS :This futuristic park has plenty of space for new forlystelser on its alien landscape +STR_DTLS :Denne futuristiske forlystelsespark, har masser af plads til nye forlystelser på sit fremmede landskab STR_SCNR :Gentle Glen STR_PARK :Gentle Glen -STR_DTLS :The local population prefer gentle and relaxing forlystelser, so it is your job to expand this park to suit their tastes +STR_DTLS :Den lokale befolkning foretrækker blide og afslappende forlystelser, så det er dit job at udvide denne forlystelsespark, så den passer til deres smag STR_SCNR :Jolly Jungle STR_PARK :Jolly Jungle -STR_DTLS :Deep in the jungle lies a large area of land ready to be turned into a theme park +STR_DTLS :Dybt inde i junglen ligger et stort landområde, klar til at blive omdannet til en forlystelsespark STR_SCNR :Hydro Hills STR_PARK :Hydro Hills -STR_DTLS :A series of stepped lakes form the basis for this new park +STR_DTLS :En række forskudte søer, danner grundlaget for denne nye forlystelsespark STR_SCNR :Sprightly Park STR_PARK :Sprightly Park -STR_DTLS :This elderly park has many historical forlystelser but is badly in debt +STR_DTLS :Denne aldrene Park har mange historiske forlystelser, men er forgældet STR_SCNR :Magic Quarters STR_PARK :Magic Quarters -STR_DTLS :A large area of land has been cleared and partially themed ready for you to develop into a landscaped theme park +STR_DTLS :Et stort område af jord er blevet ryddet og delvist tematiseret, klar til at udvikle sig til en anlagt forlystelsespark STR_SCNR :Fruit Farm STR_PARK :Fruit Farm -STR_DTLS :A thriving fruit farm has built a railroad to boost its income, your job is to develop it into a full-blown amusement park +STR_DTLS :En blomstrende frugt gård har bygget en jernbane for at øge sin indkomst, dit job er at udvikle det til en lækker forlystelsespark STR_SCNR :Butterfly Dam STR_PARK :Butterfly Dam -STR_DTLS :The area around a dam is available for you to develop into an amusement park +STR_DTLS :Området omkring en dæmning er til rådighed for dig, til at udvikle en forlystelsespark STR_SCNR :Coaster Canyon STR_PARK :Coaster Canyon -STR_DTLS :A vast canyon is yours to turn into a theme park +STR_DTLS :En stor kløft er din, til at forvandle den til en forlystelsespark STR_SCNR :Thunderstorm Park STR_PARK :Thunderstorm Park -STR_DTLS :The weather is so wet here that a giant pyramid has been built to allow some forlystelser to be built under cover +STR_DTLS :Vejret er så vådt her, at der er bygget en gigantisk pyramide, for at nogle forlystelser kan blive bygget under den. STR_SCNR :Harmonic Hills STR_PARK :Harmonic Hills -STR_DTLS :The local authority won't allow you to build above tree height in this park +STR_DTLS :Den lokale myndighed vil ikke tillade dig at bygge over træ højde i denne forlystelsespark STR_SCNR :Roman Village STR_PARK :Roman Village -STR_DTLS :Develop this Roman-themed park by adding forlystelser and rutschebaner +STR_DTLS :Udvikl denne forlystelsespark med et romersk tema, ved at tilføje forlystelser og rutschebaner STR_SCNR :Swamp Cove STR_PARK :Swamp Cove -STR_DTLS :Built partly on a series of small islands, this park already has a pair of large rutschebaner as its centrepiece +STR_DTLS :Bygget delvist på en række små øer, har denne forlystelsespark allerede et par store rutschebaner som sit centrum STR_SCNR :Adrenaline Heights STR_PARK :Adrenaline Heights -STR_DTLS :Build a park to appeal to the high-intensity thrill-seeking local people +STR_DTLS :Byg en Park for at appellere til den højintensive, gys-søgende, lokale folk STR_SCNR :Utopia Park STR_PARK :Utopia Park -STR_DTLS :An oasis in the middle of the desert provides an unusual opportunity to build an amusement park +STR_DTLS :En oase midt i ørkenen giver en usædvanlig mulighed for at bygge en forlystelsespark STR_SCNR :Rotting Heights STR_PARK :Rotting Heights -STR_DTLS :Overgrown and dilapidated, can you resurrect this once-great amusement park? +STR_DTLS :Overgroet og nedslidt, kan du genoplive denne en gang store forlystelsespark? STR_SCNR :Fiasco Forest STR_PARK :Fiasco Forest -STR_DTLS :Full of badly designed and dangerous forlystelser, you have a very limited budget and time to fix the problems and turn the park around +STR_DTLS :Fuld af dårligt designede og farlige forlystelser, har du et meget begrænset budget og tid, til at løse problemerne og vende parken rundt STR_SCNR :Pickle Park STR_PARK :Pickle Park -STR_DTLS :The local authority will not allow any kind of advertising or promotion, so this park must succeed by reputation only +STR_DTLS :De lokale myndigheder vil ikke tillade nogen form for reklame eller forfremmelse, så denne forlystelsespark skal lykkes udelukkende på omdømme STR_SCNR :Giggle Downs STR_PARK :Giggle Downs -STR_DTLS :A four lane steeplechase ride is the centrepiece of this expanding park +STR_DTLS :En fire sporet hestevæddeløbs bane er kernen i denne ekspanderende forlystelsespark STR_SCNR :Mineral Park STR_PARK :Mineral Park -STR_DTLS :Turn this abandoned stone quarry into a place to attract thrill-seeking tourists +STR_DTLS :Forvandel dette forladte stenbrud, til et sted til at tiltrække gys-søgende turister STR_SCNR :Coaster Crazy STR_PARK :Coaster Crazy -STR_DTLS :You have limited funds but unlimited time to turn this mountainside area into a vast rutschebane park +STR_DTLS :YDu har begrænsede midler, men ubegrænset tid til at vende dette bjergområde til en stor rutschebane forlystelsespark STR_SCNR :Urban Park STR_PARK :Urban Park -STR_DTLS :A tiny park has done a deal with the nearby town to allow expansion through the town itself +STR_DTLS :En lille forlystelsespark har lavet en aftale, med den nærliggende by, for at tillade ekspansion, gennem selve byen STR_SCNR :Geoffrey Gardens STR_PARK :Geoffrey Gardens -STR_DTLS :A large garden park needs turning into a thriving theme park +STR_DTLS :En stor have park skal forvandles til en blomstrende forlystelsespark ## Loopy Landscapes diff --git a/data/language/de-DE.txt b/data/language/de-DE.txt index 6e1b49cbe1..ebd53a37a3 100644 --- a/data/language/de-DE.txt +++ b/data/language/de-DE.txt @@ -551,8 +551,8 @@ STR_1167 :Wasserspiegel kann hier nicht erhöht werden... STR_1168 :Optionen STR_1169 :(Keine) STR_1170 :{STRING} -STR_1171 :{RED}Geschlossen - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Geschlossen +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Fußwege und Warteschlangenreihen anlegen STR_1174 :Banner im Weg STR_1175 :Kann nicht auf Fußweg mit Neigung angelegt werden @@ -1109,7 +1109,7 @@ STR_1726 :Land nicht zum Verkauf! STR_1727 :Baurechte nicht zum Verkauf! STR_1728 :Erwerb von Baurechten hier nicht möglich... STR_1729 :Land gehört nicht dem Park! -STR_1730 :{RED}Geschlossen - - +STR_1730 :{RED}Geschlossen STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Bauen STR_1733 :Modus @@ -1150,7 +1150,7 @@ STR_1770 :{SMALLFONT}{BLACK}Anzahl kompletter Schwünge STR_1771 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} STR_1773 :Nur ein Fahrtfoto-Bereich pro Bahn erlaubt STR_1774 :Nur ein Kabellifthügel pro Bahn erlaubt -STR_1777 :{WINDOW_COLOUR_2}Attraktionsmusik +STR_1777 :Attraktionsmusik STR_1778 :{STRINGID} - - STR_1779 :{INLINE_SPRITE}{254}{19}{00}{00} Pandakostüm STR_1780 :{INLINE_SPRITE}{255}{19}{00}{00} Tigerkostüm @@ -1873,7 +1873,7 @@ STR_2533 :Rücktaste STR_2534 :Tab STR_2535 :??? STR_2536 :??? -STR_2537 :Entf +STR_2537 :Clear STR_2538 :Eingabetaste STR_2539 :??? STR_2540 :??? @@ -1897,17 +1897,17 @@ STR_2557 :Leertaste STR_2558 :Bild auf STR_2559 :Bild ab STR_2560 :Ende -STR_2561 :Pos 1 +STR_2561 :Pos1 STR_2562 :Links -STR_2563 :Oben +STR_2563 :Rauf STR_2564 :Rechts -STR_2565 :Unten -STR_2566 :Auswählen +STR_2565 :Runter +STR_2566 :Select STR_2567 :Drucken STR_2568 :Ausführen STR_2569 :Schnappschuss STR_2570 :Einfügen -STR_2571 :Löschen +STR_2571 :Entfernen STR_2572 :Hilfe STR_2573 :0 STR_2574 :1 @@ -1957,22 +1957,22 @@ STR_2617 :??? STR_2618 :Menü STR_2619 :??? STR_2620 :??? -STR_2621 :Zehnertastatur 0 -STR_2622 :Zehnertastatur 1 -STR_2623 :Zehnertastatur 2 -STR_2624 :Zehnertastatur 3 -STR_2625 :Zehnertastatur 4 -STR_2626 :Zehnertastatur 5 -STR_2627 :Zehnertastatur 6 -STR_2628 :Zehnertastatur 7 -STR_2629 :Zehnertastatur 8 -STR_2630 :Zehnertastatur 9 -STR_2631 :Zehnertastatur * -STR_2632 :Zehnertastatur + +STR_2621 :Ziffernblock 0 +STR_2622 :Ziffernblock 1 +STR_2623 :Ziffernblock 2 +STR_2624 :Ziffernblock 3 +STR_2625 :Ziffernblock 4 +STR_2626 :Ziffernblock 5 +STR_2627 :Ziffernblock 6 +STR_2628 :Ziffernblock 7 +STR_2629 :Ziffernblock 8 +STR_2630 :Ziffernblock 9 +STR_2631 :Ziffernblock * +STR_2632 :Ziffernblock + STR_2633 :??? -STR_2634 :Zehnertastatur - -STR_2635 :Zehnertastatur . -STR_2636 :Zehnertastatur / +STR_2634 :Ziffernblock - +STR_2635 :Ziffernblock . +STR_2636 :Ziffernblock / STR_2637 :F1 STR_2638 :F2 STR_2639 :F3 @@ -2005,7 +2005,7 @@ STR_2665 :??? STR_2666 :??? STR_2667 :??? STR_2668 :??? -STR_2669 :Num +STR_2669 :Numlock STR_2670 :Rollen STR_2671 :??? STR_2672 :??? @@ -2019,7 +2019,7 @@ STR_2679 :??? STR_2680 :Alle Forschungen beendet STR_2681 :{MEDIUMFONT}{BLACK}Erhöht Ihren Kontostand um {CURRENCY} STR_2684 :{SMALLFONT}{BLACK}Eine große Gruppe von Besuchern tritt ein -STR_2685 :„Simplex Noise“ Param. +STR_2685 :Simplexrauschparameter STR_2686 :Niedrig: STR_2687 :Hoch: STR_2688 :Basisfrequenz: @@ -2034,7 +2034,7 @@ STR_2696 :Bäume platzieren STR_2697 :??? STR_2698 :??? STR_2699 :??? -STR_2700 :Autosave-Frequenz: +STR_2700 :Autosavefrequenz: STR_2701 :Jede Minute STR_2702 :Alle 5 Minuten STR_2703 :Alle 15 Minuten @@ -2077,7 +2077,7 @@ STR_2739 :Keine STR_2740 :RollerCoaster Tycoon 1 STR_2741 :RollerCoaster Tycoon 2 STR_2742 :css50.dat nicht gefunden -STR_2743 :Kopieren Sie „data/css17.dat“ aus Ihrem RCT1-Verzeichnis nach „data/css50.dat“ in Ihrem RCT2-Verzeichnis oder versichern Sie sich, dass der Pfad zur RCT1-Installation in den Optionen korrekt angegeben ist. +STR_2743 :Kopieren Sie „data/css17.dat“ aus Ihrem RCT1-Verzeichnis nach „data/css50.dat“ in Ihrem RCT2-Verzeichnis oder versichern Sie sich, dass der Pfad zur RCT1-Installation im Tab „Erweitert“ korrekt angegeben ist. STR_2744 :[ STR_2745 :\ STR_2746 :] @@ -2105,11 +2105,11 @@ STR_2776 :Sprache: STR_2777 :{MOVE_X}{SMALLFONT}{STRING} STR_2778 :»{MOVE_X}{SMALLFONT}{STRING} STR_2779 :Ansichtsfenster {COMMA16} -STR_2780 :Extra Ansichtsfenster +STR_2780 :Zusatz-Ansichtsfenster # End of new strings -STR_2781 :{STRINGID}:{MOVE_X}{195}{STRINGID} -STR_2782 :UMSCHALT + -STR_2783 :STRG + +STR_2781 :{STRINGID}:{MOVE_X}{255}{STRINGID} +STR_2782 :UMSCHALT + +STR_2783 :STRG + STR_2784 :Tastaturkürzel ändern STR_2785 :{WINDOW_COLOUR_2}Drücken Sie die neue Taste für:{NEWLINE}„{STRINGID}“ STR_2786 :{SMALLFONT}{BLACK}Klicken Sie auf die Kürzelbeschreibung, um eine neue Taste auszuwählen @@ -2222,7 +2222,7 @@ STR_2896 :{WINDOW_COLOUR_2}Hypothermia: (Allister Brimble) Copyright © Chr STR_2897 :{WINDOW_COLOUR_2}Last Sleigh Ride: (Allister Brimble) Copyright © Chris Sawyer STR_2898 :{WINDOW_COLOUR_2}Pipes of Glencairn: (Allister Brimble) Copyright © Chris Sawyer STR_2899 :{WINDOW_COLOUR_2}Traffic Jam: (Allister Brimble) Copyright © Chris Sawyer -STR_2901 :{WINDOW_COLOUR_2}(Auszüge mit freundlicher Genehmigung von Spectrasonics “Liquid Grooves“) +STR_2901 :{WINDOW_COLOUR_2}(Auszüge mit freundlicher Genehmigung von Spectrasonics „Liquid Grooves“) STR_2902 :{WINDOW_COLOUR_2}Toccata: (C.M.Widor, gespielt von Peter James Adcock) Aufnahme © Chris Sawyer STR_2903 :{WINDOW_COLOUR_2}Space Rock: (Allister Brimble) Copyright © Chris Sawyer STR_2904 :{WINDOW_COLOUR_2}Manic Mechanic: (Allister Brimble) Copyright © Chris Sawyer @@ -2239,7 +2239,7 @@ STR_2977 :Mitarbeitername STR_2978 :Neuen Namen für diesen Mitarbeiter eingeben: STR_2979 :Dieser Mitarbeiter kann nicht benannt werden... STR_2980 :Zu viele Banner im Spiel -STR_2981 :{RED}Kein Zutritt - - +STR_2981 :{RED}Kein Zutritt STR_2982 :Bannertext STR_2983 :Neuen Text für dieses Banner eingeben: STR_2984 :Neuer Text für das Banner kann nicht erstellt werden... @@ -2332,18 +2332,6 @@ STR_3074 :{RED}WARNUNG: Ihre Parkbewertung liegt immer noch unter dem Wert 70 STR_3075 :{RED}WARNUNG: Ihre Parkbewertung liegt immer noch unter dem Wert 700!{NEWLINE}Sie haben noch 2 Wochen Zeit, um die Parkbewertung zu erhöhen, oder Ihr Park wird geschlossen STR_3076 :{RED}LETZTE WARNUNG: Ihre Parkbewertung liegt immer noch unter dem Wert 700!{NEWLINE}In 7 Tagen wird Ihr Park geschlossen, es sei denn, Sie können die Bewertung erhöhen STR_3077 :{RED}SCHLIESSUNGSNACHRICHT: Ihr Park wurde geschlossen! -STR_3078 :Normaler Eingang -STR_3079 :Holzeingang -STR_3080 :Zelteingang -STR_3081 :Burgeingang (grau) -STR_3082 :Burgeingang (braun) -STR_3083 :Dschungeleingang -STR_3084 :Blockhütteneingang -STR_3085 :Klassischer/Römischer Eingang -STR_3086 :Abstrakter Eingang -STR_3087 :Schnee-/Eis-Eingang -STR_3088 :Pagodeneingang -STR_3089 :Weltraumeingang STR_3090 :{SMALLFONT}{BLACK}Wählen Sie Stil für den Eingang,{NEWLINE}Ausgang und die Station aus STR_3091 :Sie dürfen diesen Abschnitt nicht entfernen! STR_3092 :Sie dürfen die Station für diese Bahn nicht verschieben oder verändern! @@ -2430,7 +2418,6 @@ STR_3190 :Fußwegextras STR_3191 :Szeneriegruppen STR_3192 :Parkeingang STR_3193 :Wasser -STR_3194 :Szenariobeschreibung STR_3195 :Erfindungsliste STR_3196 :{WINDOW_COLOUR_2}Forschungsgruppe: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Vor dem Spielstart erfundene Objekte: @@ -3231,7 +3218,7 @@ STR_5762 :Chinesischer Yuan (CN¥) STR_5763 :Alle Dateien STR_5764 :Ungültiger Bahntyp STR_5765 :Kann Bahnen eines ungültigen Typs nicht bearbeiten -STR_5766 : +STR_5766 :Ungarischer Forint (Ft) STR_5767 :Einkünfte STR_5768 :Besucher gesamt STR_5769 :Gesamtgewinn @@ -3322,8 +3309,8 @@ STR_5855 :{SMALLFONT}{BLACK}Fenstermodus, Vollbild oder{NEWLINE}randlose Anze STR_5856 :{SMALLFONT}{BLACK}Die im Vollbildmodus verwendete{NEWLINE}Auflösung einstellen STR_5857 :{SMALLFONT}{BLACK}Spieloptionen STR_5858 :{SMALLFONT}{BLACK}GPU anstelle der CPU für die Anzeige verwenden. Verbessert die Kompatibilität mit Aufnahmeprogrammen. Möglicherweise wird die Leistung geringfügig beeinträchtigt -STR_5859 :{SMALLFONT}{BLACK}Aktiviert Tweening für ein sichtbar flüssigeres Spielverhalten. Wenn deaktiviert läuft das Spiel mit 40 FPS -STR_5860 :Umschalten zwischen original und dekompilierten Streckenbau +STR_5859 :{SMALLFONT}{BLACK}Aktiviert Tweening für ein sichtbar flüssigeres Spielverhalten. Wenn deaktiviert, läuft das Spiel mit 40 FPS +STR_5860 :Originale/dekompilierte Streckenansicht STR_5861 :Schlüsselüberprüfung fehlgeschlagen. STR_5862 :Blockiere unbekannte Spieler STR_5863 :{SMALLFONT}{BLACK}Erlaube nur Spieler, mit bekanntem{NEWLINE}Schlüssel, sich zu verbinden @@ -3590,8 +3577,8 @@ STR_6125 :Objekttyp STR_6126 :Unbekannter Typ STR_6127 :Datei: {STRING} STR_6128 :Die Datei konnte aufgrund fehlender oder beschädigter Objekte nicht geladen werden. Eine Liste dieser Elemente ist nachstehend aufgeführt. -STR_6129 :Ausgewählte Elemente kopieren -STR_6130 :Vollständige Liste kopieren +STR_6129 :Kopieren +STR_6130 :Alle kopieren STR_6131 :Objektquelle STR_6132 :Forschungsstatus ignorieren STR_6133 :{SMALLFONT}{BLACK}Ermöglicht den Zugriff auf Attraktionen und Szenerie, die bisher noch nicht erforscht wurden @@ -3626,7 +3613,7 @@ STR_6161 :Gitternetzlinien ein-/ausblenden STR_6162 :Rotierende Wilde Maus STR_6163 :Mausförmige Wagen rasen um enge Kurven und kurze Gefälle herunter, wobei sie sich sanft drehen, um die Fahrgäste zu desorientieren STR_6164 :{WHITE}❌ -STR_6165 :Vertikale Synchronisation verwenden +STR_6165 :Vertikale Synchr. STR_6166 :{SMALLFONT}{BLACK}Synchronisiert die angezeigten Frames mit der Bildwiederholrate des Monitors, dies verhindert Screen Tearing STR_6167 :{SMALLFONT}{BLACK}Erweitert STR_6168 :Titelsequenz @@ -3717,12 +3704,14 @@ STR_6252 :Twitch-API-URL STR_6253 :{SMALLFONT}{BLACK}Geben Sie die URL der Twitch-Integrations-API an. Benötigt, um die Twitch-Integration zu aktivieren. STR_6254 :URL der Twitch-Integrations-API: STR_6255 :URL ist nicht gültig -STR_6256 :Rendering effects +STR_6256 :Rendereffekte STR_6257 :Gläsern (durchsichtig) STR_6258 :Klar (transparent) STR_6259 :Deaktiviert STR_6260 :Blockierte Kacheln anzeigen STR_6261 :Breite Wege anzeigen +STR_6262 :Hauptlautstärke +STR_6263 :{SMALLFONT}{BLACK}Ton insgesamt ein-/ausschalten STR_6264 :Verwenden Sie immer den Systemdateibrowser STR_6265 :{SMALLFONT}{BLACK}Wenn Optionsfeld aktiviert wird, dann wird der Dateibrowser Ihres Systems verwendet im Gegensatz zu dem von OpenRCT2. STR_6266 :Benutzerdefinierten Inhaltsordner öffnen @@ -3733,8 +3722,65 @@ STR_6270 :Geländeflächen STR_6271 :Geländekanten STR_6272 :Stationen STR_6273 :Musik -STR_6274 :Farbgebung kann nicht erkannt werden... +STR_6274 :Farbschema kann nicht gesetzt werden... STR_6275 :{WINDOW_COLOUR_2}Stil der Station: +STR_6276 :{RED}{STRINGID} hat steckengebliebene Gäste, möglicherweise aufgrund eines ungültigen Streckentyps oder Betriebsmodus. +STR_6277 :{WINDOW_COLOUR_2}Stationsindex: {BLACK}{COMMA16} +STR_6278 :Autosaveanzahl: +STR_6279 :{SMALLFONT}{BLACK}Anzahl der Autosaves, die{NEWLINE}behalten werden sollen +STR_6280 :{SMALLFONT}{BLACK}Chat +STR_6281 :{SMALLFONT}{BLACK}Eine separate Schaltfläche für das Chatfenster in der Symbolleiste anzeigen +STR_6282 :Chat +STR_6283 :Der Chat ist derzeit nicht verfügbar. Sind Sie mit einem Server verbunden? +STR_6284 :Netzwerk +STR_6285 :Netzwerkinformation +STR_6286 :Empfangen +STR_6287 :Senden +STR_6288 :Ges. empfangen +STR_6289 :Ges. gesendet +STR_6290 :Basisprotokoll +STR_6291 :Befehle +STR_6292 :Karte +STR_6293 :B +STR_6294 :KiB +STR_6295 :MiB +STR_6296 :GiB +STR_6297 :TiB +STR_6298 :{STRING}/s +STR_6299 :Alle herunterladen +STR_6300 :{SMALLFONT}{BLACK}Falls online verfügbar, alle fehlenden Objekte herunterladen +STR_6301 :{SMALLFONT}{BLACK}Kopiert den ausgewählten Objektnamen in die Zwischenablage +STR_6302 :{SMALLFONT}{BLACK}Kopiert die gesamte Liste der fehlenden Objekte in die Zwischenablage +STR_6303 :Lädt Objekt herunter ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :Szeneriewähler öffnen +STR_6305 :Multithreading +STR_6306 :{SMALLFONT}{BLACK}Experimentelle Option, um mehrere Threads für das Rendern zu verwenden, kann Instabilität verursachen +STR_6307 :Farbschema: {BLACK}{STRINGID} +STR_6308 :„{STRINGID}{OUTLINE}{TOPAZ}“{NEWLINE}{STRINGID} +STR_6309 :Neu verbinden +STR_6310 :{WINDOW_COLOUR_2}Position: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Nächste: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(Oberfläche) +STR_6313 :(Hang {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Ziel: {BLACK}{INT32}, {INT32} Toleranz {INT32} +STR_6315 :{WINDOW_COLOUR_2}Wegfindungsziel: {BLACK}{INT32}, {INT32}, {INT32} Richt. {INT32} +STR_6316 :{WINDOW_COLOUR_2}Wegfindungshistorie: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} Richtung {INT32} +STR_6318 :Netzwerkdesynchronisation erkannt.{NEWLINE}Logdatei: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Blockbremse geschlossen +STR_6320 :{WINDOW_COLOUR_2}Unzerstörbar +STR_6321 :{WINDOW_COLOUR_2}Zusatz ist kaputt +STR_6322 :{WINDOW_COLOUR_2}Sprite-ID: {BLACK}{INT32} +STR_6323 :Simulierend +STR_6324 :Simulieren +STR_6325 :{SMALLFONT}{BLACK}Fahrgeschäft/Attraktion simulieren +STR_6326 :{POP16}{POP16}{POP16}{STRINGID} kann nicht simuliert werden ... +STR_6327 :Transparenter Hintergrund für riesige Screenshots +STR_6328 :{SMALLFONT}{BLACK}Mit dieser Option werden riesige Screenshots einen transparenten Hintergrund statt der standardmäßig verwendeten schwarzen Farbe verwenden. +STR_6329 :{STRING}{STRINGID} +STR_6330 :Lade [{STRING}] von {STRING} herunter ({COMMA16} / {COMMA16}) +STR_6331 :Enten erzeugen +STR_6332 :Enten entfernen ############# # Scenarios # diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index e11240e1af..e8555b25e4 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -551,8 +551,8 @@ STR_1167 :Can't raise water level here... STR_1168 :Options STR_1169 :(None) STR_1170 :{STRING} -STR_1171 :{RED}Closed - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Closed +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Build footpaths and queue lines STR_1174 :Banner sign in the way STR_1175 :Can't build this on sloped footpath @@ -1108,7 +1108,7 @@ STR_1726 :Land not for sale! STR_1727 :Construction rights not for sale! STR_1728 :Can't buy construction rights here... STR_1729 :Land not owned by park! -STR_1730 :{RED}Closed - - +STR_1730 :{RED}Closed STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Build STR_1733 :Mode @@ -2237,7 +2237,7 @@ STR_2977 :Staff member name STR_2978 :Enter new name for this member of staff: STR_2979 :Can't name staff member... STR_2980 :Too many banners in game -STR_2981 :{RED}No entry - - +STR_2981 :{RED}No entry STR_2982 :Banner text STR_2983 :Enter new text for this banner: STR_2984 :Can't set new text for banner... @@ -2414,7 +2414,6 @@ STR_3190 :Path Extras STR_3191 :Scenery Groups STR_3192 :Park Entrance STR_3193 :Water -STR_3194 :Scenario Description STR_3195 :Invention List STR_3196 :{WINDOW_COLOUR_2}Research Group: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Items pre-invented at start of game: @@ -3215,6 +3214,7 @@ STR_5762 :Chinese Yuan (CN¥) STR_5763 :All files STR_5764 :Invalid ride type STR_5765 :Cannot edit rides of invalid type +STR_5766 :Hungarian Forint (Ft) STR_5767 :Income STR_5768 :Total customers STR_5769 :Total profit @@ -3751,6 +3751,35 @@ STR_6300 :{SMALLFONT}{BLACK}Download all missing objects if available online. STR_6301 :{SMALLFONT}{BLACK}Copy the selected object name to the clipboard. STR_6302 :{SMALLFONT}{BLACK}Copy the entire list of missing objects to the clipboard. STR_6303 :Downloading object ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :Open scenery picker +STR_6305 :Multithreading +STR_6306 :{SMALLFONT}{BLACK}Experimental option to use multiple threads to render, may cause instability. +STR_6307 :Colour scheme: {BLACK}{STRINGID} +STR_6308 :“{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :Reconnect +STR_6310 :{WINDOW_COLOUR_2}Position: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Next: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(surface) +STR_6313 :(slope {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Dest: {BLACK}{INT32}, {INT32} tolerance {INT32} +STR_6315 :{WINDOW_COLOUR_2}Pathfind Goal: {BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6316 :{WINDOW_COLOUR_2}Pathfind history: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6318 :Network desync detected.{NEWLINE}Log file: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Block Brake Closed +STR_6320 :{WINDOW_COLOUR_2}Indestructible +STR_6321 :{WINDOW_COLOUR_2}Addition is broken +STR_6322 :{WINDOW_COLOUR_2}Sprite Id: {BLACK}{INT32} +STR_6323 :Simulating +STR_6324 :Simulate +STR_6325 :{SMALLFONT}{BLACK}Simulate ride/attraction +STR_6326 :Can't simulate {POP16}{POP16}{POP16}{STRINGID}... +STR_6327 :Transparent background for giant screenshots +STR_6328 :{SMALLFONT}{BLACK}With this option enabled, giant screenshots will have a transparent background instead of the default black colour. +STR_6329 :{STRING}{STRINGID} +STR_6330 :Downloading [{STRING}] from {STRING} ({COMMA16} / {COMMA16}) +STR_6331 :Create Ducks +STR_6332 :Remove Ducks ############# # Scenarios # diff --git a/data/language/en-US.txt b/data/language/en-US.txt index f09be14734..29f092b8ab 100644 --- a/data/language/en-US.txt +++ b/data/language/en-US.txt @@ -22,8 +22,6 @@ STR_0574 :Riders are held in special harnesses in a lying-down position, trav STR_0976 :Restrooms and Information Kiosks -STR_1035 :Local authority won't allow construction above tree-height! - STR_1102 :Traveling at {VELOCITY} STR_1105 :Traveling at {VELOCITY} STR_1108 :Traveling at {VELOCITY} @@ -37,163 +35,16 @@ STR_1138 :{SMALLFONT}{BLACK}Select additional color 2 STR_1139 :{SMALLFONT}{BLACK}Select support structure color STR_1140 :{SMALLFONT}{BLACK}Select vehicle color scheme option -STR_1361 :Can't change speed... -STR_1362 :Can't change launch speed... -STR_1363 :Too high for supports! -STR_1364 :Supports for track above can't be extended any further! -STR_1365 :In-line Twist (left) -STR_1366 :In-line Twist (right) -STR_1367 :Half Loop -STR_1368 :Half Corkscrew (left) -STR_1369 :Half Corkscrew (right) -STR_1370 :Barrel Roll (left) -STR_1371 :Barrel Roll (right) -STR_1372 :Launched Lift Hill -STR_1373 :Large Half Loop (left) -STR_1374 :Large Half Loop (right) -STR_1375 :Upper Transfer -STR_1376 :Lower Transfer -STR_1377 :Heartline Roll (left) -STR_1378 :Heartline Roll (right) -STR_1379 :Reverser (left) -STR_1380 :Reverser (right) -STR_1381 :Curved Lift Hill (left) -STR_1382 :Curved Lift Hill (right) -STR_1383 :Quarter Loop -STR_1384 :{YELLOW}{STRINGID} -STR_1385 :{SMALLFONT}{BLACK}Other track configurations -STR_1386 :Special... -STR_1387 :Can't change land type... -STR_1388 :{OUTLINE}{GREEN}+ {CURRENCY} -STR_1389 :{OUTLINE}{RED}- {CURRENCY} -STR_1390 :{CURRENCY2DP} -STR_1391 :{RED}{CURRENCY2DP} -STR_1392 :{SMALLFONT}{BLACK}View of ride/attraction -STR_1393 :{SMALLFONT}{BLACK}Vehicle details and options -STR_1394 :{SMALLFONT}{BLACK}Operating options -STR_1395 :{SMALLFONT}{BLACK}Maintenance options STR_1396 :{SMALLFONT}{BLACK}Color scheme options -STR_1397 :{SMALLFONT}{BLACK}Sound & music options -STR_1398 :{SMALLFONT}{BLACK}Measurements and test data -STR_1399 :{SMALLFONT}{BLACK}Graphs -STR_1400 :Entrance -STR_1401 :Exit -STR_1402 :{SMALLFONT}{BLACK}Build or move entrance to ride/attraction -STR_1403 :{SMALLFONT}{BLACK}Build or move exit from ride/attraction -STR_1404 :{SMALLFONT}{BLACK}Rotate 90° -STR_1405 :{SMALLFONT}{BLACK}Mirror image -STR_1406 :{SMALLFONT}{BLACK}Toggle scenery on/off (if available for this design) -STR_1407 :{WINDOW_COLOUR_2}Build this... -STR_1408 :{WINDOW_COLOUR_2}Cost: {BLACK}{CURRENCY} -STR_1409 :Entry/Exit Platform -STR_1410 :Vertical Tower -STR_1411 :{STRINGID} in the way -STR_1412 :{WINDOW_COLOUR_3}Data logging not available for this type of ride -STR_1413 :{WINDOW_COLOUR_3}Data logging will start when next {STRINGID} leaves {STRINGID} -STR_1414 :{SMALLFONT}{BLACK}{DURATION} -STR_1415 :{WINDOW_COLOUR_2}Velocity -STR_1416 :{WINDOW_COLOUR_2}Altitude -STR_1417 :{WINDOW_COLOUR_2}Vert.G's -STR_1418 :{WINDOW_COLOUR_2}Lat.G's -STR_1419 :{SMALLFONT}{BLACK}{VELOCITY} -STR_1420 :{SMALLFONT}{BLACK}{LENGTH} -STR_1421 :{SMALLFONT}{BLACK}{COMMA16}g -STR_1422 :{SMALLFONT}{BLACK}Logging data from {POP16}{STRINGID} -STR_1423 :{SMALLFONT}{BLACK}Queue line path -STR_1424 :{SMALLFONT}{BLACK}Footpath -STR_1425 :Footpath -STR_1426 :Queue Line -STR_1427 :{WINDOW_COLOUR_2}Customers: {BLACK}{COMMA32} per hour -STR_1428 :{WINDOW_COLOUR_2}Admission price: -STR_1429 :{POP16}{POP16}{POP16}{CURRENCY2DP} -STR_1430 :Free -STR_1431 :Walking -STR_1432 :Heading for {STRINGID} -STR_1433 :Queuing for {STRINGID} -STR_1434 :Drowning -STR_1435 :On {STRINGID} -STR_1436 :In {STRINGID} -STR_1437 :At {STRINGID} -STR_1438 :Sitting -STR_1439 :(select location) -STR_1440 :Mowing grass -STR_1441 :Sweeping footpath + STR_1442 :Emptying trash can -STR_1443 :Watering gardens -STR_1444 :Watching {STRINGID} -STR_1445 :Watching construction of {STRINGID} -STR_1446 :Looking at scenery -STR_1447 :Leaving the park -STR_1448 :Watching new ride being constructed -STR_1449 :{SPRITE} {STRINGID}{NEWLINE}({STRINGID}) -STR_1450 :{INLINE_SPRITE}{09}{20}{00}{00}{SPRITE} {STRINGID}{NEWLINE}({STRINGID}) -STR_1451 :{STRINGID}{NEWLINE}({STRINGID}) -STR_1452 :Guest's name -STR_1453 :Enter name for this guest: -STR_1454 :Can't name guest... -STR_1455 :Invalid name for guest -STR_1456 :{WINDOW_COLOUR_2}Cash spent: {BLACK}{CURRENCY2DP} -STR_1457 :{WINDOW_COLOUR_2}Cash in pocket: {BLACK}{CURRENCY2DP} -STR_1458 :{WINDOW_COLOUR_2}Time in park: {BLACK}{REALTIME} -STR_1459 :Track style -STR_1460 :{SMALLFONT}{BLACK}'U' shaped open track -STR_1461 :{SMALLFONT}{BLACK}'O' shaped enclosed track -STR_1462 :Too steep for lift hill -STR_1463 :Guests -STR_1464 :Helix up (small) -STR_1465 :Helix up (large) -STR_1466 :Helix down (small) -STR_1467 :Helix down (large) -STR_1468 :Staff -STR_1469 :Ride must start and end with stations -STR_1470 :Station not long enough -STR_1471 :{WINDOW_COLOUR_2}Speed: -STR_1472 :{SMALLFONT}{BLACK}Speed of this ride -STR_1473 :{WINDOW_COLOUR_2}Excitement rating: {BLACK}{COMMA2DP32} ({STRINGID}) -STR_1474 :{WINDOW_COLOUR_2}Excitement rating: {BLACK}Not yet available -STR_1475 :{WINDOW_COLOUR_2}Intensity rating: {BLACK}{COMMA2DP32} ({STRINGID}) -STR_1476 :{WINDOW_COLOUR_2}Intensity rating: {BLACK}Not yet available -STR_1477 :{WINDOW_COLOUR_2}Intensity rating: {OUTLINE}{RED}{COMMA2DP32} ({STRINGID}) -STR_1478 :{WINDOW_COLOUR_2}Nausea rating: {BLACK}{COMMA2DP32} ({STRINGID}) -STR_1479 :{WINDOW_COLOUR_2}Nausea rating: {BLACK}Not yet available -STR_1480 :{SMALLFONT}“I can't afford {STRINGID}” -STR_1481 :{SMALLFONT}“I've spent all my money” -STR_1482 :{SMALLFONT}“I feel sick” -STR_1483 :{SMALLFONT}“I feel very sick” -STR_1484 :{SMALLFONT}“I want to go on something more thrilling than {STRINGID}” -STR_1485 :{SMALLFONT}“{STRINGID} looks too intense for me” -STR_1486 :{SMALLFONT}“I haven't finished my {STRINGID} yet” -STR_1487 :{SMALLFONT}“Just looking at {STRINGID} makes me feel sick” -STR_1488 :{SMALLFONT}“I'm not paying that much to go on {STRINGID}” -STR_1489 :{SMALLFONT}“I want to go home” + STR_1490 :{SMALLFONT}“{STRINGID} is a really good value” -STR_1491 :{SMALLFONT}“I've already got {STRINGID}” -STR_1492 :{SMALLFONT}“I can't afford {STRINGID}” -STR_1493 :{SMALLFONT}“I'm not hungry” -STR_1494 :{SMALLFONT}“I'm not thirsty” -STR_1495 :{SMALLFONT}“Help! I'm drowning!” -STR_1496 :{SMALLFONT}“I'm lost!” -STR_1497 :{SMALLFONT}“{STRINGID} was great” -STR_1498 :{SMALLFONT}“I've been queuing for {STRINGID} for ages” -STR_1499 :{SMALLFONT}“I'm tired” -STR_1500 :{SMALLFONT}“I'm hungry” -STR_1501 :{SMALLFONT}“I'm thirsty” + STR_1502 :{SMALLFONT}“I need to go to the bathroom” -STR_1503 :{SMALLFONT}“I can't find {STRINGID}” -STR_1504 :{SMALLFONT}“I'm not paying that much to use {STRINGID}” -STR_1505 :{SMALLFONT}“I'm not going on {STRINGID} while it's raining” + STR_1506 :{SMALLFONT}“The trash here is really bad” -STR_1507 :{SMALLFONT}“I can't find the park exit” -STR_1508 :{SMALLFONT}“I want to get off {STRINGID}” -STR_1509 :{SMALLFONT}“I want to get out of {STRINGID}” -STR_1510 :{SMALLFONT}“I'm not going on {STRINGID} - It isn't safe” -STR_1511 :{SMALLFONT}“This path is disgusting” -STR_1512 :{SMALLFONT}“It's too crowded here” -STR_1513 :{SMALLFONT}“The vandalism here is really bad” -STR_1514 :{SMALLFONT}“Great scenery!” -STR_1515 :{SMALLFONT}“This park is really clean and tidy” -STR_1516 :{SMALLFONT}“The jumping fountains are great” -STR_1517 :{SMALLFONT}“The music is nice here” + STR_1518 :{SMALLFONT}“This balloon from {STRINGID} is a really good value” STR_1519 :{SMALLFONT}“This cuddly toy from {STRINGID} is a really good value” STR_1520 :{SMALLFONT}“This park map from {STRINGID} is a really good value” @@ -204,11 +55,9 @@ STR_1524 :{SMALLFONT}“This burger from {STRINGID} is a really good value” STR_1525 :{SMALLFONT}“These fries from {STRINGID} are a really good value” STR_1526 :{SMALLFONT}“This ice cream from {STRINGID} is a really good value” STR_1527 :{SMALLFONT}“This cotton candy from {STRINGID} is a really good value” -STR_1528 : -STR_1529 : -STR_1530 : + STR_1531 :{SMALLFONT}“This pizza from {STRINGID} is a really good value” -STR_1532 : + STR_1533 :{SMALLFONT}“This popcorn from {STRINGID} is a really good value” STR_1534 :{SMALLFONT}“This hot dog from {STRINGID} is a really good value” STR_1535 :{SMALLFONT}“This tentacle from {STRINGID} is a really good value” @@ -217,49 +66,18 @@ STR_1537 :{SMALLFONT}“This candy apple from {STRINGID} is a really good val STR_1538 :{SMALLFONT}“This T-shirt from {STRINGID} is a really good value” STR_1539 :{SMALLFONT}“This donut from {STRINGID} is a really good value” STR_1540 :{SMALLFONT}“This coffee from {STRINGID} is a really good value” -STR_1541 : + STR_1542 :{SMALLFONT}“This fried chicken from {STRINGID} is a really good value” STR_1543 :{SMALLFONT}“This lemonade from {STRINGID} is a really good value” -STR_1544 : -STR_1545 : -STR_1546 : -STR_1547 : -STR_1548 : -STR_1549 : -STR_1550 :{SMALLFONT}“Wow!” -STR_1551 :{SMALLFONT}“I have the strangest feeling someone is watching me” -STR_1552 :{SMALLFONT}“I'm not paying that much for a balloon from {STRINGID}” -STR_1553 :{SMALLFONT}“I'm not paying that much for a cuddly toy from {STRINGID}” -STR_1554 :{SMALLFONT}“I'm not paying that much for a park map from {STRINGID}” -STR_1555 :{SMALLFONT}“I'm not paying that much for an on-ride photo from {STRINGID}” -STR_1556 :{SMALLFONT}“I'm not paying that much for an umbrella from {STRINGID}” -STR_1557 :{SMALLFONT}“I'm not paying that much for a drink from {STRINGID}” -STR_1558 :{SMALLFONT}“I'm not paying that much for a burger from {STRINGID}” + STR_1559 :{SMALLFONT}“I'm not paying that much for fries from {STRINGID}” -STR_1560 :{SMALLFONT}“I'm not paying that much for an ice cream from {STRINGID}” + STR_1561 :{SMALLFONT}“I'm not paying that much for cotton candy from {STRINGID}” -STR_1562 : -STR_1563 : -STR_1564 : -STR_1565 :{SMALLFONT}“I'm not paying that much for pizza from {STRINGID}” -STR_1566 : -STR_1567 :{SMALLFONT}“I'm not paying that much for popcorn from {STRINGID}” -STR_1568 :{SMALLFONT}“I'm not paying that much for a hot dog from {STRINGID}” -STR_1569 :{SMALLFONT}“I'm not paying that much for tentacle from {STRINGID}” -STR_1570 :{SMALLFONT}“I'm not paying that much for a hat from {STRINGID}” + STR_1571 :{SMALLFONT}“I'm not paying that much for a candy apple from {STRINGID}” -STR_1572 :{SMALLFONT}“I'm not paying that much for a T-shirt from {STRINGID}” + STR_1573 :{SMALLFONT}“I'm not paying that much for a donut from {STRINGID}” -STR_1574 :{SMALLFONT}“I'm not paying that much for coffee from {STRINGID}” -STR_1575 : -STR_1576 :{SMALLFONT}“I'm not paying that much for fried chicken from {STRINGID}” -STR_1577 :{SMALLFONT}“I'm not paying that much for lemonade from {STRINGID}” -STR_1578 : -STR_1579 : -STR_1580 : -STR_1581 : -STR_1582 : -STR_1583 : + STR_1584 :{SMALLFONT}“This on-ride photo from {STRINGID} is a really good value” STR_1585 :{SMALLFONT}“This on-ride photo from {STRINGID} is a really good value” STR_1586 :{SMALLFONT}“This on-ride photo from {STRINGID} is a really good value” @@ -277,583 +95,80 @@ STR_1597 :{SMALLFONT}“This soybean milk from {STRINGID} is a really good va STR_1598 :{SMALLFONT}“This sujeonggwa from {STRINGID} is a really good value” STR_1599 :{SMALLFONT}“This sub sandwich from {STRINGID} is a really good value” STR_1600 :{SMALLFONT}“This cookie from {STRINGID} is a really good value” -STR_1601 : -STR_1602 : -STR_1603 : + STR_1604 :{SMALLFONT}“This roast sausage from {STRINGID} is a really good value” -STR_1605 : -STR_1606 : -STR_1607 : -STR_1608 : -STR_1609 : -STR_1610 : -STR_1611 : -STR_1612 : -STR_1613 : -STR_1614 : -STR_1615 : -STR_1616 :{SMALLFONT}“I'm not paying that much for an on-ride photo from {STRINGID}” -STR_1617 :{SMALLFONT}“I'm not paying that much for an on-ride photo from {STRINGID}” -STR_1618 :{SMALLFONT}“I'm not paying that much for an on-ride photo from {STRINGID}” -STR_1619 :{SMALLFONT}“I'm not paying that much for a pretzel from {STRINGID}” -STR_1620 :{SMALLFONT}“I'm not paying that much for hot chocolate from {STRINGID}” -STR_1621 :{SMALLFONT}“I'm not paying that much for iced tea from {STRINGID}” -STR_1622 :{SMALLFONT}“I'm not paying that much for a funnel cake from {STRINGID}” -STR_1623 :{SMALLFONT}“I'm not paying that much for sunglasses from {STRINGID}” -STR_1624 :{SMALLFONT}“I'm not paying that much for beef noodles from {STRINGID}” -STR_1625 :{SMALLFONT}“I'm not paying that much for fried rice noodles from {STRINGID}” -STR_1626 :{SMALLFONT}“I'm not paying that much for wonton soup from {STRINGID}” -STR_1627 :{SMALLFONT}“I'm not paying that much for meatball soup from {STRINGID}” -STR_1628 :{SMALLFONT}“I'm not paying that much for fruit juice from {STRINGID}” -STR_1629 :{SMALLFONT}“I'm not paying that much for soybean milk from {STRINGID}” -STR_1630 :{SMALLFONT}“I'm not paying that much for sujeonggwa from {STRINGID}” -STR_1631 :{SMALLFONT}“I'm not paying that much for a sub sandwich from {STRINGID}” -STR_1632 :{SMALLFONT}“I'm not paying that much for a cookie from {STRINGID}” -STR_1633 : -STR_1634 : -STR_1635 : -STR_1636 :{SMALLFONT}“I'm not paying that much for a roast sausage from {STRINGID}” -STR_1637 : -STR_1638 : -STR_1639 : -STR_1640 : -STR_1641 : -STR_1642 : -STR_1643 : -STR_1644 : -STR_1645 : -STR_1646 : -STR_1647 : -STR_1648 :{SMALLFONT}“Help! Put me down!” -STR_1649 :{SMALLFONT}“I'm running out of cash!” -STR_1650 :{SMALLFONT}“Wow! A new ride being built!” -# Two removed inside jokes about Intamin and Phoenix -STR_1653 :{SMALLFONT}“...and here we are on {STRINGID}!” -STR_1654 :{WINDOW_COLOUR_2}Recent thoughts: -STR_1655 :{SMALLFONT}{BLACK}Construct footpath on land -STR_1656 :{SMALLFONT}{BLACK}Construct bridge or tunnel footpath -STR_1657 :{WINDOW_COLOUR_2}Preferred ride -STR_1658 :{WINDOW_COLOUR_2}intensity: {BLACK}less than {COMMA16} -STR_1659 :{WINDOW_COLOUR_2}intensity: {BLACK}between {COMMA16} and {COMMA16} -STR_1660 :{WINDOW_COLOUR_2}intensity: {BLACK}more than {COMMA16} -STR_1661 :{WINDOW_COLOUR_2}Nausea tolerance: {BLACK}{STRINGID} -STR_1662 :{WINDOW_COLOUR_2}Happiness: -STR_1663 :{WINDOW_COLOUR_2}Nausea: -STR_1664 :{WINDOW_COLOUR_2}Energy: -STR_1665 :{WINDOW_COLOUR_2}Hunger: -STR_1666 :{WINDOW_COLOUR_2}Thirst: + STR_1667 :{WINDOW_COLOUR_2}Bathroom: -STR_1668 :{WINDOW_COLOUR_2}Satisfaction: {BLACK}Unknown -STR_1669 :{WINDOW_COLOUR_2}Satisfaction: {BLACK}{COMMA16}% -STR_1670 :{WINDOW_COLOUR_2}Total customers: {BLACK}{COMMA32} -STR_1671 :{WINDOW_COLOUR_2}Total profit: {BLACK}{CURRENCY2DP} -STR_1672 :Brakes -STR_1673 :Spinning Control Toggle Track -STR_1674 :Brake speed -STR_1675 :{POP16}{VELOCITY} -STR_1676 :{SMALLFONT}{BLACK}Set speed limit for brakes -STR_1677 :{WINDOW_COLOUR_2}Popularity: {BLACK}Unknown -STR_1678 :{WINDOW_COLOUR_2}Popularity: {BLACK}{COMMA16}% -STR_1679 :Helix up (left) -STR_1680 :Helix up (right) -STR_1681 :Helix down (left) -STR_1682 :Helix down (right) -STR_1683 :Base size 2 x 2 -STR_1684 :Base size 4 x 4 -STR_1685 :Base size 2 x 4 -STR_1686 :Base size 5 x 1 -STR_1687 :Water splash -STR_1688 :Base size 4 x 1 -STR_1689 :Block brakes -STR_1690 :{WINDOW_COLOUR_2}{STRINGID}{NEWLINE}{BLACK}{STRINGID} -STR_1691 :{WINDOW_COLOUR_2} Cost: {BLACK}{CURRENCY} -STR_1692 :{WINDOW_COLOUR_2} Cost: {BLACK}from {CURRENCY} -STR_1693 :{SMALLFONT}{BLACK}Guests -STR_1694 :{SMALLFONT}{BLACK}Staff -STR_1695 :{SMALLFONT}{BLACK}Income and costs -STR_1696 :{SMALLFONT}{BLACK}Customer information -STR_1697 :Cannot place these on queue line area -STR_1698 :Can only place these on queue area -STR_1699 :Too many people in game -STR_1700 :Hire new Handyman -STR_1701 :Hire new Mechanic -STR_1702 :Hire new Security Guard -STR_1703 :Hire new Entertainer -STR_1704 :Can't hire new staff... -STR_1705 :{SMALLFONT}{BLACK}Sack this staff member -STR_1706 :{SMALLFONT}{BLACK}Move this person to a new location -STR_1707 :Too many staff in game -STR_1708 :{SMALLFONT}{BLACK}Set patrol area for this staff member -STR_1709 :Sack staff -STR_1710 :Yes -STR_1711 :{WINDOW_COLOUR_1}Are you sure you want to sack {STRINGID}? -STR_1712 :{INLINE_SPRITE}{247}{19}{00}{00}{WINDOW_COLOUR_2}Sweep footpaths -STR_1713 :{INLINE_SPRITE}{248}{19}{00}{00}{WINDOW_COLOUR_2}Water gardens + STR_1714 :{INLINE_SPRITE}{249}{19}{00}{00}{WINDOW_COLOUR_2}Empty trash cans -STR_1715 :{INLINE_SPRITE}{250}{19}{00}{00}{WINDOW_COLOUR_2}Mow grass -STR_1716 :Invalid name for park -STR_1717 :Can't rename park... -STR_1718 :Park Name -STR_1719 :Enter name for park: -STR_1720 :{SMALLFONT}{BLACK}Name park -STR_1721 :Park closed -STR_1722 :Park open -STR_1723 :Can't open park... -STR_1724 :Can't close park... -STR_1725 :Can't buy land... -STR_1726 :Land not for sale! -STR_1727 :Construction rights not for sale! -STR_1728 :Can't buy construction rights here... -STR_1729 :Land not owned by park! -STR_1730 :{RED}Closed - - -STR_1731 :{WHITE}{STRINGID} - - -STR_1732 :Build -STR_1733 :Mode -STR_1734 :{WINDOW_COLOUR_2}Number of laps: -STR_1735 :{SMALLFONT}{BLACK}Number of laps of circuit -STR_1736 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} -STR_1738 :Can't change number of laps... -STR_1739 :Race won by guest {INT32} -STR_1740 :Race won by {STRINGID} -STR_1741 :Not yet constructed! -STR_1742 :{WINDOW_COLOUR_2}Max. people on ride: -STR_1743 :{SMALLFONT}{BLACK}Maximum number of people allowed on this ride at one time -STR_1744 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} -STR_1746 :Can't change this... -STR_1747 :{WINDOW_COLOUR_2}Time limit: -STR_1748 :{SMALLFONT}{BLACK}Time limit for ride -STR_1749 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{DURATION} -STR_1751 :Can't change time limit for ride... -STR_1752 :{SMALLFONT}{BLACK}Show list of individual guests in park + STR_1753 :{SMALLFONT}{BLACK}Show summarized list of guests in park -STR_1754 :{BLACK}{COMMA16} guests -STR_1755 :{BLACK}{COMMA16} guest -STR_1756 :{WINDOW_COLOUR_2}Admission price: -STR_1757 :{WINDOW_COLOUR_2}Reliability: {MOVE_X}{255}{BLACK}{COMMA16}% -STR_1758 :{SMALLFONT}{BLACK}Build mode -STR_1759 :{SMALLFONT}{BLACK}Move mode -STR_1760 :{SMALLFONT}{BLACK}Fill-in mode -STR_1761 :{SMALLFONT}{BLACK}Build maze in this direction -STR_1779 :{INLINE_SPRITE}{254}{19}{00}{00} Panda costume -STR_1780 :{INLINE_SPRITE}{255}{19}{00}{00} Tiger costume -STR_1781 :{INLINE_SPRITE}{00}{20}{00}{00} Elephant costume -STR_1782 :{INLINE_SPRITE}{01}{20}{00}{00} Roman costume -STR_1783 :{INLINE_SPRITE}{02}{20}{00}{00} Gorilla costume -STR_1784 :{INLINE_SPRITE}{03}{20}{00}{00} Snowman costume -STR_1785 :{INLINE_SPRITE}{04}{20}{00}{00} Knight costume -STR_1786 :{INLINE_SPRITE}{05}{20}{00}{00} Astronaut costume -STR_1787 :{INLINE_SPRITE}{06}{20}{00}{00} Bandit costume -STR_1788 :{INLINE_SPRITE}{07}{20}{00}{00} Sheriff costume -STR_1789 :{INLINE_SPRITE}{08}{20}{00}{00} Pirate costume + STR_1790 :{SMALLFONT}{BLACK}Select uniform color for this type of staff STR_1791 :{WINDOW_COLOUR_2}Uniform color: -STR_1792 :Responding to {STRINGID} breakdown call -STR_1793 :Heading to {STRINGID} for an inspection -STR_1794 :Fixing {STRINGID} -STR_1795 :Answering radio call -STR_1796 :Has broken down and requires fixing -STR_1798 :Whirlpool -STR_1799 :{POP16}{POP16}{POP16}{POP16}{POP16}{CURRENCY2DP} -STR_1800 :Safety cut-out -STR_1801 :Restraints stuck closed -STR_1802 :Restraints stuck open -STR_1803 :Doors stuck closed -STR_1804 :Doors stuck open -STR_1805 :Vehicle malfunction -STR_1806 :Brakes failure -STR_1807 :Control failure -STR_1808 :{WINDOW_COLOUR_2}Last breakdown: {BLACK}{STRINGID} -STR_1809 :{WINDOW_COLOUR_2}Current breakdown: {OUTLINE}{RED}{STRINGID} -STR_1810 :{WINDOW_COLOUR_2}Carrying: -STR_1811 :Can't build this here... -STR_1812 :{SMALLFONT}{BLACK}{STRINGID} -STR_1813 :Miscellaneous Objects -STR_1814 :Actions -STR_1815 :Thoughts -STR_1816 :{SMALLFONT}{BLACK}Select information type to show in guest list -STR_1817 :({COMMA16}) -STR_1818 :{WINDOW_COLOUR_2}All guests + STR_1819 :{WINDOW_COLOUR_2}All guests (summarized) -STR_1820 :{WINDOW_COLOUR_2}Guests {STRINGID} -STR_1821 :{WINDOW_COLOUR_2}Guests thinking {STRINGID} -STR_1822 :{WINDOW_COLOUR_2}Guests thinking about {POP16}{STRINGID} -STR_1823 :{SMALLFONT}{BLACK}Show guests' thoughts about this ride/attraction -STR_1824 :{SMALLFONT}{BLACK}Show guests on this ride/attraction -STR_1825 :{SMALLFONT}{BLACK}Show guests queuing for this ride/attraction -STR_1826 :Status -STR_1827 :Popularity -STR_1828 :Satisfaction -STR_1829 :Profit -STR_1830 :Queue length -STR_1831 :Queue time -STR_1832 :Reliability -STR_1833 :Down-time + STR_1834 :Guests favorite -STR_1835 :Popularity: Unknown -STR_1836 :Popularity: {COMMA16}% -STR_1837 :Satisfaction: Unknown -STR_1838 :Satisfaction: {COMMA16}% -STR_1839 :Reliability: {COMMA16}% -STR_1840 :Down-time: {COMMA16}% -STR_1841 :Profit: {CURRENCY2DP} per hour + STR_1842 :Favorite of: {COMMA16} guest STR_1843 :Favorite of: {COMMA16} guests -STR_1844 :{SMALLFONT}{BLACK}Select information type to show in ride/attraction list -STR_1845 :{MONTHYEAR} -STR_1846 :{COMMA16} guests -STR_1847 :{INLINE_SPRITE}{11}{20}{00}{00}{COMMA16} guests -STR_1848 :{INLINE_SPRITE}{10}{20}{00}{00}{COMMA16} guests -STR_1849 :{WINDOW_COLOUR_2}Play music -STR_1850 :{SMALLFONT}{BLACK}Select whether music should be played for this ride -STR_1851 :{WINDOW_COLOUR_2}Running cost: {BLACK}{CURRENCY2DP} per hour -STR_1852 :{WINDOW_COLOUR_2}Running cost: {BLACK}Unknown -STR_1853 :{WINDOW_COLOUR_2}Built: {BLACK}This Year -STR_1854 :{WINDOW_COLOUR_2}Built: {BLACK}Last Year -STR_1855 :{WINDOW_COLOUR_2}Built: {BLACK}{COMMA16} Years Ago -STR_1856 :{WINDOW_COLOUR_2}Profit per item sold: {BLACK}{CURRENCY2DP} -STR_1857 :{WINDOW_COLOUR_2}Loss per item sold: {BLACK}{CURRENCY2DP} -STR_1858 :{WINDOW_COLOUR_2}Cost: {BLACK}{CURRENCY2DP} per month -STR_1859 :Handymen -STR_1860 :Mechanics -STR_1861 :Security Guards -STR_1862 :Entertainers -STR_1863 :Handyman -STR_1864 :Mechanic -STR_1865 :Security Guard -STR_1866 :Entertainer -STR_1867 :{BLACK}{COMMA16} {STRINGID} -STR_1868 :Can't change number of rotations... -STR_1869 :{WINDOW_COLOUR_2}Number of rotations: -STR_1870 :{SMALLFONT}{BLACK}Number of complete rotations -STR_1871 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} -STR_1873 :{WINDOW_COLOUR_2}Income: {BLACK}{CURRENCY2DP} per hour -STR_1874 :{WINDOW_COLOUR_2}Profit: {BLACK}{CURRENCY2DP} per hour -STR_1875 :{BLACK} {SPRITE}{BLACK} {STRINGID} -STR_1876 :{WINDOW_COLOUR_2}{INLINE_SPRITE}{251}{19}{00}{00}Inspect Rides -STR_1877 :{WINDOW_COLOUR_2}{INLINE_SPRITE}{252}{19}{00}{00}Fix Rides -STR_1878 :{WINDOW_COLOUR_2}Inspection: -STR_1879 :Every 10 minutes -STR_1880 :Every 20 minutes -STR_1881 :Every 30 minutes -STR_1882 :Every 45 minutes -STR_1883 :Every hour -STR_1884 :Every 2 hours -STR_1885 :Never -STR_1886 :Inspecting {STRINGID} -STR_1887 :{WINDOW_COLOUR_2}Time since last inspection: {BLACK}{COMMA16} minutes -STR_1888 :{WINDOW_COLOUR_2}Time since last inspection: {BLACK}more than 4 hours -STR_1889 :{WINDOW_COLOUR_2}Down-Time: {MOVE_X}{255}{BLACK}{COMMA16}% -STR_1890 :{SMALLFONT}{BLACK}Select how often a mechanic should check this ride -STR_1891 :No {STRINGID} in park yet! -STR_1894 :{WINDOW_COLOUR_2}{STRINGID} sold: {BLACK}{COMMA32} -STR_1895 :{SMALLFONT}{BLACK}Build new ride/attraction -STR_1896 :{WINDOW_COLOUR_2}Expenditure/Income -STR_1897 :{WINDOW_COLOUR_2}Ride construction -STR_1898 :{WINDOW_COLOUR_2}Ride running costs -STR_1899 :{WINDOW_COLOUR_2}Land purchase -STR_1900 :{WINDOW_COLOUR_2}Landscaping -STR_1901 :{WINDOW_COLOUR_2}Park entrance tickets -STR_1902 :{WINDOW_COLOUR_2}Ride tickets -STR_1903 :{WINDOW_COLOUR_2}Shop sales -STR_1904 :{WINDOW_COLOUR_2}Shop stock -STR_1905 :{WINDOW_COLOUR_2}Food/drink sales -STR_1906 :{WINDOW_COLOUR_2}Food/drink stock -STR_1907 :{WINDOW_COLOUR_2}Staff wages -STR_1908 :{WINDOW_COLOUR_2}Marketing -STR_1909 :{WINDOW_COLOUR_2}Research -STR_1910 :{WINDOW_COLOUR_2}Loan interest -STR_1911 :{BLACK} at {COMMA16}% per year -STR_1912 :{MONTH} -STR_1913 :{BLACK}+{CURRENCY2DP} -STR_1914 :{BLACK}{CURRENCY2DP} -STR_1915 :{RED}{CURRENCY2DP} -STR_1916 :{WINDOW_COLOUR_2}Loan: -STR_1917 :{POP16}{POP16}{POP16}{CURRENCY} -STR_1918 :Can't borrow any more money! -STR_1919 :Not enough cash available! -STR_1920 :Can't pay back loan! -STR_1921 :{SMALLFONT}{BLACK}Start a new game -STR_1922 :{SMALLFONT}{BLACK}Continue playing a saved game -STR_1924 :{SMALLFONT}{BLACK}Exit -STR_1925 :Can't place person here... -STR_1926 :{SMALLFONT} -STR_1927 :{YELLOW}{STRINGID} has broken down -STR_1928 :{RED}{STRINGID} has crashed! STR_1929 :{RED}{STRINGID} still hasn't been fixed{NEWLINE}Check where your mechanics are and consider organizing them better -STR_1930 :{SMALLFONT}{BLACK}Turn on/off tracking information for this guest - (If tracking is on, guest's movements will be reported in the message area) -STR_1931 :{STRINGID} has joined the queue line for {STRINGID} -STR_1932 :{STRINGID} is on {STRINGID} -STR_1933 :{STRINGID} is in {STRINGID} -STR_1934 :{STRINGID} has left {STRINGID} -STR_1935 :{STRINGID} has left the park -STR_1936 :{STRINGID} has bought {STRINGID} -STR_1937 :{SMALLFONT}{BLACK}Show information about the subject of this message -STR_1938 :{SMALLFONT}{BLACK}Show view of guest -STR_1939 :{SMALLFONT}{BLACK}Show view of staff member -STR_1940 :{SMALLFONT}{BLACK}Show happiness, energy, hunger etc. for this guest -STR_1941 :{SMALLFONT}{BLACK}Show which rides this guest has been on -STR_1942 :{SMALLFONT}{BLACK}Show financial information about this guest -STR_1943 :{SMALLFONT}{BLACK}Show guest's recent thoughts -STR_1944 :{SMALLFONT}{BLACK}Show items guest is carrying -STR_1945 :{SMALLFONT}{BLACK}Show orders and options for this staff member -STR_1946 :{SMALLFONT}{BLACK}Select costume for this entertainer -STR_1947 :{SMALLFONT}{BLACK}Show areas patrolled by selected staff type, and locate the nearest staff member -STR_1948 :{SMALLFONT}{BLACK}Hire a new staff member of the selected type -STR_1949 :Financial Summary -STR_1950 :Financial Graph -STR_1951 :Park Value Graph -STR_1952 :Profit Graph -STR_1953 :Marketing -STR_1954 :Research Funding -STR_1955 :{WINDOW_COLOUR_2}Number of circuits: -STR_1956 :{SMALLFONT}{BLACK}Number of circuits of track per ride -STR_1957 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} -STR_1958 :{COMMA16} -STR_1959 :Can't change number of circuits... -STR_1960 :{WINDOW_COLOUR_2}Balloon price: -STR_1961 :{WINDOW_COLOUR_2}Cuddly Toy price: -STR_1962 :{WINDOW_COLOUR_2}Park Map price: -STR_1963 :{WINDOW_COLOUR_2}On-Ride Photo price: -STR_1964 :{WINDOW_COLOUR_2}Umbrella price: -STR_1965 :{WINDOW_COLOUR_2}Drink price: -STR_1966 :{WINDOW_COLOUR_2}Burger price: + STR_1967 :{WINDOW_COLOUR_2}Fries price: -STR_1968 :{WINDOW_COLOUR_2}Ice Cream price: + STR_1969 :{WINDOW_COLOUR_2}Cotton Candy price: -STR_1970 :{WINDOW_COLOUR_2} -STR_1971 :{WINDOW_COLOUR_2} -STR_1972 :{WINDOW_COLOUR_2} -STR_1973 :{WINDOW_COLOUR_2}Pizza price: -STR_1974 :{WINDOW_COLOUR_2} -STR_1975 :{WINDOW_COLOUR_2}Popcorn price: -STR_1976 :{WINDOW_COLOUR_2}Hot Dog price: -STR_1977 :{WINDOW_COLOUR_2}Tentacle price: -STR_1978 :{WINDOW_COLOUR_2}Hat price: + STR_1979 :{WINDOW_COLOUR_2}Candy Apple price: -STR_1980 :{WINDOW_COLOUR_2}T-Shirt price: + STR_1981 :{WINDOW_COLOUR_2}Donut price: -STR_1982 :{WINDOW_COLOUR_2}Coffee price: -STR_1983 :{WINDOW_COLOUR_2} -STR_1984 :{WINDOW_COLOUR_2}Fried Chicken price: -STR_1985 :{WINDOW_COLOUR_2}Lemonade price: -STR_1986 :{WINDOW_COLOUR_2} -STR_1987 :{WINDOW_COLOUR_2} -STR_1988 :Balloon -STR_1989 :Cuddly Toy -STR_1990 :Park Map -STR_1991 :On-Ride Photo -STR_1992 :Umbrella -STR_1993 :Drink -STR_1994 :Burger + STR_1995 :Fries -STR_1996 :Ice Cream + STR_1997 :Cotton Candy -STR_1998 :Empty Can + STR_1999 :Trash -STR_2000 :Empty Burger Box -STR_2001 :Pizza -STR_2002 :Voucher -STR_2003 :Popcorn -STR_2004 :Hot Dog -STR_2005 :Tentacle -STR_2006 :Hat + STR_2007 :Candy Apple -STR_2008 :T-Shirt + STR_2009 :Donut -STR_2010 :Coffee -STR_2011 :Empty Cup -STR_2012 :Fried Chicken -STR_2013 :Lemonade -STR_2014 :Empty Box -STR_2015 :Empty Bottle -STR_2016 :Balloons -STR_2017 :Cuddly Toys -STR_2018 :Park Maps -STR_2019 :On-Ride Photos -STR_2020 :Umbrellas -STR_2021 :Drinks -STR_2022 :Burgers + STR_2023 :Fries -STR_2024 :Ice Creams + STR_2025 :Cotton Candy -STR_2026 :Empty Cans + STR_2027 :Trash -STR_2028 :Empty Burger Boxes -STR_2029 :Pizzas -STR_2030 :Vouchers -STR_2031 :Popcorn -STR_2032 :Hot Dogs -STR_2033 :Tentacles -STR_2034 :Hats + STR_2035 :Candy Apples -STR_2036 :T-Shirts + STR_2037 :Donuts -STR_2038 :Coffees -STR_2039 :Empty Cups -STR_2040 :Fried Chicken -STR_2041 :Lemonade -STR_2042 :Empty Boxes -STR_2043 :Empty Bottles -STR_2044 :a Balloon -STR_2045 :a Cuddly Toy -STR_2046 :a Park Map -STR_2047 :an On-Ride Photo -STR_2048 :an Umbrella -STR_2049 :a Drink -STR_2050 :a Burger + STR_2051 :some Fries -STR_2052 :an Ice Cream + STR_2053 :some Cotton Candy -STR_2054 :an Empty Can + STR_2055 :some Trash -STR_2056 :an Empty Burger Box -STR_2057 :a Pizza -STR_2058 :a Voucher -STR_2059 :some Popcorn -STR_2060 :a Hot Dog -STR_2061 :a Tentacle -STR_2062 :a Hat + STR_2063 :a Candy Apple -STR_2064 :a T-Shirt + STR_2065 :a Donut -STR_2066 :a Coffee -STR_2067 :an Empty Cup -STR_2068 :some Fried Chicken -STR_2069 :some Lemonade -STR_2070 :an Empty Box -STR_2071 :an Empty Bottle -STR_2072 :“{STRINGID}” Balloon -STR_2073 :“{STRINGID}” Cuddly Toy -STR_2074 :Map of {STRINGID} -STR_2075 :On-Ride Photo of {STRINGID} -STR_2076 :“{STRINGID}” Umbrella -STR_2077 :Drink -STR_2078 :Burger + STR_2079 :Fries -STR_2080 :Ice Cream + STR_2081 :Cotton Candy -STR_2082 :Empty Can + STR_2083 :Trash -STR_2084 :Empty Burger Box -STR_2085 :Pizza -STR_2086 :Voucher for {STRINGID} -STR_2087 :Popcorn -STR_2088 :Hot Dog -STR_2089 :Tentacle -STR_2090 :“{STRINGID}” Hat + STR_2091 :Candy Apple -STR_2092 :“{STRINGID}” T-Shirt + STR_2093 :Donut -STR_2094 :Coffee -STR_2095 :Empty Cup -STR_2096 :Fried Chicken -STR_2097 :Lemonade -STR_2098 :Empty Box -STR_2099 :Empty Bottle -STR_2103 :{WINDOW_COLOUR_2}Pretzel price: -STR_2104 :{WINDOW_COLOUR_2}Hot Chocolate price: -STR_2105 :{WINDOW_COLOUR_2}Iced Tea price: -STR_2106 :{WINDOW_COLOUR_2}Funnel Cake price: -STR_2107 :{WINDOW_COLOUR_2}Sunglasses price: -STR_2108 :{WINDOW_COLOUR_2}Beef Noodles price: -STR_2109 :{WINDOW_COLOUR_2}Fried Rice Noodles price: -STR_2110 :{WINDOW_COLOUR_2}Wonton Soup price: -STR_2111 :{WINDOW_COLOUR_2}Meatball Soup price: -STR_2112 :{WINDOW_COLOUR_2}Fruit Juice price: -STR_2113 :{WINDOW_COLOUR_2}Soybean Milk price: -STR_2114 :{WINDOW_COLOUR_2}Sujeonggwa price: -STR_2115 :{WINDOW_COLOUR_2}Sub Sandwich price: -STR_2116 :{WINDOW_COLOUR_2}Cookie price: -STR_2117 :{WINDOW_COLOUR_2} -STR_2118 :{WINDOW_COLOUR_2} -STR_2119 :{WINDOW_COLOUR_2} -STR_2120 :{WINDOW_COLOUR_2}Roast Sausage price: -STR_2121 :{WINDOW_COLOUR_2} -STR_2125 :Pretzel -STR_2126 :Hot Chocolate -STR_2127 :Iced Tea -STR_2128 :Funnel Cake -STR_2129 :Sunglasses -STR_2130 :Beef Noodles -STR_2131 :Fried Rice Noodles -STR_2132 :Wonton Soup -STR_2133 :Meatball Soup -STR_2134 :Fruit Juice -STR_2135 :Soybean Milk -STR_2136 :Sujeonggwa -STR_2137 :Sub Sandwich -STR_2138 :Cookie -STR_2139 :Empty Bowl -STR_2140 :Empty Drink Carton -STR_2141 :Empty Juice Cup -STR_2142 :Roast Sausage -STR_2143 :Empty Bowl -STR_2147 :Pretzels -STR_2148 :Hot Chocolates -STR_2149 :Iced Teas -STR_2150 :Funnel Cakes -STR_2151 :Sunglasses -STR_2152 :Beef Noodles -STR_2153 :Fried Rice Noodles -STR_2154 :Wonton Soups -STR_2155 :Meatball Soups -STR_2156 :Fruit Juices -STR_2157 :Soybean Milks -STR_2158 :Sujeonggwa -STR_2159 :Sub Sandwiches -STR_2160 :Cookies -STR_2161 :Empty Bowls -STR_2162 :Empty Drink Cartons -STR_2163 :Empty Juice cups -STR_2164 :Roast Sausages -STR_2165 :Empty Bowls -STR_2169 :a Pretzel -STR_2170 :a Hot Chocolate -STR_2171 :an Iced Tea -STR_2172 :a Funnel Cake -STR_2173 :a pair of Sunglasses -STR_2174 :some Beef Noodles -STR_2175 :some Fried Rice Noodles -STR_2176 :some Wonton Soup -STR_2177 :some Meatball Soup -STR_2178 :a Fruit Juice -STR_2179 :some Soybean Milk -STR_2180 :some Sujeonggwa -STR_2181 :a Sub Sandwich -STR_2182 :a Cookie -STR_2183 :an Empty Bowl -STR_2184 :an Empty Drink Carton -STR_2185 :an Empty Juice Cup -STR_2186 :a Roast Sausage -STR_2187 :an Empty Bowl -STR_2191 :Pretzel -STR_2192 :Hot Chocolate -STR_2193 :Iced Tea -STR_2194 :Funnel Cake -STR_2195 :Sunglasses -STR_2196 :Beef Noodles -STR_2197 :Fried Rice Noodles -STR_2198 :Wonton Soup -STR_2199 :Meatball Soup -STR_2200 :Fruit Juice -STR_2201 :Soybean Milk -STR_2202 :Sujeonggwa -STR_2203 :Sub Sandwich -STR_2204 :Cookie -STR_2205 :Empty Bowl -STR_2206 :Empty Drink Carton -STR_2207 :Empty Juice Cup -STR_2208 :Roast Sausage -STR_2209 :Empty Bowl STR_2353 :{WINDOW_COLOUR_2}Trash swept: {BLACK}{COMMA16} STR_2354 :{WINDOW_COLOUR_2}Trash cans emptied: {BLACK}{COMMA16} STR_2707 :Use system dialog window -STR_2735 :{COMMA16}km/h - STR_2756 :Remove trash STR_2806 :{RED}Guests are complaining about the disgusting state of the paths in your park{NEWLINE}Check where your handymen are and consider organizing them better @@ -923,6 +238,8 @@ STR_6166 :{SMALLFONT}{BLACK}Synchronizes each frame displayed to the monitor' STR_6206 :Gray stucco STR_6212 :Gray sandstone STR_6274 :Can't set color scheme... +STR_6307 :Color scheme: {BLACK}{STRINGID} +STR_6328 :{SMALLFONT}{BLACK}With this option enabled, giant screenshots will have a transparent background instead of the default black color. ############# # Scenarios # diff --git a/data/language/es-ES.txt b/data/language/es-ES.txt index 8850ccff5f..0d8e844569 100644 --- a/data/language/es-ES.txt +++ b/data/language/es-ES.txt @@ -552,8 +552,8 @@ STR_1167 :No se puede subir el nivel del agua... STR_1168 :Opciones STR_1169 :(Ninguno) STR_1170 :{STRING} -STR_1171 :{RED}Cerrada - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Cerrada +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Construir senderos STR_1174 :Cartel de señal interfiere STR_1175 :No se puede construir esto sobre una pendiente @@ -1109,7 +1109,7 @@ STR_1726 :¡Terreno no a la venta! STR_1727 :¡Derechos de construcción no a la venta! STR_1728 :No puedes comprar derechos de construcción aquí... STR_1729 :¡El terreno no es propiedad del parque! -STR_1730 :{RED}Cerrado - - +STR_1730 :{RED}Cerrado STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Construir STR_1733 :Modo @@ -2237,7 +2237,7 @@ STR_2977 :Nombrar personal STR_2978 :Escribe un nombre para este miembro STR_2979 :No se puede nombrar este miembro del personal... STR_2980 :Demasiadas banderas en juego -STR_2981 :{RED}No pasar - - +STR_2981 :{RED}No pasar STR_2982 :Texto del letrero STR_2983 :Introduce nuevo texto para este letrero: STR_2984 :No se puede establecer texto para el letrero... @@ -2416,7 +2416,6 @@ STR_3190 :Decoración Senderos STR_3191 :Escenarios STR_3192 :Entrada del parque STR_3193 :Agua -STR_3194 :Descripción del escenario STR_3195 :Lista de inventos STR_3196 :{WINDOW_COLOUR_2}Grupo Investigación: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Elementos ya disponibles al comienzo de la partida: @@ -3224,7 +3223,7 @@ STR_5762 :Yuan Chino (CN¥) STR_5763 :Todos los archivos STR_5764 :Tipo de Atracción inválido STR_5765 :No se puede editar una atracción de un tipo no válido -STR_5766 : +STR_5766 :Forinto Húngaro (Ft) STR_5767 :Ingresos STR_5768 :Clientes totales STR_5769 :Beneficio total @@ -3761,6 +3760,31 @@ STR_6300 :{SMALLFONT}{BLACK}Descargar todos los objetos faltantes si están d STR_6301 :{SMALLFONT}{BLACK}Copia el nombre del objeto seleccionado al portapapeles. STR_6302 :{SMALLFONT}{BLACK}Copia la lista completa de objetos faltantes en el portapapeles. STR_6303 :Descargando objeto ({COMMA16} / {COMMA16}): [{STRING}] +STR_6305 :Multihilo +STR_6306 :{SMALLFONT}{BLACK}Opción experimental para usar múltiples hilos en el renderizado, puede causar inestabilidad. +STR_6307 :Esquema de color: {BLACK}{STRINGID} +STR_6309 :Reconectar +STR_6310 :{WINDOW_COLOUR_2}Posición: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Siguiente: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(superficie) +STR_6313 :(pendiente {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Dest: {BLACK}{INT32}, {INT32} tolerancia {INT32} +STR_6315 :{WINDOW_COLOUR_2}Objetivo busqueda de ruta: {BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6316 :{WINDOW_COLOUR_2}Historia busqueda de ruta: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6318 :Desincronización detectada en la red.{NEWLINE}Archivo de registro: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Bloque de freno cerrado +STR_6320 :{WINDOW_COLOUR_2}Indestructible +STR_6321 :{WINDOW_COLOUR_2}Adición rota +STR_6322 :{WINDOW_COLOUR_2}Id de Sprite: {BLACK}{INT32} +STR_6323 :Simulando +STR_6324 :Simular +STR_6325 :{SMALLFONT}{BLACK}Simular atracción +STR_6326 :No se puede simular {POP16}{POP16}{POP16}{STRINGID}... +STR_6327 :Fondo transparente en capturas enormes +STR_6328 :{SMALLFONT}{BLACK}Si se activa esta opción, las capturas de pantallas enormes tendrán un fondo transparente en lugar del fondo negro por defecto. +STR_6329 :{STRING}{STRINGID} +STR_6330 :Descargando [{STRING}] de {STRING} ({COMMA16} / {COMMA16}) ############## # Escenarios # diff --git a/data/language/fr-FR.txt b/data/language/fr-FR.txt index 4dfafbbfe5..182e787bb6 100644 --- a/data/language/fr-FR.txt +++ b/data/language/fr-FR.txt @@ -553,8 +553,8 @@ STR_1167 :Impossible de surélever le niveau de l'eau ici... STR_1168 :Options STR_1169 :(Aucun) STR_1170 :{STRING} -STR_1171 :{RED}Fermé - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Fermé +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Construire des allées et des files d'attentes STR_1174 :Panneau d'affichage sur le passage STR_1175 :Impossible de construire ceci sur une allée en pente @@ -1111,7 +1111,7 @@ STR_1726 :Ce terrain n'est pas a vendre ! STR_1727 :Les droits de construction ne sont pas à vendre ! STR_1728 :Impossible d'acheter les droits de construction ici... STR_1729 :Ce terrain n'appartient pas au parc ! -STR_1730 :{RED}Fermé - - +STR_1730 :{RED}Fermé STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Construction STR_1733 :Mode @@ -2241,7 +2241,7 @@ STR_2977 :Nom de l'employé STR_2978 :Entrer le nouveau nom de cet employé : STR_2979 :Impossible de renommer cet employé... STR_2980 :Trop de bannières dans cette partie -STR_2981 :{RED}Accès interdit - - +STR_2981 :{RED}Accès interdit STR_2982 :Texte bannière STR_2983 :Saisir nouveau texte pour cette bannière : STR_2984 :Impossible de placer nouveau texte pour la bannière... @@ -2420,7 +2420,6 @@ STR_3190 :Suppléments allées STR_3191 :Groupes de décor STR_3192 :Entrée du parc STR_3193 :Etendue d'eau -STR_3194 :Description du scénario STR_3195 :Liste d'inventions STR_3196 :{WINDOW_COLOUR_2}Groupe de recherche : {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Eléments pré-inventés au début d'une partie : @@ -3226,7 +3225,7 @@ STR_5762 :Yuan Chinois (CN¥) STR_5763 :Tous les fichiers STR_5764 :Type d'attraction invalide STR_5765 :Impossible d'éditer les attractions de type invalide -STR_5766 : +STR_5766 :Forint Hongrois (Ft) STR_5767 :Revenu STR_5768 :Clients total STR_5769 :Bénéfice total @@ -3766,6 +3765,35 @@ STR_6300 :{SMALLFONT}{BLACK}Télécharge tous les objets manquants s'ils sont STR_6301 :{SMALLFONT}{BLACK}Copie les noms des objets sélectionnés dans le presse-papier. STR_6302 :{SMALLFONT}{BLACK}Copie la liste des objets manquants dans le presse-papier. STR_6303 :Téléchargement de l'objet ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :Ouvrir le sélecteur de paysage +STR_6305 :Multithreading +STR_6306 :{SMALLFONT}{BLACK}Option expérimentale pour utiliser plusieurs threads pour le rendu, peut causer des problèmes d'instabilité. +STR_6307 :Palette de couleurs : {BLACK}{STRINGID} +STR_6308 :"{STRINGID}{OUTLINE}{TOPAZ}"{NEWLINE}{STRINGID} +STR_6309 :Reconnecter +STR_6310 :{WINDOW_COLOUR_2}Position : {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Suivant : {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(surface) +STR_6313 :(slope {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Destination : {BLACK}{INT32}, {INT32} tolérance {INT32} +STR_6315 :{WINDOW_COLOUR_2}Objectif recherche chemin : {BLACK}{INT32}, {INT32}, {INT32} dir. {INT32} +STR_6316 :{WINDOW_COLOUR_2}Historique recherche chemin : +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6318 :Desynchronisation réseau détecté.{NEWLINE}Fichier journal : {STRING} +STR_6319 :{WINDOW_COLOUR_2}Bloc de frein fermé +STR_6320 :{WINDOW_COLOUR_2}Indestructible +STR_6321 :{WINDOW_COLOUR_2}L'ajout est cassé +STR_6322 :{WINDOW_COLOUR_2}Identifiant sprite : {BLACK}{INT32} +STR_6323 :Simulation +STR_6324 :Simuler +STR_6325 :{SMALLFONT}{BLACK}Simule l'attraction +STR_6326 :Impossible de similar {POP16}{POP16}{POP16}{STRINGID}... +STR_6327 :Fond transparent pour les captures d'écran géantes +STR_6328 :{SMALLFONT}{BLACK}Quand cette option est activée, les captures d'écran auront un fond transparent plutôt qu'un fond noir. +STR_6329 :{STRING}{STRINGID} +STR_6330 :Téléchargement [{STRING}] depuis {STRING} ({COMMA16} / {COMMA16}) +STR_6331 :Créer des canards +STR_6332 :Supprimer des canards ############# # Scenarios # diff --git a/data/language/hu-HU.txt b/data/language/hu-HU.txt index ea3aadac3f..128bc3bc80 100644 --- a/data/language/hu-HU.txt +++ b/data/language/hu-HU.txt @@ -40,7 +40,7 @@ STR_0035 :Körhinta STR_0036 :Ismeretlen bódé (22) STR_0037 :Információs bódé STR_0038 :WC -STR_0039 :Óriáskerék +STR_0039 :Óriáskerék STR_0040 :Mozgásszimulátor STR_0041 :3D mozi STR_0042 :Top Spin @@ -113,9 +113,9 @@ STR_0527 :Egy sima acélpályás hullámvasút, amelyen függőleges hurkok i STR_0528 :Az utasok gumicsónakokban utaznak lefelé egy kanyargó, félkör vagy teljes kör alakú csőpályán STR_0529 :A hullámvasút bányavonat témájú vonatai acél hullámvasútpályán futnak, amit egy régi vasúti pályához tettek hasonlatossá STR_0530 :A kocsik egy acélkábelről lógnak, ami folyamatosan a játék egyik végétől a másikig fut, majd újra vissza -STR_0531 :Egy kompakt, acélpályás hullámvasút, amelyen a vonat dugóhúzókon es hurkokon halad át +STR_0531 :Egy kompakt, acélpályás hullámvasút, amelyen a vonat dugóhúzókon és hurkokon halad át STR_0532 :Az útvesztő közel 2 méteres sövényekből vagy falakból áll és a vendégek addig bolyonganak benne, amíg rá nem lelnek a kijáratra -STR_0533 :Egy belső lépcsős faépület kívül spirál csúszdával, amelyről csúszómatracokal lehet lecsúszni +STR_0533 :Egy belső lépcsős faépület kívül spirál csúszdával, amelyről csúszómatracokkal lehet lecsúszni STR_0534 :Önvezető benzinmotoros gokartok STR_0535 :Farönkhöz hasonlító csónakok utaznak a vízcsatorna mentén, amelyek nagyokat csobbannak a meredek lejtőknél, eláztatva az utasokat STR_0536 :Kerek csónakok kanyarognak egy széles vízcsatorna mentén, miközben csobogó vízesések és habzó zuhatagok biztosítják az izgalmat az utasok számára @@ -129,12 +129,12 @@ STR_0545 :Egy hagyományos forgó körhinta, faragott falovakkal STR_0547 :Egy bódé, ahol parktérképeket és esernyőket vehetnek a vendégek STR_0548 :Egy illemhely STR_0549 :Nagy forgó kerék, nyitott székekkel -STR_0550 :Az utasok filmet néznek a mozgászimulátor kabinjában, miközben azt egy hidraulikus kar forgatja és rázza +STR_0550 :Az utasok filmet néznek a mozgásszimulátor kabinjában, miközben azt egy hidraulikus kar forgatja és rázza STR_0551 :3D-s filmeket vetítő mozi egy geodéziai gömb alakú épületben STR_0552 :A nagy forgó karokkal felfüggesztett kabinban az utasok előre-hátra forognak és bukfenceznek -STR_0553 :Koncentrikus forgó gyűrűk, amelyekben az utasok szabadon forghatnak minden irányba +STR_0553 :Koncentrikus forgó gyűrűk, amelyekben az utasok szabadon foroghatnak minden irányba STR_0554 :A kocsi aszinkronmotoros gyorsítással hagyja el az állomást egy hosszú vízszintes pálya mentén, hogy felmenjen egy teljesen függőleges emelkedőn, ahonnan szabadeséssel zuhan le, hogy visszatérjen az állomásra -STR_0555 :Az utasok egy függőleges torony lifjtében utazhatnak a szintek között +STR_0555 :Az utasok egy függőleges torony liftjében utazhatnak a szintek között STR_0556 :Extra széles kocsik zuhannak le a teljesen függőleges pályán, a tökéletes szabadesés hullámvasút élmény kedvéért STR_0557 :Egy ATM (bankautomata) a pénz nélkül maradt vendégek számára STR_0558 :Az utasok a három hosszú, forgó kar végei körül forgó székpárokban utaznak @@ -142,13 +142,21 @@ STR_0559 :Nagy tematizált épület, tele ijesztő folyosókkal és kísérte STR_0560 :Egy hely, ahol a rosszul lett vendégek gyorsabban összeszedhetik magukat STR_0561 :Cirkuszi állatprodukció egy nagy sátorban STR_0562 :Motoros autók utaznak egy többszintes pálya mentén, miközben kísérteties díszletek és speciális effektusok mellett haladnak el +STR_0563 :Kényelmes, egyszerű biztonsági rudas vonatokban ülve élvezhetik az utasok a hatalmas, sima eséseket és csavaros pályákat, valamint a bőséges „légi időt” az emelkedők után +STR_0564 :Ez a fa pályán futó hullámvasút gyors, durva, hangos és „kontrollvesztett” utazási élményt nyújt, bőséges „légi idővel” +STR_0565 :Egy egyszerű, kizárólag enyhe lejtőkre és fordulókra képes fa hullámvasút, amelyen a kocsikat csak az oldalirányú súrlódásos kerekek és a gravitáció tartja a pályán +STR_0566 :Különálló hullámvasút-kocsik száguldanak egy szoros, cikcakkos alaprajzú, éles kanyarokat és rövid meredek eséseket tartalmazó pályán +STR_0569 :A pálya alatti speciális rögzítőhámokban utazva az utasok megtapasztalhatják a repülés élményét, ahogy szelik a levegőt +STR_0573 :Motoros, helikopter formájú kocsik haladnak egy acélpályán, a pedálozó utasok irányításával STR_0578 :A kocsik egy abroncsokkal körbefogott, meredek esésekkel és palástorsókkal teli pálya mentén haladnak STR_0579 :Egy enyhe minigolf-játék +STR_0580 :Egy hatalmas acél hullámvasút, amely sima esésekre és 90 méternél magasabb emelkedőkre is képes STR_0582 :Önvezető légpárnás járművek STR_0584 :Speciális, az utasok pedálozásával hajtott biciklik futnak egy acél egysínű pályán STR_0588 :Különálló kocsik futnak a cikcakkos, hajtűkanyarokkal és éles esésekkel teli pálya alatt STR_0589 :Egy nagy, repülőszőnyeg-témájú kocsi, amely ciklikusan fel-le mozog a 4 kar végén STR_0590 :Az utasok egy alámerült tengeralattjáróban utaznak végig egy víz alatti pályán +STR_0591 :A tutaj alakú csónakok szelíden kanyarognak a folyó pályája mentén STR_0599 :Egy kompakt hullámvasút különálló kocsikkal és sima, csavaros esésekkel STR_0600 :Motoros bányavonatok futnak egy sima és csavaros pálya mentén STR_0602 :A hullámvasút vonatai aszinkronmotoros gyorsítással hagyják el az állomást, hogy átszáguldjanak a csavaros fordítókon @@ -314,7 +322,7 @@ STR_0951 :Kilépés a játékból STR_0952 :Kilépés a játékból STR_0953 :Táj betöltése STR_0954 : -STR_0955 :{SMALLFONT}{BLACK}Válasszd ki az ülések forgási szögét ezen a pályaszakaszon +STR_0955 :{SMALLFONT}{BLACK}Válaszd ki az ülések forgási szögét ezen a pályaszakaszon STR_0956 :-180° STR_0957 :-135° STR_0958 :-90° @@ -413,7 +421,7 @@ STR_1047 :A játék mentése meghiúsult! STR_1048 :A pálya mentése meghiúsult! STR_1049 :A táj mentése meghiúsult! STR_1050 :Sikertelen betöltés...{NEWLINE}A fájl érvénytelen adatot tartalmaz! -STR_1051 :Láthatatlan támpillérek +STR_1051 :Láthatatlan állványzatok STR_1052 :Láthatatlan emberek STR_1053 :{SMALLFONT}{BLACK}Játékok/épületek a parkban STR_1054 :{SMALLFONT}{BLACK}Játék/épület átnevezése @@ -463,7 +471,7 @@ STR_1097 :Motoros kilövésű térközszakaszos üzemmód STR_1098 :{POP16}{STRINGID} végébe megy STR_1099 :Utasokra vár itt: {POP16}{STRINGID} STR_1100 :Várja, hogy elhagyhassa ezt: {POP16}{STRINGID} -STR_1101 :{POP16}{STRINGID} elhagyása +STR_1101 :Elhagyja ezt: {POP16}{STRINGID} STR_1102 :{VELOCITY} sebességgel halad STR_1103 :Megérkezik ide: {POP16}{STRINGID} STR_1104 :Kirakja az utasokat itt: {POP16}{STRINGID} @@ -512,11 +520,18 @@ STR_1166 :Nem süllyesztheted le itt a vízszintet.... STR_1167 :Nem emelheted fel itt a vízszintet.... STR_1146 :Még nem épült bejárat STR_1147 :Még nem épült kijárat +STR_1148 :Negyed terhelés +STR_1149 :Fél terhelés +STR_1150 :Háromnegyed terhelés +STR_1151 :Teljes terhelés +STR_1152 :Bármekkora terhelés STR_1153 :Magasságjelek a játékok pályáin STR_1154 :Magasságjelek a talajon STR_1155 :Magasságjelek az utakon STR_1156 :{MOVE_X}{10}{STRINGID} STR_1157 :✓{MOVE_X}{10}{STRINGID} +STR_1159 :{SMALLFONT}{BLACK}Díszletek, kertek és egyéb kiegészítők lerakása +STR_1160 :{SMALLFONT}{BLACK}Tavak és vizek létrehozása/módosítása STR_1158 :Nem távolítható el... STR_1161 :Nem rakhatod ide... STR_1162 :{OUTLINE}{TOPAZ}{STRINGID} @@ -526,12 +541,15 @@ STR_1165 :{STRINGID} - {STRINGID} {COMMA16} STR_1168 :Beállítások STR_1169 :(semmi) STR_1170 :{STRING} -STR_1171 :{RED}Zárva - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Zárva +STR_1172 :{YELLOW}{STRINGID} +STR_1173 :{SMALLFONT}{BLACK}Utak és várósorok építése STR_1174 :Egy hirdetőtábla útban van STR_1175 :Nem építhető lejtős úton STR_1176 :Nem építhető ide út... STR_1177 :Nem távolítható el az út... +STR_1178 :A talaj lejtése nem megfelelő +STR_1179 :Az út akadályozza STR_1180 :Nem építhető víz alá! STR_1181 :Utak STR_1182 :Típus @@ -558,10 +576,17 @@ STR_1202 :Egy ember áll a sorban STR_1203 :{COMMA16} ember áll a sorban STR_1204 :{COMMA16} perc a sorbanállási idő STR_1205 :{COMMA16} perc a sorbanállási idő +STR_1206 :{WINDOW_COLOUR_2}Várjon erre: +STR_1207 :{WINDOW_COLOUR_2}Távozzon, ha másik vonat érkezik az állomásra +STR_1208 :{WINDOW_COLOUR_2}Távozzon, ha másik csónak érkezik az állomásra +STR_1209 :{SMALLFONT}{BLACK}Válaszd ki, hogy várakozzon-e utasokra indulás előtt +STR_1210 :{SMALLFONT}{BLACK}Válaszd ki, hogy távozzon-e, ha egy másik jármű érkezik ugyanarra az állomásra STR_1211 :{WINDOW_COLOUR_2}Minimum várakozási idő STR_1212 :{WINDOW_COLOUR_2}Maximum várakozási idő: STR_1213 :{SMALLFONT}{BLACK}Az indulás előtti minimum várakozási idő kiválasztása STR_1214 :{SMALLFONT}{BLACK}Az indulás előtti maximum várakozási idő kiválasztása +STR_1215 :{WINDOW_COLOUR_2}Szinkronizálás a szomszédos állomásokkal +STR_1216 :{SMALLFONT}{BLACK}Válaszd ki, hogy szinkronizálva legyen-e a távozás az összes szomszédos állomással (a ’versenyzéshez’) STR_1217 :{COMMA16} másodperc STR_1218 :{BLACK}+ STR_1219 :{BLACK}- @@ -705,11 +730,15 @@ STR_1354 :{WINDOW_COLOUR_2}Legmagasabb esés: {BLACK}{LENGTH} STR_1355 :{WINDOW_COLOUR_2}Esések: {BLACK}{COMMA16} STR_1356 :{WINDOW_COLOUR_2}Fordítások: {BLACK}{COMMA16} STR_1357 :{WINDOW_COLOUR_2}Lyukak: {BLACK}{COMMA16} -STR_1358 :{WINDOW_COLOUR_2}Összes levegőben töltött idő: {BLACK}{COMMA2DP32} mp +STR_1358 :{WINDOW_COLOUR_2}Összes „légi” idő: {BLACK}{COMMA2DP32} mp STR_1359 :{WINDOW_COLOUR_2}Sorbanállási idő: {BLACK}{COMMA16} perc STR_1360 :{WINDOW_COLOUR_2}Sorbanállási idő: {BLACK}{COMMA16} perc STR_1361 :Nem változtatható meg a sebesség... STR_1362 :Nem változtatható meg a kilövési sebesség... +STR_1363 :Túl magas az állványzat számára! +STR_1364 :A fenti pálya állványzata nem hosszabbítható meg jobban! +STR_1365 :Kis csavar (balra) +STR_1366 :Kis csavar (jobbra) STR_1367 :Félhurok STR_1368 :Fél dugóhúzó (balra) STR_1369 :Fél dugóhúzó (jobbra) @@ -718,8 +747,14 @@ STR_1371 :Orsó (jobbra) STR_1372 :Kilövős felvonószakasz STR_1373 :Nagy félhurok (balra) STR_1374 :Nagy félhurok (jobbra) +STR_1375 :Felső átvitel +STR_1376 :Alsó átvitel STR_1377 :Palástorsó (balra) STR_1378 :Palástorsó (jobbra) +STR_1379 :Fordító (balra) +STR_1380 :Fordító (jobbra) +STR_1381 :Ívelt felvonószakasz (balra) +STR_1382 :Ívelt felvonószakasz (jobbra) STR_1383 :Negyed hurok STR_1384 :{YELLOW}{STRINGID} STR_1385 :{SMALLFONT}{BLACK}Egyéb pályaelemek @@ -739,11 +774,15 @@ STR_1398 :{SMALLFONT}{BLACK}Mérések és tesztadatok STR_1399 :{SMALLFONT}{BLACK}Grafikonok STR_1400 :Bejárat STR_1401 :Kijárat +STR_1402 :{SMALLFONT}{BLACK}Bejárat építése vagy mozgatása a játékhoz/épülethez +STR_1403 :{SMALLFONT}{BLACK}Kijárat építése vagy mozgatása a játékhoz/épülethez STR_1404 :{SMALLFONT}{BLACK}Forgatás 90°-kal +STR_1405 :{SMALLFONT}{BLACK}Kép tükrözése +STR_1406 :{SMALLFONT}{BLACK}Díszletek be/ki (ha elérhető ehhez a tervhez) STR_1407 :{WINDOW_COLOUR_2}Építés STR_1408 :{WINDOW_COLOUR_2}Ár: {BLACK}{CURRENCY} STR_1409 :Be- és kilépő terasz -STR_1410 :Fűggőleges torony +STR_1410 :Függőleges torony STR_1411 :{STRINGID} útban van STR_1412 :{WINDOW_COLOUR_3}A játéktípus számára nem elérhető az adatnaplózás STR_1413 :{WINDOW_COLOUR_3}Az adatnaplózás akkor indul, amikor a következő {STRINGID} elhagyja ezt: {STRINGID} @@ -756,6 +795,8 @@ STR_1419 :{SMALLFONT}{BLACK}{VELOCITY} STR_1420 :{SMALLFONT}{BLACK}{LENGTH} STR_1421 :{SMALLFONT}{BLACK}{COMMA16}g STR_1422 :{SMALLFONT}{BLACK}Adatnaplózás: {POP16}{STRINGID} +STR_1423 :{SMALLFONT}{BLACK}Várósor +STR_1424 :{SMALLFONT}{BLACK}Út STR_1425 :Út STR_1426 :Várósor STR_1427 :{WINDOW_COLOUR_2}Óránkénti vendégek: {BLACK}{COMMA32} @@ -785,11 +826,14 @@ STR_1450 :{INLINE_SPRITE}{09}{20}{00}{00}{SPRITE} {STRINGID}{NEWLINE}({STRING STR_1451 :{STRINGID}{NEWLINE}({STRINGID}) STR_1452 :Vendég neve STR_1453 :Írd be a vendég nevét: -STR_1454 :Nem nevezető át a vendég... +STR_1454 :Nem nevezhető át a vendég... STR_1455 :Érvénytelen vendégnév STR_1456 :{WINDOW_COLOUR_2}Elköltött pénz: {BLACK}{CURRENCY2DP} STR_1457 :{WINDOW_COLOUR_2}Pénz a zsebében: {BLACK}{CURRENCY2DP} STR_1458 :{WINDOW_COLOUR_2}Parkban töltött idő: {BLACK}{REALTIME} +STR_1459 :Pálya stílusa +STR_1460 :{SMALLFONT}{BLACK}’U’ alakú nyílt pálya +STR_1461 :{SMALLFONT}{BLACK}’O’ alakú zárt pálya STR_1462 :Túl meredek a felvonószakaszhoz STR_1463 :Vendégek STR_1464 :Spirálpálya felfelé (kicsi) @@ -879,7 +923,7 @@ STR_1547 : STR_1548 : STR_1549 : STR_1550 :{SMALLFONT}„Hűha!” -STR_1551 :{SMALLFONT}„Az a felettéb különös érzésem van, hogy valaki figyel” +STR_1551 :{SMALLFONT}„Az a felettébb különös érzésem van, hogy valaki figyel” STR_1552 :{SMALLFONT}„{STRINGID} lufija túl sokba kerül számomra” STR_1553 :{SMALLFONT}„{STRINGID} plüssállata túl sokba kerül számomra” STR_1554 :{SMALLFONT}„{STRINGID} parktérképe túl sokba kerül számomra” @@ -923,7 +967,7 @@ STR_1591 :{SMALLFONT}„{STRINGID} napszemüvege igazán megéri az árát” STR_1592 :{SMALLFONT}„{STRINGID} marhahúsos tésztája igazán megéri az árát” STR_1593 :{SMALLFONT}„{STRINGID} pirított rizstésztája igazán megéri az árát” STR_1594 :{SMALLFONT}„{STRINGID} wonton levese igazán megéri az árát” -STR_1595 :{SMALLFONT}„{STRINGID} húsgombócevese igazán megéri az árát” +STR_1595 :{SMALLFONT}„{STRINGID} húsgombóclevese igazán megéri az árát” STR_1596 :{SMALLFONT}„{STRINGID} gyümölcsleve igazán megéri az árát” STR_1597 :{SMALLFONT}„{STRINGID} szójateje igazán megéri az árát” STR_1598 :{SMALLFONT}„{STRINGID} szudzsonggvája igazán megéri az árát” @@ -955,7 +999,7 @@ STR_1623 :{SMALLFONT}„{STRINGID} napszemüvege túl sokba kerül számomra STR_1624 :{SMALLFONT}„{STRINGID} marhahúsos tésztája túl sokba kerül számomra” STR_1625 :{SMALLFONT}„{STRINGID} pirított rizstésztája túl sokba kerül számomra” STR_1626 :{SMALLFONT}„{STRINGID} wonton levese túl sokba kerül számomra” -STR_1627 :{SMALLFONT}„{STRINGID} húsgombócevese túl sokba kerül számomra” +STR_1627 :{SMALLFONT}„{STRINGID} húsgombóclevese túl sokba kerül számomra” STR_1628 :{SMALLFONT}„{STRINGID} gyümölcsleve túl sokba kerül számomra” STR_1629 :{SMALLFONT}„{STRINGID} szójateje túl sokba kerül számomra” STR_1630 :{SMALLFONT}„{STRINGID} szudzsonggvája túl sokba kerül számomra” @@ -981,6 +1025,8 @@ STR_1649 :{SMALLFONT}„Mindjárt elfogy a pénzem!” STR_1650 :{SMALLFONT}„Hű! Egy új játék épül!” STR_1653 :{SMALLFONT}„...és itt vagyunk ezen: {STRINGID}!” STR_1654 :{WINDOW_COLOUR_2}Legutóbbi gondolatok: +STR_1655 :{SMALLFONT}{BLACK}Út építése a földön +STR_1656 :{SMALLFONT}{BLACK}Út építése hídon vagy alagútban STR_1657 :{WINDOW_COLOUR_2}Előnyben részesített játék- STR_1658 :{WINDOW_COLOUR_2}intenzitás: {BLACK}kevesebb mint {COMMA16} STR_1659 :{WINDOW_COLOUR_2}intenzitás: {BLACK}{COMMA16} és {COMMA16} között @@ -997,6 +1043,7 @@ STR_1669 :{WINDOW_COLOUR_2}Elégedettség: {BLACK}{COMMA16}% STR_1670 :{WINDOW_COLOUR_2}Összes vendég: {BLACK}{COMMA32} STR_1671 :{WINDOW_COLOUR_2}Teljes nyereség: {BLACK}{CURRENCY2DP} STR_1672 :Fékek +STR_1673 :Forgásvezérlő kapcsolószakasz STR_1674 :Fékezési sebesség STR_1675 :{POP16}{VELOCITY} STR_1676 :{SMALLFONT}{BLACK}Sebességlimit beállítása a fékek számára @@ -1020,12 +1067,14 @@ STR_1693 :{SMALLFONT}{BLACK}Vendégek STR_1694 :{SMALLFONT}{BLACK}Személyzet STR_1695 :{SMALLFONT}{BLACK}Bevételek és kiadások STR_1696 :{SMALLFONT}{BLACK}Vendég információk +STR_1697 :Nem rakhatod várósorra +STR_1698 :Csak várósorra rakhatod STR_1699 :Túl sok ember van a játékban STR_1700 :Új mindenes felvétele STR_1701 :Új gépész felvétele STR_1702 :Új biztonsági őr felvétele STR_1703 :Új szórakoztató felvétele -STR_1704 :Nem vehetsz fel új alkamazottat... +STR_1704 :Nem vehetsz fel új alkalmazottat... STR_1705 :{SMALLFONT}{BLACK}Az alkalmazott kirúgása STR_1706 :{SMALLFONT}{BLACK}Személy áthelyezése új helyre STR_1707 :Túl sok alkalmazott van a játékban @@ -1051,18 +1100,27 @@ STR_1726 :A terület nem eladó! STR_1727 :Az építési jogok nem eladók! STR_1728 :Nem vásárolhatók meg az építési jogok... STR_1729 :A terület nem a park tulajdona! -STR_1730 :{RED}Zárva - - +STR_1730 :{RED}Zárva STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Építés STR_1733 :Mód +STR_1734 :{WINDOW_COLOUR_2}Körök száma: +STR_1735 :{SMALLFONT}{BLACK}A pályán megtett körök száma STR_1736 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} +STR_1738 :Nem változtatható meg a körök száma... STR_1739 :Vendég {INT32} nyerte a versenyt STR_1740 :{STRINGID} nyerte a versenyt STR_1741 :Még nincs megépítve ! STR_1742 :{WINDOW_COLOUR_2}Max. ember a játékon: STR_1743 :{SMALLFONT}{BLACK}Maximum ennyi ember lehet egyszerre a játékon STR_1744 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} +STR_1746 :Nem változtathatod meg... +STR_1747 :{WINDOW_COLOUR_2}Időkorlát: +STR_1748 :{SMALLFONT}{BLACK}A játék időkorlátja STR_1749 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{DURATION} +STR_1751 :Nem változtatható meg a játék időkorlátja... +STR_1752 :{SMALLFONT}{BLACK}A park egyedi vendégeinek listázása +STR_1753 :{SMALLFONT}{BLACK}A park vendégeinek összegző listázása STR_1754 :{BLACK}{COMMA16} vendég STR_1755 :{BLACK}{COMMA16} vendég STR_1756 :{WINDOW_COLOUR_2}Belépési díj: @@ -1071,12 +1129,18 @@ STR_1758 :{SMALLFONT}{BLACK}Építő mód STR_1759 :{SMALLFONT}{BLACK}Mozgató mód STR_1760 :{SMALLFONT}{BLACK}Kitöltő mód STR_1761 :{SMALLFONT}{BLACK}Útvesztő építése ebbe az irányba +STR_1762 :Vízesések +STR_1763 :Vadvizek +STR_1764 :Farönk bukkanók STR_1765 :Élményfotó-szakasz +STR_1766 :Fordító korong STR_1767 :Forgó alagút STR_1768 :Nem változtatható meg a lengések száma... STR_1769 :{WINDOW_COLOUR_2}Lengések száma: STR_1770 :{SMALLFONT}{BLACK}A teljes lengések száma STR_1771 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} +STR_1773 :Csak egy élményfotó-szakasz engedélyezett játékonként +STR_1774 :Csak egy kábeles felvonószakasz engedélyezett játékonként STR_1777 :Játékok zenéje STR_1778 :{STRINGID} - - STR_1779 :{INLINE_SPRITE}{254}{19}{00}{00} Panda jelmez @@ -1090,6 +1154,7 @@ STR_1786 :{INLINE_SPRITE}{05}{20}{00}{00} Űrhajós jelmez STR_1787 :{INLINE_SPRITE}{06}{20}{00}{00} Bandita jelmez STR_1788 :{INLINE_SPRITE}{07}{20}{00}{00} Seriff jelmez STR_1789 :{INLINE_SPRITE}{08}{20}{00}{00} Kalóz jelmez +STR_1790 :{SMALLFONT}{BLACK}A választott típusú alkalmazottak által viselt egyenruha színének kiválasztása STR_1791 :{WINDOW_COLOUR_2}Egyenruha színe: STR_1792 :{STRINGID} segélykérésére válaszol STR_1793 :{STRINGID} felé tart ellenőrzésre @@ -1114,6 +1179,7 @@ STR_1812 :{SMALLFONT}{BLACK}{STRINGID} STR_1813 :Egyéb objektumok STR_1814 :Tevékenységek STR_1815 :Gondolatok +STR_1816 :{SMALLFONT}{BLACK}A vendéglistában megjelenítendő információtípus kiválasztása STR_1817 :({COMMA16}) STR_1818 :{WINDOW_COLOUR_2}Minden vendég STR_1819 :{WINDOW_COLOUR_2}Minden vendég (összegezve) @@ -1172,6 +1238,8 @@ STR_1871 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COM STR_1873 :{WINDOW_COLOUR_2}Bevétel: {BLACK}{CURRENCY2DP} óránként STR_1874 :{WINDOW_COLOUR_2}Nyereség: {BLACK}{CURRENCY2DP} óránként STR_1875 :{BLACK} {SPRITE}{BLACK} {STRINGID} +STR_1876 :{WINDOW_COLOUR_2}{INLINE_SPRITE}{251}{19}{00}{00}Játékok ellenőrzése +STR_1877 :{WINDOW_COLOUR_2}{INLINE_SPRITE}{252}{19}{00}{00}Játékok javítása STR_1878 :{WINDOW_COLOUR_2}Ellenőrzés: STR_1879 :Minden 10. percben STR_1880 :Minden 20. percben @@ -1202,20 +1270,21 @@ STR_1906 :{WINDOW_COLOUR_2}Étel/ital készletek STR_1907 :{WINDOW_COLOUR_2}Alkalmazottak bére STR_1908 :{WINDOW_COLOUR_2}Marketing STR_1909 :{WINDOW_COLOUR_2}Kutatás -STR_1910 :{WINDOW_COLOUR_2}Hitelkamat +STR_1910 :{WINDOW_COLOUR_2}Kölcsön kamat STR_1911 :{BLACK} {COMMA16}%-os éves kamattal STR_1912 :{MONTH} STR_1913 :{BLACK}+{CURRENCY2DP} STR_1914 :{BLACK}{CURRENCY2DP} STR_1915 :{RED}{CURRENCY2DP} -STR_1916 :{WINDOW_COLOUR_2}Hitel: +STR_1916 :{WINDOW_COLOUR_2}Kölcsön: STR_1917 :{POP16}{POP16}{POP16}{CURRENCY} STR_1918 :Nem vehetsz fel több pénzt! STR_1919 :Nincs elég pénz! -STR_1920 :Nem tudod visszafizetni a hitelt! +STR_1920 :Nem tudod visszafizetni a kölcsönt! STR_1921 :{SMALLFONT}{BLACK}Új játék indítása STR_1922 :{SMALLFONT}{BLACK}Mentett játék folytatása STR_1924 :{SMALLFONT}{BLACK}Kilépés +STR_1925 :Nem helyezhető ide ember... STR_1926 :{SMALLFONT} STR_1927 :{YELLOW}{STRINGID} meghibásodott STR_1928 :{RED}{STRINGID} ütközött! @@ -1235,6 +1304,7 @@ STR_1941 :{SMALLFONT}{BLACK}A vendég által használt játékok STR_1942 :{SMALLFONT}{BLACK}A vendég pénzügyi információi STR_1943 :{SMALLFONT}{BLACK}A vendég legutóbbi gondolatai STR_1944 :{SMALLFONT}{BLACK}A vendégnél lévő dolgok +STR_1945 :{SMALLFONT}{BLACK}Az alkalmazott utasításai és beállításai STR_1946 :{SMALLFONT}{BLACK}A szórakoztató jelmezének kiválasztása STR_1947 :{SMALLFONT}{BLACK}A választott típusú alkalmazottak járőrözési területe és a legközelebbi alkalmazott megkeresése STR_1948 :{SMALLFONT}{BLACK}Új alkalmazott felvétele a kiválasztott munkakörbe @@ -1244,6 +1314,8 @@ STR_1951 :Parkérték grafikon STR_1952 :Nyereség grafikon STR_1953 :Marketing STR_1954 :Kutatás finanszírozása +STR_1955 :{WINDOW_COLOUR_2}Körök száma: +STR_1956 :{SMALLFONT}{BLACK}A pálya köreinek száma játékonként STR_1957 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} STR_1958 :{COMMA16} STR_1959 :Nem változtatható meg a körök száma... @@ -1433,7 +1505,7 @@ STR_2151 :Napszemüveg STR_2152 :Marhahúsos tészta STR_2153 :Pirított rizstészta STR_2154 :Wonton leves -STR_2155 :Húsgombóceves +STR_2155 :Húsgombócleves STR_2156 :Gyümölcslé STR_2157 :Szójatej STR_2158 :Szudzsonggva @@ -1452,7 +1524,7 @@ STR_2173 :egy napszemüveg STR_2174 :marhahúsos tészta STR_2175 :pirított rizstészta STR_2176 :wonton leves -STR_2177 :húsgombóceves +STR_2177 :húsgombócleves STR_2178 :gyümölcslé STR_2179 :szójatej STR_2180 :szudzsonggva @@ -1471,7 +1543,7 @@ STR_2195 :Napszemüveg STR_2196 :Marhahúsos tészta STR_2197 :Pirított rizstészta STR_2198 :Wonton leves -STR_2199 :Húsgombóceves +STR_2199 :Húsgombócleves STR_2200 :Gyümölcslé STR_2201 :Szójatej STR_2202 :Szudzsonggva @@ -1484,11 +1556,13 @@ STR_2208 :Sült kolbász STR_2209 :Üres tál STR_2210 :{SMALLFONT}{BLACK}A park mindeneseinek listája STR_2211 :{SMALLFONT}{BLACK}A park gépészeinek listája -STR_2212 :{SMALLFONT}{BLACK}A park biztonsági őreinek lilistája +STR_2212 :{SMALLFONT}{BLACK}A park biztonsági őreinek listája STR_2213 :{SMALLFONT}{BLACK}A park szórakoztatóinak listája +STR_2214 :Szünet közben nem lehet építeni! STR_2215 :{STRINGID}{NEWLINE}({STRINGID}) STR_2216 :{WINDOW_COLOUR_2}{COMMA16}°C STR_2217 :{WINDOW_COLOUR_2}{COMMA16}°F +STR_2218 :{RED}{STRINGID} itt van: {STRINGID} és még nem érkezett vissza ide: {STRINGID}!{NEWLINE}Ellenőrizd, hogy nincs-e beragadva vagy elakadva STR_2219 :{RED}{STRINGID} balesete közben {COMMA16} ember meghalt STR_2220 :{WINDOW_COLOUR_2}Park értékelése: {BLACK}{COMMA16} STR_2221 :{SMALLFONT}{BLACK}Park értékelése: {COMMA16} @@ -1501,6 +1575,7 @@ STR_2227 :{WINDOW_COLOUR_2}Vállalati érték: {BLACK}{CURRENCY} STR_2228 :{WINDOW_COLOUR_2}Az előző havi nyereség étel/ital és{NEWLINE}szuvenír eladásokból: {BLACK}{CURRENCY} STR_2230 :Függőleges pálya STR_2229 :Emelkedő függőlegeshez +STR_2231 :Rögzítőfék eséshez STR_2232 :Kábeles felvonószakasz STR_2233 :{SMALLFONT}{BLACK}Park információk STR_2234 :Legutóbbi üzenetek @@ -1521,7 +1596,7 @@ STR_2248 :A játék/épület nem rombolható le... STR_2249 :{BABYBLUE}Új játék/épület érhető el:{NEWLINE}{STRINGID} STR_2250 :{BABYBLUE}Új díszlet/téma érhető el:{NEWLINE}{STRINGID} STR_2251 :Csak útra építhető! -STR_2252 :Csak úton kereszetbe építhető! +STR_2252 :Csak úton keresztbe építhető! STR_2253 :Szállító játékok STR_2254 :Enyhe játékok STR_2255 :Hullámvasutak @@ -1549,7 +1624,7 @@ STR_2276 :{SMALLFONT}{BLACK}A kutatás-fejlesztés állapota STR_2277 :Ismeretlen STR_2278 :Szállító játék STR_2279 :Enyhe játék -STR_2280 :Hullámvasűt +STR_2280 :Hullámvasút STR_2281 :Izgalmas játék STR_2282 :Vízi játék STR_2283 :Bolt/bódé @@ -1615,7 +1690,7 @@ STR_2348 :{SMALLFONT}{BLACK}Az alkalmazott statisztikái STR_2349 :{WINDOW_COLOUR_2}Munkabér: {BLACK}{CURRENCY} havonta STR_2350 :{WINDOW_COLOUR_2}Felvéve: {BLACK}{MONTHYEAR} STR_2351 :{WINDOW_COLOUR_2}Lenyírt füvek: {BLACK}{COMMA16} -STR_2352 :{WINDOW_COLOUR_2}Megöntözöttt kertek: {BLACK}{COMMA16} +STR_2352 :{WINDOW_COLOUR_2}Megöntözött kertek: {BLACK}{COMMA16} STR_2353 :{WINDOW_COLOUR_2}Felsepert szemetek: {BLACK}{COMMA16} STR_2354 :{WINDOW_COLOUR_2}Kiürített kukák: {BLACK}{COMMA16} STR_2355 :{WINDOW_COLOUR_2}Megjavított játékok: {BLACK}{COMMA16} @@ -1648,8 +1723,8 @@ STR_2382 :Föld STR_2383 :Víz STR_2384 :{WINDOW_COLOUR_2}A célod: STR_2385 :{BLACK}Nincs -STR_2386 :{BLACK}Legyen legalább {COMMA16} vendég a parkodban {MONTHYEAR} végére, a parkod értékelése pedig legyen legalább 600 -STR_2387 :{BLACK}Legyen legalább {POP16}{POP16}{CURRENCY} a parkod értéke {PUSH16}{PUSH16}{PUSH16}{MONTHYEAR} végére +STR_2386 :{BLACK}Legyen legalább {COMMA16} vendég a parkodban {MONTHYEAR}végére, a parkod értékelése pedig legyen legalább 600 +STR_2387 :{BLACK}Legyen legalább {POP16}{POP16}{CURRENCY} a parkod értéke {PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}végére STR_2388 :{BLACK}Érezd jól magad! STR_2389 :{BLACK}A lehető legjobb {STRINGID} építése! STR_2390 :{BLACK}Építs 10 különböző, egyenként legalább 6,00 izgalmi értékű hullámvasutat a parkodban @@ -1662,6 +1737,15 @@ STR_2396 :{BLACK}Az ételek, italok és szuvenírek utáni havi bevételed le STR_2397 :Nincs STR_2398 :A vendégek száma STR_2399 :A park értéke egy meghatározott időpontban +STR_2400 :Érezd jól magad +STR_2401 :Építsd meg a lehető legjobb játékot +STR_2402 :Építs 10 hullámvasutat +STR_2403 :Vendégek száma a parkban +STR_2404 :Havi bevétel a játékjegyekből +STR_2405 :Építs 10 meghatározott hosszúságú hullámvasutat +STR_2406 :Fejezd be 5 hullámvasút építését +STR_2407 :Fizesd vissza a kölcsönt és érj el egy meghatározott park értéket +STR_2408 :Havi nyereség ételből/szuvenírből STR_2409 :{WINDOW_COLOUR_2}Folyamatban lévő marketingkampányok STR_2410 :{BLACK}Nincs STR_2411 :{WINDOW_COLOUR_2}Rendelkezésre álló marketingkampányok @@ -1698,8 +1782,8 @@ STR_2449 :{YELLOW}Az ingyen {STRINGID} marketingkampánya befejeződött STR_2450 :{YELLOW}A park reklámkampánya befejeződött STR_2451 :{YELLOW}{STRINGID} reklámkampánya befejeződött STR_2454 :{SMALLFONT}{BLACK}{CURRENCY2DP} - -STR_2452 :{WINDOW_COLOUR_2}Pénz (hitel nélkül): {BLACK}{CURRENCY2DP} -STR_2453 :{WINDOW_COLOUR_2}Pénz (hitel nélkül): {RED}{CURRENCY2DP} +STR_2452 :{WINDOW_COLOUR_2}Pénz (kölcsön nélkül): {BLACK}{CURRENCY2DP} +STR_2453 :{WINDOW_COLOUR_2}Pénz (kölcsön nélkül): {RED}{CURRENCY2DP} STR_2457 :{SMALLFONT}{BLACK}A pénzügyi számlák adatai STR_2458 :{SMALLFONT}{BLACK}Grafikon a pénz (kölcsön nélküli) alakulásáról STR_2459 :{SMALLFONT}{BLACK}Grafikon a park értékének alakulásáról @@ -1712,7 +1796,7 @@ STR_2465 :{SMALLFONT}{BLACK}A park belépési díja és bevétele STR_2466 :{SMALLFONT}{BLACK}A park statisztikái STR_2467 :{SMALLFONT}{BLACK}A jelenlegi játék céljai STR_2468 :{SMALLFONT}{BLACK}A park által mostanában elnyert díjak -STR_2469 :{SMALLFONT}{BLACK}A kutatás-fejlesztés finaszírozási szintjének kiválasztása +STR_2469 :{SMALLFONT}{BLACK}A kutatás-fejlesztés finanszírozási szintjének kiválasztása STR_2470 :{SMALLFONT}{BLACK}Új szállító játékok kutatása STR_2471 :{SMALLFONT}{BLACK}Új enyhe játékok kutatása STR_2472 :{SMALLFONT}{BLACK}Új hullámvasutak kutatása @@ -1721,10 +1805,15 @@ STR_2474 :{SMALLFONT}{BLACK}Új vízi játékok kutatása STR_2475 :{SMALLFONT}{BLACK}Új boltok és bódék kutatása STR_2476 :{SMALLFONT}{BLACK}Új díszletek és témák kutatása STR_2477 :{SMALLFONT}{BLACK}A játék/épület üzemmódjának kiválasztása +STR_2478 :{SMALLFONT}{BLACK}Grafikon a sebesség időbeli alakulásáról +STR_2479 :{SMALLFONT}{BLACK}Grafikon a magasság időbeli alakulásáról +STR_2480 :{SMALLFONT}{BLACK}Grafikon a függőleges gyorsulás időbeli alakulásáról +STR_2481 :{SMALLFONT}{BLACK}Grafikon az oldalirányú gyorsulás időbeli alakulásáról STR_2482 :{SMALLFONT}{BLACK}Nyereség: {CURRENCY} hetente, Park értéke: {CURRENCY} STR_2483 :{WINDOW_COLOUR_2}Heti nyereség: {BLACK}+{CURRENCY2DP} STR_2484 :{WINDOW_COLOUR_2}Heti nyereség: {RED}{CURRENCY2DP} STR_2487 :Vendégek „igazi” nevének mutatása +STR_2488 :{SMALLFONT}{BLACK}Váltás a vendégek „igazi” nevének és a vendégek számának mutatása között STR_2489 :Gyorsbillentyűk... STR_2490 :Gyorsbillentyűk STR_2491 :Alapértékek visszaállítása @@ -1742,7 +1831,7 @@ STR_2502 :Talaj elrejtése be/ki STR_2503 :Függőleges felületek elrejtése be/ki STR_2504 :Átlátszó játékok be/ki STR_2505 :Átlátszó díszletek be/ki -STR_2506 :Láthatatlan támpillérek be/ki +STR_2506 :Láthatatlan állványzatok be/ki STR_2507 :Láthatatlan emberek be/ki STR_2508 :Magasságjelek a talajon be/ki STR_2509 :Magasságjelek a játékok pályáin be/ki @@ -1759,7 +1848,7 @@ STR_2519 :Park információk STR_2520 :Vendéglista STR_2521 :Alkalmazottak listája STR_2522 :Legutóbbi üzenetek megjelenítése -STR_2523 :Térkép megjelenítése +STR_2523 :Térkép STR_2524 :Képmentés ### The following need to be reordered to match SDL_keycode layout. STR_2525 :??? @@ -1919,7 +2008,7 @@ STR_2678 :??? STR_2679 :??? STR_2680 :Minden kutatás kész STR_2681 :{MEDIUMFONT}{BLACK}Ennyivel növeli a pénzed: {CURRENCY} -STR_2684 :{SMALLFONT}{BLACK}Egy nagy csopot vendég érkezik +STR_2684 :{SMALLFONT}{BLACK}Egy nagy csoport vendég érkezik STR_2685 :Szimplex zajparaméterek STR_2686 :Alacsony: STR_2687 :Magas: @@ -1943,6 +2032,7 @@ STR_2704 :Minden 30. percben STR_2705 :Minden órában STR_2706 :Soha STR_2707 :Rendszer fájlkezelőjének használata +STR_2708 :{WINDOW_COLOUR_1}Biztos, hogy felül akarod írni ezt: {STRINGID}? STR_2709 :Felülírás STR_2710 :Írd be a fájl nevét. STR_2711 :; @@ -2031,7 +2121,7 @@ STR_2801 :{WINDOW_COLOUR_2}Bevétel a belépőkből: {BLACK}{CURRENCY2DP} STR_2802 :Térkép STR_2803 :{SMALLFONT}{BLACK}A kiválasztott vendégek kiemelése a térképen STR_2804 :{SMALLFONT}{BLACK}A kiválasztott alkalmazottak kiemelése a térképen -STR_2805 :{SMALLFONT}{BLACK}A park térképének megjelenítése +STR_2805 :{SMALLFONT}{BLACK}A park térképe STR_2806 :{RED}A vendégek panaszkodnak, mert az utak a parkodban gyomorforgatóak{NEWLINE}Nézd meg hol vannak a mindeneseid és próbáld meg jobban szervezni őket STR_2807 :{RED}A vendégek panaszkodnak a parkodban lévő szemét mennyisége miatt{NEWLINE}Nézd meg hol vannak a mindeneseid és próbáld meg jobban szervezni őket STR_2808 :{RED}A vendégek panaszkodnak a parkodban lévő vandalizmus miatt{NEWLINE}Nézd meg hol vannak a biztonsági őreid és próbáld meg jobban szervezni őket @@ -2073,7 +2163,7 @@ STR_2843 :{TOPAZ}A parkod megkapta „A legjobb vízi játékokkal rendelkez STR_2844 :{TOPAZ}A parkod megkapta „A legjobb egyedi tervezésű játékokkal rendelkező park” díjat! STR_2845 :{TOPAZ}A parkod megkapta „A legkáprázatosabb színsémájú játékokal rendelkező park” díjat! STR_2846 :{TOPAZ}A parkod megkapta „A legzavarbaejtőbb alaprajzú park” díjat! -STR_2847 :{TOPAZ}A parkod megkapta „A legjobb enyhe játékokal rendelkező rendelkező park” díjat! +STR_2847 :{TOPAZ}A parkod megkapta „A legjobb enyhe játékokkal rendelkező rendelkező park” díjat! STR_2848 :{WINDOW_COLOUR_2}Nincsenek friss díjak STR_2849 :Az új pálya sikeresen telepítve STR_2850 :Az új játékterv sikeresen telepítve @@ -2086,7 +2176,49 @@ STR_2858 :Nem indítható marketingkampány... STR_2861 :{WINDOW_COLOUR_2}Az Infogrames Interactive Inc. számára licencelve STR_2862 :Zenei köszönetnyilvánítások... STR_2863 :Zenei köszönetnyilvánítások +STR_2864 :{WINDOW_COLOUR_2}Induló - Die Regimentskinder: (Fucik) copyright nélküli +STR_2865 :{WINDOW_COLOUR_2}Heyken's Serenade: (J.Heyken) British Standard Music Coy; GEMA, BRITICO +STR_2866 :{WINDOW_COLOUR_2}La Belle Espagnole: (Robert Vollstedt) copyright nélküli +STR_2867 :{WINDOW_COLOUR_2}Wedding Journey: (Tradicionális) +STR_2868 :{WINDOW_COLOUR_2}Mesél a bécsi erdő: (JohannSTRauss) copyright nélküli +STR_2869 :{WINDOW_COLOUR_2}Szláv tánc: (Tradicionális) +STR_2870 :{WINDOW_COLOUR_2}Das Alpenhorn: (Tradicionális) +STR_2871 :{WINDOW_COLOUR_2}The Blond Sailor: (Tradicionális) +STR_2872 :{WINDOW_COLOUR_2}Nyitány - Költő és paraszt: (Franz von Suppé) copyright nélküli +STR_2873 :{WINDOW_COLOUR_2}Keringő egyveleg: (JohannSTRauss) copyright nélküli +STR_2874 :{WINDOW_COLOUR_2}Bella Bella Bimba: (Tradicionális) +STR_2875 :{WINDOW_COLOUR_2}Eredeti felvételek (P) 1976 C.J.Mears Organization, engedéllyel használva +STR_2876 :{WINDOW_COLOUR_2}RollerCoaster Tycoon 2 főcímzene: (Allister Brimble) copyright © Chris Sawyer +STR_2877 :{WINDOW_COLOUR_2}Dodgems Beat: (Allister Brimble) copyright © Chris Sawyer +STR_2878 :{WINDOW_COLOUR_2}Mid Summer's Heat: (Allister Brimble) copyright © Chris Sawyer +STR_2879 :{WINDOW_COLOUR_2}Pharaoh's Tomb: (Allister Brimble) copyright © Chris Sawyer +STR_2880 :{WINDOW_COLOUR_2}Caesar's March: (Allister Brimble) copyright © Chris Sawyer +STR_2881 :{WINDOW_COLOUR_2}Drifting To Heaven: (Allister Brimble) copyright © Chris Sawyer +STR_2882 :{WINDOW_COLOUR_2}Invaders: (Allister Brimble) copyright © Chris Sawyer +STR_2883 :{WINDOW_COLOUR_2}Eternal Toybox: (Allister Brimble) copyright © Chris Sawyer +STR_2884 :{WINDOW_COLOUR_2}Jungle Juice: (Allister Brimble) copyright © Chris Sawyer +STR_2885 :{WINDOW_COLOUR_2}Ninja's Noodles: (Allister Brimble) copyright © Chris Sawyer +STR_2886 :{WINDOW_COLOUR_2}Voyage to Andromeda: (Allister Brimble) copyright © Chris Sawyer +STR_2887 :{WINDOW_COLOUR_2}Brimble's Beat: (Allister Brimble) copyright © Chris Sawyer +STR_2888 :{WINDOW_COLOUR_2}Atlantis: (Allister Brimble) copyright © Chris Sawyer +STR_2889 :{WINDOW_COLOUR_2}Wild West Kid: (Allister Brimble) copyright © Chris Sawyer +STR_2890 :{WINDOW_COLOUR_2}Vampire's Lair: (Allister Brimble) copyright © Chris Sawyer +STR_2891 :{WINDOW_COLOUR_2}Blockbuster: (Allister Brimble) copyright © Chris Sawyer +STR_2892 :{WINDOW_COLOUR_2}Airtime Rock: (Allister Brimble) copyright © Chris Sawyer +STR_2893 :{WINDOW_COLOUR_2}Searchlight Rag: (Scott Joplin/Allister Brimble) copyright © Chris Sawyer +STR_2894 :{WINDOW_COLOUR_2}Flight of Fantasy: (Steve Blenkinsopp) copyright © Chris Sawyer +STR_2895 :{WINDOW_COLOUR_2}Big Rock: (Allister Brimble) copyright © Chris Sawyer +STR_2896 :{WINDOW_COLOUR_2}Hypothermia: (Allister Brimble) copyright © Chris Sawyer +STR_2897 :{WINDOW_COLOUR_2}Last Sleigh Ride: (Allister Brimble) copyright © Chris Sawyer +STR_2898 :{WINDOW_COLOUR_2}Pipes of Glencairn: (Allister Brimble) copyright © Chris Sawyer +STR_2899 :{WINDOW_COLOUR_2}Traffic Jam: (Allister Brimble) copyright © Chris Sawyer STR_2901 :{WINDOW_COLOUR_2}(A minták a Spectrasonics „Liquid Grooves”-ának köszönhetőek) +STR_2902 :{WINDOW_COLOUR_2}Toccata: (C.M.Widor, Előadja: Peter James Adcock) felvétel © Chris Sawyer +STR_2903 :{WINDOW_COLOUR_2}Space Rock: (Allister Brimble) copyright © Chris Sawyer +STR_2904 :{WINDOW_COLOUR_2}Manic Mechanic: (Allister Brimble) copyright © Chris Sawyer +STR_2905 :{WINDOW_COLOUR_2}Techno Torture: (Allister Brimble) copyright © Chris Sawyer +STR_2906 :{WINDOW_COLOUR_2}Sweat Dreams: (Allister Brimble) copyright © Chris Sawyer +STR_2907 :{WINDOW_COLOUR_2}Baj van a részeg tengerésszel: (Névtelen/Allister Brimble) copyright © Chris Sawyer STR_2971 :Fő színséma STR_2972 :Alternatív színséma 1 STR_2973 :Alternatív színséma 2 @@ -2097,7 +2229,7 @@ STR_2977 :Alkalmazott neve STR_2978 :Írj be egy új nevet az alkalmazottad számára: STR_2979 :Nem nevezhető át az alkalmazott... STR_2980 :Túl sok hirdetőtábla van a játékban -STR_2981 :{RED}Tilos a belépés - - +STR_2981 :{RED}Tilos a belépés STR_2982 :Hirdetőtábla szövege STR_2983 :Írd be a hirdetőtábla új szövegét: STR_2984 :Nem állítható be új szöveg a hirdetőtáblán... @@ -2142,7 +2274,7 @@ STR_3022 :Rémisztő stílus STR_3023 :Techno stílus STR_3024 :Könnyed stílus STR_3025 :Nyári stílus -STR_3026 :Vízes stílus +STR_3026 :Vizes stílus STR_3027 :Vadnyugati stílus STR_3028 :Jura kori stílus STR_3029 :Rock stílus @@ -2181,9 +2313,15 @@ STR_3065 :Haladó parkok STR_3066 :Nehéz parkok STR_3067 :„Igazi” parkok STR_3068 :Egyéb parkok +STR_3069 :Felső szakasz STR_3070 :Lejtés vízszinthez STR_3071 :{WINDOW_COLOUR_2}Ugyanez az ár mindenhol a parkban STR_3072 :{SMALLFONT}{BLACK}Válaszd ki, hogy ezt az árat használják-e mindenhol a parkban +STR_3073 :{RED}FIGYELMEZTETÉS: A parkod értékelése 700 alá esett !{NEWLINE}Ha 4 héten belül nem növeled az értékelést, a parkod be lesz zárva +STR_3074 :{RED}FIGYELMEZTETÉS: A parkod értékelése még mindig 700 alatt van !{NEWLINE}3 heted van növelni az értékelést +STR_3075 :{RED}FIGYELMEZTETÉS: A parkod értékelése még mindig 700 alatt van !NEWLINE}Csak 2 heted van növelni az értékelést, vagy a parkod be lesz zárva +STR_3076 :{RED}VÉGSŐ FIGYELMEZTETÉS: A parkod értékelése még mindig 700 alatt van !{NEWLINE}Már csak 7 nap van, mielőtt a parkod be lesz zárva, hacsak nem növeled az értékelést +STR_3077 :{RED}BEZÁRÁSI ÉRTESÍTŐ: A parkod be lett zárva ! STR_3090 :{SMALLFONT}{BLACK}A bejárat, a kijárat és az állomás stílusának kiválasztása STR_3091 :Nem távolíthatod el ezt a szakaszt! STR_3092 :Nem mozgathatod vagy módosíthatod a játék állomását! @@ -2206,6 +2344,7 @@ STR_3109 :Megnyitás STR_3110 :{WINDOW_COLOUR_2}Térközszakaszok: {BLACK}{COMMA16} STR_3111 :{SMALLFONT}{BLACK}Az építéshez kattints a tervre STR_3112 :{SMALLFONT}{BLACK}Az átnevezéshez vagy a törléshez kattints a tervre +STR_3113 :Másik terv választása STR_3114 :{SMALLFONT}{BLACK}Vissza a tervek választásához STR_3115 :{SMALLFONT}{BLACK}A játékterv mentése STR_3116 :{SMALLFONT}{BLACK}A játékterv mentése (Nem lehet, amíg a játék nem lett letesztelve és nincsenek statisztikák generálva) @@ -2224,11 +2363,38 @@ STR_3128 :Játékterv mentése STR_3129 :Játékterv mentése díszletekkel STR_3130 :Mentés STR_3131 :Mégse +STR_3132 :{BLACK}Klikkelj azokra a díszletelemekre, amelyeket a játéktervvel együtt el akarsz menteni… STR_3133 :Nem építhető lejtőre +STR_3134 :{RED}(A terv el nem érhető díszletet tartalmaz) +STR_3135 :{RED}(A járműterv nem elérhető - Befolyásolhatja a játék teljesítményét) +STR_3136 :Figyelmeztetés: A terv alternatív járműtípussal lesz megépítve és lehet, hogy nem az elvártnak megfelelően fog teljesíteni +STR_3137 :Közeli díszletek kiválasztása +STR_3138 :Kiválasztás törlése +STR_3139 :A kábeles felvonó nem tud üzemelni ebben a működési módban +STR_3140 :A kábeles felvonószakasznak közvetlenül az állomás után kell kezdődnie +STR_3141 :Nem lehetséges többkörös utazás kábeles felvonószakasszal STR_3142 :{WINDOW_COLOUR_2}Befogadóképesség: {BLACK}{STRINGID} STR_3143 :{SMALLFONT}{BLACK}Emberek megjelenítése a térképen STR_3144 :{SMALLFONT}{BLACK}Játékok és bódék megjelenítése a térképen +STR_3162 :Nem lehet elég memóriát lefoglalni +STR_3163 :Új adat telepítése: STR_3164 :{BLACK}{COMMA16} kiválasztva (maximum {COMMA16}) +STR_3167 :{WINDOW_COLOUR_2}Tartalmaz: {BLACK}{COMMA16} objektumot +STR_3169 :A következő objektumok adatai nem találhatóak: +STR_3170 :Nincs elég hely a grafika számára +STR_3171 :Túl sok ilyen típusú objektum van kiválasztva +STR_3172 :Előbb ki kell választani ezeket az objektumokat: {STRING} +STR_3173 :Az objektum jelenleg használatban van +STR_3174 :Az objektum egy másik objektumhoz szükséges +STR_3175 :Erre az objektumra mindig szükség van +STR_3176 :Az objektum nem választható ki +STR_3177 :Az objektum kiválasztása nem vonható vissza +STR_3178 :Legalább egy útobjektumot ki kell választani +STR_3179 :Legalább egy játék járművet/játékot ki kell választani +STR_3180 :Érvénytelen objektumkijelölés +STR_3181 :Objektum kiválasztás - {STRINGID} +STR_3182 :A parkbejárat típusát ki kell választani +STR_3183 :A víz típusát ki kell választani STR_3184 :Játékok és járműveik STR_3185 :Kis díszletek STR_3186 :Nagy díszletek @@ -2239,7 +2405,6 @@ STR_3190 :Úti extrák STR_3191 :Díszletcsoportok STR_3192 :Park bejárata STR_3193 :Víz -STR_3194 :Pálya leírása STR_3195 :Találmányok listája STR_3196 :{WINDOW_COLOUR_2}Kutatási csoport: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}A játék kezdetekor már elérhető találmányok: @@ -2261,6 +2426,14 @@ STR_3212 :{POP16}{COMMA16} x {PUSH16}{COMMA16} STR_3213 :Nem csökkenthető tovább a térkép mérete STR_3214 :Nem növelhető tovább a térkép mérete STR_3215 :Túl közel van a térkép széléhez +STR_3216 :{SMALLFONT}{BLACK}A park földtulajdonának kiválasztása stb. +STR_3217 :Birtokolt föld +STR_3218 :Birtokolt építési jogok +STR_3219 :Eladó föld +STR_3220 :Eladó építési jogok +STR_3226 :{SMALLFONT}{BLACK}Parkbejárat építése +STR_3227 :Túl sok a parkbejárat! +STR_3228 :{SMALLFONT}{BLACK}Kezdő pozíciók megadása az emberek számára STR_3232 :Beállítások - pénzügyi STR_3233 :Beállítások - vendégek STR_3234 :Beállítások - park @@ -2269,10 +2442,31 @@ STR_3236 :{SMALLFONT}{BLACK}A vendégek beállításai STR_3237 :{SMALLFONT}{BLACK}A park beállításai STR_3238 :Nem kell pénz STR_3239 :{SMALLFONT}{BLACK}Legyen ez a park egy pénzügyi korlátok nélküli, ’nem kell pénz’ park -STR_3240 :{WINDOW_COLOUR_2}Kezdőtöke: -STR_3241 :{WINDOW_COLOUR_2}Kezdeti hitel: -STR_3242 :{WINDOW_COLOUR_2}Maximális hitelösszeg: +STR_3240 :{WINDOW_COLOUR_2}Kezdőtőke: +STR_3241 :{WINDOW_COLOUR_2}Kezdeti kölcsön: +STR_3242 :{WINDOW_COLOUR_2}Maximális kölcsönösszeg: STR_3243 :{WINDOW_COLOUR_2}Éves kamatláb: +STR_3244 :Marketingkampányok tiltása +STR_3245 :{SMALLFONT}{BLACK}A hirdetések, promóciós programok és egyéb marketingkampányok tiltása +STR_3246 :{WINDOW_COLOUR_2}{CURRENCY} +STR_3247 :{WINDOW_COLOUR_2}{COMMA16}% +STR_3248 :A kezdőtőke nem növelhető tovább! +STR_3249 :A kezdőtőke nem csökkenthető tovább! +STR_3250 :A kezdeti kölcsön nem növelhető tovább! +STR_3251 :A kezdeti kölcsön nem csökkenthető tovább! +STR_3252 :A maximum kölcsön összege nem növelhető tovább! +STR_3253 :A maximum kölcsön összege nem csökkenthető tovább! +STR_3254 :A kamatláb nem növelhető tovább! +STR_3255 :A kamatláb nem csökkenthető tovább! +STR_3260 :{WINDOW_COLOUR_2}Készpénz/vendég (átlagosan): +STR_3261 :{WINDOW_COLOUR_2}Vendégek kezdeti boldogsága: +STR_3262 :{WINDOW_COLOUR_2}Vendégek kezdeti éhsége: +STR_3263 :{WINDOW_COLOUR_2}Vendégek kezdeti szomjúsága: +STR_3264 :Nem növelhető jobban! +STR_3265 :Nem csökkenthető jobban! +STR_3266 :{SMALLFONT}{BLACK}Válaszd ki a park belépőjének és játékainak fizetési módját +STR_3267 :Fák eltávolításának tiltása +STR_3268 :{SMALLFONT}{BLACK}A magas fák eltávolításának tiltása STR_3279 :Ingyenes parkbelépő / Fizetős játékok STR_3280 :Fizetős parkbelépő / Ingyenes játékok STR_3281 :{WINDOW_COLOUR_2}Belépési díj: @@ -2306,7 +2500,7 @@ STR_3308 :{WINDOW_COLOUR_2}Izgalmi érték: STR_3309 :{WINDOW_COLOUR_2}{COMMA16} STR_3310 :{WINDOW_COLOUR_2}{LENGTH} STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32} -STR_3312 :{WINDOW_COLOUR_2}Műemlékvédelemi játékok/épületek: +STR_3312 :{WINDOW_COLOUR_2}Műemlékvédelmi játékok/épületek: STR_3313 :Pálya neve STR_3314 :Írd be a pálya nevét: STR_3315 :Park/pálya részletei @@ -2314,8 +2508,13 @@ STR_3316 :Írd be a pálya leírását: STR_3317 :Még nincsenek részletek STR_3318 :{SMALLFONT}{BLACK}Válaszd ki, hogy melyik csoportban jelenjen meg ez a pálya STR_3319 :{WINDOW_COLOUR_2}Pályacsoport: +STR_3321 :Új objektumok sikeresen telepítve STR_3322 :{WINDOW_COLOUR_2}Cél: {BLACK}{STRINGID} STR_3326 :{WINDOW_COLOUR_2}(nincs kép) +STR_3327 :Nincsenek beállítva kezdő pozíciók az emberek számára +STR_3328 :Nem lehet továbblépni a következő szerkesztési szakaszba... +STR_3329 :Még nem épült parkbejárat +STR_3332 :A parkbejárat rossz irányba néz, vagy nem vezet tőle út a térkép széléig STR_3333 :Plug-in objektumok exportálása a mentett játékokkal STR_3334 :{SMALLFONT}{BLACK} Válaszd ki, hogy mentésre kerüljenek-e szükséges hozzáadott plug-in objektumok adatai (a játékhoz nem mellékelt kiegészítő adatok) a mentett játékok vagy pályák fájlaiban, hogy azok is betölthessék őket, akik nem rendelkeznek a hozzáadott objektumok adataival STR_3341 :{SMALLFONT}{BLACK}Játékeszközök @@ -2345,7 +2544,7 @@ STR_3366 :{BLACK}= Játék STR_3367 :{BLACK}= Ételes bódé STR_3368 :{BLACK}= Italos bódé STR_3369 :{BLACK}= Szuveníres bódé -STR_3370 :{BLACK}= Információs bodé +STR_3370 :{BLACK}= Információs bódé STR_3371 :{BLACK}= Elsősegély STR_3372 :{BLACK}= Bankautomata STR_3373 :{BLACK}= WC @@ -2371,7 +2570,7 @@ STR_3442 :3. oldal STR_3443 :4. oldal STR_3444 :5. oldal STR_3445 :Járőrözési terület kijelölése -STR_3446 :Járörözési terület törlése +STR_3446 :Járőrözési terület törlése # New strings, cleaner STR_5120 :Pénzügyek @@ -2380,24 +2579,29 @@ STR_5122 :A játékok pályatípus szerinti kiválasztása (mint az RCT1-ben) STR_5123 :Játékok felújítása STR_5125 :Minden lerombolható STR_5126 :Véletlenszerű főcímzene +STR_5128 :Kiválasztás mérete +STR_5129 :Add meg a kiválasztás méretét {COMMA16} és {COMMA16} között STR_5130 :Térkép mérete +STR_5131 :Add meg a térkép méretét {COMMA16} és {COMMA16} között +STR_5132 :Játékok megjavítása STR_5133 :{SMALLFONT}{BLACK}Vásárlás kisebb területen STR_5134 :{SMALLFONT}{BLACK}Vásárlás nagyobb területen STR_5135 :{SMALLFONT}{BLACK}Földterületek és építési jogok vásárlása STR_5136 :Földjogok +STR_5137 :Működési limitek feloldása STR_5138 :{SMALLFONT}{WINDOW_COLOUR_2}{STRINGID} STR_5139 :{WHITE}{STRINGID} STR_5140 :Fékhibák kikapcsolása STR_5141 :Meghibásodások kikapcsolása -STR_5142 :Normál -STR_5143 :Gyors -STR_5144 :Extra gyors -STR_5145 :Turbó +STR_5142 :Normál sebesség +STR_5143 :Gyors sebesség +STR_5144 :Extra gyors sebesség +STR_5145 :Turbó sebesség STR_5146 :Hipersebesség STR_5147 :Csalások STR_5148 :{SMALLFONT}{BLACK}A játék sebességének megváltoztatása STR_5149 :{SMALLFONT}{BLACK}A csalások beállításainak megjelenítése -STR_5150 :Hibakereső eszköszök engedélyezése +STR_5150 :Hibakereső eszközök engedélyezése STR_5151 : STR_5152 :, STR_5153 :Témák szerkesztése... @@ -2414,10 +2618,15 @@ STR_5164 :Twitch csatorna neve STR_5165 :Vendégek követők utáni elnevezése STR_5166 :{SMALLFONT}{BLACK}A vendégek a csatorna Twitch követői{NEWLINE}után kapják a nevüket STR_5167 :Követőkről elnevezett vendégek követése +STR_5168 :{SMALLFONT}{BLACK}A csatorna Twitch követőiről elnevezett vendégek követési információinak bekapcsolása STR_5169 :Vendégek Twitch chat résztvevők utáni elnevezése STR_5170 :{SMALLFONT}{BLACK}A vendégek Twitch chat résztvevők utáni elnevezése STR_5171 :Chat résztvevőkről elnevezett vendégek követése +STR_5172 :{SMALLFONT}{BLACK}A Twitch chat résztvevőiről elnevezett vendégek követési információinak bekapcsolása STR_5173 :Twitch chat hírként történő megjelenítése +STR_5174 :{SMALLFONT}{BLACK}A !news előtagú Twitch chat üzenetek játékbeli üzenetként történő használata +STR_5175 :Add meg a Twitch csatornád nevét +STR_5176 :Twitch integráció engedélyezése STR_5177 :Képernyőmód: STR_5178 :{SMALLFONT}{BLACK}Pénzügyi csalások STR_5179 :{SMALLFONT}{BLACK}Vendég-csalások @@ -2432,27 +2641,73 @@ STR_5187 :Pénzügyek STR_5188 :Új kampány STR_5189 :Kutatás STR_5190 :Térkép +STR_5191 :Látkép STR_5192 :Friss hírek +STR_5193 :Talaj STR_5194 :Víz +STR_5195 :Díszlet törlése +STR_5196 :Földjogok +STR_5197 :Díszlet +STR_5198 :Út +STR_5199 :Játék építése +STR_5200 :Játékterv lerakása +STR_5201 :Új játék +STR_5202 :Játékterv választás +STR_5203 :Játék +STR_5204 :Játéklista +STR_5205 :Vendég +STR_5206 :Vendéglista +STR_5207 :Személyzet +STR_5208 :Személyzeti lista +STR_5209 :Hirdetőtábla +STR_5210 :Objektum választás +STR_5211 :Találmányok listája +STR_5212 :Pályabeállítások +STR_5213 :Cél beállítások STR_5214 :Térkép generálása +STR_5215 :Játékterv-kezelő +STR_5216 :Játékterv-kezelő lista STR_5217 :Csalások STR_5218 :Témák STR_5219 :Beállítások +STR_5220 :Gyorsbillentyűk +STR_5221 :Gyorsbillentyűk megváltoztatása STR_5222 :Betöltés/mentés +STR_5223 :Mentés rákérdezés +STR_5224 :Játék lerombolása rákérdezés +STR_5225 :Alkalmazott kirúgása rákérdezés +STR_5226 :Pálya törlése rákérdezés +STR_5227 :Mentés felülírása rákérdezés +STR_5228 :{SMALLFONT}{BLACK}Fő felhasználói felület +STR_5229 :{SMALLFONT}{BLACK}Park +STR_5230 :{SMALLFONT}{BLACK}Eszközök +STR_5231 :{SMALLFONT}{BLACK}Játékok és vendégek +STR_5232 :{SMALLFONT}{BLACK}Szerkesztők +STR_5233 :{SMALLFONT}{BLACK}Egyéb +STR_5234 :{SMALLFONT}{BLACK}Rákérdezések +STR_5235 :{SMALLFONT}{BLACK}Beállítások STR_5236 :Ablak STR_5237 :Paletta STR_5238 :Jelenlegi téma: STR_5239 :Másolatkészítés STR_5240 :Írj be egy nevet a téma számára STR_5241 :Ez a téma nem változtatható meg +STR_5242 :Már létezik ilyen nevű téma +STR_5243 :Érvénytelen karaktereket tartalmaz STR_5244 :Témák STR_5245 :Felső eszköztár STR_5246 :Alsó eszköztár STR_5247 :Játéktervező alsó eszköztára STR_5248 :Pályaszerkesztő alsó eszköztára +STR_5249 :Főcím menü gombok +STR_5250 :Főcím kilépés gomb +STR_5251 :Főcím beállítások gomb +STR_5252 :Főcím pályaválasztás +STR_5253 :Park információ STR_5254 :Létrehozás STR_5255 :{SMALLFONT}{BLACK}Egy teljesen új főcím létrehozása -STR_5257 :{SMALLFONT}{BLACK}Új téma létehozása a jelenlegi alapján +STR_5256 :Hozz létre egy új témát, amit módosíthatsz +STR_5257 :{SMALLFONT}{BLACK}Új téma létrehozása a jelenlegi alapján STR_5258 :{SMALLFONT}{BLACK}A jelenlegi téma törlése STR_5259 :{SMALLFONT}{BLACK}A jelenlegi téma átnevezése STR_5260 :Képmentés a teljes térképről @@ -2475,6 +2730,10 @@ STR_5276 :Add meg a keresendő objektum nevét STR_5277 :Törlés STR_5278 :Homokozó mód STR_5279 :Homokozó mód ki +STR_5281 :{SMALLFONT}{BLACK}Jellemzők +STR_5282 :RCT1 játék nyitva/zárva lámpák +STR_5283 :RCT1 park nyitva/zárva lámpák +STR_5284 :RCT1 pályaválasztó betűtípus STR_5285 :ROBBANJANAK!!! STR_5286 :{SMALLFONT}{BLACK}Néhány vendéget felrobbant STR_5287 :A játék már meghibásodott @@ -2491,6 +2750,9 @@ STR_5297 :{SMALLFONT}{BLACK}A park megnyitása STR_5298 :{RED}{STRINGID} STR_5299 :{LIGHTPINK}{STRINGID} STR_5300 :{SMALLFONT}{BLACK}Az alkalmazottak gyors kirúgása +STR_5301 :{MEDIUMFONT}{BLACK}A kölcsönöd törlése +STR_5302 :Kölcsön törlése +STR_5303 :Szünet alatti építkezés engedélyezése STR_5304 :Főcím: STR_5305 :RollerCoaster Tycoon 1 STR_5306 :RollerCoaster Tycoon 1 (AA) @@ -2499,9 +2761,35 @@ STR_5308 :RollerCoaster Tycoon 2 STR_5309 :OpenRCT2 STR_5310 :Véletlenszerű STR_5311 :{SMALLFONT}{BLACK}Hibakereső eszközök -STR_5313 :Mezővizsgáló megjelenítése +STR_5312 :Konzol +STR_5313 :Mezővizsgáló STR_5314 :Mezővizsgáló +STR_5315 :Fű +STR_5316 :Homok +STR_5317 :Föld +STR_5318 :Kavics +STR_5319 :Marsi +STR_5320 :Sakktábla +STR_5321 :Fűcsomók +STR_5322 :Jég +STR_5323 :Rács (piros) +STR_5324 :Rács (sárga) +STR_5325 :Rács (kék) +STR_5326 :Rács (zöld) +STR_5327 :Homok (sötét) +STR_5328 :Homok (világos) +STR_5329 :Sakktábla (fordított) +STR_5330 :Földalatti nézet +STR_5331 :Kavics +STR_5332 :Fa (piros) +STR_5333 :Fa (fekete) STR_5334 :Jég +STR_5335 :Játék bejárata +STR_5336 :Játék kijárata +STR_5337 :Park bejárata +STR_5338 :Elem típusa +STR_5339 :{SMALLFONT}{BLACK}Alapmagasság +STR_5340 :{SMALLFONT}{BLACK}Felszín feletti magasság STR_5343 :Alkalmazottak automatikus lerakása STR_5344 :Változási napló STR_5345 :Pénzügyi csalások @@ -2519,14 +2807,19 @@ STR_5356 :{BLACK}Émelygés: STR_5357 :{BLACK}Émelygéstűrés: STR_5358 :{BLACK}WC: STR_5359 :Vendégek eltávolítása +STR_5360 :{SMALLFONT}{BLACK}Eltávolít minden vendéget a térképről STR_5361 :Minden vendég kapjon: +STR_5362 :{BLACK}A vendégek preferált játékintenzitása: STR_5363 :Több mint 1 STR_5364 :Kevesebb mint 15 STR_5365 :{BLACK}Személyzet gyors.: STR_5366 :Normál STR_5367 :Gyors +STR_5368 :Ütközés törlése STR_5369 :Park paraméterei... STR_5370 :{SMALLFONT}{BLACK}Kattints erre a gombra, hogy módosítsd{NEWLINE}a park olyan paramétereit, mint{NEWLINE}a vendégek generálása és a pénz. +STR_5371 :Objektum választás +STR_5372 :Jobb egeres húzás invertálása STR_5373 :Név {STRINGID} STR_5374 :Dátum {STRINGID} STR_5375 :▲ @@ -2609,6 +2902,7 @@ STR_5453 :Másik játék választása STR_5454 :Korlátlan FPS STR_5455 :Homokozó mód be STR_5456 :Távolság-ellenőrzés ki +STR_5457 :Állványzatlimitek ki STR_5458 :Forgatás órajárás szerint STR_5459 :Forgatás órajárással ellentétesen STR_5460 :Forgatás órajárással ellentétesen @@ -2620,6 +2914,10 @@ STR_5465 :Éghajlat STR_5466 :Személyzet STR_5467 :ALT + STR_5468 :Legutóbbi üzenetek +STR_5469 :Térkép felfelé görgetése +STR_5470 :Térkép balra görgetése +STR_5471 :Térkép lefelé görgetése +STR_5472 :Térkép jobbra görgetése STR_5473 :Nappalok és éjszakák váltakozása STR_5474 :Nagybetűs szöveg a hirdetőtáblákon STR_5475 :{COMMA16} hét @@ -2648,8 +2946,14 @@ STR_5498 :Szerver lista STR_5499 :Játékos neve: STR_5500 :Szerver hozzáadása STR_5501 :Szerver indítása +STR_5502 :Többjátékos mód +STR_5503 :Add meg a gépnevet vagy IP-címet: +STR_5504 :{SMALLFONT}{BLACK}A többjátékos mód állapota +STR_5505 :Nem lehet csatlakozni a szerverhez. +STR_5506 :Vendégeket nem érdeklik az intenzitások STR_5508 :Hibás ellenőrző összegű fájlok engedélyezése STR_5509 :{SMALLFONT}{BLACK}Engedélyezi a hibás ellenőrző összeggel{NEWLINE}rendelkező pályák és mentések{NEWLINE}betöltését, mint például a próbaverzió pályái vagy a sérült mentések. +STR_5510 :Alapértelmezett hangeszköz STR_5511 :(ISMERETLEN) STR_5512 :Játék mentése másként STR_5513 :(Gyors)mentés @@ -2687,13 +2991,21 @@ STR_5544 :{SMALLFONT}{BLACK}Élénkpiros STR_5545 :{SMALLFONT}{BLACK}Sötét rózsaszín STR_5546 :{SMALLFONT}{BLACK}Élénk rózsaszín STR_5547 :{SMALLFONT}{BLACK}Világos rózsaszín +STR_5548 :Minden működési mód megjelenítése STR_5549 :év/hónap/nap STR_5550 :{POP16}{POP16}{COMMA16}. év {PUSH16}{PUSH16}{MONTH} {PUSH16}{PUSH16}{STRINGID} STR_5551 :év/nap/hónap STR_5552 :{POP16}{POP16}{COMMA16}. év {PUSH16}{PUSH16}{PUSH16}{STRINGID} {MONTH} STR_5553 :Játék megállítása, ha a Steam Átfedés nyitva van +STR_5554 :{SMALLFONT}{BLACK}A hegy-eszköz bekapcsolása +STR_5556 :{SMALLFONT}{BLACK}Játékos kirúgása STR_5557 :Kapcsolat fenntartása hiba esetén (többjátékos mód) +STR_5558 :A beállítás érvénybe lépéséhez újraindítás szükséges +STR_5559 :10 perces ellenőrzés +STR_5560 :{SMALLFONT}{BLACK}Az ellenőrzési időt „minden 10. percben”-re állítja az összes játékon +STR_5561 :A nyelv betöltése sikertelen STR_5562 :FIGYELMEZTETÉS! +STR_5563 :Ez a funkció jelenleg instabil, legyél rendkívül elővigyázatos. STR_5564 :Sérült elem beillesztése STR_5565 :{SMALLFONT}{BLACK}Egy sérült elemet illeszt be a mező fölé. A sérült elem feletti elemek nem fognak látszani. STR_5566 :Jelszó: @@ -2712,6 +3024,7 @@ STR_5578 :Orosz rubel (₽) STR_5579 :Ablak méretezése: STR_5580 :Cseh korona (Kč) STR_5581 :FPS megjelenítése +STR_5582 :Egérmutató elfogása az ablakban STR_5583 :{COMMA1DP16} m/s STR_5584 :SI STR_5586 :Boltok és bódék automatikus megnyitása @@ -2735,6 +3048,9 @@ STR_5603 :A vendég elhagyott egy játékot STR_5604 :A vendég vett valamit STR_5605 :A vendég használt egy létesítményt STR_5606 :A vendég meghalt +STR_5607 :{SMALLFONT}{BLACK}A kiválasztott térképelem kényszerített eltávolítása. +STR_5617 :{SMALLFONT}{BLACK}A kiválasztott elem felfelé mozgatása. +STR_5618 :{SMALLFONT}{BLACK}A kiválasztott elem lefelé mozgatása. STR_5619 :RollerCoaster Tycoon STR_5620 :Added Attractions STR_5621 :Loopy Landscapes @@ -2749,6 +3065,49 @@ STR_5629 :Nehézségi szint STR_5630 :Progresszív feloldás bekapcsolása STR_5631 :Eredeti DLC parkok STR_5632 :Építsd meg a saját... +STR_5635 :{WINDOW_COLOUR_2}Elköltött pénz: {BLACK}{CURRENCY2DP} +STR_5636 :{WINDOW_COLOUR_2}Futtatott parancsok: {BLACK}{COMMA16} +STR_5637 :Ezt nem csinálhatod... +STR_5638 :Engedély megtagadva +STR_5639 :{SMALLFONT}{BLACK}Játékosok listája +STR_5640 :{SMALLFONT}{BLACK}Csoportok kezelése +STR_5641 :Alapértelmezett csoport: +STR_5642 :Csoport +STR_5643 :Új csoport +STR_5644 :Csoport törlése +STR_5645 :Chat +STR_5646 :Terraformálás +STR_5647 :Szünet be/ki +STR_5648 :Vízszint beállítása +STR_5649 :Játék létrehozása +STR_5650 :Játék eltávolítása +STR_5651 :Játék építése +STR_5652 :Játék tulajdonságai +STR_5653 :Díszletek +STR_5654 :Út +STR_5655 :Vendég +STR_5656 :Személyzet +STR_5657 :Park tulajdonságai +STR_5658 :Park finanszírozása +STR_5659 :Játékos kirúgása +STR_5660 :Csoportok módosítása +STR_5661 :Játékoscsoport beállítása +STR_5662 :Nincs adat +STR_5663 :Földterület megtisztítása +STR_5664 :Csalás +STR_5665 :Díszletcsoportok be/ki +STR_5666 :Jelszó nélküli bejelentkezés +STR_5701 :{WINDOW_COLOUR_2}Utolsó tevékenység: {BLACK}{STRINGID} +STR_5702 :{SMALLFONT}{BLACK}A játékos legutóbbi tevékenységének megkeresése +STR_5703 :A hoszt nem rúgható ki +STR_5704 :Utolsó tevékenység +STR_5705 :Nem rakható ebbe a csoportba +STR_5706 :Nem távolítható el olyan csoport, amelybe játékosok tartoznak +STR_5707 :Ez a csoport nem módosítható +STR_5708 :Nem változtatható meg az a csoport, amelybe a hoszt tartozik +STR_5709 :Cs. átnevezése +STR_5710 :Csoport neve +STR_5711 :Írj be egy új nevet a csoport számára: STR_5712 :Nem módosíthatsz olyan engedélyt, amellyel te sem rendelkezel STR_5713 :Játékos kirúgása STR_5714 :Beállítások ablak @@ -2758,7 +3117,7 @@ STR_5716 :Nem engedélyezett a többjátékos módban STR_5717 :Hálózati verzió: {STRING} STR_5718 :{SMALLFONT}{BLACK}Hálózati verzió: {STRING} STR_5719 :Napos -STR_5720 :Részlegesen felhős +STR_5720 :Részben felhős STR_5721 :Felhős STR_5722 :Esős STR_5723 :Heves esőzés @@ -2769,21 +3128,29 @@ STR_5727 :Skálázás minősége: STR_5731 :Lineáris # tooltip for tab in options window STR_5734 :{SMALLFONT}{BLACK}Megjelenítés +STR_5735 :Hálózati állapot STR_5736 :Játékos STR_5737 :Zárva, még {COMMA16} ember van a játékon STR_5738 :Zárva, még {COMMA16} ember van a játékon STR_5739 :{WINDOW_COLOUR_2}Vendégek a játékon: {BLACK}{COMMA16} -STR_5740 :Véget nem érő marketingkapányok -STR_5741 :{SMALLFONT}{BLACK}A marketingkapányok soha nem érnek véget +STR_5740 :Véget nem érő marketingkampányok +STR_5741 :{SMALLFONT}{BLACK}A marketingkampányok soha nem érnek véget STR_5742 :Hitelesítés ... STR_5743 :Csatlakozás ... STR_5744 :Feloldás ... STR_5745 :Hálózati szinkronizációs hiba észlelve +STR_5746 :Kilépett +STR_5747 :Kilépett: {STRING} STR_5748 :Kirúgva +STR_5749 :Ki a szerverről! STR_5750 :Kapcsolat lezárva STR_5751 :Nincs adat +STR_5752 :{OUTLINE}{RED}{STRING} kilépett +STR_5753 :{OUTLINE}{RED}{STRING} kilépett ({STRING}) STR_5754 :Hibás játékos név +STR_5755 :Hibás szoftververzió (A szerver ezt használja: {STRING}) STR_5756 :Hibás jelszó +STR_5757 :A szerver megtelt STR_5758 :{OUTLINE}{GREEN}{STRING} csatlakozott a játékhoz STR_5759 :Térkép letöltése ... ({INT32} / {INT32}) KiB STR_5760 :Hongkongi dollár (HK$) @@ -2792,6 +3159,7 @@ STR_5762 :Kínai jüan (CN¥) STR_5763 :Minden fájl STR_5764 :Érvénytelen játéktípus STR_5765 :Az érvénytelen típusú játékok nem szerkeszthetők +STR_5766 :Magyar forint (Ft) STR_5767 :Bevétel STR_5768 :Összes vendég STR_5769 :Teljes nyereség @@ -2807,7 +3175,7 @@ STR_5778 :Építés ideje: {COMMA16} éve STR_5779 :Bevétel: {CURRENCY2DP} óránként STR_5780 :Üzemeltetési költség: {CURRENCY2DP} óránként STR_5781 :Üzemeltetési költség: Ismeretlen -STR_5782 :Kapcsolódva vagy. A(z) ’{STRING}’ lenyomásával cseveghetsz. +STR_5782 :Csatlakoztál. A(z) ’{STRING}’ lenyomásával cseveghetsz. STR_5783 :{WINDOW_COLOUR_2}Lezárt pálya STR_5784 :{BLACK}Teljesíts korábbi pályákat a feloldásához. STR_5785 :A csoport nem nevezhető át... @@ -2815,12 +3183,23 @@ STR_5786 :Érvénytelen csoportnév STR_5787 :{COMMA32} játékos van online STR_5788 :Alapértelmezett ellenőrzési idő: STR_5789 :Villámhatás kikapcsolása +STR_5791 :{SMALLFONT}{BLACK}Az összes játék megbízhatóságát 100%-ra{NEWLINE}állítja és visszaállítja az építési idejüket „idén”-re +STR_5792 :{SMALLFONT}{BLACK}Megjavítja az összes meghibásodott játékot +STR_5793 :{SMALLFONT}{BLACK}Törli a játék ütközési előzményeit,{NEWLINE}így a vendégek nem fognak panaszkodni, hogy nem biztonságos +STR_5794 :{SMALLFONT}{BLACK}Néhány pálya nem engedélyezi pár játék{NEWLINE}szerkesztését, melyek már a parkban vannak{NEWLINE}Ez a csalás feloldja a korlátozást +STR_5795 :{SMALLFONT}{BLACK}A vendégek a park összes játékára felülnek,{NEWLINE}még az extrém magas intenzitásúakra is +STR_5796 :{SMALLFONT}{BLACK}Kényszeríti a parkot, hogy kinyisson vagy bezárjon +STR_5797 :{SMALLFONT}{BLACK}Letiltja az időjárás-változásokat{NEWLINE}és befagyasztja a jelenlegi időjárást +STR_5798 :{SMALLFONT}{BLACK}Engedélyezi az építési tevékenységeket szünet közben +STR_5799 :{SMALLFONT}{BLACK}Letiltja a játékok fékhiba miatta meghibásodásait és ütközéseit +STR_5800 :{SMALLFONT}{BLACK}Megakadályozza, hogy meghibásodjanak a játékok STR_5801 :Szemetelés kikapcsolása STR_5790 :{SMALLFONT}{BLACK}Az RCT1-stílusú árazás ki- és bekapcsolása{NEWLINE}(például belépési díj egyszerre a parkba és a játékokra) +STR_5795 :{SMALLFONT}{BLACK}A vendégek a park összes játékára felülnek,{NEWLINE}még az extrém magas intenzitásúakra is STR_5802 :{SMALLFONT}{BLACK}A vendégek nem szemetelnek és hánynak STR_5803 :{SMALLFONT}{BLACK}A kiválasztott térképelem forgatása STR_5804 :Hang némítása -STR_5805 :{SMALLFONT}{BLACK}Ha ki van pipálva, a szervered hozzá lesz adva a{NEWLINE}nyilvános szerverek listájához, így bárki rátalálhat +STR_5805 :{SMALLFONT}{BLACK}Ha ki van pipálva, a szervered hozzá lesz adva{NEWLINE}a nyilvános szerverek listájához, így bárki rátalálhat STR_5806 :Váltás ablakos/teljes képernyős mód között STR_5807 :{WINDOW_COLOUR_2}Játékok száma: {BLACK}{COMMA16} STR_5808 :{WINDOW_COLOUR_2}Boltok és bódék száma: {BLACK}{COMMA16} @@ -2837,8 +3216,14 @@ STR_5816 :{SMALLFONT}{BLACK}A játék méretezési tényezőjének beállít STR_5820 :{SMALLFONT}{BLACK}A játék minimalizálása, ha elveszti a fókuszt{NEWLINE}teljes képernyős módban STR_5822 :{SMALLFONT}{BLACK}A nap és az éjszaka váltakozásának bekapcsolása.{NEWLINE}A teljes ciklus egy játékbeli hónapig tart STR_5823 :{SMALLFONT}{BLACK}Nagybetűs megjelenítés a hirdetőtáblákon (RCT1-beli viselkedés) -STR_5824 :{SMALLFONT}{BLACK}A villámhatás kikapcsolása{NEWLINE}a viharok alatt +STR_5824 :{SMALLFONT}{BLACK}A viharok alatti villámhatás{NEWLINE}kikapcsolása +STR_5825 :{SMALLFONT}{BLACK}Az egérmutató maradjon az ablakon belül +STR_5826 :{SMALLFONT}{BLACK}A látkép jobb egeres húzásának invertálása STR_5835 :{SMALLFONT}{BLACK}A játék némítása, ha az ablak elveszti a fókuszt +STR_5838 :{SMALLFONT}{BLACK}Külön gomb a pénzügyi ablak számára az eszköztáron +STR_5839 :{SMALLFONT}{BLACK}Külön gomb a kutatás-fejlesztés ablak számára az eszköztáron +STR_5840 :{SMALLFONT}{BLACK}Külön gomb a csalások ablak számára az eszköztáron +STR_5841 :{SMALLFONT}{BLACK}Külön gomb a legutóbbi hírek ablak számára az eszköztáron STR_5843 :{SMALLFONT}{BLACK}A pályák progresszív feloldásának engedélyezése (RCT1-beli viselkedés) STR_5844 :{SMALLFONT}{BLACK}Tartsa fenn a kapcsolatot a többjátékos{NEWLINE}szerverrel akkor is, ha szinkronizációs vagy egyéb hiba jelentkezik STR_5845 :{SMALLFONT}{BLACK}Hozzáadja a hibakereső{NEWLINE}eszközök gombját az eszköztárhoz.{NEWLINE}Engedélyezi a fejlesztői konzol gyorsbillentyűjét @@ -2853,9 +3238,15 @@ STR_5857 :{SMALLFONT}{BLACK}Játékbeállítások STR_5858 :{SMALLFONT}{BLACK}CPU helyett a GPU használata megjelenítéshez. Nagyobb kompatibilitást biztosít a képernyőrögzítő programokkal. Némileg csökkentheti a teljesítményt. STR_5859 :{SMALLFONT}{BLACK}A képkocka fázisolás bekapcsolása, hogy {NEWLINE}folyamatosabb legyen a játékmenet megjelenítése. Ha ki van kapcsolva,{NEWLINE}a játék 40 FPS-el fut. STR_5860 :Váltás az eredeti és a visszafejtett pályarajzok között +STR_5861 :Kulcs hitelesítési hiba +STR_5862 :Ismeretlen játékosok blokkolása. +STR_5863 :{SMALLFONT}{BLACK}Csak az ismert kulcsokkal rendelkező játékosok csatlakozhassanak. +STR_5864 :Ehhez a szerverhez csak fehérlistás játékosok csatlakozhatnak. +STR_5865 :Chat előzmények naplózása STR_5867 :{WINDOW_COLOUR_2}Szolgáltató neve: {BLACK}{STRING} STR_5868 :{WINDOW_COLOUR_2}Szolgáltató e-mail címe: {BLACK}{STRING} STR_5869 :{WINDOW_COLOUR_2}Szolgáltató weboldala: {BLACK}{STRING} +STR_5870 :{SMALLFONT}{BLACK}Szerver információ STR_5871 :Nem öregedő növények STR_5872 :{SMALLFONT}{BLACK}A növények öregedésének kikapcsolása, hogy ne hervadjanak el STR_5875 :Grafikus motor: @@ -2866,8 +3257,14 @@ STR_5879 :OpenGL (kísérleti) STR_5880 :Csak a kiválasztottak STR_5881 :Csak a nem kiválasztottak STR_5882 :Egyedi pénznem +STR_5883 :Egyedi pénznem beállítása +STR_5884 :{WINDOW_COLOUR_2}Átváltási árfolyam: +STR_5885 :{WINDOW_COLOUR_2}annyi, mint {COMMA32} GBP (£) +STR_5886 :{WINDOW_COLOUR_2}Pénznem szimbóluma: STR_5887 :Előtag STR_5888 :Utótag +STR_5889 :Egyedi pénznem szimbólum +STR_5890 :Add meg a megjelenítendő pénznem szimbólumot STR_5891 :Alapértelmezett STR_5892 :{SMALLFONT}{BLACK}Vissza az alapértelmezett mappába STR_5893 :Árfolyam @@ -2899,15 +3296,40 @@ STR_5930 :Nagy díszlet részletei STR_5931 :Hirdetőtábla részletei STR_5932 :Sérült elem részletei STR_5933 :Tulajdonságok +STR_5937 :Nincs tulajdonban és nem eladó +STR_5938 :{WINDOW_COLOUR_2}Vízszint: {BLACK}{COMMA16} STR_5939 :Park kerítésének eltávolítása STR_5940 :Park kerítésének visszaállítása +STR_5941 :{WINDOW_COLOUR_2}Alapmagasság: +STR_5942 :{WINDOW_COLOUR_2}Út neve: {BLACK}{STRINGID} +STR_5943 :{WINDOW_COLOUR_2}Extrák: {BLACK}{STRINGID} +STR_5944 :{WINDOW_COLOUR_2}Extrák: {BLACK}Semmi +STR_5945 :{WINDOW_COLOUR_2}Összekapcsolt szélek: +STR_5946 :{WINDOW_COLOUR_2}Játék típusa: {BLACK}{STRINGID} +STR_5947 :{WINDOW_COLOUR_2}Játék ID: {BLACK}{COMMA16} +STR_5948 :{WINDOW_COLOUR_2}Játék neve: {BLACK}{STRINGID} +STR_5949 :{WINDOW_COLOUR_2}Láncos felvonó +STR_5954 :{WINDOW_COLOUR_2}Díszlet kora: {BLACK}{COMMA16} STR_5956 :Délnyugat STR_5957 :Északnyugat STR_5958 :Északkelet STR_5959 :Délkelet +STR_5962 :{WINDOW_COLOUR_2}Ütközésérzékelés: +STR_5966 :{WINDOW_COLOUR_2}Parkbejárat része: {BLACK}{STRINGID} +STR_5967 :Középső +STR_5968 :Bal +STR_5969 :Jobb +STR_5970 :{WINDOW_COLOUR_2}Bejárat ID: {BLACK}{COMMA16} +STR_5971 :{WINDOW_COLOUR_2}Kijárat ID: {BLACK}{COMMA16} +STR_5972 :{WINDOW_COLOUR_2}Játék ID: {BLACK}{COMMA16} +STR_5979 :{WINDOW_COLOUR_2}Fal típusa: {BLACK}{COMMA16} +STR_5980 :{WINDOW_COLOUR_2}Hirdetőtábla szövege: {BLACK}{STRINGID} +STR_5981 :{WINDOW_COLOUR_2}Nem hirdetőtábla +STR_5982 :{WINDOW_COLOUR_2}Nagy díszlet típusa: {BLACK}{COMMA16} +STR_5983 :{WINDOW_COLOUR_2}Nagy díszletelem ID: {BLACK}{COMMA16} STR_5984 :Blokkolt utak: STR_5985 :Új mappa -STR_5986 :Írd be az új mapa nevét. +STR_5986 :Írd be az új mappa nevét. STR_5987 :A mappát nem lehet létrehozni STR_5988 :{SMALLFONT}{BLACK}Nincs több eladó földterület STR_5989 :{SMALLFONT}{BLACK}Nincs több eladó építési jog @@ -2923,7 +3345,7 @@ STR_5998 :Pénz hozzáadása STR_5999 :Pénz beállítása STR_6000 :Add meg az új értéket STR_6001 :Fényhatások bekapcsolása (kísérleti) -STR_6002 :{SMALLFONT}{BLACK}A lámpák és a játékok éjjelente világítani fognak.{NEWLINE}A grafikus motort hardveres gyorsítára kell állítani hozzá. +STR_6002 :{SMALLFONT}{BLACK}A lámpák és a játékok éjjelente világítani fognak.{NEWLINE}A grafikus motort hardveres gyorsításra kell állítani hozzá. STR_6003 :Levágó nézet STR_6004 :Levágó nézet STR_6005 :Levágó nézet bekapcsolása @@ -2933,26 +3355,82 @@ STR_6008 :{SMALLFONT}{BLACK}Kattints ide a nyers értékek<->mértékegysége STR_6009 :{SMALLFONT}{BLACK}A vágási magasság kiválasztása STR_6010 :{COMMA2DP32} m STR_6011 :{COMMA1DP16} láb +STR_6012 :{COMMA1DP16} +STR_6015 :Lejtős STR_6016 :Mező módosítása STR_6017 :Kérlek lassíts +STR_6030 :{SMALLFONT}{BLACK}Díszletválasztó. Kattints bármelyik díszletre a térképen, hogy újat építhess ugyanabból a darabból. STR_6031 :Szerver leírása: STR_6032 :Szerver üdvözlés: STR_6033 :Az RCT1 telepítési útvonala: +STR_6034 :{SMALLFONT}{BLACK}{STRING} +STR_6035 :Kérlek válaszd ki az RCT1 mappádat +STR_6036 :{SMALLFONT}{BLACK}Törlés +STR_6037 :Kérlek válassz egy érvényes RCT1 mappát +STR_6040 :Pályabeállítások módosítása STR_6041 :{BLACK}Nem vettél fel gépészt! +STR_6042 :Magasságtérkép betöltése +STR_6043 :Magasságtérkép kiválasztása +STR_6044 :Magasságtérkép simítása +STR_6045 :Erősség +STR_6046 :Magasságtérkép normalizálása +STR_6047 :Mezők simítása +STR_6048 :Magasságtérkép hiba +STR_6049 :PNG olvasási hiba +STR_6050 :Bitmap olvasási hiba +STR_6051 :A szélesség és magasság megegyező kell, hogy legyen +STR_6052 :A magasságtérkép túl nagy és le lesz vágva +STR_6053 :A magasságtérkép nem normalizálható +STR_6054 :Csak a 24 bites bitmapek támogatottak STR_6055 :OpenRCT2 magasságtérkép fájl STR_6056 :{SMALLFONT}{BLACK}Némítás +STR_6057 :{SMALLFONT}{BLACK}Külön gomb a némítási opció számára az eszköztáron STR_6058 :Némítás STR_6059 :» STR_6060 :Vendégek vásárlásainak animálása STR_6061 :{SMALLFONT}{BLACK}Animált pénzhatás megjelenítése,{NEWLINE}mikor a vendégek vásárolnak valamit. STR_6062 :{OUTLINE}{GREEN}+ {CURRENCY2DP} STR_6063 :{OUTLINE}{RED}- {CURRENCY2DP} +STR_6064 :Minden föld a tiéd +STR_6065 :Felhasználói tevékenységek naplózása +STR_6066 :{SMALLFONT}{BLACK}Minden felhasználói tevékenységet naplóz a felhasználói mappádban lévő fájlokba STR_6067 :Szerver elindítva. STR_6068 :Szerver leállítva. +STR_6069 :{STRING} ki lett rúgva a szerverről {STRING} által. +STR_6070 :{STRING} a(z) '{STRING}' csoportba lett rakva {STRING} által. +STR_6071 :{STRING} egy új játékoscsoportot hozott létre '{STRING}' néven. +STR_6072 :{STRING} törölte a(z) '{String}' játékoscsoportot. +STR_6073 :{STRING} módosította a(z) '{String}' játékoscsoport engedélyeit. +STR_6074 :{STRING} megváltoztatta egy játékoscsoport nevét. Régi név: '{STRING}' / Új név: '{STRING}' +STR_6075 :{STRING} megváltoztatta az alap játékoscsoportot erre: '{String}'. +STR_6076 :{STRING} használt/bekapcsolt egy csalást: '{STRING}'. +STR_6077 :Pénz hozzáadása +STR_6078 :{STRING} új játékot hozott létre: '{STRING}'. +STR_6079 :{STRING} lerombolta a(z) '{STRING}' játékot. +STR_6080 :{STRING} megváltoztatta a(z) '{STRING}' játék kinézetét. +STR_6081 :{STRING} megváltoztatta a(z) '{STRING}' játék státuszát erre: zárva +STR_6082 :{STRING} megváltoztatta a(z) '{STRING}' játék státuszát erre: nyitva +STR_6083 :{STRING} megváltoztatta a(z) '{STRING}' játék státuszát erre: tesztelés +STR_6084 :{STRING} megváltoztatta '{STRING}' járműbeállításait. +STR_6085 :{STRING} megváltoztatta '{STRING}' játékbeállításait. +STR_6086 :{STRING} átnevezett egy játékot. Régi név: '{STRING}' / Új név: '{STRING}' +STR_6087 :{STRING} megváltoztatta a(z) '{STRING}' játék árát erre: {STRING} +STR_6088 :{STRING} megváltoztatta a(z) '{STRING}' játék másodlagos árát erre: {STRING} +STR_6089 :{STRING} átnevezte a parkot. Régi név: '{STRING}' / Új név: '{STRING}' +STR_6090 :{STRING} megnyitotta a parkot. +STR_6091 :{STRING} bezárta a parkot. +STR_6092 :{STRING} megváltoztatta a park belépési díját: {STRING} +STR_6093 :{STRING} egy új díszletet rakott le. +STR_6094 :{STRING} eltávolított egy díszletet. +STR_6095 :{STRING} módosított egy díszletet. +STR_6096 :{STRING} megváltoztatta egy tábla feliratát: '{STRING}'. +STR_6097 :{STRING} lerakta '{STRING}' játék egy pályaelemét. +STR_6098 :{STRING} eltávolított egy pályaelemet valamelyik játékról. STR_6099 :Kapcsolódtál a szerverhez. STR_6100 :Bontottad a kapcsolatot a szerverrel. STR_6101 :Nem csökken a játékok értéke STR_6102 :{SMALLFONT}{BLACK}A játékok értéke nem csökken az idő múlásával, így a vendégek nem fogják hirtelen azt gondolni, hogy túl drágák +STR_6103 :{SMALLFONT}{BLACK}Ez a beállítás le van tiltva hálózati játék alatt. STR_6104 :Dugóhúzó hullámvasút STR_6105 :Hiperhullámvasút STR_6106 :Autós utazás @@ -2979,10 +3457,15 @@ STR_6133 :{SMALLFONT}{BLACK}Hozzáférés a még fel nem fedezett játékokho STR_6134 :Díszlet törlése STR_6135 :A kliens érvénytelen kérést küldött STR_6136 :A szerver érvénytelen kérést küldött +STR_6137 :OpenRCT2, egy ingyenes és nyílt forráskódú Roller Coaster Tycoon 2 klón. +STR_6138 :Az OpenRCT2 számos szerző munkája, a teljes lista a „contributors.md”-ben található. További információkért látogass el ide: http://github.com/OpenRCT2/OpenRCT2 +STR_6139 :Minden termék- és vállalatnév a vonatkozó tulajdonosok birtokában van. A használatuk nem jelent semmiféle kapcsolatot vagy jóváhagyást a részükről. STR_6140 :Változási napló... STR_6141 :RCT1 alsó eszköztár STR_6142 :{WINDOW_COLOUR_2}Pálya neve: {BLACK}{STRING} STR_6143 :{WINDOW_COLOUR_2}Játék típusa: {BLACK}{STRINGID} +STR_6145 :{SMALLFONT}{BLACK}Sebességlimit beállítása a gyorsítók számára +STR_6146 :Minden rajzolható pályaelem engedve STR_6148 :Csatlakozás a főszerverhez... STR_6149 :Nem sikerült csatlakozni a főszerverhez STR_6150 :Érvénytelen válasz a főszervertől (nincs JSON-szám) @@ -2994,10 +3477,11 @@ STR_6156 :A név le van foglalva STR_6157 :Konzol STR_6158 :Fájl betöltése sikertelen...{NEWLINE}Nem kompatibilis RCTC verzió: {COMMA16} STR_6159 :Sima legközelebbi szomszéd +STR_6160 :{WINDOW_COLOUR_2}Rendelkezésre álló járművek: {BLACK}{STRING} STR_6161 :Rácsok megjelenítése be/ki STR_6162 :Forgó vad egér STR_6164 :{WHITE}❌ -STR_6165 :Vertikális szinkron használata +STR_6165 :Vertikális szinkron STR_6166 :{SMALLFONT}{BLACK}A megjelenített képkockákat a kijelző frissítési gyakoriságához igazítja, így megakadályozza a kép szétcsúszását. STR_6167 :{SMALLFONT}{BLACK}Haladó STR_6168 :Főcím @@ -3057,7 +3541,7 @@ STR_6223 :A park határain kívül kell lennie! STR_6224 :{STRING} lehelyezett egy vendég belépési pontot. STR_6225 :Nem támogatott OpenGL megjelenítéssel STR_6226 :Korai pályateljesítés -STR_6227 :{SMALLFONT}{BLACK}A pálya teljesítése, ha az összes cél teljesül a kítűzött idő előtt. +STR_6227 :{SMALLFONT}{BLACK}A pálya teljesítése, ha az összes cél teljesül a kitűzött idő előtt. STR_6228 :Pálya beállítások STR_6229 :{WINDOW_COLOUR_2}{STRINGID}: {STRINGID} STR_6230 :{STRINGID}: @@ -3083,7 +3567,7 @@ STR_6249 :{WINDOW_COLOUR_1}Biztos, hogy fel akarod újítani ezt: {STRINGID}? STR_6250 :{WINDOW_COLOUR_1}Biztos, hogy le akarod rombolni ezt: {STRINGID}? A visszatérítés összege: {CURRENCY} STR_6251 :A játék még nem üres STR_6252 :Twitch API URL-je -STR_6253 :{SMALLFONT}{BLACK}A Twitch inetgrációs API URL-jének megadása. Szükséges a Twitch integráció használatához. +STR_6253 :{SMALLFONT}{BLACK}A Twitch integrációs API URL-jének megadása. Szükséges a Twitch integráció használatához. STR_6254 :A Twitch integrációs API URL-je: STR_6255 :Érvénytelen URL STR_6256 :Megjelenítési hatások @@ -3108,10 +3592,10 @@ STR_6274 :Nem állítható be színséma... STR_6275 :{WINDOW_COLOUR_2}Állomás stílusa: STR_6276 :A vendégek beragadnak itt: {RED}{STRINGID}. Lehet, hogy érvénytelen játéktípus vagy működési mód okozza. STR_6277 :{WINDOW_COLOUR_2}Állomás index: {BLACK}{COMMA16} -STR_6278 :Automentések mennyisége +STR_6278 :Automentések száma STR_6279 :{SMALLFONT}{BLACK}A megőrzendő automentések száma STR_6280 :{SMALLFONT}{BLACK}Chat -STR_6281 :{SMALLFONT}{BLACK}Külön chat gomb mutatása az eszköztáron +STR_6281 :{SMALLFONT}{BLACK}Külön gomb a chat ablak számára az eszköztáron STR_6282 :Chat STR_6283 :A chat jelenleg nem elérhető. Csatlakozva vagy szerverhez? STR_6284 :Hálózat @@ -3134,6 +3618,35 @@ STR_6300 :{SMALLFONT}{BLACK}Az összes hiányzó objektum letöltése, ha el STR_6301 :{SMALLFONT}{BLACK}A kiválasztott objektumnév vágólapra másolása. STR_6302 :{SMALLFONT}{BLACK}A hiányzó objektumok teljes listájának vágólapra másolása. STR_6303 :Objektum letöltése ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :Díszletválasztó megnyitása +STR_6305 :Többszálúság +STR_6306 :{SMALLFONT}{BLACK}Kísérleti beállítás, mellyel bekapcsolható a több szálon történő megjelenítés, instabilitást okozhat. +STR_6307 :Színséma: {BLACK}{STRINGID} +STR_6308 :„{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :Újracsatlakozás +STR_6310 :{WINDOW_COLOUR_2}Pozíció: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Következő: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(felület) +STR_6313 :(lejtő {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Úti cél: {BLACK}{INT32}, {INT32} tolerancia {INT32} +STR_6315 :{WINDOW_COLOUR_2}Útkeresési cél: {BLACK}{INT32}, {INT32}, {INT32} irány {INT32} +STR_6316 :{WINDOW_COLOUR_2}Útkeresési előzmények: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} irány {INT32} +STR_6318 :Hálózati szinkronizációs hiba észlelve.{NEWLINE}Naplófájl: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Blokkolófék zárva +STR_6320 :{WINDOW_COLOUR_2}Elpusztíthatatlan +STR_6321 :{WINDOW_COLOUR_2}Törött hozzáadott elem +STR_6322 :{WINDOW_COLOUR_2}Sprite Id: {BLACK}{INT32} +STR_6323 :Szimuláció folyamatban +STR_6324 :Szimulálás +STR_6325 :{SMALLFONT}{BLACK}Játék/épület szimulálása +STR_6326 :{POP16}{POP16}{POP16}{STRINGID} nem szimulálható... +STR_6327 :Átlátszó hátterű óriás képmentések +STR_6328 :{SMALLFONT}{BLACK}Ha ez az opció ki van jelölve, az óriás képmentések átlátszó hátterűek lesznek az alapértelmezett fekete szín helyett. +STR_6329 :{STRING}{STRINGID} +STR_6330 :[{STRING}] letöltése innen: {STRING} ({COMMA16} / {COMMA16}) +STR_6331 :Kacsák létrehozása +STR_6332 :Kacsák törlése ############# # Scenarios # @@ -3221,8 +3734,8 @@ STR_PARK :Recsegő Rengeteg STR_DTLS :Egy nagy park jól tervezett, de meglehetősen öreg játékokkal - Cseréld le az öreg játékokat, vagy építs újakat, hogy népszerűbb legyen a park -STR_SCNR :Paradicsomi Móló -STR_PARK :Paradicsomi Móló +STR_SCNR :Paradicsom Móló +STR_PARK :Paradicsom Móló STR_DTLS :Alakítsd át ennek az álmos kisvárosnak a mólóját virágzó attrakcióvá @@ -3294,7 +3807,7 @@ STR_DTLS :E futurisztikus park földönkívüli táján bőven van hely az ú STR_SCNR :Szende-szurdok STR_PARK :Szende-szurdok -STR_DTLS :A helyi lakosság az enyhe és nyugtató játékokat kedveli, úgyhogy a te feladatod a park izlésüknek megfelelő bővítése +STR_DTLS :A helyi lakosság az enyhe és nyugtató játékokat kedveli, úgyhogy a te feladatod a park ízlésüknek megfelelő bővítése STR_SCNR :Derűs Dzsungel @@ -3329,7 +3842,7 @@ STR_DTLS :Egy gát körüli területen kell vidámparkot kialakítanod STR_SCNR :Hullámvasút-szurdok STR_PARK :Hullámvasút-szurdok -STR_DTLS :Alakíts ki egy vidámparkot a rendelekezésedre álló hatalmas szurdokban +STR_DTLS :Alakíts ki egy vidámparkot a rendelkezésedre álló hatalmas szurdokban STR_SCNR :Zuhé Park @@ -3403,6 +3916,155 @@ STR_DTLS :Egy nagy kertnek arra van szüksége, hogy sikeres vidámpark legye ## Loopy Landscapes + +STR_SCNR :Jéghegy-szigetek +STR_PARK :Jéghegy-szigetek +STR_DTLS :Ez az ambiciózus vidámpark egy csoportnyi jéghegyre épült + + +STR_SCNR :Vulkánia +STR_PARK :Vulkánia +STR_DTLS :Egy szunnyadó vulkán adja ennek a hullámvasút-építő kihívásnak a helyszínét + + +STR_SCNR :Száraz Magasságok +STR_PARK :Száraz Magasságok +STR_DTLS :Az a kihívásod, hogy gondoskodj a park fejlesztéséről, miközben biztosítod a vendégek jókedvét, pénzügyi korlátok nélkül + + +STR_SCNR :Borotva Sziklák +STR_PARK :Borotva Sziklák +STR_DTLS :Az a feladatod, hogy egy masszív, hullámvasutakkal teli parkot alakíts ki a Borotva Sziklák között + + +STR_SCNR :Kráter-tó +STR_PARK :Kráter-tó +STR_DTLS :Egy ősi kráterben elterülő hatalmas tó a park a helyszíne + + +STR_SCNR :Szédítő Kilátások +STR_PARK :Szédítő Kilátások +STR_DTLS :Ebben a nagy parkban már van egy kiváló hiperhullámvasút, de sokkal nyereségesebbé kell tenned + + +STR_SCNR :Paradicsom Móló 2 +STR_PARK :Paradicsom Móló 2 +STR_DTLS :Paradicsom Móló kiterjesztette a tenger feletti sétány-hálózatát és a feladatod az, hogy a park bővítésével biztosítsd az extra hely kihasználtságát + + +STR_SCNR :Sárkány-öböl +STR_PARK :Sárkány-öböl +STR_DTLS :Ez a tengerparti öböl a helyszíne ennek a hullámvasút-építő kihívásnak + + +STR_SCNR :Jó Lovag Park +STR_PARK :Jó Lovag Park +STR_DTLS :Egy kastély két hullámvasúttal, amit egy nagyobb vidámparkká kell fejlesztened + + +STR_SCNR :Nyüzsgő Nyúlkert +STR_PARK :Nyüzsgő Nyúlkert +STR_DTLS :Egy park, ahol az utak és hullámvasutak nagy része föld alatt van + + +STR_SCNR :Nagy Gleccser +STR_PARK :Nagy Gleccser +STR_DTLS :Fejleszd vidámparkká ezt a gleccserekkel teli völgyet + + +STR_SCNR :Őrült Kráterek +STR_PARK :Őrült Kráterek +STR_DTLS :Egy messzi világban, ahol nincs szükség pénzre, szórakoztató központot kell építened, hogy biztosítsd az emberek boldogságát + + +STR_SCNR :Sivár Sivatag +STR_PARK :Sivár Sivatag +STR_DTLS :Öt hullámvasutat kell befejezni ebben a sivatagi parkban + + +STR_SCNR :Fakukac Park +STR_PARK :Fakukac Park +STR_DTLS :Ez a történelmi park csak régi stílusú játékokat építhet + + +STR_SCNR :Ikarusz Park +STR_PARK :Ikarusz Park +STR_DTLS :Fejleszd ezt a földönkívüli parkot, hogy maximalizáld a nyereségét + + +STR_SCNR :Napos Mocsarak +STR_PARK :Napos Mocsarak +STR_DTLS :Ennek a jól tematizált vidámparknak már számos játéka van, de bőven van helye a terjeszkedésre + + +STR_SCNR :Rémálom Dombok +STR_PARK :Rémálom Dombok +STR_DTLS :Egy rémisztő vidámpark, a közepén egy hatalmas hullámvasúttal + + +STR_SCNR :Mennydörgő-sziklák +STR_PARK :Mennydörgő-sziklák +STR_DTLS :A homokból két nagydarab szikla áll ki, melyeken megkezdődött egy vidámpark építése + + +STR_SCNR :Oktogon Park +STR_PARK :Oktogon Park +STR_DTLS :Tíz hullámvasutat kell építened ebben a nagy parkban + + +STR_SCNR :Élménysziget +STR_PARK :Élménysziget +STR_DTLS :A hosszú, keskeny sziget kihívásokkal teli helyszín a megadott hullámvasutak építésére + + +STR_SCNR :Jégcsap Világok +STR_PARK :Jégcsap Világok +STR_DTLS :Alakítsd át a jeges tájat virágzó vidámparkká + + +STR_SCNR :Déli Homok +STR_PARK :Déli Homok +STR_DTLS :Ez a néhány okosan tervezett hullámvasúttal rendelkező sivatagi park arra vár, hogy kibővítsd + + +STR_SCNR :Törpe Tornyok +STR_PARK :Törpe Tornyok +STR_DTLS : Öt meglévő hullámvasutat kell befejezned ebben a kis parkban + + +STR_SCNR :Sohamár Park +STR_PARK :Sohamár Park +STR_DTLS :Egy nagy park, egyedi közlekedési rendszerrel a széle körül + + +STR_SCNR :Pacifica +STR_PARK :Pacifica +STR_DTLS :Ez a nagy sziget csak a tiéd, hogy vidámparkká fejleszd + + +STR_SCNR :Városi Dzsungel +STR_PARK :Városi Dzsungel +STR_DTLS :Ez a hatalmas elhagyatott felhőkarcoló egyedi lehetőség egy vidámpark-fejlesztő számára + + +STR_SCNR :Terrorváros +STR_PARK :Terrorváros +STR_DTLS :Ez a városi terület csak a tiéd, hogy vidámparkká fejleszd + + +STR_SCNR :Megavilág Park +STR_PARK :Megavilág Park +STR_DTLS :Van mit javítani ezen az óriási, játékokkal teli parkon + + +STR_SCNR :Vénuszi Tavak +STR_PARK :Vénuszi Tavak +STR_DTLS :Alakítsd át egy messzi bolygó földterületét vidámparkká + + +STR_SCNR :Mikro Park +STR_PARK :Mikro Park +STR_DTLS :Próbáld meg létrehozni a világ legkisebb nyereséges parkját ## Real Parks from RCT1 # None of them had details diff --git a/data/language/it-IT.txt b/data/language/it-IT.txt index 49a6a26494..714f33884d 100644 --- a/data/language/it-IT.txt +++ b/data/language/it-IT.txt @@ -477,7 +477,7 @@ STR_1091 :Modalità spettacolo circense STR_1092 :Lancio in discesa STR_1093 :Modalità casa distorta STR_1094 :Modalità caduta libera -STR_1095 :Mod. circuito continuo sezi. a blocchi +STR_1095 :Mod. circuito continuo sez. a blocchi STR_1096 :Lancio a motore (without passing station) STR_1097 :Mod. lancio a motore sez. a blocchi STR_1098 :In viaggio verso la fine della {POP16}{STRINGID} @@ -553,8 +553,8 @@ STR_1167 :Impossibile alzare il livello dell'acqua in questo punto... STR_1168 :Opzioni STR_1169 :(Nessuno) STR_1170 :{STRING} -STR_1171 :{RED}Chiuso - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Chiuso +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Costruisci sentieri e corsie d'attesa STR_1174 :Un'insegna è d'intralcio STR_1175 :Impossibile costruirlo su un sentiero in pendenza @@ -1110,8 +1110,8 @@ STR_1726 :Il terreno non è in vendita! STR_1727 :I diritti di costruzione non sono in vendita! STR_1728 :Impossibile comprare i diritti di costruzione qui... STR_1729 :Questo terreno non è di proprietà del parco! -STR_1730 :{RED}Chiuso - - -STR_1731 :{WHITE}{STRINGID} - - +STR_1730 :{RED}Chiuso +STR_1731 :{WHITE}{STRINGID} STR_1732 :Costruisci STR_1733 :Modalità STR_1734 :{WINDOW_COLOUR_2}Numero di giri: @@ -2018,12 +2018,12 @@ STR_2677 :??? STR_2678 :??? STR_2679 :??? STR_2680 :Tutte le ricerche sono completate -STR_2681 :{MEDIUMFONT}{BLACK}Increases your money by {CURRENCY} +STR_2681 :{MEDIUMFONT}{BLACK}Incrementa il tuo denaro di {CURRENCY} STR_2684 :{SMALLFONT}{BLACK}Arriva un largo numero di visitatori STR_2685 :Simplex Noise Parameters STR_2686 :Basso: STR_2687 :Alto: -STR_2688 :Base Frequency: +STR_2688 :Frequenza Base: STR_2689 :Ottavi: STR_2690 :Generazione Mappa STR_2691 :Base height: @@ -2239,7 +2239,7 @@ STR_2977 :Nome del dipendente STR_2978 :Digita il nome di questo dipendente: STR_2979 :Impossibile battezzare il dipendente... STR_2980 :Troppe insegne nella partita -STR_2981 :{RED}Vietato l'ingresso - - +STR_2981 :{RED}Vietato l'ingresso STR_2982 :Testo dell'insegna STR_2983 :Digita il testo di questa insegna: STR_2984 :Impossibile fissare il testo dell'insegna... @@ -2418,7 +2418,6 @@ STR_3190 :Percorsi (extra) STR_3191 :Gruppi di scenari STR_3192 :Entrata del parco STR_3193 :Acqua -STR_3194 :Descrizione dello scenario STR_3195 :Lista delle invenzioni STR_3196 :{WINDOW_COLOUR_2}Gruppo di ricerca: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Oggetti già inventati all'avvio della partita @@ -2553,7 +2552,7 @@ STR_3328 :Impossibile passare alla fase successiva dell'editor... STR_3329 :L'entrata del parco non è stata ancora costruita STR_3330 :Il parco deve possedere del terreno STR_3331 :Il percorso dall'entrata del parco al bordo della mappa non è completo o è troppo complesso. Dev'essere di larghezza singola con il minor numero possibile di collegamenti e curve -STR_3332 :L'entrata del parco è oriantata nel senso sbagliato o non ha un percorso che conduce al bordo della mappa +STR_3332 :L'entrata del parco è orientata nel senso sbagliato o non ha un percorso che conduce al bordo della mappa STR_3333 :Esporta oggetti di plug-in insieme alle partite salvate STR_3334 :{SMALLFONT}{BLACK}Scegli se salvare gli eventuali dati necessari degli oggetti di plug-in (dati non forniti con il prodotto principale) all'interno dei file di scenario o delle partite salvate, permettendone così l'accesso anche a chi non possiede questi dati addizionali STR_3335 :Editor di percorsi - Selezione tipo percorso e veicoli @@ -2754,11 +2753,11 @@ STR_5251 :Pulsante Opzioni Titolo STR_5252 :Selezione Titolo Scenario STR_5253 :Informazioni Parco STR_5254 :Crea -STR_5255 :{SMALLFONT}{BLACK}Create a new title sequence from scratch -STR_5256 :Create a new theme to make changes to -STR_5257 :{SMALLFONT}{BLACK}Create a new theme based on the current one -STR_5258 :{SMALLFONT}{BLACK}Delete the current theme -STR_5259 :{SMALLFONT}{BLACK}Rename the current theme +STR_5255 :{SMALLFONT}{BLACK} Crea una nuova sequenza di titoli da zero +STR_5256 :Crea un nuovo tema da modificare +STR_5257 :{SMALLFONT}{BLACK}Crea un nuovo tema basato sul tema corrente +STR_5258 :{SMALLFONT}{BLACK}Elimina il tema corrente +STR_5259 :{SMALLFONT}{BLACK}Rinomina il tema corrente STR_5260 :Schermata gigante STR_5261 :Filtro STR_5262 :Wacky Worlds @@ -2771,8 +2770,8 @@ STR_5268 :{SMALLFONT}{BLACK}Audio STR_5269 :{SMALLFONT}{BLACK}Controlli e interfaccia STR_5270 :{SMALLFONT}{BLACK}Varie STR_5271 :{SMALLFONT}{BLACK}Twitch -STR_5272 :{SMALLFONT}{BLACK}Sceneri piccoli -STR_5273 :{SMALLFONT}{BLACK}Sceneri grandi +STR_5272 :{SMALLFONT}{BLACK}Scenari piccoli +STR_5273 :{SMALLFONT}{BLACK}Scenari grandi STR_5274 :{SMALLFONT}{BLACK}Percorso STR_5275 :Cerca per oggetti STR_5276 :Inserisci il nome di un oggetto da cercare @@ -3010,7 +3009,7 @@ STR_5511 :(UNKNOWN) STR_5512 :Salva partita con nome... STR_5513 :Salvataggio veloce STR_5514 :Disattiva vandalismi -STR_5515 :{SMALLFONT}{BLACK}Impedisce ai visitatori di vandalizzare il parco quando sono arrabbiati +STR_5515 :{SMALLFONT}{BLACK}Impedisce ai visitatori del parco di compiere atti vandalici quando sono arrabbiati STR_5516 :{SMALLFONT}{BLACK}Nero STR_5517 :{SMALLFONT}{BLACK}Grigio STR_5518 :{SMALLFONT}{BLACK}Bianco @@ -3226,7 +3225,7 @@ STR_5762 :Yuan cinesi (CN¥) STR_5763 :Tutti i file STR_5764 :Tipo di attrazione non valida STR_5765 :Non è possibile modificare attrazioni non valide -STR_5766 : +STR_5766 :Fiorino Ungherese (Ft) STR_5767 :Fatturato STR_5768 :Clienti totali STR_5769 :Profitto totale @@ -3307,7 +3306,7 @@ STR_5844 :{SMALLFONT}{BLACK}Connettiti al server multigiocatore{NEWLINE}anche STR_5845 :{SMALLFONT}{BLACK}Aggiungi un pulsante{NEWLINE}per debuggare nella barra.{NEWLINE}Abilita le shortcut nellatastiera per la console sviluppatori STR_5846 :{SMALLFONT}{BLACK}Imposta i salvataggi automatici di OpenRCT2 spesso STR_5847 :{SMALLFONT}{BLACK}Seleziona la sequenza del parco utilizzata nei titoli.{NEWLINE}La sequenza del titolo in RCT1/2 richiede l'importazione degli scenari per funzionare -STR_5848 :{SMALLFONT}{BLACK}Crea e gestici sequenze di titoli personalizzate +STR_5848 :{SMALLFONT}{BLACK}Crea e gestisci sequenze di titoli personalizzate STR_5849 :{SMALLFONT}{BLACK}Colloca sulla mappa{NEWLINE}i nuovi membri del personale automaticamente STR_5851 :{SMALLFONT}{BLACK}Imposta l'intervallo predefinito di inspezione{NEWLINE}nelle nuove attrazioni STR_5852 :{SMALLFONT}{BLACK}Imposta il nome del canale TwitchTV che sarà utilizzato per l'integrazione di Twitch @@ -3587,8 +3586,8 @@ STR_6125 :Tipo oggetto STR_6126 :Tipo sconosciuto STR_6127 :File: {STRING} STR_6128 :Non è stato possibile caricare il file perché alcuni riferimenti ad oggetti presenti sono corrotti o mancanti. Una lista di tali oggetti è riportata qui di seguito. -STR_6129 :Copia oggetto selezionato -STR_6130 :Copia l'intera lista di oggetti +STR_6129 :Copia +STR_6130 :Copia tutti STR_6131 :Origine oggetto STR_6132 :Ignora stato di ricerca STR_6133 :{SMALLFONT}{BLACK}Permette di accedere ad attrazioni e scenari non ancora inventati @@ -3736,6 +3735,59 @@ STR_6274 :Impossibile impostare schema colore... STR_6275 :{WINDOW_COLOUR_2}Stile stazione: STR_6276 :{RED}{STRINGID} ha ospiti bloccati, probabilmente a causa di un errato tipo di attrazione o modalità operativa. STR_6277 :{WINDOW_COLOUR_2}Indice stazione: {BLACK}{COMMA16} +STR_6278 :Numero autosalvataggi +STR_6279 :{SMALLFONT}{BLACK}Numero di autosalvataggi da mantenere +STR_6280 :{SMALLFONT}{BLACK}Chat +STR_6281 :{SMALLFONT}{BLACK}Mostra un tasto separato nella barra degli strumenti per la finestra di chat +STR_6282 :Chat +STR_6283 :La chat non è disponibile al momento. Si è connessi a un server? +STR_6284 :Rete +STR_6285 :Informazioni di rete +STR_6286 :Ricevi +STR_6287 :Invia +STR_6288 :Totale ricevuto +STR_6289 :Totale inviato +STR_6290 :Protocollo di base +STR_6291 :Comandi +STR_6292 :Mappa +STR_6293 :B +STR_6294 :KiB +STR_6295 :MiB +STR_6296 :GiB +STR_6297 :TiB +STR_6298 :{STRING}/sec +STR_6299 :Scarica tutti +STR_6300 :{SMALLFONT}{BLACK}Scarica tutti gli oggetti mancanti se sono disponibili online. +STR_6301 :{SMALLFONT}{BLACK}Copia il nome dell'oggetto selezionato negli appunti. +STR_6302 :{SMALLFONT}{BLACK}Copia l'intera lista degli oggetti mancanti negli appunti. +STR_6303 :Scaricamento oggetto ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :Apri selezione scenario +STR_6305 :Multithreading +STR_6306 :{SMALLFONT}{BLACK}Opzione sperimentale per utilizzare più thread per il rendering, potrebbe causare instabilità. +STR_6307 :Schema colore: {BLACK}{STRINGID} +STR_6308 :“{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :Riconnessione +STR_6310 :{WINDOW_COLOUR_2}Posizione: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Successivo: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(surface) +STR_6313 :(slope {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Dest: {BLACK}{INT32}, {INT32} tolerance {INT32} +STR_6315 :{WINDOW_COLOUR_2}Obiettivo Pathfind: {BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6316 :{WINDOW_COLOUR_2}Storico Pathfind: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6318 :Network desync rilevato.{NEWLINE}File log: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Blocco freno chiuso +STR_6320 :{WINDOW_COLOUR_2}Indistruttibile +STR_6321 :{WINDOW_COLOUR_2}L'aggiunta è rotta +STR_6322 :{WINDOW_COLOUR_2}Sprite Id: {BLACK}{INT32} +STR_6323 :Simulazione +STR_6324 :Simula +STR_6325 :{SMALLFONT}{BLACK}Simula attrazione +STR_6326 :Non è stato possibile simulare {POP16}{POP16}{POP16}{STRINGID}... +STR_6327 :Sfondo trasparente per schermata gigante +STR_6328 :{SMALLFONT}{BLACK}Con questa opzione attivata, le schermate giganti avranno uno sfondo trasparente invece di quello nero +STR_6329 :{STRING}{STRINGID} +STR_6330 :Scaricamento [{STRING}] da {STRING} ({COMMA16} / {COMMA16}) ############# # Scenarios # @@ -4053,7 +4105,7 @@ STR_DTLS :Un castello con un paio di montagne russe deve diventare un parco t STR_SCNR :Wacky Warren STR_PARK :Wacky Warren -STR_DTLS :Un parco che ha molti dei suoi sentieri e montange russe sotterranee +STR_DTLS :Un parco che ha molti dei suoi sentieri e montagne russe sotterranee STR_SCNR :Grand Glacier diff --git a/data/language/ko-KR.txt b/data/language/ko-KR.txt index c2fd5052e2..454b27bc52 100644 --- a/data/language/ko-KR.txt +++ b/data/language/ko-KR.txt @@ -46,7 +46,7 @@ STR_0041 :3D 영화관 STR_0042 :톱 스핀 STR_0043 :스페이스 링 STR_0044 :역방향 자유낙하 코스터 -STR_0045 :리프트 +STR_0045 :엘리베이터 STR_0046 :수직 낙하 롤러코스터 STR_0047 :현금 지급기 STR_0048 :트위스트 @@ -73,7 +73,7 @@ STR_0068 :하트라인 트위스터 코스터 STR_0069 :미니 골프 STR_0070 :기가 코스터 STR_0071 :자이로드롭 -STR_0072 :비행 접시 +STR_0072 :비행접시 STR_0073 :비뚤어진 집 STR_0074 :모노레일 사이클 STR_0075 :컴팩트 인버티드 코스터 @@ -95,30 +95,30 @@ STR_0090 :마인 라이드 STR_0091 :알 수 없는 놀이기구 (59) STR_0092 :LIM 발진 롤러코스터 STR_0512 :나선형 리프트 힐과 부드럽게 꼬인 낙하를 가진 알찬 구성의 롤러코스터입니다. -STR_0513 :손님들이 일어선 자세로 탑승하는 루핑 롤러코스터입니다. -STR_0514 :코너를 돌 때 롤러코스터 트랙 아래에 달린 차량이 흔들리는 열차입니다. -STR_0515 :복잡하고 꼬인 트랙 요소를 가진 트랙 아래를 달리는 스틸 롤러코스터 차량입니다. +STR_0513 :일어선 자세로 탑승하는 루핑 롤러코스터입니다. +STR_0514 :코너를 돌 때 롤러코스터 트랙 아래에 매달린 차량이 좌우로 흔들리는 열차입니다. +STR_0515 :트랙 아래에 매달려 복잡하고 꼬인 트랙을 달리는 철제 롤러코스터 차량입니다. STR_0516 :큰 놀이기구를 탈 용기가 없는 사람들을 위한 얌전한 롤러코스터입니다. -STR_0517 :손님들이 협궤 선로를 따라 달리는 모형 열차에 탑승하는 놀이기구입니다. -STR_0518 :손님들이 모노레일 트랙을 따라 달리는 전기 차량에 탑승하는 놀이기구입니다. -STR_0519 :손님들이 단선 트랙 아래에 달려서 코너를 돌 때마다 좌우로 흔들리는 작은 차량에 탑승하는 놀이기구입니다. -STR_0520 :손님들이 물 위를 다니는 배를 운전하거나 또는 저을 수 있는 선착장입니다. -STR_0521 :급커브와 급하강을 가진 빠르게 꼬인 롤러코스터입니다. 격렬도가 높게 나올 것입니다. -STR_0522 :탑승객이 차량 없이 트랙 위에 앉는 작은 롤러코스터입니다. +STR_0517 :협궤 선로를 따라 달리는 모형 열차에 탑승하는 놀이기구입니다. +STR_0518 :모노레일 트랙을 따라 달리는 전동 차량에 탑승하는 놀이기구입니다. +STR_0519 :단선 트랙 아래에 달려서 코너를 돌 때마다 좌우로 흔들리는 작은 차량에 탑승하는 놀이기구입니다. +STR_0520 :노를 젓거나 운전해서 물 위를 다니는 배가 있는 선착장입니다. +STR_0521 :급커브와 급하강 트랙을 빠르게 달리는 롤러코스터입니다. 격렬도가 높게 나올 것입니다. +STR_0522 :탑승객이 주변을 감싸는 차량 없이 트랙 위에 얹혀진 좌석에 앉는 작은 롤러코스터입니다. STR_0523 :정해진 트랙을 따라 천천히 움직이는 놀이기구입니다. -STR_0524 :자유낙하 차량은 공기 압축 방식으로 높은 철제 탑을 향해 차량을 쏘아올려 자유낙하하는 차량입니다. -STR_0525 :손님들이 반원형으로 휘어있고 꼬여있는 트랙을 따라 이동합니다. -STR_0526 :손님들이 높은 곳으로 회전하면서 올라가는 관망대 캐빈에 탑승하는 놀이기구입니다. +STR_0524 :공기 압축 방식으로 높은 철제 탑을 따라 차량을 쏘아 올려 자유낙하하는 놀이기구입니다. +STR_0525 :반원 모양의 트랙을 구불구불 커브를 돌며 내려오는 롤러코스터입니다. +STR_0526 :손님들이 회전하는 전망대 캐빈에 탑승해서 높은 곳을 구경하는 놀이기구입니다. STR_0527 :수직 루프를 사용할 수 있는 부드러운 철제 트랙의 롤러코스터입니다. -STR_0528 :손님들이 공기를 넣은 고무 보트를 타고 반원이나 원형의 튜브 트랙을 따라 이동하는 놀이기구입니다. +STR_0528 :고무보트를 타고 반원이나 원형 모양의 트랙을 따라 미끄러지는 놀이기구입니다. STR_0529 :오래된 기차 선로처럼 보이게 만든 철제 트랙을 따라 움직이는 탄광 열차 테마를 한 롤러코스터 차량입니다. -STR_0530 :철제 케이블에 달린 차량이 연속적으로 놀이기구의 한쪽 끝에서 다른쪽 끝을 왕복 운행합니다. +STR_0530 :철제 케이블에 달린 차량이 연속적으로 놀이기구의 한쪽 끝에서 다른 쪽 끝을 왕복 운행합니다. STR_0531 :차량이 나선 트랙과 루프를 통과하는 알찬 구성의 철제 트랙 롤러코스터입니다. STR_0532 : STR_0533 : STR_0534 :패트롤 엔진으로 움직이는 고 카트입니다. STR_0535 :통나무 모양의 보트가 수로를 따라 이동하고 낙하하면서 탑승객을 쫄딱 젖게 만듭니다. -STR_0536 :원형의 보트가 수로를 따라 요동치며, 폭포를 통과하며 물을 튀기고 탑승객들에게 거품 소용돌이를 통과하며 스릴을 선사합니다. +STR_0536 :원형의 보트가 수로를 따라 요동치면서, 폭포를 통과하고 물을 튀기며 거품 소용돌이를 통과하여 탑승객들에게 스릴을 선사합니다. STR_0537 : STR_0538 : STR_0539 : @@ -135,7 +135,7 @@ STR_0552 : STR_0553 : STR_0554 :차량이 선형 유도 모터에 의해 긴 평지 트랙을 따라 가속된 뒤, 수직 상승 트랙에 의해 위로 상승한 뒤 다시 뒤로 자유낙하하여 탑승장에 돌아옵니다. STR_0555 :손님은 엘리베이터에 탑승하여 한쪽에서 다른쪽으로 이동할 수 있습니다. -STR_0556 :폭이 넓은 차량이 수직으로 기울어진 트랙을 따라 내려오면서 극강의 자유 낙하 롤러코스터를 경험하게 해줍니다. +STR_0556 :폭이 넓은 차량이 수직으로 기울어진 트랙을 따라 내려오면서 최고의 자유 낙하를 경험할 수 있습니다. STR_0557 : STR_0558 : STR_0559 : @@ -144,27 +144,27 @@ STR_0561 : STR_0562 :무서운 풍경과 특수 효과를 통과하며 여러 높이의 트랙을 따라 이동하는 전동 차량입니다. STR_0563 :탑승객들은 한 바퀴를 운행하는 편안한 좌석에 앉아 언덕을 오르면서 느낄 수 있는 '무중력 시간'뿐만이 아니라 크고 부드러운 낙하와 꼬인 트랙의 느낌을 즐길 수 있습니다. STR_0564 :목재 트랙 위를 움직이는 이 롤러코스터는 빠르고, 거칠고, 시끄럽고, 많은 '무중력 시간'과 함께 '제어 불가능할 것 같은' 탑승감을 선사합니다. -STR_0565 :완만한 경사와 커브만을 가진 간단한 목재 롤러코스터로, 이 롤러코스터의 차량은 오직 측면 마찰륜과 중력만으로 트랙 위에 얹혀진 상태로 운행합니다. -STR_0566 :개별적인 롤러코스터 차량이 급커브와 짧고 급한 경사를 가진 지그재그 모양의 트랙을 어지럽게 돌아다닙니다. +STR_0565 :완만한 경사와 커브만을 가진 간단한 나무 롤러코스터로, 이 롤러코스터의 차량은 트랙 위에 얹힌 상태에서 오직 측면 마찰용 바퀴와 중력에 의해 운행됩니다. +STR_0566 :한 대의 롤러코스터 차량이 급커브와 짧고 급한 경사를 가진 지그재그 모양의 트랙을 어지럽게 돌아다닙니다. STR_0567 :트랙 반대편에 매달린 좌석에 앉아서, 탑승객들은 급강하와 여러 가지 반전 트랙을 통과하면서 거꾸러지는 동안 머리와 다리가 뒤집히게 됩니다. -STR_0569 :차량 밑에 달린 특별한 마차에 탑승하여, 탑승객들은 공중으로 급강하면서 하늘을 나는 기분을 느끼게 만듭니다. +STR_0569 :트랙에 매달린 특별한 차량에 탑승하여 공중으로 급강하면서, 하늘을 나는 기분을 느낄 수 있습니다. STR_0571 :원형의 차량이 나무 트랙을 지그재그로 이동하면서 회전하는 놀이기구입니다. STR_0572 :대형 보트가 넓은 수로를 따라 컨베이어 벨트에 의해 언덕을 올라간 뒤, 급강하하면서 큰 물보라를 일으켜 탑승객을 젖게 만듭니다. -STR_0573 :탑승객이 직접 페달을 밟아 이동하는, 철제 트랙을 달리는 헬리콥터 모양의 전동 차량입니다. +STR_0573 :탑승객이 직접 페달을 밟아 철제 트랙을 따라 이동하는 헬리콥터 모양의 전동 차량입니다. STR_0574 :탑승객은 특수한 마차에 누운 자세로 매달려, 이리 저리 꼬이고 뒤집히는 트랙을 땅을 등지거나 마주보는 자세로 탑승하게 됩니다. STR_0575 :공원 여기 저기로 사람들을 실어나르는, 단선 레일에 매달린 전동 열차입니다. -STR_0577 :특수한 반전 섹션에서 앞뒤가 바뀌는 목재 트랙을 달리는 보우기 차입니다 +STR_0577 :특수한 반전 섹션에서 앞뒤가 바뀌는 나무 트랙을 달리는 보우기 차입니다 STR_0578 :원형 고리로 둘러싸여 급강하와 하트라인 트위스트를 횡단하는 트랙을 달리는 차량입니다. STR_0579 : -STR_0580 :90m 이상의 언덕을 부드럽게 오르내리는 거대한 철제 롤러코스터입니다. +STR_0580 :90m 이상의 높이를 부드럽게 오르내리는 거대한 철제 롤러코스터입니다. STR_0581 :원형 모양의 좌석이 맨 위까지 끌어올려진 다음, 자유 낙하하며, 자기장에 의해 부드럽게 바닥에 정지합니다. STR_0582 : STR_0583 : -STR_0584 :탑승객이 스스로 페달을 밟아 이동해야 하는, 특별한 철제 모노레일 트랙 위를 이동하는 자전거입니다. +STR_0584 :특별한 철제 모노레일 트랙 위에서 탑승객이 스스로 페달을 밟아 이동하는 자전거입니다. STR_0585 :탑승객은 트랙 밑에 매달린 좌석 한 쌍에 앉아 루프와 트위스트를 급격히 통과합니다. STR_0586 :보트 모양의 차량이 꼬인 커브와 급강하가 가능한 롤러코스터 트랙을 달리며, 강 섹션의 물에서는 물보라를 일으키며 감속합니다. -STR_0587 :아주 짜릿한 공기 압축 방식으로 발진하여 차량이 수직 트랙을 향해 속력을 높인 뒤, 꼭대기에 다다르면 다시 수직으로 하강하며 다시 탑승장에 도착합니다. -STR_0588 :개별적인 차량이 헤이펀 커브와 급강하를 포함한 지그재그 모양의 트랙 아래를 이동합니다. +STR_0587 :공기 압축 방식으로 아주 짜릿하게 발진하여 차량이 수직 트랙을 향해 속력을 높인 뒤, 꼭대기에서 다시 수직으로 하강하며 다시 탑승장에 도착합니다. +STR_0588 :한 대의 차량이 트랙에 매달려서 헤어핀 커브와 급강하 트랙을 지그재그 모양으로 달립니다. STR_0589 : STR_0590 :탑승객들은 잠수가 가능한 잠수함을 타고 수중 코스를 돕니다. STR_0591 :뗏목 모양의 보트가 강 모양의 트랙을 따라 얌전히 이동합니다. @@ -262,8 +262,8 @@ STR_0875 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP STR_0876 :{BLACK}▼ STR_0877 :너무 낮습니다! STR_0878 :너무 높습니다! -STR_0879 :여기를 더 이상 내릴 수 없습니다... -STR_0880 :여기를 더 이상 올릴 수 없습니다... +STR_0879 :여기를 더 내릴 수 없습니다... +STR_0880 :여기를 더 올릴 수 없습니다... STR_0881 :오브젝트가 있습니다 STR_0882 :게임 불러오기 STR_0883 :게임 저장하기 @@ -361,7 +361,7 @@ STR_0976 :화장실 및 안내소 STR_0977 :새로운 운송용 놀이기구 STR_0978 :새로운 얌전한 놀이기구 STR_0979 :새로운 롤러코스터 -STR_0980 :새로운 스릴있는 놀이기구 +STR_0980 :새로운 스릴 있는 놀이기구 STR_0981 :새로운 물 놀이기구 STR_0982 :새로운 상점·매점 STR_0983 :연구 & 개발 @@ -552,9 +552,9 @@ STR_1167 :이곳의 물을 올릴 수 없습니다... STR_1168 :설정 STR_1169 :(없음) STR_1170 :{STRING} -STR_1171 :{RED}닫힘 - - -STR_1172 :{YELLOW}{STRINGID} - - -STR_1173 :{SMALLFONT}{BLACK}보도와 대기줄을 만듭니다 +STR_1171 :{RED}닫힘 +STR_1172 :{YELLOW}{STRINGID} +STR_1173 :{SMALLFONT}{BLACK}보도와 대기 줄을 만듭니다 STR_1174 :전광판 사인이 있습니다 STR_1175 :경사진 보도 위에 건설할 수 없습니다 STR_1176 :보도를 여기에 건설할 수 없습니다... @@ -607,92 +607,92 @@ STR_1222 :출구 없음 STR_1223 :{SMALLFONT}{BLACK}운송용 놀이기구 STR_1224 :{SMALLFONT}{BLACK}얌전한 놀이기구 STR_1225 :{SMALLFONT}{BLACK}롤러코스터 -STR_1226 :{SMALLFONT}{BLACK}스릴있는 놀이기구 +STR_1226 :{SMALLFONT}{BLACK}스릴 있는 놀이기구 STR_1227 :{SMALLFONT}{BLACK}물 놀이기구 STR_1228 :{SMALLFONT}{BLACK}상점 & 매점 STR_1229 :열차 STR_1230 :열차 STR_1231 :열차 STR_1232 :열차 -STR_1233 :{COMMA16} 열차 -STR_1234 :{COMMA16} 열차 +STR_1233 :열차 {COMMA16}대 +STR_1234 :열차 {COMMA16}대 STR_1235 :열차 {COMMA16} STR_1236 :보트 STR_1237 :보트 STR_1238 :보트 STR_1239 :보트 -STR_1240 :{COMMA16} 보트 -STR_1241 :{COMMA16} 보트 +STR_1240 :보트 {COMMA16}대 +STR_1241 :보트 {COMMA16}대 STR_1242 :보트 {COMMA16} STR_1243 :트랙 STR_1244 :트랙 STR_1245 :트랙 STR_1246 :트랙 -STR_1247 :{COMMA16} 트랙 -STR_1248 :{COMMA16} 트랙 +STR_1247 :트랙 {COMMA16}개 +STR_1248 :트랙 {COMMA16}개 STR_1249 :트랙 {COMMA16} STR_1250 :도킹 승강장 STR_1251 :도킹 승강장 STR_1252 :도킹 승강장 STR_1253 :도킹 승강장 -STR_1254 :{COMMA16} 도킹 승강장 -STR_1255 :{COMMA16} 도킹 승강장 +STR_1254 :도킹 승강장 {COMMA16}개 +STR_1255 :도킹 승강장 {COMMA16}개 STR_1256 :도킹 승강장 {COMMA16} STR_1257 :탑승장 STR_1258 :탑승장 STR_1259 :탑승장 STR_1260 :탑승장 -STR_1261 :{COMMA16} 탑승장 -STR_1262 :{COMMA16} 탑승장 +STR_1261 :탑승장 {COMMA16}개 +STR_1262 :탑승장 {COMMA16}개 STR_1263 :탑승장 {COMMA16} -STR_1264 :차 -STR_1265 :차 -STR_1266 :차 -STR_1267 :차 -STR_1268 :{COMMA16} 차 -STR_1269 :{COMMA16} 차 +STR_1264 :대의 차량 +STR_1265 :대의 차량 +STR_1266 :차3 +STR_1267 :차4 +STR_1268 :차량 {COMMA16}대 +STR_1269 :차량 {COMMA16}대 STR_1270 :차 {COMMA16} STR_1271 :건물 STR_1272 :건물 STR_1273 :건물 STR_1274 :건물 -STR_1275 :{COMMA16} 건물 -STR_1276 :{COMMA16} 건물 +STR_1275 :건물 {COMMA16}개 +STR_1276 :건물 {COMMA16}개 STR_1277 :건물 {COMMA16} STR_1278 :구조물 STR_1279 :구조물 STR_1280 :구조물 STR_1281 :구조물 -STR_1282 :{COMMA16} 구조물 -STR_1283 :{COMMA16} 구조물 +STR_1282 :구조물 {COMMA16}개 +STR_1283 :구조물 {COMMA16}개 STR_1284 :구조물 {COMMA16} STR_1285 :배 STR_1286 :배 STR_1287 :배 STR_1288 :배 -STR_1289 :{COMMA16} 배 -STR_1290 :{COMMA16} 배 +STR_1289 :배 {COMMA16}대 +STR_1290 :배 {COMMA16}대 STR_1291 :배 {COMMA16} STR_1292 :객실 STR_1293 :객실 STR_1294 :객실 STR_1295 :객실 -STR_1296 :{COMMA16} 객실 -STR_1297 :{COMMA16} 객실 +STR_1296 :객실 {COMMA16}개 +STR_1297 :객실 {COMMA16}개 STR_1298 :객실 {COMMA16} STR_1299 :휠 STR_1300 :휠 STR_1301 :휠 STR_1302 :휠 -STR_1303 :{COMMA16} 휠 -STR_1304 :{COMMA16} 휠 +STR_1303 :휠 {COMMA16}개 +STR_1304 :휠 {COMMA16}개 STR_1305 :휠 {COMMA16} STR_1306 :링 STR_1307 :링 STR_1308 :링 STR_1309 :링 -STR_1310 :{COMMA16} 링 -STR_1311 :{COMMA16} 링 +STR_1310 :링 {COMMA16}개 +STR_1311 :링 {COMMA16}개 STR_1312 :링 {COMMA16} STR_1313 :플레이어 STR_1314 :플레이어 @@ -745,7 +745,7 @@ STR_1360 :{WINDOW_COLOUR_2}대기 시간: {BLACK}{COMMA16}분 STR_1361 :속력을 바꿀 수 없습니다... STR_1362 :발진 속도를 바꿀 수 없습니다... STR_1363 :지지대가 버틸 수 있는 최대 높이입니다! -STR_1364 :위 트랙의 지지대를 더 이상 늘릴 수 없습니다! +STR_1364 :위 트랙의 지지대를 더 늘릴 수 없습니다! STR_1365 :인라인 트위스트 (왼쪽) STR_1366 :인라인 트위스트 (오른쪽) STR_1367 :1/2 루프 @@ -804,10 +804,10 @@ STR_1419 :{SMALLFONT}{BLACK}{VELOCITY} STR_1420 :{SMALLFONT}{BLACK}{LENGTH} STR_1421 :{SMALLFONT}{BLACK}{COMMA16}g STR_1422 :{SMALLFONT}{BLACK}{POP16}{STRINGID}을 기준으로 데이터 기록 중 -STR_1423 :{SMALLFONT}{BLACK}대기줄 +STR_1423 :{SMALLFONT}{BLACK}대기 줄 STR_1424 :{SMALLFONT}{BLACK}보도 STR_1425 :보도 -STR_1426 :대기줄 +STR_1426 :대기 줄 STR_1427 :{WINDOW_COLOUR_2}손님: {BLACK}{COMMA32}명/시간 STR_1428 :{WINDOW_COLOUR_2}입장료: STR_1429 :{POP16}{POP16}{POP16}{CURRENCY2DP} @@ -816,7 +816,7 @@ STR_1431 :걷는 중 STR_1432 :목적지 : {STRINGID} STR_1433 :{STRINGID} 대기 중 STR_1434 :물에 빠짐 -STR_1435 :{STRINGID} 탑승중 +STR_1435 :{STRINGID} 탑승 중 STR_1436 :{STRINGID} 안에 STR_1437 :{STRINGID}에 STR_1438 :앉음 @@ -865,7 +865,7 @@ STR_1480 :{SMALLFONT}“{STRINGID}에 쓸 돈은 없어” STR_1481 :{SMALLFONT}“내 돈을 전부 다 써버렸어” STR_1482 :{SMALLFONT}“속이 메스꺼워” STR_1483 :{SMALLFONT}“속이 정말 메스꺼워” -STR_1484 :{SMALLFONT}“{STRINGID}보다 더 스릴있는 걸 타고 싶어” +STR_1484 :{SMALLFONT}“{STRINGID}보다 더 스릴 있는 걸 타고 싶어” STR_1485 :{SMALLFONT}“{STRINGID}(은)는 내가 타기엔 너무 격렬해 보여” STR_1486 :{SMALLFONT}“아직 {STRINGID}(을)를 다 먹지 않았어” STR_1487 :{SMALLFONT}“{STRINGID}(은)는 바라만 봐도 속이 메스꺼워” @@ -879,7 +879,7 @@ STR_1494 :{SMALLFONT}“목마르지 않아” STR_1495 :{SMALLFONT}“살려주세요! 물에 빠졌어요!” STR_1496 :{SMALLFONT}“길을 잃어버렸어!” STR_1497 :{SMALLFONT}“{STRINGID}(은)는 정말 훌륭했어” -STR_1498 :{SMALLFONT}“{STRINGID}(을)를 타려고 한참동안이나 기다렸어” +STR_1498 :{SMALLFONT}“{STRINGID}(을)를 타려고 한참이나 기다렸어” STR_1499 :{SMALLFONT}“피곤해” STR_1500 :{SMALLFONT}“배고파” STR_1501 :{SMALLFONT}“목말라” @@ -906,7 +906,7 @@ STR_1521 :{SMALLFONT}“{STRINGID}에서 산 탑승 사진은 가격이 참 STR_1522 :{SMALLFONT}“{STRINGID}에서 산 우산은 가격이 참 적당한 것 같아” STR_1523 :{SMALLFONT}“{STRINGID}에서 산 음료수는 가격이 참 적당한 것 같아” STR_1524 :{SMALLFONT}“{STRINGID}에서 산 햄버거는 가격이 참 적당한 것 같아” -STR_1525 :{SMALLFONT}“{STRINGID}에서 산 감자칩은 가격이 참 적당한 것 같아” +STR_1525 :{SMALLFONT}“{STRINGID}에서 산 감자 칩은 가격이 참 적당한 것 같아” STR_1526 :{SMALLFONT}“{STRINGID}에서 산 아이스크림은 가격이 참 적당한 것 같아” STR_1527 :{SMALLFONT}“{STRINGID}에서 산 솜사탕은 가격이 참 적당한 것 같아” STR_1528 : @@ -923,7 +923,7 @@ STR_1538 :{SMALLFONT}“{STRINGID}에서 산 티셔츠는 가격이 참 적 STR_1539 :{SMALLFONT}“{STRINGID}에서 산 도넛은 가격이 참 적당한 것 같아” STR_1540 :{SMALLFONT}“{STRINGID}에서 산 커피는 가격이 참 적당한 것 같아” STR_1541 : -STR_1542 :{SMALLFONT}“{STRINGID}에서 산 프라이드 치킨은 가격이 참 적당한 것 같아” +STR_1542 :{SMALLFONT}“{STRINGID}에서 산 프라이드치킨은 가격이 참 적당한 것 같아” STR_1543 :{SMALLFONT}“{STRINGID}에서 산 레모네이드는 가격이 참 적당한 것 같아” STR_1544 : STR_1545 : @@ -940,7 +940,7 @@ STR_1555 :{SMALLFONT}“{STRINGID}의 탑승 사진에 돈을 쓰고 싶지 STR_1556 :{SMALLFONT}“{STRINGID}의 우산에 돈을 쓰고 싶지 않아” STR_1557 :{SMALLFONT}“{STRINGID}의 음료수에 돈을 쓰고 싶지 않아” STR_1558 :{SMALLFONT}“{STRINGID}의 햄버거에 돈을 쓰고 싶지 않아” -STR_1559 :{SMALLFONT}“{STRINGID}의 감자칩에 돈을 쓰고 싶지 않아” +STR_1559 :{SMALLFONT}“{STRINGID}의 감자 칩에 돈을 쓰고 싶지 않아” STR_1560 :{SMALLFONT}“{STRINGID}의 아이스크립에 돈을 쓰고 싶지 않아” STR_1561 :{SMALLFONT}“{STRINGID}의 솜사탕에 돈을 쓰고 싶지 않아” STR_1562 : @@ -957,7 +957,7 @@ STR_1572 :{SMALLFONT}“{STRINGID}의 티셔츠에 돈을 쓰고 싶지 않 STR_1573 :{SMALLFONT}“{STRINGID}의 도넛에 돈을 쓰고 싶지 않아” STR_1574 :{SMALLFONT}“{STRINGID}의 커피에 돈을 쓰고 싶지 않아” STR_1575 : -STR_1576 :{SMALLFONT}“{STRINGID}의 프라이드 치킨에 돈을 쓰고 싶지 않아” +STR_1576 :{SMALLFONT}“{STRINGID}의 프라이드치킨에 돈을 쓰고 싶지 않아” STR_1577 :{SMALLFONT}“{STRINGID}의 레모네이드에 돈을 쓰고 싶지 않아” STR_1578 : STR_1579 : @@ -969,14 +969,14 @@ STR_1584 :{SMALLFONT}“{STRINGID}에서 산 탑승 사진은 가격이 참 STR_1585 :{SMALLFONT}“{STRINGID}에서 산 탑승 사진은 가격이 참 적당한 것 같아” STR_1586 :{SMALLFONT}“{STRINGID}에서 산 탑승 사진은 가격이 참 적당한 것 같아” STR_1587 :{SMALLFONT}“{STRINGID}에서 산 프레첼은 가격이 참 적당한 것 같아” -STR_1588 :{SMALLFONT}“{STRINGID}에서 산 핫 초코는 가격이 참 적당한 것 같아” +STR_1588 :{SMALLFONT}“{STRINGID}에서 산 핫초코는 가격이 참 적당한 것 같아” STR_1589 :{SMALLFONT}“{STRINGID}에서 산 아이스 티는 가격이 참 적당한 것 같아” STR_1590 :{SMALLFONT}“{STRINGID}에서 산 퍼넬 케이크는 가격이 참 적당한 것 같아” STR_1591 :{SMALLFONT}“{STRINGID}에서 산 선글라스는 가격이 참 적당한 것 같아” -STR_1592 :{SMALLFONT}“{STRINGID}에서 산 고기국수는 가격이 참 적당한 것 같아” -STR_1593 :{SMALLFONT}“{STRINGID}에서 산 볶음쌀국수은 가격이 참 적당한 것 같아” +STR_1592 :{SMALLFONT}“{STRINGID}에서 산 고기 국수는 가격이 참 적당한 것 같아” +STR_1593 :{SMALLFONT}“{STRINGID}에서 산 볶음 쌀국수은 가격이 참 적당한 것 같아” STR_1594 :{SMALLFONT}“{STRINGID}에서 산 만둣국은 가격이 참 적당한 것 같아” -STR_1595 :{SMALLFONT}“{STRINGID}에서 산 미트볼 스프는 가격이 참 적당한 것 같아” +STR_1595 :{SMALLFONT}“{STRINGID}에서 산 미트볼 수프는 가격이 참 적당한 것 같아” STR_1596 :{SMALLFONT}“{STRINGID}에서 산 과일 주스는 가격이 참 적당한 것 같아” STR_1597 :{SMALLFONT}“{STRINGID}에서 산 두유는 가격이 참 적당한 것 같아” STR_1598 :{SMALLFONT}“{STRINGID}에서 산 수정과는 가격이 참 적당한 것 같아” @@ -1001,14 +1001,14 @@ STR_1616 :{SMALLFONT}“{STRINGID}의 탑승 사진에 돈을 쓰고 싶지 STR_1617 :{SMALLFONT}“{STRINGID}의 탑승 사진에 돈을 쓰고 싶지 않아” STR_1618 :{SMALLFONT}“{STRINGID}의 탑승 사진에 돈을 쓰고 싶지 않아” STR_1619 :{SMALLFONT}“{STRINGID}의 프레첼에 돈을 쓰고 싶지 않아” -STR_1620 :{SMALLFONT}“{STRINGID}의 핫 초코에 돈을 쓰고 싶지 않아” +STR_1620 :{SMALLFONT}“{STRINGID}의 핫초코에 돈을 쓰고 싶지 않아” STR_1621 :{SMALLFONT}“{STRINGID}의 아이스 티에 돈을 쓰고 싶지 않아” STR_1622 :{SMALLFONT}“{STRINGID}의 퍼넬 케이크에 돈을 쓰고 싶지 않아” STR_1623 :{SMALLFONT}“{STRINGID}의 선글라스에 돈을 쓰고 싶지 않아” STR_1624 :{SMALLFONT}“{STRINGID}의 고기 국수에 돈을 쓰고 싶지 않아” -STR_1625 :{SMALLFONT}“{STRINGID}의 볶음쌀국수에 돈을 쓰고 싶지 않아” +STR_1625 :{SMALLFONT}“{STRINGID}의 볶음 쌀국수에 돈을 쓰고 싶지 않아” STR_1626 :{SMALLFONT}“{STRINGID}의 만둣국에 돈을 쓰고 싶지 않아” -STR_1627 :{SMALLFONT}“{STRINGID}의 미트볼 스프에 돈을 쓰고 싶지 않아” +STR_1627 :{SMALLFONT}“{STRINGID}의 미트볼 수프에 돈을 쓰고 싶지 않아” STR_1628 :{SMALLFONT}“{STRINGID}의 과일 주스에 돈을 쓰고 싶지 않아” STR_1629 :{SMALLFONT}“{STRINGID}의 두유에 돈을 쓰고 싶지 않아” STR_1630 :{SMALLFONT}“{STRINGID}의 수정과에 돈을 쓰고 싶지 않아” @@ -1077,8 +1077,8 @@ STR_1693 :{SMALLFONT}{BLACK}손님 STR_1694 :{SMALLFONT}{BLACK}직원 STR_1695 :{SMALLFONT}{BLACK}수익 및 지출 STR_1696 :{SMALLFONT}{BLACK}고객 정보 -STR_1697 :대기줄에 놓을 수 없습니다 -STR_1698 :대기줄에만 놓을 수 있습니다 +STR_1697 :대기 줄에 놓을 수 없습니다 +STR_1698 :대기 줄에만 놓을 수 있습니다 STR_1699 :게임에 사람이 너무 많습니다 STR_1700 :새 미화원 고용 STR_1701 :새 정비기술자 고용 @@ -1105,12 +1105,12 @@ STR_1721 :공원 닫힘 STR_1722 :공원 열림 STR_1723 :공원을 열 수 없습니다... STR_1724 :공원을 닫을 수 없습니다... -STR_1725 :땅을 구입할 수 없습니다... +STR_1725 :땅을 살 수 없습니다... STR_1726 :판매 중인 땅이 아닙니다! STR_1727 :판매 중인 건설권이 아닙니다! -STR_1728 :이 곳의 건설권을 구입할 수 없습니다... +STR_1728 :이곳의 건설권을 살 수 없습니다... STR_1729 :공원 소유의 땅이 아닙니다! -STR_1730 :{RED}닫힘 - - +STR_1730 :{RED}닫힘 STR_1731 :{WHITE}{STRINGID} - - STR_1732 :건설 STR_1733 :모드 @@ -1153,7 +1153,7 @@ STR_1773 :한 놀이기구에는 하나의 탑승 사진 섹션만 만들 수 STR_1774 :한 놀이기구에는 하나의 케이블 리프트만 만들 수 있습니다 STR_1777 :놀이기구 음악 STR_1778 :{STRINGID} - - -STR_1779 :{INLINE_SPRITE}{254}{19}{00}{00} 팬더 복장 +STR_1779 :{INLINE_SPRITE}{254}{19}{00}{00} 판다 복장 STR_1780 :{INLINE_SPRITE}{255}{19}{00}{00} 호랑이 복장 STR_1781 :{INLINE_SPRITE}{00}{20}{00}{00} 코끼리 복장 STR_1782 :{INLINE_SPRITE}{01}{20}{00}{00} 로마 전사 복장 @@ -1208,9 +1208,9 @@ STR_1831 :대기 시간 STR_1832 :신뢰도 STR_1833 :고장률 STR_1834 :좋아하는 손님 수 -STR_1835 :인기도 : 알 수 없음 +STR_1835 :인기도: 알 수 없음 STR_1836 :인기도: {COMMA16}% -STR_1837 :만족도: 알 수 없움 +STR_1837 :만족도: 알 수 없음 STR_1838 :만족도: {COMMA16}% STR_1839 :신뢰도: {COMMA16}% STR_1840 :고장률: {COMMA16}% @@ -1290,7 +1290,7 @@ STR_1914 :{BLACK}{CURRENCY2DP} STR_1915 :{RED}{CURRENCY2DP} STR_1916 :{WINDOW_COLOUR_2}대출: STR_1917 :{POP16}{POP16}{POP16}{CURRENCY} -STR_1918 :더 이상 돈을 빌릴 수 없습니다! +STR_1918 :돈을 더 빌릴 수 없습니다! STR_1919 :돈이 충분하지 않습니다! STR_1920 :대출을 갚을 수 없습니다! STR_1921 :{SMALLFONT}{BLACK}새로운 게임을 시작합니다 @@ -1302,7 +1302,7 @@ STR_1927 :{YELLOW}{STRINGID} 고장 발생 STR_1928 :{RED}{STRINGID} 충돌! STR_1929 :{RED}{STRINGID} - 아직 수리되지 않음{NEWLINE}정비기술자의 위치와 업무 효율을 점검하세요 STR_1930 :{SMALLFONT}{BLACK}이 손님의 행동을 추적합니다.{NEWLINE}(이 설정을 켜면 이 손님의 모든 행동을 메시지 창으로 알려줍니다) -STR_1931 :{STRINGID} - {STRINGID}(을)를 타기 위해 대기줄에 섰습니다 +STR_1931 :{STRINGID} - {STRINGID}(을)를 타기 위해 대기 줄에 섰습니다 STR_1932 :{STRINGID} - {STRINGID}(을)를 탔습니다 STR_1933 :{STRINGID} - {STRINGID} 안에 있습니다 STR_1934 :{STRINGID} - {STRINGID}(을)를 떠났습니다 @@ -1338,7 +1338,7 @@ STR_1963 :{WINDOW_COLOUR_2}탑승 사진 가격: STR_1964 :{WINDOW_COLOUR_2}우산 가격: STR_1965 :{WINDOW_COLOUR_2}음료수 가격: STR_1966 :{WINDOW_COLOUR_2}햄버거 가격: -STR_1967 :{WINDOW_COLOUR_2}감자칩 가격: +STR_1967 :{WINDOW_COLOUR_2}감자 칩 가격: STR_1968 :{WINDOW_COLOUR_2}아이스크림 가격: STR_1969 :{WINDOW_COLOUR_2}솜사탕 가격: STR_1970 :{WINDOW_COLOUR_2} @@ -1366,7 +1366,7 @@ STR_1991 :탑승 사진 STR_1992 :우산 STR_1993 :음료수 STR_1994 :햄버거 -STR_1995 :감자칩 +STR_1995 :감자 칩 STR_1996 :아이스크림 STR_1997 :솜사탕 STR_1998 :빈 캔 @@ -1394,7 +1394,7 @@ STR_2019 :탑승 사진 STR_2020 :우산 STR_2021 :음료수 STR_2022 :햄버거 -STR_2023 :감자칩 +STR_2023 :감자 칩 STR_2024 :아이스크림 STR_2025 :솜사탕 STR_2026 :빈 캔 @@ -1422,7 +1422,7 @@ STR_2047 :탑승 사진 STR_2048 :우산 STR_2049 :음료수 STR_2050 :햄버거 -STR_2051 :감자칩 +STR_2051 :감자 칩 STR_2052 :아이스크림 STR_2053 :솜사탕 STR_2054 :빈 캔 @@ -1450,7 +1450,7 @@ STR_2075 :{STRINGID} 탑승 사진 STR_2076 :“{STRINGID}” 우산 STR_2077 :음료수 STR_2078 :햄버거 -STR_2079 :감자칩 +STR_2079 :감자 칩 STR_2080 :아이스크림 STR_2081 :솜사탕 STR_2082 :빈 캔 @@ -1477,9 +1477,9 @@ STR_2105 :{WINDOW_COLOUR_2}아이스 티 가격: STR_2106 :{WINDOW_COLOUR_2}퍼넬 케이크 가격: STR_2107 :{WINDOW_COLOUR_2}선글라스 가격: STR_2108 :{WINDOW_COLOUR_2}고기 국수 가격: -STR_2109 :{WINDOW_COLOUR_2}볶음쌀국수 가격: +STR_2109 :{WINDOW_COLOUR_2}볶음 쌀국수 가격: STR_2110 :{WINDOW_COLOUR_2}만둣국 가격: -STR_2111 :{WINDOW_COLOUR_2}미트볼 스프 가격: +STR_2111 :{WINDOW_COLOUR_2}미트볼 수프 가격: STR_2112 :{WINDOW_COLOUR_2}과일 주스 가격: STR_2113 :{WINDOW_COLOUR_2}두유 가격: STR_2114 :{WINDOW_COLOUR_2}수정과 가격: @@ -1496,16 +1496,16 @@ STR_2127 :아이스 티 STR_2128 :퍼넬 케이크 STR_2129 :선글라스 STR_2130 :고기 국수 -STR_2131 :볶음쌀국수 +STR_2131 :볶음 쌀국수 STR_2132 :만둣국 -STR_2133 :미트볼 스프 +STR_2133 :미트볼 수프 STR_2134 :과일 주스 STR_2135 :두유 STR_2136 :수정과 STR_2137 :샌드위치 STR_2138 :쿠키 STR_2139 :빈 사발 -STR_2140 :빈 음료수 통 +STR_2140 :빈 음료수 캔 STR_2141 :빈 주스 컵 STR_2142 :구운 소시지 STR_2143 :빈 사발 @@ -1515,9 +1515,9 @@ STR_2149 :아이스 티 STR_2150 :퍼넬 케이크 STR_2151 :선글라스 STR_2152 :고기 국수 -STR_2153 :볶음쌀국수 +STR_2153 :볶음 쌀국수 STR_2154 :만둣국 -STR_2155 :미트볼 스프 +STR_2155 :미트볼 수프 STR_2156 :과일 주스 STR_2157 :두유 STR_2158 :수정과 @@ -1534,9 +1534,9 @@ STR_2171 :아이스 티 STR_2172 :퍼넬 케이크 STR_2173 :선글라스 STR_2174 :고기 국수 -STR_2175 :볶음쌀국수 +STR_2175 :볶음 쌀국수 STR_2176 :만둣국 -STR_2177 :미트볼 스프 +STR_2177 :미트볼 수프 STR_2178 :과일 주스 STR_2179 :두유 STR_2180 :수정과 @@ -1553,9 +1553,9 @@ STR_2193 :아이스 티 STR_2194 :퍼넬 케이크 STR_2195 :선글라스 STR_2196 :고기 국수 -STR_2197 :볶음쌀국수 +STR_2197 :볶음 쌀국수 STR_2198 :만둣국 -STR_2199 :미트볼 스프 +STR_2199 :미트볼 수프 STR_2200 :과일 주스 STR_2201 :두유 STR_2202 :수정과 @@ -1612,7 +1612,7 @@ STR_2252 :보도를 가로질러야만 설치할 수 있습니다! STR_2253 :운송용 놀이기구 STR_2254 :얌전한 놀이기구 STR_2255 :롤러코스터 -STR_2256 :스릴있는 놀이기구 +STR_2256 :스릴 있는 놀이기구 STR_2257 :물 놀이기구 STR_2258 :상점 & 매점 STR_2259 :풍경 & 테마 @@ -1630,14 +1630,14 @@ STR_2270 :{WINDOW_COLOUR_2}진행: {BLACK}{STRINGID} STR_2271 :{WINDOW_COLOUR_2}예상: {BLACK}{STRINGID} STR_2272 :{WINDOW_COLOUR_2}놀이기구/시설:{NEWLINE}{BLACK}{STRINGID} STR_2273 :{WINDOW_COLOUR_2}풍경/테마:{NEWLINE}{BLACK}{STRINGID} -STR_2274 :{SMALLFONT}{BLACK}이 개발에 대한 상세 내역을 보여줍니다 +STR_2274 :{SMALLFONT}{BLACK}이 개발에 대한 상세 내용을 보여줍니다 STR_2275 :{SMALLFONT}{BLACK}연구 & 개발을 위한 투자 설정 창을 보여줍니다 STR_2276 :{SMALLFONT}{BLACK}연구 & 개발 상황을 보여줍니다 STR_2277 :알 수 없음 STR_2278 :운송용 놀이기구 STR_2279 :얌전한 놀이기구 STR_2280 :롤러코스터 -STR_2281 :스릴있는 놀이기구 +STR_2281 :스릴 있는 놀이기구 STR_2282 :물 놀이기구 STR_2283 :상점/매점 STR_2284 :풍경/테마 @@ -1711,9 +1711,9 @@ STR_2358 :유닛 STR_2359 :실제 값 STR_2360 :해상도: STR_2361 :땅 테두리를 부드럽게 -STR_2362 :{SMALLFONT}{BLACK}땅의 테두리를 부드럽게 표시합니다. -STR_2363 :땅에 격자선 보이기 -STR_2364 :{SMALLFONT}{BLACK}땅에 격자선을 보여줍니다 +STR_2362 :{SMALLFONT}{BLACK}땅의 테두리를 부드럽게 표시합니다 +STR_2363 :땅에 격자선 표시 +STR_2364 :{SMALLFONT}{BLACK}땅에 격자선을 표시합니다 STR_2365 :은행이 대출을 증가시키는 것을 거절했습니다! STR_2366 :섭씨 (°C) STR_2367 :화씨 (°F) @@ -1739,7 +1739,7 @@ STR_2386 :{BLACK}공원에 최소 {COMMA16}명 이상의 손님을 {MONTHYEAR STR_2387 :{BLACK}최소 {POP16}{POP16}{CURRENCY} 이상의 공원 가치를 {PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}까지 달성하세요 STR_2388 :{BLACK}즐기세요! STR_2389 :{BLACK}최고의 {STRINGID}(을)를 건설하세요! -STR_2390 :{BLACK}흥미도가 최소 6.00을 넘는 10개의 다른 종류의 롤러코스터를 공원에 운용하세요 +STR_2390 :{BLACK}흥미도가 최소 6.00을 넘는 10종의 다른 롤러코스터를 공원에 운용하세요 STR_2391 :{BLACK}공원에 최소 {COMMA16}명 이상의 손님을 유치하세요. 단 한 순간이라도 공원 등급을 700 아래로 떨어뜨려서는 안 됩니다! STR_2392 :{BLACK}놀이기구 탑승 수익만으로 월 수익을 최소 {POP16}{POP16}{CURRENCY} 이상 달성하세요 STR_2393 :{BLACK}최소 길이가 {LENGTH} 이상이고 흥미도가 최소 7.00을 넘는 10개의 다른 종류의 롤러코스터를 공원에 운용하세요 @@ -1812,7 +1812,7 @@ STR_2469 :{SMALLFONT}{BLACK}연구 & 개발 수준을 선택합니다 STR_2470 :{SMALLFONT}{BLACK}새 운송용 놀이기구를 개발합니다 STR_2471 :{SMALLFONT}{BLACK}새 얌전한 놀이기구를 개발합니다 STR_2472 :{SMALLFONT}{BLACK}새 롤러코스터를 개발합니다 -STR_2473 :{SMALLFONT}{BLACK}새 스릴있는 놀이기구를 개발합니다 +STR_2473 :{SMALLFONT}{BLACK}새 스릴 있는 놀이기구를 개발합니다 STR_2474 :{SMALLFONT}{BLACK}새 물 놀이기구를 개발합니다 STR_2475 :{SMALLFONT}{BLACK}새 상점/매점을 개발합니다 STR_2476 :{SMALLFONT}{BLACK}새 풍경/테마를 개발합니다 @@ -2020,7 +2020,7 @@ STR_2678 :??? STR_2679 :??? STR_2680 :모든 연구가 완료되었습니다 STR_2681 :{MEDIUMFONT}{BLACK}돈 {CURRENCY} 늘리기 -STR_2684 :{SMALLFONT}{BLACK}손님 대규모로 불러오기 +STR_2684 :{SMALLFONT}{BLACK}손님을 대규모로 부릅니다 STR_2685 :단일 노이즈 매개 변수 STR_2686 :저: STR_2687 :고: @@ -2142,40 +2142,40 @@ STR_2810 :{RED}손님들이 목이 마르지만 음료수를 살 곳을 찾 STR_2811 :{RED}손님들이 공원에서 화장실을 찾을 수 없다고 불평하고 있습니다 STR_2812 :{RED}손님들이 길을 잃었거나 갇혔습니다{NEWLINE}보도의 구조를 손님들이 길을 더 쉽게 찾을 수 있도록 개선하세요 STR_2813 :{RED}공원 입장료가 너무 비쌉니다!{NEWLINE}손님을 더 유치하려면 공원 입장료를 내리거나 공원 가치를 올리세요 -STR_2814 :{WINDOW_COLOUR_2}가장 지저분한 공원 상 -STR_2815 :{WINDOW_COLOUR_2}가장 깔끔한 공원 상 -STR_2816 :{WINDOW_COLOUR_2}최고의 롤러코스터가 있는 공원 상 -STR_2817 :{WINDOW_COLOUR_2}최고의 공원 이용료 상 -STR_2818 :{WINDOW_COLOUR_2}가장 아름다운 공원 상 -STR_2819 :{WINDOW_COLOUR_2}최악의 공원 이용료 상 -STR_2820 :{WINDOW_COLOUR_2}가장 안전한 공원 상 -STR_2821 :{WINDOW_COLOUR_2}최고의 직원 상 -STR_2822 :{WINDOW_COLOUR_2}최고의 공원 음식 상 -STR_2823 :{WINDOW_COLOUR_2}최악의 공원 음식 상 -STR_2824 :{WINDOW_COLOUR_2}최고의 공원 화장실 상 -STR_2825 :{WINDOW_COLOUR_2}가장 실망스러운 공원 상 -STR_2826 :{WINDOW_COLOUR_2}최고의 물 놀이기구 상 -STR_2827 :{WINDOW_COLOUR_2}최고의 커스텀 디자인 놀이기구 상 -STR_2828 :{WINDOW_COLOUR_2}최고의 눈부신 놀이기구 색상 조합 상 -STR_2829 :{WINDOW_COLOUR_2}가장 복잡한 공원 구조 상 -STR_2830 :{WINDOW_COLOUR_2}최고의 얌전한 놀이기구 상 -STR_2831 :{TOPAZ}공원이 '우리나라에서 가장 지저분한 공원 상'을 받았습니다... -STR_2832 :{TOPAZ}공원이 '우리나라에서 가장 깔끔한 공원 상'을 받았습니다! -STR_2833 :{TOPAZ}공원이 '우리나라 최고의 롤러코스터가 있는 공원 상'을 받았습니다! -STR_2834 :{TOPAZ}공원이 '우리나라 최고의 공원 이용료 상'을 받았습니다! -STR_2835 :{TOPAZ}공원이 '우리나라에서 가장 아름다운 공원 상'을 받았습니다! -STR_2836 :{TOPAZ}공원이 '우리나라 최악의 공원 이용료 상'을 받았습니다... -STR_2837 :{TOPAZ}공원이 '우리나라에서 가장 안전한 공원 상'을 받았습니다! -STR_2838 :{TOPAZ}공원이 '우리나라 최고의 직원 상'을 받았습니다! -STR_2839 :{TOPAZ}공원이 '우리나라 최고의 공원 음식 상'을 받았습니다! -STR_2840 :{TOPAZ}공원이 '우리나라 최악의 공원 음식 상'을 받았습니다... -STR_2841 :{TOPAZ}공원이 '우리나라 최고의 공원 화장실 상'을 받았습니다! -STR_2842 :{TOPAZ}공원이 '우리나라에서 가장 실망스러운 공원 상'을 받았습니다... -STR_2843 :{TOPAZ}공원이 '우리나라 최고의 물 놀이기구 상'을 받았습니다! -STR_2844 :{TOPAZ}공원이 '우리나라 최고의 커스텀 디자인 놀이기구 상'을 받았습니다! -STR_2845 :{TOPAZ}공원이 '우리나라 최고의 눈부신 놀이기구 색상 조합 상'을 받았습니다! -STR_2846 :{TOPAZ}공원이 '우리나라에서 가장 복잡한 공원 구조 상'을 받았습니다... -STR_2847 :{TOPAZ}공원이 '우리나라 최고의 얌전한 놀이기구 상'을 받았습니다! +STR_2814 :{WINDOW_COLOUR_2}가장 지저분한 공원상 +STR_2815 :{WINDOW_COLOUR_2}가장 깔끔한 공원상 +STR_2816 :{WINDOW_COLOUR_2}최고의 롤러코스터가 있는 공원상 +STR_2817 :{WINDOW_COLOUR_2}최고의 공원 이용료상 +STR_2818 :{WINDOW_COLOUR_2}가장 아름다운 공원상 +STR_2819 :{WINDOW_COLOUR_2}최악의 공원 이용료상 +STR_2820 :{WINDOW_COLOUR_2}가장 안전한 공원상 +STR_2821 :{WINDOW_COLOUR_2}최고의 직원상 +STR_2822 :{WINDOW_COLOUR_2}최고의 공원 음식상 +STR_2823 :{WINDOW_COLOUR_2}최악의 공원 음식상 +STR_2824 :{WINDOW_COLOUR_2}최고의 공원 화장실상 +STR_2825 :{WINDOW_COLOUR_2}가장 실망스러운 공원상 +STR_2826 :{WINDOW_COLOUR_2}최고의 물 놀이기구상 +STR_2827 :{WINDOW_COLOUR_2}최고의 커스텀 디자인 놀이기구상 +STR_2828 :{WINDOW_COLOUR_2}최고의 눈부신 놀이기구 색상 조합상 +STR_2829 :{WINDOW_COLOUR_2}가장 복잡한 공원 구조상 +STR_2830 :{WINDOW_COLOUR_2}최고의 얌전한 놀이기구상 +STR_2831 :{TOPAZ}공원이 '우리나라에서 가장 지저분한 공원상'을 받았습니다... +STR_2832 :{TOPAZ}공원이 '우리나라에서 가장 깔끔한 공원상'을 받았습니다! +STR_2833 :{TOPAZ}공원이 '우리나라 최고의 롤러코스터가 있는 공원상'을 받았습니다! +STR_2834 :{TOPAZ}공원이 '우리나라 최고의 공원 이용료상'을 받았습니다! +STR_2835 :{TOPAZ}공원이 '우리나라에서 가장 아름다운 공원상'을 받았습니다! +STR_2836 :{TOPAZ}공원이 '우리나라 최악의 공원 이용료상'을 받았습니다... +STR_2837 :{TOPAZ}공원이 '우리나라에서 가장 안전한 공원상'을 받았습니다! +STR_2838 :{TOPAZ}공원이 '우리나라 최고의 직원상'을 받았습니다! +STR_2839 :{TOPAZ}공원이 '우리나라 최고의 공원 음식상'을 받았습니다! +STR_2840 :{TOPAZ}공원이 '우리나라 최악의 공원 음식상'을 받았습니다... +STR_2841 :{TOPAZ}공원이 '우리나라 최고의 공원 화장실상'을 받았습니다! +STR_2842 :{TOPAZ}공원이 '우리나라에서 가장 실망스러운 공원상'을 받았습니다... +STR_2843 :{TOPAZ}공원이 '우리나라 최고의 물 놀이기구상'을 받았습니다! +STR_2844 :{TOPAZ}공원이 '우리나라 최고의 커스텀 디자인 놀이기구상'을 받았습니다! +STR_2845 :{TOPAZ}공원이 '우리나라 최고의 눈부신 놀이기구 색상 조합상'을 받았습니다! +STR_2846 :{TOPAZ}공원이 '우리나라에서 가장 복잡한 공원 구조상'을 받았습니다... +STR_2847 :{TOPAZ}공원이 '우리나라 최고의 얌전한 놀이기구상'을 받았습니다! STR_2848 :{WINDOW_COLOUR_2}최근에 받은 상 없음 STR_2849 :새 시나리오 설치가 성공적으로 완료되었습니다 STR_2850 :새 트랙 디자인 설치가 성공적으로 완료되었습니다 @@ -2192,12 +2192,12 @@ STR_2864 :{WINDOW_COLOUR_2}March - Children of the Regiment: (푸치크) STR_2865 :{WINDOW_COLOUR_2}Heyken's Serenade: (J.Heyken) British Standard Music Coy; GEMA, BRITICO STR_2866 :{WINDOW_COLOUR_2}La Belle Espagnole: (Robert Vollstedt) 저작권 없음 STR_2867 :{WINDOW_COLOUR_2}Wedding Journey: (민요) -STR_2868 :{WINDOW_COLOUR_2}Tales from the Vienna Woods: (요한 스트라우스) 저작권 없음 +STR_2868 :{WINDOW_COLOUR_2}Tales from the Vienna Woods: (요한 슈트라우스) 저작권 없음 STR_2869 :{WINDOW_COLOUR_2}Slavonic Dance: (민요) STR_2870 :{WINDOW_COLOUR_2}Das Alpenhorn: (민요) STR_2871 :{WINDOW_COLOUR_2}The Blond Sailor: (민요) STR_2872 :{WINDOW_COLOUR_2}Overture - Poet and Peasant: (주페) 저작권 없음 -STR_2873 :{WINDOW_COLOUR_2}Waltz Medley: (요한 스트라우스) 저작권 없음 +STR_2873 :{WINDOW_COLOUR_2}Waltz Medley: (요한 슈트라우스) 저작권 없음 STR_2874 :{WINDOW_COLOUR_2}Bella Bella Bimba: (민요) STR_2875 :{WINDOW_COLOUR_2}Original recordings (P) 1976 C.J.Mears Organization, used with consent STR_2876 :{WINDOW_COLOUR_2}RollerCoaster Tycoon 2 타이틀 음악: (Allister Brimble) copyright © Chris Sawyer @@ -2241,7 +2241,7 @@ STR_2977 :직원 이름 STR_2978 :새 직원 이름을 입력하세요: STR_2979 :직원 이름을 바꿀 수 없습니다... STR_2980 :게임에 전광판이 너무 많습니다 -STR_2981 :{RED}출입 금지 - - +STR_2981 :{RED}출입 금지 STR_2982 :전광판 내용 STR_2983 :이 전광판의 문구를 입력하세요: STR_2984 :전광판의 내용을 바꿀 수 없습니다... @@ -2393,7 +2393,7 @@ STR_3159 :목록 STR_3160 :{SMALLFONT}{BLACK}한 운행마다 트랙을 몇 번 돌지를 선택합니다. STR_3162 :충분한 메모리를 할당할 수 없습니다 STR_3163 :새 데이터 설치중: -STR_3164 :{BLACK}{COMMA16}개 선택 됨 (최대 {COMMA16}개) +STR_3164 :{BLACK}{COMMA16}개 선택됨 (최대 {COMMA16}개) STR_3167 :{WINDOW_COLOUR_2}{BLACK}{COMMA16}개의 오브젝트 포함 STR_3169 :다음 오브젝트에 대한 데이터를 찾을 수 없습니다: STR_3170 :그래픽을 저장할 공간이 충분하지 않습니다 @@ -2420,7 +2420,6 @@ STR_3190 :공원 기타 STR_3191 :조형물 분류 STR_3192 :공원 입구 STR_3193 :물 -STR_3194 :시나리오 설명 STR_3195 :연구 목록 STR_3196 :{WINDOW_COLOUR_2}개발 그룹: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}게임 시작시 바로 사용할 수 있는 항목: @@ -2439,8 +2438,8 @@ STR_3209 :이전 단계로: STR_3210 :다음 단계로: STR_3211 :지도 크기: STR_3212 :{POP16}{COMMA16} x {PUSH16}{COMMA16} -STR_3213 :더 이상 맵 크기를 줄일 수 없습니다 -STR_3214 :더 이상 맵 크기를 늘릴 수 없습니다 +STR_3213 :맵 크기를 더 줄일 수 없습니다 +STR_3214 :맵 크기를 더 늘릴 수 없습니다 STR_3215 :지도 가장자리와 너무 가깝습니다 STR_3216 :{SMALLFONT}{BLACK}공원 소유지 등을 선택합니다 STR_3217 :소유한 땅 @@ -2449,8 +2448,8 @@ STR_3219 :판매 중인 땅 STR_3220 :판매 중인 건설권 STR_3221 :{SMALLFONT}{BLACK}공원이 이미 소유하고 있는 땅을 선택합니다 STR_3222 :{SMALLFONT}{BLACK}공원이 이미 소유하고 있는 건설권을 선택합니다 -STR_3223 :{SMALLFONT}{BLACK}공원이 향후 구입할 수 있는 땅을 선택합니다 -STR_3224 :{SMALLFONT}{BLACK}공원이 향후 구입할 수 있는 건설권을 선택합니다 +STR_3223 :{SMALLFONT}{BLACK}공원이 추가로 살 수 있는 땅을 선택합니다 +STR_3224 :{SMALLFONT}{BLACK}공원이 추가로 살 수 있는 건설권을 선택합니다 STR_3225 :{SMALLFONT}{BLACK}선택한 위치 주변에 오브젝트를 임의로 무리지어 설치하는 모드를 켜거나 끕니다 STR_3226 :{SMALLFONT}{BLACK}공원 입구 건설 STR_3227 :공원 입구가 너무 많습니다! @@ -2474,14 +2473,14 @@ STR_3244 :광고 이벤트를 금지 STR_3245 :{SMALLFONT}{BLACK}광고나 이벤트를 진행할 수 없도록 금지합니다 STR_3246 :{WINDOW_COLOUR_2}{CURRENCY} STR_3247 :{WINDOW_COLOUR_2}{COMMA16}% -STR_3248 :초기 보유 자금을 더 이상 늘릴 수 없습니다! -STR_3249 :초기 보유 자금을 더 이상 줄일 수 없습니다! -STR_3250 :초기 대출금을 더 이상 늘릴 수 없습니다! -STR_3251 :초기 대출금을 더 이상 줄일 수 없습니다! -STR_3252 :최대 대출금을 더 이상 늘릴 수 없습니다! -STR_3253 :최대 대출금을 더 이상 줄일 수 없습니다! -STR_3254 :이자율을 더 이상 늘릴 수 없습니다! -STR_3255 :이자율을 더 이상 줄일 수 없습니다! +STR_3248 :초기 보유 자금을 더 늘릴 수 없습니다! +STR_3249 :초기 보유 자금을 더 줄일 수 없습니다! +STR_3250 :초기 대출금을 더 늘릴 수 없습니다! +STR_3251 :초기 대출금을 더 줄일 수 없습니다! +STR_3252 :최대 대출금을 더 늘릴 수 없습니다! +STR_3253 :최대 대출금을 더 줄일 수 없습니다! +STR_3254 :이자율을 더 늘릴 수 없습니다! +STR_3255 :이자율을 더 줄일 수 없습니다! STR_3256 :손님들이 격렬하지 않은 놀이기구를 선호함 STR_3257 :{SMALLFONT}{BLACK}손님들이 일반적으로 덜 격렬한 놀이기구를 선호하게 만들지를 선택합니다 STR_3258 :손님들이 격렬한 놀이기구를 선호함 @@ -2490,8 +2489,8 @@ STR_3260 :{WINDOW_COLOUR_2}각 손님의 보유금 (평균): STR_3261 :{WINDOW_COLOUR_2}손님의 초기 행복도: STR_3262 :{WINDOW_COLOUR_2}손님의 초기 배고픔: STR_3263 :{WINDOW_COLOUR_2}손님의 초기 목마름: -STR_3264 :더 이상 늘릴 수 없습니다! -STR_3265 :더 이상 줄일 수 없습니다! +STR_3264 :더 늘릴 수 없습니다! +STR_3265 :더 줄일 수 없습니다! STR_3266 :{SMALLFONT}{BLACK}공원 수익을 입장료로 낼지 놀이기구 탑승료로 낼지를 선택하세요 STR_3267 :나무 제거 금지 STR_3268 :{SMALLFONT}{BLACK}키 큰 나무의 제거를 금지합니다 @@ -2608,7 +2607,7 @@ STR_3384 :같은 이름의 트랙 디자인이 이미 존재합니다 이 디 STR_3389 :추가 조형물 항목을 선택할 수 없습니다... STR_3390 :항목을 너무 많이 선택하였습니다 STR_3437 :{SMALLFONT}{BLACK}조형물을 한꺼번에 제거 -STR_3438 :여기에 있는 모든 조형물을 다 제거하지 못 하였습니다... +STR_3438 :여기에 있는 모든 조형물을 다 제거하지 못했습니다... STR_3439 :조형물 제거 STR_3440 :1쪽 STR_3441 :2쪽 @@ -2621,15 +2620,15 @@ STR_3446 :순찰 영역 해제 # New strings, cleaner STR_5120 :재정 STR_5121 :연구 -STR_5122 :놀이기구를 (RCT1처럼) 트랙 종류 별로 선택 +STR_5122 :RCT1처럼 놀이기구를 트랙 종류 별로 선택 STR_5123 :놀이기구 새 걸로 STR_5125 :모두 파괴 가능 STR_5126 :무작위 타이틀 음악 STR_5127 :{SMALLFONT}{BLACK}땅을 올리거나 내리지 않고 지형을 편집합니다 STR_5128 :선택 도구 크기 -STR_5129 :{COMMA16}에서 {COMMA16} 사이의 값을 입력하세요 +STR_5129 :{COMMA16}∼{COMMA16} 사이의 값을 입력하세요 STR_5130 :지도 크기 -STR_5131 :지도 크기를 {COMMA16}에서 {COMMA16} 사이의 값으로 입력하세요 +STR_5131 :{COMMA16}∼{COMMA16} 사이의 지도 크기 값을 입력하세요 STR_5132 :모든 놀이기구 수리 STR_5133 :{SMALLFONT}{BLACK}땅 구입 도구 크기 줄이기 STR_5134 :{SMALLFONT}{BLACK}땅 구입 도구 크기 늘이기 @@ -2669,7 +2668,7 @@ STR_5168 :{SMALLFONT}{BLACK}채널의 트위치 팔로워 이름을 딴 손 STR_5169 :손님 이름을 트위치 채팅 접속자 이름을 따서 붙이기 STR_5170 :{SMALLFONT}{BLACK}트위치 채팅의 접속자 이름을 따서 손님 이름으로 사용합니다 STR_5171 :채팅 인원 추적 -STR_5172 :{SMALLFONT}{BLACK}트위치 채팅에 참가하고 있는 사람들의 이름을 딴 손님의 행적을 추적합니다 +STR_5172 :{SMALLFONT}{BLACK}트위치 채팅에 참여하고 있는 사람들의 이름을 딴 손님의 행적을 추적합니다 STR_5173 :트위치 채팅 내용을 뉴스로 전송 STR_5174 :{SMALLFONT}{BLACK}트위치의 채팅 메시지를 !news를 이용하여 게임 메시지로 알려줍니다 STR_5175 :트위치 채널 이름 입력 @@ -2681,9 +2680,9 @@ STR_5180 :{SMALLFONT}{BLACK}공원 치트를 보여줍니다 STR_5181 :{SMALLFONT}{BLACK}놀이기구 치트를 보여줍니다 STR_5182 :{INT32} STR_5183 :기본 땅 높이 -STR_5184 :기본 땅 높이를 {COMMA16} 사이의 {COMMA16} 사이의 값으로 입력하세요 +STR_5184 :{COMMA16}∼{COMMA16} 사이의 기본 땅 높이 값을 입력하세요 STR_5185 :물 높이 -STR_5186 :물 높이를 {COMMA16}에서 {COMMA16} 사이의 값으로 입력하세요 +STR_5186 :{COMMA16}∼{COMMA16} 사이의 물 높이 값을 입력하세요 STR_5187 :재정 STR_5188 :새 광고 STR_5189 :연구 @@ -2867,7 +2866,7 @@ STR_5368 :충돌 이력 제거 STR_5369 :공원 설정... STR_5370 :{SMALLFONT}{BLACK}제한, 손님 생성, 재정 등의 공원과 관련된 설정을 수정하려면 이 버튼을 클릭하세요. STR_5371 :오브젝트 선택 -STR_5372 :마우스 오른쪽 드래그시 좌우 반전 +STR_5372 :마우스 오른쪽 드래그 시 좌우 반전 STR_5373 :이름 {STRINGID} STR_5374 :날짜 {STRINGID} STR_5375 :▲ @@ -2893,11 +2892,11 @@ STR_5394 :{SMALLFONT}{BLACK}선택한 게임 저장 파일 이름을 바꿉 STR_5395 :{SMALLFONT}{BLACK}선택한 게임 저장 파일을 게임에서 불러옵니다 STR_5396 :{SMALLFONT}{BLACK}게임 바깥에서 뭔가 변화가 생기면 타이틀 시퀀스를 다시 불러옵니다 STR_5397 :타이틀 화면에서만 사용할 수 있습니다 -STR_5398 :타이틀 시퀀스가 재생 중일 때에는 수정할 수 없습니다 +STR_5398 :타이틀 시퀀스가 재생 중일 때는 수정할 수 없습니다 STR_5399 :계속 수정하려면 중지 버튼을 누르세요 STR_5400 :타이틀 시퀀스를 변경할 수 없습니다 STR_5401 :다음으로 바꾸기 위해 타이틀 시퀀스를 생성합니다: -STR_5402 :타이틀 시퀀스를 불러오는데 실패하였습니다 +STR_5402 :타이틀 시퀀스를 불러오는 데 실패하였습니다 STR_5403 :불러오기나 기다리기 명령이 없거나 유효하지 않은 게임 저장 파일이 포함되었을 수 있습니다 STR_5404 :이미 존재하는 이름입니다 STR_5405 :세이브 파일의 이름을 입력하세요 @@ -2934,9 +2933,9 @@ STR_5435 :세이브 이름 바꾸기 STR_5436 :타이틀 시퀀스 편집하기... STR_5437 :선택된 세이브 없음 STR_5438 :명령 에디터가 열려있을 때 변경할 수 없습니다 -STR_5439 :4초 이상의 기다림 명령은 재시작 명령이 필요합니다. +STR_5439 :4초 이상의 기다림 명령은 재시작 명령이 필요합니다 STR_5440 :커서가 게임 밖으로 나가면 전체 화면을 최소화 -STR_5441 :{SMALLFONT}{BLACK}놀이기구를 종류별로 구분합니다. (RCT1처럼) +STR_5441 :{SMALLFONT}{BLACK}놀이기구를 RCT1처럼 종류별로 구분합니다 STR_5442 :공원 등급 고정: STR_5443 :속도{MOVE_X}{87}{STRINGID} STR_5444 :속도: @@ -3001,8 +3000,8 @@ STR_5503 :호스트네임이나 IP 주소를 입력하세요: STR_5504 :{SMALLFONT}{BLACK}접속자 목록 창을 엽니다 STR_5505 :서버에 접속할 수 없습니다 STR_5506 :손님이 격렬도를 무시하고 탐 -STR_5508 :올바르지 않은 검사합을 가진 파일 불러오기 허용 -STR_5509 :{SMALLFONT}{BLACK}데모에서 저장한 시나리오나 손상된 저장 파일과 같이 올바르지 않은 검사합을 가진 시나리오나 저장 파일을 불러오는 것을 허용합니다. +STR_5508 :올바르지 않은 검사 합을 가진 파일 불러오기 허용 +STR_5509 :{SMALLFONT}{BLACK}데모에서 저장한 시나리오나 손상된 저장 파일과 같이 올바르지 않은 검사 합을 가진 시나리오나 저장 파일을 불러오는 것을 허용합니다. STR_5510 :기본 사운드 장치 STR_5511 :(알 수 없음) STR_5512 :다른 이름으로 저장 @@ -3024,8 +3023,8 @@ STR_5527 :{SMALLFONT}{BLACK}짙은 녹색 STR_5528 :{SMALLFONT}{BLACK}어두운 녹색 STR_5529 :{SMALLFONT}{BLACK}모스 그린 STR_5530 :{SMALLFONT}{BLACK}밝은 녹색 -STR_5531 :{SMALLFONT}{BLACK}올리브 그린 -STR_5532 :{SMALLFONT}{BLACK}어두운 올리브 그린 +STR_5531 :{SMALLFONT}{BLACK}올리브그린 +STR_5532 :{SMALLFONT}{BLACK}어두운 올리브그린 STR_5533 :{SMALLFONT}{BLACK}밝은 노란색 STR_5534 :{SMALLFONT}{BLACK}노란색 STR_5535 :{SMALLFONT}{BLACK}어두운 노란색 @@ -3066,7 +3065,7 @@ STR_5569 :이 서버에 접속하려면 암호가 필요합니다 STR_5570 :서버 검색 STR_5571 :게임 참여 STR_5572 :즐겨찾기 등록 -STR_5573 :즐거찾기 제거 +STR_5573 :즐겨찾기 제거 STR_5574 :서버 이름: STR_5575 :최대 플레이어 수: STR_5576 :포트: @@ -3075,7 +3074,7 @@ STR_5578 :러시아 루블 (₽) STR_5579 :창 크기 조절 배수: STR_5580 :체코 코루나 (Kč) STR_5581 :FPS 보기 -STR_5582 :마우스 커서를 창 밖으로 나가지 못하게 +STR_5582 :마우스 커서를 창밖으로 나가지 못하게 STR_5583 :{COMMA1DP16}m/s STR_5584 :국제단위(SI)법 STR_5585 :{SMALLFONT}{BLACK}체인/발사 속력을 {VELOCITY}으로 만드는 등, 일부 놀이기구 운행 제한을 없애줍니다 @@ -3083,7 +3082,7 @@ STR_5586 :상점이나 가게를 자동으로 엶 STR_5587 :{SMALLFONT}{BLACK}상점이나 가게를 건설한 뒤에 자동으로 엽니다. STR_5588 :{SMALLFONT}{BLACK}다른 참가자와 함께 플레이합니다. STR_5589 :알림 설정 -STR_5590 :공원 수상 내역 +STR_5590 :공원 수상 기록 STR_5591 :광고 종료 STR_5592 :공원에 대한 경고 STR_5593 :공원 등급 경고 @@ -3094,7 +3093,7 @@ STR_5597 :놀이기구 / 풍경 연구 완료 STR_5598 :손님에 대한 경고 STR_5599 :손님의 길 잃음 STR_5600 :손님의 공원 퇴장 -STR_5601 :손님의 놀이기구 대기줄 진입 +STR_5601 :손님의 놀이기구 대기 줄 진입 STR_5602 :손님의 놀이기구 탑승 STR_5603 :손님의 놀이기구 하차 STR_5604 :손님의 물건 구입 @@ -3123,7 +3122,7 @@ STR_5626 :기타 공원 STR_5627 :시나리오 목록 분류 방식: STR_5628 :게임별 STR_5629 :난이도별 -STR_5630 :시나리오 클리어시 새 시나리오 열기 켜기 +STR_5630 :시나리오 클리어 시 새 시나리오 열기 켜기 STR_5631 :오리지널 다운로드 콘텐츠 공원 STR_5632 :직접 만들기... STR_5633 :CMD + @@ -3171,7 +3170,7 @@ STR_5708 :서버가 속한 그룹을 변경할 수 없습니다 STR_5709 :그룹 이름 변경 STR_5710 :그룹 이름 STR_5711 :이 그룹의 이름을 입력하세요: -STR_5712 :본인에게 없는 권한은 수정할 수 업습니다 +STR_5712 :본인에게 없는 권한은 수정할 수 없습니다 STR_5713 :플레이어 추방 STR_5714 :설정 창 보기 STR_5715 :새로운 게임 @@ -3224,7 +3223,7 @@ STR_5762 :중국 위안 (CN¥) STR_5763 :모든 파일 STR_5764 :잘못된 놀이기구 종류 STR_5765 :놀이기구를 유효하지 않은 종류로 수정할 수 없습니다 -STR_5766 :<사용 가능한 문자열 아이디> +STR_5766 :헝가리 포린트 (Ft) STR_5767 :수익 STR_5768 :총 고객 수 STR_5769 :총 수익 @@ -3252,8 +3251,8 @@ STR_5790 :{SMALLFONT}{BLACK}RCT1식으로 돈을 받습니다.{NEWLINE}(예: STR_5791 :{SMALLFONT}{BLACK}모든 놀이기구의 신뢰도를 100%로 설정하고{NEWLINE}건설 시점을 "올해"로 바꿉니다. STR_5792 :{SMALLFONT}{BLACK}고장난 놀이기구를 모두 고칩니다. STR_5793 :{SMALLFONT}{BLACK}놀이기구의 충돌 이력을 제거하여,{NEWLINE}손님들이 안전하지 않다고 불평하지 않게 만듭니다. -STR_5794 :{SMALLFONT}{BLACK}일부 시나리오에서 공원에 이미 존재하는 놀이기구를 수정할 수 없게 해놓은 것을 해제해줍니다. -STR_5795 :{SMALLFONT}{BLACK}손님들이 놀이기구의 격렬도가 아무리 높아도 그 놀이기구에 타도록 만듭니다. +STR_5794 :{SMALLFONT}{BLACK}일부 시나리오에서 공원에 이미 존재하는 놀이기구를 수정할 수 없게 해놓은 것을 해제합니다. +STR_5795 :{SMALLFONT}{BLACK}놀이기구의 격렬도가 아무리 높아도 손님들이 그 놀이기구에 타도록 만듭니다. STR_5796 :{SMALLFONT}{BLACK}공원의 문을 강제로 열거나 닫습니다. STR_5797 :{SMALLFONT}{BLACK}날씨를 선택한 날씨로 고정하고 바뀌지 않게 만듭니다. STR_5798 :{SMALLFONT}{BLACK}일시정지 상태에서 건설을 허용합니다. @@ -3280,7 +3279,7 @@ STR_5817 :{SMALLFONT}{BLACK}게임 UI 크기 조절 방식을 설정합니다 STR_5819 :{SMALLFONT}{BLACK}[하드웨어 디스플레이 설정 필요]{NEWLINE}스팀 인게임 오버레이가 열려있으면 게임을 일시정지시킵니다. STR_5820 :{SMALLFONT}{BLACK}전체 화면 모드에서 커서가 게임 밖으로 나가면 게임을 최소화시킵니다. STR_5822 :{SMALLFONT}{BLACK}낮밤을 순환하도록 설정합니다.{NEWLINE}낮밤이 바뀌는 데에는 한 달이 걸립니다. -STR_5823 :{SMALLFONT}{BLACK}팻말의 영문을 대문자로 표시합니다. (RCT1처럼) +STR_5823 :{SMALLFONT}{BLACK}RCT1처럼 팻말의 영문을 대문자로 표시합니다. STR_5824 :{SMALLFONT}{BLACK}폭풍이 칠 때 번개가 치는 효과를 끕니다. STR_5825 :{SMALLFONT}{BLACK}마우스 커서를 화면 밖으로 나가지 못하게 만듭니다. STR_5826 :{SMALLFONT}{BLACK}게임 화면에서 마우스 오른쪽 클릭으로 드래그할 때의 방향을 반대로 바꿉니다. @@ -3299,8 +3298,8 @@ STR_5838 :{SMALLFONT}{BLACK}메인 메뉴에 재정 창을 위한 별도의 STR_5839 :{SMALLFONT}{BLACK}메인 매뉴에 연구 및 개발 창을 위한 별도의 버튼을 표시합니다. STR_5840 :{SMALLFONT}{BLACK}메인 메뉴에 치트 창을 위한 별도의 버튼을 표시합니다. STR_5841 :{SMALLFONT}{BLACK}메인 메뉴에 최근 메시지 창을 위한 별도의 버튼을 표시합니다. -STR_5842 :{SMALLFONT}{BLACK}시나리오를 (RCT2처럼) 난이도 별로 정렬하거나 (RCT1처럼) 출신 게임별로 정렬합니다. -STR_5843 :{SMALLFONT}{BLACK}(RCT1 처럼) 시나리오를 클리어하면 다음 시나리오를 해제합니다. +STR_5842 :{SMALLFONT}{BLACK}시나리오를 RCT2처럼 난이도별로 정렬하거나, RCT1처럼 출신 게임별로 정렬합니다. +STR_5843 :{SMALLFONT}{BLACK}RCT1처럼 시나리오를 클리어하면 다음 시나리오를 해제합니다. STR_5844 :{SMALLFONT}{BLACK}멀티 플레이 서버에서 비동기화나 오류가 발생하여도 접속을 유지합니다. STR_5845 :{SMALLFONT}{BLACK}메인 메뉴에 디버깅 툴을 위한 버튼을 추가합니다.{NEWLINE}개발자 콘솔을 여는 키보드 단축키가 활성화됩니다. STR_5846 :{SMALLFONT}{BLACK}OpenRCT2의 자동 저장 빈도를 설정합니다. @@ -3315,14 +3314,14 @@ STR_5854 :{SMALLFONT}{BLACK}놀이기구 음악 켜기/끄기 STR_5855 :{SMALLFONT}{BLACK}전체화면, 창 모드, 전체화면 (창모드) 중에서 선택합니다. STR_5856 :{SMALLFONT}{BLACK}전체화면에서 사용할 게임 해상도를 설정합니다. STR_5857 :{SMALLFONT}{BLACK}게임 설정 -STR_5858 :{SMALLFONT}{BLACK}CPU 대신 GPU를 이용하여 화면을 표시합니다. 스크린 캡쳐 소프트웨어의 호환성을 향상시켜줍니다. 약간의 속도 저하가 발생할 수 있습니다. +STR_5858 :{SMALLFONT}{BLACK}CPU 대신 GPU를 이용하여 화면을 표시합니다. 스크린 캡처 소프트웨어의 호환성을 향상시켜줍니다. 약간의 속도 저하가 발생할 수 있습니다. STR_5859 :{SMALLFONT}{BLACK}시각적으로 더 부드러운 게임 플레이를 위해 프레임 제한을 해제합니다. 설정을 끄면 게임이 40 FPS로 실행됩니다. STR_5860 :오리지널 트랙 표시 방식 전환 STR_5861 :키 인증 실패 -STR_5862 :알 수 없는 플레이어를 차단합니다. +STR_5862 :알 수 없는 플레이어 차단 STR_5863 :{SMALLFONT}{BLACK}알려진 키를 가진 플레이어만 입장을 허용합니다. -STR_5864 :이 서버는 알려진 키를 가진 플레이어의 입장만을 허용합니다. -STR_5865 :대화 내역 기록하기 +STR_5864 :이 서버는 알려진 키를 가진 플레이어만 입장할 수 있습니다. +STR_5865 :대화 내용 기록하기 STR_5866 :{SMALLFONT}{BLACK}모든 대화 기록을 사용자 문서 폴더에 파일로 기록합니다. STR_5867 :{WINDOW_COLOUR_2}서버 운영자: {BLACK}{STRING} STR_5868 :{WINDOW_COLOUR_2}서버 전자우편: {BLACK}{STRING} @@ -3333,7 +3332,7 @@ STR_5872 :{SMALLFONT}{BLACK}꽃이 시들지 않게 만듭니다. STR_5873 :모든 트랙에 체인 걸기 허용 STR_5874 :{SMALLFONT}{BLACK}모든 트랙 조각에 체인 리프트를 걸 수 있도록 허용합니다. STR_5875 :드로잉 엔진: -STR_5876 :{SMALLFONT}{BLACK}게임 요소를 표시는데 사용할 엔진을 선택합니다. +STR_5876 :{SMALLFONT}{BLACK}게임 요소를 표시하는 데 사용할 엔진을 선택합니다. STR_5877 :소프트웨어 STR_5878 :소프트웨어 (하드웨어 디스플레이) STR_5879 :OpenGL (개발중) @@ -3379,7 +3378,7 @@ STR_5919 :{COMMA16} STR_5920 :날씨 효과 표현 STR_5921 :{SMALLFONT}{BLACK}이 설정을 켜면, 폭풍우가 치는 동안 비와 흐린 색상을 표현합니다. STR_5922 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{SMALLFONT}{BLACK}최대 {STRINGID} -STR_5923 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{SMALLFONT}{BLACK}열차당 최대 {COMMA16} {STRINGID} +STR_5923 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{SMALLFONT}{BLACK}열차당 최대 {COMMA16}{STRINGID} STR_5924 :지면 상세정보 STR_5925 :보도 상세정보 STR_5926 :트랙 상세정보 @@ -3399,8 +3398,8 @@ STR_5939 :공원 울타리 제거 STR_5940 :공원 울타리 복구 STR_5941 :{WINDOW_COLOUR_2}기본 높이: STR_5942 :{WINDOW_COLOUR_2}공원 이름: {BLACK}{STRINGID} -STR_5943 :{WINDOW_COLOUR_2}추가: {BLACK}{STRINGID} -STR_5944 :{WINDOW_COLOUR_2}추가: {BLACK}없음 +STR_5943 :{WINDOW_COLOUR_2}기물: {BLACK}{STRINGID} +STR_5944 :{WINDOW_COLOUR_2}기물: {BLACK}없음 STR_5945 :{WINDOW_COLOUR_2}연결된 면: STR_5946 :{WINDOW_COLOUR_2}놀이기구 종류: {BLACK}{STRINGID} STR_5947 :{WINDOW_COLOUR_2}놀이기구 ID: {BLACK}{COMMA16} @@ -3444,9 +3443,9 @@ STR_5984 :막힌 보도: STR_5985 :새 폴더 STR_5986 :새 폴더의 이름을 입력하세요. STR_5987 :폴더를 만들 수 없습니다. -STR_5988 :{SMALLFONT}{BLACK}판매 중인 땅이 더 이상 없습니다. -STR_5989 :{SMALLFONT}{BLACK}판매 중인 건설권이 더 이상 없습니다. -STR_5990 :{SMALLFONT}{BLACK}판매 중인 땅이나 건설권이 더 이상 없습니다. +STR_5988 :{SMALLFONT}{BLACK}판매 중인 땅이 없습니다. +STR_5989 :{SMALLFONT}{BLACK}판매 중인 건설권이 없습니다. +STR_5990 :{SMALLFONT}{BLACK}판매 중인 땅이나 건설권이 없습니다. STR_5991 :붙여넣을 수 없습니다... STR_5992 :맵 요소 제한 개수를 초과했습니다 STR_5993 :{SMALLFONT}{BLACK}선택한 요소를 복사합니다. @@ -3516,12 +3515,12 @@ STR_6056 :{SMALLFONT}{BLACK}음소거 STR_6057 :{SMALLFONT}{BLACK}메인 메뉴에 음소거를 설정할 수 있는 별도의 버튼을 표시합니다. STR_6058 :음소거 STR_6059 :» -STR_6060 :손님의 돈 사용을 애니메이션 효과로 표시 -STR_6061 :{SMALLFONT}{BLACK}손님들이 돈을 사용하면{NEWLINE}움직이는 효과로 표시해줍니다. +STR_6060 :손님이 돈을 쓰면 화면에 표시 +STR_6061 :{SMALLFONT}{BLACK}손님들이 돈을 사용하면{NEWLINE}화면에 움직이는 글씨로 표시합니다. STR_6062 :{OUTLINE}{GREEN}+ {CURRENCY2DP} STR_6063 :{OUTLINE}{RED}- {CURRENCY2DP} STR_6064 :모든 땅 소유 -STR_6065 :유저 행동 기록 +STR_6065 :유저 행동 기록하기 STR_6066 :{SMALLFONT}{BLACK}모든 유저의 행동을 사용자 폴더에 파일로 기록합니다. STR_6067 :서버 시작됨 STR_6068 :서버가 종료됨 @@ -3544,7 +3543,7 @@ STR_6084 :{STRING}, '{STRING}' 놀이기구의 차량 설정을 변경함. STR_6085 :{STRING}, '{STRING}' 놀이기구의 설정을 변경함. STR_6086 :{STRING}, '{STRING}' 놀이기구의 이름을 '{STRING}'(으)로 변경. STR_6087 :{STRING}, '{STRING}' 놀이기구의 가격을 {STRING}(으)로 변경 -STR_6088 :{STRING}, '{STRING}' 놀이기구의 부가격을 {STRING}(으)로 변경 +STR_6088 :{STRING}, '{STRING}' 놀이기구의 두 번째 가격을 {STRING}(으)로 변경 STR_6089 :{STRING}, 공원 이름을 '{STRING}'에서 '{STRING}'(으)로 변경 STR_6090 :{STRING}, 공원 문을 엶 STR_6091 :{STRING}, 공원 문을 닫음 @@ -3568,16 +3567,16 @@ STR_6108 :스틸 트위스터 STR_6109 :하이퍼 트위스터 STR_6110 :주니어 롤러코스터 STR_6111 :클래식 미니 롤러코스터 -STR_6112 :차량이 나선 트랙과 루프를 통과하는 알찬 구성의 철제 트랙 롤러코스터입니다. +STR_6112 :나선 트랙과 루프를 통과하는 알찬 구성의 철제 트랙 롤러코스터입니다. STR_6113 :큰 낙하, 높은 속도, 그리고 무릎 안전바만 있는 편안한 차량을 가진 높고 뒤집어질 수 없는 롤러코스터입니다. STR_6114 :정해진 트랙을 따라 천천히 움직이는 놀이기구입니다. STR_6115 :급경사를 오를 수 있는 거대한 4 x 4 트럭 차량입니다. STR_6116 :여러 가지 반전 트랙을 통과하면서 부드러운 강철 트랙을 따라서 활강하는, 폭이 넓은 롤러코스터입니다. STR_6117 :탑승객들은 한 바퀴를 운행하는 편안한 좌석에 앉아 언덕을 오르면서 느낄 수 있는 '무중력 시간'뿐만이 아니라 크고 부드러운 낙하와 꼬인 트랙의 느낌을 즐길 수 있습니다. STR_6118 :큰 놀이기구를 탈 용기가 없는 사람들을 위한 얌전한 롤러코스터입니다. -STR_6119 :높이 제한이 있지만 값 싸고 건설하기 쉬운 롤러코스터입니다. +STR_6119 :높이 제한이 있지만, 값싸고 건설하기 쉬운 롤러코스터입니다. STR_6120 :{BABYBLUE}{STRINGID}에 새로운 차량이 추가되었습니다:{NEWLINE}{STRINGID} -STR_6121 :{SMALLFONT}{BLACK}공원이 소유한 땅을 맵 끝까지 모든 방향으로 확장시켜줍니다. +STR_6121 :{SMALLFONT}{BLACK}공원이 소유한 땅을 맵 끝까지 모든 방향으로 확장합니다. STR_6122 :이 시나리오에 롤러코스터가 충분히 많이 있지 않습니다! STR_6123 :공원에 사용된 오브젝트 불러오기 오류 STR_6124 :오브젝트 이름 @@ -3603,11 +3602,11 @@ STR_6143 :{WINDOW_COLOUR_2}놀이기구 종류: {BLACK}{STRINGID} STR_6144 :시각적 업데이트 표시 STR_6145 :{SMALLFONT}{BLACK}부스터 속도 제한 설정 STR_6146 :모든 표시 가능한 트랙 조각 사용 -STR_6147 :{SMALLFONT}{BLACK}놀이기구 건설 창에서 해당 놀이기구가 지원하는 지의 여부와 관계 없이, 그 놀이기구에서 사용할 수 있는 모든 트랙 조각의 사용을 허용합니다. +STR_6147 :{SMALLFONT}{BLACK}놀이기구 건설 창에서 해당 놀이기구가 지원하는지의 여부와 관계없이, 그 놀이기구에서 사용할 수 있는 모든 트랙 조각의 사용을 허용합니다. STR_6148 :마스터 서버에 접속 중... STR_6149 :마스터 서버에 접속할 수 없습니다 STR_6150 :마스터 서버에서 잘못된 응답이 왔습니다 (JSON 숫자 데이터 없음) -STR_6151 :마스터 서버가 서버 목록을 보내주지 못 했습니다 +STR_6151 :마스터 서버가 서버 목록을 보내주지 못했습니다 STR_6152 :마스터 서버에서 잘못된 응답이 왔습니다 (JSON 배열 데이터 없음) STR_6153 :모두 받기 STR_6154 :보안상의 이유로, 높은 권한으로 OpenRCT2를 실행하는 것을 추천하지 않습니다 @@ -3646,7 +3645,7 @@ STR_6186 :선택된 스프라이트 없음 STR_6187 :{MEDIUMFONT}{OUTLINE}{WINDOW_COLOUR_2}{STRING} STR_6188 :구토 STR_6189 :오리 -STR_6190 :{SMALLFONT}{BLACK}메인 타이틀 시퀀스가 활성화되어 있을 때에는 스프라이트를 선택할 수 없습니다. +STR_6190 :{SMALLFONT}{BLACK}메인 타이틀 시퀀스가 활성화되어 있을 때는 스프라이트를 선택할 수 없습니다. STR_6191 :지면 STR_6192 :벽 STR_6193 :손님 {COMMA16}명 @@ -3659,7 +3658,7 @@ STR_6199 :날짜 설정 STR_6200 :날짜 초기화 STR_6201 :{MONTH} STR_6202 :가상 지면 표시 -STR_6203 :{SMALLFONT}{BLACK}이 설정을 켜면, Ctrl이나 Shift 키를 누르고 있을 때 오브젝트의 수직 설치에 도움이 되는 가상 지면을 표시해줍니다. +STR_6203 :{SMALLFONT}{BLACK}이 설정을 켜면, Ctrl이나 Shift 키를 누르고 있을 때 오브젝트의 수직 설치에 도움이 되는 가상 지면을 표시합니다. STR_6204 :벽돌 STR_6205 :철 STR_6206 :회색 절벽 @@ -3759,7 +3758,36 @@ STR_6299 :전체 다운로드 STR_6300 :{SMALLFONT}{BLACK}빠진 오브젝트를 온라인에서 다운로드 할 수 있으면 모두 다운로드합니다. STR_6301 :{SMALLFONT}{BLACK}선택한 오브젝트의 이름을 클립보드에 복사합니다. STR_6302 :{SMALLFONT}{BLACK}빠진 오브젝트의 전체 목록을 클립보드에 복사합니다. -STR_6303 :오브젝트 다운로드 중 ({COMMA16} / {COMMA16}): [{STRING}] +STR_6303 :[{POP16}{POP16}{STRING}] 다운로드 중 ({COMMA16} / {COMMA16}) +STR_6304 :오브젝트 스포이드 +STR_6305 :멀티스레딩 +STR_6306 :{SMALLFONT}{BLACK}렌더링을 하기 위해 여러 개의 스레드를 사용합니다. 실험적인 기능이므로 불안정할 수 있습니다. +STR_6307 :색상 조합: {BLACK}{STRINGID} +STR_6308 :“{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :재접속 +STR_6310 :{WINDOW_COLOUR_2}현재 위치: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}다음 위치: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(지표면) +STR_6313 :(높이 {INT32}) +STR_6314 :{WINDOW_COLOUR_2}목표: {BLACK}{INT32}, {INT32} 인내심 {INT32} +STR_6315 :{WINDOW_COLOUR_2}경로탐색 목표: {BLACK}{INT32}, {INT32}, {INT32} 방향 {INT32} +STR_6316 :{WINDOW_COLOUR_2}경로탐색 기록: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} 방향 {INT32} +STR_6318 :네트워크 비동기화가 감지되었습니다.{NEWLINE}로그 파일: {STRING} +STR_6319 :{WINDOW_COLOUR_2}블록 브레이크 닫기 +STR_6320 :{WINDOW_COLOUR_2}파괴 불가 +STR_6321 :{WINDOW_COLOUR_2}기물 부서짐 +STR_6322 :{WINDOW_COLOUR_2}스프라이트 ID: {BLACK}{INT32} +STR_6323 :가상 운행 중 +STR_6324 :가상 운행 +STR_6325 :{SMALLFONT}{BLACK}놀이기구 가상 운행 +STR_6326 :{POP16}{POP16}{POP16}{STRINGID}의 가상 운행을 시작할 수 없습니다... +STR_6327 :초대형 스크린 샷의 배경을 투명하게 처리 +STR_6328 :{SMALLFONT}{BLACK}이 설정을 켜면, 초대형 스크린 샷을 찍을 경우 배경색이 기본 검은색 대신 투명으로 처리됩니다. +STR_6329 :{STRING}{STRINGID} +STR_6330 :{POP16}{POP16}{POP16}{POP16}{STRING}에서 [{PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{STRING}] 다운로드 중 ({COMMA16} / {COMMA16}) +STR_6331 :오리 생성하기 +STR_6332 :오리 제거하기 ############# @@ -3810,12 +3838,12 @@ STR_DTLS :몇 개의 놀이기구와 확장할 공간이 있는 작은 놀이 STR_SCNR :Pokey Park STR_PARK :비좁은 공원 -STR_DTLS :대대적인 확장을 해야하는 비좁은 놀이공원입니다. +STR_DTLS :대대적인 확장을 해야 하는 비좁은 놀이공원입니다. STR_SCNR :White Water Park STR_PARK :맑은 물 공원 -STR_DTLS :훌륭한 물 놀이기구를 몇 개 갖고있지만 확장이 필요한 공원입니다. +STR_DTLS :훌륭한 물 놀이기구를 몇 개 갖고 있지만 확장이 필요한 공원입니다. STR_SCNR :Millennium Mines @@ -3825,12 +3853,12 @@ STR_DTLS :버려져있는 커다란 탄광을 관광시설에서 놀이공원 STR_SCNR :Karts & Coasters STR_PARK :카트 & 코스터 -STR_DTLS :몇 개의 고 카트 트랙과 우든 롤러코스터만이 숲 속에 숨겨져 있는 대형 놀이공원입니다. +STR_DTLS :몇 개의 고 카트 트랙과 우든 롤러코스터만이 숲속에 숨겨져 있는 대형 놀이공원입니다. STR_SCNR :Mel's World STR_PARK :멜의 세계 -STR_DTLS :이 놀이공원에는 잘 디자인된 현대식 놀이기구가 있지만 아직도 확장해야 할 곳이 많습니다. +STR_DTLS :이 놀이공원에는 잘 디자인된 현대식 놀이기구가 있지만, 아직도 확장해야 할 곳이 많습니다. STR_SCNR :Mystic Mountain @@ -3870,7 +3898,7 @@ STR_DTLS :무지개 계곡의 지역 당국은 풍경을 바꾸거나 큰 나 STR_SCNR :Thunder Rock STR_PARK :천둥 바위 -STR_DTLS :천둥 바위는 사막 한가운데에서 많은 관광객을 끌어 모으고 있습니다. 사용 가능한 공간을 이용해 놀이기구를 건설해서 더 많은 사람들을 끌어 보세요. +STR_DTLS :천둥 바위는 사막 한가운데에서 많은 관광객을 끌어모으고 있습니다. 사용 가능한 공간을 이용해 놀이기구를 건설해서 더 많은 사람을 끌어 보세요. STR_SCNR :Mega Park @@ -3881,12 +3909,12 @@ STR_DTLS :그냥 즐기세요! STR_SCNR :Whispering Cliffs STR_PARK :속삭이는 절벽 -STR_DTLS :해변의 절벽을 인기있는 놀이공원으로 발전시키세요. +STR_DTLS :해변의 절벽을 인기 있는 놀이공원으로 발전시키세요. STR_SCNR :Three Monkeys Park STR_PARK :세 원숭이 공원 -STR_DTLS :개발 중인 이 거대한 공원의 한 가운데에는 동시에 경주하는 거대한 세 쌍둥이 스틸 롤러코스터가 있습니다. +STR_DTLS :개발 중인 이 거대한 공원의 한 가운데에는 동시에 경주하는 거대한 세쌍둥이 스틸 롤러코스터가 있습니다. STR_SCNR :Canary Mines @@ -3991,7 +4019,7 @@ STR_DTLS :사막 한가운데의 오아시스에 놀이공원을 건설해보 STR_SCNR :Rotting Heights STR_PARK :썩어버린 언덕 -STR_DTLS :지나치게 성장해서 황폐화되어버린 이 곳을 다시 멋진 공원으로 바꿀 수 있습니까? +STR_DTLS :지나치게 성장해서 황폐화되어버린 이곳을 다시 멋진 공원으로 바꿀 수 있습니까? STR_SCNR :Fiasco Forest @@ -4043,7 +4071,7 @@ STR_DTLS :이번 롤러코스터 건설 도전에서는 휴화산이 무대 STR_SCNR :Arid Heights STR_PARK :건조한 언덕 -STR_DTLS :아무런 재정적 제한 없는 상황에서, 지속적으로 손님들을 행복하게 만들 수 있는 공원을 사막에 건설해보세요. +STR_DTLS :아무런 재정적 제한 없는 상황에서, 손님들을 언제나 행복하게 만들 수 있는 공원을 사막에 건설해보세요. STR_SCNR :Razor Rocks @@ -4058,12 +4086,12 @@ STR_DTLS :이 공원의 부지는 고대 분화구에 만들어진 커다란 STR_SCNR :Vertigo Views STR_PARK :어지러운 풍경 -STR_DTLS :이 거대한 공원에는 이미 훌륭한 하이퍼코스터가 있습니다. 여러분의 임무는 그 매출을 아주 극대화시키는 것입니다. +STR_DTLS :이 거대한 공원에는 이미 훌륭한 하이퍼코스터가 있습니다. 여러분의 임무는 그 매출을 아주 극대화하는 것입니다. STR_SCNR :Paradise Pier 2 STR_PARK :파라다이스 부두 2 -STR_DTLS :파라다이스 부두는 바다 위의 산책로를 확장했습니다. 여러분의 임무는 이 새로운 공간을 이용하여 공원을 확장하는 것 입니다. +STR_DTLS :파라다이스 부두는 바다 위의 산책로를 확장했습니다. 여러분의 임무는 이 새로운 공간을 이용하여 공원을 확장하는 것입니다. STR_SCNR :Dragon's Cove @@ -4260,12 +4288,12 @@ STR_DTLS :이 작은 시장에 고객들을 유치하기 위해 놀이기구 STR_SCNR :이상한 성 STR_PARK :이상한 성 -STR_DTLS :당신은 큰 성을 물려받았습니다. 이제 이 성을 작은 놀이공원으로 바꾸는 것이 당신이 해야할 일입니다. +STR_DTLS :당신은 큰 성을 물려받았습니다. 이제 이 성을 작은 놀이공원으로 바꾸는 것이 당신이 해야 할 일입니다. STR_SCNR :먼지투성이 그린 STR_PARK :먼지투성이 그린 -STR_DTLS :사막의 고속도로 교차로 근처에 있는 먼지투성이 그린을 작은 골프 리조트에서 번화한 놀이공원으로 개발할 수 있는 기회가 생겼습니다. +STR_DTLS :사막의 고속도로 교차로 근처에 있는 먼지투성이 그린을 작은 골프 리조트에서 번화한 놀이공원으로 개발할 기회가 생겼습니다. STR_SCNR :일렉트릭 농장 @@ -4310,7 +4338,7 @@ STR_DTLS :자본에는 제한이 없지만 호수가 있는 곳이기 때문 STR_SCNR :무지개 정상 STR_PARK :무지개 정상 -STR_DTLS :이 공원은 산 허리에 만들어야 하고, 높은 건물은 지을 수 없습니다. 공원을 확장하여 성공할 자신이 있습니까? +STR_DTLS :이 공원은 산허리에 만들어야 하고, 높은 건물은 지을 수 없습니다. 공원을 확장하여 성공할 자신이 있습니까? STR_SCNR :식스 플래그 벨기에 @@ -4383,7 +4411,7 @@ STR_DTLS :문화 인식 프로그램의 일환으로, 오스트레일리아 STR_SCNR :오스트레일리아 - 해변의 즐거움 STR_PARK :해변의 바베큐 파티 -STR_DTLS :근처 지역 사업가의 해안 공원이 파산해버렸습니다. 이미 작은 공원을 운영하고 있는 당신은 건설사로부터 그 공원을 구입하였습니다. 두 공원을 합쳐 크게 사업을 확장해보세요. +STR_DTLS :근처 지역 사업가의 해안 공원이 파산해버렸습니다. 이미 작은 공원을 운영하는 당신은 건설사로부터 그 공원을 구입하였습니다. 두 공원을 합쳐 크게 사업을 확장해보세요. STR_SCNR :유럽 - 유럽 문화 축제 @@ -4393,20 +4421,20 @@ STR_DTLS :유럽 문화 축제의 경영권을 확보한 당신은 이제 유 STR_SCNR :유럽 - 혁신 STR_PARK :잿더미를 딛고 -STR_DTLS :방치되어 있는 낡은 공원이 있습니다. 이 공원이 과거에 누렸던 영광을 재현하기 위해 유럽 연합의 허가를 받았습니다! 허가를 받은 만큼 공원을 혁신해서 보답하세요. +STR_DTLS :방치된 낡은 공원이 있습니다. 이 공원이 과거에 누렸던 영광을 재현하기 위해 유럽 연합의 허가를 받았습니다! 허가를 받은 만큼 공원을 혁신해서 보답하세요. -STR_SCNR :북 아메리카 - 최고의 하와이 섬 +STR_SCNR :북아메리카 - 최고의 하와이 섬 STR_PARK :왁자지껄 와이키키 -STR_DTLS :하와이 주민들이 이제 파도타기가 지겨워져서 좀 더 자극적인 뭔가를 원하고 있습니다. 주민들도 만족시키면서 이 지역의 관광객들이 찾아오고 싶어하는 공원을 만드세요. +STR_DTLS :하와이 주민들이 이제 파도타기가 지겨워져서 좀 더 자극적인 뭔가를 원하고 있습니다. 주민들도 만족시키면서 이 지역의 관광객들이 찾아오고 싶어 하는 공원을 만드세요. -STR_SCNR :북 아메리카 - 그랜드 캐니언 +STR_SCNR :북아메리카 - 그랜드 캐니언 STR_PARK :불행의 계곡 -STR_DTLS :이 아름답지만 제한적인 공간에 공원을 만들어야 합니다. 미국 원주민에게서 반대편 땅을 구입할 수 있습니다. 줄어들고 있는 마을 인구를 유지하기 위해서라도 목표를 달성해야 합니다. +STR_DTLS :이 아름답지만 제한적인 공간에 공원을 만들어야 합니다. 미국 원주민에게서 반대편 땅을 살 수 있습니다. 줄어들고 있는 마을 인구를 유지하기 위해서라도 목표를 달성해야 합니다. -STR_SCNR :북 아메리카 - 롤러코스터 천국 +STR_SCNR :북아메리카 - 롤러코스터 천국 STR_PARK :롤러코스터 천국 STR_DTLS :당신은 긴 휴식기를 가지는 동안 이 도시 공원을 롤러코스터 천국으로 바꾸고자 하는 성공한 사업 거물입니다. 돈은 문제가 되지 않는군요! @@ -4423,7 +4451,7 @@ STR_DTLS :소중한 열대우림 속의 제한된 공간만을 받았습니 STR_SCNR :남 아메리카 - 리오 축제 STR_PARK :슈거로프 해안 -STR_DTLS :리오 근처의 작은 공원을 운영하고 있는데 은행이 대출금을 조기 회수하려 합니다. 때이른 빚 독촉을 갚기 위해서 빨리 돈을 벌어야 합니다. +STR_DTLS :리오 근처의 작은 공원을 운영하고 있는데 은행이 대출금을 조기 회수하려 합니다. 때 이른 빚 독촉을 갚기 위해서 빨리 돈을 벌어야 합니다. ############################################################################### ## Time Twister Scenarios @@ -4434,14 +4462,14 @@ STR_PARK :클리프사이드 성 STR_DTLS :이 지역의 전투 재현 단체는 자신들의 취미를 진지하게 생각하고 있습니다. 그들은 당신에게 클리프사이드 성 부지에 암흑시대 테마파크를 건설해달라고 요청해왔습니다. -STR_SCNR :암흑 시대 - 로빈 후드 +STR_SCNR :암흑시대 - 로빈 후드 STR_PARK :셔우드 숲 STR_DTLS :부자들의 재산을 빼앗아 필요한 사람들에게 나누어주기 위해서 당신과 수하들은 셔우드 숲에 놀이공원을 만들기로 결정했습니다. STR_SCNR :미래 - 첫 만남 STR_PARK :외계인의 축제 -STR_DTLS :먼 행성에서 생명체가 발견되었습니다. 외계인을 주제로 한 놀이공원을 만들어서 전에 없던 뜨거운 관심을 받아 돈을 벌어보세요. +STR_DTLS :먼 행성에서 생명체가 발견되었습니다. 외계인을 주제로 한 놀이공원을 만들어서 전에 없던 관심을 받아 돈을 벌어보세요. STR_SCNR :미래 - 미래 세계 @@ -4471,7 +4499,7 @@ STR_DTLS :당신은 쥐라기 시대 놀이공원을 건설하는 임무를 STR_SCNR :선사시대 - 석기시대 STR_PARK :돌무더기 산책 -STR_DTLS :고속도로 개발자들을 막고 고대 열석을 보호하기 위해, 석기 시대 테마파크를 건설하여 수익을 내야 합니다. 하지만 사람이 살기 조금 힘든 곳인만큼 손님을 모으기가 어려울 것입니다. +STR_DTLS :고속도로 개발자들을 막고 고대 열석을 보호하기 위해, 석기 시대 테마파크를 건설하여 수익을 내야 합니다. 하지만 사람이 살기 조금 힘든 곳인 만큼 손님을 모으기가 어려울 것입니다. STR_SCNR :광란의 20년대 - 감옥 섬 diff --git a/data/language/nb-NO.txt b/data/language/nb-NO.txt index 57517d1942..b330007d93 100644 --- a/data/language/nb-NO.txt +++ b/data/language/nb-NO.txt @@ -552,8 +552,8 @@ STR_1167 :Kan ikke heve vannivå her... STR_1168 :Valg STR_1169 :(Ingenting) STR_1170 :{STRING} -STR_1171 :{RED}Stengt - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Stengt +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Bygg gangstier og køer STR_1174 :Skilt i veien STR_1175 :Kan ikke bygges på hellende gangsti @@ -1109,7 +1109,7 @@ STR_1726 :Land not for sale! STR_1727 :Construction rights not for sale! STR_1728 :Can't buy construction rights here... STR_1729 :Land not owned by park! -STR_1730 :{RED}Closed - - +STR_1730 :{RED}Closed STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Build STR_1733 :Mode @@ -2240,7 +2240,7 @@ STR_2977 :Staff member name STR_2978 :Enter new name for this member of staff: STR_2979 :Can't name staff member... STR_2980 :Too many banners in game -STR_2981 :{RED}No entry - - +STR_2981 :{RED}No entry STR_2982 :Banner text STR_2983 :Enter new text for this banner: STR_2984 :Can't set new text for banner... @@ -2419,7 +2419,6 @@ STR_3190 :Path Extras STR_3191 :Scenery Groups STR_3192 :Park Entrance STR_3193 :Water -STR_3194 :Scenario Description STR_3195 :Invention List STR_3196 :{WINDOW_COLOUR_2}Research Group: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Items pre-invented at start of game: @@ -3589,8 +3588,8 @@ STR_6125 :Objekttype STR_6126 :Ukjent type STR_6127 :Fil: {STRING} STR_6128 :Filen kunne ikke bli lastet, siden noen av objektene den refererer til mangler eller er korrupte. En liste av disse objektene følger. -STR_6129 :Kopier valgt element til utklippstavlen -STR_6130 :Kopier hele listen til utklippstavlen +STR_6129 :Kopier +STR_6130 :Kopier alt STR_6131 :Objektkilde STR_6132 :Ignorer forskningsstatus STR_6133 :{SMALLFONT}{BLACK}Gir tilgang til attraksjoner og pynt som ikke har blitt funnet opp enda @@ -3740,6 +3739,58 @@ STR_6276 :{RED}Gjester sitter fast i {STRINGID}, mulig grunnet ugyldig attrak STR_6277 :{WINDOW_COLOUR_2}Stasjonsindeks: {BLACK}{COMMA16} STR_6278 :Antall autolagringer STR_6279 :{SMALLFONT}{BLACK}Antall automatiske lagringer som blir beholdt +STR_6280 :{SMALLFONT}{BLACK}Chat +STR_6281 :{SMALLFONT}{BLACK}Vis en separat knapp for chattevinduet i verktøyjinjen +STR_6282 :Chat +STR_6283 :Chat not available at this time. Are you connected to a server? +STR_6283 :Chatten er for øyeblikket ikke tilgjengelig. Er du koblet til en server? +STR_6284 :Nettverk +STR_6285 :Nettverksinformasjon +STR_6286 :Motta +STR_6287 :Sende +STR_6288 :Totalt mottatt +STR_6289 :Totalt sendt +STR_6290 :Base protocol +STR_6291 :Kommandoer +STR_6292 :Kart +STR_6293 :B +STR_6294 :KiB +STR_6295 :MiB +STR_6296 :GiB +STR_6297 :TiB +STR_6298 :{STRING}/s +STR_6299 :Last ned alt +STR_6300 :{SMALLFONT}{BLACK}Last ned manglende objekter hvis de er tilgjengelige på nettet. +STR_6301 :{SMALLFONT}{BLACK}Kopier navnet til det valgte objektet til utklippstavlen. +STR_6302 :{SMALLFONT}{BLACK}Kopier hele listen manglende objekter til utklippstavlen. +STR_6303 :Laster ned objekt ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :Åpne pyntvelger +STR_6305 :Multithreading +STR_6306 :{SMALLFONT}{BLACK}Eksperimentelt valg for å bruke flere tråder til rendering. Kan gjøre spillet ustabilt. +STR_6307 :Fargeskjema: {BLACK}{STRINGID} +STR_6308 :“{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :Koble til på nytt +STR_6310 :{WINDOW_COLOUR_2}Posisjon: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Neste: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(flate) +STR_6313 :(bakke {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Destinasjon: {BLACK}{INT32}, {INT32} slingring {INT32} +STR_6315 :{WINDOW_COLOUR_2}Stifinner: {BLACK}{INT32}, {INT32}, {INT32} retning {INT32} +STR_6316 :{WINDOW_COLOUR_2}Stifinnerhistorie: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} retning {INT32} +STR_6318 :Mistet synkronisering med server.{NEWLINE}Loggfil: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Blokkbremser stengt +STR_6320 :{WINDOW_COLOUR_2}Kan ikke ødelegges +STR_6321 :{WINDOW_COLOUR_2}Gangstitillegg er ødelagt +STR_6322 :{WINDOW_COLOUR_2}Sprite-ID: {BLACK}{INT32} +STR_6323 :Simulerer +STR_6324 :Simuler +STR_6325 :{SMALLFONT}{BLACK}Simuler attraksjon +STR_6326 :Kan ikke simulere {POP16}{POP16}{POP16}{STRINGID}... +STR_6327 :Gjennomsiktig bakgrunn for gigantiske skjermbilder +STR_6328 :{SMALLFONT}{BLACK}Gigantiske skjermbilder blir lagret med gjennomsiktig bakgrunn istedenfor vanlig svart. +STR_6329 :{STRING}{STRINGID} +STR_6330 :Laster ned [{STRING}] fra {STRING} ({COMMA16} / {COMMA16}) ############# # Scenarios # diff --git a/data/language/nl-NL.txt b/data/language/nl-NL.txt index 30ec7939d1..73004bbf04 100644 --- a/data/language/nl-NL.txt +++ b/data/language/nl-NL.txt @@ -224,7 +224,7 @@ STR_0822 :Kan bestand met grafische gegevens niet openen STR_0823 :Ontbrekend of ontoegankelijk gegevensbestand STR_0824 :{BLACK}❌ STR_0825 :Gekozen naam is al in gebruik -STR_0826 :Teveel namen gedefinieerd +STR_0826 :Te veel namen gedefinieerd STR_0827 :Onvoldoende geld - {CURRENCY2DP} benodigd STR_0828 :{SMALLFONT}{BLACK}Venster sluiten STR_0829 :{SMALLFONT}{BLACK}Venstertitel - Sleep dit om het venster te verplaatsen @@ -365,7 +365,7 @@ STR_0983 :Onderzoek & Ontwikkeling STR_0984 :{WINDOW_COLOUR_2}▲{BLACK} {CURRENCY2DP} STR_0985 :{WINDOW_COLOUR_2}▼{BLACK} {CURRENCY2DP} STR_0986 :{BLACK}{CURRENCY2DP} -STR_0987 :Teveel attracties +STR_0987 :Te veel attracties STR_0988 :Kan geen nieuwe attractie aanmaken… STR_0989 :{STRINGID} STR_0990 :{SMALLFONT}{BLACK}Constructie @@ -549,8 +549,8 @@ STR_1167 :Kan het waterniveau hier niet verhogen… STR_1168 :Opties STR_1169 :(Geen) STR_1170 :{STRING} -STR_1171 :{RED}Gesloten - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Gesloten +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Voetpaden en wachtrijen aanleggen STR_1174 :Lichtkrant in de weg STR_1175 :Kan dit niet op hellend voetpad neerzetten @@ -1076,7 +1076,7 @@ STR_1695 :{SMALLFONT}{BLACK}Inkomsten en kosten STR_1696 :{SMALLFONT}{BLACK}Klantinformatie STR_1697 :Dit kan niet op wachtrijen worden geplaatst STR_1698 :Dit kan alleen op wachtrijen worden geplaatst -STR_1699 :Teveel personen in het spel +STR_1699 :Te veel personen in het spel STR_1700 :Klusjesman aannemen… STR_1701 :Monteur aannemen… STR_1702 :Bewaker aannemen… @@ -1084,7 +1084,7 @@ STR_1703 :Entertainer aannemen… STR_1704 :Kan geen nieuwe werknemer aannemen… STR_1705 :{SMALLFONT}{BLACK}Deze werknemer ontslaan STR_1706 :{SMALLFONT}{BLACK}Deze persoon naar een andere locatie verplaatsen -STR_1707 :Teveel werknemers in dit park +STR_1707 :Te veel werknemers in dit park STR_1708 :{SMALLFONT}{BLACK}Werkgebied van deze werknemer instellen STR_1709 :Werknemer ontslaan STR_1710 :Ja @@ -1107,7 +1107,7 @@ STR_1726 :Dit land is niet te koop! STR_1727 :Voor dit land zijn geen bouwrechten te koop! STR_1728 :Kan hier geen bouwrechten kopen… STR_1729 :Land is geen eigendom van het park! -STR_1730 :{RED}Gesloten - - +STR_1730 :{RED}Gesloten STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Bouwen STR_1733 :Modus @@ -2234,8 +2234,8 @@ STR_2976 :{SMALLFONT}{BLACK}Met het geselecteerde kleurenschema een onderdeel STR_2977 :Naam werknemer STR_2978 :Voer een nieuwe naam in voor deze werknemer: STR_2979 :Kan de naam van deze werknemer niet veranderen… -STR_2980 :Teveel lichtkranten in dit park -STR_2981 :{RED}Geen toegang - - +STR_2980 :Te veel lichtkranten in dit park +STR_2981 :{RED}Geen toegang STR_2982 :Lichtkranttekst STR_2983 :Voer een nieuwe tekst in voer deze lichtkrant: STR_2984 :Kan de tekst op deze lichtkrant niet veranderen… @@ -2403,7 +2403,7 @@ STR_3164 :{BLACK}{COMMA16} geselecteerd (maximum: {COMMA16}) STR_3167 :{WINDOW_COLOUR_2}Bevat {BLACK}{COMMA16} objecten STR_3169 :Data voor de volgende objecten niet gevonden: STR_3170 :Niet genoeg ruimte voor graphics. -STR_3171 :er zijn teveel objecten van dit type geselecteerd. +STR_3171 :er zijn te veel objecten van dit type geselecteerd. STR_3172 :De volgende objecten moeten eerst geselecteerd worden: STR_3173 :dit object is momenteel in gebruik. STR_3174 :een ander object is afhankelijk van dit object. @@ -2426,7 +2426,6 @@ STR_3190 :Extra's voor paden STR_3191 :Decorgroepen STR_3192 :Parkingang STR_3193 :Water -STR_3194 :Scenariobeschrijving STR_3195 :Lijst van uitvindingen STR_3196 :{WINDOW_COLOUR_2}Onderzoeksgroep: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Items die aan het begin al zijn uitgevonden: @@ -2459,7 +2458,7 @@ STR_3223 :{SMALLFONT}{BLACK}Instellen welk land gekocht kan worden door het p STR_3224 :{SMALLFONT}{BLACK}Instellen voor welk land bouwrechten te koop zijn STR_3225 :{SMALLFONT}{BLACK}Een willekeurig cluster van dit object neerzetten rondom de geselecteerde plek STR_3226 :{SMALLFONT}{BLACK}Parkingang bouwen -STR_3227 :Teveel parkingangen! +STR_3227 :Te veel parkingangen! STR_3228 :{SMALLFONT}{BLACK}Geef startposities voor bezoekers aan STR_3229 :Blokremmen kunnen niet direct na het station geplaatst worden STR_3230 :Blokremmen kunnen niet direct achter elkaar gebruikt worden @@ -2575,7 +2574,7 @@ STR_3343 :Opgeslagen spel omzetten naar scenario STR_3344 :Baanontwerper STR_3345 :Baanontwerpbeheer STR_3346 :Kan baanontwerp niet opslaan -STR_3347 :Attractie is te groot, bevat teveel elementen of het decor is te ver verspreid +STR_3347 :Attractie is te groot, bevat te veel elementen of het decor is te ver verspreid STR_3348 :Hernoemen STR_3349 :Verwijderen STR_3350 :Naam baanontwerp @@ -2600,7 +2599,7 @@ STR_3370 :{BLACK}= Informatiekiosk STR_3371 :{BLACK}= Eerste hulp STR_3372 :{BLACK}= Geldautomaat STR_3373 :{BLACK}= Toilet -STR_3374 :Waarschuwing: teveel objecten geselecteerd! +STR_3374 :Waarschuwing: te veel objecten geselecteerd! STR_3375 :Niet alle objecten in deze decorgroep kunnen worden geselecteerd STR_3376 :Installeren… STR_3377 :{SMALLFONT}{BLACK}Bestand met nieuw baanontwerp installeren @@ -2765,7 +2764,7 @@ STR_5256 :Maak een nieuw thema aan om wijzigingen aan te brengen. STR_5257 :{SMALLFONT}{BLACK}Een nieuw thema aanmaken dat is gebaseerd op het huidige STR_5258 :{SMALLFONT}{BLACK}Het huidige thema verwijderen STR_5259 :{SMALLFONT}{BLACK}Het huidige thema hernoemen -STR_5260 :Enorm screenshot +STR_5260 :Reuzenscreenshot STR_5261 :Filter STR_5262 :Wacky Worlds STR_5263 :Time Twister @@ -3232,7 +3231,7 @@ STR_5762 :Chinese yuan (CN¥) STR_5763 :Alle bestanden STR_5764 :Ongeldig attractietype STR_5765 :Kan attracties met een ongeldig type niet bewerken. -STR_5766 : +STR_5766 :Hongaarse forint (Ft) STR_5767 :Inkomsten STR_5768 :Totaal aant. bez. STR_5769 :Totale winst @@ -3767,6 +3766,35 @@ STR_6300 :{SMALLFONT}{BLACK}Download alle ontbrekende objecten, mits ze onlin STR_6301 :{SMALLFONT}{BLACK}Kopieert de naam van het geselecteerde object naar het klembord. STR_6302 :{SMALLFONT}{BLACK}Kopieert de lijst van ontbrekende objecten naar het klembord. STR_6303 :Bezig met downloaden van object ({COMMA16} van {COMMA16}): [{STRING}] +STR_6304 :Decorselector openen +STR_6305 :Multithreading +STR_6306 :{SMALLFONT}{BLACK}Experimentele optie om met meerdere threads te renderen. Dit kan de snelheid verhogen, maar ook instabiliteit veroorzaken. +STR_6307 :Kleurenschema: {BLACK}{STRINGID} +STR_6308 :“{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :Opnieuw verbinden +STR_6310 :{WINDOW_COLOUR_2}Positie: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Volgende: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(oppervlak) +STR_6313 :(helling {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Bestemming: {BLACK}{INT32}, {INT32} tolerantie {INT32} +STR_6315 :{WINDOW_COLOUR_2}Pathfind-doel: {BLACK}{INT32}, {INT32}, {INT32} richting {INT32} +STR_6316 :{WINDOW_COLOUR_2}Pathfind-geschiedenis: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} richting {INT32} +STR_6318 :Netwerkdesynchronisatie gedetecteerd.{NEWLINE}Logbestand: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Blokrem is gesloten +STR_6320 :{WINDOW_COLOUR_2}Mag niet worden verwijderd +STR_6321 :{WINDOW_COLOUR_2}Straatmeubel is kapot +STR_6322 :{WINDOW_COLOUR_2}Sprite-id: {BLACK}{INT32} +STR_6323 :Simulatie +STR_6324 :Simuleren +STR_6325 :{SMALLFONT}{BLACK}Testrit simuleren +STR_6326 :Kan {POP16}{POP16}{POP16}{STRINGID} niet in simulatiemodus zetten... +STR_6327 :Reuzenscreenshots hebben transparante achtergrond +STR_6328 :{SMALLFONT}{BLACK}Als deze optie is ingeschakeld, worden reuzenscreenshots met een transparante achtergrond genomen, in plaats van de zwarte standaardachtergrond. +STR_6329 :{STRING}{STRINGID} +STR_6330 :[{STRING}] wordt gedownload van {STRING} ({COMMA16} / {COMMA16}) +STR_6331 :Eenden aanmaken +STR_6332 :Eenden verw. ############# # Scenarios # diff --git a/data/language/pt-BR.txt b/data/language/pt-BR.txt index b3158db882..20fa85a636 100644 --- a/data/language/pt-BR.txt +++ b/data/language/pt-BR.txt @@ -10,7 +10,7 @@ STR_0005 :Montanha-Russa Invertida STR_0006 :Montanha-Russa Junior STR_0007 :Miniferrovia STR_0008 :Monotrilho -STR_0009 :Mini-Montanha Suspensa +STR_0009 :Minimontanha Suspensa STR_0010 :Aluguel de Barcos STR_0011 :Rato Maluco de Madeira STR_0012 :Corrida de Obstáculos @@ -47,7 +47,7 @@ STR_0042 :Gira-Gira STR_0043 :Anéis Espaciais STR_0044 :Montanha de Queda-Livre Reversa STR_0045 :Elevador -STR_0046 :Montanha-Russa de Queda Vertical +STR_0046 :Montanha-Russa de Queda Vertical STR_0047 :Caixa Eletrônico STR_0048 :Twist STR_0049 :Casa Mal-Assombrada @@ -64,7 +64,7 @@ STR_0059 :Montanha-Russa Voadora STR_0060 :Brinquedo Desconhecido (3A) STR_0061 :Virginia Reel STR_0062 :Barcos de Splash -STR_0063 :Mini Helicópteros +STR_0063 :Mini-Helicópteros STR_0064 :Montanha-Russa Deitada STR_0065 :Monotrilho Suspenso STR_0066 :Brinquedo Desconhecido (40) @@ -90,7 +90,7 @@ STR_0085 :Brinquedo Desconhecido (53) STR_0086 :Brinquedo Desconhecido (54) STR_0087 :Brinquedo Desconhecido (55) STR_0088 :Montanha de Impulso Invertida -STR_0089 :Mini Montanha-Russa +STR_0089 :Minimontanha-Russa STR_0090 :Brinquedo de Mina STR_0091 :Brinquedo Desconhecido (59) STR_0092 :Montanha-Russa de Indução Linear @@ -145,10 +145,10 @@ STR_0562 :Carros motorizados percorrem uma pista de vários níveis passando STR_0563 :Sentados em trens confortáveis apenas com uma trava simples, passageiros divertem-se em grandes quedas suaves e trilhos retorcidos como também muitos 'tempo no ar' pelas colinas STR_0564 :Correndo em trilho de madeira, esta montanha-russa é rápida, selvagem, barulhenta, e dá a sensação de 'fora de controle' com muitos 'tempo no ar' STR_0565 :Uma montanha-russa de madeira simples capaz de fazer só ladeiras e curvas suaves, onde os carros são mantidos no trilho pela fricção lateral e gravidade -STR_0566 :Carros individuais de montanha-russa percorrem em alta velocidade pistas em zig-zag com curvas fechadas e pequenas quedas +STR_0566 :Carros individuais de montanha-russa percorrem em alta velocidade pistas em zigue-zague com curvas fechadas e pequenas quedas STR_0567 :Sentados em cadeiras suspensas de cada lado do trilho, passageiros são lançados para cima enquanto são puxados para baixo em quedas íngremes e viajam por várias inversões STR_0569 :Passeando embaixo do trilho em travas especiais, passageiros tem a sensação de voo enquanto arrebatam pelo ar -STR_0571 :Carros circulares giram enquanto viajam por uma pista de madeira em zig-zag +STR_0571 :Carros circulares giram enquanto viajam por uma pista de madeira em zigue-zague STR_0572 :Barcos de alta capacidade viajam por um largo canal d'água, subindo por uma correia transportadora, acelerando ladeira abaixo para molhar os passageiros com uma onda gigante STR_0573 :Carros motorizados em formato de helicópteros correm por um trilho de aço, controlados pelo pedalar dos passageiros STR_0574 :Passageiros são segurados por uma trava especial na posição deitada, viajando por trilhos retorcidos e inversões tanto de costas quanto olhando para o chão @@ -157,14 +157,14 @@ STR_0577 :Carros correm em trilhos de madeira, virando em seções reversoras STR_0578 :Carros correm em trilhos envolvidos por anéis, passando por quedas íngremes e giros na 'linha do coração' STR_0579 :Um jogo suave de golfe em miniatura STR_0580 :Uma montanha-russa de aço gigante capaz de quedas suaves e colinas maiores que 90m -STR_0581 :Um anel de assentos é puxado para o topo de uma alta torre enquando gira lentamente, então desce em queda livre, parando suavemente na parte inferior usando freios magnéticos +STR_0581 :Um anel de assentos é puxado para o topo de uma alta torre enquanto gira lentamente, então desce em queda livre, parando suavemente na parte inferior usando freios magnéticos STR_0582 :Veículos aerobarcos guiados pelos passageiros STR_0583 :Prédio contendo salas tortas e corredores inclinados para desorientar as pessoas que andam por eles STR_0584 :Bicicletas especiais correm em uma pista de aço de monotrilho, controladas pelo pedalar dos passageiros STR_0585 :Passageiros sentam em um par de cadeiras suspensas abaixo do trilho enquanto fazem loops e giros por inversões estreitas STR_0586 :Carros em formato de barcos correm em um trilho de montanha-russa que permite curvas retorcidas e quedas íngremes, esparrinhando água em seções aquáticas que viram um rio suave. STR_0587 :Depois de um lançamento de propulsão hidráulica emocionante, o trem acelerado sobe verticalmente, passa pelo topo, e desce verticalmente ao outro lado para voltar à estação -STR_0588 :Carros individuais correm por baixo de um trilho em zig-zag com curvas fechadas e quedas acentuadas +STR_0588 :Carros individuais correm por baixo de um trilho em zigue-zague com curvas fechadas e quedas acentuadas STR_0589 :Um grande carro tematizado de tapete voador que move para cima e para baixo ciclicamente no extremo de 4 braços STR_0590 :Passageiros passeiam em um submarino submergido por um percurso subaquático STR_0591 :Barcos em forma de balsas serpenteiam delicadamente em uma pista de rio @@ -292,7 +292,7 @@ STR_0908 :Giro/Inclinação STR_0909 :Rot. Banco STR_0910 :{SMALLFONT}{BLACK}Curva em 'S' à Esquerda STR_0911 :{SMALLFONT}{BLACK}Curva em 'S' à Direita -STR_0912 :{SMALLFONT}{BLACK}Sem retorção +STR_0912 :{SMALLFONT}{BLACK}Sem volta STR_0913 :{SMALLFONT}{BLACK}Mover para a seção anterior STR_0914 :{SMALLFONT}{BLACK}Mover para a próxima seção STR_0915 :{SMALLFONT}{BLACK}Construir seção destacada @@ -521,7 +521,7 @@ STR_1137 :{SMALLFONT}{BLACK}Selecione a cor adicional 1 STR_1138 :{SMALLFONT}{BLACK}Selecione a cor adicional 2 STR_1139 :{SMALLFONT}{BLACK}Selecione a cor do suporte das estruturas STR_1140 :{SMALLFONT}{BLACK}Selecione a opção do esquema de cor do veículo -STR_1141 :{SMALLFONT}{BLACK}Selecione o veiculo/trem para modificar +STR_1141 :{SMALLFONT}{BLACK}Selecione o veículo/trem para modificar STR_1142 :{MOVE_X}{SMALLFONT}{STRINGID} STR_1143 :»{MOVE_X}{SMALLFONT}{STRINGID} STR_1144 :Impossível construir/mover a entrada dessa atração... @@ -551,8 +551,8 @@ STR_1167 :Impossível aumentar o nível da água aqui... STR_1168 :Opções STR_1169 :(Nenhum) STR_1170 :{STRING} -STR_1171 :{RED}Fechado - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Fechado +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Construir calçadas e filas de espera STR_1174 :Banner está no caminho STR_1175 :Impossível construir isto na calçada inclinada @@ -724,7 +724,7 @@ STR_1340 :{WINDOW_COLOUR_2}Velocidade máx.: {BLACK}{VELOCITY} STR_1341 :{WINDOW_COLOUR_2}Duração da atração: {BLACK}{STRINGID}{STRINGID}{STRINGID}{STRINGID} STR_1342 :{DURATION} STR_1343 :{DURATION} / -STR_1344 :{WINDOW_COLOUR_2}Estensão do percurso: {BLACK}{STRINGID}{STRINGID}{STRINGID}{STRINGID} +STR_1344 :{WINDOW_COLOUR_2}Extensão do percurso: {BLACK}{STRINGID}{STRINGID}{STRINGID}{STRINGID} STR_1345 :{LENGTH} STR_1346 :{LENGTH} / STR_1347 :{WINDOW_COLOUR_2}Velocidade média: {BLACK}{VELOCITY} @@ -738,7 +738,7 @@ STR_1354 :{WINDOW_COLOUR_2}Altura da queda mais alta: {BLACK}{LENGTH} STR_1355 :{WINDOW_COLOUR_2}Quedas: {BLACK}{COMMA16} STR_1356 :{WINDOW_COLOUR_2}Inversões: {BLACK}{COMMA16} STR_1357 :{WINDOW_COLOUR_2}Túneis: {BLACK}{COMMA16} -STR_1358 :{WINDOW_COLOUR_2}'Tempo no Ar' Total: {BLACK}{COMMA2DP32}segs +STR_1358 :{WINDOW_COLOUR_2}'Tempo no Ar' Total: {BLACK}{COMMA2DP32}s STR_1359 :{WINDOW_COLOUR_2}Tempo de espera: {BLACK}{COMMA16} minuto STR_1360 :{WINDOW_COLOUR_2}Tempo de espera: {BLACK}{COMMA16} minutos STR_1361 :Impossível mudar velocidade... @@ -1079,7 +1079,7 @@ STR_1697 :Impossível colocar estes na fila de espera STR_1698 :Somente possível colocar estes na fila de espera STR_1699 :Muitas pessoas no jogo STR_1700 :Contratar novo faxineiro -STR_1701 :Contratar novo mecânico +STR_1701 :Contratar novo mecânico STR_1702 :Contratar novo segurança STR_1703 :Contratar novo animador STR_1704 :Impossível contratar novos funcionários... @@ -1108,7 +1108,7 @@ STR_1726 :Região não está à venda! STR_1727 :Direitos de construção não está à venda! STR_1728 :Não pode comprar os direitos de construção aqui... STR_1729 :Os terrenos não são propriedade do parque! -STR_1730 :{RED}Fechado - - +STR_1730 :{RED}Fechado STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Construir STR_1733 :Modo @@ -1300,7 +1300,7 @@ STR_1929 :{RED}{STRINGID} ainda não foi consertado{NEWLINE}Verifique aonde s STR_1930 :{SMALLFONT}{BLACK}Ligar/Desligar informações de rastreamento para este visitante - (Se o rastreamento está ligado, movimentos do visitante serão reportados na área de mensagem) STR_1931 :{STRINGID} entrou na fila de espera para {STRINGID} STR_1932 :{STRINGID} está no {STRINGID} -STR_1933 :{STRINGID} esta no {STRINGID} +STR_1933 :{STRINGID} está no {STRINGID} STR_1934 :{STRINGID} saiu do {STRINGID} STR_1935 :{STRINGID} saiu do parque STR_1936 :{STRINGID} comprou {STRINGID} @@ -1671,7 +1671,7 @@ STR_2316 :{WINDOW_COLOUR_2}Espaço necessário: {BLACK}{COMMA16} x {COMMA16} STR_2321 :{WINDOW_COLOUR_2}Número de atrações: {BLACK}{COMMA16} STR_2322 :{WINDOW_COLOUR_2}Funcionário: {BLACK}{COMMA16} STR_2323 :{WINDOW_COLOUR_2}Tamanho do parque: {BLACK}{COMMA32}m² -STR_2324 :{WINDOW_COLOUR_2}Tamanho do parque: {BLACK}{COMMA32}sq.ft. +STR_2324 :{WINDOW_COLOUR_2}Tamanho do parque: {BLACK}{COMMA32}pés². STR_2325 :{SMALLFONT}{BLACK}Comprar terreno para aumentar o tamanho do parque STR_2326 :{SMALLFONT}{BLACK}Comprar os direitos de construção para permitir a construção acima ou abaixo do terreno fora do parque STR_2327 :Opções @@ -1685,7 +1685,7 @@ STR_2334 :Libras (£) STR_2335 :Dólar ($) STR_2336 :Franco (F) STR_2337 :Marco Alemão(DM) -STR_2338 :Yen (¥) +STR_2338 :Iene (¥) STR_2339 :Peseta (Pts) STR_2340 :Lira (L) STR_2341 :Florim Neerlandês (ƒ) @@ -1807,7 +1807,7 @@ STR_2468 :{SMALLFONT}{BLACK}Mostrar prêmios recentes que este parque recebeu STR_2469 :{SMALLFONT}{BLACK}Escolha um nível de pesquisa e desenvolvimento STR_2470 :{SMALLFONT}{BLACK}Pesquisar novas atrações de transporte STR_2471 :{SMALLFONT}{BLACK}Pesquisar novas atrações tranquilas -STR_2472 :{SMALLFONT}{BLACK}Pesquisar novas montanhas-russas +STR_2472 :{SMALLFONT}{BLACK}Pesquisar novas montanhas-russas STR_2473 :{SMALLFONT}{BLACK}Pesquisar novas atrações emocionantes STR_2474 :{SMALLFONT}{BLACK}Pesquisar novas atrações aquáticas STR_2475 :{SMALLFONT}{BLACK}Pesquisar novas lojas e barracas @@ -2052,19 +2052,19 @@ STR_2716 :/ STR_2717 :' STR_2718 :Voltar Pasta STR_2719 :Novo Arquivo -STR_2720 :{UINT16}seg -STR_2721 :{UINT16}segs -STR_2722 :{UINT16}min:{UINT16}seg -STR_2723 :{UINT16}min:{UINT16}segs -STR_2724 :{UINT16}mins:{UINT16}seg -STR_2725 :{UINT16}mins:{UINT16}segs +STR_2720 :{UINT16}s +STR_2721 :{UINT16}s +STR_2722 :{UINT16}min:{UINT16}s +STR_2723 :{UINT16}min:{UINT16}s +STR_2724 :{UINT16}mins:{UINT16}s +STR_2725 :{UINT16}mins:{UINT16}s STR_2726 :{UINT16}min STR_2727 :{UINT16}mins STR_2728 :{UINT16}hora:{UINT16}min STR_2729 :{UINT16}hora:{UINT16}mins STR_2730 :{UINT16}horas:{UINT16}min STR_2731 :{UINT16}horas:{UINT16}mins -STR_2732 :{COMMA16}ft +STR_2732 :{COMMA16}pés STR_2733 :{COMMA16}m STR_2734 :{COMMA16}mph STR_2735 :{COMMA16}km/h @@ -2168,7 +2168,7 @@ STR_2840 :{TOPAZ}Seu parque foi premiado por ter a pior comida do país! STR_2841 :{TOPAZ}Seu parque foi premiado por ter as melhores instalações sanitárias do país! STR_2842 :{TOPAZ}Seu parque foi premiado por ser o mais decepcionante do país! STR_2843 :{TOPAZ}Seu parque foi premiado por ter as melhores atrações aquáticas do país! -STR_2844 :{TOPAZ}Seu parque foi premiado por ter as melhores atrações pesonalizadas! +STR_2844 :{TOPAZ}Seu parque foi premiado por ter as melhores atrações personalizadas! STR_2845 :{TOPAZ}Seu parque foi premiado por ter a escolha de esquemas de cores mais deslumbrante! STR_2846 :{TOPAZ}Seu parque foi premiado por ter o traçado mais confuso'! STR_2847 :{TOPAZ}Seu parque recebeu um prêmio por ser 'O parque com as melhores atrações tranquilas'! @@ -2237,7 +2237,7 @@ STR_2977 :Nome do Funcionário STR_2978 :Inserir novo nove para este funcionário: STR_2979 :Impossível nomear funcionário... STR_2980 :Muitos banners no jogo -STR_2981 :{RED}Entrada Proibida - - +STR_2981 :{RED}Entrada Proibida STR_2982 :Texto do banner STR_2983 :Insira um novo texto para este banner: STR_2984 :Impossível definir novo texto para banner... @@ -2341,7 +2341,7 @@ STR_3097 :{SMALLFONT}{BLACK}Selecione a velocidade da corrente de elevação STR_3099 :{SMALLFONT}{BLACK}Selecionar cor STR_3100 :{SMALLFONT}{BLACK}Selecionar segunda cor STR_3101 :{SMALLFONT}{BLACK}Selecionar terceira cor -STR_3102 :{SMALLFONT}{BLACK}Repintar cenário colorível no terreno +STR_3102 :{SMALLFONT}{BLACK}Repintar cenário coloreável no terreno STR_3103 :Impossível repintar isto... STR_3104 :{SMALLFONT}{BLACK}Listar atrações STR_3105 :{SMALLFONT}{BLACK}Listar lojas e barracas @@ -2397,7 +2397,7 @@ STR_3173 :Este objeto está atualmente em uso STR_3174 :Este objeto é requisitado por outro objeto STR_3175 :Este objeto é sempre requisitado STR_3176 :Impossível selecionar este objeto -STR_3177 :Impossível desselecionar este objeto +STR_3177 :Impossível deselecionar este objeto STR_3178 :Pelo menos um caminho deve ser selecionado STR_3179 :Pelo menos um objeto de atração/veículo deve ser selecionado STR_3180 :Seleção de objetos inválida @@ -2414,7 +2414,6 @@ STR_3190 :Caminhos Extras STR_3191 :Grupo de Cenários STR_3192 :Entrada do Parque STR_3193 :Água -STR_3194 :Descrição do Cenário STR_3195 :Lista de Invenções STR_3196 :{WINDOW_COLOUR_2}Grupo de Pesquisa: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Itens pré-inventados no começo do jogo: @@ -2579,7 +2578,7 @@ STR_3359 :{BLACK}Nenhum projeto de pista deste tipo de atração STR_3360 :Aviso! STR_3361 :Muitos projetos de pista deste tipo de atração - Alguns não serão listados. STR_3364 :Avançado -STR_3365 :{SMALLFONT}{BLACK}Permite seleção de itens individuais do cenário, além dos grupos de cenário +STR_3365 :{SMALLFONT}{BLACK}Permite seleção de itens individuais do cenário, além dos grupos de cenário STR_3366 :{BLACK}= Atração STR_3367 :{BLACK}= Barraca de Comida STR_3368 :{BLACK}= Barraca de Bebida @@ -2589,7 +2588,7 @@ STR_3371 :{BLACK}= Primeiros Socorros STR_3372 :{BLACK}= Caixa Eletrônico STR_3373 :{BLACK}= Banheiro STR_3374 :Aviso: Muitos objetos selecionados! -STR_3375 :Nem todos os objetos neste grupo de cenário podem ser selecionado +STR_3375 :Nem todos os objetos neste grupo de cenário podem ser selecionados STR_3376 :Instalar novo STR_3377 :{SMALLFONT}{BLACK}Instalar um novo arquivo de projeto de pista STR_3378 :Instalar @@ -2677,7 +2676,7 @@ STR_5182 :{INT32} STR_5183 :Altura da base STR_5184 :Insira altura da base entre {COMMA16} e {COMMA16} STR_5185 :Nível da água -STR_5186 :Insira o nível da água entre {COMMA16} e {COMMA16} +STR_5186 :Insira o nível da água entre {COMMA16} e {COMMA16} STR_5187 :Finanças STR_5188 :Nova Campanha STR_5189 :Pesquisa @@ -2751,7 +2750,7 @@ STR_5256 :Criar um novo tema para fazer mudanças STR_5257 :{SMALLFONT}{BLACK}Cria um novo tema baseado no atual STR_5258 :{SMALLFONT}{BLACK}Exclui o tema atual STR_5259 :{SMALLFONT}{BLACK}Renomeia o tema atual -STR_5260 :Captura Gigante de Tela +STR_5260 :Captura Gigante de Tela STR_5261 :Filtrar STR_5262 :Wacky Worlds STR_5263 :Time Twister @@ -3215,6 +3214,7 @@ STR_5762 :Yuan Chinês(CN¥) STR_5763 :Todos os arquivos STR_5764 :Tipo de atração inválida STR_5765 :Não é possível editar atração de tipo inválido +STR_5766 :Florim húngaro (Ft) STR_5767 :Receita STR_5768 :Clientes totais STR_5769 :Lucro total @@ -3282,7 +3282,7 @@ STR_5830 :{SMALLFONT}{BLACK}Muda qual língua é usada STR_5831 :{SMALLFONT}{BLACK}Muda qual formato de{NEWLINE}temperatura é mostrado no jogo STR_5832 :{SMALLFONT}{BLACK}Mostra altura como unidade genérica ao invés de formato de medida definido em "Distância e Velocidade" STR_5833 :{SMALLFONT}{BLACK}Muda qual formato de data é usado -STR_5834 :{SMALLFONT}{BLACK}Seleciona qual dispositivo de audio o OpenRCT2 irá usar +STR_5834 :{SMALLFONT}{BLACK}Seleciona qual dispositivo de áudio o OpenRCT2 irá usar STR_5835 :{SMALLFONT}{BLACK}Deixa o jogo mudo se a janela perder foco STR_5836 :{SMALLFONT}{BLACK}Seleciona música que será usada no menu principal.{NEWLINE}Selecionar o tema do RCT1 requer que você copie 'data/css17.dat' da pasta do seu RCT1 para 'data/css50.dat' da pasta do seu RCT2, ou definir o caminho para o RCT1 na aba Variados. STR_5837 :{SMALLFONT}{BLACK}Cria e gerencia temas de interface de usuário personalizadas @@ -3552,13 +3552,13 @@ STR_6101 :Atrações não perdem valor ao longo do{NEWLINE}tempo STR_6102 :{SMALLFONT}{BLACK}O valor de uma atração não diminuirá com o tempo, então visitantes não pensarão de repente que uma atração é muito cara. STR_6103 :{SMALLFONT}{BLACK}Esta opção é desabilitada durante jogo de rede. STR_6104 :Montanha-Russa Saca-Rolhas -STR_6105 :Hiper Montanha-Russa +STR_6105 :Hipermontanha-Russa STR_6106 :Passeio de carro STR_6107 :Caminhões Monstruosos STR_6108 :Tornado de Aço -STR_6109 :Hiper Tornado +STR_6109 :Hipertornado STR_6110 :Montanha-Russa Júnior -STR_6111 :Mini Montanha-Russa Clássica +STR_6111 :Minimontanha-Russa Clássica STR_6112 :Uma montanha russa de aço compacta onde trens viajam por parafusos e loops STR_6113 :Uma montanha russa alta que não inverte com grandes quedas, alta velocidade, e trens confortáveis com apenas uma trava de colo STR_6114 :Passageiros viajam devagar em veículos eletrizados ao longo de uma rota baseada em pista @@ -3613,7 +3613,7 @@ STR_6162 :Rato Maluco Giratório STR_6163 :Carros no formato de rato correm por curvas fechadas e pequenas quedas, girando suavemente para desorientar os passageiros STR_6164 :{WHITE}❌ STR_6165 :Usar sincronização vertical -STR_6166 :{SMALLFONT}{BLACK}Sincroniza cada quadro mostrado com a taxa de atualização do monitor, previnindo cortes na tela. +STR_6166 :{SMALLFONT}{BLACK}Sincroniza cada quadro mostrado com a taxa de atualização do monitor, prevenindo cortes na tela. STR_6167 :{SMALLFONT}{BLACK}Avançado STR_6168 :Sequência de Título STR_6169 :Seleção de Cenários @@ -3745,12 +3745,41 @@ STR_6294 :KiB STR_6295 :MiB STR_6296 :GiB STR_6297 :TiB -STR_6298 :{STRING}/seg +STR_6298 :{STRING}/s STR_6299 :Baixar todos STR_6300 :{SMALLFONT}{BLACK}Baixa todos os objetos faltantes se disponíveis online. STR_6301 :{SMALLFONT}{BLACK}Copia o nome do objeto selecionado para a área de transferência. STR_6302 :{SMALLFONT}{BLACK}Copia toda a lista de objetos faltantes para a área de transferência. STR_6303 :Baixando objeto ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :Abrir seletor de cenário +STR_6305 :Multithreading +STR_6306 :{SMALLFONT}{BLACK}Opção experimental que usa múltiplas threads para desenhar, pode causar instabilidade. +STR_6307 :Esquema de cores: {BLACK}{STRINGID} +STR_6308 :“{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :Reconectar +STR_6310 :{WINDOW_COLOUR_2}Posição: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Próxima: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(superfície) +STR_6313 :(inclinação {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Dest: {BLACK}{INT32}, {INT32} tolerância {INT32} +STR_6315 :{WINDOW_COLOUR_2}Objetivo de Desbravamento: {BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6316 :{WINDOW_COLOUR_2}Histórico de Desbravamento: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} dir {INT32} +STR_6318 :Assincronia de rede detectada.{NEWLINE}Arquivo de registro: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Freios Bloqueadores Fechados +STR_6320 :{WINDOW_COLOUR_2}Indestrutível +STR_6321 :{WINDOW_COLOUR_2}Adição quebrada +STR_6322 :{WINDOW_COLOUR_2}Id da Sprite: {BLACK}{INT32} +STR_6323 :Simulando +STR_6324 :Simular +STR_6325 :{SMALLFONT}{BLACK}Simular brinquedo/atração +STR_6326 :Não é possível simular {POP16}{POP16}{POP16}{STRINGID}.. +STR_6327 :Fundo transparente para capturas de tela gigantes +STR_6328 :{SMALLFONT}{BLACK}Com essa opção habilitada, capturas de tela gigantes terão fundo transparente em vez da cor preta padrão. +STR_6329 :{STRING}{STRINGID} +STR_6330 :Baixando [{STRING}] de {STRING} ({COMMA16} / {COMMA16}) +STR_6331 :Criar Patos +STR_6332 :Remover Patos ############# # Scenarios # @@ -3795,7 +3824,7 @@ STR_DTLS :Algumas ilhas formam a base para este novo parque. STR_SCNR :Mundo de Katie STR_PARK :Mundo de Katie -STR_DTLS :Um pequeno parque temático com alguns briquedos e espaço para expansão - Seu objetivo é dobrar o valor do parque. +STR_DTLS :Um pequeno parque temático com alguns brinquedos e espaço para expansão - Seu objetivo é dobrar o valor do parque. STR_SCNR :Parque Pequeno @@ -3835,7 +3864,7 @@ STR_DTLS :Converta a atração turística Ruínas Egípcias em um próspero p STR_SCNR :Madeiras Velhas STR_PARK :Madeiras Velhas -STR_DTLS :Um grande parque com atrações bem projetadas porém muito velhas - Substitua as atrações velhas ou adicione novas para tornar o parque mais popular. +STR_DTLS :Um grande parque com atrações bem projetadas, porém muito velhas - Substitua as atrações velhas ou adicione novas para tornar o parque mais popular. STR_SCNR :Cais do Paraíso @@ -3926,7 +3955,7 @@ STR_DTLS :Uma série de lagos íngremes formam a base para este novo parque. STR_SCNR :Parque Animado STR_PARK :Parque Animado -STR_DTLS :Este parque ancião tem muitas atrações históricas mas está muito endividado. +STR_DTLS :Este parque ancião tem muitas atrações históricas, mas está muito endividado. STR_SCNR :Áreas Mágicas @@ -4006,7 +4035,7 @@ STR_DTLS :Transforme esta pedreira abandonada em um lugar para atrair turista STR_SCNR :Encostas Loucas STR_PARK :Encostas Loucas -STR_DTLS :Você tem fundos limitados mas tempo ilimitado para transformar esta área montanhosa em um vasto parque de montanha-russa. +STR_DTLS :Você tem fundos limitados, mas tempo ilimitado para transformar esta área montanhosa em um vasto parque de montanha-russa. STR_SCNR :Parque Urbano @@ -4033,7 +4062,7 @@ STR_DTLS :Um vulcão dormente é o ambiente para este desafio de construção STR_SCNR :Picos Áridos STR_PARK :Picos Áridos -STR_DTLS :Livre de qualquer limite financeiro,seu desafio é desenvolver este parque desértico enquanto mantém os visitantes felizes. +STR_DTLS :Livre de qualquer limite financeiro, seu desafio é desenvolver este parque desértico enquanto mantém os visitantes felizes. STR_SCNR :Pedras de Navalha @@ -4048,7 +4077,7 @@ STR_DTLS :Um grande lago numa cratera anciã é o ambiente para este parque. STR_SCNR :Visões de Vertigem STR_PARK :Visões de Vertigem -STR_DTLS :Este grande parque já tem uma excelente montanha-russa hiper, mas sua tarefa é aumentar massivamente seu lucro. +STR_DTLS :Este grande parque já tem uma excelente hipermontanha-russa, mas sua tarefa é aumentar massivamente seu lucro. STR_SCNR :Cais do Paraíso 2 @@ -4380,7 +4409,7 @@ STR_DTLS :Um parque precariamente aninhado em pedras de lava com vapores de m STR_SCNR :Lago da Sorte STR_PARK :Lago da Sorte -STR_DTLS :Com fundos ilimitados mas com uma localização desafiadora no lago, este parque será um desafio para expandir e gerenciar +STR_DTLS :Com fundos ilimitados, mas com uma localização desafiadora no lago, este parque será um desafio para expandir e gerenciar STR_SCNR :Cúpula do Arco-Íris @@ -4433,7 +4462,7 @@ STR_DTLS :Uma barreira foi construída oferecendo energia hidroelétrica bara STR_SCNR :Antártica - Resgate Ecológico STR_PARK :Aventuras Geladas -STR_DTLS :A agência de ambiente voltou-se para você para transformar uma grande velha refinaria ecológica de petróleo em uma atração turística de alto nível. Terreno é barato mas o juros do empréstimo é alto. Você pode vender os prédios velhos para melhorar a situação. +STR_DTLS :Ajude a agência de ambiente a transformar uma grande e velha refinaria ecológica de petróleo em atração turística de alto nível. Terrenos baratos, mas juros do empréstimo não. Pode-se vender os prédios velhos para melhorar a situação. STR_SCNR :Ásia - Melhorias Turísticas na Grande Muralha da China @@ -4478,7 +4507,7 @@ STR_DTLS :As pessoas do Havaí estão entediadas de surfar e estão procurand STR_SCNR :América do Norte - Grand Canyon STR_PARK :Calamidades do Canyon -STR_DTLS :Você tem que construir um parque num terreno limitado nos dois lados desse tesouro natural - você tem a oportunidade de comprar terrenos vizinhos dos Índios Nativos Americanos. Você precisa completar o objetivo para suprir a população da cidade local. +STR_DTLS :Você tem que construir um parque num terreno limitado nos dois lados desse tesouro natural - tenha a oportunidade de comprar terrenos vizinhos dos Índios Nativos Americanos. Você precisa completar o objetivo para suprir a população da cidade local. STR_SCNR :América do Norte - Paraíso da Montanha-Russa @@ -4516,7 +4545,7 @@ STR_DTLS :Para liberar a riqueza dos ricos e distribuir para os necessitados, STR_SCNR :Futuro - Primeiro Contado STR_PARK :Extravagância Extraterrestre -STR_DTLS :Vida foi descoberta em um planeta distante. Construa um parque temático alien para ganhar dinheiro com a onda de juros sem precedentes. +STR_DTLS :Vida foi descoberta em um planeta distante. Construa um parque temático alienígena para ganhar dinheiro com a onda de juros sem precedentes. STR_SCNR :Futuro - Mundo Futurista @@ -4526,7 +4555,7 @@ STR_DTLS :Mostre sua inventiva, utópica visão do futuro - mostre um projeto STR_SCNR :Mitológico - Set de Filmagem Animatrônico STR_PARK :Travessuras Animatrônicas -STR_DTLS :Você recebeu uma tarefa de gerir e melhorar um parque temático existente, que foi construído num velho set de filme. Construa um tributo para os animadores pioneiros de stop-motion que foram os primeiros a trazer criaturas míticas à vida na tela prateada. +STR_DTLS :Você foi designado para gerir e melhorar um parque temático existente, que foi construído num velho set de filme. Faça um tributo aos animadores pioneiros de stop-motion, os primeiros a trazer criaturas míticas à vida na tela prateada. STR_SCNR :Mitológico - Berço da Civilização @@ -4546,7 +4575,7 @@ STR_DTLS :Você recebeu a tarefa de construir um parque temático da Era Jur STR_SCNR :Pré-Histórico - Idade da Pedra STR_PARK :Passeio Rochoso -STR_DTLS :Para frustrar os desenvolvedores da rodovia e preservar os misteriosos círculos de pedra antigos, você precisará construir um parque temático da Idade da Pedra e obter lucro. No entanto, atrair visitantes pode representar um desafio, já que o terreno é um pouco inóspito. +STR_DTLS :Frustre os construtores da rodovia e preserve os misteriosos círculos de pedra antigos ao construir um parque temático da Idade da Pedra e lucrar. No entanto, atrair visitantes pode ser desafiador, já que o terreno é um pouco inóspito. STR_SCNR :Anos 20 - Ilha-Prisão diff --git a/data/language/sv-SE.txt b/data/language/sv-SE.txt index 3257ed5c64..372aec33b2 100644 --- a/data/language/sv-SE.txt +++ b/data/language/sv-SE.txt @@ -4,147 +4,147 @@ # Use # at the beginning of a line to leave a comment. STR_0000 : STR_0001 :{STRINGID} {COMMA16} -STR_0002 :Spiral berg- och dalbana +STR_0002 :Spiralberg- och dalbana STR_0003 :Stående berg- och dalbana -STR_0004 :Hängande svängande bana +STR_0004 :Hängande svängbana STR_0005 :Inverterad berg- och dalbana -STR_0006 :Junior berg- och dalbana -STR_0007 :Miniatyr-tåg +STR_0006 :Juniorberg- och dalbana +STR_0007 :Miniatyrtåg STR_0008 :Balkbana -STR_0009 :Liten hängande bana -STR_0010 :Båttur -STR_0011 :Vilda musen i trä +STR_0009 :Liten upphängd bana +STR_0010 :Båtuthyrning +STR_0011 :Vildmus i trä STR_0012 :Hinderbana -STR_0013 :Bilåktur -STR_0014 :Fritt Fall +STR_0013 :Biltur +STR_0014 :Fritt fall med uppskjut STR_0015 :Bobslädesbana STR_0016 :Observationstorn STR_0017 :Loopande berg- och dalbana STR_0018 :Jollebana STR_0019 :Gruvtåg STR_0020 :Stolslift -STR_0021 :Korkskruvs berg- och dalbana +STR_0021 :Korkskruvsberg- och dalbana STR_0022 :Labyrint -STR_0023 :Spiral Rutschkana -STR_0024 :Go Karts -STR_0025 :Stock Forsen +STR_0023 :Spiralrutschkana +STR_0024 :Gokart +STR_0025 :Stockfors STR_0026 :Forsränning STR_0027 :Radiobilar STR_0028 :Piratskepp -STR_0029 :Svängande omvänt Piratskepp +STR_0029 :Svängande omvänt piratskepp STR_0030 :Matstånd -STR_0031 :Okänt Stånd (1D) +STR_0031 :Okänt stånd (1D) STR_0032 :Dryckesstånd -STR_0033 :Okänt Stånd (1F) +STR_0033 :Okänt stånd (1F) STR_0034 :Stånd STR_0035 :Karusell -STR_0036 :Okänt Stånd (22) -STR_0037 :Kiosk -STR_0038 :Toalett +STR_0036 :Okänt stånd (22) +STR_0037 :Informationskiosk +STR_0038 :Toaletter STR_0039 :Pariserhjul STR_0040 :Rörelsesimulator -STR_0041 :3D Bio -STR_0042 :Top Spin +STR_0041 :3D-bio +STR_0042 :Top spin STR_0043 :Rymdringar STR_0044 :Bakvänd fritt fall-bana STR_0045 :Lift -STR_0046 :Vertikalt Stup +STR_0046 :Vertikalt stup STR_0047 :Bankomat STR_0048 :Twister -STR_0049 :Spökhuset -STR_0050 :Första Hjälpen -STR_0051 :Cirkusföreställning -STR_0052 :Spöktåget -STR_0053 :Hyper- och dalbana +STR_0049 :Spökhus +STR_0050 :Första hjälpen +STR_0051 :Cirkus +STR_0052 :Spöktåg +STR_0053 :Hyperberg- och dalbana STR_0054 :Berg- och dalbana i trä -STR_0055 :Sidofriktion berg- och dalbana -STR_0056 :Vilda Musen +STR_0055 :Sidfriktionsberg- och dalbana +STR_0056 :Vildmus i stål STR_0057 :Flerdimensionell berg- och dalbana -STR_0058 :Okänd Åktur (38) -STR_0059 :Inverterad berg- och dalbana -STR_0060 :Okänd Åktur (3A) -STR_0061 :Virginiavirveln +STR_0058 :Okänd åktur (38) +STR_0059 :Flygande berg- och dalbana +STR_0060 :Okänd åktur (3A) +STR_0061 :Virginiavirvel STR_0062 :Plaskbåtar -STR_0063 :Mini Helikoptrar +STR_0063 :Minihelikoptrar STR_0064 :Liggande berg- och dalbana STR_0065 :Hängande balkbana -STR_0066 :Okänd Åktur (40) -STR_0067 :Vändberg- och dalbana -STR_0068 :Skruvbana -STR_0069 :Mini Golf -STR_0070 :Giga- och dalbana -STR_0071 :Roto-Fall -STR_0072 :Flygande Tefat -STR_0073 :Lustiga Huset -STR_0074 :Enspårs-cyklar -STR_0075 :Kompakt Omvänd Bana +STR_0066 :Okänd åktur (40) +STR_0067 :Vändande berg- och dalbana +STR_0068 :Skruvberg- och dalbana +STR_0069 :Minigolf +STR_0070 :Gigaberg- och dalbana +STR_0071 :Roto-fall +STR_0072 :Flygande tefat +STR_0073 :Lustiga huset +STR_0074 :Enspårscyklar +STR_0075 :Kompakt omvänd berg- och dalbana STR_0076 :Vattenbana -STR_0077 :Luftdriven Vertikal Bana -STR_0078 :Inverterad Hårnålsbana -STR_0079 :Flygande Mattan -STR_0080 :Ubåts Bana +STR_0077 :Luftdriven vertikal bana +STR_0078 :Inverterad hårnålsbana +STR_0079 :Flygande matta +STR_0080 :Ubåtsbana STR_0081 :Flodflottar -STR_0082 :Okänd Åktur (50) +STR_0082 :Okänd åktur (50) STR_0083 :Enterprise -STR_0084 :Okänd Åktur (52) -STR_0085 :Okänd Åktur (53) -STR_0086 :Okänd Åktur (54) -STR_0087 :Okänd Åktur (55) -STR_0088 :Inverterad Impulsbana -STR_0089 :Mini berg- och dalbana +STR_0084 :Okänd åktur (52) +STR_0085 :Okänd åktur (53) +STR_0086 :Okänd åktur (54) +STR_0087 :Okänd åktur (55) +STR_0088 :Inverterad impulsbana +STR_0089 :Miniberg- och dalbana STR_0090 :Gruvattraktion -STR_0091 :Okänd Åktur (59) -STR_0092 :Induktion berg- och dalbana -STR_0512 :En kompakt berg- och dalbana med en spiral lyftkedja och mjuka, vridna stup. +STR_0091 :Okänd åktur (59) +STR_0092 :Induktionsberg- och dalbana +STR_0512 :En kompakt berg- och dalbana med en spiralvriden kedjalift och mjuka, vridna stup. STR_0513 :En ringlande berg- och dalbana där passagerarna åker stående -STR_0514 :Hängande tåg under banan svingar ut åt sidan i svängar -STR_0515 :En stål berg- och dalbana med tåg som hålls under spåren, med många vridna och spännande delar -STR_0516 :En snäll berg- och dalbana för de som inte har mod nog att utmana större åkturer -STR_0517 :Passagerare åker i miniatyrtåg på en smal tågräls +STR_0514 :Tåg hängande under banan som svingar ut åt sidan i svängarna +STR_0515 :En berg- och dalbana i stål med många spännande och kurviga spårsektioner och tåg som hänger under spåren +STR_0516 :En snäll berg- och dalbana för dem som inte har mod nog att utmana större åkturer +STR_0517 :Passagerare åker i miniatyrtåg på smalspårig räls STR_0518 :Passagerare åker i elektriska tåg på en balkbana STR_0519 :Passagerare åker i små vagnar som hänger under en enkelspårig bana, fritt svingande från sida till sida i svängar -STR_0520 :En plattform där gästerna kan köra/ro vattenfartyg på vatten -STR_0521 :En snabb och tvistande berg- och dalbana med skarpa kurvor. Intensiteten kommer vara hög. +STR_0520 :En brygga varifrån besökarna kan köra eller ro vattenfarkoster på en vattensamling +STR_0521 :En snabb och svängande berg- och dalbana med skarpa kurvor. Intensiteten kommer vara hög. STR_0522 :En mindre berg- och dalbana där passagerarna sitter på spåret utan ett fordon runt dem -STR_0523 :Passagerare åker sakta i motoriserade fordon på en spår-baserad väg -STR_0524 :Fritt fall konstruerade fordon är uppskjutna med lufttryck upp för ett högt stål torn innan den rusar ner igen i fritt fallande stil -STR_0525 :Passagerarna dundrar nerför ett slingrande spår som styrs enbart av det halvcirkelformade spårets kurvatur och lutning -STR_0526 :Roterande utsiktskabin som åker upp för ett högt torn -STR_0527 :En mjuk berg- och dalbana byggd av stål som kan göra vertikala loopar -STR_0528 :Passagerarna åker i uppblåsbara jollar ner för ett skruvat halvrunt eller helt slutet rör -STR_0529 :Gruvtåg berg- och dalbana - tågen rusar fram längst stålskenor som ser ut som riktiga gamla järnvägsspår -STR_0530 :Vagnar hänger från en stålkabel som kör kontinuerligt från ena sidan åkturen till den andra, och sedan tillbaka igen -STR_0531 :En kompakt stål berg- och dalbana där tågen åker längs korkskruvar och loopar -STR_0532 : -STR_0533 : -STR_0534 :Kör själv- gokarts som är bensindrivna -STR_0535 :Stockformade båtar som åker längs en lång vattenkanal, plaskar ner längs branta lutningar för att blöta ner passagerarna -STR_0536 :Runda båtar snirklar sig längst en lång bred vattenkanal, plaskar genom vattenfall och för de nervösa passagerarna genom skummande forsar -STR_0537 : -STR_0538 : -STR_0539 : -STR_0540 : -STR_0542 : -STR_0544 : -STR_0545 : -STR_0547 : -STR_0548 : -STR_0549 : -STR_0550 : -STR_0551 : -STR_0552 : -STR_0553 : -STR_0554 :Vagnen accelereras ut ur stationen på en platt bana med hjälp av linjärmotorer, sen åker den vertikalt uppåt tills gravitationen tar över och vagnen åker tillbaka till stationen -STR_0555 :Gästerna åker en hiss vertikalt upp eller ner till olika våningar -STR_0556 :Extra breda vagnar går ner för fullständigt lodräta spår för den ultimata fritt fall-upplevelsen i berg- och dalbanan -STR_0557 : -STR_0558 : -STR_0559 : -STR_0560 : -STR_0561 : +STR_0523 :Passagerare åker sakta i motorfordon på en spårlagd väg +STR_0524 :Fordonet skjuts uppför ett högt stål torn med lufttryck för att sedan rusa ner i fritt fall +STR_0525 :Passagerarna dundrar nerför ett slingrande spår som styrs enbart av det halvcirkelformade spårets krökning och lutning +STR_0526 :Passagerarna åker i en roterande utsiktskabin som åker uppför ett högt torn +STR_0527 :En mjukgående berg- och dalbana med stålräls som kan göra vertikala loopar +STR_0528 :Passagerarna åker i uppblåsbara jollar nerför ett slingrande halvöppet eller helt slutet rör +STR_0529 :Berg- och dalbanetåg med gruvtema rusar fram längst stålskenor som ser ut som riktiga gamla järnvägsspår +STR_0530 :Vagnar hänger från en stålkabel som kör kontinuerligt från ena sidan till den andra och tillbaks igen +STR_0531 :En kompakt berg- och dalbana av stål där tågen åker genom korkskruvar och loopar +STR_0532 :Labyrinten består av två meter höga häckar eller murar och besökarna får leta sig fram genom den för att hitta utgången +STR_0533 :Träbyggnad med en trappa på insidan och en spiralvriden rutschkana på utsidan som åks på med glidmattor +STR_0534 :Kör bensindrivna gokarter +STR_0535 :Stockliknande båtar som åker längs en lång vattenkanal och plaskar nerför branta sluttningar för att blöta ner passagerarna +STR_0536 :Runda båtar snirklar sig fram längs en lång bred vattenkanal, plaskar genom vattenfall och för passagerarna genom skummande forsar +STR_0537 :Kör elektriska radiobilar +STR_0538 :Stort krängande piratskepp +STR_0539 :Ett skepp, fäst vid en arm med motvikt på motsatt sida, som svänger hela varvet runt +STR_0540 :Ett stånd där besökare kan köpa mat +STR_0542 :Ett stånd där besökare kan köpa dryck +STR_0544 :Ett stånd som säljer souvenirer +STR_0545 :Klassisk snurrande karusell med snidade trähästar +STR_0547 :Ett stånd där besökare kan skaffa parkkartor och köpa paraplyer +STR_0548 :En toalettbyggnad +STR_0549 :Stort snurrande hjul med öppna stolar +STR_0550 :Passagerarna tittar på en film inuti rörelsesimulatorn som samtidigt snurras och flyttas runt på en hydraulisk arm +STR_0551 :Biograf som visar 3D-film i en byggnad med geodetisk kupol +STR_0552 :Passagerarna åker i en gondol, upphängd av långa roterande armar, och snurras handlöst framåt och bakåt +STR_0553 :Koncentriskt snurrande ringar låter passageraren rotera fritt i alla riktningar +STR_0554 :Vagnen accelereras ut ur stationen på en platt bana med hjälp av linjärmotorer, sedan åker den vertikalt uppåt tills gravitationen tar över och vagnen åker tillbaka till stationen +STR_0555 :Besökarna åker en hiss vertikalt upp eller ner till olika våningar +STR_0556 :Extra breda vagnar åker utför på helt lodräta spår för den ultimata fritt fall-upplevelsen i berg- och dalbanan +STR_0557 :En bankomat besökarna kan använda om finanserna börjar sina +STR_0558 :Passagerarna åker parvis i stolar som snurrar kring ändarna på tre långa roterande armar +STR_0559 :Stor scenisk byggnad med skräckinjagande korridorer och kusliga rum +STR_0560 :En plats dit krassliga besökare kan gå för at återhämta sig snabbare +STR_0561 :En djurföreställning i ett stort cirkustält STR_0562 :Motordrivna monsterformade vagnar som åker längst ett flernivåspår förbi spöklika scener och specialeffekter STR_0563 :Sittandes i bekväma vagnar njuter passagerare av enorma nedförsbackar och spännande svängar, samt mycket tid flygandes över topparna -STR_0564 :Den här berg- och dalbanan i trä är snabb, tuff, högljudd, och ger en känsla av att tappa kontrollen. Dessutom spenderar man mycket tid flygandes över topparna +STR_0564 :Den här berg- och dalbanan i trä är snabb, tuff, högljudd och ger en känsla av att förlora kontrollen. Dessutom spenderar man mycket tid flygandes över topparna STR_0565 :En enkel berg- och dalbana i trä som bara har snälla backar och svängar. Vagnarna hålls på banan med sidofriktion på hjulen och gravitation STR_0566 :Individuella vagnar åker runt i en tät sicksack-bana med skarpa svängar och korta branta nedförsbackar STR_0567 :Passagerare sitter i säten på varsin sida av banan och vänds upp och ner medan de åker i branta fall och olika inversioner @@ -153,28 +153,28 @@ STR_0571 :Runda vagnar snurrar runt medan de åker längst ett sicksack spår STR_0572 :Högkapacitetsbåtar färdas längst en bred vattenkanal, drivs upp för lutningar av transportband, accelererar ner för branter för att blöta ner passagerarna med ett jätteplask STR_0573 :Motordrivna helikopterformade vagnar kör på ett stålspår och styrs med pedaler av passagerarna STR_0574 :Passagerarna hålls fast i speciella seldon i liggande läge, åker genom ett tvinnat spår och håller dem antingen på rygg eller med ansiktet mot marken -STR_0575 :Motoriserade tåg som hänger från en enkelspårig bana transporterar folk runt om i parken +STR_0575 :Motordrivna tåg som hänger från en enkelspårig bana transporterar folk runt om i parken STR_0577 :Boggivagnar kör på ett träspår, vänder runt på speciella vändsektioner STR_0578 :Vagnar åker runt en bana omgiven av ringar utför branta nedförsbackar och skruvar -STR_0579 : -STR_0580 :En gigantisk stål berg- och dalbana som klarar mjuka fall och höjder på över 100 m -STR_0581 :En ring med säten dras upp till toppen av ett högt torn medan det roterar sakta, därefter faller det fritt varefter det stoppas mjukt vid botten med hjälp av magnetbromsar -STR_0582 : -STR_0583 : -STR_0584 :Speciella cyklar åker på en stålskensbana, glidande fram av passagerarens cyklande +STR_0579 :En stillsam runda minigolf +STR_0580 :En gigantisk stål berg- och dalbana som klarar mjuka fall och höjder på över 100 meter +STR_0581 :En ring med säten dras upp till toppen av ett högt torn medan det sakta roterar för att sedan falla fritt och stoppas mjukt vid botten med hjälp av magnetbromsar +STR_0582 :Kör svävare +STR_0583 :Byggnad med förvrängda rum och vinklade korridorer för att förvilla människorna som går igenom den +STR_0584 :Speciella cyklar åker på en stålskensbana, framdrivna av passagerarens cyklande STR_0585 :Passagerarna sitter i par med ansiktena antingen bakåt eller framåt då de åker genom loopar och skruvar med snabba omkastningar STR_0586 :Båtformade vagnar åker på berg- och dalbanespår med snurriga svängar och branta nedförsbackar, men plumsar då och då ner i lugna vattensektioner STR_0587 :Efter en luftdriven snabbstart susar vagnarna uppför och nedför en vertikal bana innan de återvänder till stationen STR_0588 :Vagnarna kör på ett träspår som lutar farligt över då de korsar hårnålssvängar och skarpa nedgångar -STR_0589 : +STR_0589 :En stor flygande matta som upprepande hissas upp och ner på fyra armar STR_0590 :Passagerarna åker i en nedsänkt ubåt genom en undervattensbana STR_0591 :Flottformade båtar som lugnt snirklar sig runt ett flodspår -STR_0593 : -STR_0598 :Omvända berg- och dalbanevagnar som accelereras ut från stationen kör upp för ett lodrät brant torn, vänder sedan och åker baklänges genom stationen upp för ett annan lodrät brant torn -STR_0599 :En kompakt berg- och dalbana med individuella vagnar och mjuka snurrade nedförsbackar -STR_0600 :Motordrivna gruvtåg rusar längst en mjuk tvinnad spårlayout +STR_0593 :Snurrande hjul med upphängda passagerarkapslar som först börjar snurra och sedan reses upp av en arm +STR_0598 :Omvända berg- och dalbanevagnar som accelereras ut från stationen för att åka uppför en lodrät brant, vända och sedan åka baklänges genom stationen och uppför ett annat lodrätt torn +STR_0599 :En kompakt berg- och dalbana med individuella vagnar och mjukt vridna nedförsbackar +STR_0600 :Motordrivna gruvtåg rusar längst en mjuk och kurvig spårlayout STR_0602 :Berg- och dalbanetågen accelereras ut från stationen med induktionsmotorer för att fara genom tvinnande inversioner -STR_0767 :Gäst {INT32} +STR_0767 :Besökare {INT32} STR_0768 :Vaktmästare {INT32} STR_0769 :Mekaniker {INT32} STR_0770 :Säkerhetsvakt {INT32} @@ -212,33 +212,33 @@ STR_0806 :28 STR_0807 :29 STR_0808 :30 STR_0809 :31 -STR_0810 :Jan -STR_0811 :Feb -STR_0812 :Mar -STR_0813 :Apr -STR_0814 :Maj -STR_0815 :Jun -STR_0816 :Jul -STR_0817 :Aug -STR_0818 :Sep -STR_0819 :Okt -STR_0820 :Nov -STR_0821 :Dec +STR_0810 :jan +STR_0811 :feb +STR_0812 :mar +STR_0813 :apr +STR_0814 :maj +STR_0815 :jun +STR_0816 :jul +STR_0817 :aug +STR_0818 :sep +STR_0819 :okt +STR_0820 :nov +STR_0821 :dec STR_0822 :Kan inte komma åt grafikfilen STR_0823 :Saknad eller otillgänglig datafil STR_0824 :{BLACK}❌ STR_0825 :Det valda namnet används redan STR_0826 :För många namn är definierade -STR_0827 :Inte tillräckligt med pengar - det behövs {CURRENCY2DP} +STR_0827 :Inte tillräckligt med pengar – {CURRENCY2DP} behövs STR_0828 :{SMALLFONT}{BLACK}Stäng fönster -STR_0829 :{SMALLFONT}{BLACK}Fönstertitel - Dra för att flytta fönstret +STR_0829 :{SMALLFONT}{BLACK}Fönstertitel – Dra för att flytta fönstret STR_0830 :{SMALLFONT}{BLACK}Zooma in STR_0831 :{SMALLFONT}{BLACK}Zooma ut STR_0832 :{SMALLFONT}{BLACK}Rotera vyn 90° medurs STR_0833 :{SMALLFONT}{BLACK}Pausa spelet -STR_0834 :{SMALLFONT}{BLACK}Inställningar -STR_0839 :{UINT16} x {UINT16} -STR_0840 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{UINT16} x {UINT16} +STR_0834 :{SMALLFONT}{BLACK}Spel- och lagringsinställningar +STR_0839 :{UINT16} × {UINT16} +STR_0840 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{UINT16} × {UINT16} STR_0847 :Om 'OpenRCT2' STR_0850 :{WINDOW_COLOUR_2}Copyright © 2002 Chris Sawyer, alla rättigheter förbehålls STR_0851 :{WINDOW_COLOUR_2}Designat och programmerat av Chris Sawyer @@ -260,22 +260,22 @@ STR_0873 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{STRINGID} STR_0874 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{STRINGID} STR_0875 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{STRINGID} STR_0876 :{BLACK}▼ -STR_0877 :För lågt ! -STR_0878 :För högt ! -STR_0879 :Kan inte sänka landnivån här... -STR_0880 :Kan inte höja landnivån här... +STR_0877 :För lågt! +STR_0878 :För högt! +STR_0879 :Kan inte sänka landnivån här … +STR_0880 :Kan inte höja landnivån här … STR_0881 :Ett objekt är i vägen -STR_0882 :Ladda Spel -STR_0883 :Spara Spel -STR_0884 :Ladda Landskap -STR_0885 :Spara Landskap -STR_0887 :Avsluta Scenarioskaparen -STR_0888 :Avsluta Åktur Designer -STR_0889 :Avsluta Spårredigeraren +STR_0882 :Ladda spel +STR_0883 :Spara spel +STR_0884 :Ladda landskap +STR_0885 :Spara landskap +STR_0887 :Avsluta scenarioredigeraren +STR_0888 :Avsluta åktursdesignern +STR_0889 :Avsluta spårdesignredigeraren STR_0891 :Skärmdump STR_0892 :Skärmdumpen sparades som '{STRINGID}' STR_0893 :Skärmdumpen misslyckades! -STR_0894 :Landskapens minnesområde är fullt ! +STR_0894 :Landskapens minnesutrymme är fullt! STR_0895 :Kan inte bygga både ovan och under jord samtidigt STR_0896 :{POP16}{POP16}{STRINGID} Konstruktion STR_0897 :Riktning @@ -283,18 +283,18 @@ STR_0898 :{SMALLFONT}{BLACK}Vänsterkurva STR_0899 :{SMALLFONT}{BLACK}Högerkurva STR_0900 :{SMALLFONT}{BLACK}Vänsterkurva (liten radie) STR_0901 :{SMALLFONT}{BLACK}Högerkurva (liten radie) -STR_0902 :{SMALLFONT}{BLACK}Vänsterkurva (väldigt liten radie) -STR_0903 :{SMALLFONT}{BLACK}Högerkurva (väldigt liten radie) +STR_0902 :{SMALLFONT}{BLACK}Vänsterkurva (mycket liten radie) +STR_0903 :{SMALLFONT}{BLACK}Högerkurva (mycket liten radie) STR_0904 :{SMALLFONT}{BLACK}Vänsterkurva (stor radie) STR_0905 :{SMALLFONT}{BLACK}Högerkurva (stor radie) STR_0906 :{SMALLFONT}{BLACK}Rakt STR_0907 :Sluttning -STR_0908 :Lutning +STR_0908 :Dosering STR_0909 :Säteslutning -STR_0910 :{SMALLFONT}{BLACK}Lutning för Vänsterkurva -STR_0911 :{SMALLFONT}{BLACK}Lutning för Högerkurva -STR_0912 :{SMALLFONT}{BLACK}Ingen lutning -STR_0913 :{SMALLFONT}{BLACK}Tidigare sektion +STR_0910 :{SMALLFONT}{BLACK}Dosering för vänsterkurva +STR_0911 :{SMALLFONT}{BLACK}Dosering för högerkurva +STR_0912 :{SMALLFONT}{BLACK}Ingen dosering +STR_0913 :{SMALLFONT}{BLACK}Föregående sektion STR_0914 :{SMALLFONT}{BLACK}Nästa sektion STR_0915 :{SMALLFONT}{BLACK}Bygg den valda sektionen STR_0916 :{SMALLFONT}{BLACK}Ta bort den valda sektionen @@ -307,34 +307,34 @@ STR_0922 :{SMALLFONT}{BLACK}Brant uppförsbacke STR_0923 :{SMALLFONT}{BLACK}Vertikalt uppåt STR_0924 :{SMALLFONT}{BLACK}Helix nedåt STR_0925 :{SMALLFONT}{BLACK}Helix uppåt -STR_0926 :Kan inte ta bort det här... -STR_0927 :Kan inte bygga här... +STR_0926 :Kan inte ta bort det här … +STR_0927 :Kan inte bygga här … STR_0928 :{SMALLFONT}{BLACK}Lyftkedjor, för att dra vagnar uppför backar -STR_0929 :'S'-Böj (vänster) -STR_0930 :'S'-Böj (höger) -STR_0931 :Vertikal Loop (vänster) -STR_0932 :Vertikal Loop (höger) +STR_0929 :S-kurva (vänster) +STR_0930 :S-kurva (höger) +STR_0931 :Vertikal loop (vänster) +STR_0932 :Vertikal loop (höger) STR_0933 :Höj eller sänk landnivån först STR_0934 :En åkturs ingång är i vägen STR_0935 :En åkturs utgång är i vägen STR_0936 :Parkentrén är i vägen STR_0937 :{SMALLFONT}{BLACK}Vyinställningar -STR_0938 :{SMALLFONT}{BLACK}Justera landnivån och sluttning +STR_0938 :{SMALLFONT}{BLACK}Justera landnivå och sluttning STR_0939 :Underjordsvy STR_0940 :Dölj terräng STR_0941 :Dölj vertikala sidor -STR_0942 :Genomskinliga Åkturer -STR_0943 :Genomskinliga Dekorationer +STR_0942 :Genomskinliga åkturer +STR_0943 :Genomskinliga dekorationer STR_0944 :Spara STR_0945 :Spara inte STR_0946 :Avbryt -STR_0947 :Spara innan du laddar ? -STR_0948 :Spara innan du avslutar ? -STR_0949 :Spara innan du avslutar ? -STR_0950 :Ladda Spel -STR_0951 :Avsluta Spel -STR_0952 :Avsluta Spel -STR_0953 :Ladda Landskap +STR_0947 :Spara innan du laddar? +STR_0948 :Spara innan du avslutar? +STR_0949 :Spara innan du avslutar? +STR_0950 :Ladda spel +STR_0951 :Avsluta spel +STR_0952 :Avsluta spel +STR_0953 :Ladda landskap STR_0954 : STR_0955 :{SMALLFONT}{BLACK}Välj säteslutningen för den här sektionen STR_0956 :-180° @@ -356,41 +356,41 @@ STR_0971 :+495° STR_0972 :Avbryt STR_0973 :OK STR_0974 :Åkturer -STR_0975 :Affärer och Stånd -STR_0976 :Toaletter och Informationskiosker -STR_0977 :Nya Transportåkturer -STR_0978 :Nya Lugna Åkturer -STR_0979 :Nya Berg- och Dalbanor -STR_0980 :Nya Spännande Åkturer -STR_0981 :Nya Vattenåkturer -STR_0982 :Nya Affärer & Stånd -STR_0983 :Forskning & Utveckling +STR_0975 :Affärer och stånd +STR_0976 :Toaletter och informationskiosker +STR_0977 :Nya transportåkturer +STR_0978 :Nya lugna åkturer +STR_0979 :Nya berg- och dalbanor +STR_0980 :Nya spännande åkturer +STR_0981 :Nya vattenåkturer +STR_0982 :Nya affärer och stånd +STR_0983 :Forskning och utveckling STR_0984 :{WINDOW_COLOUR_2}▲{BLACK} {CURRENCY2DP} STR_0985 :{WINDOW_COLOUR_2}▼{BLACK} {CURRENCY2DP} STR_0986 :{BLACK}{CURRENCY2DP} -STR_0987 :För många åkturer/attraktioner -STR_0988 :Kan inte skapa ny åktur/attraktion... +STR_0987 :För många åkturer och attraktioner +STR_0988 :Kan inte skapa ny åktur eller attraktion … STR_0989 :{STRINGID} STR_0990 :{SMALLFONT}{BLACK}Konstruktion STR_0991 :Stationsplattform -STR_0992 :{SMALLFONT}{BLACK}Riv hela åkturen/attraktionen -STR_0993 :Riv åktur/attraktion +STR_0992 :{SMALLFONT}{BLACK}Riv hela åkturen eller attraktionen +STR_0993 :Riv åktur eller attraktion STR_0994 :Riv -STR_0995 :{WINDOW_COLOUR_1}Är du säker på att du vill riva {STRINGID} fullständigt? +STR_0995 :{WINDOW_COLOUR_1}Är du säker på att du vill riva {STRINGID} helt? STR_0996 :Översiktsvy STR_0997 :{SMALLFONT}{BLACK}Visa markering STR_0998 :Inga fler stationer är tillåtna för den här åkturen STR_0999 :En stationsplattform krävs STR_1000 :Banan är inte ihopkopplad STR_1001 :Banan är ej lämplig för vagntypen -STR_1002 :Kan inte öppna {POP16}{POP16}{POP16}{STRINGID}... -STR_1003 :Kan inte testa {POP16}{POP16}{POP16}{STRINGID}... -STR_1004 :Kan inte stänga {POP16}{POP16}{POP16}{STRINGID}... -STR_1005 :Kan inte påbörja byggnadsarbetet på {POP16}{POP16}{POP16}{STRINGID}... +STR_1002 :Kan inte öppna {POP16}{POP16}{POP16}{STRINGID} … +STR_1003 :Kan inte testa {POP16}{POP16}{POP16}{STRINGID} … +STR_1004 :Kan inte stänga {POP16}{POP16}{POP16}{STRINGID} … +STR_1005 :Kan inte påbörja byggnadsarbetet på {POP16}{POP16}{POP16}{STRINGID} … STR_1006 :Måste vara stängd först STR_1007 :Kan inte skapa tillräckligt med vagnar -STR_1008 :{SMALLFONT}{BLACK}Öppna, stäng eller testa åktur/attraktion -STR_1009 :{SMALLFONT}{BLACK}Öppna eller stäng alla åkturer/attraktioner +STR_1008 :{SMALLFONT}{BLACK}Öppna, stäng eller testa åktur eller attraktion +STR_1009 :{SMALLFONT}{BLACK}Öppna eller stäng alla åkturer och attraktioner STR_1010 :{SMALLFONT}{BLACK}Öppna eller stäng parken STR_1011 :Stäng alla STR_1012 :Öppna alla @@ -398,10 +398,10 @@ STR_1013 :Stäng parken STR_1014 :Öppna parken STR_1015 :Åkturen kan inte köras med fler än en stationsplattform i detta läge STR_1016 :Åkturen kan inte köras med mindre än två stationer i detta läge -STR_1017 :Kan inte byta körläge... -STR_1018 :Kan inte göra förändringar... -STR_1019 :Kan inte göra förändringar... -STR_1020 :Kan inte göra förändringar... +STR_1017 :Kan inte byta körläge … +STR_1018 :Kan inte göra förändringar … +STR_1019 :Kan inte göra förändringar … +STR_1020 :Kan inte göra förändringar … STR_1021 :{POP16}{POP16}{POP16}{POP16}{STRINGID} STR_1022 :{POP16}{POP16}{POP16}{COMMA16} vagn per tåg STR_1023 :{POP16}{POP16}{POP16}{COMMA16} vagnar per tåg @@ -416,37 +416,37 @@ STR_1031 :Kan inte bygga detta under vatten! STR_1032 :Kan endast bygga detta på vatten! STR_1033 :Kan endast bygga detta ovanför land! STR_1034 :Kan endast bygga detta på land! -STR_1035 :Lokala myndigheter tillåter inte konstruktion ovanför trähöjd! -STR_1036 :Ladda Spel -STR_1037 :Ladda Landskap +STR_1035 :Lokala myndigheter tillåter inte konstruktion ovanför trädhöjd! +STR_1036 :Ladda spel +STR_1037 :Ladda landskap STR_1038 :Omvandla sparat spel till scenario STR_1039 :Installera ny bandesign -STR_1040 :Spara Spel -STR_1041 :Spara Scenario -STR_1042 :Spara Landskap -STR_1043 :OpenRCT2 Sparat Spel -STR_1044 :OpenRCT2 Sparat Scenario -STR_1045 :OpenRCT2 Sparat Landskap -STR_1046 :OpenRCT2 Sparad Bandesign +STR_1040 :Spara spel +STR_1041 :Spara scenario +STR_1042 :Spara landskap +STR_1043 :OpenRCT2 Sparat spel +STR_1044 :OpenRCT2 Sparat scenario +STR_1045 :OpenRCT2 Sparat landskap +STR_1046 :OpenRCT2 Sparad bandesign STR_1047 :Misslyckades med att spara spelet! STR_1048 :Misslyckades med att spara scenario! STR_1049 :Misslyckades med att spara landskap! -STR_1050 :Misslyckades att ladda...{NEWLINE}Filen innehåller ogiltig data! -STR_1051 :Dölj Stödpelare -STR_1052 :Dölj Besökare -STR_1053 :{SMALLFONT}{BLACK}Åkturer/attraktioner i parken -STR_1054 :{SMALLFONT}{BLACK}Namnge åktur/attraktion +STR_1050 :Misslyckades att ladda …{NEWLINE}Filen innehåller ogiltig data! +STR_1051 :Dölj stödpelare +STR_1052 :Dölj besökare +STR_1053 :{SMALLFONT}{BLACK}Åkturer eller attraktioner i parken +STR_1054 :{SMALLFONT}{BLACK}Namnge åktur eller attraktion STR_1055 :{SMALLFONT}{BLACK}Namnge person STR_1056 :{SMALLFONT}{BLACK}Namnge anställd -STR_1057 :Åkturens/attraktionens namn +STR_1057 :Åkturens eller attraktionens namn STR_1058 :Skriv in ett nytt namn för den här åkturen: -STR_1059 :Kan inte namnge åktur/attraktion... -STR_1060 :Ogiltigt åktur/attraktionsnamn +STR_1059 :Kan inte namnge åktur eller attraktion … +STR_1060 :Ogiltigt namn för åktur eller attraktion STR_1061 :Normalt läge STR_1062 :Kontinuerligt banläge STR_1063 :Backande kedjelyftstart STR_1064 :Accelererad start (passerar station) -STR_1065 :Pendlarläge +STR_1065 :Pendlingsläge STR_1066 :Uthyrning STR_1067 :Uppskjut STR_1068 :Roterande hiss @@ -466,19 +466,19 @@ STR_1081 :3D-film: ”Mussvansar” STR_1082 :Rymdringar STR_1083 :Nybörjarläge STR_1084 :LIM-accelererad start -STR_1085 :Film: ”Spänning i Farten” -STR_1086 :3D film: ”Stormjägarna” -STR_1087 :3D film: ”Rymdrädarna” +STR_1085 :Film: ”Spänning i farten” +STR_1086 :3D-film: ”Stormjägarna” +STR_1087 :3D-film: ”Rymdhjältarna” STR_1088 :Intensivt läge -STR_1089 :Berserkläge +STR_1089 :Bärsärkaläge STR_1090 :Spökhuset STR_1091 :Cirkusvisning STR_1092 :Nedåtriktad start STR_1093 :Lustiga huset STR_1094 :Fritt fall -STR_1095 :Blockavdelat kontinuerligt banläge -STR_1096 :Motoriserad start (utan att passera station) -STR_1097 :Blockavdelad motoriserad start +STR_1095 :Blockuppdelat kontinuerligt banläge +STR_1096 :Motordriven start (utan att passera station) +STR_1097 :Blockuppdelad motordriven start STR_1098 :Rör sig mot slutet av {POP16}{STRINGID} STR_1099 :Väntar på passagerare vid {POP16}{STRINGID} STR_1100 :Väntar på avgång vid {POP16}{STRINGID} @@ -498,7 +498,7 @@ STR_1113 :Visar film STR_1114 :Roterar STR_1115 :Pågår STR_1116 :Pågår -STR_1117 :Cirkusshow pågår +STR_1117 :Cirkusuppvisning pågår STR_1118 :Pågår STR_1119 :Väntar på linbanevagn STR_1120 :Åker i {VELOCITY} @@ -522,11 +522,11 @@ STR_1137 :{SMALLFONT}{BLACK}Välj andra färg STR_1138 :{SMALLFONT}{BLACK}Välj tredje färg STR_1139 :{SMALLFONT}{BLACK}Välj färg för stödpelare STR_1140 :{SMALLFONT}{BLACK}Välj färgpalett för fordon -STR_1141 :{SMALLFONT}{BLACK}Välj vilket fordon/tåg som ska modifieras +STR_1141 :{SMALLFONT}{BLACK}Välj vilket fordon eller tåg som ska modifieras STR_1142 :{MOVE_X}{SMALLFONT}{STRINGID} STR_1143 :»{MOVE_X}{SMALLFONT}{STRINGID} -STR_1144 :Kan inte bygga/flytta ingången för den här åkturen/attraktionen... -STR_1145 :Kan inte bygga/flytta utgången för den här åkturen/attraktionen... +STR_1144 :Kan inte bygga eller flytta ingången för den här åkturen eller attraktionen … +STR_1145 :Kan inte bygga eller flytta utgången för den här åkturen eller attraktionen … STR_1146 :Ingången är inte byggd STR_1147 :Utgången är inte byggd STR_1148 :En kvarts fullt @@ -534,31 +534,31 @@ STR_1149 :Halvfullt STR_1150 :Trekvarts fullt STR_1151 :Fullt STR_1152 :Minst en person -STR_1153 :Höjdmarkeringar på Åkturer -STR_1154 :Höjdmarkeringar på Land -STR_1155 :Höjdmarkeringar på Gångvägar +STR_1153 :Höjdmarkeringar på åkturer +STR_1154 :Höjdmarkeringar på land +STR_1155 :Höjdmarkeringar på gångvägar STR_1156 :{MOVE_X}{SMALLFONT}{STRINGID} STR_1157 :✓{MOVE_X}{SMALLFONT}{STRINGID} -STR_1158 :Kan inte ta bort detta... -STR_1159 :{SMALLFONT}{BLACK}Placera dekorationer, trädgårdar och andra tillbehör -STR_1160 :{SMALLFONT}{BLACK}Skapa/justera sjöar och vatten -STR_1161 :Kan inte placera detta här... +STR_1158 :Kan inte ta bort detta … +STR_1159 :{SMALLFONT}{BLACK}Placera dekorationer, planteringar och andra tillbehör +STR_1160 :{SMALLFONT}{BLACK}Skapa eller justera sjöar och vatten +STR_1161 :Kan inte placera detta här … STR_1162 :{OUTLINE}{TOPAZ}{STRINGID} -STR_1163 :{STRINGID}{NEWLINE}(Höger-klicka för att modifiera) -STR_1164 :{STRINGID}{NEWLINE}(Höger-klicka för att ta bort) -STR_1165 :{STRINGID} - {STRINGID} {COMMA16} -STR_1166 :Kan inte sänka vattennivån... -STR_1167 :Kan inte höja vattennivån... +STR_1163 :{STRINGID}{NEWLINE}(Högerklicka för att ändra) +STR_1164 :{STRINGID}{NEWLINE}(Högerklicka för att ta bort) +STR_1165 :{STRINGID} – {STRINGID} {COMMA16} +STR_1166 :Kan inte sänka vattennivån … +STR_1167 :Kan inte höja vattennivån … STR_1168 :Inställningar STR_1169 :(Inga) STR_1170 :{STRING} -STR_1171 :{RED}Stängd - - -STR_1172 :{YELLOW}{STRINGID} - - +STR_1171 :{RED}Stängd +STR_1172 :{YELLOW}{STRINGID} STR_1173 :{SMALLFONT}{BLACK}Bygg gångvägar och köer STR_1174 :En skylt är i vägen STR_1175 :Kan inte bygga på en sluttande gångväg -STR_1176 :Kan inte bygga gångväg här... -STR_1177 :Kan inte ta bort gångvägen här... +STR_1176 :Kan inte bygga gångväg här … +STR_1177 :Kan inte ta bort gångvägen härifrån … STR_1178 :Markens sluttning är inte lämplig STR_1179 :Gångvägen är i vägen STR_1180 :Kan inte bygga det här under vatten! @@ -609,7 +609,7 @@ STR_1224 :{SMALLFONT}{BLACK}Lugna åkturer STR_1225 :{SMALLFONT}{BLACK}Berg- och dalbanor STR_1226 :{SMALLFONT}{BLACK}Spännande åkturer STR_1227 :{SMALLFONT}{BLACK}Vattenåkturer -STR_1228 :{SMALLFONT}{BLACK}Affärer & stånd +STR_1228 :{SMALLFONT}{BLACK}Affärer och stånd STR_1229 :tåg STR_1230 :tåg STR_1231 :Tåg @@ -714,13 +714,13 @@ STR_1329 :{WINDOW_COLOUR_2}Starthastighet: STR_1330 :{SMALLFONT}{BLACK}Maxhastighet när tåget lämnar stationen STR_1331 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{VELOCITY} STR_1332 :{VELOCITY} -STR_1333 :{STRINGID} - {STRINGID}{POP16} -STR_1334 :{STRINGID} - {STRINGID} {COMMA16} -STR_1335 :{STRINGID} - Ingång{POP16}{POP16} -STR_1336 :{STRINGID} - Station {POP16}{COMMA16} Ingång -STR_1337 :{STRINGID} - Utgång{POP16}{POP16} -STR_1338 :{STRINGID} - Station {POP16}{COMMA16} Utgång -STR_1339 :{BLACK}Inga testresultat än... +STR_1333 :{STRINGID} – {STRINGID}{POP16} +STR_1334 :{STRINGID} – {STRINGID} {COMMA16} +STR_1335 :{STRINGID} – Ingång{POP16}{POP16} +STR_1336 :{STRINGID} – Station {POP16}{COMMA16} Ingång +STR_1337 :{STRINGID} – Utgång{POP16}{POP16} +STR_1338 :{STRINGID} – Station {POP16}{COMMA16} Utgång +STR_1339 :{BLACK}Inga testresultat än … STR_1340 :{WINDOW_COLOUR_2}Maxhastighet: {BLACK}{VELOCITY} STR_1341 :{WINDOW_COLOUR_2}Åktid: {BLACK}{STRINGID}{STRINGID}{STRINGID}{STRINGID} STR_1342 :{DURATION} @@ -733,47 +733,47 @@ STR_1348 :{WINDOW_COLOUR_2}Max positiva vertikala G-krafter: {BLACK}{COMMA2DP STR_1349 :{WINDOW_COLOUR_2}Max positiva vertikala G-krafter: {OUTLINE}{RED}{COMMA2DP32}g STR_1350 :{WINDOW_COLOUR_2}Max negativa vertikala G-krafter: {BLACK}{COMMA2DP32}g STR_1351 :{WINDOW_COLOUR_2}Max negativa vertikala G-krafter: {OUTLINE}{RED}{COMMA2DP32}g -STR_1352 :{WINDOW_COLOUR_2}Max laterala G-krafter: {BLACK}{COMMA2DP32}g -STR_1353 :{WINDOW_COLOUR_2}Max laterala G-krafter: {OUTLINE}{RED}{COMMA2DP32}g +STR_1352 :{WINDOW_COLOUR_2}Max G-krafter i sidled: {BLACK}{COMMA2DP32}g +STR_1353 :{WINDOW_COLOUR_2}Max G-krafter i sidled: {OUTLINE}{RED}{COMMA2DP32}g STR_1354 :{WINDOW_COLOUR_2}Högsta fallhöjd: {BLACK}{LENGTH} STR_1355 :{WINDOW_COLOUR_2}Fall: {BLACK}{COMMA16} STR_1356 :{WINDOW_COLOUR_2}Inversioner: {BLACK}{COMMA16} STR_1357 :{WINDOW_COLOUR_2}Hål: {BLACK}{COMMA16} -STR_1358 :{WINDOW_COLOUR_2}Total 'flyg'-tid: {BLACK}{COMMA2DP32}secs +STR_1358 :{WINDOW_COLOUR_2}Total flygtid: {BLACK}{COMMA2DP32}secs STR_1359 :{WINDOW_COLOUR_2}Kötid: {BLACK}{COMMA16} minut STR_1360 :{WINDOW_COLOUR_2}Kötid: {BLACK}{COMMA16} minuter -STR_1361 :Kan inte ändra hastighet... -STR_1362 :Kan inte ändra starthastighet... +STR_1361 :Kan inte ändra hastighet … +STR_1362 :Kan inte ändra starthastighet … STR_1363 :För högt för stödpelare! STR_1364 :Stödpelarna kan inte förlängas mer! STR_1365 :Tvist (vänster) STR_1366 :Tvist (höger) STR_1367 :Halvloop -STR_1368 :Halv Korkskruv (vänster) -STR_1369 :Halv Korkskruv (höger) +STR_1368 :Halv korkskruv (vänster) +STR_1369 :Halv korkskruv (höger) STR_1370 :Roll (vänster) STR_1371 :Roll (höger) STR_1372 :Uppskjutskulle -STR_1373 :Stor Halvloop (vänster) -STR_1374 :Stor Halvloop (höger) -STR_1375 :Övre Vidarekoppling -STR_1376 :Nedre Vidarekoppling -STR_1377 :Heartline-Roll (vänster) -STR_1378 :Heartline-Roll (höger) +STR_1373 :Stor halvloop (vänster) +STR_1374 :Stor halvloop (höger) +STR_1375 :Övre vidarekoppling +STR_1376 :Nedre vidarekoppling +STR_1377 :Mittpunktsroll (vänster) +STR_1378 :Mittpunktsroll (höger) STR_1379 :Omvändare (vänster) STR_1380 :Omvändare (höger) -STR_1381 :Kurvad Uppskjutskulle (vänster) -STR_1382 :Kurvad Uppskjutskulle (höger) -STR_1383 :Kvarts Loop +STR_1381 :Kurvad uppskjutskulle (vänster) +STR_1382 :Kurvad uppskjutskulle (höger) +STR_1383 :Kvartsloop STR_1384 :{YELLOW}{STRINGID} STR_1385 :{SMALLFONT}{BLACK}Andra bankonfigurationer -STR_1386 :Speciell... -STR_1387 :Kan inte byta terrängtyp... +STR_1386 :Speciell … +STR_1387 :Kan inte byta terrängtyp … STR_1388 :{OUTLINE}{GREEN}+ {CURRENCY} STR_1389 :{OUTLINE}{RED}- {CURRENCY} STR_1390 :{CURRENCY2DP} STR_1391 :{RED}{CURRENCY2DP} -STR_1392 :{SMALLFONT}{BLACK}Åkturs/attraktions-vy +STR_1392 :{SMALLFONT}{BLACK}Åkturs- eller attraktionsvy STR_1393 :{SMALLFONT}{BLACK}Fordonsdetaljer och inställningar STR_1394 :{SMALLFONT}{BLACK}Manövrerings inställningar STR_1395 :{SMALLFONT}{BLACK}Underhållsinställningar @@ -783,23 +783,23 @@ STR_1398 :{SMALLFONT}{BLACK}Mätvärden och testdata STR_1399 :{SMALLFONT}{BLACK}Grafer STR_1400 :Ingång STR_1401 :Utgång -STR_1402 :{SMALLFONT}{BLACK}Bygg eller flytta ingången till åkturen/attraktionen -STR_1403 :{SMALLFONT}{BLACK}Bygg eller flytta utgången till åkturen/attraktionen +STR_1402 :{SMALLFONT}{BLACK}Bygg eller flytta ingången till åkturen eller attraktionen +STR_1403 :{SMALLFONT}{BLACK}Bygg eller flytta utgången till åkturen eller attraktionen STR_1404 :{SMALLFONT}{BLACK}Rotera 90° STR_1405 :{SMALLFONT}{BLACK}Spegla -STR_1406 :{SMALLFONT}{BLACK}Dölj/visa dekorationer (om de finns på designen) -STR_1407 :{WINDOW_COLOUR_2}Bygg detta... +STR_1406 :{SMALLFONT}{BLACK}Dölj eller visa dekorationer (om de finns på designen) +STR_1407 :{WINDOW_COLOUR_2}Bygg detta … STR_1408 :{WINDOW_COLOUR_2}Kostnad: {BLACK}{CURRENCY} -STR_1409 :Ingång/utgång Plattform -STR_1410 :Vertikalt Torn +STR_1409 :In- eler utgång för plattform +STR_1410 :Vertikalt torn STR_1411 :{STRINGID} är i vägen STR_1412 :{WINDOW_COLOUR_3}Den här sortens åktur kan inte spara data STR_1413 :{WINDOW_COLOUR_3}Sparande av data börjar när nästa {STRINGID} lämnar {STRINGID} STR_1414 :{SMALLFONT}{BLACK}{DURATION} STR_1415 :{WINDOW_COLOUR_2}Hastighet STR_1416 :{WINDOW_COLOUR_2}Höjd -STR_1417 :{WINDOW_COLOUR_2}Vert.G's -STR_1418 :{WINDOW_COLOUR_2}Lat.G's +STR_1417 :{WINDOW_COLOUR_2}Lodrät G-kraft +STR_1418 :{WINDOW_COLOUR_2}Vågrät G-kraft STR_1419 :{SMALLFONT}{BLACK}{VELOCITY} STR_1420 :{SMALLFONT}{BLACK}{LENGTH} STR_1421 :{SMALLFONT}{BLACK}{COMMA16}g @@ -812,7 +812,7 @@ STR_1427 :{WINDOW_COLOUR_2}Kunder: {BLACK}{COMMA32} per timme STR_1428 :{WINDOW_COLOUR_2}Biljettpris: STR_1429 :{POP16}{POP16}{POP16}{CURRENCY2DP} STR_1430 :Gratis -STR_1431 :Vandrar +STR_1431 :Går STR_1432 :På väg mot {STRINGID} STR_1433 :Köar till {STRINGID} STR_1434 :Drunknar @@ -821,10 +821,10 @@ STR_1436 :I {STRINGID} STR_1437 :Vid {STRINGID} STR_1438 :Sitter STR_1439 :(välj plats) -STR_1440 :Klipper gräset -STR_1441 :Sopar gångvägen -STR_1442 :Tömmer sopkorgen -STR_1443 :Vattnar trädgården +STR_1440 :Klipper gräs +STR_1441 :Sopar gångväg +STR_1442 :Tömmer sopkorg +STR_1443 :Vattnar plantering STR_1444 :Kollar på {STRINGID} STR_1445 :Kollar på konstruktionen av {STRINGID} STR_1446 :Kollar på dekorationer @@ -833,18 +833,18 @@ STR_1448 :Kollar på konstruktionen av en ny åktur STR_1449 :{SPRITE} {STRINGID}{NEWLINE}({STRINGID}) STR_1450 :{INLINE_SPRITE}{09}{20}{00}{00}{SPRITE} {STRINGID}{NEWLINE}({STRINGID}) STR_1451 :{STRINGID}{NEWLINE}({STRINGID}) -STR_1452 :Gästens namn -STR_1453 :Skriv in namn för den här gästen: -STR_1454 :Kan inte namnge gäst... -STR_1455 :Ogiltigt gästnamn +STR_1452 :Besökarens namn +STR_1453 :Skriv in namn för den här besökaren: +STR_1454 :Kan inte namnge besökare … +STR_1455 :Ogiltigt besökarnamn STR_1456 :{WINDOW_COLOUR_2}Spenderade pengar: {BLACK}{CURRENCY2DP} STR_1457 :{WINDOW_COLOUR_2}Pengar i fickan: {BLACK}{CURRENCY2DP} STR_1458 :{WINDOW_COLOUR_2}Tid i parken: {BLACK}{REALTIME} STR_1459 :Banstil -STR_1460 :{SMALLFONT}{BLACK}'U'-formad öppen bana -STR_1461 :{SMALLFONT}{BLACK}'O'-formad stängd bana +STR_1460 :{SMALLFONT}{BLACK}U-formad öppen bana +STR_1461 :{SMALLFONT}{BLACK}O-formad stängd bana STR_1462 :För brant för uppskjutskulle -STR_1463 :Gäster +STR_1463 :Besökare STR_1464 :Helix uppåt (liten) STR_1465 :Helix uppåt (stor) STR_1466 :Helix nedåt (liten) @@ -861,131 +861,131 @@ STR_1476 :{WINDOW_COLOUR_2}Intensitetsnivå: {BLACK}Inte tillgänglig STR_1477 :{WINDOW_COLOUR_2}Intensitetsnivå: {OUTLINE}{RED}{COMMA2DP32} ({STRINGID}) STR_1478 :{WINDOW_COLOUR_2}Illamåendenivå: {BLACK}{COMMA2DP32} ({STRINGID}) STR_1479 :{WINDOW_COLOUR_2}Illamåendenivå: {BLACK}Inte tillgänglig -STR_1480 :{SMALLFONT}“Jag har inte råd med {STRINGID}” -STR_1481 :{SMALLFONT}“Jag har spenderat alla mina pengar” -STR_1482 :{SMALLFONT}“Jag mår illa” -STR_1483 :{SMALLFONT}“Jag mår riktigt illa” -STR_1484 :{SMALLFONT}“Jag vill åka något mer spännande än {STRINGID}” -STR_1485 :{SMALLFONT}“{STRINGID} ser för intensiv ut för mig” -STR_1486 :{SMALLFONT}“Jag är inte klar med min {STRINGID} än” -STR_1487 :{SMALLFONT}“Bara att kolla på {STRINGID} får mig att må illa” -STR_1488 :{SMALLFONT}“Jag tänker inte betala så mycket för {STRINGID}” -STR_1489 :{SMALLFONT}“Jag vill gå hem” -STR_1490 :{SMALLFONT}“{STRINGID} är verkligen värd” -STR_1491 :{SMALLFONT}“Jag har redan {STRINGID}” -STR_1492 :{SMALLFONT}“Jag har inte råd med {STRINGID}” -STR_1493 :{SMALLFONT}“Jag är inte hungrig” -STR_1494 :{SMALLFONT}“Jag är inte törstig” -STR_1495 :{SMALLFONT}“Hjälp! Jag drunknar!” -STR_1496 :{SMALLFONT}“Jag har tappat bort mig!” -STR_1497 :{SMALLFONT}“{STRINGID} var superkul” -STR_1498 :{SMALLFONT}“Jag har köat till {STRINGID} i åratal” -STR_1499 :{SMALLFONT}“Jag är trött” -STR_1500 :{SMALLFONT}“Jag är hungrig” -STR_1501 :{SMALLFONT}“Jag är törstig” -STR_1502 :{SMALLFONT}“Jag måste gå på toa” -STR_1503 :{SMALLFONT}“Jag kan inte hitta {STRINGID}” -STR_1504 :{SMALLFONT}“Jag tänker inte betala så mycket för att använda {STRINGID}” -STR_1505 :{SMALLFONT}“Jag tänker inte åka {STRINGID} när det regnar” -STR_1506 :{SMALLFONT}“Nedskräpningen här är hemsk” -STR_1507 :{SMALLFONT}“Jag kan inte hitta parkutgången” -STR_1508 :{SMALLFONT}“Jag vill hoppa av {STRINGID}” -STR_1509 :{SMALLFONT}“Jag vill ut ur {STRINGID}” -STR_1510 :{SMALLFONT}“Jag tänker inte åka {STRINGID} - Det är inte säkert nog” -STR_1511 :{SMALLFONT}“Den här gångvägen är vidrig” -STR_1512 :{SMALLFONT}“Det är för mycket folk här” -STR_1513 :{SMALLFONT}“Skadegörelsen här är verkligen illa” -STR_1514 :{SMALLFONT}“Snygga dekorationer!” -STR_1515 :{SMALLFONT}“Den här parken är verkligen ren och fin” -STR_1516 :{SMALLFONT}“Fontänerna är fantastiska” -STR_1517 :{SMALLFONT}“Musiken här är trevlig” -STR_1518 :{SMALLFONT}“Ballongen från {STRINGID} var verkligen värd pengarna” -STR_1519 :{SMALLFONT}“Kramdjuret från {STRINGID} var verkligen värt pengarna” -STR_1520 :{SMALLFONT}“Kartan från {STRINGID} var verkligen värd pengarna” -STR_1521 :{SMALLFONT}“Fotot från åkturen {STRINGID} var verkligen värt pengarna” -STR_1522 :{SMALLFONT}“Paraplyet från {STRINGID} var verkligen värt pengarna” -STR_1523 :{SMALLFONT}“Drickan från {STRINGID} var verkligen värd pengarna” -STR_1524 :{SMALLFONT}“Hamburgaren från {STRINGID} var verkligen värd pengarna” -STR_1525 :{SMALLFONT}“Pommes fritesen från {STRINGID} var verkligen värda pengarna” -STR_1526 :{SMALLFONT}“Glassen från {STRINGID} var verkligen värd pengarna” -STR_1527 :{SMALLFONT}“Sockervadden från {STRINGID} var verkligen värd pengarna” +STR_1480 :{SMALLFONT}”Jag har inte råd med {STRINGID}” +STR_1481 :{SMALLFONT}”Jag har spenderat alla mina pengar” +STR_1482 :{SMALLFONT}”Jag mår illa” +STR_1483 :{SMALLFONT}”Jag mår riktigt illa” +STR_1484 :{SMALLFONT}”Jag vill åka något mer spännande än {STRINGID}” +STR_1485 :{SMALLFONT}”{STRINGID} ser för intensiv ut för mig” +STR_1486 :{SMALLFONT}”Jag är inte klar med min {STRINGID} än” +STR_1487 :{SMALLFONT}”Bara att kolla på {STRINGID} får mig att må illa” +STR_1488 :{SMALLFONT}”Jag tänker inte betala så mycket för {STRINGID}” +STR_1489 :{SMALLFONT}”Jag vill gå hem” +STR_1490 :{SMALLFONT}”{STRINGID} är verkligen värd pengarna” +STR_1491 :{SMALLFONT}”Jag har redan {STRINGID}” +STR_1492 :{SMALLFONT}”Jag har inte råd med {STRINGID}” +STR_1493 :{SMALLFONT}”Jag är inte hungrig” +STR_1494 :{SMALLFONT}”Jag är inte törstig” +STR_1495 :{SMALLFONT}”Hjälp! Jag drunknar!” +STR_1496 :{SMALLFONT}”Jag har tappat bort mig!” +STR_1497 :{SMALLFONT}”{STRINGID} var superkul” +STR_1498 :{SMALLFONT}”Jag har köat till {STRINGID} i åratal” +STR_1499 :{SMALLFONT}”Jag är trött” +STR_1500 :{SMALLFONT}”Jag är hungrig” +STR_1501 :{SMALLFONT}”Jag är törstig” +STR_1502 :{SMALLFONT}”Jag måste gå på toa” +STR_1503 :{SMALLFONT}”Jag kan inte hitta {STRINGID}” +STR_1504 :{SMALLFONT}”Jag tänker inte betala så mycket för att använda {STRINGID}” +STR_1505 :{SMALLFONT}”Jag tänker inte åka {STRINGID} när det regnar” +STR_1506 :{SMALLFONT}”Nedskräpningen här är hemsk” +STR_1507 :{SMALLFONT}”Jag kan inte hitta parkutgången” +STR_1508 :{SMALLFONT}”Jag vill hoppa av {STRINGID}” +STR_1509 :{SMALLFONT}”Jag vill ut ur {STRINGID}” +STR_1510 :{SMALLFONT}”Jag tänker inte åka {STRINGID} – Det är inte säkert nog” +STR_1511 :{SMALLFONT}”Den här gångvägen är vidrig” +STR_1512 :{SMALLFONT}”Det är för mycket folk här” +STR_1513 :{SMALLFONT}”Skadegörelsen här är verkligen illa” +STR_1514 :{SMALLFONT}”Snygga dekorationer!” +STR_1515 :{SMALLFONT}”Den här parken är verkligen ren och fin” +STR_1516 :{SMALLFONT}”Fontänerna är fantastiska” +STR_1517 :{SMALLFONT}”Musiken här är trevlig” +STR_1518 :{SMALLFONT}”Ballongen från {STRINGID} var verkligen värd pengarna” +STR_1519 :{SMALLFONT}”Kramdjuret från {STRINGID} var verkligen värt pengarna” +STR_1520 :{SMALLFONT}”Kartan från {STRINGID} var verkligen värd pengarna” +STR_1521 :{SMALLFONT}”Fotot från åkturen {STRINGID} var verkligen värt pengarna” +STR_1522 :{SMALLFONT}”Paraplyet från {STRINGID} var verkligen värt pengarna” +STR_1523 :{SMALLFONT}”Drickan från {STRINGID} var verkligen värd pengarna” +STR_1524 :{SMALLFONT}”Hamburgaren från {STRINGID} var verkligen värd pengarna” +STR_1525 :{SMALLFONT}”Pommes fritesen från {STRINGID} var verkligen värda pengarna” +STR_1526 :{SMALLFONT}”Glassen från {STRINGID} var verkligen värd pengarna” +STR_1527 :{SMALLFONT}”Sockervadden från {STRINGID} var verkligen värd pengarna” STR_1528 : STR_1529 : STR_1530 : -STR_1531 :{SMALLFONT}“Pizzan från {STRINGID} var verkligen värd pengarna” +STR_1531 :{SMALLFONT}”Pizzan från {STRINGID} var verkligen värd pengarna” STR_1532 : -STR_1533 :{SMALLFONT}“Popcornen från {STRINGID} var verkligen värda pengarna” -STR_1534 :{SMALLFONT}“Varmkorven från {STRINGID} var verkligen värd pengarna” -STR_1535 :{SMALLFONT}“Tentakeln från {STRINGID} var verkligen värd pengarna” -STR_1536 :{SMALLFONT}“Hatten från {STRINGID} var verkligen värd pengarna” -STR_1537 :{SMALLFONT}“Kanderat äpplet från {STRINGID} var verkligen värt pengarna” -STR_1538 :{SMALLFONT}“T-shirten från {STRINGID} var verkligen värd pengarna” -STR_1539 :{SMALLFONT}“Munken från {STRINGID} var verkligen värd pengarna” -STR_1540 :{SMALLFONT}“Kaffet från {STRINGID} var verkligen värt pengarna” +STR_1533 :{SMALLFONT}”Popcornen från {STRINGID} var verkligen värda pengarna” +STR_1534 :{SMALLFONT}”Varmkorven från {STRINGID} var verkligen värd pengarna” +STR_1535 :{SMALLFONT}”Tentakeln från {STRINGID} var verkligen värd pengarna” +STR_1536 :{SMALLFONT}”Hatten från {STRINGID} var verkligen värd pengarna” +STR_1537 :{SMALLFONT}”Kanderat äpplet från {STRINGID} var verkligen värt pengarna” +STR_1538 :{SMALLFONT}”T-shirten från {STRINGID} var verkligen värd pengarna” +STR_1539 :{SMALLFONT}”Munken från {STRINGID} var verkligen värd pengarna” +STR_1540 :{SMALLFONT}”Kaffet från {STRINGID} var verkligen värt pengarna” STR_1541 : -STR_1542 :{SMALLFONT}“Friterade kycklingen från {STRINGID} var verkligen värd pengarna” -STR_1543 :{SMALLFONT}“Saften från {STRINGID} var verkligen värd pengarna” +STR_1542 :{SMALLFONT}”Friterade kycklingen från {STRINGID} var verkligen värd pengarna” +STR_1543 :{SMALLFONT}”Saften från {STRINGID} var verkligen värd pengarna” STR_1544 : STR_1545 : STR_1546 : STR_1547 : STR_1548 : STR_1549 : -STR_1550 :{SMALLFONT}“Wow!” -STR_1551 :{SMALLFONT}“Det känns som att någon iakttar mig” -STR_1552 :{SMALLFONT}“Jag tänker inte betala så mycket för en ballong från {STRINGID}” -STR_1553 :{SMALLFONT}“Jag tänker inte betala så mycket för ett mjukisdjur från {STRINGID}” -STR_1554 :{SMALLFONT}“Jag tänker inte betala så mycket för en karta från {STRINGID}” -STR_1555 :{SMALLFONT}“Jag tänker inte betala så mycket för ett foto från åkturen {STRINGID}” -STR_1556 :{SMALLFONT}“Jag tänker inte betala så mycket för ett paraply från {STRINGID}” -STR_1557 :{SMALLFONT}“Jag tänker inte betala så mycket för en dricka från {STRINGID}” -STR_1558 :{SMALLFONT}“Jag tänker inte betala så mycket för en hamburgare från {STRINGID}” -STR_1559 :{SMALLFONT}“Jag tänker inte betala så mycket för pommes frites från {STRINGID}” -STR_1560 :{SMALLFONT}“Jag tänker inte betala så mycket för en glass från {STRINGID}” -STR_1561 :{SMALLFONT}“Jag tänker inte betala så mycket för sockervadd från {STRINGID}” +STR_1550 :{SMALLFONT}”Wow!” +STR_1551 :{SMALLFONT}”Det känns som att någon iakttar mig” +STR_1552 :{SMALLFONT}”Jag tänker inte betala så mycket för en ballong från {STRINGID}” +STR_1553 :{SMALLFONT}”Jag tänker inte betala så mycket för ett mjukisdjur från {STRINGID}” +STR_1554 :{SMALLFONT}”Jag tänker inte betala så mycket för en karta från {STRINGID}” +STR_1555 :{SMALLFONT}”Jag tänker inte betala så mycket för ett foto från åkturen {STRINGID}” +STR_1556 :{SMALLFONT}”Jag tänker inte betala så mycket för ett paraply från {STRINGID}” +STR_1557 :{SMALLFONT}”Jag tänker inte betala så mycket för en dricka från {STRINGID}” +STR_1558 :{SMALLFONT}”Jag tänker inte betala så mycket för en hamburgare från {STRINGID}” +STR_1559 :{SMALLFONT}”Jag tänker inte betala så mycket för pommes frites från {STRINGID}” +STR_1560 :{SMALLFONT}”Jag tänker inte betala så mycket för en glass från {STRINGID}” +STR_1561 :{SMALLFONT}”Jag tänker inte betala så mycket för sockervadd från {STRINGID}” STR_1562 : STR_1563 : STR_1564 : -STR_1565 :{SMALLFONT}“Jag tänker inte betala så mycket för pizza from {STRINGID}” +STR_1565 :{SMALLFONT}”Jag tänker inte betala så mycket för pizza from {STRINGID}” STR_1566 : -STR_1567 :{SMALLFONT}“Jag tänker inte betala så mycket för popcorn från {STRINGID}” -STR_1568 :{SMALLFONT}“Jag tänker inte betala så mycket för en varmkorv från {STRINGID}” -STR_1569 :{SMALLFONT}“Jag tänker inte betala så mycket för tentakel från {STRINGID}” -STR_1570 :{SMALLFONT}“Jag tänker inte betala så mycket för en hatt från {STRINGID}” -STR_1571 :{SMALLFONT}“Jag tänker inte betala så mycket för ett kanderat äpple från {STRINGID}” -STR_1572 :{SMALLFONT}“Jag tänker inte betala så mycket för en T-shirt från {STRINGID}” -STR_1573 :{SMALLFONT}“Jag tänker inte betala så mycket för en munk från {STRINGID}” -STR_1574 :{SMALLFONT}“Jag tänker inte betala så mycket för en kaffe från {STRINGID}” +STR_1567 :{SMALLFONT}”Jag tänker inte betala så mycket för popcorn från {STRINGID}” +STR_1568 :{SMALLFONT}”Jag tänker inte betala så mycket för en varmkorv från {STRINGID}” +STR_1569 :{SMALLFONT}”Jag tänker inte betala så mycket för tentakel från {STRINGID}” +STR_1570 :{SMALLFONT}”Jag tänker inte betala så mycket för en hatt från {STRINGID}” +STR_1571 :{SMALLFONT}”Jag tänker inte betala så mycket för ett kanderat äpple från {STRINGID}” +STR_1572 :{SMALLFONT}”Jag tänker inte betala så mycket för en T-shirt från {STRINGID}” +STR_1573 :{SMALLFONT}”Jag tänker inte betala så mycket för en munk från {STRINGID}” +STR_1574 :{SMALLFONT}”Jag tänker inte betala så mycket för en kaffe från {STRINGID}” STR_1575 : -STR_1576 :{SMALLFONT}“Jag tänker inte betala så mycket för friterad kyckling från {STRINGID}” -STR_1577 :{SMALLFONT}“Jag tänker inte betala så mycket för saft från {STRINGID}” +STR_1576 :{SMALLFONT}”Jag tänker inte betala så mycket för friterad kyckling från {STRINGID}” +STR_1577 :{SMALLFONT}”Jag tänker inte betala så mycket för saft från {STRINGID}” STR_1578 : STR_1579 : STR_1580 : STR_1581 : STR_1582 : STR_1583 : -STR_1584 :{SMALLFONT}“Fotot från åkturen {STRINGID} var verkligen värt pengarna” -STR_1585 :{SMALLFONT}“Fotot från åkturen {STRINGID} var verkligen värt pengarna” -STR_1586 :{SMALLFONT}“Fotot från åkturen {STRINGID} var verkligen värt pengarna” -STR_1587 :{SMALLFONT}“Kringlan från {STRINGID} var verkligen värd pengarna” -STR_1588 :{SMALLFONT}“Varma chokladen från {STRINGID} var verkligen värd pengarna” -STR_1589 :{SMALLFONT}“Istéet från {STRINGID} var verkligen värt pengarna” -STR_1590 :{SMALLFONT}“Struvan från {STRINGID} var verkligen värd pengarna” -STR_1591 :{SMALLFONT}“Solglasögonen från {STRINGID} var verkligen värda pengarna” -STR_1592 :{SMALLFONT}“Biffnudlarna från {STRINGID} var verkligen värda pengarna” -STR_1593 :{SMALLFONT}“De stekta nudlarna från {STRINGID} var verkligen värda pengarna” -STR_1594 :{SMALLFONT}“Wontonsoppan från {STRINGID} var verkligen värd pengarna” -STR_1595 :{SMALLFONT}“Köttbullssoppan från {STRINGID} var verkligen värd pengarna” -STR_1596 :{SMALLFONT}“Fruktjuicen från {STRINGID} var verkligen värd pengarna” -STR_1597 :{SMALLFONT}“Sojamjölken från {STRINGID} var verkligen värd pengarna” -STR_1598 :{SMALLFONT}“Sujeonggwan från {STRINGID} var verkligen värd pengarna” -STR_1599 :{SMALLFONT}“Mackan från {STRINGID} var verkligen värd pengarna” -STR_1600 :{SMALLFONT}“Kakan från {STRINGID} var verkligen värd pengarna” +STR_1584 :{SMALLFONT}”Fotot från åkturen {STRINGID} var verkligen värt pengarna” +STR_1585 :{SMALLFONT}”Fotot från åkturen {STRINGID} var verkligen värt pengarna” +STR_1586 :{SMALLFONT}”Fotot från åkturen {STRINGID} var verkligen värt pengarna” +STR_1587 :{SMALLFONT}”Kringlan från {STRINGID} var verkligen värd pengarna” +STR_1588 :{SMALLFONT}”Varma chokladen från {STRINGID} var verkligen värd pengarna” +STR_1589 :{SMALLFONT}”Istéet från {STRINGID} var verkligen värt pengarna” +STR_1590 :{SMALLFONT}”Struvan från {STRINGID} var verkligen värd pengarna” +STR_1591 :{SMALLFONT}”Solglasögonen från {STRINGID} var verkligen värda pengarna” +STR_1592 :{SMALLFONT}”Biffnudlarna från {STRINGID} var verkligen värda pengarna” +STR_1593 :{SMALLFONT}”De stekta nudlarna från {STRINGID} var verkligen värda pengarna” +STR_1594 :{SMALLFONT}”Wontonsoppan från {STRINGID} var verkligen värd pengarna” +STR_1595 :{SMALLFONT}”Köttbullssoppan från {STRINGID} var verkligen värd pengarna” +STR_1596 :{SMALLFONT}”Fruktjuicen från {STRINGID} var verkligen värd pengarna” +STR_1597 :{SMALLFONT}”Sojamjölken från {STRINGID} var verkligen värd pengarna” +STR_1598 :{SMALLFONT}”Sujeonggwan från {STRINGID} var verkligen värd pengarna” +STR_1599 :{SMALLFONT}”Mackan från {STRINGID} var verkligen värd pengarna” +STR_1600 :{SMALLFONT}”Kakan från {STRINGID} var verkligen värd pengarna” STR_1601 : STR_1602 : STR_1603 : -STR_1604 :{SMALLFONT}“Ugnskorven från {STRINGID} var verkligen värd pengarna” +STR_1604 :{SMALLFONT}”Ugnskorven från {STRINGID} var verkligen värd pengarna” STR_1605 : STR_1606 : STR_1607 : @@ -997,27 +997,27 @@ STR_1612 : STR_1613 : STR_1614 : STR_1615 : -STR_1616 :{SMALLFONT}“Jag tänker inte betala så mycket för ett foto från åkturen {STRINGID}” -STR_1617 :{SMALLFONT}“Jag tänker inte betala så mycket för ett foto från åkturen {STRINGID}” -STR_1618 :{SMALLFONT}“Jag tänker inte betala så mycket för ett foto från åkturen {STRINGID}” -STR_1619 :{SMALLFONT}“Jag tänker inte betala så mycket för en kringla från {STRINGID}” -STR_1620 :{SMALLFONT}“Jag tänker inte betala så mycket för varm choklad från {STRINGID}” -STR_1621 :{SMALLFONT}“Jag tänker inte betala så mycket för isté från {STRINGID}” -STR_1622 :{SMALLFONT}“Jag tänker inte betala så mycket för en struva från {STRINGID}” -STR_1623 :{SMALLFONT}“Jag tänker inte betala så mycket för solglasögon från {STRINGID}” -STR_1624 :{SMALLFONT}“Jag tänker inte betala så mycket för biffnudlar från {STRINGID}” -STR_1625 :{SMALLFONT}“Jag tänker inte betala så mycket för stekta nudlar från {STRINGID}” -STR_1626 :{SMALLFONT}“Jag tänker inte betala så mycket för wontonsoppa från {STRINGID}” -STR_1627 :{SMALLFONT}“Jag tänker inte betala så mycket för köttbullssoppa från {STRINGID}” -STR_1628 :{SMALLFONT}“Jag tänker inte betala så mycket för fruktjuice från {STRINGID}” -STR_1629 :{SMALLFONT}“Jag tänker inte betala så mycket för sojamjölk från {STRINGID}” -STR_1630 :{SMALLFONT}“Jag tänker inte betala så mycket för sujeonggwa från {STRINGID}” -STR_1631 :{SMALLFONT}“Jag tänker inte betala så mycket för en macka från {STRINGID}” -STR_1632 :{SMALLFONT}“Jag tänker inte betala så mycket för en kaka från {STRINGID}” +STR_1616 :{SMALLFONT}”Jag tänker inte betala så mycket för ett foto från åkturen {STRINGID}” +STR_1617 :{SMALLFONT}”Jag tänker inte betala så mycket för ett foto från åkturen {STRINGID}” +STR_1618 :{SMALLFONT}”Jag tänker inte betala så mycket för ett foto från åkturen {STRINGID}” +STR_1619 :{SMALLFONT}”Jag tänker inte betala så mycket för en kringla från {STRINGID}” +STR_1620 :{SMALLFONT}”Jag tänker inte betala så mycket för varm choklad från {STRINGID}” +STR_1621 :{SMALLFONT}”Jag tänker inte betala så mycket för isté från {STRINGID}” +STR_1622 :{SMALLFONT}”Jag tänker inte betala så mycket för en struva från {STRINGID}” +STR_1623 :{SMALLFONT}”Jag tänker inte betala så mycket för solglasögon från {STRINGID}” +STR_1624 :{SMALLFONT}”Jag tänker inte betala så mycket för biffnudlar från {STRINGID}” +STR_1625 :{SMALLFONT}”Jag tänker inte betala så mycket för stekta nudlar från {STRINGID}” +STR_1626 :{SMALLFONT}”Jag tänker inte betala så mycket för wontonsoppa från {STRINGID}” +STR_1627 :{SMALLFONT}”Jag tänker inte betala så mycket för köttbullssoppa från {STRINGID}” +STR_1628 :{SMALLFONT}”Jag tänker inte betala så mycket för fruktjuice från {STRINGID}” +STR_1629 :{SMALLFONT}”Jag tänker inte betala så mycket för sojamjölk från {STRINGID}” +STR_1630 :{SMALLFONT}”Jag tänker inte betala så mycket för sujeonggwa från {STRINGID}” +STR_1631 :{SMALLFONT}”Jag tänker inte betala så mycket för en macka från {STRINGID}” +STR_1632 :{SMALLFONT}”Jag tänker inte betala så mycket för en kaka från {STRINGID}” STR_1633 : STR_1634 : STR_1635 : -STR_1636 :{SMALLFONT}“Jag tänker inte betala så mycket för en ugnskorv från {STRINGID}” +STR_1636 :{SMALLFONT}”Jag tänker inte betala så mycket för en ugnskorv från {STRINGID}” STR_1637 : STR_1638 : STR_1639 : @@ -1029,11 +1029,11 @@ STR_1644 : STR_1645 : STR_1646 : STR_1647 : -STR_1648 :{SMALLFONT}“Hjälp! Släpp ner mig!” -STR_1649 :{SMALLFONT}“Jag börjar få slut på pengar!” -STR_1650 :{SMALLFONT}“Wow! De bygger en ny åktur!” -STR_1653 :{SMALLFONT}“...och nu är vi på {STRINGID}!” -STR_1654 :{WINDOW_COLOUR_2}Nyliga tankar: +STR_1648 :{SMALLFONT}”Hjälp! Släpp ner mig!” +STR_1649 :{SMALLFONT}”Jag börjar få slut på pengar!” +STR_1650 :{SMALLFONT}”Wow! De bygger en ny åktur!” +STR_1653 :{SMALLFONT}”… och nu är vi på {STRINGID}!” +STR_1654 :{WINDOW_COLOUR_2}Senaste tankar: STR_1655 :{SMALLFONT}{BLACK}Bygg gångväg på land STR_1656 :{SMALLFONT}{BLACK}Bygg en bro eller tunnel STR_1657 :{WINDOW_COLOUR_2}Favoritåktur @@ -1062,76 +1062,76 @@ STR_1679 :Helix uppåt (vänster) STR_1680 :Helix uppåt (höger) STR_1681 :Helix nedåt (vänster) STR_1682 :Helix nedåt (höger) -STR_1683 :Basstorlek 2 x 2 -STR_1684 :Basstorlek 4 x 4 -STR_1685 :Basstorlek 2 x 4 -STR_1686 :Basstorlek 5 x 1 +STR_1683 :Basstorlek 2 × 2 +STR_1684 :Basstorlek 4 × 4 +STR_1685 :Basstorlek 2 × 4 +STR_1686 :Basstorlek 5 × 1 STR_1687 :Vattenplums -STR_1688 :Basstorlek 4 x 1 +STR_1688 :Basstorlek 4 × 1 STR_1689 :Blockbromsar STR_1690 :{WINDOW_COLOUR_2}{STRINGID}{NEWLINE}{BLACK}{STRINGID} STR_1691 :{WINDOW_COLOUR_2} Kostnad: {BLACK}{CURRENCY} STR_1692 :{WINDOW_COLOUR_2} Kostnad: {BLACK}från {CURRENCY} -STR_1693 :{SMALLFONT}{BLACK}Gäster +STR_1693 :{SMALLFONT}{BLACK}Besökare STR_1694 :{SMALLFONT}{BLACK}Personal STR_1695 :{SMALLFONT}{BLACK}Inkomst och utgifter STR_1696 :{SMALLFONT}{BLACK}Kundinformation STR_1697 :Kan inte placera dessa i ett köområde STR_1698 :Kan endast placera dessa i ett köområde STR_1699 :För mycket folk i spelet -STR_1700 :Anställ ny Vaktmästare -STR_1701 :Anställ ny Mekaniker -STR_1702 :Anställ ny Säkerhetsvakt -STR_1703 :Anställ ny Underhållare -STR_1704 :Kan inte anställa ny personal... -STR_1705 :{SMALLFONT}{BLACK}Sparka den här personalen +STR_1700 :Anställ ny vaktmästare +STR_1701 :Anställ ny mekaniker +STR_1702 :Anställ ny säkerhetsvakt +STR_1703 :Anställ ny underhållare +STR_1704 :Kan inte anställa ny personal … +STR_1705 :{SMALLFONT}{BLACK}Avskeda den här personen STR_1706 :{SMALLFONT}{BLACK}Flytta den här personen till en ny plats STR_1707 :För mycket personal i spelet -STR_1708 :{SMALLFONT}{BLACK}Bestäm patrullområde för den här personalen -STR_1709 :Sparka personal +STR_1708 :{SMALLFONT}{BLACK}Bestäm patrullområde för den här personen +STR_1709 :Avskeda personal STR_1710 :Ja -STR_1711 :{WINDOW_COLOUR_1}Är du säker på att du vill sparka {STRINGID}? +STR_1711 :{WINDOW_COLOUR_1}Är du säker på att du vill avskeda {STRINGID}? STR_1712 :{INLINE_SPRITE}{247}{19}{00}{00}{WINDOW_COLOUR_2}Sopa gångvägar -STR_1713 :{INLINE_SPRITE}{248}{19}{00}{00}{WINDOW_COLOUR_2}Vattna trädgårdar +STR_1713 :{INLINE_SPRITE}{248}{19}{00}{00}{WINDOW_COLOUR_2}Vattna planteringar STR_1714 :{INLINE_SPRITE}{249}{19}{00}{00}{WINDOW_COLOUR_2}Töm sopkorgar STR_1715 :{INLINE_SPRITE}{250}{19}{00}{00}{WINDOW_COLOUR_2}Klipp gräs STR_1716 :Ogiltigt parknamn -STR_1717 :Kan inte namnge park... +STR_1717 :Kan inte namnge park … STR_1718 :Parknamn STR_1719 :Skriv in namn på parken: STR_1720 :{SMALLFONT}{BLACK}Namnge park STR_1721 :Parken är stängd STR_1722 :Parken är öppen -STR_1723 :Kan inte öppna parken... -STR_1724 :Kan inte stänga parken... -STR_1725 :Kan inte köpa mark... +STR_1723 :Kan inte öppna parken … +STR_1724 :Kan inte stänga parken … +STR_1725 :Kan inte köpa mark … STR_1726 :Marken är inte till salu! STR_1727 :Bygglov är inte till salu! -STR_1728 :Kan inte få bygglov här... +STR_1728 :Kan inte få bygglov här … STR_1729 :Marken ägs inte av parken! -STR_1730 :{RED}Stängd - - +STR_1730 :{RED}Stängd STR_1731 :{WHITE}{STRINGID} - - STR_1732 :Bygg STR_1733 :Läge STR_1734 :{WINDOW_COLOUR_2}Antal varv: STR_1735 :{SMALLFONT}{BLACK}Antal varv STR_1736 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} -STR_1738 :Kan inte ändra antal varv... -STR_1739 :Gäst {INT32} vann rallyt +STR_1738 :Kan inte ändra antal varv … +STR_1739 :Besökare {INT32} vann rallyt STR_1740 :{STRINGID} vann rallyt -STR_1741 :Inte byggt än ! -STR_1742 :{WINDOW_COLOUR_2}Max. personer på Åktur: -STR_1743 :{SMALLFONT}{BLACK}Maximalt antal personer på denna Åktur samtidigt +STR_1741 :Inte byggt än! +STR_1742 :{WINDOW_COLOUR_2}Max. personer på åktur: +STR_1743 :{SMALLFONT}{BLACK}Maximalt antal personer på denna åktur samtidigt STR_1744 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} -STR_1746 :Kan inte ändra detta... +STR_1746 :Kan inte ändra detta … STR_1747 :{WINDOW_COLOUR_2}Tidsgräns: STR_1748 :{SMALLFONT}{BLACK}Tidsgräns för åkturen STR_1749 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{DURATION} -STR_1751 :Kan inte ändra tidsgränsen för åkturen... -STR_1752 :{SMALLFONT}{BLACK}Visa lista över individuella gäster i parken -STR_1753 :{SMALLFONT}{BLACK}Visa sammanfattning av alla gäster i parken -STR_1754 :{BLACK}{COMMA16} gäster -STR_1755 :{BLACK}{COMMA16} gäst +STR_1751 :Kan inte ändra tidsgränsen för åkturen … +STR_1752 :{SMALLFONT}{BLACK}Visa lista över individuella besökare i parken +STR_1753 :{SMALLFONT}{BLACK}Visa sammanfattning av alla besökare i parken +STR_1754 :{BLACK}{COMMA16} besökare +STR_1755 :{BLACK}{COMMA16} Besökare STR_1756 :{WINDOW_COLOUR_2}Biljettpris: STR_1757 :{WINDOW_COLOUR_2}Tillförlitlighet: {MOVE_X}{255}{BLACK}{COMMA16}% STR_1758 :{SMALLFONT}{BLACK}Byggläge @@ -1144,14 +1144,14 @@ STR_1764 :Stockar STR_1765 :Fotosektion STR_1766 :Omvändare STR_1767 :Snurrande tunnel -STR_1768 :Kan inte ändra antalet gungningar... +STR_1768 :Kan inte ändra antalet gungningar … STR_1769 :{WINDOW_COLOUR_2}Antal gungningar: STR_1770 :{SMALLFONT}{BLACK}Antal hela gungningar STR_1771 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} STR_1773 :Bara en fotosektion tillåten per åktur STR_1774 :Bara en uppskjutskulle tillåten per åktur STR_1777 :Musik för åkturer -STR_1778 : +STR_1778 :{STRINGID} - - STR_1779 :{INLINE_SPRITE}{254}{19}{00}{00} Pandadräkt STR_1780 :{INLINE_SPRITE}{255}{19}{00}{00} Tigerdräkt STR_1781 :{INLINE_SPRITE}{00}{20}{00}{00} Elefantdräkt @@ -1173,8 +1173,8 @@ STR_1796 :Har gått sönder och behöver fixas STR_1798 :Strömvirvel STR_1799 :{POP16}{POP16}{POP16}{POP16}{POP16}{CURRENCY2DP} STR_1800 :Nödstopp -STR_1801 :Byglarna kan ej fällas ner -STR_1802 :Byglarna kan ej fällas upp +STR_1801 :Byglarna kan ej fällas upp +STR_1802 :Byglarna kan ej fällas ner STR_1803 :Dörrarna går inte att öppna STR_1804 :Dörrarna går inte att stänga STR_1805 :Fordonshaveri @@ -1183,21 +1183,21 @@ STR_1807 :Kontrollfel STR_1808 :{WINDOW_COLOUR_2}Senaste haveri: {BLACK}{STRINGID} STR_1809 :{WINDOW_COLOUR_2}Nuvarande haveri: {OUTLINE}{RED}{STRINGID} STR_1810 :{WINDOW_COLOUR_2}Bär på: -STR_1811 :Kan inte bygga detta här... +STR_1811 :Kan inte bygga detta här … STR_1812 :{SMALLFONT}{BLACK}{STRINGID} -STR_1813 :Diverse Saker +STR_1813 :Diverse saker STR_1814 :Handlingar STR_1815 :Tankar -STR_1816 :{SMALLFONT}{BLACK}Välj informationstyp som ska visas i gästlistan +STR_1816 :{SMALLFONT}{BLACK}Välj informationstyp som ska visas i besökarlistan STR_1817 :({COMMA16}) -STR_1818 :{WINDOW_COLOUR_2}Alla gäster -STR_1819 :{WINDOW_COLOUR_2}Alla gäster (sammanfattat) -STR_1820 :{WINDOW_COLOUR_2}Gäster {STRINGID} -STR_1821 :{WINDOW_COLOUR_2}Gäster som tänker {STRINGID} -STR_1822 :{WINDOW_COLOUR_2}Gäster som tänker på {POP16}{STRINGID} -STR_1823 :{SMALLFONT}{BLACK}Visa gästers tankar om den här åkturen/attraktionen -STR_1824 :{SMALLFONT}{BLACK}Visa gäster på den här åkturen/attraktionen -STR_1825 :{SMALLFONT}{BLACK}Visa gäster som köar till den här åkturen/attraktionen +STR_1818 :{WINDOW_COLOUR_2}Alla besökare +STR_1819 :{WINDOW_COLOUR_2}Alla besökare (sammanfattat) +STR_1820 :{WINDOW_COLOUR_2}Besökare {STRINGID} +STR_1821 :{WINDOW_COLOUR_2}Besökare som tänker {STRINGID} +STR_1822 :{WINDOW_COLOUR_2}Besökare som tänker på {POP16}{STRINGID} +STR_1823 :{SMALLFONT}{BLACK}Visa besökares tankar om den här åkturen eller attraktionen +STR_1824 :{SMALLFONT}{BLACK}Visa besökare på den här åkturen eller attraktionen +STR_1825 :{SMALLFONT}{BLACK}Visa besökare som köar till den här åkturen eller attraktionen STR_1826 :Status STR_1827 :Popularitet STR_1828 :Tillfredsställelse @@ -1206,7 +1206,7 @@ STR_1830 :Kölängd STR_1831 :Kötid STR_1832 :Tillförlitlighet STR_1833 :Dötid -STR_1834 :Gästernas favorit +STR_1834 :Besökarnas favorit STR_1835 :Popularitet: Okänd STR_1836 :Popularitet: {COMMA16}% STR_1837 :Tillfredsställelse: Okänd @@ -1214,20 +1214,20 @@ STR_1838 :Tillfredsställelse: {COMMA16}% STR_1839 :Tillförlitlighet: {COMMA16}% STR_1840 :Dötid: {COMMA16}% STR_1841 :Vinst: {CURRENCY} per timme -STR_1842 :{COMMA16} gästs favorit -STR_1843 :{COMMA16} gästers favorit -STR_1844 :{SMALLFONT}{BLACK}Välj informationstyp som ska visas i åkturs/attraktions-listan +STR_1842 :{COMMA16} besökares favorit +STR_1843 :{COMMA16} besökares favorit +STR_1844 :{SMALLFONT}{BLACK}Välj informationstyp som ska visas i åkturs- och attraktionslistan STR_1845 :{MONTHYEAR} -STR_1846 :{COMMA16} gäster -STR_1847 :{INLINE_SPRITE}{11}{20}{00}{00}{COMMA16} gäster -STR_1848 :{INLINE_SPRITE}{10}{20}{00}{00}{COMMA16} gäster +STR_1846 :{COMMA16} besökare +STR_1847 :{INLINE_SPRITE}{11}{20}{00}{00}{COMMA16} besökare +STR_1848 :{INLINE_SPRITE}{10}{20}{00}{00}{COMMA16} besökare STR_1849 :{WINDOW_COLOUR_2}Spela musik{MOVE_X}{138}Tema till dekorationer STR_1850 :{SMALLFONT}{BLACK}Välj om musik ska spelas på denna åktur STR_1851 :{WINDOW_COLOUR_2}Underhållskostnad: {BLACK}{CURRENCY2DP} per timme STR_1852 :{WINDOW_COLOUR_2}Underhållskostnad: {BLACK}Okänd STR_1853 :{WINDOW_COLOUR_2}Byggd: {BLACK}Detta år STR_1854 :{WINDOW_COLOUR_2}Byggd: {BLACK}Förra året -STR_1855 :{WINDOW_COLOUR_2}Byggd: {BLACK}{COMMA16} År sedan +STR_1855 :{WINDOW_COLOUR_2}Byggd: {BLACK}För {COMMA16} år sedan STR_1856 :{WINDOW_COLOUR_2}Vinst per styck: {BLACK}{CURRENCY2DP} STR_1857 :{WINDOW_COLOUR_2}Förlust per styck: {BLACK}{CURRENCY2DP} STR_1858 :{WINDOW_COLOUR_2}Kostnad: {BLACK}{CURRENCY} per månad @@ -1240,7 +1240,7 @@ STR_1864 :Mekaniker STR_1865 :Säkerhetsvakt STR_1866 :Underhållare STR_1867 :{BLACK}{COMMA16} {STRINGID} -STR_1868 :Kan inte ändra antal rotationer... +STR_1868 :Kan inte ändra antal rotationer … STR_1869 :{WINDOW_COLOUR_2}Antal rotationer: STR_1870 :{SMALLFONT}{BLACK}Antal hela rotationer STR_1871 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} @@ -1264,23 +1264,23 @@ STR_1889 :{WINDOW_COLOUR_2}Dötid: {MOVE_X}{255}{BLACK}{COMMA16}% STR_1890 :{SMALLFONT}{BLACK}Välj hur ofta en mekaniker ska göra underhåll på den här åkturen STR_1891 :Ingen {STRINGID} i parken än! STR_1894 :{WINDOW_COLOUR_2}{STRINGID} sålde: {BLACK}{COMMA32} -STR_1895 :{SMALLFONT}{BLACK}Bygg ny Åktur/attraktion -STR_1896 :{WINDOW_COLOUR_2}Utgifter/Inkomster -STR_1897 :{WINDOW_COLOUR_2}Åkturs konstruktioner -STR_1898 :{WINDOW_COLOUR_2}Åkturs underhållnad +STR_1895 :{SMALLFONT}{BLACK}Bygg ny åktur eller attraktion +STR_1896 :{WINDOW_COLOUR_2}Utgifter och inkomster +STR_1897 :{WINDOW_COLOUR_2}Konstruktion av åkturer +STR_1898 :{WINDOW_COLOUR_2}Underhållning av åkturer STR_1899 :{WINDOW_COLOUR_2}Markinköp STR_1900 :{WINDOW_COLOUR_2}Terrängmodifiering STR_1901 :{WINDOW_COLOUR_2}Parkinträde STR_1902 :{WINDOW_COLOUR_2}Åktursbiljetter STR_1903 :{WINDOW_COLOUR_2}Sålda varor STR_1904 :{WINDOW_COLOUR_2}Affärslager -STR_1905 :{WINDOW_COLOUR_2}Såld mat/dryck -STR_1906 :{WINDOW_COLOUR_2}Mat/dryck-lager +STR_1905 :{WINDOW_COLOUR_2}Såld mat och dryck +STR_1906 :{WINDOW_COLOUR_2}Mat- och dryckeslager STR_1907 :{WINDOW_COLOUR_2}Löner STR_1908 :{WINDOW_COLOUR_2}Marknadsföring STR_1909 :{WINDOW_COLOUR_2}Forskning STR_1910 :{WINDOW_COLOUR_2}Låneränta -STR_1911 :{BLACK} på {COMMA16}% per år +STR_1911 :{BLACK} vid {COMMA16}% per år STR_1912 :{MONTH} STR_1913 :{BLACK}+{CURRENCY2DP} STR_1914 :{BLACK}{CURRENCY2DP} @@ -1293,12 +1293,12 @@ STR_1920 :Kan inte betala tillbaka lån! STR_1921 :{SMALLFONT}{BLACK}Starta ett nytt spel STR_1922 :{SMALLFONT}{BLACK}Fortsätt spela ett sparat spel STR_1924 :{SMALLFONT}{BLACK}Avsluta -STR_1925 :Kan inte placera personen här... +STR_1925 :Kan inte placera personen här … STR_1926 :{SMALLFONT} STR_1927 :{YELLOW}{STRINGID} har gått sönder STR_1928 :{RED}{STRINGID} har kraschat! -STR_1929 :{RED}{STRINGID} har fortfarande inte blivit fixad{NEWLINE}Undersök var dina mekaniker är och överväg att organisera dem bättre -STR_1930 :{SMALLFONT}{BLACK}Aktivera/inaktivera spårning av denna gäst - (Om spårning är på kommer gästens handlingar att visas i meddelandeområdet) +STR_1929 :{RED}{STRINGID} har fortfarande inte blivit fixad{NEWLINE}Ta reda på var dina mekaniker är och överväg att organisera dem bättre +STR_1930 :{SMALLFONT}{BLACK}Slå av eller på spårning av denna besökare – (Om spårning är på kommer besökarens handlingar att visas i meddelandeområdet) STR_1931 :{STRINGID} har ställt sig i kö till {STRINGID} STR_1932 :{STRINGID} är på {STRINGID} STR_1933 :{STRINGID} är i {STRINGID} @@ -1306,18 +1306,18 @@ STR_1934 :{STRINGID} har lämnat {STRINGID} STR_1935 :{STRINGID} has lämnat parken STR_1936 :{STRINGID} har köpt {STRINGID} STR_1937 :{SMALLFONT}{BLACK}Visa information om meddelandets ämne -STR_1938 :{SMALLFONT}{BLACK}Visa gästvyn +STR_1938 :{SMALLFONT}{BLACK}Visa besökarvyn STR_1939 :{SMALLFONT}{BLACK}Visa personalvyn -STR_1940 :{SMALLFONT}{BLACK}Visa glädje, energi, hunger etc. för denna gäst -STR_1941 :{SMALLFONT}{BLACK}Visa vilka åkturer denna gäst har varit på -STR_1942 :{SMALLFONT}{BLACK}Visa ekonomisk information om denna gäst -STR_1943 :{SMALLFONT}{BLACK}Visa gästens nyliga tankar -STR_1944 :{SMALLFONT}{BLACK}Visa souvenirer som gästen bär på +STR_1940 :{SMALLFONT}{BLACK}Visa glädje, energi, hunger etc. för denna besökare +STR_1941 :{SMALLFONT}{BLACK}Visa vilka åkturer denna besökare har varit på +STR_1942 :{SMALLFONT}{BLACK}Visa ekonomisk information om denna besökare +STR_1943 :{SMALLFONT}{BLACK}Visa besökarens senaste tankar +STR_1944 :{SMALLFONT}{BLACK}Visa souvenirer som besökaren bär på STR_1945 :{SMALLFONT}{BLACK}Visa order och inställningar för personal STR_1946 :{SMALLFONT}{BLACK}Välj dräkt för den här underhållaren -STR_1947 :{SMALLFONT}{BLACK}Visa områden som patrulleras av den valda personaltypen, och hitta den närmsta personalen +STR_1947 :{SMALLFONT}{BLACK}Visa områden som patrulleras av den valda personaltypen, och hitta den närmsta anställda STR_1948 :{SMALLFONT}{BLACK}Anställ ny personal av den valda typen -STR_1949 :Ekonomisk Sammanfattning +STR_1949 :Ekonomisk sammanfattning STR_1950 :Ekonomigraf STR_1951 :Parkvärdesgraf STR_1952 :Vinstgraf @@ -1327,33 +1327,33 @@ STR_1955 :{WINDOW_COLOUR_2}Antal varv: STR_1956 :{SMALLFONT}{BLACK}Antal varv per åk STR_1957 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} STR_1958 :{COMMA16} -STR_1959 :Kan inte ändra antal varv... -STR_1960 :{WINDOW_COLOUR_2}Pris på Ballong: -STR_1961 :{WINDOW_COLOUR_2}Pris på Mjukisdjur: -STR_1962 :{WINDOW_COLOUR_2}Pris på Karta: -STR_1963 :{WINDOW_COLOUR_2}Pris på Åktursfoto: -STR_1964 :{WINDOW_COLOUR_2}Pris på Paraply: -STR_1965 :{WINDOW_COLOUR_2}Pris på Dricka: -STR_1966 :{WINDOW_COLOUR_2}Pris på Hamburgare: -STR_1967 :{WINDOW_COLOUR_2}Pris på Pommes frites: -STR_1968 :{WINDOW_COLOUR_2}Pris på Glass: -STR_1969 :{WINDOW_COLOUR_2}Pris på Sockervadd: +STR_1959 :Kan inte ändra antal varv … +STR_1960 :{WINDOW_COLOUR_2}Pris på ballong: +STR_1961 :{WINDOW_COLOUR_2}Pris på mjukisdjur: +STR_1962 :{WINDOW_COLOUR_2}Pris på karta: +STR_1963 :{WINDOW_COLOUR_2}Pris på åktursfoto: +STR_1964 :{WINDOW_COLOUR_2}Pris på paraply: +STR_1965 :{WINDOW_COLOUR_2}Pris på dricka: +STR_1966 :{WINDOW_COLOUR_2}Pris på hamburgare: +STR_1967 :{WINDOW_COLOUR_2}Pris på pommes frites: +STR_1968 :{WINDOW_COLOUR_2}Pris på glass: +STR_1969 :{WINDOW_COLOUR_2}Pris på sockervadd: STR_1970 :{WINDOW_COLOUR_2} STR_1971 :{WINDOW_COLOUR_2} STR_1972 :{WINDOW_COLOUR_2} -STR_1973 :{WINDOW_COLOUR_2}Pris på Pizza: +STR_1973 :{WINDOW_COLOUR_2}Pris på pizza: STR_1974 :{WINDOW_COLOUR_2} -STR_1975 :{WINDOW_COLOUR_2}Pris på Popcorn: -STR_1976 :{WINDOW_COLOUR_2}Pris på Varmkorv: -STR_1977 :{WINDOW_COLOUR_2}Pris på Tentakel: -STR_1978 :{WINDOW_COLOUR_2}Pris på Hatt: -STR_1979 :{WINDOW_COLOUR_2}Pris på Kanderat Äpple: -STR_1980 :{WINDOW_COLOUR_2}Pris på T-Shirt: -STR_1981 :{WINDOW_COLOUR_2}Pris på Munk: -STR_1982 :{WINDOW_COLOUR_2}Pris på Kaffe: +STR_1975 :{WINDOW_COLOUR_2}Pris på popcorn: +STR_1976 :{WINDOW_COLOUR_2}Pris på varmkorv: +STR_1977 :{WINDOW_COLOUR_2}Pris på tentakel: +STR_1978 :{WINDOW_COLOUR_2}Pris på hatt: +STR_1979 :{WINDOW_COLOUR_2}Pris på kanderat äpple: +STR_1980 :{WINDOW_COLOUR_2}Pris på T-shirt: +STR_1981 :{WINDOW_COLOUR_2}Pris på munk: +STR_1982 :{WINDOW_COLOUR_2}Pris på kaffe: STR_1983 :{WINDOW_COLOUR_2} -STR_1984 :{WINDOW_COLOUR_2}Pris på Friterad Kyckling: -STR_1985 :{WINDOW_COLOUR_2}Pris på Saft: +STR_1984 :{WINDOW_COLOUR_2}Pris på friterad kyckling: +STR_1985 :{WINDOW_COLOUR_2}Pris på saft: STR_1986 :{WINDOW_COLOUR_2} STR_1987 :{WINDOW_COLOUR_2} STR_1988 :Ballong @@ -1363,26 +1363,26 @@ STR_1991 :Åktursfoto STR_1992 :Paraply STR_1993 :Dricka STR_1994 :Hamburgare -STR_1995 :Pommes Frites +STR_1995 :Pommes frites STR_1996 :Glass STR_1997 :Sockervadd -STR_1998 :Tom Mugg +STR_1998 :Tomburk STR_1999 :Skräp -STR_2000 :Tom Förpackning +STR_2000 :Tom hamburgerförpackning STR_2001 :Pizza STR_2002 :Kupong STR_2003 :Popcorn STR_2004 :Varmkorv STR_2005 :Tentakel STR_2006 :Hatt -STR_2007 :Kanderat Äpple -STR_2008 :T-Shirt +STR_2007 :Kanderat äpple +STR_2008 :T-shirt STR_2009 :Munk STR_2010 :Kaffe -STR_2011 :Tom Mugg +STR_2011 :Tom mugg STR_2012 :Friterad Kyckling STR_2013 :Saft -STR_2014 :Tom Låda +STR_2014 :Tom låda STR_2015 :Tomflaska STR_2016 :Ballonger STR_2017 :Mjukisdjur @@ -1391,109 +1391,109 @@ STR_2019 :Åktursfoton STR_2020 :Paraplyn STR_2021 :Drickor STR_2022 :Hamburgare -STR_2023 :Pommes Frites +STR_2023 :Pommes frites STR_2024 :Glassar STR_2025 :Sockervadd STR_2026 :Tomburkar STR_2027 :Skräp -STR_2028 :Tomma Förpackningar +STR_2028 :Tomma förpackningar STR_2029 :Pizzor STR_2030 :Kuponger STR_2031 :Popcorn STR_2032 :Varmkorvar STR_2033 :Tentakler STR_2034 :Hattar -STR_2035 :Kanderade Äpplen -STR_2036 :T-Shirts +STR_2035 :Kanderade äpplen +STR_2036 :T-shirtar STR_2037 :Munkar STR_2038 :Kaffekoppar -STR_2039 :Tomma Muggar -STR_2040 :Friterad Kyckling +STR_2039 :Tomma muggar +STR_2040 :Friterad kyckling STR_2041 :Saft -STR_2042 :Tomma Lådor +STR_2042 :Tomlådor STR_2043 :Tomflaskor -STR_2044 :en Ballong -STR_2045 :ett Mjukisdjur -STR_2046 :en Karta -STR_2047 :ett Åktursfoto -STR_2048 :ett Paraply -STR_2049 :en Dricka -STR_2050 :en Hamburgare -STR_2051 :Pommes Frites -STR_2052 :en Glass -STR_2053 :Sockervadd -STR_2054 :en Tomburk -STR_2055 :Skräp -STR_2056 :en Tom Förpackning -STR_2057 :en Pizza -STR_2058 :en Kupong -STR_2059 :Popcorn -STR_2060 :en Varmkorv -STR_2061 :en Tentakel -STR_2062 :en Hatt -STR_2063 :ett Kanderat Äpple +STR_2044 :en ballong +STR_2045 :ett mjukisdjur +STR_2046 :en karta +STR_2047 :ett åktursfoto +STR_2048 :ett paraply +STR_2049 :en dricka +STR_2050 :en hamburgare +STR_2051 :lite pommes frites +STR_2052 :en glass +STR_2053 :lite sockervadd +STR_2054 :en tomburk +STR_2055 :lite skräp +STR_2056 :en tom förpackning +STR_2057 :en pizza +STR_2058 :en kupong +STR_2059 :lite popcorn +STR_2060 :en varmkorv +STR_2061 :en tentakel +STR_2062 :en hatt +STR_2063 :ett kanderat äpple STR_2064 :en T-Shirt STR_2065 :en Munk STR_2066 :en Kaffe STR_2067 :en Tom Mugg -STR_2068 :Friterad Kyckling -STR_2069 :Saft -STR_2070 :en Tom Låda -STR_2071 :en Tomflaska -STR_2072 :“{STRINGID}” Ballong -STR_2073 :“{STRINGID}” Mjukisdjur +STR_2068 :lite friterad kyckling +STR_2069 :lite saft +STR_2070 :en tomlåda +STR_2071 :en tomflaska +STR_2072 :Ballong från ”{STRINGID}” +STR_2073 :Mjukisdjur från ”{STRINGID}” STR_2074 :Karta över {STRINGID} STR_2075 :Åktursfoto från {STRINGID} -STR_2076 :“{STRINGID}” Paraply +STR_2076 :Paraply från ”{STRINGID}” STR_2077 :Dricka STR_2078 :Hamburgare -STR_2079 :Pommes Frites +STR_2079 :Pommes frites STR_2080 :Glass STR_2081 :Sockervadd STR_2082 :Tomburk STR_2083 :Skräp -STR_2084 :Tom Förpackning +STR_2084 :Tom förpackning STR_2085 :Pizza STR_2086 :Kupong för {STRINGID} STR_2087 :Popcorn STR_2088 :Varmkorv STR_2089 :Tentakel -STR_2090 :“{STRINGID}” Hatt -STR_2091 :Kanderat Äpple -STR_2092 :“{STRINGID}” T-Shirt +STR_2090 :Hatt från ”{STRINGID}” +STR_2091 :Kanderat äpple +STR_2092 :T-shirt från ”{STRINGID}” STR_2093 :Munk STR_2094 :Kaffe STR_2095 :Tom Mugg -STR_2096 :Friterad Kyckling +STR_2096 :Friterad kyckling STR_2097 :Saft -STR_2098 :Tom Låda +STR_2098 :Tom låda STR_2099 :Tomflaska -STR_2103 :{WINDOW_COLOUR_2}Pris på Kringla: -STR_2104 :{WINDOW_COLOUR_2}Pris på Varm Choklad: -STR_2105 :{WINDOW_COLOUR_2}Pris på Isté: -STR_2106 :{WINDOW_COLOUR_2}Pris på Struva: -STR_2107 :{WINDOW_COLOUR_2}Pris på Solglasögon: -STR_2108 :{WINDOW_COLOUR_2}Pris på Biffnudlar: -STR_2109 :{WINDOW_COLOUR_2}Pris på Stekta Nudlar: -STR_2110 :{WINDOW_COLOUR_2}Pris på Wontonsoppa: -STR_2111 :{WINDOW_COLOUR_2}Pris på Köttbullssoppa: -STR_2112 :{WINDOW_COLOUR_2}Pris på Fruktjuice: -STR_2113 :{WINDOW_COLOUR_2}Pris på Sojamjölk: -STR_2114 :{WINDOW_COLOUR_2}Pris på Sujeonggwa: -STR_2115 :{WINDOW_COLOUR_2}Pris på Macka: -STR_2116 :{WINDOW_COLOUR_2}Pris på Kaka: +STR_2103 :{WINDOW_COLOUR_2}Pris på kringla: +STR_2104 :{WINDOW_COLOUR_2}Pris på varm choklad: +STR_2105 :{WINDOW_COLOUR_2}Pris på iste: +STR_2106 :{WINDOW_COLOUR_2}Pris på struva: +STR_2107 :{WINDOW_COLOUR_2}Pris på solglasögon: +STR_2108 :{WINDOW_COLOUR_2}Pris på nudlar med biff: +STR_2109 :{WINDOW_COLOUR_2}Pris på stekta nudlar: +STR_2110 :{WINDOW_COLOUR_2}Pris på wontonsoppa: +STR_2111 :{WINDOW_COLOUR_2}Pris på köttbullssoppa: +STR_2112 :{WINDOW_COLOUR_2}Pris på fruktjuice: +STR_2113 :{WINDOW_COLOUR_2}Pris på sojamjölk: +STR_2114 :{WINDOW_COLOUR_2}Pris på sujeonggwa: +STR_2115 :{WINDOW_COLOUR_2}Pris på macka: +STR_2116 :{WINDOW_COLOUR_2}Pris på kaka: STR_2117 :{WINDOW_COLOUR_2} STR_2118 :{WINDOW_COLOUR_2} STR_2119 :{WINDOW_COLOUR_2} -STR_2120 :{WINDOW_COLOUR_2}Pris på Ugnskorv: +STR_2120 :{WINDOW_COLOUR_2}Pris på ugnskorv: STR_2121 :{WINDOW_COLOUR_2} STR_2125 :Kringla -STR_2126 :Varm Choklad -STR_2127 :Isté +STR_2126 :Varm choklad +STR_2127 :Iste STR_2128 :Struva STR_2129 :Solglasögon -STR_2130 :Biffnudlar -STR_2131 :Stekta Nudlar +STR_2130 :Nudlar med biff +STR_2131 :Stekta nudlar STR_2132 :Wontonsoppa STR_2133 :Köttbullssoppa STR_2134 :Fruktjuice @@ -1501,56 +1501,56 @@ STR_2135 :Sojamjölk STR_2136 :Sujeonggwa STR_2137 :Macka STR_2138 :Kaka -STR_2139 :Tom Skål -STR_2140 :Tom Pappmugg -STR_2141 :Tom Juiceförpackning +STR_2139 :Tom skål +STR_2140 :Tom pappmugg +STR_2141 :Tom juiceförpackning STR_2142 :Ugnskorv -STR_2143 :Tom Skål +STR_2143 :Tom skål STR_2147 :Kringla -STR_2148 :Muggar Varm Choklad -STR_2149 :Istéer +STR_2148 :Muggar med varm choklad +STR_2149 :Isteer STR_2150 :Struvor STR_2151 :Solglasögon -STR_2152 :Biffnudlar -STR_2153 :Friterade Nudlar +STR_2152 :Nudlar med biff +STR_2153 :Friterade nudlar STR_2154 :Wontonsoppor STR_2155 :Köttbullssoppor STR_2156 :Fruktjuicer -STR_2157 :Glas Sojamjölk +STR_2157 :Glas med sojamjölk STR_2158 :Sujeonggwas STR_2159 :Mackor STR_2160 :Kakor -STR_2161 :Tomma Skålar -STR_2162 :Tomma Pappmuggar -STR_2163 :Tomma Juiceförpackningar +STR_2161 :Tomma skålar +STR_2162 :Tomma pappmuggar +STR_2163 :Tomma juiceförpackningar STR_2164 :Ugnskorvar -STR_2165 :Tomma Skålar -STR_2169 :en Kringla -STR_2170 :en Varm Choklad -STR_2171 :en Isté -STR_2172 :en Struva -STR_2173 :ett par Solglasögon -STR_2174 :Biffnudlar -STR_2175 :Stekta Nudlar -STR_2176 :Wontonsoppa -STR_2177 :Köttbullssoppa -STR_2178 :en Fruktjuice -STR_2179 :Sojamjölk -STR_2180 :Sujeonggwa -STR_2181 :en Macka -STR_2182 :en Kaka -STR_2183 :en Tom Skål -STR_2184 :en Tom Pappmugg -STR_2185 :en Tom Juiceförpackning -STR_2186 :en Ugnskorv -STR_2187 :en Tom Skål +STR_2165 :Tomma skålar +STR_2169 :en kringla +STR_2170 :en varm choklad +STR_2171 :en iste +STR_2172 :en struva +STR_2173 :ett par solglasögon +STR_2174 :lite nudlar med biff +STR_2175 :lite stekta nudlar +STR_2176 :lite wontonsoppa +STR_2177 :lite köttbullssoppa +STR_2178 :en fruktjuice +STR_2179 :lite sojamjölk +STR_2180 :lite sujeonggwa +STR_2181 :en macka +STR_2182 :en kaka +STR_2183 :en tom skål +STR_2184 :en tom pappmugg +STR_2185 :en tom juiceförpackning +STR_2186 :en ugnskorv +STR_2187 :en tom skål STR_2191 :Kringla -STR_2192 :Varm Choklad -STR_2193 :Isté +STR_2192 :Varm choklad +STR_2193 :Iste STR_2194 :Struva STR_2195 :Solglasögon -STR_2196 :Biffnudlar -STR_2197 :Stekta Nudlar +STR_2196 :Nudlar med biff +STR_2197 :Stekta nudlar STR_2198 :Wontonsoppa STR_2199 :Köttbullssoppa STR_2200 :Fruktjuice @@ -1558,61 +1558,61 @@ STR_2201 :Sojamjölk STR_2202 :Sujeonggwa STR_2203 :Macka STR_2204 :Kaka -STR_2205 :Tom Skål -STR_2206 :Tom Pappmugg -STR_2207 :Tom Juiceförpackning +STR_2205 :Tom skål +STR_2206 :Tom pappmugg +STR_2207 :Tom juiceförpackning STR_2208 :Ugnskorv -STR_2209 :Tom Skål -STR_2210 :{SMALLFONT}{BLACK}Visa en lista över alla vaktmästare -STR_2211 :{SMALLFONT}{BLACK}Visa en lista över alla mekaniker -STR_2212 :{SMALLFONT}{BLACK}Visa en lista över alla säkerhetsvakter -STR_2213 :{SMALLFONT}{BLACK}Visa en lista över alla underhållare +STR_2209 :Tom skål +STR_2210 :{SMALLFONT}{BLACK}Visa listan över alla vaktmästare +STR_2211 :{SMALLFONT}{BLACK}Visa listan över alla mekaniker +STR_2212 :{SMALLFONT}{BLACK}Visa listan över alla säkerhetsvakter +STR_2213 :{SMALLFONT}{BLACK}Visa listan över alla underhållare STR_2214 :Konstruktion är inte möjligt när spelet är pausat! STR_2215 :{STRINGID}{NEWLINE}({STRINGID}) STR_2216 :{WINDOW_COLOUR_2}{COMMA16}°C STR_2217 :{WINDOW_COLOUR_2}{COMMA16}°F -STR_2218 :{RED}{STRINGID} på {STRINGID} har inte kommit tillbaka till {STRINGID} än!{NEWLINE}Kolla om den är fast +STR_2218 :{RED}{STRINGID} på {STRINGID} har inte kommit tillbaka till {STRINGID} än!{NEWLINE}Kolla om den sitter fast eller har fått motorstopp STR_2219 :{RED}{COMMA16} personer har dött i en olycka på {STRINGID} STR_2220 :{WINDOW_COLOUR_2}Parkomdöme: {BLACK}{COMMA16} STR_2221 :{SMALLFONT}{BLACK}Parkomdöme: {COMMA16} STR_2222 :{SMALLFONT}{BLACK}{STRINGID} -STR_2223 :{WINDOW_COLOUR_2}Antal gäster: {BLACK}{COMMA16} +STR_2223 :{WINDOW_COLOUR_2}Antal besökare: {BLACK}{COMMA16} STR_2224 :{WINDOW_COLOUR_2}Pengar: {BLACK}{CURRENCY2DP} STR_2225 :{WINDOW_COLOUR_2}Pengar: {RED}{CURRENCY2DP} STR_2226 :{WINDOW_COLOUR_2}Parkvärde: {BLACK}{CURRENCY} STR_2227 :{WINDOW_COLOUR_2}Företagsvärde: {BLACK}{CURRENCY} -STR_2228 :{WINDOW_COLOUR_2}Förra månadens vinst från mat/dricka och{NEWLINE}souvenirer: {BLACK}{CURRENCY} +STR_2228 :{WINDOW_COLOUR_2}Förra månadens vinst från mat, dryck och{NEWLINE}souvenirer: {BLACK}{CURRENCY} STR_2229 :Uppförsbacke till vertikalt STR_2230 :Vertikalt STR_2231 :Stoppbroms för nedförsbacke STR_2232 :Upplyftsbacke STR_2233 :{SMALLFONT}{BLACK}Parkinformation -STR_2234 :Nyliga meddelanden +STR_2234 :Senaste meddelanden STR_2235 :{SMALLFONT}{STRINGID} {STRINGID} -STR_2236 :Januari -STR_2237 :Februari -STR_2238 :Mars -STR_2239 :April -STR_2240 :Maj -STR_2241 :Juni -STR_2242 :Juli -STR_2243 :Augusti -STR_2244 :September -STR_2245 :Oktober -STR_2246 :November -STR_2247 :December -STR_2248 :Kan inte riva åktur/attraktion... -STR_2249 :{BABYBLUE}Ny åktur/attraktion är nu tillgänglig:{NEWLINE}{STRINGID} -STR_2250 :{BABYBLUE}Nya dekorationer/teman är nu tillgängliga:{NEWLINE}{STRINGID} -STR_2251 :Kan bara byggas på gångvägar! -STR_2252 :Kan bara byggas över gångvägar! +STR_2236 :januari +STR_2237 :februari +STR_2238 :mars +STR_2239 :april +STR_2240 :maj +STR_2241 :juni +STR_2242 :juli +STR_2243 :augusti +STR_2244 :september +STR_2245 :oktober +STR_2246 :november +STR_2247 :december +STR_2248 :Kan inte riva åktur eller attraktion … +STR_2249 :{BABYBLUE}Ny åktur eller attraktion är nu tillgänglig:{NEWLINE}{STRINGID} +STR_2250 :{BABYBLUE}Nya dekorationer eller teman är nu tillgängliga:{NEWLINE}{STRINGID} +STR_2251 :Kan endast byggas på gångvägar! +STR_2252 :Kan endast byggas över gångvägar! STR_2253 :Transportåkturer -STR_2254 :Lugna Åkturer -STR_2255 :Berg- och Dalbanor -STR_2256 :Spännande Åkturer +STR_2254 :Lugna åkturer +STR_2255 :Berg- och dalbanor +STR_2256 :Spännande åkturer STR_2257 :Vattenåkturer -STR_2258 :Affärer & Stånd -STR_2259 :Dekorationer & Teman +STR_2258 :Affärer och stånd +STR_2259 :Dekorationer och teman STR_2260 :Ingen finansiering STR_2261 :Minimal finansiering STR_2262 :Normal finansiering @@ -1625,19 +1625,19 @@ STR_2268 :Tidigare utveckling STR_2269 :{WINDOW_COLOUR_2}Typ: {BLACK}{STRINGID} STR_2270 :{WINDOW_COLOUR_2}Status: {BLACK}{STRINGID} STR_2271 :{WINDOW_COLOUR_2}Slutfört: {BLACK}{STRINGID} -STR_2272 :{WINDOW_COLOUR_2}Åktur/attraktion:{NEWLINE}{BLACK}{STRINGID} -STR_2273 :{WINDOW_COLOUR_2}Dekorationer/teman:{NEWLINE}{BLACK}{STRINGID} +STR_2272 :{WINDOW_COLOUR_2}Åktur eller attraktion:{NEWLINE}{BLACK}{STRINGID} +STR_2273 :{WINDOW_COLOUR_2}Dekorationer och teman:{NEWLINE}{BLACK}{STRINGID} STR_2274 :{SMALLFONT}{BLACK}Visa detaljer kring denna uppfinning eller utveckling -STR_2275 :{SMALLFONT}{BLACK}Visa finansiering och inställningar för forskning & utveckling +STR_2275 :{SMALLFONT}{BLACK}Visa finansiering och inställningar för forskning och utveckling STR_2276 :{SMALLFONT}{BLACK}Visa status för forskning och utveckling STR_2277 :Okänd STR_2278 :Transportåktur -STR_2279 :Lugn Åktur -STR_2280 :Berg- och Dalbana -STR_2281 :Spännande Åktur +STR_2279 :Lugn åktur +STR_2280 :Berg- och dalbana +STR_2281 :Spännande åktur STR_2282 :Vattenåktur -STR_2283 :Affär/Stånd -STR_2284 :Dekorationer/Teman +STR_2283 :Affär eller stånd +STR_2284 :Dekorationer och teman STR_2285 :Inledande forskning STR_2286 :Design STR_2287 :Slutför design @@ -1660,24 +1660,24 @@ STR_2304 :{BLACK}Spenderade {CURRENCY2DP}{WINDOW_COLOUR_2} på {BLACK}{COMMA1 STR_2305 :Bandesignfiler STR_2306 :Spara bandesign STR_2307 :Välj designen {STRINGID} -STR_2308 :{STRINGID} Bandesigns +STR_2308 :{STRINGID} bandesigner STR_2309 :Installera ny bandesign STR_2310 :Bygg egen design -STR_2311 :{WINDOW_COLOUR_2}Glädjenivå: {BLACK}{COMMA2DP32} (approx.) -STR_2312 :{WINDOW_COLOUR_2}Intensitetsnivå: {BLACK}{COMMA2DP32} (approx.) -STR_2313 :{WINDOW_COLOUR_2}Illamående nivå: {BLACK}{COMMA2DP32} (approx.) +STR_2311 :{WINDOW_COLOUR_2}Glädjenivå: {BLACK}{COMMA2DP32} (ungefär) +STR_2312 :{WINDOW_COLOUR_2}Intensitetsnivå: {BLACK}{COMMA2DP32} (ungefär) +STR_2313 :{WINDOW_COLOUR_2}Illamående nivå: {BLACK}{COMMA2DP32} (ungefär) STR_2314 :{WINDOW_COLOUR_2}Åktur längd: {BLACK}{STRINGID} -STR_2315 :{WINDOW_COLOUR_2}Kostnad: {BLACK}ca {CURRENCY} -STR_2316 :{WINDOW_COLOUR_2}Storlek: {BLACK}{COMMA16} x {COMMA16} blocks -STR_2321 :{WINDOW_COLOUR_2}Antal åkturer/attraktioner: {BLACK}{COMMA16} +STR_2315 :{WINDOW_COLOUR_2}Kostnad: {BLACK}omkring {CURRENCY} +STR_2316 :{WINDOW_COLOUR_2}Storlek: {BLACK}{COMMA16} × {COMMA16} block +STR_2321 :{WINDOW_COLOUR_2}Antal åkturer och attraktioner: {BLACK}{COMMA16} STR_2322 :{WINDOW_COLOUR_2}Personal: {BLACK}{COMMA16} STR_2323 :{WINDOW_COLOUR_2}Parkstorlek: {BLACK}{COMMA32}m² -STR_2324 :{WINDOW_COLOUR_2}Parkstorlek: {BLACK}{COMMA32}sq.ft. +STR_2324 :{WINDOW_COLOUR_2}Parkstorlek: {BLACK}{COMMA32}fot² STR_2325 :{SMALLFONT}{BLACK}Köp land för att utöka parken STR_2326 :{SMALLFONT}{BLACK}Köp bygglov för att tillåta konstruktion över eller under land utanför parken STR_2327 :Inställningar STR_2328 :{WINDOW_COLOUR_2}Valuta: -STR_2329 :{WINDOW_COLOUR_2}Avstånd och Hastighet: +STR_2329 :{WINDOW_COLOUR_2}Avstånd och hastighet: STR_2330 :{WINDOW_COLOUR_2}Temperatur: STR_2331 :{WINDOW_COLOUR_2}Höjdmarkeringar: STR_2332 :Enheter @@ -1685,7 +1685,7 @@ STR_2333 :Ljudeffekter STR_2334 :Pund (£) STR_2335 :Dollar ($) STR_2336 :Franc (F) -STR_2337 :Deutsche mark (DM) +STR_2337 :Tyska mark (DM) STR_2338 :Yen (¥) STR_2339 :Peseta (Pts) STR_2340 :Lira (L) @@ -1695,22 +1695,22 @@ STR_2343 :Euro (€) STR_2344 :Brittiska STR_2345 :Meter STR_2347 :{RED}{STRINGID} har drunknat! -STR_2348 :{SMALLFONT}{BLACK}Visa statistik för personal +STR_2348 :{SMALLFONT}{BLACK}Visa statistik för anställd STR_2349 :{WINDOW_COLOUR_2}Lön: {BLACK}{CURRENCY} per månad STR_2350 :{WINDOW_COLOUR_2}Anställd: {BLACK}{MONTHYEAR} STR_2351 :{WINDOW_COLOUR_2}Klippta gräsmattor: {BLACK}{COMMA16} -STR_2352 :{WINDOW_COLOUR_2}Vattnade trädgårdar: {BLACK}{COMMA16} -STR_2353 :{WINDOW_COLOUR_2}Svepta sopor: {BLACK}{COMMA16} +STR_2352 :{WINDOW_COLOUR_2}Vattnade planteringar: {BLACK}{COMMA16} +STR_2353 :{WINDOW_COLOUR_2}Upplockat skräp: {BLACK}{COMMA16} STR_2354 :{WINDOW_COLOUR_2}Tömda sopkorgar: {BLACK}{COMMA16} -STR_2355 :{WINDOW_COLOUR_2}åkturer fixade: {BLACK}{COMMA16} -STR_2356 :{WINDOW_COLOUR_2}åkturer inspekterade: {BLACK}{COMMA16} +STR_2355 :{WINDOW_COLOUR_2}Reparerade åkturer: {BLACK}{COMMA16} +STR_2356 :{WINDOW_COLOUR_2}Inspekterade åkturer: {BLACK}{COMMA16} STR_2358 :Enheter -STR_2359 :Riktiga Värden +STR_2359 :Riktiga värden STR_2360 :{WINDOW_COLOUR_2}Skärmupplösning: STR_2361 :Terrängutjämning -STR_2362 :{SMALLFONT}{BLACK}Sätt på/av terrängkantutjämning +STR_2362 :{SMALLFONT}{BLACK}Slå på eller av terrängkantutjämning STR_2363 :Rutmönster på terräng -STR_2364 :{SMALLFONT}{BLACK}Sätt på/av rutmönster på terrängen +STR_2364 :{SMALLFONT}{BLACK}Slå på eller av rutmönster på terrängen STR_2365 :Banken vägrar att utöka ditt lån! STR_2366 :Celsius (°C) STR_2367 :Fahrenheit (°F) @@ -1723,7 +1723,7 @@ STR_2373 :Mellan STR_2374 :Hög STR_2375 :Väldigt hög STR_2376 :Extrem -STR_2377 :Ultra-Extrem +STR_2377 :Ultraextrem STR_2378 :{SMALLFONT}{BLACK}Justera ett mindre landområde STR_2379 :{SMALLFONT}{BLACK}Justera ett större landområde STR_2380 :{SMALLFONT}{BLACK}Justera ett mindre vattenområde @@ -1732,35 +1732,35 @@ STR_2382 :Land STR_2383 :Vatten STR_2384 :{WINDOW_COLOUR_2}Ditt uppdrag: STR_2385 :{BLACK}Inget -STR_2386 :{BLACK}Att ha minst {COMMA16} gäster i parken tills {MONTHYEAR}, med ett parkomdöme över 600 +STR_2386 :{BLACK}Att ha minst {COMMA16} besökare i parken tills {MONTHYEAR}, med ett parkomdöme över 600 STR_2387 :{BLACK}Att uppnå ett parkvärde över {POP16}{POP16}{CURRENCY} tills {PUSH16}{PUSH16}{PUSH16}{MONTHYEAR} -STR_2388 :{BLACK}Ha Kul! +STR_2388 :{BLACK}Ha kul! STR_2389 :{BLACK}Bygg den bästa {STRINGID} du kan! -STR_2390 :{BLACK}Att ha 10 olika sorters berg- och dalbanor i din park, alla med en glädjenivå över 6.00 -STR_2391 :{BLACK}Att ha minst {COMMA16} gäster i din park. Du får inte låta parkomdömet gå under 700! +STR_2390 :{BLACK}Att ha tio olika sorters berg- och dalbanor i din park, alla med en glädjenivå över 6.00 +STR_2391 :{BLACK}Att ha minst {COMMA16} besökare i din park. Du får inte låta parkomdömet gå under 700! STR_2392 :{BLACK}Att få en månadsinkomst från åkturs biljetter på minst {POP16}{POP16}{CURRENCY} -STR_2393 :{BLACK}Att ha 10 olika sorters berg- och dalbanor i din park, alla ska vara längre än {LENGTH}, och med en glädjenivå över 7.00 -STR_2394 :{BLACK}Att bygga klart alla 5 delvis byggda berg- och dalbanor i parken, designade så att de alla får en glädjenivå över {POP16}{POP16}{COMMA2DP32} +STR_2393 :{BLACK}Att ha tio olika sorters berg- och dalbanor i din park, alla ska vara längre än {LENGTH}, och med en glädjenivå över 7.00 +STR_2394 :{BLACK}Att bygga klart alla fem delvis byggda berg- och dalbanor i parken, designade så att de alla får en glädjenivå över {POP16}{POP16}{COMMA2DP32} STR_2395 :{BLACK}Att betala tillbaka ditt lån och uppnå ett parkvärde över {POP16}{POP16}{CURRENCY} STR_2396 :{BLACK}Att få en månadsinkomst från mat, dricka och souvenirer på minst {POP16}{POP16}{CURRENCY} STR_2397 :Inga -STR_2398 :Antal gäster vid ett visst datum +STR_2398 :Antal besökare vid ett visst datum STR_2399 :Parkvärde vid ett visst datum STR_2400 :Ha kul STR_2401 :Bygg den bästa åkturen du kan -STR_2402 :Bygg 10 berg- och dalbanor -STR_2403 :Antal gäster i parken +STR_2402 :Bygg tio berg- och dalbanor +STR_2403 :Antal besökare i parken STR_2404 :Månadsinkomst från åkturs biljetter -STR_2405 :Bygg 10 berg- och dalbanor med en viss längd -STR_2406 :Bygg klart 5 berg- och dalbanor +STR_2405 :Bygg tio berg- och dalbanor med en viss längd +STR_2406 :Bygg klart fem berg- och dalbanor STR_2407 :Betala tillbaka lån och uppnå ett visst parkvärde -STR_2408 :Månadsinkomst från mat/souvenirer +STR_2408 :Månadsinkomst från mat och souvenirer STR_2409 :{WINDOW_COLOUR_2}Aktiva kampanjer STR_2410 :{BLACK}Inga STR_2411 :{WINDOW_COLOUR_2}Tillgängliga kampanjer STR_2412 :{SMALLFONT}{BLACK}Påbörja kampanj STR_2413 :{BLACK}({CURRENCY2DP} per vecka) -STR_2414 :(Ej Vald) +STR_2414 :(Ej vald) STR_2415 :{WINDOW_COLOUR_2}Åktur: STR_2416 :{WINDOW_COLOUR_2}Produkt: STR_2417 :{WINDOW_COLOUR_2}Varaktighet: @@ -1792,7 +1792,7 @@ STR_2450 :{YELLOW}Din kampanj för parken har slutförts STR_2451 :{YELLOW}Din kampanj för {STRINGID} har slutförts STR_2452 :{WINDOW_COLOUR_2}Pengar (utan lån): {BLACK}{CURRENCY2DP} STR_2453 :{WINDOW_COLOUR_2}Pengar (utan lån): {RED}{CURRENCY2DP} -STR_2454 :{SMALLFONT}{BLACK}{CURRENCY2DP} - +STR_2454 :{SMALLFONT}{BLACK}{CURRENCY2DP} – STR_2457 :{SMALLFONT}{BLACK}Visa finansiella konton STR_2458 :{SMALLFONT}{BLACK}Visa graf med pengar (utan lån) över tid STR_2459 :{SMALLFONT}{BLACK}Visa graf med parkvärde över tid @@ -1800,11 +1800,11 @@ STR_2460 :{SMALLFONT}{BLACK}Visa graf med veckovis vinst STR_2461 :{SMALLFONT}{BLACK}Visa kampanjer STR_2462 :{SMALLFONT}{BLACK}Visa parkentrén STR_2463 :{SMALLFONT}{BLACK}Visa graf med parkomdömen över tid -STR_2464 :{SMALLFONT}{BLACK}Visa graf med gästantal över tid +STR_2464 :{SMALLFONT}{BLACK}Visa graf med besökarantal över tid STR_2465 :{SMALLFONT}{BLACK}Visa parkinträdespris och information STR_2466 :{SMALLFONT}{BLACK}Visa parkstatistik STR_2467 :{SMALLFONT}{BLACK}Visa uppdrag -STR_2468 :{SMALLFONT}{BLACK}Visa nyliga utmärkelser som denna park har blivit tilldelad +STR_2468 :{SMALLFONT}{BLACK}Visa de utmärkelser som parken tilldelats senast STR_2469 :{SMALLFONT}{BLACK}Välj nivå på forskning och utveckling STR_2470 :{SMALLFONT}{BLACK}Forska fram nya transportåkturer STR_2471 :{SMALLFONT}{BLACK}Forska fram nya lugna åkturer @@ -1813,7 +1813,7 @@ STR_2473 :{SMALLFONT}{BLACK}Forska fram nya spännande åkturer STR_2474 :{SMALLFONT}{BLACK}Forska fram nya vattenåkturer STR_2475 :{SMALLFONT}{BLACK}Forska fram nya affärer och stånd STR_2476 :{SMALLFONT}{BLACK}Forska fram nya dekorationer and teman -STR_2477 :{SMALLFONT}{BLACK}Välj körläge för denna åktur/attraktion +STR_2477 :{SMALLFONT}{BLACK}Välj körläge för denna åktur eller attraktion STR_2478 :{SMALLFONT}{BLACK}Visa graf med hastighet över tid STR_2479 :{SMALLFONT}{BLACK}Visa graf med höjd över tid STR_2480 :{SMALLFONT}{BLACK}Visa graf med vertikal acceleration över tid @@ -1821,9 +1821,9 @@ STR_2481 :{SMALLFONT}{BLACK}Visa graf med lateral acceleration över tid STR_2482 :{SMALLFONT}{BLACK}Vinst: {CURRENCY} per vecka, Parkvärde: {CURRENCY} STR_2483 :{WINDOW_COLOUR_2}Veckovis vinst: {BLACK}+{CURRENCY2DP} STR_2484 :{WINDOW_COLOUR_2}Veckovis vinst: {RED}{CURRENCY2DP} -STR_2487 :Visa 'riktiga' namn på gäster -STR_2488 :{SMALLFONT}{BLACK}Byt mellan 'riktiga' namn och gästnummer -STR_2489 :Snabbtangenter... +STR_2487 :Visa 'riktiga' namn på besökare +STR_2488 :{SMALLFONT}{BLACK}Byt mellan 'riktiga' namn och besökarnummer +STR_2489 :Snabbtangenter … STR_2490 :Snabbtangenter STR_2491 :Återställ snabbtangenter STR_2492 :{SMALLFONT}{BLACK}Återställ alla snabbtangenter till standardinställningar @@ -1854,9 +1854,9 @@ STR_2516 :Visa finansiell information STR_2517 :Visa forskningsinformation STR_2518 :Visa lista över åkturer STR_2519 :Visa parkinformation -STR_2520 :Visa gästlista +STR_2520 :Visa besökarlista STR_2521 :Visa personallista -STR_2522 :Visa nyliga meddelanden +STR_2522 :Visa senaste meddelanden STR_2523 :Visa karta STR_2524 :Skärmdump STR_2525 :??? @@ -1877,7 +1877,7 @@ STR_2539 :??? STR_2540 :??? STR_2541 :??? STR_2542 :??? -STR_2543 :Alt/Meny +STR_2543 :Alt / Meny STR_2544 :Pause STR_2545 :Caps STR_2546 :??? @@ -2017,18 +2017,18 @@ STR_2679 :??? STR_2680 :All forskning färdig STR_2681 :{MEDIUMFONT}{BLACK}Ökar pengarna med {CURRENCY} STR_2684 :{SMALLFONT}{BLACK}En stor grupp anländer -STR_2685 :Simpla Brusinställningar +STR_2685 :Parametrar för simplex-brus STR_2686 :Låg: STR_2687 :Hög: STR_2688 :Basfrekvens: -STR_2689 :Oktav: +STR_2689 :Oktaver: STR_2690 :Markgenerator -STR_2691 :Bas höjd: -STR_2692 :Vatten: +STR_2691 :Bashöjd: +STR_2692 :Vattennivå: STR_2693 :Terräng: STR_2694 :Generera -STR_2695 :Slumpvis terräng -STR_2696 :Placera trä +STR_2695 :Slumpmässig terräng +STR_2696 :Placera träd STR_2697 :??? STR_2698 :??? STR_2699 :??? @@ -2050,32 +2050,32 @@ STR_2714 :- STR_2715 :. STR_2716 :/ STR_2717 :' -STR_2718 :(upp) -STR_2719 :(Ny fil) -STR_2720 :{UINT16}sek -STR_2721 :{UINT16}sek -STR_2722 :{UINT16}min:{UINT16}sek -STR_2723 :{UINT16}min:{UINT16}sek -STR_2724 :{UINT16}mins:{UINT16}sek -STR_2725 :{UINT16}mins:{UINT16}sek +STR_2718 :Upp +STR_2719 :Ny fil +STR_2720 :{UINT16}s +STR_2721 :{UINT16}s +STR_2722 :{UINT16}min:{UINT16}s +STR_2723 :{UINT16}min:{UINT16}s +STR_2724 :{UINT16}mins:{UINT16}s +STR_2725 :{UINT16}mins:{UINT16}s STR_2726 :{UINT16}min -STR_2727 :{UINT16}mins +STR_2727 :{UINT16}min STR_2728 :{UINT16}tim:{UINT16}min -STR_2729 :{UINT16}tim:{UINT16}mins +STR_2729 :{UINT16}tim:{UINT16}min STR_2730 :{UINT16}tim:{UINT16}min -STR_2731 :{UINT16}tim:{UINT16}mins -STR_2732 :{COMMA16}ft +STR_2731 :{UINT16}tim:{UINT16}min +STR_2732 :{COMMA16}fot STR_2733 :{COMMA16}m STR_2734 :{COMMA16}mph STR_2735 :{COMMA16}km/h -STR_2736 :{MONTH}, År {COMMA16} -STR_2737 :{STRINGID} {MONTH}, År {COMMA16} +STR_2736 :{MONTH}, år {COMMA16} +STR_2737 :{STRINGID} {MONTH}, år {COMMA16} STR_2738 :Startskärmens musik: STR_2739 :Ingen STR_2740 :RollerCoaster Tycoon 1 STR_2741 :RollerCoaster Tycoon 2 STR_2742 :css50.dat hittades inte -STR_2743 :Kopiera 'data/css17.dat' från din RCT1-installation till 'data/css50.dat' i din RCT2-installation eller kolla så att sökvägen till RCT1 i 'Diverse' fliken är korrekt. +STR_2743 :Kopiera 'data/css17.dat' från din RCT1-installation till 'data/css50.dat' i din RCT2-installation eller kolla så att sökvägen till RCT1 under fliken 'Diverse' är korrekt. STR_2744 :[ STR_2745 :\ STR_2746 :] @@ -2085,13 +2085,13 @@ STR_2749 :Mitt nya scenario STR_2750 :Flytta alla föremål till toppen STR_2751 :Flytta alla föremål till botten STR_2752 :Plocka ogräs -STR_2753 :Klipp gräsmattan +STR_2753 :Klippte gräs STR_2754 :Vattna blommorna -STR_2755 :Fixa skadegörelsen +STR_2755 :Fixa skadegörelse STR_2756 :Städa nedskräpning # New strings used in the cheats window previously these were STR_2763 :??? -STR_2765 :Stort Tåg +STR_2765 :Stort tåg STR_2766 :Vinn scenario STR_2767 :Frys väder STR_2768 :Avfrys väder @@ -2099,22 +2099,22 @@ STR_2769 :Öppna Park STR_2770 :Stäng Park STR_2773 :Fönster STR_2774 :Fullskärm -STR_2775 :Fönster (Utan kanter) +STR_2775 :Fullskärm (utan kanter) STR_2776 :Språk: -STR_2777 :{MOVE_X}{SMALLFONT}{STRING} -STR_2778 :»{MOVE_X}{SMALLFONT}{STRING} +STR_2777 :{MOVE_X}{10}{STRING} +STR_2778 :»{MOVE_X}{10}{STRING} # End of new strings STR_2779 :Bildskärm #{COMMA16} STR_2780 :Extra bildskärm -STR_2781 :{STRINGID}:{MOVE_X}{195}{STRINGID} +STR_2781 :{STRINGID}:{MOVE_X}{255}{STRINGID} STR_2782 :SHIFT + STR_2783 :CTRL + STR_2784 :Ändra snabbtangenter -STR_2785 :{WINDOW_COLOUR_2}Välj ny snabbtangent för:{NEWLINE}“{STRINGID}” +STR_2785 :{WINDOW_COLOUR_2}Välj ny snabbtangent för:{NEWLINE}”{STRINGID}” STR_2786 :{SMALLFONT}{BLACK}Klicka på en beskrivning för att välja en ny snabbtangent STR_2787 :{WINDOW_COLOUR_2}Parkvärde: {BLACK}{CURRENCY} -STR_2788 :{WINDOW_COLOUR_2}Grattis !{NEWLINE}{BLACK}Du uppnådde ditt mål med ett företagsvärde på {CURRENCY} ! -STR_2789 :{WINDOW_COLOUR_2}Du har misslyckats med ditt mål ! +STR_2788 :{WINDOW_COLOUR_2}Grattis!{NEWLINE}{BLACK}Du uppnådde ditt mål med ett företagsvärde på {CURRENCY}! +STR_2789 :{WINDOW_COLOUR_2}Du har misslyckats med ditt mål! STR_2790 :Skriv in namn i scenariolistan STR_2791 :Skriv in namn STR_2792 :Var god skriv in ditt namn i scenariolistan: @@ -2128,17 +2128,17 @@ STR_2799 :{SMALLFONT}{BLACK}Visa eller ändra kontrollerna STR_2800 :{WINDOW_COLOUR_2}Totalt antal inträden: {BLACK}{COMMA32} STR_2801 :{WINDOW_COLOUR_2}Inkomst från inträden: {BLACK}{CURRENCY2DP} STR_2802 :Karta -STR_2803 :{SMALLFONT}{BLACK}Markera dessa gäster på kartan -STR_2804 :{SMALLFONT}{BLACK}Markera denna personal på kartan +STR_2803 :{SMALLFONT}{BLACK}Markera dessa besökare på kartan +STR_2804 :{SMALLFONT}{BLACK}Markera dessa anställda på kartan STR_2805 :{SMALLFONT}{BLACK}Visa karta över parken -STR_2806 :{RED}Gäster klagar på gångvägarnas snuskiga tillstånd{NEWLINE}Undersök var dina vaktmästare är och överväg att organisera dem bättre -STR_2807 :{RED}Gäster klagar på mängden skräp i din park{NEWLINE}Undersök var dina vaktmästare är och överväg att organisera dem bättre -STR_2808 :{RED}Gäster klagar på skadegörelsen i din park{NEWLINE}Undersök var dina säkerhetsvakter är och överväg att organisera dem bättre -STR_2809 :{RED}Gäster är hungriga och kan inte hitta någonstans att köpa mat -STR_2810 :{RED}Gäster är törstiga och kan inte hitta någonstans att köpa dricka -STR_2811 :{RED}Gäster klagar för att de inte kan hitta toaletterna i din park -STR_2812 :{RED}Gäster tappar bort sig eller fastnar{NEWLINE}Undersök om din vägplanering behöver förbättras för att hjälpa gästerna att hitta -STR_2813 :{RED}Ditt parkinträde är för dyrt!{NEWLINE}Sänk ditt inträde eller höj värdet på parken för att attrahera fler gäster +STR_2806 :{RED}Besökare klagar på gångvägarnas snuskiga tillstånd{NEWLINE}Undersök var dina vaktmästare är och överväg att organisera dem bättre +STR_2807 :{RED}Besökare klagar på mängden skräp i din park{NEWLINE}Undersök var dina vaktmästare är och överväg att organisera dem bättre +STR_2808 :{RED}Besökare klagar på skadegörelsen i din park{NEWLINE}Undersök var dina säkerhetsvakter är och överväg att organisera dem bättre +STR_2809 :{RED}Besökare är hungriga och kan inte hitta någonstans att köpa mat +STR_2810 :{RED}Besökare är törstiga och kan inte hitta någonstans att köpa dricka +STR_2811 :{RED}Besökare klagar för att de inte kan hitta toaletterna i din park +STR_2812 :{RED}Besökare tappar bort sig eller fastnar{NEWLINE}Undersök om din vägplanering behöver förbättras för att hjälpa besökarna att hitta +STR_2813 :{RED}Ditt parkinträde är för dyrt!{NEWLINE}Sänk ditt inträde eller höj värdet på parken för att attrahera fler besökare STR_2814 :{WINDOW_COLOUR_2}Utmärkelse för mest ostädade park STR_2815 :{WINDOW_COLOUR_2}Utmärkelse för mest välstädade park STR_2816 :{WINDOW_COLOUR_2}Utmärkelse för parken med bäst berg- och dalbanor @@ -2150,7 +2150,7 @@ STR_2821 :{WINDOW_COLOUR_2}Utmärkelse för bästa personal STR_2822 :{WINDOW_COLOUR_2}Utmärkelse för bästa mat STR_2823 :{WINDOW_COLOUR_2}Utmärkelse för sämsta mat STR_2824 :{WINDOW_COLOUR_2}Utmärkelse för bästa toaletterna -STR_2825 :{WINDOW_COLOUR_2}Utmärkelse för flest besvikna gäster +STR_2825 :{WINDOW_COLOUR_2}Utmärkelse för flest besvikna besökare STR_2826 :{WINDOW_COLOUR_2}Utmärkelse för bästa vattenåkturer STR_2827 :{WINDOW_COLOUR_2}Utmärkelse för bästa egendesignade åkturer STR_2828 :{WINDOW_COLOUR_2}Utmärkelse för finast färger på åkturer @@ -2167,25 +2167,25 @@ STR_2838 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med bäst per STR_2839 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med bäst mat i landet'! STR_2840 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med sämst mat i landet'! STR_2841 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med bäst toaletter i landet'! -STR_2842 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med flest besvikna gäster i landet'! +STR_2842 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med flest besvikna besökare i landet'! STR_2843 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med bäst vattenåkturer i landet'! STR_2844 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med bäst egendesignade åkturer'! STR_2845 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med finast färgval på åkturer'! STR_2846 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med mest förvirrande vägplanering'! STR_2847 :{TOPAZ}Din park har fått en utmärkelse för 'Parken med bäst lugna åkturer'! -STR_2848 :{WINDOW_COLOUR_2}Inga nyliga utmärkelser +STR_2848 :{WINDOW_COLOUR_2}Inga utmärkelser nyligen STR_2849 :Nytt scenario installerat STR_2850 :Ny bandesign installerad STR_2851 :Scenario redan installerat STR_2852 :Bandesign redan installerad STR_2853 :Förbjuden av lokala myndigheter! -STR_2854 :{RED}Gäster kan inte komma fram till ingången till {STRINGID} !{NEWLINE}Bygg en gångväg till ingången +STR_2854 :{RED}Besökare kan inte komma fram till ingången till {STRINGID}!{NEWLINE}Bygg en gångväg till ingången STR_2855 :{RED}{STRINGID} har ingen gångväg från sin utgång !{NEWLINE}Bygg en gångväg från åkturens utgång -STR_2858 :Kan inte påbörja kampanj... +STR_2858 :Kan inte påbörja kampanj … STR_2861 :{WINDOW_COLOUR_2}Licensed to Infogrames Interactive Inc. -STR_2862 :Om Musik... -STR_2863 :Om Musik -STR_2864 :{WINDOW_COLOUR_2}March - Children of the Regiment: (Fucik) non copyright +STR_2862 :Om musik … +STR_2863 :Om musik +STR_2864 :{WINDOW_COLOUR_2}March – Children of the Regiment: (Fucik) non copyright STR_2865 :{WINDOW_COLOUR_2}Heyken's Serenade: (J.Heyken) British Standard Music Coy; GEMA, BRITICO STR_2866 :{WINDOW_COLOUR_2}La Belle Espagnole: (Robert Vollstedt) non copyright STR_2867 :{WINDOW_COLOUR_2}Wedding Journey: (Traditional) @@ -2193,7 +2193,7 @@ STR_2868 :{WINDOW_COLOUR_2}Tales from the Vienna Woods: (Johann Strauss) no STR_2869 :{WINDOW_COLOUR_2}Slavonic Dance: (Traditional) STR_2870 :{WINDOW_COLOUR_2}Das Alpenhorn: (Traditional) STR_2871 :{WINDOW_COLOUR_2}The Blond Sailor: (Traditional) -STR_2872 :{WINDOW_COLOUR_2}Overture - Poet and Peasant: (Suppe) non copyright +STR_2872 :{WINDOW_COLOUR_2}Overture – Poet and Peasant: (Suppe) non copyright STR_2873 :{WINDOW_COLOUR_2}Waltz Medley: (Johann Strauss) non copyright STR_2874 :{WINDOW_COLOUR_2}Bella Bella Bimba: (Traditional) STR_2875 :{WINDOW_COLOUR_2}Original recordings (P) 1976 C.J.Mears Organization, used with consent @@ -2214,7 +2214,7 @@ STR_2889 :{WINDOW_COLOUR_2}Wild West Kid: (Allister Brimble) copyright © C STR_2890 :{WINDOW_COLOUR_2}Vampire's Lair: (Allister Brimble) copyright © Chris Sawyer STR_2891 :{WINDOW_COLOUR_2}Blockbuster: (Allister Brimble) copyright © Chris Sawyer STR_2892 :{WINDOW_COLOUR_2}Airtime Rock: (Allister Brimble) copyright © Chris Sawyer -STR_2893 :{WINDOW_COLOUR_2}Searchlight Rag: (Scott Joplin/Allister Brimble) copyright © Chris Sawyer +STR_2893 :{WINDOW_COLOUR_2}Searchlight Rag: (Scott Joplin / Allister Brimble) copyright © Chris Sawyer STR_2894 :{WINDOW_COLOUR_2}Flight of Fantasy: (Steve Blenkinsopp) copyright © Chris Sawyer STR_2895 :{WINDOW_COLOUR_2}Big Rock: (Allister Brimble) copyright © Chris Sawyer STR_2896 :{WINDOW_COLOUR_2}Hypothermia: (Allister Brimble) copyright © Chris Sawyer @@ -2227,24 +2227,24 @@ STR_2903 :{WINDOW_COLOUR_2}Space Rock: (Allister Brimble) copyright © Chri STR_2904 :{WINDOW_COLOUR_2}Manic Mechanic: (Allister Brimble) copyright © Chris Sawyer STR_2905 :{WINDOW_COLOUR_2}Techno Torture: (Allister Brimble) copyright © Chris Sawyer STR_2906 :{WINDOW_COLOUR_2}Sweat Dreams: (Allister Brimble) copyright © Chris Sawyer -STR_2907 :{WINDOW_COLOUR_2}What shall we do with the Drunken Sailor: (Anon/Allister Brimble) copyright © Chris Sawyer +STR_2907 :{WINDOW_COLOUR_2}What shall we do with the Drunken Sailor: (Anon / Allister Brimble) copyright © Chris Sawyer STR_2971 :Huvudfärg STR_2972 :Andra färg STR_2973 :Tredje färg STR_2974 :Fjärde färg STR_2975 :{SMALLFONT}{BLACK}Välj vilken färgpalett som ska ändras eller användas för målning av en åktur STR_2976 :{SMALLFONT}{BLACK}Måla ett enstaka område på denna åktur med den valda färgpaletten -STR_2977 :Personalnamn -STR_2978 :Skriv in nytt namn på personal: -STR_2979 :Kan inte namnge personal... +STR_2977 :Namn på anställd +STR_2978 :Skriv in nytt namn på anställd: +STR_2979 :Kan inte namnge anställd … STR_2980 :För många banderoller i spelet -STR_2981 :{RED}Tillträde förbjudet - - +STR_2981 :{RED}Tillträde förbjudet STR_2982 :Banderolltext STR_2983 :Skriv in ny text för denna banderoll: -STR_2984 :Kan inte sätta ny text på banderollen... +STR_2984 :Kan inte sätta ny text på banderollen … STR_2985 :Banderoll STR_2986 :{SMALLFONT}{BLACK}Byt text på banderoll -STR_2987 :{SMALLFONT}{BLACK}Sätt denna banderoll som en 'Tillträde förbjudet'-banderoll för gäster +STR_2987 :{SMALLFONT}{BLACK}Gör denna banderoll till en 'Tillträde förbjudet'-banderoll för besökare STR_2988 :{SMALLFONT}{BLACK}Riv denna banderoll STR_2989 :{SMALLFONT}{BLACK}Välj huvudfärg STR_2990 :{SMALLFONT}{BLACK}Välj textfärg @@ -2267,50 +2267,50 @@ STR_3006 :{PALEGOLD}ABC STR_3007 :{LIGHTPINK}ABC STR_3008 :{PEARLAQUA}ABC STR_3009 :{PALESILVER}ABC -STR_3010 :Kan inte ladda fil... +STR_3010 :Kan inte ladda fil … STR_3011 :Filen innehåller ogiltig data -STR_3012 :Diskoteket{MOVE_X}{150}Abstrakt tema -STR_3013 :Hästarnas vaggvisa -STR_3014 :Romarriket{MOVE_X}{150}Romanskt tema -STR_3015 :Chinatown{MOVE_X}{150}Pagod-tema -STR_3016 :Det okända...{MOVE_X}{150}Rymd- och marstema -STR_3017 :Djungelboken{MOVE_X}{150}Djungeltema -STR_3018 :Pyramiderna{MOVE_X}{150}Egyptiskt tema -STR_3019 :Leklandet{MOVE_X}{150}Underlandstema +STR_3012 :Abstrakt tema +STR_3013 :Tivolitema +STR_3014 :Romerskt tema +STR_3015 :Pagod-tema +STR_3016 :Marstema +STR_3017 :Djungeltema +STR_3018 :Egyptiskt tema +STR_3019 :Underlandstema STR_3020 : -STR_3021 :Mörkret{MOVE_X}{150}Rymd- och marstema -STR_3022 :Frankenstein{MOVE_X}{150}Spök- och kräldjurstema -STR_3023 :Raveparty{MOVE_X}{150}Abstrakt tema -STR_3024 :Mysteriet +STR_3021 :Rymdtema +STR_3022 :Skräcktema +STR_3023 :Abstrakt tema +STR_3024 :Stillsamt tema STR_3025 :Sommartider -STR_3026 :Älven{MOVE_X}{150}Vattentema -STR_3027 :Skjutande Cowboys{MOVE_X}{150}Vilda västern-tema -STR_3028 :Jurassic Park{MOVE_X}{150}Jurassic-tema -STR_3029 :Rocka! -STR_3030 :Glade-Chapplie's piano -STR_3031 :Ljuva drömmar -STR_3032 :Rockare! -STR_3033 :Istiden{MOVE_X}{150}Snö- och istema -STR_3034 :Pudrade bergstoppar{MOVE_X}{150}Snö- och istema +STR_3026 :Vattentema +STR_3027 :Vilda västern-tema +STR_3028 :Dinosaurietema +STR_3029 :Rocktema +STR_3030 :Glade Chapplies piano +STR_3031 :Fantasitema +STR_3032 :Rocktema 2 +STR_3033 :Istema +STR_3034 :Snötema STR_3035 :Egen musik 1 STR_3036 :Egen musik 2 -STR_3037 :Medeltidsmarknaden{MOVE_X}{150}Medeltidstema -STR_3038 :Stadslivet{MOVE_X}{150}Stadstema -STR_3039 :Kyrkans orgeln -STR_3040 :Pumpande mekaniker{MOVE_X}{150}Mekaniktema -STR_3041 :Luftäventyr -STR_3042 :Sjörövare{MOVE_X}{150}Pirattema -STR_3043 :Rockande! -STR_3044 :Gottegris{MOVE_X}{150}Godislandstema +STR_3037 :Medeltidstema +STR_3038 :Stadstema +STR_3039 :Orgeltema +STR_3040 :Mekaniktema +STR_3041 :Modernt tema +STR_3042 :Pirattema +STR_3043 :Rocktema 3 +STR_3044 :Godislandstema STR_3045 :{SMALLFONT}{BLACK}Välj musikstil som ska spelas{NEWLINE}Teman på sånger och dekorationer går mixa utan problem -STR_3047 :Lokala myndigheter har förbjudit rivning och modifikation av denna Åktur +STR_3047 :Lokala myndigheter har förbjudit rivning och modifikation av denna åktur STR_3048 :Kampanjer är förbjudna av lokala myndigheter STR_3049 :Golfhål A STR_3050 :Golfhål B STR_3051 :Golfhål C STR_3052 :Golfhål D STR_3053 :Golfhål E -STR_3054 :Laddar... +STR_3054 :Laddar … STR_3058 :Tegelstensväggar STR_3059 :Häckar STR_3060 :Isblock @@ -2318,31 +2318,19 @@ STR_3061 :Trästaket STR_3062 :{SMALLFONT}{BLACK}Standard berg- och dalbane-spår STR_3063 :{SMALLFONT}{BLACK}Vattenkanal (spår under vatten) STR_3064 :Nybörjarparker -STR_3065 :Utmanande Parker +STR_3065 :Utmanande parker STR_3066 :Expertparker -STR_3067 :“Riktiga” Parker -STR_3068 :Andra Parker +STR_3067 :”Riktiga” parker +STR_3068 :Andra parker STR_3069 :Toppsektion STR_3070 :Sluttning till platt STR_3071 :{WINDOW_COLOUR_2}Samma pris genom parken STR_3072 :{SMALLFONT}{BLACK}Välj om det här priset ska användas genom hela parken -STR_3073 :{RED}VARNING: Ditt parkomdöme har gått under 700 !{NEWLINE}Om du inte har höjt parkomdömet inom 4 veckor kommer din park att stängas -STR_3074 :{RED}VARNING: Ditt parkomdöme är fortfarande under 700 !{NEWLINE}Du har 3 veckor på dig att höja parkomdömet -STR_3075 :{RED}VARNING: Ditt parkomdöme är fortfarande under 700 !{NEWLINE}Du har bara 2 veckor på dig att höja parkomdömet, annars kommer din park att stängas -STR_3076 :{RED}SISTA VARNING: Ditt parkomdöme är fortfarande under 700 !{NEWLINE}Om bara 7 dagar kommer din park att stängas om du inte kan höja parkomdömet -STR_3077 :{RED}STÄNGNINGSNOTIS: Din park har blivit stängd ! -STR_3078 :Vanlig ingång -STR_3079 :Träingång -STR_3080 :Tältingång -STR_3081 :Slottsingång (grå) -STR_3082 :Slottsingång (brun) -STR_3083 :Djungelingång -STR_3084 :Timmerstugeingång -STR_3085 :Klassisk/romersk ingång -STR_3086 :Abstrakt ingång -STR_3087 :Snö/is-ingång -STR_3088 :Pagodaingång -STR_3089 :Rymdingång +STR_3073 :{RED}VARNING: Ditt parkomdöme har gått under 700!{NEWLINE}Om du inte har höjt parkomdömet inom fyra veckor kommer din park att stängas +STR_3074 :{RED}VARNING: Ditt parkomdöme är fortfarande under 700!{NEWLINE}Du har tre veckor på dig att höja parkomdömet +STR_3075 :{RED}VARNING: Ditt parkomdöme är fortfarande under 700!{NEWLINE}Du har bara två veckor på dig att höja parkomdömet, annars kommer din park att stängas +STR_3076 :{RED}SISTA VARNING: Ditt parkomdöme är fortfarande under 700!{NEWLINE}Om bara sju dagar kommer din park att stängas om du inte kan höja parkomdömet +STR_3077 :{RED}STÄNGNINGSNOTIS: Din park har stängts! STR_3090 :{SMALLFONT}{BLACK}Välj stil för ingången, utgången och stationen STR_3091 :Du får inte ta bort den här sektionen! STR_3092 :Du får inte flytta eller modifiera stationen för den här åkturen! @@ -2355,10 +2343,10 @@ STR_3099 :{SMALLFONT}{BLACK}Välj huvudfärg STR_3100 :{SMALLFONT}{BLACK}Välj andra färg STR_3101 :{SMALLFONT}{BLACK}Välj tredje färg STR_3102 :{SMALLFONT}{BLACK}Måla om färgade dekorationer -STR_3103 :Kan inte måla om detta... +STR_3103 :Kan inte måla om detta … STR_3104 :{SMALLFONT}{BLACK}Lista åkturer STR_3105 :{SMALLFONT}{BLACK}Lista affärer och stånd -STR_3106 :{SMALLFONT}{BLACK}Lista informationskiosker och andra gästfaciliteter +STR_3106 :{SMALLFONT}{BLACK}Lista informationskiosker och andra besökarfaciliteter STR_3107 :Stäng STR_3108 :Testa STR_3109 :Öppna @@ -2369,36 +2357,34 @@ STR_3113 :Välj en annan design STR_3114 :{SMALLFONT}{BLACK}Gå tillbaka till menyn för att välja design STR_3115 :{SMALLFONT}{BLACK}Spara bandesign STR_3116 :{SMALLFONT}{BLACK}Spara bandesign (Inte möjligt innan åkturen har blivit testad och statistik har genererats) -STR_3117 :{BLACK}Anropar mekaniker... +STR_3117 :{BLACK}Anropar mekaniker … STR_3118 :{BLACK}{STRINGID} är på väg till åkturen STR_3119 :{BLACK}{STRINGID} fixar åkturen STR_3120 :{SMALLFONT}{BLACK}Hitta närmsta tillgängliga mekaniker, eller mekanikern som fixar åkturen STR_3121 :Kan inte hitta en mekaniker, eller så är alla i närheten upptagna -STR_3122 :{BLACK}{COMMA16} gästs {WINDOW_COLOUR_2}favoritåktur -STR_3123 :{BLACK}{COMMA16} gästers {WINDOW_COLOUR_2}favoritåktur +STR_3122 :{BLACK}{COMMA16} besökares {WINDOW_COLOUR_2}favoritåktur +STR_3123 :{BLACK}{COMMA16} besökares {WINDOW_COLOUR_2}favoritåktur STR_3124 :Trasig {STRINGID} STR_3125 :{WINDOW_COLOUR_2}Glädjefaktor: {BLACK}+{COMMA16}% STR_3126 :{WINDOW_COLOUR_2}Intensitetsfaktor: {BLACK}+{COMMA16}% STR_3127 :{WINDOW_COLOUR_2}Illamåendefaktor: {BLACK}+{COMMA16}% -STR_3128 :Spara Bandesign -STR_3129 :Spara Bandesign med Dekorationer +STR_3128 :Spara bandesign +STR_3129 :Spara bandesign med dekorationer STR_3130 :Spara STR_3131 :Avbryt -STR_3132 :{BLACK}Klicka på dekorationer för att markera dem för sparning med bandesignen... +STR_3132 :{BLACK}Klicka på dekorationer för att markera dem för sparning med bandesignen … STR_3133 :Kan inte bygga detta på en sluttning STR_3134 :{RED}(Designen inkluderar dekorationer som är otillgängliga) -STR_3135 :{RED}(Fordonstyp otillgänglig - Åktursbeteende kan påverkas) +STR_3135 :{RED}(Fordonstyp inte tillgänglig – åktursbeteende kan påverkas) STR_3136 :Varning: Denna design kommer att byggas med en alternativ fordonstyp och kanske inte beter sig som väntat -STR_3137 :Välj Närliggande Dekorationer -STR_3138 :Återställ Markering +STR_3137 :Välj närliggande dekorationer +STR_3138 :Återställ markering STR_3139 :Uppskjuts fungerar inte i detta körläge STR_3140 :Uppskjutsbacken måste börja direkt efter en station STR_3141 :Kontinuerligt banläge går inte att kombinera med en uppskjutskulle STR_3142 :{WINDOW_COLOUR_2}Kapacitet: {BLACK}{STRINGID} STR_3143 :{SMALLFONT}{BLACK}Visa besökare på kartan STR_3144 :{SMALLFONT}{BLACK}Visa åkturer och stånd på kartan -STR_3158 :graf -STR_3159 :lista STR_3160 :{SMALLFONT}{BLACK}Välj hur många gånger åkturen åker runt STR_3162 :Kan inte allokera tillräckligt med minne STR_3163 :Installerar ny data: @@ -2416,62 +2402,61 @@ STR_3177 :Kan inte avmarkera detta objekt STR_3178 :Minst ett gångvägsobjekt måste markeras STR_3179 :Minst ett åktursfordons- eller attraktionsobjekt måste markeras STR_3180 :Ogiltig markering -STR_3181 :Objektmarkering - {STRINGID} +STR_3181 :Objektmarkering – {STRINGID} STR_3182 :Parkentrén måste markeras STR_3183 :Vattentyp måste markeras -STR_3184 :Åktursfordon/attraktioner -STR_3185 :Små Dekorationer -STR_3186 :Stora Dekorationer -STR_3187 :Väggar/Staket +STR_3184 :Åk fordon eller attraktioner +STR_3185 :Små dekorationer +STR_3186 :Stora dekorationer +STR_3187 :Väggar och staket STR_3188 :Gångvägsskyltar STR_3189 :Gångvägar STR_3190 :Gångvägsdetaljer STR_3191 :Dekorationsgrupper STR_3192 :Parkentré STR_3193 :Vatten -STR_3194 :Scenariobeskrivning STR_3195 :Uppfinningslista STR_3196 :{WINDOW_COLOUR_2}Forskningsgrupp: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}Saker som är utvecklade vid spelets början: STR_3198 :{WINDOW_COLOUR_2}Saker som ska utvecklas under spelets gång: -STR_3199 :Slumpvis Ordning +STR_3199 :Slumpmässig ordning STR_3200 :{SMALLFONT}{BLACK}Slumpa listan av saker som ska utvecklas under spelets gång -STR_3201 :Välj Objekt -STR_3202 :Terrängeditor +STR_3201 :Välj objekt +STR_3202 :Terrängredigerare STR_3203 :Bygg upp uppfinningslista -STR_3204 :Välj Inställningar -STR_3205 :Välj Uppdrag -STR_3206 :Spara Scenario -STR_3207 :Åktur Designer +STR_3204 :Välj inställningar +STR_3205 :Välj uppdrag +STR_3206 :Spara scenario +STR_3207 :Bandesigner STR_3208 :Bandesignhanterare STR_3209 :Tillbaka till tidigare steg: STR_3210 :Vidare till nästa steg: STR_3211 :Kartstorlek: -STR_3212 :{POP16}{COMMA16} x {PUSH16}{COMMA16} +STR_3212 :{POP16}{COMMA16} × {PUSH16}{COMMA16} STR_3213 :Kan inte minska kartstorleken mer STR_3214 :Kan inte utöka kartstorleken mer STR_3215 :För nära kanten av kartan -STR_3216 :{SMALLFONT}{BLACK}Välj mark ägd av parken m.m -STR_3217 :Ägd Mark +STR_3216 :{SMALLFONT}{BLACK}Välj mark ägd av parken etc. +STR_3217 :Ägd mark STR_3218 :Bygglov innehas -STR_3219 :Mark Till Salu -STR_3220 :Bygglov Till Salu +STR_3219 :Mark till salu +STR_3220 :Bygglov till salu STR_3221 :{SMALLFONT}{BLACK}Välj att marken ska ägas av parken STR_3222 :{SMALLFONT}{BLACK}Välj att bara bygglov ska ägas av parken STR_3223 :{SMALLFONT}{BLACK}Välj att mark ska vara tillgängligt för köp STR_3224 :{SMALLFONT}{BLACK}Välj att bygglov ska vara tillgängligt för köp -STR_3225 :{SMALLFONT}{BLACK}Aktivera/inaktivera generering av en slumpmässig samling objekt runt den valda positionen +STR_3225 :{SMALLFONT}{BLACK}Slå av eller på generering av en slumpmässig samling objekt runt den valda positionen STR_3226 :{SMALLFONT}{BLACK}Bygg parkentré STR_3227 :För många parkentréer! STR_3228 :{SMALLFONT}{BLACK}Sätt startpositioner för besökare STR_3229 :Blockbromsar kan inte användas direkt efter en station STR_3230 :Blockbromsar kan inte användas direkt efter varandra STR_3231 :Blockbromsar kan inte användas efter en uppskjutskulle -STR_3232 :Inställningar - Finansiella -STR_3233 :Inställningar - Gäster -STR_3234 :Inställningar - Parken +STR_3232 :Inställningar – Finanser +STR_3233 :Inställningar – Besökare +STR_3234 :Inställningar – Parken STR_3235 :{SMALLFONT}{BLACK}Visa finansiella inställningar -STR_3236 :{SMALLFONT}{BLACK}Visa gästinställningar +STR_3236 :{SMALLFONT}{BLACK}Visa besökarinställningar STR_3237 :{SMALLFONT}{BLACK}Visa parkinställningar STR_3238 :Inga pengar STR_3239 :{SMALLFONT}{BLACK}Gör detta till en 'inga pengar'-park utan finansiella begränsningar @@ -2491,14 +2476,14 @@ STR_3252 :Kan inte höja maxlån mer! STR_3253 :Kan inte sänka maxlån mer! STR_3254 :Kan inte höja räntan mer! STR_3255 :Kan inte sänka räntan mer! -STR_3256 :Gäster föredrar mindre intensiva åkturer -STR_3257 :{SMALLFONT}{BLACK}Välj om gäster endast ska föredra mindre intensiva åkturer -STR_3258 :Gäster föredrar mer intensiva åkturer -STR_3259 :{SMALLFONT}{BLACK}Välj om gäster endast ska föredra mer intensiva åkturer -STR_3260 :{WINDOW_COLOUR_2}Pengar per gäst (genomsnitt): -STR_3261 :{WINDOW_COLOUR_2}Gästers startglädje: -STR_3262 :{WINDOW_COLOUR_2}Gästers starthunger: -STR_3263 :{WINDOW_COLOUR_2}Gästers starttörst: +STR_3256 :Besökare föredrar mindre intensiva åkturer +STR_3257 :{SMALLFONT}{BLACK}Välj om besökare endast ska föredra mindre intensiva åkturer +STR_3258 :Besökare föredrar mer intensiva åkturer +STR_3259 :{SMALLFONT}{BLACK}Välj om besökare endast ska föredra mer intensiva åkturer +STR_3260 :{WINDOW_COLOUR_2}Pengar per besökare (genomsnitt): +STR_3261 :{WINDOW_COLOUR_2}Besökares startglädje: +STR_3262 :{WINDOW_COLOUR_2}Besökares starthunger: +STR_3263 :{WINDOW_COLOUR_2}Besökares starttörst: STR_3264 :Kan inte höja detta mer! STR_3265 :Kan inte sänka detta mer! STR_3266 :{SMALLFONT}{BLACK}Välj hur den här parken tar betalt för entré och åkturer @@ -2510,12 +2495,12 @@ STR_3271 :Förbjud höga konstruktioner STR_3272 :{SMALLFONT}{BLACK}Förbjud alla höga konstruktioner STR_3273 :Svårare parkomdöme STR_3274 :{SMALLFONT}{BLACK}Gör så att bra parkomdöme är svårare att få -STR_3275 :Svårare gäster -STR_3276 :{SMALLFONT}{BLACK}Gör så att det blir svårare att få gäster +STR_3275 :Svårare besökare +STR_3276 :{SMALLFONT}{BLACK}Gör så att det blir svårare att få besökare STR_3277 :{WINDOW_COLOUR_2}Kostnad för att köpa mark: STR_3278 :{WINDOW_COLOUR_2}Kostnad för att köpa bygglov: -STR_3279 :Gratis parkentré / Betala per åktur -STR_3280 :Betala för parkentré / Gratis åkturer +STR_3279 :Gratis parkentré och betala per åktur +STR_3280 :Gratis åkturer och betala för parkentré STR_3281 :{WINDOW_COLOUR_2}Entrépris: STR_3282 :{SMALLFONT}{BLACK}Välj uppdrag och parknamn STR_3283 :{SMALLFONT}{BLACK}Välj åkturer som ska bevaras @@ -2529,16 +2514,16 @@ STR_3290 :Svalt och blött STR_3291 :Varmt STR_3292 :Hett och torrt STR_3293 :Kallt -STR_3294 :Byt... +STR_3294 :Ändra … STR_3295 :{SMALLFONT}{BLACK}Byt namn på parken STR_3296 :{SMALLFONT}{BLACK}Byt namn på scenario -STR_3297 :{SMALLFONT}{BLACK}Byt detaljer om parken / scenariot +STR_3297 :{SMALLFONT}{BLACK}Ändra detaljer om parken eller scenariot STR_3298 :{WINDOW_COLOUR_2}Parknamn: {BLACK}{STRINGID} -STR_3299 :{WINDOW_COLOUR_2}Park- och Scenariodetaljer: +STR_3299 :{WINDOW_COLOUR_2}Park- och scenariodetaljer: STR_3300 :{WINDOW_COLOUR_2}Scenarionamn: {BLACK}{STRINGID} STR_3301 :{WINDOW_COLOUR_2}Uppdragsdatum: STR_3302 :{WINDOW_COLOUR_2}{MONTHYEAR} -STR_3303 :{WINDOW_COLOUR_2}Antal gäster: +STR_3303 :{WINDOW_COLOUR_2}Antal besökare: STR_3304 :{WINDOW_COLOUR_2}Parkvärde: STR_3305 :{WINDOW_COLOUR_2}Månadsinkomst: STR_3306 :{WINDOW_COLOUR_2}Månadsvinst: @@ -2547,52 +2532,52 @@ STR_3308 :{WINDOW_COLOUR_2}Glädjenivå: STR_3309 :{WINDOW_COLOUR_2}{COMMA16} STR_3310 :{WINDOW_COLOUR_2}{LENGTH} STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32} -STR_3312 :{WINDOW_COLOUR_2}Åkturer/attraktioner som måste bevaras: +STR_3312 :{WINDOW_COLOUR_2}Åkturer och attraktioner som måste bevaras: STR_3313 :Scenarionamn STR_3314 :Skriv in namn för scenariot: -STR_3315 :Park- och Scenariodetaljer +STR_3315 :Park- och scenariodetaljer STR_3316 :Skriv in en beskrivning för detta scenario: STR_3317 :Inga detaljer än STR_3318 :{SMALLFONT}{BLACK}Välj vilken grupp detta scenario hör till STR_3319 :{WINDOW_COLOUR_2}Scenariogrupp: -STR_3320 :Kan inte spara scenario fil... +STR_3320 :Kan inte spara scenariofil … STR_3321 :Nya objekt installerade STR_3322 :{WINDOW_COLOUR_2}Uppdrag: {BLACK}{STRINGID} STR_3326 :{WINDOW_COLOUR_2}(ingen bild) STR_3327 :Startpositioner för besökare är inte bestämda -STR_3328 :Kan inte gå vidare till nästa steg... +STR_3328 :Kan inte gå vidare till nästa steg … STR_3329 :Parkentrén är inte byggd än STR_3330 :Parken måste äga någon mark -STR_3331 :Vägen från parkentrén till kartkanten är antingen inte komplett eller för komplex - Vägen måste ha en konstant bredd med så få korsningar och hörn som möjligt +STR_3331 :Vägen från parkentrén till kartkanten är antingen inte komplett eller för komplex – Vägen måste ha en konstant bredd med så få korsningar och hörn som möjligt STR_3332 :Parkentrén är åt fel håll eller har ingen väg som leder till kartkanten STR_3333 :Exportera plug-in-objekt med sparade spel STR_3334 :{SMALLFONT}{BLACK}Välj om plug-in objekt (extra objekt som inte följer med basspelet) ska sparas i sparade spel och scenarion, vilket låter spelare importera denna data -STR_3335 :Åktur Designer - Välj Åkturstyp & Fordon -STR_3336 :Bandesign-hanterare - Välj Åkturstyp +STR_3335 :Åktursdesigner – Välj åkturstyp och fordon +STR_3336 :Bandesign-hanterare – Välj åkturstyp STR_3338 :{BLACK}Egendesignad layout STR_3339 :{BLACK}{COMMA16} design tillgänglig, eller egendesignad layout STR_3340 :{BLACK}{COMMA16} designer tillgängliga, eller egendesignad layout STR_3341 :{SMALLFONT}{BLACK}Spelverktyg STR_3342 :Scenarioredigerare -STR_3343 :Konvertera Sparat Spel till Scenario -STR_3344 :Åktur Designer +STR_3343 :Omvandla sparat spel till scenario +STR_3344 :Bandesigner STR_3345 :Bandesignhanterare -STR_3346 :Kan inte spara bandesign... +STR_3346 :Kan inte spara bandesign … STR_3347 :Åkturen är för stor, innehåller för många delar, eller har för utspridda dekorationer -STR_3348 :Döp om +STR_3348 :Byt namn STR_3349 :Ta bort STR_3350 :Bandesignnamn STR_3351 :Skriv in nytt namn på denna bandesign: -STR_3352 :Kan inte namnge bandesign... +STR_3352 :Kan inte namnge bandesign … STR_3353 :Det nya namnet innehåller ogiltiga bokstäver eller symboler STR_3354 :En annan fil finns redan med detta namn eller är skrivskyddad STR_3355 :Filen är skrivskyddad eller låst STR_3356 :Ta bort fil STR_3357 :{WINDOW_COLOUR_2}Är du säker på att du vill ta bort {STRING} permanent ? -STR_3358 :Kan inte ta bort bandesign... +STR_3358 :Kan inte ta bort bandesign … STR_3359 :{BLACK}Inga bandesigner av denna typ STR_3360 :Varning! -STR_3361 :För många bandesigner av denna typ - Vissa kommer inte att visas. +STR_3361 :För många bandesigner av denna typ – Vissa kommer inte att visas. STR_3364 :Avancerat STR_3365 :{SMALLFONT}{BLACK}Tillåt urval av individuella dekorationer utöver dekorationsgrupper STR_3366 :{BLACK}= Åktur @@ -2600,7 +2585,7 @@ STR_3367 :{BLACK}= Matstånd STR_3368 :{BLACK}= Dryckstånd STR_3369 :{BLACK}= Souvenirstånd STR_3370 :{BLACK}= Infokiosk -STR_3371 :{BLACK}= Första Hjälpen +STR_3371 :{BLACK}= Första hjälpen STR_3372 :{BLACK}= Bankomat STR_3373 :{BLACK}= Toalett STR_3374 :Varning: För många markerade objekt! @@ -2609,25 +2594,25 @@ STR_3376 :Installera ny STR_3377 :{SMALLFONT}{BLACK}Installera en ny bandesign fil STR_3378 :Installera STR_3379 :Avbryt -STR_3380 :Kan inte installera denna bandesign... +STR_3380 :Kan inte installera denna bandesign … STR_3381 :Filen är inte kompatibel eller innehåller ogiltig data STR_3382 :Filkopiering misslyckades STR_3383 :Välj nytt namn på denna bandesign -STR_3384 :En existerande bandesign har redan detta namn - Var god välj ett nytt namn för denna design: -STR_3389 :Kan inte markera fler dekorationer... +STR_3384 :En existerande bandesign har redan detta namn – Var god välj ett nytt namn för denna design: +STR_3389 :Kan inte markera fler dekorationer … STR_3390 :För många saker markerade # Start of tutorial strings. Not used at the moment, so not necessary to translate. # End of tutorial strings STR_3437 :{SMALLFONT}{BLACK}Rensa stora områden av dekorationer från terrängen -STR_3438 :Kan inte ta bort alla dekorationer härifrån... -STR_3439 :Rensa Land +STR_3438 :Kan inte ta bort alla dekorationer härifrån … +STR_3439 :Rensa land STR_3440 :Sida 1 STR_3441 :Sida 2 STR_3442 :Sida 3 STR_3443 :Sida 4 STR_3444 :Sida 5 -STR_3445 :Sätt Patrullområde -STR_3446 :Avbryt Patrullområde +STR_3445 :Sätt satrullområde +STR_3446 :Avbryt patrullområde # New strings, cleaner STR_5120 :Finans @@ -2651,43 +2636,43 @@ STR_5138 :{SMALLFONT}{WINDOW_COLOUR_2}{STRINGID} STR_5139 :{WHITE}{STRINGID} STR_5140 :Avaktivera bromsfel STR_5141 :Avaktivera alla haverin -STR_5142 :Normal Hastighet -STR_5143 :Kvick Hastighet -STR_5144 :Snabb Hastighet -STR_5145 :Turbo Hastighet -STR_5146 :Hyper Hastighet +STR_5142 :Normal hastighet +STR_5143 :Kvick hastighet +STR_5144 :Snabb hastighet +STR_5145 :Turbohastighet +STR_5146 :Hyperhastighet STR_5147 :Fusk STR_5148 :{SMALLFONT}{BLACK}Ändra spelets hastighet STR_5149 :{SMALLFONT}{BLACK}Visa fusk STR_5150 :Aktivera verktyg för felsökning STR_5151 :, STR_5152 :. -STR_5153 :Redigera Teman... +STR_5153 :Redigera teman … STR_5154 :Hårdvaruhantering STR_5155 :Tillåt testning av spår som inte är färdiga -STR_5156 :{SMALLFONT}{BLACK}Tillåter testning av de flesta åkturer även när spåret är oklart, gäller inte Blockavdelade kontinuerliga banlägen +STR_5156 :{SMALLFONT}{BLACK}Tillåter testning av de flesta åkturer även när spåret är oklart, gäller inte blockuppdelade kontinuerliga banlägen STR_5158 :Avsluta till menyn STR_5159 :Avsluta OpenRCT2 -STR_5160 :{POP16}{MONTH} {PUSH16}{PUSH16}{STRINGID}, År {POP16}{COMMA16} +STR_5160 :{POP16}{MONTH} {PUSH16}{PUSH16}{STRINGID}, år {POP16}{COMMA16} STR_5161 :Datumformat: STR_5162 :Dag/Månad/År STR_5163 :Månad/Dag/År -STR_5164 :Twitch kanalnamn -STR_5165 :Namnge gäster efter följare -STR_5166 :{SMALLFONT}{BLACK}Namnger gäster efter Twitchkanalens följare -STR_5167 :Spåra följargäster -STR_5168 :{SMALLFONT}{BLACK}Aktiverar spårningsinformation för gäster döpta efter kanalens Twitchföljare -STR_5169 :Döp gäster efter namn i Twitchchatten -STR_5170 :{SMALLFONT}{BLACK}Döper gäster till namn i Twitchkanalens chatt -STR_5171 :Spåra chattgäster -STR_5172 :{SMALLFONT}{BLACK}Aktiverar spårningsinformation för gäster döpta efter Twitchkanalens chattare +STR_5164 :Kanalnamn på Twitch +STR_5165 :Namnge besökare efter följare +STR_5166 :{SMALLFONT}{BLACK}Namnger besökare efter Twitchkanalens följare +STR_5167 :Spåra följarbesökare +STR_5168 :{SMALLFONT}{BLACK}Aktiverar spårningsinformation för besökare uppkallade efter kanalens Twitchföljare +STR_5169 :Namnge besökare efter namn i Twitchchatten +STR_5170 :{SMALLFONT}{BLACK}Ger besökare namn från Twitchkanalens chatt +STR_5171 :Spåra chattbesökare +STR_5172 :{SMALLFONT}{BLACK}Aktiverar spårningsinformation för besökare namngivna efter Twitchkanalens chattare STR_5173 :Använd Twitchchatten som nyheter -STR_5174 :{SMALLFONT}{BLACK}Använder Twitchchattens meddelanden föregått av !nyheter som notifikationer i spelet +STR_5174 :{SMALLFONT}{BLACK}Använder Twitchchattens meddelanden som börjar med !nyheter som notifikationer i spelet STR_5175 :Skriv in namnet på Twitchkanalen STR_5176 :Aktivera Twitchfunktionen STR_5177 :Skärmlägen: STR_5178 :{SMALLFONT}{BLACK}Visa ekonomiska fusk -STR_5179 :{SMALLFONT}{BLACK}Visa gästfusk +STR_5179 :{SMALLFONT}{BLACK}Visa besökarfusk STR_5180 :{SMALLFONT}{BLACK}Visa parkfusk STR_5181 :{SMALLFONT}{BLACK}Visa åktursfusk STR_5182 :{INT32} @@ -2696,14 +2681,14 @@ STR_5184 :Skriv in bashöjden mellan {COMMA16} och {COMMA16} STR_5185 :Vattennivå STR_5186 :Skriv in vattennivån mellan {COMMA16} och {COMMA16} STR_5187 :Finansiering -STR_5188 :Ny Kampanj +STR_5188 :Ny kampanj STR_5189 :Forskning STR_5190 :Karta STR_5191 :Bildskärm -STR_5192 :Nyliga Nyheter +STR_5192 :Senaste nyheter STR_5193 :Land STR_5194 :Vatten -STR_5195 :Rensa Land +STR_5195 :Rensa land STR_5196 :Landsrättigheter STR_5197 :Dekorationer STR_5198 :Gångväg @@ -2713,8 +2698,8 @@ STR_5201 :Ny åktur STR_5202 :Spårdesignval STR_5203 :Åkturer STR_5204 :Åkturslista -STR_5205 :Gäst -STR_5206 :Gästlista +STR_5205 :Besökare +STR_5206 :Besökarlista STR_5207 :Personal STR_5208 :Personallista STR_5209 :Banderoll @@ -2729,37 +2714,37 @@ STR_5217 :Fusk STR_5218 :Teman STR_5219 :Inställningar STR_5220 :Snabbtangenter -STR_5221 :Ändra Snabbtangenter -STR_5222 :Ladda/Spara -STR_5223 :Spara Inmatning -STR_5224 :Riva Åktur-Fönstret -STR_5225 :Avskeda Personal-Fönstret -STR_5226 :Radera Spår-Fönstret -STR_5227 :Sparfilöverskrivningsfönstret +STR_5221 :Ändra snabbtangenter +STR_5222 :Ladda eller spara +STR_5223 :Spara inmatning +STR_5224 :Riv åktur-fönster +STR_5225 :Avskeda personal-fönster +STR_5226 :Radera spår-fönster +STR_5227 :Sparfilöverskrivningsfönster STR_5228 :{SMALLFONT}{BLACK}Huvudgränsnitt STR_5229 :{SMALLFONT}{BLACK}Park STR_5230 :{SMALLFONT}{BLACK}Verktyg -STR_5231 :{SMALLFONT}{BLACK}Åkturer och Gäster +STR_5231 :{SMALLFONT}{BLACK}Åkturer och besökare STR_5232 :{SMALLFONT}{BLACK}Redigerare STR_5233 :{SMALLFONT}{BLACK}Övrigt STR_5234 :{SMALLFONT}{BLACK}Hanterare STR_5235 :{SMALLFONT}{BLACK}Inställningar STR_5236 :Fönster STR_5237 :Palett -STR_5238 :Nuvarande Tema: +STR_5238 :Nuvarande tema: STR_5239 :Kopiera -STR_5240 :Döp ditt tema +STR_5240 :Namnge temat STR_5241 :Kan inte ändra detta tema STR_5242 :Temats namn finns redan STR_5243 :Ogiltiga tecken använda STR_5244 :Teman -STR_5245 :Toppens Verktygsfält -STR_5246 :Bottens Verktygsfält -STR_5247 :Spårredigerarens Verktygsfält -STR_5248 :Dekorationredigerarens Verktygsfält -STR_5249 :Titelmenyns Knappar -STR_5250 :Titelns Avsluta-Knapp (Dörren) -STR_5251 :Titelinställningsknappen (Inställningar) +STR_5245 :Toppens verktygsfält +STR_5246 :Bottens verktygsfält +STR_5247 :Spårredigerarens verktygsfält +STR_5248 :Dekorationredigerarens verktygsfält +STR_5249 :Titelmenyns knappar +STR_5250 :Titelnradens avsluta-knapp +STR_5251 :Titelradens inställningsknapp STR_5252 :Scenarioväljaren STR_5253 :Parkinformation STR_5254 :Skapa @@ -2767,7 +2752,7 @@ STR_5255 :{SMALLFONT}{BLACK}Skapa ny titelsekvens från början STR_5256 :Skapa ett nytt tema att göra ändringar på STR_5257 :{SMALLFONT}{BLACK}Skapa ett nytt tema baserat på denna STR_5258 :{SMALLFONT}{BLACK}Ta bort det använda temat -STR_5259 :{SMALLFONT}{BLACK}Döp om det använda temat +STR_5259 :{SMALLFONT}{BLACK}Byt namn på det använda temat STR_5260 :Gigantisk skärmdump STR_5261 :Filter STR_5262 :Wacky Worlds @@ -2775,35 +2760,35 @@ STR_5263 :Time Twister STR_5264 :Egna STR_5265 :{SMALLFONT}{BLACK}Välj vilket innehåll som ska vara synligt STR_5266 :{SMALLFONT}{BLACK}Skärm -STR_5267 :{SMALLFONT}{BLACK}Kultur och Enheter +STR_5267 :{SMALLFONT}{BLACK}Kultur och enheter STR_5268 :{SMALLFONT}{BLACK}Ljud -STR_5269 :{SMALLFONT}{BLACK}Kontroller och Gränssnitt +STR_5269 :{SMALLFONT}{BLACK}Kontroller och gränssnitt STR_5270 :{SMALLFONT}{BLACK}Diverse STR_5271 :{SMALLFONT}{BLACK}Twitch -STR_5272 :{SMALLFONT}{BLACK}Små Dekorationer -STR_5273 :{SMALLFONT}{BLACK}Stora Dekorationer +STR_5272 :{SMALLFONT}{BLACK}Små dekorationer +STR_5273 :{SMALLFONT}{BLACK}Stora dekorationer STR_5274 :{SMALLFONT}{BLACK}Gångväg -STR_5275 :Sök efter Objekt +STR_5275 :Sök efter objekt STR_5276 :Skriv in namnet på objektet du letar efter STR_5277 :Rensa STR_5278 :Sandlådeläge STR_5279 :Sandlådeläge av -STR_5280 :{SMALLFONT}{BLACK}Tillåter ändring vid köp av mark genom Kartfönstret och andra inställningar som normalt är begränsat till Scenarioredigeraren +STR_5280 :{SMALLFONT}{BLACK}Tillåter ändring vid köp av mark genom kartfönstret och andra inställningar som normalt är begränsat till scenarioredigeraren STR_5281 :{SMALLFONT}{BLACK}Tillägg STR_5282 :RCT1 Öppen/Stängd Åktursljus STR_5283 :RCT1 Öppen/Stängd Parkljus STR_5284 :RCT1 Scenarioväljartypsnitt STR_5285 :EXPLODERA!!! -STR_5286 :{SMALLFONT}{BLACK}Får gäster att explodera +STR_5286 :{SMALLFONT}{BLACK}Får besökare att explodera STR_5287 :Åkturen är redan trasig STR_5288 :Åkturen är stängd STR_5289 :Inga haverier tillgängliga för denna åktur STR_5290 :Laga åktur STR_5291 :Kan inte tvinga haveri STR_5292 :{SMALLFONT}{BLACK}Tvinga haveri -STR_5293 :{SMALLFONT}{BLACK}Stäng åktur/attraktion -STR_5294 :{SMALLFONT}{BLACK}Testa åktur/attraktion -STR_5295 :{SMALLFONT}{BLACK}Öppna åktur/attraktion +STR_5293 :{SMALLFONT}{BLACK}Stäng åktur eller attraktion +STR_5294 :{SMALLFONT}{BLACK}Testa åktur eller attraktion +STR_5295 :{SMALLFONT}{BLACK}Öppna åktur eller attraktion STR_5296 :{SMALLFONT}{BLACK}Stäng parken STR_5297 :{SMALLFONT}{BLACK}Öppna parken STR_5298 :{RED}{STRINGID} @@ -2837,10 +2822,10 @@ STR_5325 :Nät (blå) STR_5326 :Nät (grön) STR_5327 :Sand (mörk) STR_5328 :Sand (ljus) -STR_5329 :Schackbräde (omvänt) +STR_5329 :Schackbräde (omvänt) STR_5330 :Underjordisk vy STR_5331 :Sten -STR_5332 :Trä (röd) +STR_5332 :Trä (rött) STR_5333 :Trä (svart) STR_5334 :Is STR_5335 :Åktursingång @@ -2852,7 +2837,7 @@ STR_5340 :{SMALLFONT}{BLACK}Frihöjd STR_5343 :Placera personal automatiskt STR_5344 :Logg över Förändringar STR_5345 :Finansfusk -STR_5346 :Gästfusk +STR_5346 :Besökarfusk STR_5347 :Parkfusk STR_5348 :Åktursfusk STR_5349 :{SMALLFONT}{BLACK}Alla åkturer @@ -2865,18 +2850,18 @@ STR_5355 :{BLACK}Törst: STR_5356 :{BLACK}Illamående: STR_5357 :{BLACK}Illamående tolerans: STR_5358 :{BLACK}Blåsa: -STR_5359 :Ta bort gäster -STR_5360 :{SMALLFONT}{BLACK}Tar bort alla gäster från parken -STR_5361 :Ge alla gäster: -STR_5362 :{BLACK}Gästernas föredragna åktursintensitet: -STR_5363 :Mer än 1 (Lågt) -STR_5364 :Mindre än 15 (Högt) +STR_5359 :Ta bort besökare +STR_5360 :{SMALLFONT}{BLACK}Tar bort alla besökare från parken +STR_5361 :Ge alla besökare: +STR_5362 :{BLACK}Besökarnas föredragna åktursintensitet: +STR_5363 :Mer än 1 +STR_5364 :Mindre än 15 STR_5365 :{BLACK}Personalhastighet: STR_5366 :Normal STR_5367 :Snabb STR_5368 :Återställ krasch -STR_5369 :Parkinställningar... -STR_5370 :{SMALLFONT}{BLACK}Klicka på denna knapp för att modifiera{NEWLINE}parkinställningar som begränsningar,{NEWLINE}gästgenerering och pengar. +STR_5369 :Parkinställningar … +STR_5370 :{SMALLFONT}{BLACK}Klicka på denna knapp för att modifiera{NEWLINE}parkinställningar som begränsningar,{NEWLINE}besökargenerering och pengar. STR_5371 :Objektväljare STR_5372 :Invertera höger musdragning STR_5373 :Namn {STRINGID} @@ -2891,7 +2876,7 @@ STR_5381 :{SMALLFONT}{BLACK}Sluta spela titelsekvensen STR_5382 :{SMALLFONT}{BLACK}Börja om titelsekvensen STR_5383 :{SMALLFONT}{BLACK}Skapa en ny titelsekvens baserat på nuvarande STR_5384 :{SMALLFONT}{BLACK}Ta bort nuvarande titelsekvens -STR_5385 :{SMALLFONT}{BLACK}Döp om nuvarande titelsekvens +STR_5385 :{SMALLFONT}{BLACK}Byt namn på nuvarande titelsekvens STR_5386 :{SMALLFONT}{BLACK}Skriv in nytt kommando STR_5387 :{SMALLFONT}{BLACK}Redigera det valda kommandot STR_5388 :{SMALLFONT}{BLACK}Ta bort det valda kommandot @@ -2900,7 +2885,7 @@ STR_5390 :{SMALLFONT}{BLACK}Flytta det valda kommandot nedåt STR_5391 :{SMALLFONT}{BLACK}Flytta det valda kommandot uppåt STR_5392 :{SMALLFONT}{BLACK}Lägg till en sparfil till titelsekvensen STR_5393 :{SMALLFONT}{BLACK}Ta bort den valda sparfilen från titelsekvensen -STR_5394 :{SMALLFONT}{BLACK}Döp om den valda sparfilen +STR_5394 :{SMALLFONT}{BLACK}Byt namn på den valda sparfilen STR_5395 :{SMALLFONT}{BLACK}Ladda den valda sparfilen i spelet STR_5396 :{SMALLFONT}{BLACK}Ladda om titelsekvensen om ändringar har gjorts utanför spelet STR_5397 :Kan endast användas i titelsekvensen @@ -2941,45 +2926,45 @@ STR_5431 :Sparfil att ladda: STR_5432 :Kommando: STR_5433 :Titelsekvens STR_5434 :Kommandoredigerare -STR_5435 :Döp om sparat spel -STR_5436 :Redigera titelsekvens... +STR_5435 :Byt namn på sparat spel +STR_5436 :Redigera titelsekvens … STR_5437 :Ingen sparfil vald STR_5438 :Kan inte göra ändringar medan kommandoredigeraren är öppen -STR_5439 :Ett vänta-kommando med åtminstone 4 sekunder krävs för ett 'börja om'-kommando +STR_5439 :Ett vänta-kommando med åtminstone fyra sekunder krävs för ett 'börja om'-kommando STR_5440 :Minimera fullskärm vid tappad fokus -STR_5441 :{SMALLFONT}{BLACK}Identifiera åkturer via spårtyp,{NEWLINE}så att vagnar kan bli ändrade{NEWLINE}efteråt (Som i RCT1) +STR_5441 :{SMALLFONT}{BLACK}Identifiera åkturer via spårtyp,{NEWLINE}så att vagnar kan bli ändrade{NEWLINE}efteråt (som i RCT1) STR_5442 :Tvinga parkbetyg: STR_5443 :Hastighet{MOVE_X}{87}{STRINGID} STR_5444 :Hastighet: STR_5445 :Hastighet STR_5446 :Få STR_5447 :Typ {STRINGID} -STR_5448 :Åktur / Fordon {STRINGID} +STR_5448 :Åktur eller fordon {STRINGID} STR_5449 :Minska spelhastigheten STR_5450 :Öka spelhastigheten STR_5451 :Öppna fuskfönstret STR_5452 :Växla visning av verktygsrader STR_5453 :Välj en annan åktur -STR_5454 :Lås upp FPS +STR_5454 :Lås upp bildhastighet STR_5455 :Aktivera Sandlådeläge STR_5456 :Placera föremål på varandra STR_5457 :Ingen gräns för stödpelare STR_5458 :Rotera medurs STR_5459 :Rotera moturs STR_5460 :Rotera vyn moturs -STR_5461 :Gästernas inställningar +STR_5461 :Besökarnas inställningar STR_5462 :{CURRENCY} STR_5463 :Mål: Ha kul! STR_5464 :Allmänt STR_5465 :Väder STR_5466 :Personal STR_5467 :ALT + -STR_5468 :Nyliga meddelanden -STR_5469 :Skrolla kartan upp -STR_5470 :Skrolla kartan vänster -STR_5471 :Skrolla kartan ner -STR_5472 :Skrolla kartan höger -STR_5473 :Växla mellan dag / natt +STR_5468 :Senaste meddelanden +STR_5469 :Skrolla kartan uppåt +STR_5470 :Skrolla kartan åt vänster +STR_5471 :Skrolla kartan neråt +STR_5472 :Skrolla kartan åt höger +STR_5473 :Växla mellan dag och natt STR_5474 :Visa text i banderoller med versaler STR_5475 :{COMMA16} veckor STR_5476 :Hårdvara @@ -2993,13 +2978,12 @@ STR_5483 :{BLACK}({COMMA16} veckor återstår) STR_5484 :{BLACK}({COMMA16} vecka återstår) STR_5485 :{SMALLFONT}{STRING} STR_5486 :{BLACK}{COMMA16} -STR_5487 :{SMALLFONT}{BLACK}Visa nyliga meddelanden -STR_5488 :Ingen ingång (endast OpenRCT2!) -STR_5489 :{SMALLFONT}{BLACK}Visa endast gäster du följer +STR_5487 :{SMALLFONT}{BLACK}Visa senaste meddelanden +STR_5489 :{SMALLFONT}{BLACK}Visa endast besökare du följer STR_5490 :Stäng av ljudet vid minimerat spel STR_5491 :Uppfinningslist STR_5492 :Scenarioinställningar -STR_5493 :Skicka Meddelande +STR_5493 :Skicka meddelande STR_5495 :Spelarlista STR_5496 :Spelare STR_5497 :Ping @@ -3011,15 +2995,15 @@ STR_5502 :Online STR_5503 :Skriv in hostnamn eller IP-adress: STR_5504 :{SMALLFONT}{BLACK}Visa online-status STR_5505 :Kunde inte ansluta till servern. -STR_5506 :Gästerna ignorerar intensivitetnivån -STR_5508 :Tillåt filer med inkorrekta 'kontrollsummor' att laddas +STR_5506 :Besökarna ignorerar intensivitetnivån +STR_5508 :Tillåt filer med inkorrekta kontrollsummor att laddas STR_5509 :{SMALLFONT}{BLACK}Tillåter spelet att ladda scenarion och sparfiler som har inkorrekta kontrollsummor, som scenarion från demon och skadade sparfiler. -STR_5510 :Standard ljudenhet +STR_5510 :Standardljudenhet STR_5511 :(UNKNOWN) STR_5512 :Spara spelet som STR_5513 :(Snabb) spara spelet STR_5514 :Stäng av skadegörelse -STR_5515 :{SMALLFONT}{BLACK}Förhindrar gäster från att vandalisera parken när de är arga +STR_5515 :{SMALLFONT}{BLACK}Förhindrar besökare från att vandalisera parken när de är arga STR_5516 :{SMALLFONT}{BLACK}Svart STR_5517 :{SMALLFONT}{BLACK}Grå STR_5518 :{SMALLFONT}{BLACK}Vit @@ -3061,7 +3045,7 @@ STR_5553 :Pausa spelet när Steam overlay är uppe STR_5554 :{SMALLFONT}{BLACK}Tillåt bergverktyget STR_5555 :Visa fordon från andra karuseller STR_5556 :{SMALLFONT}{BLACK}Sparka ut spelare -STR_5557 :Fortsätt vara ansluten efter synkproblem (Online) +STR_5557 :Fortsätt vara ansluten efter synkproblem (multiplayer) STR_5558 :En omstart krävs för att denna inställning ska gälla STR_5559 :10 min. inspektion STR_5560 :{SMALLFONT}{BLACK}Sätter inspektionstiden till 'Var 10:e minut' på alla åkturer @@ -3074,18 +3058,18 @@ STR_5566 :Lösenord: STR_5567 :Visa för alla spelare STR_5568 :Lösenord krävs STR_5569 :Denna server kräver lösenord -STR_5570 :Hämta Servrar -STR_5571 :Gå med i Spel -STR_5572 :Lägg Till I Favoriter -STR_5573 :Ta Bort Från Favoriter +STR_5570 :Hämta servrar +STR_5571 :Gå med i spel +STR_5572 :Lägg till i favoriter +STR_5573 :Ta bort från favoriter STR_5574 :Servernamn: -STR_5575 :Max Spelare: +STR_5575 :Max antal spelare: STR_5576 :Port: -STR_5577 :Sydkoreansk Won (W) -STR_5578 :Rysk Rouble (₽) +STR_5577 :Sydkoreansk won (W) +STR_5578 :Rysk rubel (₽) STR_5579 :Fönsterskalning: -STR_5580 :Tjeckisk koruna (Kč) -STR_5581 :Visa FPS +STR_5580 :Tjeckisk krona (Kč) +STR_5581 :Visa bildhastighet STR_5582 :Behåll muspekaren i spelet STR_5583 :{COMMA1DP16}m/s STR_5584 :SI @@ -3101,20 +3085,20 @@ STR_5593 :Parkbetygsvarningar STR_5594 :Åktur har gått sönder STR_5595 :Åktur har kraschat STR_5596 :Åktursvarningar -STR_5597 :Åktur / dekoration utvecklad -STR_5598 :Gästvarningar -STR_5599 :Gäst är vilse -STR_5600 :Gäst har lämnat parken -STR_5601 :Gäst köar för åktur -STR_5602 :Gäst är på åktur -STR_5603 :Gäst har lämnat åktur -STR_5604 :Gäst har köpt föremål -STR_5605 :Gäst har använt toaletter -STR_5606 :Gäst har dött -STR_5607 :{SMALLFONT}{BLACK}Tvinga borttagning av vald ruta/element. +STR_5597 :Åktur eller dekoration utvecklad +STR_5598 :Besökarvarningar +STR_5599 :Besökare är vilse +STR_5600 :Besökare har lämnat parken +STR_5601 :Besökare köar för åktur +STR_5602 :Besökare är på åktur +STR_5603 :Besökare har lämnat åktur +STR_5604 :Besökare har köpt föremål +STR_5605 :Besökare har använt toaletter +STR_5606 :Besökare har dött +STR_5607 :{SMALLFONT}{BLACK}Tvinga borttagning av vald ruta eller element. STR_5608 :BH STR_5609 :CH -STR_5610 :{SMALLFONT}{BLACK}Ta bort den valda rutan/elementet. Detta kommer tvinga en borttagning, inga pengar kommer ges/användas. Använd med försiktighet. +STR_5610 :{SMALLFONT}{BLACK}Ta bort den valda rutan eller elementet. Detta kommer tvinga en borttagning, inga pengar kommer ges eller användas. Använd med försiktighet. STR_5611 :G STR_5612 :{SMALLFONT}{BLACK}Spökflagga STR_5613 :B @@ -3124,31 +3108,31 @@ STR_5616 :{SMALLFONT}{BLACK}Sista elementet för rutflagga STR_5617 :{SMALLFONT}{BLACK}Flytta valda elementet upp. STR_5618 :{SMALLFONT}{BLACK}Flytta valda elementet ner. STR_5619 :RollerCoaster Tycoon -STR_5620 :Tillagda Attraktioner +STR_5620 :Tillagda attraktioner STR_5621 :Loopy Landscapes STR_5622 :RollerCoaster Tycoon 2 STR_5623 :Wacky Worlds STR_5624 :Time Twister -STR_5625 :“Riktiga” Parker +STR_5625 :”Riktiga” Parker STR_5626 :Andra parker STR_5627 :Sortera scenarion via: STR_5628 :Spelpaket STR_5629 :Svårighetsgrad STR_5630 :Tillåt progressiv-upplåsning STR_5631 :Original DLC parker -STR_5632 :Bygg din egna... +STR_5632 :Bygg din egna … STR_5633 :CMD + STR_5634 :OPTION + STR_5635 :{WINDOW_COLOUR_2}Pengar spenderat: {BLACK}{CURRENCY2DP} STR_5636 :{WINDOW_COLOUR_2}Utförda kommandon: {BLACK}{COMMA16} -STR_5637 :Kan inte göra detta... +STR_5637 :Kan inte göra detta … STR_5638 :Åtkomst nekad STR_5639 :{SMALLFONT}{BLACK}Visa lista över spelare på servern STR_5640 :{SMALLFONT}{BLACK}Hantera grupper STR_5641 :Standardgrupp: STR_5642 :Grupp -STR_5643 :Lägg till Grupp -STR_5644 :Ta bort Grupp +STR_5643 :Lägg till grupp +STR_5644 :Ta bort grupp STR_5645 :Chatta STR_5646 :Terräng STR_5647 :Pausa @@ -3159,7 +3143,7 @@ STR_5651 :Bygg åktur STR_5652 :Åktursinställningar STR_5653 :Dekorationer STR_5654 :Gångväg -STR_5655 :Gäster +STR_5655 :Besökare STR_5656 :Personal STR_5657 :Parkinställningar STR_5658 :Parkfinanser @@ -3167,10 +3151,10 @@ STR_5659 :Sparka ut spelare STR_5660 :Modifiera grupper STR_5661 :Sätt spelares grupp STR_5662 :N/A -STR_5663 :Rensa Land +STR_5663 :Rensa land STR_5664 :Fusk STR_5665 :Dekorationsutspridning -STR_5666 :Lösenordslös Inloggning +STR_5666 :Inloggning utan lösenord STR_5701 :{WINDOW_COLOUR_2}Senaste handling: {BLACK}{STRINGID} STR_5702 :{SMALLFONT}{BLACK}Visa spelarens senaste handling STR_5703 :Kan inte sparka ut ägaren @@ -3179,28 +3163,26 @@ STR_5705 :Kan inte välja denna grupp STR_5706 :Kan inte ta bort grupper spelare finns i STR_5707 :Denna grupp kan inte modifieras STR_5708 :Kan inte ändra gruppen ägaren är i -STR_5709 :Döp om Grupp +STR_5709 :Byt namn på grupp STR_5710 :Gruppnamn STR_5711 :Skriv in ett nytt namn för gruppen: STR_5712 :Kan inte modifiera tillstånd du inte själv har -STR_5713 :Sparka ut Spelaren +STR_5713 :Sparka ut spelaren STR_5714 :Visa inställningsfönstret -STR_5715 :Nytt Spel +STR_5715 :Nytt spel STR_5716 :Inte tillåtet online # For identifying client network version in server list window -STR_5717 :Nätverkets version: {STRING} +STR_5717 :Nätverksversion: {STRING} STR_5718 :{SMALLFONT}{BLACK}Nätverkets version: {STRING} STR_5719 :Soligt -STR_5720 :Delvis Molnigt +STR_5720 :Växlande molnighet STR_5721 :Molnigt STR_5722 :Regn -STR_5723 :Spöregn +STR_5723 :Regnoväder STR_5724 :Åska -STR_5725 :{BLACK}Tvinga Väder: +STR_5725 :{BLACK}Tvinga väder: STR_5726 :{SMALLFONT}{BLACK}Sätter vädret i parken STR_5727 :Skalningskvalité: -STR_5728 :Kräver att Hårdvaruhantering är ikryssad -STR_5729 :{SMALLFONT}{BLACK}Kräver att Hårdvaruhantering är ikryssad STR_5731 :Linjär # tooltip for tab in options window STR_5734 :{SMALLFONT}{BLACK}Rendering @@ -3211,9 +3193,9 @@ STR_5738 :Stängd, {COMMA16} personer fortfarande på åkturen STR_5739 :{WINDOW_COLOUR_2}Kunder på åktur: {BLACK}{COMMA16} STR_5740 :Oändliga kampanjer STR_5741 :{SMALLFONT}{BLACK}Marknadskampanjer slutar aldrig -STR_5742 :Verifierar ... -STR_5743 :Ansluter ... -STR_5744 :Laddar ... +STR_5742 :Verifierar … +STR_5743 :Ansluter … +STR_5744 :Laddar … STR_5745 :Du är inte i synk med servern STR_5746 :Frånkopplad STR_5747 :Frånkopplad: {STRING} @@ -3223,14 +3205,14 @@ STR_5750 :Anslutningen är avslutad STR_5751 :Ingen Data STR_5752 :{OUTLINE}{RED}{STRING} har lämnat STR_5753 :{OUTLINE}{RED}{STRING} har lämnat ({STRING}) -STR_5754 :Ogiltigt Spelarnamn -STR_5755 :Fel Version av OpenRCT (Servern använder {STRING}) -STR_5756 :Fel Lösenord -STR_5757 :Servern är Full +STR_5754 :Ogiltigt spelarnamn +STR_5755 :Fel version av OpenRCT (servern använder {STRING}) +STR_5756 :Fel lösenord +STR_5757 :Servern är full STR_5758 :{OUTLINE}{GREEN}{STRING} har anslutit till spelet -STR_5759 :Laddar ner banan ... ({INT32} / {INT32}) KiB -STR_5760 :Hong Kong Dollar (HK$) -STR_5761 :New Taiwan Dollar (NT$) +STR_5759 :Laddar ner banan … ({INT32} av {INT32}) KiB +STR_5760 :Hong Kong-dollar (HK$) +STR_5761 :New Taiwan-dollar (NT$) STR_5762 :Kinesiska Yuan (CN¥) STR_5763 :Alla filer STR_5764 :Ogiltig åkturstyp @@ -3247,31 +3229,31 @@ STR_5774 :Total vinst: {CURRENCY2DP} STR_5775 :Kunder: {COMMA32} per timme STR_5776 :Byggd: Detta år STR_5777 :Byggd: Förra året -STR_5778 :Byggd: {COMMA16} År sen +STR_5778 :Byggd: {COMMA16} år sedan STR_5779 :Inkomst: {CURRENCY2DP} per timme STR_5780 :Körkostnad: {CURRENCY2DP} per timme STR_5781 :Körkostnad: Okänd STR_5782 :Du är nu ansluten. Tryck på '{STRING}' för att skriva. -STR_5783 :{WINDOW_COLOUR_2}Scenario Låst +STR_5783 :{WINDOW_COLOUR_2}Scenario låst STR_5784 :{BLACK}Klara tidigare scenarion för att låsa upp denna -STR_5785 :Kan inte byta gruppnamn... +STR_5785 :Kan inte byta gruppnamn … STR_5786 :Ogiltigt gruppnamn STR_5787 :{COMMA32} spelare anslutna STR_5788 :Inspektionsmellanrum: STR_5789 :Avaktivera åskeffekter STR_5790 :{SMALLFONT}{BLACK}Växlar mellan RCT1-stilens prissättning{NEWLINE}(både karusell- och entré biljettpris) -STR_5791 :{SMALLFONT}{BLACK}Sätter tillförlitligheten på alla karuseller till 100%{NEWLINE}och deras byggda datum till 'Detta år' +STR_5791 :{SMALLFONT}{BLACK}Sätter tillförlitligheten på alla karuseller till 100%{NEWLINE}och deras byggda datum till 'detta år' STR_5792 :{SMALLFONT}{BLACK}Fixar alla trasiga karuseller -STR_5793 :{SMALLFONT}{BLACK}Tar bort kraschhistoriken för alla åkturer,{NEWLINE}så att gäster inte klagar på farliga attraktioner +STR_5793 :{SMALLFONT}{BLACK}Tar bort kraschhistoriken för alla åkturer,{NEWLINE}så att besökare inte klagar på farliga attraktioner STR_5794 :{SMALLFONT}{BLACK}Vissa scenarion tillåter inte redigering{NEWLINE}av vissa åkturer som redan existerar i parken.{NEWLINE}Detta fusk lyfter den begränsningen -STR_5795 :{SMALLFONT}{BLACK}Gäster åker alla attraktioner i parken{NEWLINE}även om intensiteten är extremt hög på attraktionen +STR_5795 :{SMALLFONT}{BLACK}Besökare åker alla attraktioner i parken{NEWLINE}även om intensiteten är extremt hög på attraktionen STR_5796 :{SMALLFONT}{BLACK}Tvingar parken att stängas eller öppnas STR_5797 :{SMALLFONT}{BLACK}Avaktiverar väderändringar och{NEWLINE}behåller det valda vädret STR_5798 :{SMALLFONT}{BLACK}Tillåter byggnationer när spelet är pausat STR_5799 :{SMALLFONT}{BLACK}Avaktiverar haveriet bromsfel så att åkturernas vagnar inte krockar med varandra STR_5800 :{SMALLFONT}{BLACK}Hindrar karuseller från att gå sönder STR_5801 :Avaktivera nedskräpning -STR_5802 :{SMALLFONT}{BLACK}Förhindrar gästerna från att skräpa ner och kräkas i parken +STR_5802 :{SMALLFONT}{BLACK}Förhindrar besökarna från att skräpa ner och kräkas i parken STR_5803 :{SMALLFONT}{BLACK}Rotera valda element STR_5804 :Stäng av ljudet STR_5805 :{SMALLFONT}{BLACK}Om ikryssad kommer din server att läggas till i{NEWLINE}den offentliga serverlistan där alla kan hitta den @@ -3280,12 +3262,12 @@ STR_5807 :{WINDOW_COLOUR_2}Antal åkturer: {BLACK}{COMMA16} STR_5808 :{WINDOW_COLOUR_2}Antal affärer och stånd: {BLACK}{COMMA16} STR_5809 :{WINDOW_COLOUR_2}Antal informationskioskar och toaletter: {BLACK}{COMMA16} STR_5810 :Avaktivera gränsen för vagnar per tåg -STR_5811 :{SMALLFONT}{BLACK}Om ikryssad kan du ha upp till{NEWLINE}255 vagnar per tåg och{NEWLINE}31 tåg per attraktion +STR_5811 :{SMALLFONT}{BLACK}Om ikryssad kan du ha upp till{NEWLINE}255 vagnar per tåg och 31{NEWLINE}tåg per attraktion STR_5812 :Visa flerspelarfönstret -STR_5813 :“{STRING}” -STR_5814 :{WINDOW_COLOUR_1}“{STRING}” +STR_5813 :”{STRING}” +STR_5814 :{WINDOW_COLOUR_1}”{STRING}” #tooltips -STR_5815 :{SMALLFONT}{BLACK}Visa FPS-mätare i spelet +STR_5815 :{SMALLFONT}{BLACK}Visa bildhastighetsmätare i spelet STR_5816 :{SMALLFONT}{BLACK}Sätter skalan i spelet.{NEWLINE}Mest användbar när du spelar i{NEWLINE}hög upplösning STR_5817 :{SMALLFONT}{BLACK}Väljer UI-skalningstyp, kräver att hårdvaruhantering används.{NEWLINE}'Linjär'-skalning är mjuk men suddig.'Jämna närmsta heltal'-skalning är skarp även vid decimaler men kan ta mer prestanda. STR_5819 :{SMALLFONT}{BLACK}[Kräver Hårdvaruhantering]{NEWLINE}Pausa spelet om Steam{NEWLINE}in-game overlay är öppet @@ -3295,8 +3277,8 @@ STR_5823 :{SMALLFONT}{BLACK}Visa banderoller med versaler (Som i RCT1) STR_5824 :{SMALLFONT}{BLACK}Avaktivera blixtrarna som kommer när det åskar{NEWLINE}Blixtrarna visar en ljusare bild i ungefär 1 sekund varje gång det åskar till STR_5825 :{SMALLFONT}{BLACK}Behåll musen i fönstret om du spelar i ett fönster STR_5826 :{SMALLFONT}{BLACK}Växla 'upp och ner' med varandra när du drar spelets kamera vid höger klick -STR_5827 :{SMALLFONT}{BLACK}Välj färgschema för spelets GUI (menyer o.s.v) -STR_5828 :{SMALLFONT}{BLACK}Ändra vilket mått du vill använda för längd, hastighet, o.s.v +STR_5827 :{SMALLFONT}{BLACK}Välj färgschema för spelets grafiska användargränsnitt +STR_5828 :{SMALLFONT}{BLACK}Ändra vilket måttsystem du vill använda för längd, hastighet och annat STR_5829 :{SMALLFONT}{BLACK}Ändra vilken valuta du vill använda.{NEWLINE}Endast visuellt, det finns ingen valutaomvandlare i spelet STR_5830 :{SMALLFONT}{BLACK}Välj vilket språk du ska använda STR_5831 :{SMALLFONT}{BLACK}Välj vilket format{NEWLINE}temperaturen visas i @@ -3307,28 +3289,28 @@ STR_5835 :{SMALLFONT}{BLACK}Tysta spelet om spelet tappar fokus STR_5836 :{SMALLFONT}{BLACK}Välj vilken musik som ska spelas på huvudmenyn.{NEWLINE}Val av RCT1 sången kräver att du kopierar 'data/css17.dat' från din RCT1-mapp till 'data/css50.dat' i din RCT2-mapp eller kolla så att sökvägen till RCT1 i 'Diverse' fliken är korrekt. STR_5837 :{SMALLFONT}{BLACK}Skapa och hantera egna UI-teman STR_5838 :{SMALLFONT}{BLACK}Visa en knapp för 'finans'-fönstret i verktygsfältet -STR_5839 :{SMALLFONT}{BLACK}Visa en knapp för 'forskning & utveckling'-fönstret i verktygsfältet +STR_5839 :{SMALLFONT}{BLACK}Visa en knapp för 'forskning och utveckling'-fönstret i verktygsfältet STR_5840 :{SMALLFONT}{BLACK}Visa en knapp för 'fusk' i verktygsfältet STR_5841 :{SMALLFONT}{BLACK}Visa en knapp för 'senaste nyheterna'-fönstret i verktygsfältet -STR_5842 :{SMALLFONT}{BLACK}Sortera scenarion via svårighetsgrad (Som i RCT2) eller som det kommer från början (Som i RCT1) -STR_5843 :{SMALLFONT}{BLACK}Tillåter upplåsning av scenarion när du slutför dem (som i RCT1 & RCT3) +STR_5842 :{SMALLFONT}{BLACK}Sortera scenarion via svårighetsgrad (Som i RCT2) eller som det kommer från början (som i RCT1) +STR_5843 :{SMALLFONT}{BLACK}Tillåter upplåsning av scenarion när du slutför dem (som i RCT1 och RCT3) STR_5844 :{SMALLFONT}{BLACK}Behåll anslutningen på servrar i flerspelarläget{NEWLINE}även om du hamnar i osynk eller fel uppstår STR_5845 :{SMALLFONT}{BLACK}Lägger till en knapp för{NEWLINE}'verktyg för felsökning' i verktygsraden.{NEWLINE}Aktiverar snabbknapp för utvecklarkonsolen STR_5846 :{SMALLFONT}{BLACK}Välj hur ofta OpenRCT2 ska spara automatiskt -STR_5847 :{SMALLFONT}{BLACK}Välj vilket parkintro spelet ska använda på huvudmenyn. Titelsekvenser från RCT1/2 kräver importerade filer för att fungera korrekt +STR_5847 :{SMALLFONT}{BLACK}Välj vilket parkintro spelet ska använda på huvudmenyn. Titelsekvenser från RCT1 och RCT2 kräver importerade filer för att fungera korrekt STR_5848 :{SMALLFONT}{BLACK}Skapa och hantera egna titelsekvenser STR_5849 :{SMALLFONT}{BLACK}Placera automatiskt ut ny personal när de anställs -STR_5850 : +STR_5850 : STR_5851 :{SMALLFONT}{BLACK}Sätter standarden på inspektionsmellanrummen{NEWLINE}på nybyggda attraktioner STR_5852 :{SMALLFONT}{BLACK}Välj namnet på TwitchTV kanalen som kan använda funktionerna nedanför efteråt -STR_5853 :{SMALLFONT}{BLACK}Stäng av/Sätt på ljudeffekter -STR_5854 :{SMALLFONT}{BLACK}Stäng av/Sätt på åkturernas musik +STR_5853 :{SMALLFONT}{BLACK}Slå av eller på ljudeffekter +STR_5854 :{SMALLFONT}{BLACK}Slå av eller på åkturernas musik STR_5855 :{SMALLFONT}{BLACK}Välj att spela i fullskärm, fönster,{NEWLINE}eller fullskärmsläge utan kanter STR_5856 :{SMALLFONT}{BLACK}Sätter spelets upplösning när du spelar i fullskärm STR_5857 :{SMALLFONT}{BLACK}Spelinställningar STR_5858 :{SMALLFONT}{BLACK}Använd GPU för grafiken istället för CPU. Ökar kompatibiliteten med skärminspelningsprogram som till exempel Fraps{NEWLINE}Kan försämra prestandan lite grand -STR_5859 :{SMALLFONT}{BLACK}Aktiverar 'frame tweening' för en visuellt mjukare spelupplevelse.{NEWLINE}Om avaktiverad kommer spelet köras i 40 fps. -STR_5860 :Växla original/omskrivna koden för texturer +STR_5859 :{SMALLFONT}{BLACK}Aktiverar 'frame tweening' för en visuellt mjukare spelupplevelse.{NEWLINE}Om avaktiverad kommer spelet köras i 40 bilder per sekund. +STR_5860 :Växla mellan originalkoden eller omskrivna koden för texturer STR_5861 :Nyckelverifiering misslyckades. STR_5862 :Blockera okända spelare. STR_5863 :{SMALLFONT}{BLACK}Tillåt endast spelare med kända nycklar att ansluta. @@ -3347,10 +3329,10 @@ STR_5875 :Grafikmotor: STR_5876 :{SMALLFONT}{BLACK}Spelmotorn som ska användas för att rita spelet. STR_5877 :Mjukvara STR_5878 :Mjukvara (hårdvaruhantering) -STR_5879 :OpenGL (experiment) -STR_5880 :Ibockade objekt -STR_5881 :Obockade objekt -STR_5882 :Skapa valuta +STR_5879 :OpenGL (experimentellt) +STR_5880 :Ikryssade objekt +STR_5881 :Ej ikryssade objekt +STR_5882 :Egen valuta STR_5883 :Inställningar för egen valuta STR_5884 :{WINDOW_COLOUR_2}Valutakurs: STR_5885 :{WINDOW_COLOUR_2}är lika med {COMMA32} GBP (Pund £) @@ -3376,9 +3358,9 @@ STR_5905 :{SMALLFONT}{BLACK}Ett verktyg kallad 'markgenerator' som automatisk STR_5906 :Zooma till musens position STR_5907 :{SMALLFONT}{BLACK}Inzoomning kommer förstoras till musen, istället för centrum av skärmen. STR_5908 :Tillåt ändringar av attraktion -STR_5909 :{SMALLFONT}{BLACK}Tillåter ändringar på attraktionstypen via karusellens meny.{NEWLINE}Instabil - kan krascha spelet +STR_5909 :{SMALLFONT}{BLACK}Tillåter ändringar på attraktionstypen via karusellens meny.{NEWLINE}Instabilt – kan krascha spelet STR_5910 :Verkställ -STR_5911 :Dölj Gångvägar +STR_5911 :Dölj gångvägar STR_5912 :Växla synlighet av gångvägar STR_5913 :Chatt STR_5914 :Okänd åktur @@ -3388,28 +3370,28 @@ STR_5917 :{COMMA16} spelare STR_5918 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} STR_5919 :{COMMA16} STR_5920 :Visa väder effekter -STR_5921 :{SMALLFONT}{BLACK}Om aktiverad, regn och mörkare färger kommer att visas när det stormar. +STR_5921 :{SMALLFONT}{BLACK}Om aktiverad kommer regn och mörkare färger att visas när det stormar. STR_5922 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{SMALLFONT}{BLACK}Max {STRINGID} STR_5923 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{SMALLFONT}{BLACK}Max {COMMA16} {STRINGID} per tåg -STR_5924 :Terräng info -STR_5925 :Gång-info -STR_5926 :Spår-info -STR_5927 :Dekoration-info -STR_5928 :Entré-info -STR_5929 :Stängsel-info -STR_5930 :Stora dekorationer-info -STR_5931 :Banderoll-info -STR_5932 :Korrupt ämne-info +STR_5924 :Terränginformation +STR_5925 :Gånginformation +STR_5926 :Spårinformation +STR_5927 :Information om dekorationer +STR_5928 :Entréinformation +STR_5929 :Stängselinformation +STR_5930 :Information om stora dekorationer +STR_5931 :Banderollinformation +STR_5932 :Information om korrupt ämne STR_5933 :Egenskaper -STR_5934 :{WINDOW_COLOUR_2}Terräng-textur: {BLACK}{STRINGID} -STR_5935 :{WINDOW_COLOUR_2}Terräng-kant: {BLACK}{STRINGID} +STR_5934 :{WINDOW_COLOUR_2}Terrängtextur: {BLACK}{STRINGID} +STR_5935 :{WINDOW_COLOUR_2}Terrängkant: {BLACK}{STRINGID} STR_5936 :{WINDOW_COLOUR_2}Ägd mark: {BLACK}{STRINGID} STR_5937 :Inte ägd och inte till salu STR_5938 :{WINDOW_COLOUR_2}Vattennivå: {BLACK}{COMMA16} STR_5939 :Ta bort park stängsel STR_5940 :Återställ park stängsel STR_5941 :{WINDOW_COLOUR_2}Bashöjd: -STR_5942 :{WINDOW_COLOUR_2}Gång-namn: {BLACK}{STRINGID} +STR_5942 :{WINDOW_COLOUR_2}Gångnamn: {BLACK}{STRINGID} STR_5943 :{WINDOW_COLOUR_2}Tillägg: {BLACK}{STRINGID} STR_5944 :{WINDOW_COLOUR_2}Tillägg: {BLACK}Ingen STR_5945 :{WINDOW_COLOUR_2}Anslutna kanter: @@ -3418,28 +3400,28 @@ STR_5947 :{WINDOW_COLOUR_2}Åktur identifikation: {BLACK}{COMMA16} STR_5948 :{WINDOW_COLOUR_2}Åktur namn: {BLACK}{STRINGID} STR_5949 :{WINDOW_COLOUR_2}Kjedjelyft STR_5950 :{WINDOW_COLOUR_2}Verkställ ändringar till hela spåret -STR_5951 :{WINDOW_COLOUR_2}Spårdelens-ID: {BLACK}{COMMA16} +STR_5951 :{WINDOW_COLOUR_2}Spårdels-id: {BLACK}{COMMA16} STR_5952 :{WINDOW_COLOUR_2}Sekvensnummer: {BLACK}{COMMA16} STR_5953 :{SMALLFONT}{BLACK}Sortera ämnen på den valda rutan baserat på deras höjd. STR_5954 :{WINDOW_COLOUR_2}Dekorationens ålder: {BLACK}{COMMA16} -STR_5955 :{WINDOW_COLOUR_2}Quadrant placement: {BLACK}{STRINGID} +STR_5955 :{WINDOW_COLOUR_2}Placering i väderstreck: {BLACK}{STRINGID} STR_5956 :Sydväst STR_5957 :Nordväst STR_5958 :Nordost STR_5959 :Sydost -STR_5960 :{WINDOW_COLOUR_2}Quadrant placement: -STR_5961 :{WINDOW_COLOUR_2}Entry index: {BLACK}{COMMA16} -STR_5962 :{WINDOW_COLOUR_2}Kollision detection: -STR_5963 :{WINDOW_COLOUR_2}Höjda Hörn: +STR_5960 :{WINDOW_COLOUR_2}Placering i väderstreck: +STR_5961 :{WINDOW_COLOUR_2}Entré-index: {BLACK}{COMMA16} +STR_5962 :{WINDOW_COLOUR_2}Kollisionsdetektering: +STR_5963 :{WINDOW_COLOUR_2}Upphöjda hörn: STR_5964 :{WINDOW_COLOUR_2}Diagonal -STR_5965 :{WINDOW_COLOUR_2}Entré typ: {BLACK}{STRINGID} -STR_5966 :{WINDOW_COLOUR_2}Park-entré del: {BLACK}{STRINGID} +STR_5965 :{WINDOW_COLOUR_2}Entrétyp: {BLACK}{STRINGID} +STR_5966 :{WINDOW_COLOUR_2}Parkentré-del: {BLACK}{STRINGID} STR_5967 :Mitten STR_5968 :Vänster STR_5969 :Höger -STR_5970 :{WINDOW_COLOUR_2}Entré ID: {BLACK}{COMMA16} -STR_5971 :{WINDOW_COLOUR_2}Utgång ID: {BLACK}{COMMA16} -STR_5972 :{WINDOW_COLOUR_2}Åktur ID: {BLACK}{COMMA16} +STR_5970 :{WINDOW_COLOUR_2}Entré-id: {BLACK}{COMMA16} +STR_5971 :{WINDOW_COLOUR_2}Utgångs-id: {BLACK}{COMMA16} +STR_5972 :{WINDOW_COLOUR_2}Åkturs-id: {BLACK}{COMMA16} STR_5973 :Fäst till nästa STR_5974 :{SMALLFONT}{BLACK}Ändra bas- och klarhöjd så det är samma som nästa ämne på den valda rutan. Genom att göra detta blir det lättare bygga på denna ruta. STR_5975 :Backe: @@ -3450,7 +3432,7 @@ STR_5979 :{WINDOW_COLOUR_2}Stängsel typ: {BLACK}{COMMA16} STR_5980 :{WINDOW_COLOUR_2}Banderoll text: {BLACK}{STRINGID} STR_5981 :{WINDOW_COLOUR_2}Inte en banderoll STR_5982 :{WINDOW_COLOUR_2}Stor dekoration typ: {BLACK}{COMMA16} -STR_5983 :{WINDOW_COLOUR_2}Stor dekortaion del ID: {BLACK}{COMMA16} +STR_5983 :{WINDOW_COLOUR_2}Stor dekortaionsdels-id: {BLACK}{COMMA16} STR_5984 :Blockerade gångar: STR_5985 :Ny mapp STR_5986 :Skriv in namnet på den nya mappen. @@ -3458,56 +3440,56 @@ STR_5987 :Kan inte skapa mappen STR_5988 :{SMALLFONT}{BLACK}Ingen mer mark till salu STR_5989 :{SMALLFONT}{BLACK}Inget mer bygglov till salu STR_5990 :{SMALLFONT}{BLACK}Ingen mer mark eller bygglov till salu -STR_5991 :Kan inte klistra in ämnet... +STR_5991 :Kan inte klistra in ämnet … STR_5992 :Kartan har nått sin gräns STR_5993 :{SMALLFONT}{BLACK}Kopiera valda ämne STR_5994 :{SMALLFONT}{BLACK}Klistra in kopierat ämne STR_5995 :Booster -STR_5996 :Booster hastighet -STR_5997 :Addera/Välj summa pengar -STR_5998 :Addera pengar +STR_5996 :Booster-hastighet +STR_5997 :Lägg till eller välj summa pengar +STR_5998 :Lägg till pengar STR_5999 :Välj summa pengar STR_6000 :Skriv in nytt värde STR_6001 :Aktivera ljuseffekter (experiment) -STR_6002 :{SMALLFONT}{BLACK}Lampor och åkturer kommer lysa upp i natten.{NEWLINE}Kräver rendering-motorn vara satt på Hårdvaruhantering. -STR_6003 :Avskärnings-vy -STR_6004 :Avskärnings-vy -STR_6005 :Aktivera Avskärnings-vy +STR_6002 :{SMALLFONT}{BLACK}Lampor och åkturer kommer lysa upp i natten.{NEWLINE}Kräver att renderingsmotorn är satt till hårdvaruhantering. +STR_6003 :Avskärningsvy +STR_6004 :Avskärningsvy +STR_6005 :Aktivera Avskärningsvy STR_6006 :{SMALLFONT}{BLACK}Avskärnings-vyn kommer endast visa element på eller under den avskurna höjden (vertikal avskärning)och det markerade området (horizontal avskärning) STR_6007 :Höjd STR_6008 :{SMALLFONT}{BLACK}Klicka för att växla råa-värden<->värden i måttenheter STR_6009 :{SMALLFONT}{BLACK}Välj höjd på avskärning STR_6010 :{COMMA2DP32}m -STR_6011 :{COMMA1DP16}ft +STR_6011 :{COMMA1DP16}fot STR_6012 :{COMMA1DP16} -STR_6013 :{SMALLFONT}{BLACK}Gästerna kommer endast betala biljetterna för att komma in i parken och tjänster.{NEWLINE}Åktursbiljetterna kommer vara gratis. -STR_6014 :{SMALLFONT}{BLACK}Gästerna kommer endast betala för åkturer och tjänster.{NEWLINE}Dem kommer inte betala något för inträde. +STR_6013 :{SMALLFONT}{BLACK}Besökarna kommer endast betala biljetterna för att komma in i parken och tjänster.{NEWLINE}Åktursbiljetterna kommer vara gratis. +STR_6014 :{SMALLFONT}{BLACK}Besökarna kommer endast betala för åkturer och tjänster.{NEWLINE}Dem kommer inte betala något för inträde. STR_6015 :Lutande STR_6016 :Modifiera Ruta STR_6017 :Vänligen sakta ned -STR_6018 :Åkturkonstruktion - Sväng vänster -STR_6019 :Åkturkonstruktion - Sväng höger -STR_6020 :Åkturkonstruktion - Rakt spår -STR_6021 :Åkturkonstruktion - Lutning nedåt -STR_6022 :Åkturkonstruktion - Lutning uppåt -STR_6023 :Åkturkonstruktion - Växla lyftkjedjor -STR_6024 :Åkturkonstruktion - Lutning vänster -STR_6025 :Åkturkonstruktion - Lutning höger -STR_6026 :Åkturkonstruktion - Föregående spår -STR_6027 :Åkturkonstruktion - Nästa spår -STR_6028 :Åkturkonstruktion - Bygg valda spår -STR_6029 :Åkturkonstruktion - Riv valda spår -STR_6030 :{SMALLFONT}{BLACK}Dekorations-väljaren. Klicka på en dekoration på kartan för att välja samma del för konstruktion. -STR_6031 :Server Beskrivning: -STR_6032 :Server Hälsning: +STR_6018 :Åkturkonstruktion – Sväng vänster +STR_6019 :Åkturkonstruktion – Sväng höger +STR_6020 :Åkturkonstruktion – Rakt spår +STR_6021 :Åkturkonstruktion – Lutning nedåt +STR_6022 :Åkturkonstruktion – Lutning uppåt +STR_6023 :Åkturkonstruktion – Växla lyftkjedjor +STR_6024 :Åkturkonstruktion – Dosering vänster +STR_6025 :Åkturkonstruktion – Dosering höger +STR_6026 :Åkturkonstruktion – Föregående spår +STR_6027 :Åkturkonstruktion – Nästa spår +STR_6028 :Åkturkonstruktion – Bygg valda spår +STR_6029 :Åkturkonstruktion – Riv valda spår +STR_6030 :{SMALLFONT}{BLACK}Dekorationsväljaren. Klicka på en dekoration på kartan för att välja samma del för konstruktion. +STR_6031 :Serverbeskrivning: +STR_6032 :Serverhälsning: STR_6033 :Sökvägen till RCT1-mappen: STR_6034 :{SMALLFONT}{BLACK}{STRING} STR_6035 :Vänligen välj installationsmappen för RCT1 STR_6036 :{SMALLFONT}{BLACK}Rensa STR_6037 :Vänligen välj en giltig sökväg till RCT1-mappen -STR_6038 :{SMALLFONT}{BLACK}Om du har RCT1-installerat, navigera till den mappen för att ladda scenarion, musik, m.m. +STR_6038 :{SMALLFONT}{BLACK}Om du har RCT1-installerat, navigera till den mappen för att ladda scenarion, musik m.m. STR_6039 :{SMALLFONT}{BLACK}Demolera attraktioner snabbt -STR_6040 :Redigera scenario-inställningar +STR_6040 :Redigera scenarioinställningar STR_6041 :{BLACK}Inga mekaniker är anställda! STR_6042 :Ladda höjdkarta STR_6043 :Välj höjdkarta @@ -3517,18 +3499,18 @@ STR_6046 :Normalisera höjdkarta STR_6047 :Utjämna rutor STR_6048 :Fel med höjdkarta STR_6049 :Fel vid inläsning av PNG -STR_6050 :Fel vid inläsning av bitmap +STR_6050 :Fel vid inläsning av bitmapsbild STR_6051 :Höjden och bredden måste vara lika STR_6052 :Höjdkartan är för stor och kommer skäras av STR_6053 :Höjdkartan kan inte normaliseras -STR_6054 :Endast 24-bit bitmaps stöds -STR_6055 :OpenRCT2 Höjdkart-filer -STR_6056 :{SMALLFONT}{BLACK}Tysta -STR_6057 :{SMALLFONT}{BLACK}Visa en knapp för att 'Tysta' spelet i verktygsfältet -STR_6058 :Tysta +STR_6054 :Endast 24-bitars bitmapsbilder stöds +STR_6055 :OpenRCT2-höjdkartor +STR_6056 :{SMALLFONT}{BLACK}Slå av eller på ljudet +STR_6057 :{SMALLFONT}{BLACK}Visa en knapp för att slå av eller på ljudet i verktygsfältet +STR_6058 :Slå av eller på ljudet STR_6059 :» -STR_6060 :Visa animation när gäster spenderar pengar -STR_6061 :{SMALLFONT}{BLACK}Visar en animerad effekt när gästerna gör ett köp. +STR_6060 :Visa animation när besökare spenderar pengar +STR_6061 :{SMALLFONT}{BLACK}Visar en animerad effekt när besökarna gör ett köp. STR_6062 :{OUTLINE}{GREEN}+ {CURRENCY2DP} STR_6063 :{OUTLINE}{RED}- {CURRENCY2DP} STR_6064 :Ta all mark @@ -3536,14 +3518,14 @@ STR_6065 :Logga handlingar på användare STR_6066 :{SMALLFONT}{BLACK}Loggar allt användarna gör i multiplayer till filer i 'dina dokument' STR_6067 :Server startade. STR_6068 :Server avslutades. -STR_6069 :{STRING} vart kickad från servern av {STRING}. +STR_6069 :{STRING} kickades från servern av {STRING}. STR_6070 :{STRING} fick grupp-behörigheten '{STRING}' av {STRING}. STR_6071 :{STRING} skapade en ny grupp-behörighet '{STRING}'. STR_6072 :{STRING} tog bort grupp-behörigheten '{String}'. STR_6073 :{STRING} redigerade behörigheterna på gruppen '{String}'. STR_6074 :{STRING} ändrade namn på grupp-behörigheten '{String}' till '{String}'. STR_6075 :{STRING} ändrade standardgruppen till '{String}'. -STR_6076 :{STRING} använde/växlade fusk '{STRING}'. +STR_6076 :{STRING} använde eller växlade fusk '{STRING}'. STR_6077 :Pengarna ändrades STR_6078 :{STRING} skapade åktur '{STRING}'. STR_6079 :{STRING} demolerade åktur '{STRING}'. @@ -3553,7 +3535,7 @@ STR_6082 :{STRING} ändrade status på åktur '{STRING}' till öppen. STR_6083 :{STRING} ändrade status på åktur '{STRING}' till testa. STR_6084 :{STRING} ändrade vagnarnas inställningar på åktur '{STRING}'. STR_6085 :{STRING} ändrade inställningar på åkturen '{STRING}'. -STR_6086 :{STRING} döpte om åktur '{STRING}' till '{STRING}'. +STR_6086 :{STRING} bytte namn på åktur från '{STRING}' till '{STRING}'. STR_6087 :{STRING} ändrade priset på åktur '{STRING}' till {STRING} STR_6088 :{STRING} ändrade sekundära priset på åktur '{STRING}' till {STRING} STR_6089 :{STRING} ändrade parkens namn från '{STRING}' till '{STRING}'. @@ -3569,20 +3551,20 @@ STR_6098 :{STRING} tog bort ett spår av en åktur. STR_6099 :Du anslöt till servern. STR_6100 :Du lämnade servern. STR_6101 :Åkturer minskar aldrig i värde -STR_6102 :{SMALLFONT}{BLACK}Värdet på åkturer minskar inte vid längre perioder, så gäster kommer inte tycka den helt plötsligt är för dyr. +STR_6102 :{SMALLFONT}{BLACK}Värdet på åkturer minskar inte vid längre perioder, så besökare kommer inte tycka den helt plötsligt är för dyr. STR_6103 :{SMALLFONT}{BLACK}Denna inställning är avaktiverad i multiplayer. -STR_6104 :Korkskruvs berg- och dalbana -STR_6105 :Hyper berg- och dalbana -STR_6106 :Bilåktur -STR_6107 :Monster Truckar -STR_6108 :Snurr berg- och dalbana -STR_6109 :Hyper berg- och dalbana -STR_6110 :Junior berg- och dalbana -STR_6111 :Klassisk mini berg- och dalbana -STR_6112 :En kompakt stål berg- och dalbana där tågen åker längs korkskruvar och loopar +STR_6104 :Korkskruvsberg- och dalbana +STR_6105 :Hyperberg- och dalbana +STR_6106 :Biltur +STR_6107 :Monstertruckar +STR_6108 :Snurrberg- och dalbana +STR_6109 :Hyperberg- och dalbana +STR_6110 :Juniorberg- och dalbana +STR_6111 :Klassisk miniberg- och dalbana +STR_6112 :En kompakt stålberg- och dalbana där tågen åker längs korkskruvar och loopar STR_6113 :En hög berg- och dalbana som inte vänder på sig med stora dropp, höga hastigheter, och bekväma tåg med endast säkerhetsbygel runt midjan -STR_6114 :Passagerare åker sakta i motoriserade fordon på en spår-baserad väg -STR_6115 :Motoriserade gigantiska 4 x 4 truckar som kan klättra över stora stockar +STR_6114 :Passagerare åker sakta i motorfordon på en spårlagd väg +STR_6115 :Motordrivna gigantiska fyrhjulsdrivna bilar som kan klättra över stora stockar STR_6116 :Bred berg- och dalbana tåg som glider efter ett mjukt stålspår, rusande genom en variation av inversioner STR_6117 :Sittandes i bekväma vagnar med endast säkerhetsbygel runt midjan njuter passagerare av enorma nedförsbackar och spännande svängar, samt mycket tid flygandes över topparna STR_6118 :En snäll berg- och dalbana för de som inte har mod nog att utmana större åkturer @@ -3604,36 +3586,36 @@ STR_6133 :{SMALLFONT}{BLACK}Ger tillgång till åkturer och dekorationer som STR_6134 :Rensa dekoration STR_6135 :Klient skickade ogiltig begäran STR_6136 :Server skickade ogiltig begäran -STR_6137 :OpenRCT2, en gratis klon med öppen källkod av Roller Coaster Tycoon 2. -STR_6138 :OpenRCT2 är ett samarbete med flera deltagare, en lista med alla deltagare kan hittas i “contributors.md”. För mer information, besök http://github.com/OpenRCT2/OpenRCT2 +STR_6137 :OpenRCT2, en gratis klon av Roller Coaster Tycoon 2 med öppen källkod. +STR_6138 :OpenRCT2 är ett samarbete med flera deltagare, en lista med alla deltagare kan hittas i ”contributors.md”. För mer information, besök http://github.com/OpenRCT2/OpenRCT2 STR_6139 :Alla produkter och företagsnamn tillhör deras respektive innehavare och ägare. Användningen av dem är ingen anknytning eller bekräftelse från respektive innehavare. -STR_6140 :Ändringslogg... -STR_6141 :RCT1-bottens Verktygsfält +STR_6140 :Ändringslogg … +STR_6141 :RCT1s nedre verktygsfält STR_6142 :{WINDOW_COLOUR_2}Namn: {BLACK}{STRING} STR_6143 :{WINDOW_COLOUR_2}Typ: {BLACK}{STRINGID} -STR_6144 :Visa visuella-uppdateringar +STR_6144 :Visa visuella uppdateringar STR_6145 :{SMALLFONT}{BLACK}Sätt hastighetsgräns för boosters STR_6146 :Aktivera alla ritbara spår STR_6147 :{SMALLFONT}{BLACK}Aktiverar alla spår attraktionstypen är tillgänglig att visa i konstruktionsfönstret, oavsett om fordonet har en ritbar version av spåret. -STR_6148 :Ansluter till huvudservern... +STR_6148 :Ansluter till huvudservern … STR_6149 :Det gick inte ansluta till huvudservern STR_6150 :Ogiltigt svar från huvudservern (inget JSON-nummer) STR_6151 :Huvudservern misslyckades att söka upp servrar -STR_6152 :Ogiltigt svar från huvudservern (ingen JSON-array) -STR_6153 :Betala för inträde / Betala för åktur +STR_6152 :Ogiltigt svar från huvudservern (ingen JSON-lista) +STR_6153 :Betala för inträde eller betala för åktur STR_6154 :Av säkerhetsskäl är det inte rekommenderat att köra OpenRCT2 med extra behörigheter. STR_6155 :Varken KDialog eller Zenity är installerade. Vänligen installera en av dem eller konfigurera från kommandoraden. -STR_6156 :Namn är reserverat +STR_6156 :Namnet är reserverat STR_6157 :Utvecklarkonsol -STR_6158 :Misslyckades att ladda denna fil...{NEWLINE}Inkompatibel RCTC-version: {COMMA16} -STR_6159 :Utjämna närmsta heltal +STR_6158 :Misslyckades att ladda denna fil …{NEWLINE}Inkompatibel RCTC-version: {COMMA16} +STR_6159 :Avrunda till närmsta granne STR_6160 :{WINDOW_COLOUR_2}Tillgängliga fordon: {BLACK}{STRING} STR_6161 :Växla rutmönster på terräng -STR_6162 :Snurrande vilda musen +STR_6162 :Snurrande vildmus STR_6163 :Musformade vagnar susar förbi snäva kurvor och korta stup medan vagnen försiktigt snurrar för att desorientera passagerarna STR_6164 :{WHITE}❌ -STR_6165 :Använd Vertical Sync (V-sync) -STR_6166 :{SMALLFONT}{BLACK}Synkroniserar varje bild som ritas med skärmens "refresh rate", vilket förhindrar "Screen tearing". +STR_6165 :Använd vertikal synkronisering (V-sync) +STR_6166 :{SMALLFONT}{BLACK}Synkroniserar varje bild som ritas med skärmens uppdateringshastighet, vilket förhindrar så kallad 'screen tearing'. STR_6167 :{SMALLFONT}{BLACK}Avancerat STR_6168 :Titelsekvens STR_6169 :Scenarioväljaren @@ -3660,16 +3642,16 @@ STR_6189 :Anka STR_6190 :{SMALLFONT}{BLACK}Du kan inte välja ett objekt medan titelsekvensen spelas. STR_6191 :Mark STR_6192 :Vägg -STR_6193 :{COMMA16} gäst -STR_6194 :{INLINE_SPRITE}{11}{20}{00}{00}{COMMA16} gäst -STR_6195 :{INLINE_SPRITE}{10}{20}{00}{00}{COMMA16} gäst +STR_6193 :{COMMA16} besökare +STR_6194 :{INLINE_SPRITE}{11}{20}{00}{00}{COMMA16} besökare +STR_6195 :{INLINE_SPRITE}{10}{20}{00}{00}{COMMA16} besökare STR_6196 :{BLACK}År: STR_6197 :{BLACK}Månad: STR_6198 :{BLACK}Dag: STR_6199 :Välj datum STR_6200 :Återställ datum STR_6201 :{MONTH} -STR_6202 :Stil på virutiella golv +STR_6202 :Stil på virutiella golv: STR_6203 :{SMALLFONT}{BLACK}När aktiverad, kommer ett rutnät att ritas i samma höjd som elementen är på när du håller in Ctrl eller Shift vilket förenklar vertikala placeringar. STR_6204 :Tegel STR_6205 :Järn @@ -3684,25 +3666,25 @@ STR_6213 :Skyskrapa A STR_6214 :Skyskrapa B STR_6215 :Konstruktion STR_6216 :Manövrering -STR_6217 :Åktur-/spårtillgänglighet +STR_6217 :Åkturs- och spårtillgänglighet STR_6218 :Officiella OpenRCT2 STR_6219 :Visa problem på gångvägar STR_6220 :Skapa gång -STR_6221 :{SMALLFONT}{BLACK}Detta kommer ställa in åkturens kända ingång- eller utgångsposition till den nuvarande valda rutan. Endast en ingång/utgång kan ställas in per station. -STR_6222 :Kan inte placera en ingångspunkt här... +STR_6221 :{SMALLFONT}{BLACK}Detta kommer ställa in åkturens kända ingång- eller utgångsposition till den nuvarande valda rutan. Endast en ingång och utgång kan ställas in per station. +STR_6222 :Kan inte placera en ingångspunkt här … STR_6223 :Måste vara utanför parkens gränser! STR_6224 :{STRING} placerade en ingångspunkt. STR_6225 :Visningen stöds inte i OpenGL-rendering STR_6226 :Tillåt vinst innan scenarions tid gått ut STR_6227 :{SMALLFONT}{BLACK}Räknas som att scenarion avklarades när alla mål i scenarion är slutförda innan det satta datumet. -STR_6228 :Scenario-inställningar +STR_6228 :Scenarioinställningar STR_6229 :{WINDOW_COLOUR_2}{STRINGID}: {STRINGID} STR_6230 :{STRINGID}: STR_6231 :{WINDOW_COLOUR_2}{STRINGID}: {MOVE_X}{185}{STRINGID} -STR_6232 :Frys -STR_6233 :Avskärnings-vy +STR_6232 :Fryst +STR_6233 :Avskärningsvy STR_6234 :Visa problem på gångvägar -STR_6235 :Server information +STR_6235 :Serverinformation STR_6236 :Spelare STR_6237 :Grupper STR_6238 :Inställningar online @@ -3711,16 +3693,16 @@ STR_6240 :Horisontal avskärning STR_6241 :Markera område STR_6242 :Rensa område STR_6243 :{SMALLFONT}{BLACK}Renoverar åkturen,{NEWLINE}blir som ny igen -STR_6244 :Kan inte renovera åkturen... +STR_6244 :Kan inte renovera åkturen … STR_6245 :{SMALLFONT}{BLACK}Åkturen behöver inte renoveras STR_6246 :Renovera -STR_6247 :Renovera åkturen/attraktionen +STR_6247 :Renovera åkturen eller attraktionen STR_6248 :{WINDOW_COLOUR_1}Vill du renovera {STRINGID} för {CURRENCY}? STR_6249 :{WINDOW_COLOUR_1}Vill du renovera {STRINGID}? STR_6250 :{WINDOW_COLOUR_1}Är du säker på att du vill riva {STRINGID} och tjäna {CURRENCY}? STR_6251 :Åkturen är inte tom STR_6252 :Twitch API-URL -STR_6253 :{SMALLFONT}{BLACK}Skriv in URL:en för Twitch integrerade-API. Krävs för att aktivera Twitch integration. +STR_6253 :{SMALLFONT}{BLACK}Skriv in URL:en för Twitch integrerade-API. Krävs för att aktivera Twitch-integration. STR_6254 :URL:en av Twitch integrerade-API: STR_6255 :URL:en är inte giltig STR_6256 :Rendering-effekt @@ -3730,11 +3712,72 @@ STR_6259 :Inaktivera STR_6260 :Visa blockerade rutor STR_6261 :Visa breda gångar STR_6262 :Huvudvolym -STR_6263 :{SMALLFONT}{BLACK}Växla alla ljud på/av +STR_6263 :{SMALLFONT}{BLACK}Slå av eller på alla ljud STR_6264 :Använd alltid operativsystemets filhanterare STR_6265 :{SMALLFONT}{BLACK}Om aktiverad kommer spelet använda filhanteraren på datorn istället för OpenRCT2s egna. STR_6266 :Öppna mappen för anpassat innehåll STR_6267 :Öppna rutinspekteraren +STR_6268 :Fortsätt till nästa tick +STR_6269 :Ogiltigt klimat-id +STR_6270 :Terrängytor +STR_6271 :Terrängsidor +STR_6272 :Stationer +STR_6273 :Musik +STR_6274 :Kan inte välja färgschema … +STR_6275 :{WINDOW_COLOUR_2}Stationsstil: +STR_6276 :{RED}{STRINGID} har besökare som sitter fast, troligen på grund av ogiltig typ av åktur eller felaktigt körläge. +STR_6277 :{WINDOW_COLOUR_2}Stationsindex: {BLACK}{STRINGID} +STR_6278 :Antal automatiska sparningar +STR_6279 :{SMALLFONT}{BLACK}Antal automatiska sparningar att behålla +STR_6280 :{SMALLFONT}{BLACK}Chatt +STR_6281 :{SMALLFONT}{BLACK}Visa en egen knapp för chattfönstret i verktygsfältet +STR_6282 :Chatt +STR_6283 :Chatten är inte tillgänglig just nu. Är du ansluten till en server? +STR_6284 :Nätverk +STR_6285 :Nätverksinformation +STR_6286 :Ta emot +STR_6287 :Skicka +STR_6288 :Summa mottaget +STR_6289 :Summa skickat +STR_6290 :Grundprotokoll +STR_6291 :Kommandon +STR_6292 :Karta +STR_6293 :B +STR_6294 :KiB +STR_6295 :MiB +STR_6296 :GiB +STR_6297 :TiB +STR_6298 :{STRING}/s +STR_6299 :Ladda ner allt +STR_6300 :{SMALLFONT}{BLACK}Ladda ner samtliga saknade objekt om de finns tillgängliga online. +STR_6301 :{SMALLFONT}{BLACK}Kopiera det markerade objektnamnet till urklipp. +STR_6302 :{SMALLFONT}{BLACK}Kopiera hela listan över saknade objekt till urklipp. +STR_6303 :Laddar ner objekt ({COMMA16} av {COMMA16}): [{STRING}] +STR_6304 :Öppen dekorationsväljare +STR_6305 :Flertrådsprocess +STR_6306 :{SMALLFONT}{BLACK}Experiment att använda flera processortrådar för rendering, kan orsaka instabilitet. +STR_6307 :Färgschema: {BLACK}{STRINGID} +STR_6308 :”{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :Återanslut +STR_6310 :{WINDOW_COLOUR_2}Position: {BLACK}{INT32} {INT32} {INT32} +STR_6311 :{WINDOW_COLOUR_2}Nästa: {BLACK}{INT32} {INT32} {INT32} +STR_6312 :(yta) +STR_6313 :(sluttning {INT32}) +STR_6314 :{WINDOW_COLOUR_2}Mål: {BLACK}{INT32}, {INT32} tolerans {INT32} +STR_6315 :{WINDOW_COLOUR_2}Sök efter mål: {BLACK}{INT32}, {INT32}, {INT32} riktning {INT32} +STR_6316 :{WINDOW_COLOUR_2}Sök efter historik: +STR_6317 :{BLACK}{INT32}, {INT32}, {INT32} riktning {INT32} +STR_6318 :Synkroniseringsfel upptäckt över nätverket.{NEWLINE}Loggfil: {STRING} +STR_6319 :{WINDOW_COLOUR_2}Blockera Bromsa Stängd +STR_6320 :{WINDOW_COLOUR_2}Oförstörbar +STR_6321 :{WINDOW_COLOUR_2}Addition fungerar inte +STR_6322 :{WINDOW_COLOUR_2}Sprite-id: {BLACK}{INT32} +STR_6323 :Simulerar +STR_6324 :Simulera +STR_6325 :{SMALLFONT}{BLACK}Simulera åktur eller attraktion +STR_6326 :Kan inte simulera {POP16}{POP16}{POP16}{STRINGID} … +STR_6327 :Genomskinlig bakgrund för stora skärmdumpar +STR_6328 :{SMALLFONT}{BLACK}Med det här alernativet aktiverat kommer stora skärmdumpar ha genomskinlig bakgrund istället för den vanliga svarta. @@ -3761,7 +3804,7 @@ STR_DTLS :Börja från början med att bygga ett nöjesfält runt en stor sj STR_SCNR :Diamond Heights STR_PARK :Diamond Heights -STR_DTLS :Diamond Heights är redan en framgångsrik nöjespark - utveckla den för att dubbla värdet +STR_DTLS :Diamond Heights är redan en framgångsrik nöjespark – utveckla den för att dubbla värdet STR_SCNR :Evergreen Gardens @@ -3781,7 +3824,7 @@ STR_DTLS :Flera små öar som är till för att bygga en megapark STR_SCNR :Katie's Dreamland STR_PARK :Katie's Dreamland -STR_DTLS :Ett litet nöjesfält som behöver fräschas upp och utökas - Ditt mål är att dubbla parkvärdet +STR_DTLS :Ett litet nöjesfält som behöver fräschas upp och utökas – Ditt mål är att dubbla parkvärdet STR_SCNR :Pokey Park @@ -3801,7 +3844,7 @@ STR_DTLS :Bygg om en stor övergiven gruva till ett stort nöjesfält STR_SCNR :Karts & Coasters STR_PARK :Karts & Coasters -STR_DTLS :En stor park gömd i skogen, med endast go-karts och trä berg- och dalbanor +STR_DTLS :En stor park gömd i skogen, med endast gokarter och träberg- och dalbanor STR_SCNR :Mel's World @@ -3821,7 +3864,7 @@ STR_DTLS :Förvandla de Egyptiska Ruinerna till en spännande nöjespark STR_SCNR :Crumbly Woods STR_PARK :Crumbly Woods -STR_DTLS :En stor park med väldesignade, men äldre attraktioner - Byt ut dem gamla eller skaffa nya för att göra parken mer populär +STR_DTLS :En stor park med väldesignade, men äldre attraktioner – Byt ut dem gamla eller skaffa nya för att göra parken mer populär STR_SCNR :Paradise Pier @@ -3831,7 +3874,7 @@ STR_DTLS :Förvandla denna trötta Pir till en välkänd attraktion STR_SCNR :Lightning Peaks STR_PARK :Lightning Peaks -STR_DTLS :De vackra bergen vid Lightning Peaks är populära bland fotgängare och äventyrare - Använd marken för att attrahera nya spänningssökande gäster +STR_DTLS :De vackra bergen vid Lightning Peaks är populära bland fotgängare och äventyrare – Använd marken för att attrahera nya spänningssökande besökare STR_SCNR :Ivory Towers @@ -3846,7 +3889,7 @@ STR_DTLS :Rainbow Valleys lokala myndighet tillåter inga landändringar elle STR_SCNR :Thunder Rock STR_PARK :Thunder Rock -STR_DTLS :Thunder Rock ligger i mitten av öknen och attraherar många turister - Använd den tillgängliga marken för att locka mer turister +STR_DTLS :Thunder Rock ligger i mitten av öknen och attraherar många turister – Använd den tillgängliga marken för att locka mer turister STR_SCNR :Mega Park @@ -3862,7 +3905,7 @@ STR_DTLS :Utveckla havskustens klippor till en spännande nöjespark STR_SCNR :Three Monkeys Park STR_PARK :Three Monkeys Park -STR_DTLS :I centrum av denna stora utvecklande park finns ett trippel-spår racing/duellering stål berg- och dalbana +STR_DTLS :I centrum av denna stora utvecklande park finns ett trespårig racingberg- och dalbana i stål STR_SCNR :Canary Mines @@ -3937,7 +3980,7 @@ STR_DTLS :En stor ravin som är din för att byggas till ett nöjesfält STR_SCNR :Thunderstorm Park STR_PARK :Thunderstorm Park -STR_DTLS :Vädret är så vått att den stora pyramiden är byggd för att låta attraktioner ha skydd från regnet +STR_DTLS :Vädret är så vått att den stora pyramiden byggdes för att skydda attraktioner från regnet STR_SCNR :Harmonic Hills @@ -4019,7 +4062,7 @@ STR_DTLS :En passiv vulkan är grunden till denna utmanande park som ska fyll STR_SCNR :Arid Heights STR_PARK :Arid Heights -STR_DTLS :Fri från finansiella gränser, din utmaning är att utöka denna ökenpark medan du håller gästerna glada +STR_DTLS :Fri från finansiella gränser, din utmaning är att utöka denna ökenpark medan du håller besökarna glada STR_SCNR :Razor Rocks @@ -4222,7 +4265,7 @@ STR_DTLS :Skapa din egen version av det här omfattande Six Flags nöjesfält STR_SCNR :Bygg din egen Six Flags Park STR_PARK :Six Flags -STR_DTLS :Bygg din egen designade Six Flags park - Bygg antingen attraktioner från andra Six Flags parker eller designa och bygg dina egna attraktioner +STR_DTLS :Bygg din egen designade Six Flags park – Bygg antingen attraktioner från andra Six Flags parker eller designa och bygg dina egna attraktioner STR_SCNR :Bygg ditt eget Six Flags över Texas @@ -4237,7 +4280,7 @@ STR_DTLS :Börja med en liten marknad, din utmaning är att öka vinsten frå STR_SCNR :Roliga slottet STR_PARK :Roliga slottet -STR_DTLS :Du har ärvt ett stort slott - Ditt jobb är att förvandla det till en liten temapark. +STR_DTLS :Du har ärvt ett stort slott – Ditt jobb är att förvandla det till en liten temapark. STR_SCNR :Dammiga greener @@ -4272,7 +4315,7 @@ STR_DTLS :Anställd av en stor nöjesparkskedja, din uppgift är att bygga en STR_SCNR :Gravitationsträdgårdarna STR_PARK :Gravitationsträdgårdarna -STR_DTLS :Din utmaning är att bygga en berg- och dalbanepark i Gravitationsträdgårdarna - Inga andra attraktioner, bara berg- och dalbanor! +STR_DTLS :Din utmaning är att bygga en berg- och dalbanepark i Gravitationsträdgårdarna – Inga andra attraktioner, bara berg- och dalbanor! STR_SCNR :Infernaliska utsikter @@ -4297,7 +4340,7 @@ STR_DTLS :Utmana dig själv genom att leda och förbättra den här Six Flags STR_SCNR :Six Flags Great Adventure STR_PARK :Six Flags Great Adventure -STR_DTLS :Bygg de Six Flags attraktionerna som saknas eller skapa dina egna för att förbättra parken! Men glöm inte bort ditt syfte - Att locka fler besökare till parken +STR_DTLS :Bygg de Six Flags attraktionerna som saknas eller skapa dina egna för att förbättra parken! Men glöm inte bort ditt syfte – Att locka fler besökare till parken STR_SCNR :Six Flags Holland @@ -4307,98 +4350,98 @@ STR_DTLS :Utmana dig själv genom att leda och förbättra den här Six Flags STR_SCNR :Six Flags Magic Mountain STR_PARK :Six Flags Magic Mountain -STR_DTLS :Bygg de Six Flags attraktionerna som saknas eller skapa dina egna för att förbättra parken! Men glöm inte bort ditt syfte - Att betala tillbaka dina lån medan du behåller parkens värde uppe! +STR_DTLS :Bygg de Six Flags attraktionerna som saknas eller skapa dina egna för att förbättra parken! Men glöm inte bort ditt syfte – Att betala tillbaka dina lån medan du behåller parkens värde uppe! STR_SCNR :Six Flags över Texas STR_PARK :Six Flags över Texas -STR_DTLS :Bygg de Six Flags attraktionerna som saknas eller skapa dina egna för att förbättra parken! Men glöm inte bort ditt syfte - Att locka fler besökare till parken +STR_DTLS :Bygg de Six Flags attraktionerna som saknas eller skapa dina egna för att förbättra parken! Men glöm inte bort ditt syfte – Att locka fler besökare till parken ############################################################################### ## Wacky Worlds Scenarios ############################################################################### -STR_SCNR :Afrika - Afrikansk diamantgruva +STR_SCNR :Afrika – Afrikansk diamantgruva STR_PARK :Afrikas gruvor STR_DTLS :Du har hittat en diamant efter att ha ärvt en övergiven diamantgruva. Du beslutar dig för att satsa pengarna på en storslaget nöjesfält. -STR_SCNR :Afrika - Oas +STR_SCNR :Afrika – Oas STR_PARK :Hägring och fägring STR_DTLS :Man har upptäckt en ökenoas som skulle passa utmärkt till ett nöjesfält. Transportmöjligheter till oasen existerar redan. -STR_SCNR :Afrika - Victoriafallen +STR_SCNR :Afrika – Victoriafallen STR_PARK :Över kanten STR_DTLS :En nybyggd damm ger tillräckligt med billig vattenkraft för att kunna driva ett nöjesfält. Du måste dock uppnå ett högt parkvärde för att kunna betala tillbaka lånet för dammen. -STR_SCNR :Antarktis - Miljöräddning +STR_SCNR :Antarktis – Miljöräddning STR_PARK :Iskallt äventyr STR_DTLS :En miljöorganisation har bett dig att bygga om ett gammalt, miljöfarligt oljeraffinaderi till en toppmodern turistattraktion. Marken är billig, men låneräntan är hög. Du kan dock sälja de gamla byggnaderna. -STR_SCNR :Asien - Turistsatsning vid Kinesiska muren +STR_SCNR :Asien – Turistsatsning vid Kinesiska muren STR_PARK :Kinesiska muren STR_DTLS :Myndigheterna har beslutat att stödja turismen kring Kinesiska muren genom att bygga ett nöjesfält i dess närhet. Pengar är inget hinder! -STR_SCNR :Asien - Japansk kustremsa +STR_SCNR :Asien – Japansk kustremsa STR_PARK :Okinawa-kusten STR_DTLS :Ett nöjesfält behöver mer plats. Din enda möjlighet är att ta ett lån och bygga ut mot havet. Höjdrestriktioner råder på grund av jordbävningsfaran. -STR_SCNR :Asien - Maharadjans palats +STR_SCNR :Asien – Maharadjans palats STR_PARK :Maharadjaparken STR_DTLS :Du har fått i uppdrag av maharadjan att underhålla lokalbefolkningen. Inhämta inspiration från maharadjans palats när du bygger nöjesfältet. -STR_SCNR :Australasien - Ayers Rock +STR_SCNR :Australasien – Ayers Rock STR_PARK :Ayers äventyr STR_DTLS :Du hjälper urbefolkningen att bygga ett nöjesfält som en del av ett kulturprogram. Du måste locka tillräckligt med besökare för att kunna sprida urbefolkningens kultur. -STR_SCNR :Australasien - Fun at the Beach +STR_SCNR :Australasien – Fun at the Beach STR_PARK :Strandhugg STR_DTLS :En lokal affärsmans vattenpark har gått i konkurs. Du måste göra ditt eget, mindre nöjesfält tillräckligt lönsamt för att kunna köpa den nya parken och expandera. -STR_SCNR :Europa - Europeisk kulturfestival +STR_SCNR :Europa – Europeisk kulturfestival STR_PARK :Europeisk festival STR_DTLS :Du har blivit erbjuden att ta över en europeisk turistattraktion, men måste öka besöksfrekvensen för att kunna betala tillbaka ett EU-lån innan det europeiska parlamentet tar sommarlov. -STR_SCNR :Europa - Renovering +STR_SCNR :Europa – Renovering STR_PARK :Ur askan STR_DTLS :Ett gammalt nöjesfält har förfallit. Du har beviljats ett EU-lån för att kunna återställa det nedgångna området till sin forna prakt. Du måste renovera nöjesfältet och betala tillbaka lånet. -STR_SCNR :Nordamerika - Extrem Hawaii-ö +STR_SCNR :Nordamerika – Extrem Hawaii-ö STR_PARK :Wacky Waikiki STR_DTLS :Folket på Hawaii har tröttnat på att surfa och vill göra något mer spännande. Du måste bygga ett nöjesfält för att hålla dem på gott humör och bibehålla områdets attraktionskraft. -STR_SCNR :Nordamerika - Grand Canyon +STR_SCNR :Nordamerika – Grand Canyon STR_PARK :Trubbel i Canyon STR_DTLS :Du måste bygga ett nöjesfält på ett begränsat område på båda sidor om detta naturfenomen. Du har möjlighet att köpa upp angränsande land från urbefolkningen. Målet med bygget är att bibehålla den lokala befolkningssiffran. -STR_SCNR :Nordamerika - Rollercoaster Heaven +STR_SCNR :Nordamerika – Rollercoaster Heaven STR_PARK :Rollercoaster Heaven STR_DTLS :Du är en framgångsrik affärsman som under ett sabbatsår beslutar dig för att förvandla en stadspark till ett Rollercoaster Heaven oavsett kostnad! -STR_SCNR :Sydamerika - Förlorad inka-stad +STR_SCNR :Sydamerika – Förlorad inka-stad STR_PARK :Förlorad stadsgrundare STR_DTLS :Du måste öka de lokala turistintäkterna genom att bygga ett nöjesfält som passar omgivningen och håller sig inom höjdrestriktionerna. -STR_SCNR :Sydamerika - Regnskogsplatå +STR_SCNR :Sydamerika – Regnskogsplatå STR_PARK :Kul i regnskog STR_DTLS :Utrymmet är begränsat i den utrotningshotade regnskogen. Du måste få plats med så mycket som möjligt i gläntan för att kunna erbjuda ett alternativ till den lokala timmerindustrin. -STR_SCNR :Sydamerika - Rio-karneval +STR_SCNR :Sydamerika – Rio-karneval STR_PARK :Sockersöta stränder STR_DTLS :Du driver ett litet nöjesfält nära Rio, men banken kräver plötslig betalning av ditt lån. Du måste öka dina intäkter snabbt för att kunna klara den uppkomna krisen. @@ -4406,72 +4449,72 @@ STR_DTLS :Du driver ett litet nöjesfält nära Rio, men banken kräver plöt ## Time Twister Scenarios ############################################################################### -STR_SCNR :Medeltiden - Slott +STR_SCNR :Medeltiden – Slott STR_PARK :Slott på klipporna STR_DTLS :Medlemmarna i den lokala medeltidsföreningen tar sin hobby på allvar. De har bett dig konstruera en nöjespark med medeltidstema på det gamla slottets ägor. -STR_SCNR :Medeltiden - Robin Hood +STR_SCNR :Medeltiden – Robin Hood STR_PARK :Sherwoodskogen STR_DTLS :För att kunna stjäla mer från de rika och ge till de fattiga har du och dina män bestämt er för att bygga ett nöjesfält i Sherwoodskogen. -STR_SCNR :Framtid - Första kontakt +STR_SCNR :Framtid – Första kontakt STR_PARK :Utomjordisk extravagans STR_DTLS :Liv har påträffats på en främmande planet. Bygg en park med utomjordiskt tema för att tjäna pengar på folks nyväckta intresse. -STR_SCNR :Framtid - Framtidsvärld +STR_SCNR :Framtid – Framtidsvärld STR_PARK :Gemini City STR_DTLS :Realisera din innovativa och utopiska vision av framtiden. Bygg en futuristisk park med de allra senaste attraktionerna. -STR_SCNR :Mytologisk tid - Animatronisk filminspelning +STR_SCNR :Mytologisk tid – Animatronisk filminspelning STR_PARK :Animatroniska äventyr STR_DTLS :Du har fått i uppgift att driva och förbättra en existerande park som ligger på en gammal filminspelningsplats. Bygg en hyllning till filmhistoriens animationspionjärer som först gav liv åt förhistoriska vidunder på vita duken. -STR_SCNR :Mytologisk tid - Civilisationens vagga +STR_SCNR :Mytologisk tid – Civilisationens vagga STR_PARK :Mytologiskt arv STR_DTLS :Du äger en ö av högt arkeologiskt värde. Du har bestämt dig för att finansiera dess bevarande genom att konstruera ett nöjesfält med ett tema baserat på områdets rika mytologiska arv. -STR_SCNR :Förhistorisk tid - Efter asteroiden +STR_SCNR :Förhistorisk tid – Efter asteroiden STR_PARK :Kraterkatastrof STR_DTLS :Du äger en dammig gammal meteorkrater. Som den entreprenör du är bestämmer du dig för att bygga ett nöjesfält med asteroidtema och omvandla den till synes värdelösa marken till en förmögenhet. -STR_SCNR :Förhistorisk tid - Jurasafari +STR_SCNR :Förhistorisk tid – Dinosauriesafari STR_PARK :Bergosaurier STR_DTLS :Du har fått i uppgift att bygga en park med Juratema. För att besökarna ska kunna uppleva den exotiska floran och faunan ordentligt krävs det att du bygger attraktioner som går över och ned i dalen. -STR_SCNR :Förhistorisk tid - Stenålder +STR_SCNR :Förhistorisk tid – Stenålder STR_PARK :Stenigt värre STR_DTLS :Stoppa motorvägsbygget och bevara de urtida stencirklarna genom att bygga ett nöjesfält med stenålderstema som går med vinst. Det kan dock bli en smula problematiskt att locka besökare eftersom terrängen är aningen oländig. -STR_SCNR :Det glada tjugotalet - Fängelseön +STR_SCNR :Det glada tjugotalet – Fängelseön STR_PARK :Alcatraz STR_DTLS :Den ökända fängelseön, vars befolkning en gång i tiden utgjordes av smugglare och gangsters, är till salu. Du har bestämt dig för att förvandla den till en förstklassig turistattraktion. Pengar är inget hinder -STR_SCNR :Det glada tjugotalet - Schneidertrofén +STR_SCNR :Det glada tjugotalet – Schneidertrofén STR_PARK :Schneiders strand STR_DTLS :75-årsjubiléet av din faders vinst av Schneidertrofén inträffar om några år. Du vill hedra hans prestation genom att bygga en park baserad på den berömda sjöflygplanstävlingen. -STR_SCNR :Det glada tjugotalet - Skyskrapor +STR_SCNR :Det glada tjugotalet – Skyskrapor STR_PARK :Metropolis STR_DTLS :Du äger en tom tomt i närheten av stadens lågt byggda kvarter. För att få ut så mycket vinst som möjligt av marken bygger du ett nöjesfält i form av en skyskrapa inspirerad av tjugotalets art déco-arkitektur. -STR_SCNR :Rock'n'roll - Flower power +STR_SCNR :Rock'n'roll – Flower power STR_PARK :Woodstock STR_DTLS :En stor årlig rockfestival äger rum på din mark. Bygg en hipp nöjespark i den fritänkande publikens smak. -STR_SCNR :Rock'n'roll - Rock'n'roll +STR_SCNR :Rock'n'roll – Rock'n'roll STR_PARK :Rock'n'roll-revival STR_DTLS :Denna åldrande park har sett bättre dagar. Hjälp ägaren med att ge den ett retro-rock'n'roll-tema för att förvandla den till en framgångsrik park. diff --git a/data/language/zh-CN.txt b/data/language/zh-CN.txt index ad89e03fbb..6edcd3977c 100644 --- a/data/language/zh-CN.txt +++ b/data/language/zh-CN.txt @@ -12,7 +12,7 @@ STR_0007 :迷你火车 STR_0008 :单轨电车 STR_0009 :迷你悬挂过山车 STR_0010 :游船出租 -STR_0011 :木制疯狂老鼠过山车 +STR_0011 :木制野生鼠过山车 STR_0012 :赛马过山车 STR_0013 :轨道小车 STR_0014 :喷射-自由落体 @@ -100,30 +100,30 @@ STR_0091 :Unknown Ride (59) # LIM: Linear Induction Motors STR_0092 :LIM喷射过山车 STR_0512 :一种紧凑的过山车,有着螺旋状的爬升和平滑盘旋的下坠 -STR_0513 :A looping roller coaster where the riders ride in a standing position -STR_0514 :Trains suspended beneath the roller coaster track swing out to the side around corners -STR_0515 :A steel roller coaster with trains that are held beneath the track, with many complex and twisting track elements -STR_0516 :A gentle roller coaster for people who haven't yet got the courage to face the larger rides -STR_0517 :Passengers ride in miniature trains along a narrow-gauge railway track -STR_0518 :Passengers travel in electric trains along a monorail track -STR_0519 :Passengers ride in small cars hanging beneath the single-rail track, swinging freely from side to side around corners -STR_0520 :A dock platform where guests can drive/row personal watercraft on a body of water -STR_0521 :A fast and twisting roller coaster with tight turns and steep drops. Intensity is bound to be high. -STR_0522 :A smaller roller coaster where the riders sit above the track with no car around them -STR_0523 :Riders travel slowly in powered vehicles along a track-based route -STR_0524 :Freefall car is pneumatically launched up a tall steel tower and then allowed to freefall down -STR_0525 :Riders career down a twisting track guided only by the curvature and banking of the semi-circular track -STR_0526 :Passengers travel in a rotating observation cabin which travels up a tall tower -STR_0527 :A smooth steel-tracked roller coaster capable of vertical loops -STR_0528 :Riders travel in inflatable dinghies down a twisting semi-circular or completely enclosed tube track -STR_0529 :Mine train themed roller coaster trains career along steel roller coaster track made to look like old railway track -STR_0530 :Cars hang from a steel cable which runs continuously from one end of the ride to the other and back again -STR_0531 :A compact steel-tracked roller coaster where the train travels through corkscrews and loops +STR_0513 :乘客采用站立方式乘坐的过山车,全身都能感受到翻滚时血液在体内的涌动 +STR_0514 :一种转弯的时候会自行产生离心运动,车厢可自由摆动的过山车 +STR_0515 :一种车厢在轨道下方的铁制的过山车, 同时附有复杂且扭曲的轨道元素 +STR_0516 :一种在各方面都比较缓和的过山车,适合不擅长乘着过山车的乘坐 +STR_0517 :乘客乘坐小型列车沿着一条窄轨道前进 +STR_0518 :乘客乘坐电车沿着一条单轨铁道 +STR_0519 :乘客乘坐悬挂在单轨轨道下方的车厢,在拐角处从一侧到另一侧自由摆动 +STR_0520 :一个码头,允许游客在水上驾驶/划船 +STR_0521 :快速且扭曲的过山车,紧凑地转弯,陡峭地下落造就了极高的刺激度。 +STR_0522 :一款较小型的过山车, 乘客坐在轨道上而并没有其他车包围他们 +STR_0523 :乘客会坐在有动力的车辆内, 慢慢地依照轨道进行观光游览 +STR_0524 :自由落体的车辆会被气体喷射到高塔顶, 并随后自由落下 +STR_0525 :乘客飞驰在扭曲的轨道上, 只靠弯道及半月型的轨道引领他们 +STR_0526 :乘客会在不断旋转并上升到最顶的座舱内观看乐园景色 +STR_0527 :一款以顺滑轨道为特征的过山车, 并附有垂直回环可供使用 +STR_0528 :乘客坐在充气小艇中于半月型或完全封闭的轨道上滑翔 +STR_0529 :以采矿列车为主题的过山车沿着铁架轨道行驶, 令它很像在古老的轨道行驶上 +STR_0530 :列车悬挂在钢缆下并不断于车站与车站之间游走 +STR_0531 :一款列车会穿过螺旋及回环的较小型过山车 STR_0532 : STR_0533 : -STR_0534 :Self-drive petrol-engined go karts -STR_0535 :Log-shaped boats travel along a water channel, splashing down steep slopes to soak the riders -STR_0536 :Circular boats meander along a wide water channel, splashing through waterfalls and thrilling riders through foaming rapids +STR_0534 :自驾式, 并以气油驱动的小型赛车(卡丁车) +STR_0535 :以运木为形状的小船穿梭于有水的轨道, 并会于下滑时溅湿游客 +STR_0536 :圆形的小船穿梭于比较广阔的水道, 于水道中被瀑布溅湿, 并有激流使乘客的紧张感大增 STR_0537 : STR_0538 : STR_0539 : @@ -138,50 +138,50 @@ STR_0550 : STR_0551 : STR_0552 : STR_0553 : -STR_0554 :The car is accelerated out of the station along a long level track using Linear Induction Motors, then heads straight up a vertical spike of track, freefalling back down to return to the station -STR_0555 :Guests ride in an elevator up or down a vertical tower to get from one level to another -STR_0556 :Extra-wide cars descend completely vertical sloped track for the ultimate freefall roller coaster experience +STR_0554 :列车会被又长又直的轨道上的直线发动机加速, 然后衝上垂直轨道, 自由落体后回到车站 +STR_0555 :游客乘搭升降机上下穿梭于垂直的管道中, 并借此访问另一层 +STR_0556 :加阔的列车于垂直下坡上完全滑落, 给予人终极的过山车自由落体体验 STR_0557 : STR_0558 : STR_0559 : STR_0560 : STR_0561 : -STR_0562 :Powered cars travel along a multi-level track past spooky scenery and special effects -STR_0563 :Sitting in comfortable trains with only simple lap restraints riders enjoy giant smooth drops and twisting track as well as plenty of 'air time' over the hills -STR_0564 :Running on wooden track, this coaster is fast, rough, noisy, and gives an 'out of control' riding experience with plenty of 'air time' -STR_0565 :A simple wooden roller coaster capable of only gentle slopes and turns, where the cars are only kept on the track by side friction wheels and gravity -STR_0566 :Individual roller coaster cars zip around a tight zig-zag layout of track with sharp corners and short sharp drops -STR_0567 :Sitting in seats suspended either side of the track, riders are pitched head-over-heels while they plunge down steep drops and travel through various inversions -STR_0569 :Riding in special harnesses below the track, riders experience the feeling of flight as they swoop through the air -STR_0571 :Circular cars spin around as they travel along the zig-zagging wooden track -STR_0572 :Large capacity boats travel along a wide water channel, propelled up slopes by a conveyer belt, accelerating down steep slopes to soak the riders with a gaint splash -STR_0573 :Powered helicoper shaped cars running on a steel track, controlled by the pedalling of the riders -STR_0574 :Riders are held in special harnesses in a lying-down position, travlling through twisted track and inversions either on their backs or facing the ground -STR_0575 :Powered trains hanging from a single rail transport people around the park -STR_0577 :Bogied cars run on wooden tracks, turning around on special reversing sections -STR_0578 :Cars run along track enclosed by circular hoops, traversing steep drops and heartline twists +STR_0562 :有动力的车辆将会穿梭于有著恐怖景物及特效的多层轨道中 +STR_0563 :乘客坐在舒适并只有简单安全装备的列车上享受巨大而顺滑的下滑, 扭曲的轨道和充足的'空中'时间 +STR_0564 :一款在木製轨道运行的过山车, 这个过山车会嘈吵地飞驰过高低不平的轨道, 给乘客充足的'空中时间'之馀,同时会给人一种'失去控制'的乘坐体验 +STR_0565 :一款只能使用普通下坡及弯道的简单木製过山车, 车辆只靠产生轨道阻力的车轮及重力滑翔于轨道上 +STR_0566 :每辆单独的过山车都会游走在附有急弯及急短下坡的Z型轨道上 +STR_0567 :乘客坐在悬挂在轨道的不同位置下, 衝下陡下坡及各种倒转轨道时, 会被翻腾得四脚朝天 +STR_0569 :乘客将会乘坐在轨道下的特别繫带, 在空中盘旋时体验飞一般的感受 +STR_0571 :圆型的车辆于他们行走在Z型轨道上被不停的转动 +STR_0572 :载客量大的小船游走于宽敞的水道上, 由输送带送他们上坡, 并于下坡中加速, 务求溅出巨型的水花令到乘客全身湿透 +STR_0573 :有动力的直升机型车辅于钢轨上游走, 他们由乘客的脚踏所控制 +STR_0574 :乘客躺卧在特别繫带中, 以他们面向的背或头游走于曲折的轨道及翻转 +STR_0575 :有动力的列车悬挂在单轨下运送乘客到乐园内的不同地方 +STR_0577 :拥有转向架的车辅于木製轨道上, 被特别的倒转装置转向 +STR_0578 :车辆运行于O型的轨道中, 并会穿过陡下坡及横滚轨道 STR_0579 : -STR_0580 :A giant steel roller coaster capable of smooth drops and hills of over 300ft -STR_0581 :A ring of seats is pulled to the top of a tall tower while gently rotating, then allowed to free-fall down, stopping gently at the bottom using magnetic brakes +STR_0580 :一款可建造顺滑下坡及高达300英尺坡道的巨型铁製过山车 +STR_0581 :围绕著塔的座位会在不断慢慢旋转中被拉至塔顶, 然后便自由落体, 再被有磁力的煞车装置慢慢煞停于站台 STR_0582 : STR_0583 : -STR_0584 :Special bicycles run on a steel monorail track, propelled by the pedalling of the riders -STR_0585 :Riders sit in pairs of seats suspended beneath the track as they loop and twist through tight inversions -STR_0586 :Boat shaped cars run on roller coaster track to allow twisting curves and steep drops, splashing down into sections of water for gentle river sections -STR_0587 :After an exhilarating air-powered launch, the train speeds up a vertical track, over the top, and vertically down the other side to return to the station -STR_0588 :Individual cars run beneath a zig-zagging track with hairpin turns and sharp drops +STR_0584 :运行在铁製单轨上的特别单车, 像一般单车一样, 会被乘客踏行 +STR_0585 :乘客坐在轨道悬挂下的一对对的座位经历被紧密的轨道上旋转及迴转 +STR_0586 :船型的车辆行驶于过山车的轨道上, 允许他们能够拥有扭曲式的弯道陡下坡等轨道配置, 更会被普通的水道所溅湿 +STR_0587 :过山车列车被令人胆跳心惊的气动发车加速衝上垂直轨道后, 穿过最顶, 然后垂直下落到另一边回到车站 +STR_0588 :每辆单独的过山车游走在附有髮夹弯及急下坡的Z型轨道上 STR_0589 : -STR_0590 :Riders ride in a submerged submarine through an underwater course -STR_0591 :Raft-shaped boats gently meander around a river track +STR_0590 :乘客在潜艇中观赏水底的景观 +STR_0591 :木筏造型船于河道上慢慢地游览著 STR_0593 : -STR_0598 :Inverted roller coaster trains are accelerated out of the station to travel up a vertical spike of track, then reverse back through the station to travel backwards up another vertical spike of track -STR_0599 :A compact roller coaster with individual cars and smooth twisting drops -STR_0600 :Powered mine trains career along a smooth and twisted track layout -STR_0602 :Roller coaster trains are accelerated out of the station by linear induction motors to speed through twisting inversions +STR_0598 :反转式过山车加速离开车站, 穿过到垂直轨道的尖端, 然后倒转返回到车站, 再穿越到另一边垂直轨道的尖端 +STR_0599 :一款拥有独立车辆及顺滑又弯曲的下坡的紧凑式过山车 +STR_0600 :有动车的採矿列车游走在顺滑但又曲折的轨道配置中 +STR_0602 :过山车列车由直线发动机加速离开车站, 穿过盘绕的倒转轨道后回到车站 STR_0767 :游客 {INT32} -STR_0768 :勤杂工 {INT32} -STR_0769 :机械师 {INT32} -STR_0770 :警卫 {INT32} +STR_0768 :清洁工人 {INT32} +STR_0769 :维修人员 {INT32} +STR_0770 :安全警卫 {INT32} STR_0771 :表演人员 {INT32} STR_0777 :未命名游乐园{POP16}{POP16} STR_0778 :指示牌 @@ -314,7 +314,7 @@ STR_0924 :{SMALLFONT}{BLACK}向下螺旋 STR_0925 :{SMALLFONT}{BLACK}向上螺旋 STR_0926 :不能移除此物... STR_0927 :不能在此建造... -STR_0928 :{SMALLFONT}{BLACK}Chain lift, to pull cars up slopes +STR_0928 :{SMALLFONT}{BLACK}提升坡将会把车厢拉上坡 STR_0929 :'S'型弯道 (向左) STR_0930 :'S'型弯道 (向右) STR_0931 :垂直回环 (向左) @@ -391,7 +391,7 @@ STR_1001 :轨道不适合此类型的车 STR_1002 :不能开启 {POP16}{POP16}{POP16}{STRINGID}... STR_1003 :不能测试 {POP16}{POP16}{POP16}{STRINGID}... STR_1004 :不能关闭 {POP16}{POP16}{POP16}{STRINGID}... -STR_1005 :Can't start construction on {POP16}{POP16}{POP16}{STRINGID}... +STR_1005 :不能开始建造 {POP16}{POP16}{POP16}{STRINGID}... STR_1006 :必须先关闭 STR_1007 :不能创建足够的车辆 STR_1008 :{SMALLFONT}{BLACK}开放, 关闭或测试游乐设施/店铺 @@ -408,20 +408,20 @@ STR_1018 :不能做出改变... STR_1019 :不能做出改变... STR_1020 :不能做出改变... STR_1021 :{POP16}{POP16}{POP16}{POP16}{STRINGID} -STR_1022 :{POP16}{POP16}{POP16}{COMMA16} car per train -STR_1023 :{POP16}{POP16}{POP16}{COMMA16} cars per train -STR_1024 :{COMMA16} car per train -STR_1025 :{COMMA16} cars per train +STR_1022 :{POP16}{POP16}{POP16}{COMMA16} 车/列车 +STR_1023 :{POP16}{POP16}{POP16}{COMMA16} 车/列车 +STR_1024 :{COMMA16} 车/列车 +STR_1025 :{COMMA16} 车/列车 STR_1026 :车站站台太长! -STR_1027 :{SMALLFONT}{BLACK}Locate this on Main View +STR_1027 :{SMALLFONT}{BLACK}将主视定位到此 STR_1028 :离开地图边缘! -STR_1029 :Cannot build partly above and partly below water! -STR_1030 :Can only build this underwater! -STR_1031 :Can't build this underwater! -STR_1032 :Can only build this on water! -STR_1033 :Can only build this above ground! -STR_1034 :Can only build this on land! -STR_1035 :Local authority won't allow construction above tree-height! +STR_1029 :不能在半水上半水下地形上建造! +STR_1030 :只能建造在水下! +STR_1031 :不能建造在水下! +STR_1032 :只能建造在水上! +STR_1033 :只能建造在地上! +STR_1034 :只能建造在地面! +STR_1035 :当地权威不允许建筑物高度超过树的高度 STR_1036 :加载游戏 STR_1037 :加载地形 STR_1038 :将存档转换为关卡 @@ -443,493 +443,493 @@ STR_1053 :{SMALLFONT}{BLACK}游乐园内的游乐设施 STR_1054 :{SMALLFONT}{BLACK}命名游乐设施/店铺 STR_1055 :{SMALLFONT}{BLACK}命名游客 STR_1056 :{SMALLFONT}{BLACK}命名职员 -STR_1057 :游乐设施/店铺名称 -STR_1058 :Enter new name for this ride/attraction: -STR_1059 :Can't rename ride/attraction... -STR_1060 :Invalid ride/attraction name -STR_1061 :Normal mode -STR_1062 :Continuous circuit mode -STR_1063 :Reverse-Incline launched shuttle mode -STR_1064 :Powered launch (passing station) -STR_1065 :Shuttle mode -STR_1066 :Boat hire mode -STR_1067 :Upward launch -STR_1068 :Rotating lift mode -STR_1069 :Station to station mode -STR_1070 :Single ride per admission -STR_1071 :Unlimited rides per admission +STR_1057 :游乐设施/店铺命名 +STR_1058 :请输入这个游乐设施/店铺的新名字: +STR_1059 :重命名 设施/店铺 失败... +STR_1060 :无效的设施/店铺名字 +STR_1061 :正常模式 +STR_1062 :连续循环模式 +STR_1063 :反向倾斜循环发车模式 +STR_1064 :动力发车 (通过车站) +STR_1065 :循环模式 +STR_1066 :出租小船模式 +STR_1067 :向上发车 +STR_1068 :旋转上升模式 +STR_1069 :站对站模式 +STR_1070 :每次入场单程 +STR_1071 :每次入场无限次乘坐 STR_1072 :迷宫模式 STR_1073 :赛车模式 -STR_1074 :Bumper-car mode +STR_1074 :碰碰车模式 STR_1075 :摇摆模式 -STR_1076 :Shop stall mode +STR_1076 :商店摆摊模式 STR_1077 :旋转模式 STR_1078 :向前旋转 STR_1079 :向后旋转 STR_1080 :电影: ”飞行员复仇记” STR_1081 :3D电影: ”鼠尾” -STR_1082 :Space rings mode -STR_1083 :Beginners mode -STR_1084 :LIM-powered launch +STR_1082 :太空转轮模式 +STR_1083 :菜鸟模式 +STR_1084 :LIM动力模式 STR_1085 :电影: ”尖叫乘客” STR_1086 :3D电影: ”暴风猎人” STR_1087 :3D电影: ”太空海盗” -STR_1088 :Intense mode -STR_1089 :Berserk mode +STR_1088 :刺激模式 +STR_1089 :惊悚模式 STR_1090 :鬼屋模式 -STR_1091 :Circus show mode -STR_1092 :Downward launch -STR_1093 :Crooked house mode -STR_1094 :Freefall drop mode -STR_1095 :Continuous circuit block sectioned mode -STR_1096 :Powered launch (without passing station) -STR_1097 :Powered launch block sectioned mode -STR_1098 :Moving to end of {POP16}{STRINGID} -STR_1099 :Waiting for passengers at {POP16}{STRINGID} -STR_1100 :Waiting to depart {POP16}{STRINGID} -STR_1101 :Departing {POP16}{STRINGID} -STR_1102 :Travelling at {VELOCITY} -STR_1103 :Arriving at {POP16}{STRINGID} -STR_1104 :Unloading passengers at {POP16}{STRINGID} -STR_1105 :Travelling at {VELOCITY} -STR_1106 :Crashing! -STR_1107 :Crashed! -STR_1108 :Travelling at {VELOCITY} -STR_1109 :Swinging -STR_1110 :Rotating -STR_1111 :Rotating -STR_1112 :Operating -STR_1113 :Showing film -STR_1114 :Rotating -STR_1115 :Operating -STR_1116 :Operating -STR_1117 :Doing circus show -STR_1118 :Operating -STR_1119 :Waiting for cable lift -STR_1120 :Travelling at {VELOCITY} -STR_1121 :Stopping -STR_1122 :Waiting for passengers -STR_1123 :Waiting to start -STR_1124 :Starting -STR_1125 :Operating -STR_1126 :Stopping -STR_1127 :Unloading passengers -STR_1128 :Stopped by block brakes -STR_1129 :All vehicles in same colours -STR_1130 :Different colours per {STRINGID} -STR_1131 :Different colours per vehicle -STR_1132 :Vehicle {POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} -STR_1133 :Vehicle {POP16}{COMMA16} +STR_1091 :马戏团表演模式 +STR_1092 :向下发车 +STR_1093 :古怪小屋模式 +STR_1094 :自由落体式下降模式 +STR_1095 :区域刹车连续循环模式 +STR_1096 :动力发车(不通过车站) +STR_1097 :动力发车区域刹车模式 +STR_1098 :移动到 {POP16}{STRINGID} 尾端 +STR_1099 :等待乘客 {POP16}{STRINGID} +STR_1100 :等待离站 {POP16}{STRINGID} +STR_1101 :离站 {POP16}{STRINGID} +STR_1102 :以 {VELOCITY} 行驶 +STR_1103 :到达 {POP16}{STRINGID} +STR_1104 :乘客在 {POP16}{STRINGID} 下车 +STR_1105 :以 {VELOCITY} 行驶 +STR_1106 :毁坏中! +STR_1107 :已毁坏! +STR_1108 :以 {VELOCITY} 行驶 +STR_1109 :摇摆中 +STR_1110 :旋转中 +STR_1111 :旋转中 +STR_1112 :运行中 +STR_1113 :放映中 +STR_1114 :旋转中 +STR_1115 :运行中 +STR_1116 :运行中 +STR_1117 :马戏表演中 +STR_1118 :运行中 +STR_1119 :等待锁链拉升中 +STR_1120 :以 {VELOCITY} 行驶 +STR_1121 :停止中 +STR_1122 :等待乘客 +STR_1123 :等待开始 +STR_1124 :开始中 +STR_1125 :运行中 +STR_1126 :停止中 +STR_1127 :卸客中 +STR_1128 :被区域刹车刹停 +STR_1129 :全部车辆使用相同颜色 +STR_1130 :不同{STRINGID}使用不同颜色 +STR_1131 :不同车辆使用不同颜色 +STR_1132 :车辆 {POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} +STR_1133 :车辆 {POP16}{COMMA16} STR_1134 :{POP16}{POP16}{POP16}{POP16}{POP16}{STRINGID} {COMMA16} STR_1135 :{STRINGID} {COMMA16} -STR_1136 :{SMALLFONT}{BLACK}Select main colour -STR_1137 :{SMALLFONT}{BLACK}Select additional colour 1 -STR_1138 :{SMALLFONT}{BLACK}Select additional colour 2 -STR_1139 :{SMALLFONT}{BLACK}Select support structure colour -STR_1140 :{SMALLFONT}{BLACK}Select vehicle colour scheme option -STR_1141 :{SMALLFONT}{BLACK}Select which vehicle/train to modify +STR_1136 :{SMALLFONT}{BLACK}选择主要配色 +STR_1137 :{SMALLFONT}{BLACK}选择额外配色 1 +STR_1138 :{SMALLFONT}{BLACK}选择额外配色 2 +STR_1139 :{SMALLFONT}{BLACK}选择支撑框架配色 +STR_1140 :{SMALLFONT}{BLACK}选择车辆配色主题 +STR_1141 :{SMALLFONT}{BLACK}选择需要魔改的车辆/列车 STR_1142 :{MOVE_X}{SMALLFONT}{STRINGID} STR_1143 :»{MOVE_X}{SMALLFONT}{STRINGID} -STR_1144 :Can't build/move entrance for this ride/attraction... -STR_1145 :Can't build/move exit for this ride/attraction... -STR_1146 :Entrance not yet built -STR_1147 :Exit not yet built -STR_1148 :Quarter load -STR_1149 :Half load -STR_1150 :Three-quarter load -STR_1151 :Full load -STR_1152 :Any load -STR_1153 :Height Marks on Ride Tracks设施轨道高度标记 -STR_1154 :Height Marks on Land土地高度标记 -STR_1155 :Height Marks on Paths道路高度标记 +STR_1144 :不能建造/移动这个游乐设施的入口... +STR_1145 :不能建造/移动这个游乐设施的出口... +STR_1146 :尚未建造入口 +STR_1147 :尚未建造出口 +STR_1148 :四分之一载客量 +STR_1149 :二分之一载客量 +STR_1150 :四分之三载客量 +STR_1151 :满载 +STR_1152 :随意载客 +STR_1153 :设施轨道高度标记 +STR_1154 :土地高度标记 +STR_1155 :道路高度标记 STR_1156 :{MOVE_X}{SMALLFONT}{STRINGID} STR_1157 :✓{MOVE_X}{SMALLFONT}{STRINGID} -STR_1158 :Can't remove this... -STR_1159 :{SMALLFONT}{BLACK}Place scenery, gardens, and other accessories -STR_1160 :{SMALLFONT}{BLACK}Create/adjust lakes & water -STR_1161 :Can't position this here... +STR_1158 :不能移除此物... +STR_1159 :{SMALLFONT}{BLACK}放置景观,花园和其他美化物 +STR_1160 :{SMALLFONT}{BLACK}创建/调整湖泊和水塘 +STR_1161 :不能放置于此... STR_1162 :{OUTLINE}{TOPAZ}{STRINGID} -STR_1163 :{STRINGID}{NEWLINE}(Right-Click to Modify) -STR_1164 :{STRINGID}{NEWLINE}(Right-Click to Remove) +STR_1163 :{STRINGID}{NEWLINE}(按右键修改) +STR_1164 :{STRINGID}{NEWLINE}(按右键移除) STR_1165 :{STRINGID} - {STRINGID} {COMMA16} -STR_1166 :Can't lower water level here... -STR_1167 :Can't raise water level here... +STR_1166 :无法降低此处水位... +STR_1167 :无法升高此处水位... STR_1168 :选项 STR_1169 :(无) STR_1170 :{STRING} STR_1171 :{RED}已关闭 - - STR_1172 :{YELLOW}{STRINGID} - - -STR_1173 :{SMALLFONT}{BLACK}Build footpaths and queue lines -STR_1174 :Banner sign in the way -STR_1175 :Can't build this on sloped footpath -STR_1176 :Can't build footpath here... -STR_1177 :Can't remove footpath from here... -STR_1178 :Land slope unsuitable -STR_1179 :Footpath in the way -STR_1180 :Can't build this underwater! -STR_1181 :Footpaths -STR_1182 :Type -STR_1183 :Direction -STR_1184 :Slope -STR_1185 :{SMALLFONT}{BLACK}Direction -STR_1186 :{SMALLFONT}{BLACK}Slope down -STR_1187 :{SMALLFONT}{BLACK}Level -STR_1188 :{SMALLFONT}{BLACK}Slope up -STR_1189 :{SMALLFONT}{BLACK}Construct the selected footpath section -STR_1190 :{SMALLFONT}{BLACK}Remove previous footpath section +STR_1173 :{SMALLFONT}{BLACK}建造道路和队列线 +STR_1174 :横幅挡到了 +STR_1175 :不能在倾斜的小径上建造这个 +STR_1176 :不能在此建造道路... +STR_1177 :不能移除此处道路... +STR_1178 :坡道坡度不合适 +STR_1179 :道路挡到了 +STR_1180 :不能建造在水下! +STR_1181 :道路 +STR_1182 :类型 +STR_1183 :方向 +STR_1184 :坡度 +STR_1185 :{SMALLFONT}{BLACK}方向 +STR_1186 :{SMALLFONT}{BLACK}下坡 +STR_1187 :{SMALLFONT}{BLACK}水平 +STR_1188 :{SMALLFONT}{BLACK}上坡 +STR_1189 :{SMALLFONT}{BLACK}建造选中的道路 +STR_1190 :{SMALLFONT}{BLACK}移除上一段道路 STR_1191 :{BLACK}{STRINGID} STR_1192 :{OUTLINE}{RED}{STRINGID} STR_1193 :{WINDOW_COLOUR_2}{STRINGID} -STR_1194 :Closed -STR_1195 :Test Run -STR_1196 :Open -STR_1197 :Broken Down -STR_1198 :Crashed! -STR_1199 :{COMMA16} person on ride -STR_1200 :{COMMA16} people on ride -STR_1201 :Nobody in queue line -STR_1202 :1 person in queue line -STR_1203 :{COMMA16} people in queue line -STR_1204 :{COMMA16} minute queue time -STR_1205 :{COMMA16} minutes queue time -STR_1206 :{WINDOW_COLOUR_2}Wait for: -STR_1207 :{WINDOW_COLOUR_2}Leave if another train arrives at station -STR_1208 :{WINDOW_COLOUR_2}Leave if another boat arrives at station -STR_1209 :{SMALLFONT}{BLACK}Select whether should wait for passengers before departing -STR_1210 :{SMALLFONT}{BLACK}Select whether should leave if another vehicle arrives at the same station -STR_1211 :{WINDOW_COLOUR_2}Minimum waiting time: -STR_1212 :{WINDOW_COLOUR_2}Maximum waiting time: -STR_1213 :{SMALLFONT}{BLACK}Select minimum length of time to wait before departing -STR_1214 :{SMALLFONT}{BLACK}Select maximum length of time to wait before departing -STR_1215 :{WINDOW_COLOUR_2}Synchronise with adjacent stations -STR_1216 :{SMALLFONT}{BLACK}Select whether to synchronise departure with all adjacent stations (for 'racing') -STR_1217 :{COMMA16} seconds +STR_1194 :已关闭 +STR_1195 :测试运行 +STR_1196 :开启 +STR_1197 :故障了 +STR_1198 :已毁坏! +STR_1199 :{COMMA16}名游客在游乐设施上 +STR_1200 :{COMMA16}名游客在游乐设施上 +STR_1201 :无人在排队 +STR_1202 :1人在排队 +STR_1203 :{COMMA16}人在排队 +STR_1204 :{COMMA16}分钟等候时间 +STR_1205 :{COMMA16}分钟等候时间 +STR_1206 :{WINDOW_COLOUR_2}等待: +STR_1207 :{WINDOW_COLOUR_2}当其他列车进站后就发车 +STR_1208 :{WINDOW_COLOUR_2}当其他船到站后就发车 +STR_1209 :{SMALLFONT}{BLACK}选择发车前是否等待乘客 +STR_1210 :{SMALLFONT}{BLACK}选择当其他车辆入站后,是否发车 +STR_1211 :{WINDOW_COLOUR_2}最短等待时间: +STR_1212 :{WINDOW_COLOUR_2}最长等待时间: +STR_1213 :{SMALLFONT}{BLACK}选择发车前最短等待时长 +STR_1214 :{SMALLFONT}{BLACK}选择发车前最长等待时长 +STR_1215 :{WINDOW_COLOUR_2}与相邻站同步 +STR_1216 :{SMALLFONT}{BLACK}选择是否与所有相邻车站同步发车(为了“赛车”) +STR_1217 :{COMMA16} 秒 STR_1218 :{BLACK}➕ STR_1219 :{BLACK}➖ -STR_1220 :Exit only -STR_1221 :No entrance -STR_1222 :No exit -STR_1223 :{SMALLFONT}{BLACK}Transport rides -STR_1224 :{SMALLFONT}{BLACK}Gentle rides -STR_1225 :{SMALLFONT}{BLACK}Roller coasters -STR_1226 :{SMALLFONT}{BLACK}Thrill rides -STR_1227 :{SMALLFONT}{BLACK}Water rides -STR_1228 :{SMALLFONT}{BLACK}Shops & stalls -STR_1229 :train -STR_1230 :trains -STR_1231 :Train -STR_1232 :Trains -STR_1233 :{COMMA16} train -STR_1234 :{COMMA16} trains -STR_1235 :Train {COMMA16} -STR_1236 :boat -STR_1237 :boats -STR_1238 :Boat -STR_1239 :Boats -STR_1240 :{COMMA16} boat -STR_1241 :{COMMA16} boats -STR_1242 :Boat {COMMA16} -STR_1243 :track -STR_1244 :tracks -STR_1245 :Track -STR_1246 :Tracks -STR_1247 :{COMMA16} track -STR_1248 :{COMMA16} tracks -STR_1249 :Track {COMMA16} -STR_1250 :docking platform -STR_1251 :docking platforms -STR_1252 :Docking platform -STR_1253 :Docking platforms -STR_1254 :{COMMA16} docking platform -STR_1255 :{COMMA16} docking platforms -STR_1256 :Docking platform {COMMA16} -STR_1257 :station -STR_1258 :stations -STR_1259 :Station -STR_1260 :Stations -STR_1261 :{COMMA16} station -STR_1262 :{COMMA16} stations -STR_1263 :Station {COMMA16} -STR_1264 :car -STR_1265 :cars -STR_1266 :Car -STR_1267 :Cars -STR_1268 :{COMMA16} car -STR_1269 :{COMMA16} cars -STR_1270 :Car {COMMA16} -STR_1271 :building -STR_1272 :buildings -STR_1273 :Building -STR_1274 :Buildings -STR_1275 :{COMMA16} building -STR_1276 :{COMMA16} buildings -STR_1277 :Building {COMMA16} -STR_1278 :structure -STR_1279 :structures -STR_1280 :Structure -STR_1281 :Structures -STR_1282 :{COMMA16} structure -STR_1283 :{COMMA16} structures -STR_1284 :Structure {COMMA16} -STR_1285 :ship -STR_1286 :ships -STR_1287 :Ship -STR_1288 :Ships -STR_1289 :{COMMA16} ship -STR_1290 :{COMMA16} ships -STR_1291 :Ship {COMMA16} -STR_1292 :cabin -STR_1293 :cabins -STR_1294 :Cabin -STR_1295 :Cabins -STR_1296 :{COMMA16} cabin -STR_1297 :{COMMA16} cabins -STR_1298 :Cabin {COMMA16} -STR_1299 :wheel -STR_1300 :wheels -STR_1301 :Wheel -STR_1302 :Wheels -STR_1303 :{COMMA16} wheel -STR_1304 :{COMMA16} wheels -STR_1305 :Wheel {COMMA16} -STR_1306 :ring -STR_1307 :rings -STR_1308 :Ring -STR_1309 :Rings -STR_1310 :{COMMA16} ring -STR_1311 :{COMMA16} rings -STR_1312 :Ring {COMMA16} -STR_1313 :player -STR_1314 :players -STR_1315 :Player -STR_1316 :Players -STR_1317 :{COMMA16} player -STR_1318 :{COMMA16} players -STR_1319 :Player {COMMA16} -STR_1320 :course -STR_1321 :courses -STR_1322 :Course -STR_1323 :Courses -STR_1324 :{COMMA16} course -STR_1325 :{COMMA16} courses -STR_1326 :Course {COMMA16} -STR_1327 :{SMALLFONT}{BLACK}Rotate objects by 90° -STR_1328 :Level land required -STR_1329 :{WINDOW_COLOUR_2}Launch speed: -STR_1330 :{SMALLFONT}{BLACK}Maximum speed when leaving station +STR_1220 :出口 +STR_1221 :没有入口 +STR_1222 :没有出口 +STR_1223 :{SMALLFONT}{BLACK}运输类游乐设施 +STR_1224 :{SMALLFONT}{BLACK}温和类游乐设施 +STR_1225 :{SMALLFONT}{BLACK}过山车 +STR_1226 :{SMALLFONT}{BLACK}惊险游乐设施 +STR_1227 :{SMALLFONT}{BLACK}水上游乐设施 +STR_1228 :{SMALLFONT}{BLACK}商店及摊贩 +STR_1229 :列车 +STR_1230 :列车 +STR_1231 :列车 +STR_1232 :列车 +STR_1233 :{COMMA16} 列车 +STR_1234 :{COMMA16} 列车 +STR_1235 :列车 {COMMA16} +STR_1236 :小船 +STR_1237 :小船 +STR_1238 :小船 +STR_1239 :小船 +STR_1240 :{COMMA16} 小船 +STR_1241 :{COMMA16} 小船 +STR_1242 :船 {COMMA16} +STR_1243 :轨道 +STR_1244 :轨道 +STR_1245 :轨道 +STR_1246 :轨道 +STR_1247 :{COMMA16} 轨道 +STR_1248 :{COMMA16} 轨道 +STR_1249 :轨道 {COMMA16} +STR_1250 :码头 +STR_1251 :码头 +STR_1252 :码头 +STR_1253 :码头 +STR_1254 :{COMMA16} 码头 +STR_1255 :{COMMA16} 码头 +STR_1256 :码头 {COMMA16} +STR_1257 :车站 +STR_1258 :车站 +STR_1259 :车站 +STR_1260 :车站 +STR_1261 :{COMMA16} 车站 +STR_1262 :{COMMA16} 车站 +STR_1263 :车站 {COMMA16} +STR_1264 :车辆 +STR_1265 :车辆 +STR_1266 :车辆 +STR_1267 :车辆 +STR_1268 :{COMMA16} 车辆 +STR_1269 :{COMMA16} 车辆 +STR_1270 :车辆 {COMMA16} +STR_1271 :建筑物 +STR_1272 :建筑物 +STR_1273 :建筑物 +STR_1274 :建筑物 +STR_1275 :{COMMA16} 建筑物 +STR_1276 :{COMMA16} 建筑物 +STR_1277 :建筑物 {COMMA16} +STR_1278 :结构 +STR_1279 :结构 +STR_1280 :结构 +STR_1281 :结构 +STR_1282 :{COMMA16} 结构 +STR_1283 :{COMMA16} 结构 +STR_1284 :结构 {COMMA16} +STR_1285 :船 +STR_1286 :船 +STR_1287 :船 +STR_1288 :船 +STR_1289 :{COMMA16} 条船 +STR_1290 :{COMMA16} 条船 +STR_1291 :船 {COMMA16} +STR_1292 :座舱 +STR_1293 :座舱 +STR_1294 :座舱 +STR_1295 :座舱 +STR_1296 :{COMMA16} 个座舱 +STR_1297 :{COMMA16} 个座舱 +STR_1298 :座舱 {COMMA16} +STR_1299 :摩天轮 +STR_1300 :摩天轮 +STR_1301 :摩天轮 +STR_1302 :摩天轮 +STR_1303 :{COMMA16} 个摩天轮 +STR_1304 :{COMMA16} 个摩天轮 +STR_1305 :摩天轮 {COMMA16} +STR_1306 :转轮 +STR_1307 :转轮 +STR_1308 :转轮 +STR_1309 :转轮 +STR_1310 :{COMMA16} 个转轮 +STR_1311 :{COMMA16} 个转轮 +STR_1312 :转轮 {COMMA16} +STR_1313 :玩家 +STR_1314 :玩家 +STR_1315 :玩家 +STR_1316 :玩家 +STR_1317 :{COMMA16} 名玩家 +STR_1318 :{COMMA16} 名玩家 +STR_1319 :玩家 {COMMA16} +STR_1320 :场地 +STR_1321 :场地 +STR_1322 :场地 +STR_1323 :场地 +STR_1324 :{COMMA16} 个场地 +STR_1325 :{COMMA16} 个场地 +STR_1326 :场地 {COMMA16} +STR_1327 :{SMALLFONT}{BLACK}旋转物体 90° +STR_1328 :需要水平地面 +STR_1329 :{WINDOW_COLOUR_2}发车速度: +STR_1330 :{SMALLFONT}{BLACK}离站时最高速度 STR_1331 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{VELOCITY} STR_1332 :{VELOCITY} STR_1333 :{STRINGID} - {STRINGID}{POP16} STR_1334 :{STRINGID} - {STRINGID} {COMMA16} -STR_1335 :{STRINGID} - Entrance{POP16}{POP16} -STR_1336 :{STRINGID} - Station {POP16}{COMMA16} Entrance -STR_1337 :{STRINGID} - Exit{POP16}{POP16} -STR_1338 :{STRINGID} - Station {POP16}{COMMA16} Exit -STR_1339 :{BLACK}No test results yet... -STR_1340 :{WINDOW_COLOUR_2}Max. speed: {BLACK}{VELOCITY} -STR_1341 :{WINDOW_COLOUR_2}Ride time: {BLACK}{STRINGID}{STRINGID}{STRINGID}{STRINGID} +STR_1335 :{STRINGID} - 入口{POP16}{POP16} +STR_1336 :{STRINGID} - 车站 {POP16}{COMMA16} 入口 +STR_1337 :{STRINGID} - 出口{POP16}{POP16} +STR_1338 :{STRINGID} - 车站 {POP16}{COMMA16} 出口 +STR_1339 :{BLACK}暂无测试结果... +STR_1340 :{WINDOW_COLOUR_2}最高速度: {BLACK}{VELOCITY} +STR_1341 :{WINDOW_COLOUR_2}乘坐时间: {BLACK}{STRINGID}{STRINGID}{STRINGID}{STRINGID} STR_1342 :{DURATION} STR_1343 :{DURATION} / -STR_1344 :{WINDOW_COLOUR_2}Ride length: {BLACK}{STRINGID}{STRINGID}{STRINGID}{STRINGID} +STR_1344 :{WINDOW_COLOUR_2}轨道长度: {BLACK}{STRINGID}{STRINGID}{STRINGID}{STRINGID} STR_1345 :{LENGTH} STR_1346 :{LENGTH} / -STR_1347 :{WINDOW_COLOUR_2}Average speed: {BLACK}{VELOCITY} -STR_1348 :{WINDOW_COLOUR_2}Max. positive vertical G's: {BLACK}{COMMA2DP32}g -STR_1349 :{WINDOW_COLOUR_2}Max. positive vertical G's: {OUTLINE}{RED}{COMMA2DP32}g -STR_1350 :{WINDOW_COLOUR_2}Max. negative vertical G's: {BLACK}{COMMA2DP32}g -STR_1351 :{WINDOW_COLOUR_2}Max. negative vertical G's: {OUTLINE}{RED}{COMMA2DP32}g -STR_1352 :{WINDOW_COLOUR_2}Max. lateral G's: {BLACK}{COMMA2DP32}g -STR_1353 :{WINDOW_COLOUR_2}Max. lateral G's: {OUTLINE}{RED}{COMMA2DP32}g -STR_1354 :{WINDOW_COLOUR_2}Highest drop height: {BLACK}{LENGTH} -STR_1355 :{WINDOW_COLOUR_2}Drops: {BLACK}{COMMA16} -STR_1356 :{WINDOW_COLOUR_2}Inversions: {BLACK}{COMMA16} -STR_1357 :{WINDOW_COLOUR_2}Holes: {BLACK}{COMMA16} -STR_1358 :{WINDOW_COLOUR_2}Total 'air' time: {BLACK}{COMMA2DP32}secs -STR_1359 :{WINDOW_COLOUR_2}Queue time: {BLACK}{COMMA16} minute -STR_1360 :{WINDOW_COLOUR_2}Queue time: {BLACK}{COMMA16} minutes -STR_1361 :Can't change speed... -STR_1362 :Can't change launch speed... -STR_1363 :Too high for supports! -STR_1364 :Supports for track above can't be extended any further! -STR_1365 :In-line Twist (left) -STR_1366 :In-line Twist (right) -STR_1367 :Half Loop -STR_1368 :Half Corkscrew (left) -STR_1369 :Half Corkscrew (right) -STR_1370 :Barrel Roll (left) -STR_1371 :Barrel Roll (right) -STR_1372 :Launched Lift Hill -STR_1373 :Large Half Loop (left) -STR_1374 :Large Half Loop (right) -STR_1375 :Upper Transfer -STR_1376 :Lower Transfer -STR_1377 :Heartline Roll (left) -STR_1378 :Heartline Roll (right) -STR_1379 :Reverser (left) -STR_1380 :Reverser (right) -STR_1381 :Curved Lift Hill (left) -STR_1382 :Curved Lift Hill (right) -STR_1383 :Quarter Loop +STR_1347 :{WINDOW_COLOUR_2}平均速度: {BLACK}{VELOCITY} +STR_1348 :{WINDOW_COLOUR_2}最大正垂直重力: {BLACK}{COMMA2DP32}g +STR_1349 :{WINDOW_COLOUR_2}最大正垂直重力: {OUTLINE}{RED}{COMMA2DP32}g +STR_1350 :{WINDOW_COLOUR_2}最大负垂直重力: {BLACK}{COMMA2DP32}g +STR_1351 :{WINDOW_COLOUR_2}最大负垂直重力: {OUTLINE}{RED}{COMMA2DP32}g +STR_1352 :{WINDOW_COLOUR_2}最大横向重力: {BLACK}{COMMA2DP32}g +STR_1353 :{WINDOW_COLOUR_2}最大横向重力: {OUTLINE}{RED}{COMMA2DP32}g +STR_1354 :{WINDOW_COLOUR_2}最高下落高度: {BLACK}{LENGTH} +STR_1355 :{WINDOW_COLOUR_2}下落次数: {BLACK}{COMMA16} +STR_1356 :{WINDOW_COLOUR_2}反转次数: {BLACK}{COMMA16} +STR_1357 :{WINDOW_COLOUR_2}杆洞次数: {BLACK}{COMMA16} +STR_1358 :{WINDOW_COLOUR_2}总 '悬空' 时间: {BLACK}{COMMA2DP32}secs +STR_1359 :{WINDOW_COLOUR_2}等待时间: {BLACK}{COMMA16} minute +STR_1360 :{WINDOW_COLOUR_2}等待时间: {BLACK}{COMMA16} minutes +STR_1361 :不能改变速度... +STR_1362 :不能改变发车速度... +STR_1363 :高度过高无法支撑! +STR_1364 :无法再扩展轨道支撑! +STR_1365 :中心线翻滚 (向左转) +STR_1366 :中心线翻滚 (向右转) +STR_1367 :半环 +STR_1368 :半螺旋 (向左转) +STR_1369 :半螺旋 (向右转) +STR_1370 :油桶滚 (向左转) +STR_1371 :油桶滚 (向右转) +STR_1372 :加速提升坡 +STR_1373 :巨型半环 (向左转) +STR_1374 :巨型半环 (向右转) +STR_1375 :向上传输 +STR_1376 :向下传输 +STR_1377 :心形线滚 (向左转) +STR_1378 :心形线滚 (向右转) +STR_1379 :逆向(向左转) +STR_1380 :逆向 (向右转) +STR_1381 :曲面提升坡 (向左转) +STR_1382 :曲面提升坡 (向右转) +STR_1383 :四分之一回环 STR_1384 :{YELLOW}{STRINGID} -STR_1385 :{SMALLFONT}{BLACK}Other track configurations -STR_1386 :Special... -STR_1387 :Can't change land type... +STR_1385 :{SMALLFONT}{BLACK}其他轨道选项 +STR_1386 :特殊... +STR_1387 :无法改变土地类型... STR_1388 :{OUTLINE}{GREEN}+ {CURRENCY} STR_1389 :{OUTLINE}{RED}- {CURRENCY} STR_1390 :{CURRENCY2DP} STR_1391 :{RED}{CURRENCY2DP} -STR_1392 :{SMALLFONT}{BLACK}View of ride/attraction -STR_1393 :{SMALLFONT}{BLACK}Vehicle details and options -STR_1394 :{SMALLFONT}{BLACK}Operating options -STR_1395 :{SMALLFONT}{BLACK}Maintenance options -STR_1396 :{SMALLFONT}{BLACK}Colour scheme options -STR_1397 :{SMALLFONT}{BLACK}Sound & music options -STR_1398 :{SMALLFONT}{BLACK}Measurements and test data -STR_1399 :{SMALLFONT}{BLACK}Graphs -STR_1400 :Entrance -STR_1401 :Exit -STR_1402 :{SMALLFONT}{BLACK}Build or move entrance to ride/attraction -STR_1403 :{SMALLFONT}{BLACK}Build or move exit from ride/attraction -STR_1404 :{SMALLFONT}{BLACK}Rotate 90° -STR_1405 :{SMALLFONT}{BLACK}Mirror image -STR_1406 :{SMALLFONT}{BLACK}Toggle scenery on/off (if available for this design) -STR_1407 :{WINDOW_COLOUR_2}Build this... -STR_1408 :{WINDOW_COLOUR_2}Cost: {BLACK}{CURRENCY} -STR_1409 :Entry/Exit Platform -STR_1410 :Vertical Tower -STR_1411 :{STRINGID} in the way -STR_1412 :{WINDOW_COLOUR_3}Data logging not available for this type of ride -STR_1413 :{WINDOW_COLOUR_3}Data logging will start when next {STRINGID} leaves {STRINGID} +STR_1392 :{SMALLFONT}{BLACK}观看游乐设施/ 店铺 +STR_1393 :{SMALLFONT}{BLACK}车辆详细与选项 +STR_1394 :{SMALLFONT}{BLACK}运行选项 +STR_1395 :{SMALLFONT}{BLACK}维修选项 +STR_1396 :{SMALLFONT}{BLACK}配色选项 +STR_1397 :{SMALLFONT}{BLACK}声音与音乐选项 +STR_1398 :{SMALLFONT}{BLACK}统计与测试数据 +STR_1399 :{SMALLFONT}{BLACK}图表 +STR_1400 :入口 +STR_1401 :出口 +STR_1402 :{SMALLFONT}{BLACK}建造或移动游乐设施入口 +STR_1403 :{SMALLFONT}{BLACK}建造或移动游乐设施出口 +STR_1404 :{SMALLFONT}{BLACK}旋转 90° +STR_1405 :{SMALLFONT}{BLACK}镜像 +STR_1406 :{SMALLFONT}{BLACK}启用/弃用 景物 (如果设计附带景物) +STR_1407 :{WINDOW_COLOUR_2}建造此... +STR_1408 :{WINDOW_COLOUR_2}费用: {BLACK}{CURRENCY} +STR_1409 :出/入 口 +STR_1410 :垂直高塔 +STR_1411 :{STRINGID}挡道 +STR_1412 :{WINDOW_COLOUR_3}无该类型游乐设施数据记录 +STR_1413 :{WINDOW_COLOUR_3}数据将于下一个 {STRINGID} 离开 {STRINGID}后记录 STR_1414 :{SMALLFONT}{BLACK}{DURATION} -STR_1415 :{WINDOW_COLOUR_2}Velocity -STR_1416 :{WINDOW_COLOUR_2}Altitude -STR_1417 :{WINDOW_COLOUR_2}Vert.G's -STR_1418 :{WINDOW_COLOUR_2}Lat.G's +STR_1415 :{WINDOW_COLOUR_2}速度 +STR_1416 :{WINDOW_COLOUR_2}高度 +STR_1417 :{WINDOW_COLOUR_2}垂直重力 +STR_1418 :{WINDOW_COLOUR_2}水平重力 STR_1419 :{SMALLFONT}{BLACK}{VELOCITY} STR_1420 :{SMALLFONT}{BLACK}{LENGTH} STR_1421 :{SMALLFONT}{BLACK}{COMMA16}g -STR_1422 :{SMALLFONT}{BLACK}Logging data from {POP16}{STRINGID} -STR_1423 :{SMALLFONT}{BLACK}Queue line path -STR_1424 :{SMALLFONT}{BLACK}Footpath -STR_1425 :Footpath -STR_1426 :Queue Line -STR_1427 :{WINDOW_COLOUR_2}Customers: {BLACK}{COMMA32} per hour -STR_1428 :{WINDOW_COLOUR_2}Admission price: +STR_1422 :{SMALLFONT}{BLACK}来自 {POP16}{STRINGID}的记录数据 +STR_1423 :{SMALLFONT}{BLACK}队列线路 +STR_1424 :{SMALLFONT}{BLACK}道路 +STR_1425 :道路 +STR_1426 :队列线 +STR_1427 :{WINDOW_COLOUR_2}乘客: {BLACK}{COMMA32} /小时 +STR_1428 :{WINDOW_COLOUR_2}门票价格: STR_1429 :{POP16}{POP16}{POP16}{CURRENCY2DP} -STR_1430 :Free -STR_1431 :Walking -STR_1432 :Heading for {STRINGID} -STR_1433 :Queuing for {STRINGID} -STR_1434 :Drowning -STR_1435 :On {STRINGID} -STR_1436 :In {STRINGID} -STR_1437 :At {STRINGID} -STR_1438 :Sitting -STR_1439 :(select location) -STR_1440 :修剪草皮中 -STR_1441 :Sweeping footpath -STR_1442 :Emptying litter bin -STR_1443 :Watering gardens -STR_1444 :Watching {STRINGID} -STR_1445 :Watching construction of {STRINGID} -STR_1446 :Looking at scenery -STR_1447 :Leaving the park -STR_1448 :Watching new ride being constructed +STR_1430 :免费 +STR_1431 :行走中 +STR_1432 :前往{STRINGID} +STR_1433 :等待{STRINGID}中 +STR_1434 :溺水 +STR_1435 :在{STRINGID}上 +STR_1436 :在{STRINGID}中 +STR_1437 :在{STRINGID} +STR_1438 :坐下 +STR_1439 :(选择位置) +STR_1440 :正在修剪草皮 +STR_1441 :正在清扫道路 +STR_1442 :正在清空垃圾桶 +STR_1443 :正在灌溉花园 +STR_1444 :正在观看{STRINGID} +STR_1445 :正在观看{STRINGID}的建造 +STR_1446 :正在欣赏景物 +STR_1447 :正在离开游乐园 +STR_1448 :正在观看新游乐设施的建造过程 STR_1449 :{SPRITE} {STRINGID}{NEWLINE}({STRINGID}) STR_1450 :{INLINE_SPRITE}{09}{20}{00}{00}{SPRITE} {STRINGID}{NEWLINE}({STRINGID}) STR_1451 :{STRINGID}{NEWLINE}({STRINGID}) -STR_1452 :Guest's name -STR_1453 :Enter name for this guest: -STR_1454 :Can't name guest... -STR_1455 :Invalid name for guest -STR_1456 :{WINDOW_COLOUR_2}Cash spent: {BLACK}{CURRENCY2DP} -STR_1457 :{WINDOW_COLOUR_2}Cash in pocket: {BLACK}{CURRENCY2DP} -STR_1458 :{WINDOW_COLOUR_2}Time in park: {BLACK}{REALTIME} +STR_1452 :游客命名 +STR_1453 :请输入这名游客的姓名: +STR_1454 :不能命名该游客... +STR_1455 :无效的游客名字 +STR_1456 :{WINDOW_COLOUR_2}花费金额: {BLACK}{CURRENCY2DP} +STR_1457 :{WINDOW_COLOUR_2}钱包中金额: {BLACK}{CURRENCY2DP} +STR_1458 :{WINDOW_COLOUR_2}逗留时间: {BLACK}{REALTIME} STR_1459 :Track style -STR_1460 :{SMALLFONT}{BLACK}'U' shaped open track -STR_1461 :{SMALLFONT}{BLACK}'O' shaped enclosed track -STR_1462 :Too steep for lift hill +STR_1460 :{SMALLFONT}{BLACK}U型开放式轨道 +STR_1461 :{SMALLFONT}{BLACK}O型封闭式轨道 +STR_1462 :提升坡太陡了 STR_1463 :游客 -STR_1464 :Helix up (small) -STR_1465 :Helix up (large) -STR_1466 :Helix down (small) -STR_1467 :Helix down (large) +STR_1464 :螺旋向上 (小) +STR_1465 :螺旋向上 (大) +STR_1466 :螺旋向下 (小) +STR_1467 :螺旋向下 (大) STR_1468 :职员 -STR_1469 :Ride must start and end with stations -STR_1470 :Station not long enough -STR_1471 :{WINDOW_COLOUR_2}Speed: -STR_1472 :{SMALLFONT}{BLACK}Speed of this ride -STR_1473 :{WINDOW_COLOUR_2}Excitement rating: {BLACK}{COMMA2DP32} ({STRINGID}) -STR_1474 :{WINDOW_COLOUR_2}Excitement rating: {BLACK}Not yet available -STR_1475 :{WINDOW_COLOUR_2}Intensity rating: {BLACK}{COMMA2DP32} ({STRINGID}) -STR_1476 :{WINDOW_COLOUR_2}Intensity rating: {BLACK}Not yet available -STR_1477 :{WINDOW_COLOUR_2}Intensity rating: {OUTLINE}{RED}{COMMA2DP32} ({STRINGID}) -STR_1478 :{WINDOW_COLOUR_2}Nausea rating: {BLACK}{COMMA2DP32} ({STRINGID}) -STR_1479 :{WINDOW_COLOUR_2}Nausea rating: {BLACK}Not yet available -STR_1480 :{SMALLFONT}“I can't afford {STRINGID}” -STR_1481 :{SMALLFONT}“I've spent all my money” +STR_1469 :设施必须要开始和结束于车站 +STR_1470 :车站长度过短 +STR_1471 :{WINDOW_COLOUR_2}速度: +STR_1472 :{SMALLFONT}{BLACK}此车速度 +STR_1473 :{WINDOW_COLOUR_2}兴奋度: {BLACK}{COMMA2DP32} ({STRINGID}) +STR_1474 :{WINDOW_COLOUR_2}兴奋度: {BLACK}暂无 +STR_1475 :{WINDOW_COLOUR_2}刺激度: {BLACK}{COMMA2DP32} ({STRINGID}) +STR_1476 :{WINDOW_COLOUR_2}刺激度: {BLACK}暂无 +STR_1477 :{WINDOW_COLOUR_2}刺激度: {OUTLINE}{RED}{COMMA2DP32} ({STRINGID}) +STR_1478 :{WINDOW_COLOUR_2}恶心度: {BLACK}{COMMA2DP32} ({STRINGID}) +STR_1479 :{WINDOW_COLOUR_2}恶心度: {BLACK}暂无 +STR_1480 :{SMALLFONT}“我负担不起{STRINGID}” +STR_1481 :{SMALLFONT}“这个月又要吃土了” STR_1482 :{SMALLFONT}“我感到不舒服” -STR_1483 :{SMALLFONT}“我感到非常不舒服” -STR_1484 :{SMALLFONT}“I want to go on something more thrilling than {STRINGID}” -STR_1485 :{SMALLFONT}“{STRINGID} looks too intense for me” -STR_1486 :{SMALLFONT}“I haven't finished my {STRINGID} yet” -STR_1487 :{SMALLFONT}“Just looking at {STRINGID} makes me feel sick” -STR_1488 :{SMALLFONT}“I'm not paying that much to go on {STRINGID}” +STR_1483 :{SMALLFONT}“香菇蓝瘦” +STR_1484 :{SMALLFONT}“我想试试比{STRINGID}更刺激的” +STR_1485 :{SMALLFONT}“对我来说,{STRINGID}看起来太过刺激了” +STR_1486 :{SMALLFONT}“等等,我还没试过{STRINGID}” +STR_1487 :{SMALLFONT}“看看{STRINGID}就让我感到不适” +STR_1488 :{SMALLFONT}“{STRINGID}太贵了吧,这个乐园真坑爹” STR_1489 :{SMALLFONT}“我想回家” -STR_1490 :{SMALLFONT}“{STRINGID} is really good value” -STR_1491 :{SMALLFONT}“I've already got {STRINGID}” -STR_1492 :{SMALLFONT}“I can't afford {STRINGID}” -STR_1493 :{SMALLFONT}“I'm not hungry” -STR_1494 :{SMALLFONT}“I'm not thirsty” -STR_1495 :{SMALLFONT}“Help! I'm drowning!” -STR_1496 :{SMALLFONT}“I'm lost!” -STR_1497 :{SMALLFONT}“{STRINGID} was great” -STR_1498 :{SMALLFONT}“I've been queuing for {STRINGID} for ages” +STR_1490 :{SMALLFONT}“{STRINGID}真是超值”” +STR_1491 :{SMALLFONT}“我已经有{STRINGID}了” +STR_1492 :{SMALLFONT}“我负担不起{STRINGID}” +STR_1493 :{SMALLFONT}“我不饿” +STR_1494 :{SMALLFONT}“我不渴” +STR_1495 :{SMALLFONT}“救命! 我溺水了!” +STR_1496 :{SMALLFONT}“我迷路了!” +STR_1497 :{SMALLFONT}“{STRINGID}棒棒哒” +STR_1498 :{SMALLFONT}“我等候在这{STRINGID}老久了,啥时才轮到我啊 ” STR_1499 :{SMALLFONT}“我累了” STR_1500 :{SMALLFONT}“我饿了” STR_1501 :{SMALLFONT}“我渴了” STR_1502 :{SMALLFONT}“我需要去厕所” STR_1503 :{SMALLFONT}“我找不到 {STRINGID}” -STR_1504 :{SMALLFONT}“我没有那么多钱去 {STRINGID}” -STR_1505 :{SMALLFONT}“我不去 {STRINGID} 因为在下雨” -STR_1506 :{SMALLFONT}“这里的垃圾真的很糟糕” -STR_1507 :{SMALLFONT}“我找不到乐园的出口” +STR_1504 :{SMALLFONT}“我才没有那么多钱去 {STRINGID}” +STR_1505 :{SMALLFONT}“我不想去 {STRINGID} 因为那里在下雨” +STR_1506 :{SMALLFONT}“这里是垃圾场吗?” +STR_1507 :{SMALLFONT}“哇塞!这个乐园的出口在哪?” STR_1508 :{SMALLFONT}“我想下车 {STRINGID}” STR_1509 :{SMALLFONT}“我想离开 {STRINGID}” -STR_1510 :{SMALLFONT}“我不想去{STRINGID} - 它不安全” -STR_1511 :{SMALLFONT}“这条路是令人厌恶的” -STR_1512 :{SMALLFONT}“这里太拥挤了” +STR_1510 :{SMALLFONT}“我不想去{STRINGID},感觉它随时都会出问题” +STR_1511 :{SMALLFONT}“这路有点恶心啊!” +STR_1512 :{SMALLFONT}“这里怎么这么挤啊!” STR_1513 :{SMALLFONT}“这里的损坏是非常糟糕的” STR_1514 :{SMALLFONT}“壮丽的景色!” STR_1515 :{SMALLFONT}“这个公园真的很干净” STR_1516 :{SMALLFONT}“跳跃喷泉太好了” STR_1517 :{SMALLFONT}“这里的音乐很好” -STR_1518 :{SMALLFONT}“这个{STRINGID}的热气球真是便宜” -STR_1519 :{SMALLFONT}“这个{STRINGID}的可爱的玩具真是便宜” -STR_1520 :{SMALLFONT}“这个{STRINGID}的乐园地图真是便宜” -STR_1521 :{SMALLFONT}“这个{STRINGID}的在乘坐时的照片真是便宜” -STR_1522 :{SMALLFONT}“这个{STRINGID}的伞真是便宜” -STR_1523 :{SMALLFONT}“这个{STRINGID}的饮料真是便宜” -STR_1524 :{SMALLFONT}“这个{STRINGID}的汉堡真是便宜” -STR_1525 :{SMALLFONT}“这个{STRINGID}的薯条真是便宜” -STR_1526 :{SMALLFONT}“这个{STRINGID}的冰激凌真是便宜” -STR_1527 :{SMALLFONT}“这个{STRINGID}的棉花糖真是便宜” +STR_1518 :{SMALLFONT}“这个{STRINGID}的气球好便宜” +STR_1519 :{SMALLFONT}“这个{STRINGID}的毛绒公仔好便宜” +STR_1520 :{SMALLFONT}“这个{STRINGID}的乐园地图好便宜” +STR_1521 :{SMALLFONT}“这个{STRINGID}的即时照片好便宜” +STR_1522 :{SMALLFONT}“这个{STRINGID}的伞好便宜” +STR_1523 :{SMALLFONT}“这个{STRINGID}的饮料好便宜” +STR_1524 :{SMALLFONT}“这个{STRINGID}的汉堡好便宜” +STR_1525 :{SMALLFONT}“这个{STRINGID}的薯条好便宜” +STR_1526 :{SMALLFONT}“这个{STRINGID}的冰激好是便宜” +STR_1527 :{SMALLFONT}“这个{STRINGID}的棉花糖好便宜” STR_1528 : STR_1529 : STR_1530 : -STR_1531 :{SMALLFONT}“这个{STRINGID}的披萨真是便宜” +STR_1531 :{SMALLFONT}“这个{STRINGID}的披萨好便宜” STR_1532 : -STR_1533 :{SMALLFONT}“这个{STRINGID}的爆米花真是便宜” -STR_1534 :{SMALLFONT}“这个{STRINGID}的热狗真是便宜” -STR_1535 :{SMALLFONT}“这个{STRINGID}的触手真是便宜” -STR_1536 :{SMALLFONT}“这个{STRINGID}的帽子真是便宜” -STR_1537 :{SMALLFONT}“这个{STRINGID}的太妃糖苹果真是便宜” -STR_1538 :{SMALLFONT}“这个{STRINGID}的T恤真是便宜” -STR_1539 :{SMALLFONT}“这个{STRINGID}的甜甜圈真是便宜” -STR_1540 :{SMALLFONT}“这个{STRINGID}的咖啡真是便宜” +STR_1533 :{SMALLFONT}“这个{STRINGID}的爆米花好便宜” +STR_1534 :{SMALLFONT}“这个{STRINGID}的热狗好便宜” +STR_1535 :{SMALLFONT}“这个{STRINGID}的触手好便宜” +STR_1536 :{SMALLFONT}“这个{STRINGID}的帽子好便宜” +STR_1537 :{SMALLFONT}“这个{STRINGID}的太妃糖苹果好便宜” +STR_1538 :{SMALLFONT}“这个{STRINGID}的T恤好便宜” +STR_1539 :{SMALLFONT}“这个{STRINGID}的甜甜圈好便宜” +STR_1540 :{SMALLFONT}“这个{STRINGID}的咖啡好便宜” STR_1541 : -STR_1542 :{SMALLFONT}“这个{STRINGID}的炸鸡真是便宜” -STR_1543 :{SMALLFONT}“这个{STRINGID}的柠檬水真是便宜” +STR_1542 :{SMALLFONT}“这个{STRINGID}的炸鸡好便宜” +STR_1543 :{SMALLFONT}“这个{STRINGID}的柠檬水好便宜” STR_1544 : STR_1545 : STR_1546 : @@ -938,20 +938,20 @@ STR_1548 : STR_1549 : STR_1550 :{SMALLFONT}“哇!” STR_1551 :{SMALLFONT}“我有种奇怪的感觉, 像是有人在监视我” -STR_1552 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一个气球” -STR_1553 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一只毛绒公仔” -STR_1554 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一份游乐园地图” -STR_1555 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一张即时照片” -STR_1556 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一把雨伞” -STR_1557 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一杯饮料” -STR_1558 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一个汉堡” -STR_1559 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一份薯条” -STR_1560 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一个冰激淋” -STR_1561 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一球棉花糖” +STR_1552 :{SMALLFONT}“{STRINGID}的气球里充的是仙气吧,怎么这么贵” +STR_1553 :{SMALLFONT}“我相信{STRINGID}的毛绒公仔里面塞的不是棉花,是钱” +STR_1554 :{SMALLFONT}“{STRINGID}的游乐园地图是都精确,居然卖的这么贵” +STR_1555 :{SMALLFONT}“都什么年代了,{STRINGID}的即时照片还这么贵” +STR_1556 :{SMALLFONT}“我宁可淋雨,也不会买{STRINGID}的天价雨伞” +STR_1557 :{SMALLFONT}“{STRINGID}的饮料,可真是滴水寸金” +STR_1558 :{SMALLFONT}“{STRINGID}的汉堡比上海迪斯尼的包子还贵” +STR_1559 :{SMALLFONT}“{STRINGID}的薯条使用什么土豆做的?怎么这么贵” +STR_1560 :{SMALLFONT}“{STRINGID}的冰淇淋贵的有点离谱” +STR_1561 :{SMALLFONT}“{STRINGID}的棉花糖是真滴贵” STR_1562 : STR_1563 : STR_1564 : -STR_1565 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一份披萨” +STR_1565 :{SMALLFONT}“{STRINGID}的披萨真的贵” STR_1566 : STR_1567 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一份爆米花” STR_1568 :{SMALLFONT}“我不会花那么多钱去{STRINGID}购买一个热狗” @@ -990,7 +990,7 @@ STR_1600 :{SMALLFONT}“这份从{STRINGID}购买的曲奇饼干真是超值 STR_1601 : STR_1602 : STR_1603 : -STR_1604 :{SMALLFONT}“这条从{STRINGID}购买的烤香肠真是超值” +STR_1604 :{SMALLFONT}“这根从{STRINGID}购买的烤香肠真是超值” STR_1605 : STR_1606 : STR_1607 : @@ -1068,16 +1068,16 @@ STR_1679 :向上盘旋 (向左) STR_1680 :向上盘旋 (向右) STR_1681 :向下盘旋 (向左) STR_1682 :向下盘旋 (向右) -STR_1683 :Base size 2 x 2 -STR_1684 :Base size 4 x 4 -STR_1685 :Base size 2 x 4 -STR_1686 :Base size 5 x 1 -STR_1687 :Water splash -STR_1688 :Base size 4 x 1 -STR_1689 :Block brakes +STR_1683 :2 x 2地基 +STR_1684 :4 x 4地基 +STR_1685 :2 x 4地基 +STR_1686 :5 x 1地基 +STR_1687 :水溅 +STR_1688 :4 x 1地基 +STR_1689 :刹车装置 STR_1690 :{WINDOW_COLOUR_2}{STRINGID}{NEWLINE}{BLACK}{STRINGID} -STR_1691 :{WINDOW_COLOUR_2} Cost: {BLACK}{CURRENCY} -STR_1692 :{WINDOW_COLOUR_2} Cost: {BLACK}from {CURRENCY} +STR_1691 :{WINDOW_COLOUR_2} 费用: {BLACK}{CURRENCY} +STR_1692 :{WINDOW_COLOUR_2} 费用: {BLACK}from {CURRENCY} STR_1693 :{SMALLFONT}{BLACK}游客 STR_1694 :{SMALLFONT}{BLACK}职员 STR_1695 :{SMALLFONT}{BLACK}收入和支出 @@ -1090,50 +1090,50 @@ STR_1701 :雇佣新的机械师 STR_1702 :雇佣新的安保人员 STR_1703 :雇佣新的表演人员 STR_1704 :不能雇佣新职员... -STR_1705 :{SMALLFONT}{BLACK}Sack this staff member -STR_1706 :{SMALLFONT}{BLACK}Move this person to a new location -STR_1707 :Too many staff in game -STR_1708 :{SMALLFONT}{BLACK}Set patrol area for this staff member -STR_1709 :Sack staff -STR_1710 :Yes -STR_1711 :{WINDOW_COLOUR_1}Are you sure you want to sack {STRINGID}? -STR_1712 :{INLINE_SPRITE}{247}{19}{00}{00}{WINDOW_COLOUR_2}Sweep footpaths -STR_1713 :{INLINE_SPRITE}{248}{19}{00}{00}{WINDOW_COLOUR_2}Water gardens -STR_1714 :{INLINE_SPRITE}{249}{19}{00}{00}{WINDOW_COLOUR_2}Empty litter bins -STR_1715 :{INLINE_SPRITE}{250}{19}{00}{00}{WINDOW_COLOUR_2}Mow grass -STR_1716 :Invalid name for park -STR_1717 :Can't rename park... -STR_1718 :Park Name -STR_1719 :Enter name for park: -STR_1720 :{SMALLFONT}{BLACK}Name park +STR_1705 :{SMALLFONT}{BLACK}解雇该员工 +STR_1706 :{SMALLFONT}{BLACK}移动该人物到新的位置 +STR_1707 :游戏里员工太多了 +STR_1708 :{SMALLFONT}{BLACK}为该员工设置巡逻区域 +STR_1709 :解雇员工 +STR_1710 :确认 +STR_1711 :{WINDOW_COLOUR_1}你确定解雇{STRINGID}? +STR_1712 :{INLINE_SPRITE}{247}{19}{00}{00}{WINDOW_COLOUR_2}清扫道路 +STR_1713 :{INLINE_SPRITE}{248}{19}{00}{00}{WINDOW_COLOUR_2}灌溉花园 +STR_1714 :{INLINE_SPRITE}{249}{19}{00}{00}{WINDOW_COLOUR_2}清空垃圾桶 +STR_1715 :{INLINE_SPRITE}{250}{19}{00}{00}{WINDOW_COLOUR_2}修剪草地 +STR_1716 :无效的游乐园名称 +STR_1717 :不能重命名乐园... +STR_1718 :乐园命名 +STR_1719 :请输入乐园的名称: +STR_1720 :{SMALLFONT}{BLACK}命名乐园 STR_1721 :游乐园已关闭 STR_1722 :游乐园开放中 -STR_1723 :Can't open park... -STR_1724 :Can't close park... -STR_1725 :Can't buy land... -STR_1726 :Land not for sale! -STR_1727 :Construction rights not for sale! -STR_1728 :Can't buy construction rights here... -STR_1729 :Land not owned by park! -STR_1730 :{RED}Closed - - +STR_1723 :不能开放乐园... +STR_1724 :不能关闭乐园... +STR_1725 :不能购买地块... +STR_1726 :地块不供购买! +STR_1727 :建筑权不供购买! +STR_1728 :不能在此购买建权... +STR_1729 :非乐园所属地块! +STR_1730 :{RED}已关闭 - - STR_1731 :{WHITE}{STRINGID} - - -STR_1732 :Build -STR_1733 :Mode -STR_1734 :{WINDOW_COLOUR_2}Number of laps: -STR_1735 :{SMALLFONT}{BLACK}Number of laps of circuit +STR_1732 :建造 +STR_1733 :模式 +STR_1734 :{WINDOW_COLOUR_2}圈数: +STR_1735 :{SMALLFONT}{BLACK}每次循环的圈数 STR_1736 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} -STR_1738 :Can't change number of laps... -STR_1739 :Race won by guest {INT32} -STR_1740 :Race won by {STRINGID} -STR_1741 :Not yet constructed ! -STR_1742 :{WINDOW_COLOUR_2}Max. people on ride: -STR_1743 :{SMALLFONT}{BLACK}Maximum number of people allowed on this ride at one time +STR_1738 :不能更改圈数... +STR_1739 :游客{INT32}赢得本比赛 +STR_1740 :{STRINGID}赢得本比赛 +STR_1741 :尚未建造 ! +STR_1742 :{WINDOW_COLOUR_2}设施最大载客量: +STR_1743 :{SMALLFONT}{BLACK}该游乐设施同时最多可以乘坐的乘客数量、 STR_1744 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} -STR_1746 :Can't change this... -STR_1747 :{WINDOW_COLOUR_2}Time limit: -STR_1748 :{SMALLFONT}{BLACK}Time limit for ride +STR_1746 :不能更改... +STR_1747 :{WINDOW_COLOUR_2}时间限制: +STR_1748 :{SMALLFONT}{BLACK}此游乐设施的游玩时间限制 STR_1749 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{DURATION} -STR_1751 :Can't change time limit for ride... +STR_1751 :不能更改此游乐设施的游玩时间限制... STR_1752 :{SMALLFONT}{BLACK}Show list of individual guests in park STR_1753 :{SMALLFONT}{BLACK}Show summarised list of guests in park STR_1754 :{BLACK}{COMMA16}名游客 @@ -1142,17 +1142,17 @@ STR_1756 :{WINDOW_COLOUR_2}门票价格: STR_1757 :{WINDOW_COLOUR_2}可靠性: {MOVE_X}{255}{BLACK}{COMMA16}% STR_1758 :{SMALLFONT}{BLACK}建造模式 STR_1759 :{SMALLFONT}{BLACK}移动模式 -STR_1760 :{SMALLFONT}{BLACK}Fill-in mode -STR_1761 :{SMALLFONT}{BLACK}Build maze in this direction -STR_1762 :Waterfalls -STR_1763 :Rapids -STR_1764 :Log Bumps -STR_1765 :On-ride photo section -STR_1766 :Reverser turntable -STR_1767 :Spinning tunnel -STR_1768 :Can't change number of swings... -STR_1769 :{WINDOW_COLOUR_2}Number of swings: -STR_1770 :{SMALLFONT}{BLACK}Number of complete swings +STR_1760 :{SMALLFONT}{BLACK}填补模式 +STR_1761 :{SMALLFONT}{BLACK}以此方向建造迷宫 +STR_1762 :瀑布 +STR_1763 :急流 +STR_1764 :漩涡 +STR_1765 :即时拍照区域 +STR_1766 :反向转盘 +STR_1767 :旋转隧道 +STR_1768 :不能改变摇摆次数... +STR_1769 :{WINDOW_COLOUR_2}摇摆次数: +STR_1770 :{SMALLFONT}{BLACK}完全摇摆次数 STR_1771 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} STR_1773 :每个游乐设施只能建造一个照相区域 STR_1774 :每个游乐设施只能建造一个牵引轨道 @@ -1225,8 +1225,8 @@ STR_1843 :{COMMA16}游客最喜爱的游乐设施 STR_1844 :{SMALLFONT}{BLACK}选择要在游乐设施/店面列表中显示的信息类别 STR_1845 :{MONTHYEAR} STR_1846 :{COMMA16}游客 -STR_1847 :{INLINE_SPRITE}{11}{20}{00}{00}{COMMA16} guests -STR_1848 :{INLINE_SPRITE}{10}{20}{00}{00}{COMMA16} guests +STR_1847 :{INLINE_SPRITE}{11}{20}{00}{00}{COMMA16}游客 +STR_1848 :{INLINE_SPRITE}{10}{20}{00}{00}{COMMA16}游客 STR_1849 :{WINDOW_COLOUR_2}播放音乐 STR_1850 :{SMALLFONT}{BLACK}选择此游乐设施是否播放音乐 STR_1851 :{WINDOW_COLOUR_2}运行成本: {BLACK}每小时{CURRENCY2DP} @@ -1237,24 +1237,24 @@ STR_1855 :{WINDOW_COLOUR_2}建造年份: {BLACK}{COMMA16}年前 STR_1856 :{WINDOW_COLOUR_2}每件产品售出的利润: {BLACK}{CURRENCY2DP} STR_1857 :{WINDOW_COLOUR_2}每件产品售出的亏损: {BLACK}{CURRENCY2DP} STR_1858 :{WINDOW_COLOUR_2}成本: {BLACK}每个月{CURRENCY2DP} -STR_1859 :位清洁工 -STR_1860 :位机械师 -STR_1861 :位警卫 -STR_1862 :位演员 -STR_1863 :位清洁工 -STR_1864 :位机械师 -STR_1865 :位警卫 -STR_1866 :位演员 +STR_1859 :位环保工人 +STR_1860 :位维修人员 +STR_1861 :位安保警卫 +STR_1862 :位表演人员 +STR_1863 :位环保工人 +STR_1864 :位维修人员 +STR_1865 :位安保警卫 +STR_1866 :位表演人员 STR_1867 :{BLACK}{COMMA16} {STRINGID} STR_1868 :不能改变旋转次数... STR_1869 :{WINDOW_COLOUR_2}旋转次数: STR_1870 :{SMALLFONT}{BLACK}完整旋转次数 STR_1871 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{COMMA16} -STR_1873 :{WINDOW_COLOUR_2}Income: {BLACK}{CURRENCY2DP} per hour -STR_1874 :{WINDOW_COLOUR_2}Profit: {BLACK}{CURRENCY2DP} per hour +STR_1873 :{WINDOW_COLOUR_2}收入: {BLACK}{CURRENCY2DP}/时 +STR_1874 :{WINDOW_COLOUR_2}盈利: {BLACK}{CURRENCY2DP}/时 STR_1875 :{BLACK} {SPRITE}{BLACK} {STRINGID} -STR_1876 :{WINDOW_COLOUR_2}{INLINE_SPRITE}{251}{19}{00}{00}Inspect Rides -STR_1877 :{WINDOW_COLOUR_2}{INLINE_SPRITE}{252}{19}{00}{00}Fix Rides +STR_1876 :{WINDOW_COLOUR_2}{INLINE_SPRITE}{251}{19}{00}{00}检查游乐设施 +STR_1877 :{WINDOW_COLOUR_2}{INLINE_SPRITE}{252}{19}{00}{00}维修游乐设施 STR_1878 :{WINDOW_COLOUR_2}检查时间间隔: STR_1879 :每10分钟 STR_1880 :每20分钟 @@ -1263,11 +1263,11 @@ STR_1882 :每45分钟 STR_1883 :每小时 STR_1884 :每2小时 STR_1885 :从不 -STR_1886 :Inspecting {STRINGID} +STR_1886 :检查 {STRINGID} STR_1887 :{WINDOW_COLOUR_2}上次检查时间: {BLACK}{COMMA16}分钟前 STR_1888 :{WINDOW_COLOUR_2}上次检查时间: {BLACK}大于4小时 STR_1889 :{WINDOW_COLOUR_2}故障度: {MOVE_X}{255}{BLACK}{COMMA16}% -STR_1890 :{SMALLFONT}{BLACK}选择机械师检查此游乐设施的时间间隔 +STR_1890 :{SMALLFONT}{BLACK}选择维修人员检查此游乐设施的时间间隔 STR_1891 :在乐园里暂未有{STRINGID}! # The following two strings were used to display an error when the disc was missing. # This has been replaced in OpenRCT2. @@ -1288,12 +1288,12 @@ STR_1907 :{WINDOW_COLOUR_2}员工薪金 STR_1908 :{WINDOW_COLOUR_2}营销费用 STR_1909 :{WINDOW_COLOUR_2}研发费用 STR_1910 :{WINDOW_COLOUR_2}贷款利息 -STR_1911 :{BLACK} at {COMMA16}% per year +STR_1911 :{BLACK} 每年{COMMA16}% STR_1912 :{MONTH} STR_1913 :{BLACK}+{CURRENCY2DP} STR_1914 :{BLACK}{CURRENCY2DP} STR_1915 :{RED}{CURRENCY2DP} -STR_1916 :{WINDOW_COLOUR_2}Loan: +STR_1916 :{WINDOW_COLOUR_2}贷款: STR_1917 :{POP16}{POP16}{POP16}{CURRENCY} STR_1918 :不能再借贷更多钱! STR_1919 :没有足够的现金! @@ -1378,7 +1378,7 @@ STR_1998 :空罐子 STR_1999 :垃圾 STR_2000 :空汉堡包装盒 STR_2001 :披萨 -STR_2002 :Voucher +STR_2002 :优惠券 STR_2003 :爆米花 STR_2004 :热狗 STR_2005 :触手 @@ -1406,7 +1406,7 @@ STR_2026 :空罐子 STR_2027 :垃圾 STR_2028 :空汉堡包装盒 STR_2029 :披萨 -STR_2030 :Voucher +STR_2030 :优惠券 STR_2031 :爆米花 STR_2032 :热狗 STR_2033 :触手 @@ -1579,7 +1579,7 @@ STR_2214 :游戏暂停时无法建造任何物体! STR_2215 :{STRINGID}{NEWLINE}({STRINGID}) STR_2216 :{WINDOW_COLOUR_2}{COMMA16}°C STR_2217 :{WINDOW_COLOUR_2}{COMMA16}°F -STR_2218 :{RED}{STRINGID}, {STRINGID}的一部份, 尚未返回到{STRINGID}!{NEWLINE}检查是否被卡住或被停止了 +STR_2218 :{RED}{STRINGID}, {STRINGID}的一部份,尚未返回到{STRINGID}!{NEWLINE}检查是否被卡住或被停止了 STR_2219 :{RED}{COMMA16}人已丧生于{STRINGID}的意外中 STR_2220 :{WINDOW_COLOUR_2}乐园评价: {BLACK}{COMMA16} STR_2221 :{SMALLFONT}{BLACK}乐园评价: {COMMA16} @@ -1616,9 +1616,9 @@ STR_2251 :只可以在道路上建造! STR_2252 :只可以横过道路建造! STR_2253 :运输类游乐设施 STR_2254 :温和类游乐设施 -STR_2255 :云霄飞车 +STR_2255 :过山车 STR_2256 :剌激类游乐设施 -STR_2257 :水文类游乐设施 +STR_2257 :水上乐园设施 STR_2258 :商店及摊贩 STR_2259 :景物及主题景物 STR_2260 :无资金 @@ -1641,9 +1641,9 @@ STR_2276 :{SMALLFONT}{BLACK}显示研发项目进度 STR_2277 :未知 STR_2278 :运输类游乐设施 STR_2279 :温和类游乐设施 -STR_2280 :云霄飞车 +STR_2280 :过山车 STR_2281 :剌激类游乐设施 -STR_2282 :水文类游乐设施 +STR_2282 :水上游乐设施 STR_2283 :商店及摊贩 STR_2284 :景物及主题景物 STR_2285 :初始研发 @@ -1709,7 +1709,7 @@ STR_2350 :{WINDOW_COLOUR_2}雇用日期: {BLACK}{MONTHYEAR} STR_2351 :{WINDOW_COLOUR_2}除草次数: {BLACK}{COMMA16} STR_2352 :{WINDOW_COLOUR_2}灌溉次数: {BLACK}{COMMA16} STR_2353 :{WINDOW_COLOUR_2}扫地次数: {BLACK}{COMMA16} -STR_2354 :{WINDOW_COLOUR_2}清空拉坡筒次数: {BLACK}{COMMA16} +STR_2354 :{WINDOW_COLOUR_2}清空垃圾桶次数: {BLACK}{COMMA16} STR_2355 :{WINDOW_COLOUR_2}修理游乐设施次数: {BLACK}{COMMA16} STR_2356 :{WINDOW_COLOUR_2}检验游乐设施次数: {BLACK}{COMMA16} STR_2358 :单位 @@ -1738,17 +1738,17 @@ STR_2380 :{SMALLFONT}{BLACK}调整较小的水塘区域 STR_2381 :{SMALLFONT}{BLACK}调整较大的水塘区域 STR_2382 :土地 STR_2383 :水面 -STR_2384 :{WINDOW_COLOUR_2}你的目标 +STR_2384 :{WINDOW_COLOUR_2}你的目标: STR_2385 :{BLACK}无 -STR_2386 :{BLACK}在 {MONTHYEAR} 之前乐园内至少有 {COMMA16} 游客, 并且乐园评分不低于 600 -STR_2387 :{BLACK}在 {PUSH16}{PUSH16}{PUSH16}{MONTHYEAR} 之前乐园价值不低于 {POP16}{POP16}{CURRENCY} +STR_2386 :{BLACK}在{POP16}{MONTHYEAR} 之前乐园内至少有 {PUSH16}{PUSH16}{COMMA16} 游客, 并且乐园评分不低于 600 +STR_2387 :{BLACK}在{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}之前乐园价值不低于{POP16}{POP16}{CURRENCY} STR_2388 :{BLACK}尽情玩吧! STR_2389 :{BLACK}建造你的最佳{STRINGID}吧! STR_2390 :{BLACK}在你的乐园中运营10种类型的过山车, 并且每一个的兴奋度不低于 6.00 STR_2391 :{BLACK}乐园内至少有 {COMMA16} 游客,并且在任何时候你都不能让公园评分低于700 ! STR_2392 :{BLACK}每个月的门票收入至少 {POP16}{POP16}{CURRENCY} STR_2393 :{BLACK}在你的乐园中运营10种类型的过山车, 每一个的长度至少 {LENGTH}, 并且每一个的兴奋度不低于 7.00 -STR_2394 :{BLACK}建造好五座未完成的云霄飞车, 将他们每座设计成兴奋度至少达到{POP16}{POP16}{COMMA2DP32} +STR_2394 :{BLACK}建造好五座未完成的过山车, 将他们每座设计成兴奋度至少达到{POP16}{POP16}{COMMA2DP32} STR_2395 :{BLACK}还清乐园的贷款, 并令其价值至少达到{POP16}{POP16}{CURRENCY} STR_2396 :{BLACK}食物, 饮料及纪念品的销售收入至少要达到每月{POP16}{POP16}{CURRENCY} STR_2397 :无 @@ -1756,11 +1756,11 @@ STR_2398 :指定日期要达到的游客数量 STR_2399 :指定日期要达到的乐园评价 STR_2400 :尽情玩吧 STR_2401 :建造你的最佳游乐设施 -STR_2402 :建造十座云霄飞车 +STR_2402 :建造十座过山车 STR_2403 :乐园内游客数量 STR_2404 :游乐设施门票的每月收入 -STR_2405 :建造十座指定长度的云霄飞车 -STR_2406 :完成建造五座云霄飞车 +STR_2405 :建造十座指定长度的过山车 +STR_2406 :完成建造五座过山车 STR_2407 :付清贷款及达到指定乐园评价 STR_2408 :食物/饮料或其他商品的每月收入 STR_2409 :{WINDOW_COLOUR_2}实行中的营销计划 @@ -1816,7 +1816,7 @@ STR_2468 :{SMALLFONT} {BLACK}显示这个乐园最近获得的奖项 STR_2469 :{SMALLFONT} {BLACK}选择投放多少资金到研发当中 STR_2470 :{SMALLFONT} {BLACK}研发新的运输类游游设施 STR_2471 :{SMALLFONT} {BLACK}研发新的温和类游乐设施 -STR_2472 :{SMALLFONT} {BLACK}研发新的云霄飞车 +STR_2472 :{SMALLFONT} {BLACK}研发新的过山车 STR_2473 :{SMALLFONT} {BLACK}研发新的刺激类游乐设施 STR_2474 :{SMALLFONT} {BLACK}研发新的水文类游乐设施 STR_2475 :{SMALLFONT} {BLACK}研发新的商店及摊贩 @@ -2023,21 +2023,21 @@ STR_2676 :??? STR_2677 :??? STR_2678 :??? STR_2679 :??? -STR_2680 :All research complete -STR_2681 :{MEDIUMFONT}{BLACK}Increases your money by {CURRENCY} -STR_2684 :{SMALLFONT}{BLACK}Large group of guests arrive -STR_2685 :Simplex Noise Parameters -STR_2686 :Low: -STR_2687 :High: -STR_2688 :Base Frequency: -STR_2689 :Octaves: -STR_2690 :Map Generation -STR_2691 :Base height: -STR_2692 :Water level: -STR_2693 :Terrain: -STR_2694 :Generate -STR_2695 :Random terrain -STR_2696 :Place trees +STR_2680 :所有项目已研发完毕 +STR_2681 :{MEDIUMFONT}{BLACK}增加{CURRENCY}到你的账上 +STR_2684 :{SMALLFONT}{BLACK}一大批游客到访 +STR_2685 :单形噪声参数 +STR_2686 :低值: +STR_2687 :高值: +STR_2688 :基础频率: +STR_2689 :八音度: +STR_2690 :生成地图 +STR_2691 :土地基础高度: +STR_2692 :水面高度: +STR_2693 :地形: +STR_2694 :生成 +STR_2695 :随机地形 +STR_2696 :放置树木 STR_2697 :??? STR_2698 :??? STR_2699 :??? @@ -2049,9 +2049,9 @@ STR_2704 :每30分钟 STR_2705 :每小时 STR_2706 :从不 STR_2707 :使用系统的文件浏览器 -STR_2708 :{WINDOW_COLOUR_1}Are you sure you want to overwrite {STRINGID}? -STR_2709 :Overwrite -STR_2710 :Type the name of the file. +STR_2708 :{WINDOW_COLOUR_1}你确定要覆盖存档{STRINGID}? +STR_2709 :覆盖 +STR_2710 :请输入存档名称: STR_2711 :; STR_2712 := STR_2713 :, @@ -2059,8 +2059,8 @@ STR_2714 :- STR_2715 :. STR_2716 :/ STR_2717 :' -STR_2718 :Up -STR_2719 :New file +STR_2718 :向上一层 +STR_2719 :新存档 STR_2720 :{UINT16}秒 STR_2721 :{UINT16}秒 STR_2722 :{UINT16}分:{UINT16}秒 @@ -2077,8 +2077,8 @@ STR_2732 :{COMMA16}ft STR_2733 :{COMMA16}m STR_2734 :{COMMA16}mph STR_2735 :{COMMA16}km/h -STR_2736 :{COMMA16}年, {MONTH} -STR_2737 :{STRINGID}{COMMA16}年, {MONTH} +STR_2736 :第{POP16}{COMMA16}年{PUSH16}{PUSH16}{MONTH} +STR_2737 :{STRINGID} 第{POP16}{COMMA16}年{PUSH16}{PUSH16}{MONTH} STR_2738 :主菜单音乐: STR_2739 :无 STR_2740 :过山车大亨1(RCT1) @@ -2089,17 +2089,17 @@ STR_2744 :[ STR_2745 :\ STR_2746 :] STR_2747 :” -STR_2749 :My new scenario +STR_2749 :我的新剧情 # New strings used in the cheats window previously these were ??? -STR_2750 :Move all items to top -STR_2751 :Move all items to bottom -STR_2752 :Clear grass -STR_2753 :修剪好的草地 +STR_2750 :移动全部物品到顶部 +STR_2751 :移动全部物品到底部 +STR_2752 :清理草地 +STR_2753 :已修剪的草地 STR_2754 :浇灌植物 STR_2755 :修复路面设施破损 STR_2756 :去除路面垃圾 STR_2763 :??? -STR_2765 :Large Tram +STR_2765 :添加超多游客到访 STR_2766 :完成剧情目标 STR_2767 :冻结天气 STR_2768 :解除冻结天气 @@ -2119,7 +2119,7 @@ STR_2782 :SHIFT + STR_2783 :CTRL + STR_2784 :变更快捷键 STR_2785 :{WINDOW_COLOUR_2}Press new shortcut key for:{NEWLINE}“{STRINGID}” -STR_2786 :{SMALLFONT}{BLACK}Click on shortcut description to select new key +STR_2786 :{SMALLFONT}{BLACK}点击快捷键描述来设置新的快捷键 STR_2787 :{WINDOW_COLOUR_2}乐园价值: {BLACK}{CURRENCY} STR_2788 :{WINDOW_COLOUR_2}恭喜您!{NEWLINE}{BLACK}你已经达到你的目标, 并令公司价值增至 {CURRENCY} ! STR_2789 :{WINDOW_COLOUR_2}你未达成目标 ! @@ -2129,58 +2129,58 @@ STR_2792 :请输入你要在剧情列表显示的名字: STR_2793 :{SMALLFONT}(由{STRINGID}达成) STR_2794 :{WINDOW_COLOUR_2}完成者: {BLACK}{STRINGID}{NEWLINE}{WINDOW_COLOUR_2} 公司价值: {BLACK}{CURRENCY} STR_2795 :排序 -STR_2796 :{SMALLFONT}{BLACK}Sort the ride list into order using the information type displayed +STR_2796 :{SMALLFONT}{BLACK}将游乐设施按照不同类别排列 STR_2797 :当鼠标位于屏幕边缘时滚动视图 STR_2798 :{SMALLFONT}{BLACK}当鼠标位于屏幕边缘时滚动视图 STR_2799 :{SMALLFONT}{BLACK}查看或改变控制按键分配 -STR_2800 :{WINDOW_COLOUR_2}Total admissions: {BLACK}{COMMA32} -STR_2801 :{WINDOW_COLOUR_2}Income from admissions: {BLACK}{CURRENCY2DP} +STR_2800 :{WINDOW_COLOUR_2}总购票人数: {BLACK}{COMMA32} +STR_2801 :{WINDOW_COLOUR_2}门票收入: {BLACK}{CURRENCY2DP} STR_2802 :地图 STR_2803 :{SMALLFONT}{BLACK}高亮显示游客 STR_2804 :{SMALLFONT}{BLACK}高亮显示雇员 STR_2805 :{SMALLFONT}{BLACK}显示游乐园地图 -STR_2806 :{RED}游客都在投诉乐园里的道路十分肮脏及恶心{NEWLINE}请检查你的清洁工人位置, 并考虑以更好的方法管理他们 -STR_2807 :{RED}游客都在投诉乐园里十分多随地抛弃的垃圾{NEWLINE}请检查你的清洁工人位置, 并考虑以更好的方法管理他们 -STR_2808 :{RED}游客都在投诉乐园里的公物被毁坏{NEWLINE}请检查你的安全警卫位置, 并考虑以更好的方法管理他们 -STR_2809 :{RED}游客都饿了, 但他们找不到卖食物的店舖 -STR_2810 :{RED}游客都渴了, 但他们找不到卖饮料的店舖 -STR_2811 :{RED}游客都在抱怨于乐园里找不到厕所 -STR_2812 :{RED}游客都被卡住或迷失了{NEWLINE}请检查乐园的道路设计是否需要改善, 以便游客找到出路 -STR_2813 :{RED}你的乐园入场费太贵了!{NEWLINE}减低入场费或增加乐团价值来吸引更多游客来访 -STR_2814 :{WINDOW_COLOUR_2}最杂乱无章乐园奖 +STR_2806 :{RED}大量游客投诉乐园里的道路非常肮脏及恶心{NEWLINE}请检查你的清洁工人位置, 并考虑调整的管理方法 +STR_2807 :{RED}大量游客投诉乐园里随处可见的垃圾{NEWLINE}请检查你的清洁工人位置, 并考虑调整管理方法 +STR_2808 :{RED}大量游客投诉乐园里的公物被毁坏{NEWLINE}请检查你的安保人员位置, 并考虑调整管理方法 +STR_2809 :{RED}游客都饿了, 因为他们找不到卖食物的店舖 +STR_2810 :{RED}游客都渴了, 因为他们找不到卖饮料的店舖 +STR_2811 :{RED}游客都在抱怨乐园里找不到厕所 +STR_2812 :{RED}乐园内有游客被卡住或迷路{NEWLINE}请检查乐园的道路设计是否需要改善, 以便游客找到出路 +STR_2813 :{RED}你的游乐园门票太贵了!{NEWLINE}减低门费或增加乐园价值来吸引更多游客来访 +STR_2814 :{WINDOW_COLOUR_2}最肮脏乐园奖 STR_2815 :{WINDOW_COLOUR_2}最整洁乐园奖 -STR_2816 :{WINDOW_COLOUR_2}拥有最棒过山车乐园奖 +STR_2816 :{WINDOW_COLOUR_2}拥有最棒过山车奖 STR_2817 :{WINDOW_COLOUR_2}最有价值乐园奖 STR_2818 :{WINDOW_COLOUR_2}最美乐园奖 STR_2819 :{WINDOW_COLOUR_2}最差价值乐园奖 STR_2820 :{WINDOW_COLOUR_2}最安全乐园奖 -STR_2821 :{WINDOW_COLOUR_2}拥有最佳员工乐园奖 -STR_2822 :{WINDOW_COLOUR_2}拥有最佳食物乐园奖 -STR_2823 :{WINDOW_COLOUR_2}拥有最差食物乐园奖 -STR_2824 :{WINDOW_COLOUR_2}拥有最佳厕所乐园奖 +STR_2821 :{WINDOW_COLOUR_2}拥有最佳员工奖 +STR_2822 :{WINDOW_COLOUR_2}拥有最佳食物奖 +STR_2823 :{WINDOW_COLOUR_2}拥有最差食物奖 +STR_2824 :{WINDOW_COLOUR_2}拥有最佳厕所奖 STR_2825 :{WINDOW_COLOUR_2}最让人失望乐园奖 -STR_2826 :{WINDOW_COLOUR_2}拥有最佳水上游乐设施乐园奖 -STR_2827 :{WINDOW_COLOUR_2}拥有最佳自行设计的游乐设施乐园奖 -STR_2828 :{WINDOW_COLOUR_2}拥有最大胆色彩搭配乐园奖 -STR_2829 :{WINDOW_COLOUR_2}拥有最令人迷失布局乐园奖 -STR_2830 :{WINDOW_COLOUR_2}拥有最温和游乐设施乐园奖 -STR_2831 :{TOPAZ}你的游乐园获得了'全国最杂乱无章乐园奖'! +STR_2826 :{WINDOW_COLOUR_2}拥有最佳水上游乐设施奖 +STR_2827 :{WINDOW_COLOUR_2}拥有最佳自行设计的游乐设施奖 +STR_2828 :{WINDOW_COLOUR_2}拥有最大胆色彩搭配奖 +STR_2829 :{WINDOW_COLOUR_2}拥有最令人迷失布局奖 +STR_2830 :{WINDOW_COLOUR_2}拥有最温和游乐设施奖 +STR_2831 :{TOPAZ}你的游乐园获得了'全国最肮脏乐园奖'! STR_2832 :{TOPAZ}你的游乐园获得了'全国最整洁乐园奖'! -STR_2833 :{TOPAZ}你的游乐园获得了'拥有最棒过山车乐园奖'! +STR_2833 :{TOPAZ}你的游乐园获得了'拥有最棒过山车奖'! STR_2834 :{TOPAZ}你的游乐园获得了'全国最有价值乐园奖'! STR_2835 :{TOPAZ}你的游乐园获得了'全国最美乐园奖'! STR_2836 :{TOPAZ}你的游乐园获得了'全国最差价值乐园奖'! STR_2837 :{TOPAZ}你的游乐园获得了'全国最安全乐园奖'! -STR_2838 :{TOPAZ}你的游乐园获得了'拥有最佳员工乐园奖'! -STR_2839 :{TOPAZ}你的游乐园获得了'拥有最佳食物乐园奖'! -STR_2840 :{TOPAZ}你的游乐园获得了'拥有最差食物乐园奖'! -STR_2841 :{TOPAZ}你的游乐园获得了'拥有最佳厕所乐园奖'! +STR_2838 :{TOPAZ}你的游乐园获得了'拥有最佳员工奖'! +STR_2839 :{TOPAZ}你的游乐园获得了'拥有最佳食物奖'! +STR_2840 :{TOPAZ}你的游乐园获得了'拥有最差食物奖'! +STR_2841 :{TOPAZ}你的游乐园获得了'拥有最佳厕所奖'! STR_2842 :{TOPAZ}你的游乐园获得了'全国最让人失望乐园奖'! -STR_2843 :{TOPAZ}你的游乐园获得了'拥有最佳水上游乐设施乐园奖'! -STR_2844 :{TOPAZ}你的游乐园获得了'拥有最佳自行设计的游乐设施乐园奖'! -STR_2845 :{TOPAZ}你的游乐园获得了'拥有最大胆色彩搭配乐园奖'! -STR_2846 :{TOPAZ}你的游乐园获得了'拥有最令人迷失布局乐园奖'! -STR_2847 :{TOPAZ}你的游乐园获得了'拥有最温和游乐设施乐园奖'! +STR_2843 :{TOPAZ}你的游乐园获得了'拥有最佳水上游乐设施奖'! +STR_2844 :{TOPAZ}你的游乐园获得了'拥有最佳自行设计的游乐设施奖'! +STR_2845 :{TOPAZ}你的游乐园获得了'拥有最大胆色彩搭配奖'! +STR_2846 :{TOPAZ}你的游乐园获得了'拥有最令人迷失布局奖'! +STR_2847 :{TOPAZ}你的游乐园获得了'拥有最温和游乐设施奖'! STR_2848 :{WINDOW_COLOUR_2}暂无获得任何奖项 STR_2849 :新的剧情副本安装完毕 STR_2850 :新的轨道设计安装完毕 @@ -2193,74 +2193,74 @@ STR_2858 :不能开始实行营销计划... STR_2861 :{WINDOW_COLOUR_2}Licensed to Infogrames Interactive Inc. STR_2862 :音乐鸣谢列表... STR_2863 :音乐鸣谢列表 -STR_2864 :{WINDOW_COLOUR_2}March - Children of the Regiment: (Fucik) non copyright +STR_2864 :{WINDOW_COLOUR_2}March - Children of the Regiment: (Fucik) 无版权 STR_2865 :{WINDOW_COLOUR_2}Heyken's Serenade: (J.Heyken) British Standard Music Coy; GEMA, BRITICO -STR_2866 :{WINDOW_COLOUR_2}La Belle Espagnole: (Robert Vollstedt) Copyright Control +STR_2866 :{WINDOW_COLOUR_2}La Belle Espagnole: (Robert Vollstedt) 版权控制 STR_2867 :{WINDOW_COLOUR_2}Wedding Journey: (Traditional) -STR_2868 :{WINDOW_COLOUR_2}Tales from the Vienna Woods: (Johann Strauss) non copyright +STR_2868 :{WINDOW_COLOUR_2}Tales from the Vienna Woods: (Johann Strauss) 无版权 STR_2869 :{WINDOW_COLOUR_2}Slavonic Dance: (Traditional) STR_2870 :{WINDOW_COLOUR_2}Das Alpenhorn: (Traditional) STR_2871 :{WINDOW_COLOUR_2}The Blond Sailor: (Traditional) -STR_2872 :{WINDOW_COLOUR_2}Overture - Poet and Peasant: (Suppe) non copyright -STR_2873 :{WINDOW_COLOUR_2}Waltz Medley: (Johann Strauss) non copyright +STR_2872 :{WINDOW_COLOUR_2}Overture - Poet and Peasant: (Suppe) 无版权 +STR_2873 :{WINDOW_COLOUR_2}Waltz Medley: (Johann Strauss) 无版权 STR_2874 :{WINDOW_COLOUR_2}Bella Bella Bimba: (Traditional) STR_2875 :{WINDOW_COLOUR_2}Original recordings (P) 1976 C.J.Mears Organization, used with consent -STR_2876 :{WINDOW_COLOUR_2}RollerCoaster Tycoon 2 Title Music: (Allister Brimble) copyright © Chris Sawyer -STR_2877 :{WINDOW_COLOUR_2}Dodgems Beat: (Allister Brimble) copyright © Chris Sawyer -STR_2878 :{WINDOW_COLOUR_2}Mid Summer's Heat: (Allister Brimble) copyright © Chris Sawyer -STR_2879 :{WINDOW_COLOUR_2}Pharaoh's Tomb: (Allister Brimble) copyright © Chris Sawyer -STR_2880 :{WINDOW_COLOUR_2}Caesar's March: (Allister Brimble) copyright © Chris Sawyer -STR_2881 :{WINDOW_COLOUR_2}Drifting To Heaven: (Allister Brimble) copyright © Chris Sawyer -STR_2882 :{WINDOW_COLOUR_2}Invaders: (Allister Brimble) copyright © Chris Sawyer -STR_2883 :{WINDOW_COLOUR_2}Eternal Toybox: (Allister Brimble) copyright © Chris Sawyer -STR_2884 :{WINDOW_COLOUR_2}Jungle Juice: (Allister Brimble) copyright © Chris Sawyer -STR_2885 :{WINDOW_COLOUR_2}Ninja's Noodles: (Allister Brimble) copyright © Chris Sawyer -STR_2886 :{WINDOW_COLOUR_2}Voyage to Andromeda: (Allister Brimble) copyright © Chris Sawyer -STR_2887 :{WINDOW_COLOUR_2}Brimble's Beat: (Allister Brimble) copyright © Chris Sawyer -STR_2888 :{WINDOW_COLOUR_2}Atlantis: (Allister Brimble) copyright © Chris Sawyer -STR_2889 :{WINDOW_COLOUR_2}Wild West Kid: (Allister Brimble) copyright © Chris Sawyer -STR_2890 :{WINDOW_COLOUR_2}Vampire's Lair: (Allister Brimble) copyright © Chris Sawyer -STR_2891 :{WINDOW_COLOUR_2}Blockbuster: (Allister Brimble) copyright © Chris Sawyer -STR_2892 :{WINDOW_COLOUR_2}Airtime Rock: (Allister Brimble) copyright © Chris Sawyer -STR_2893 :{WINDOW_COLOUR_2}Searchlight Rag: (Scott Joplin/Allister Brimble) copyright © Chris Sawyer -STR_2894 :{WINDOW_COLOUR_2}Flight of Fantasy: (Steve Blenkinsopp) copyright © Chris Sawyer -STR_2895 :{WINDOW_COLOUR_2}Big Rock: (Allister Brimble) copyright © Chris Sawyer -STR_2896 :{WINDOW_COLOUR_2}Hypothermia: (Allister Brimble) copyright © Chris Sawyer -STR_2897 :{WINDOW_COLOUR_2}Last Sleigh Ride: (Allister Brimble) copyright © Chris Sawyer -STR_2898 :{WINDOW_COLOUR_2}Pipes of Glencairn: (Allister Brimble) copyright © Chris Sawyer -STR_2899 :{WINDOW_COLOUR_2}Traffic Jam: (Allister Brimble) copyright © Chris Sawyer +STR_2876 :{WINDOW_COLOUR_2}RollerCoaster Tycoon 2 Title Music: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2877 :{WINDOW_COLOUR_2}Dodgems Beat: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2878 :{WINDOW_COLOUR_2}Mid Summer's Heat: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2879 :{WINDOW_COLOUR_2}Pharaoh's Tomb: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2880 :{WINDOW_COLOUR_2}Caesar's March: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2881 :{WINDOW_COLOUR_2}Drifting To Heaven: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2882 :{WINDOW_COLOUR_2}Invaders: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2883 :{WINDOW_COLOUR_2}Eternal Toybox: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2884 :{WINDOW_COLOUR_2}Jungle Juice: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2885 :{WINDOW_COLOUR_2}Ninja's Noodles: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2886 :{WINDOW_COLOUR_2}Voyage to Andromeda: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2887 :{WINDOW_COLOUR_2}Brimble's Beat: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2888 :{WINDOW_COLOUR_2}Atlantis: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2889 :{WINDOW_COLOUR_2}Wild West Kid: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2890 :{WINDOW_COLOUR_2}Vampire's Lair: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2891 :{WINDOW_COLOUR_2}Blockbuster: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2892 :{WINDOW_COLOUR_2}Airtime Rock: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2893 :{WINDOW_COLOUR_2}Searchlight Rag: (Scott Joplin/Allister Brimble) 版权所有 © Chris Sawyer +STR_2894 :{WINDOW_COLOUR_2}Flight of Fantasy: (Steve Blenkinsopp) 版权所有 © Chris Sawyer +STR_2895 :{WINDOW_COLOUR_2}Big Rock: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2896 :{WINDOW_COLOUR_2}Hypothermia: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2897 :{WINDOW_COLOUR_2}Last Sleigh Ride: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2898 :{WINDOW_COLOUR_2}Pipes of Glencairn: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2899 :{WINDOW_COLOUR_2}Traffic Jam: (Allister Brimble) 版权所有 © Chris Sawyer STR_2901 :{WINDOW_COLOUR_2}(Samples courtesy of Spectrasonics “Liquid Grooves”) STR_2902 :{WINDOW_COLOUR_2}Toccata: (C.M.Widor, played by Peter James Adcock) recording © Chris Sawyer -STR_2903 :{WINDOW_COLOUR_2}Space Rock: (Allister Brimble) copyright © Chris Sawyer -STR_2904 :{WINDOW_COLOUR_2}Manic Mechanic: (Allister Brimble) copyright © Chris Sawyer -STR_2905 :{WINDOW_COLOUR_2}Techno Torture: (Allister Brimble) copyright © Chris Sawyer -STR_2906 :{WINDOW_COLOUR_2}Sweat Dreams: (Allister Brimble) copyright © Chris Sawyer -STR_2907 :{WINDOW_COLOUR_2}What shall we do with the Drunken Sailor: (Anon/Allister Brimble) copyright © Chris Sawyer -STR_2971 :Main colour scheme -STR_2972 :Alternative colour scheme 1 -STR_2973 :Alternative colour scheme 2 -STR_2974 :Alternative colour scheme 3 -STR_2975 :{SMALLFONT}{BLACK}Select which colour scheme to change, or paint ride with -STR_2976 :{SMALLFONT}{BLACK}Paint an individual area of this ride using the selected colour scheme -STR_2977 :Staff member name -STR_2978 :Enter new name for this member of staff: -STR_2979 :Can't name staff member... -STR_2980 :Too many banners in game -STR_2981 :{RED}No entry - - -STR_2982 :Banner text -STR_2983 :Enter new text for this banner: -STR_2984 :Can't set new text for banner... -STR_2985 :Banner -STR_2986 :{SMALLFONT}{BLACK}Change text on banner -STR_2987 :{SMALLFONT}{BLACK}Set this banner as a 'no-entry' sign for guests -STR_2988 :{SMALLFONT}{BLACK}Demolish this banner -STR_2989 :{SMALLFONT}{BLACK}Select main colour -STR_2990 :{SMALLFONT}{BLACK}Select text colour -STR_2991 :Sign -STR_2992 :Sign text -STR_2993 :Enter new text for this sign: -STR_2994 :{SMALLFONT}{BLACK}Change text on sign -STR_2995 :{SMALLFONT}{BLACK}Demolish this sign +STR_2903 :{WINDOW_COLOUR_2}Space Rock: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2904 :{WINDOW_COLOUR_2}Manic Mechanic: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2905 :{WINDOW_COLOUR_2}Techno Torture: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2906 :{WINDOW_COLOUR_2}Sweat Dreams: (Allister Brimble) 版权所有 © Chris Sawyer +STR_2907 :{WINDOW_COLOUR_2}What shall we do with the Drunken Sailor: (Anon/Allister Brimble) 版权所有 © Chris Sawyer +STR_2971 :主要配色主题 +STR_2972 :额外配色主题1 +STR_2973 :额外配色主题2 +STR_2974 :额外配色主题3 +STR_2975 :{SMALLFONT}{BLACK}选取游乐设施的配色选项来修改 +STR_2976 :{SMALLFONT}{BLACK}将指定区域使用目前选择的配色主题 +STR_2977 :员工命名 +STR_2978 :请输入该员工的新名字: +STR_2979 :不能更改该员工姓名... +STR_2980 :游戏中有过多的横幅 +STR_2981 :{RED}游客止步 - - +STR_2982 :横幅内容 +STR_2983 :请输入新的内容: +STR_2984 :不能设置新的横幅内容... +STR_2985 :横幅 +STR_2986 :{SMALLFONT}{BLACK}修改横幅内容 +STR_2987 :{SMALLFONT}{BLACK}设置当前横幅为“游客止步” +STR_2988 :{SMALLFONT}{BLACK}拆除该横幅 +STR_2989 :{SMALLFONT}{BLACK}选择横幅配色 +STR_2990 :{SMALLFONT}{BLACK}选择文字配色 +STR_2991 :标志 +STR_2992 :标志内容 +STR_2993 :请输入新的标志内容: +STR_2994 :{SMALLFONT}{BLACK}修改标志的内容 +STR_2995 :{SMALLFONT}{BLACK}拆除该标志 STR_2996 :{BLACK}ABC STR_2997 :{GREY}ABC STR_2998 :{WHITE}ABC @@ -2288,10 +2288,10 @@ STR_3019 :童真风格 STR_3020 : STR_3021 :太空风格 STR_3022 :恐怖风格 -STR_3023 :Techno style +STR_3023 :科幻风格 STR_3024 :温和风格 STR_3025 :夏日风格 -STR_3026 :Water style +STR_3026 :水上风格 STR_3027 :西部荒野风格 STR_3028 :侏罗纪风格 STR_3029 :摇滚风格 @@ -2323,7 +2323,7 @@ STR_3058 :砖墙 STR_3059 :树篱 STR_3060 :冰砖墙 STR_3061 :木栅栏 -STR_3062 :{SMALLFONT}{BLACK}标准云霄飞车轨道 +STR_3062 :{SMALLFONT}{BLACK}标准过山车轨道 STR_3063 :{SMALLFONT}{BLACK}水溅轨道 (轨道半浸于水中) STR_3064 :新手级乐园 STR_3065 :挑战级乐园 @@ -2344,9 +2344,9 @@ STR_3091 :你无法删除此轨道! STR_3092 :你无法移动或修改这个游乐设施的车站! STR_3093 :{WINDOW_COLOUR_2}最爱: {BLACK}{STRINGID} STR_3094 :N/A -STR_3095 :{WINDOW_COLOUR_2}链条坡道的链条速度: +STR_3095 :{WINDOW_COLOUR_2}提升坡的上升速度: STR_3096 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{VELOCITY} -STR_3097 :{SMALLFONT}{BLACK}选择链条坡道的链条上拉速度 +STR_3097 :{SMALLFONT}{BLACK}选择提升坡的链条上拉速度 STR_3099 :{SMALLFONT}{BLACK}选择颜色 STR_3100 :{SMALLFONT}{BLACK}选择第二颜色 STR_3101 :{SMALLFONT}{BLACK}选择第三颜色 @@ -2369,7 +2369,7 @@ STR_3117 :{BLACK}调用维修人员中... STR_3118 :{BLACK}{STRINGID}正在前往到这个游乐设施 STR_3119 :{BLACK}{STRINGID}正在修复这个游乐设施 STR_3120 :{SMALLFONT}{BLACK}将最邻近, 或在维修此游乐设施的维修人员定位 -STR_3121 :无法将维修人员定位, 或者附近的维修人员都在工作中 +STR_3121 :无法定位维修人员, 或者附近的维修人员都在工作中 STR_3122 :{WINDOW_COLOUR_2}游客的最爱: {BLACK}{COMMA16}位游客 STR_3123 :{WINDOW_COLOUR_2}游客的最爱: {BLACK}{COMMA16}位游客 STR_3124 :故障的{STRINGID} @@ -2425,7 +2425,6 @@ STR_3190 :额外的道路对象 STR_3191 :景物组别 STR_3192 :乐园入口 STR_3193 :水塘 -STR_3194 :剧情简介 STR_3195 :研发列表 STR_3196 :{WINDOW_COLOUR_2}研发组别: {BLACK}{STRINGID} STR_3197 :{WINDOW_COLOUR_2}游戏开始时已研发好的项目: @@ -2462,7 +2461,7 @@ STR_3227 :太多乐园入口! STR_3228 :{SMALLFONT}{BLACK}设置游客的开始步行位置 STR_3229 :区域煞车不能建造在车站之后 STR_3230 :区域煞车不能建造在另一个区域煞车之后 -STR_3231 :区域煞车不能建造在这个链条坡道的顶部之后 +STR_3231 :区域煞车不能建造在这个提升坡的顶部之后 STR_3232 :选项 - 财政 STR_3233 :选项 - 游客 STR_3234 :选项 - 乐园 @@ -2544,7 +2543,7 @@ STR_3309 :{WINDOW_COLOUR_2}{COMMA16} STR_3310 :{WINDOW_COLOUR_2}{LENGTH} STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32} STR_3312 :{WINDOW_COLOUR_2}请选取要保留的游乐设施/店铺: -STR_3313 :剧情名称 +STR_3313 :剧情命名 STR_3314 :请输入这个剧情的名称: STR_3315 :乐园/剧情的简介 STR_3316 :请输入这个乐园/剧情的简介: @@ -2562,35 +2561,35 @@ STR_3330 :乐园必需有一些己拥有的土地 STR_3331 :由地图边缘到乐园入口的道路尚未连接好, 或者太复杂 - 这些道路必需是一格宽度, 并以越少交界及弯位越好 STR_3332 :乐园入口倒转了, 或没有道路连接至地图边缘 STR_3333 :在存档中导出附加物 -STR_3334 :{SMALLFONT}{BLACK}Select whether to save any additional plug-in object data required (add-in data not supplied with the main product) in saved game or scenario files, allowing them to be loaded by someone who doesn't have the additional object data +STR_3334 :{SMALLFONT}{BLACK}选择是否存储插件的储存文件,以便那些没有插件的玩家载入存档 STR_3335 :轨道设计工具 - 选择游乐设施的类型及车辆种类 STR_3336 :轨道设计管理工具 - 选择游乐设施的类型 -STR_3338 :{BLACK}Custom-designed layout -STR_3339 :{BLACK}{COMMA16} design available, or custom-designed layout -STR_3340 :{BLACK}{COMMA16} designs available, or custom-designed layout +STR_3338 :{BLACK}自定义布局 +STR_3339 :{BLACK}{COMMA16} 预设设计 或者 自定布局 +STR_3340 :{BLACK}{COMMA16} 预设设计 或者 自定布局 STR_3341 :{SMALLFONT}{BLACK}游戏工具 STR_3342 :剧情编辑器 STR_3343 :将游戏存档转换成剧情关 STR_3344 :轨道设计工具 STR_3345 :轨道设计管理工具 STR_3346 :不能保存轨道设计... -STR_3347 :Ride is too large, contains too many elements, or scenery is too spread out +STR_3347 :游乐设施过于庞大,包含太多要素或者景物过于分散 STR_3348 :重命名 STR_3349 :删除 -STR_3350 :轨道设计名称 +STR_3350 :轨道设计命名 STR_3351 :请输入此轨道设计的新名称: STR_3352 :不能重命名此轨道设计... STR_3353 :新名称包含不可用的字符 -STR_3354 :Another file exists with this name, or file is write-protected -STR_3355 :File is write-protected or locked -STR_3356 :Delete File -STR_3357 :{WINDOW_COLOUR_2}Are you sure you want to permanently delete {STRING} ? -STR_3358 :Can't delete track design... -STR_3359 :{BLACK}No track designs of this type -STR_3360 :Warning! -STR_3361 :Too many track designs of this type - Some will not be listed. -STR_3364 :Advanced -STR_3365 :{SMALLFONT}{BLACK}Allow selection of individual items of scenery in addition to scenery groups +STR_3354 :已有相同名称的文件,或文件有写入保护 +STR_3355 :文件有写入保护或被锁定 +STR_3356 :删除文件 +STR_3357 :{WINDOW_COLOUR_2}你确定要永久删除{STRING} ? +STR_3358 :无法删除轨道设计... +STR_3359 :{BLACK}该设施没有已设计好的轨道设计 +STR_3360 :警告! +STR_3361 :该设施有太多设计-列表不会完全列出 +STR_3364 :进阶 +STR_3365 :{SMALLFONT}{BLACK}在选择景物类别之外,允许选择单项景物 STR_3366 :{BLACK}= 游乐设施 STR_3367 :{BLACK}= 食品店 STR_3368 :{BLACK}= 饮品店 @@ -2614,39 +2613,39 @@ STR_3389 :无法选择额外的景观对象... STR_3390 :选取了太多对象 # Start of tutorial strings. Not used at the moment, so not necessary to translate. # End of tutorial strings -STR_3437 :{SMALLFONT}{BLACK}在地形中清理大范围内的景物Clear large areas of scenery from landscape -STR_3438 :Unable to remove all scenery from here... -STR_3439 :Clear Scenery -STR_3440 :Page 1 -STR_3441 :Page 2 -STR_3442 :Page 3 -STR_3443 :Page 4 -STR_3444 :Page 5 -STR_3445 :Set Patrol Area -STR_3446 :Cancel Patrol Area +STR_3437 :{SMALLFONT}{BLACK}在地形中清理大范围内的景物 +STR_3438 :无法移除这里的所有景物... +STR_3439 :移除景物 +STR_3440 :第1页 +STR_3441 :第2页 +STR_3442 :第3页 +STR_3443 :第4页 +STR_3444 :第5页 +STR_3445 :设置巡逻范围 +STR_3446 :取消巡逻范围 # New strings, cleaner STR_5120 :财务 STR_5121 :研发 STR_5122 :根据轨道类型选择游乐设施 (像RCT1一样) -STR_5123 :Renew rides -STR_5125 :All destructable +STR_5123 :翻新游乐设施 +STR_5125 :全可拆除 STR_5126 :随机主题音乐 -STR_5127 :{SMALLFONT}{BLACK}While dragging, paint landscape instead of changing elevation -STR_5128 :Selection size -STR_5129 :Enter selection size between {COMMA16} and {COMMA16} -STR_5130 :Map size -STR_5131 :Enter map size between {COMMA16} and {COMMA16} -STR_5132 :Fix all rides +STR_5127 :{SMALLFONT}{BLACK}拖曳调整地形, 而不调整地势 +STR_5128 :选择大小 +STR_5129 :请输入介于{COMMA16}-{COMMA16}之间的数字 +STR_5130 :地图大小 +STR_5131 :请输入介于{COMMA16}-{COMMA16}之间的数字 +STR_5132 :维修全部游乐设施 STR_5133 :{SMALLFONT}{BLACK}选择较小一些的土地范围 STR_5134 :{SMALLFONT}{BLACK}选择较大一些的土地范围 -STR_5135 :{SMALLFONT}{BLACK}Buy land rights and construction rights -STR_5136 :Land rights -STR_5137 :Unlock operating limits +STR_5135 :{SMALLFONT}{BLACK}购买土地权和建造权 +STR_5136 :土地权 +STR_5137 :解锁操作限制 STR_5138 :{SMALLFONT}{WINDOW_COLOUR_2}{STRINGID} STR_5139 :{WHITE}{STRINGID} -STR_5140 :Disable brakes failure -STR_5141 :Disable all breakdowns +STR_5140 :禁用制动器故障 +STR_5141 :禁用所有故障 STR_5142 :正常速度 STR_5143 :稍快速度 STR_5144 :加快速度 @@ -2682,10 +2681,10 @@ STR_5174 :{SMALLFONT}{BLACK}将Twitch聊天中所有标'!news'的内容以通 STR_5175 :请输入你的Twitch频道名称 STR_5176 :允许Twitch与游戏集成 STR_5177 :显示模式: -STR_5178 :{SMALLFONT}{BLACK}Show financial cheats -STR_5179 :{SMALLFONT}{BLACK}Show guest cheats -STR_5180 :{SMALLFONT}{BLACK}Show park cheats -STR_5181 :{SMALLFONT}{BLACK}Show ride cheats +STR_5178 :{SMALLFONT}{BLACK}显示财务秘籍 +STR_5179 :{SMALLFONT}{BLACK}显示游客秘籍 +STR_5180 :{SMALLFONT}{BLACK}显示乐园秘籍 +STR_5181 :{SMALLFONT}{BLACK}显示设备秘籍 STR_5182 :{INT32} STR_5183 :地面高度 STR_5184 :请输入介乎{COMMA16}及{COMMA16}的地面高度 @@ -2713,7 +2712,7 @@ STR_5205 :游客 STR_5206 :游客列表 STR_5207 :员工 STR_5208 :员工列表 -STR_5209 :横额 +STR_5209 :横幅 STR_5210 :选取对象 STR_5211 :研发列表 STR_5212 :剧情选项 @@ -2743,33 +2742,33 @@ STR_5235 :{SMALLFONT}{BLACK}设置 STR_5236 :Window STR_5237 :调色板 STR_5238 :当前主题: -STR_5239 :Duplicate -STR_5240 :Enter a name for the theme -STR_5241 :Can't change this theme -STR_5242 :Theme name already exists -STR_5243 :Invalid characters used +STR_5239 :复制 +STR_5240 :请输入该主题的名称 +STR_5241 :不能更改主题 +STR_5242 :主题名称已使用 +STR_5243 :使用了无效字符 STR_5244 :主题 -STR_5245 :Top Toolbar -STR_5246 :Bottom Toolbar -STR_5247 :Track Editor Bottom Toolbar -STR_5248 :Scenario Editor Bottom Toolbar -STR_5249 :Title Menu Buttons -STR_5250 :Title Exit Button -STR_5251 :Title Options Button -STR_5252 :Title Scenario Selection -STR_5253 :Park Information -STR_5254 :Create -STR_5255 :{SMALLFONT}{BLACK}Create a new title sequence from scratch -STR_5256 :Create a new theme to make changes to -STR_5257 :{SMALLFONT}{BLACK}Create a new theme based on the current one -STR_5258 :{SMALLFONT}{BLACK}Delete the current theme -STR_5259 :{SMALLFONT}{BLACK}Rename the current theme +STR_5245 :顶部工具栏 +STR_5246 :底部工具栏 +STR_5247 :轨道编辑器底部工具栏 +STR_5248 :场景编辑器底部工具栏 +STR_5249 :主界面菜单按钮 +STR_5250 :主界面退出按钮 +STR_5251 :主界面选项按钮 +STR_5252 :主界面剧情选择按钮 +STR_5253 :乐园信息 +STR_5254 :创建 +STR_5255 :{SMALLFONT}{BLACK}建立全新的主界面动画 +STR_5256 :创建新的主题用于 +STR_5257 :{SMALLFONT}{BLACK}基于当前主题创建新主题 +STR_5258 :{SMALLFONT}{BLACK}删除当前主题 +STR_5259 :{SMALLFONT}{BLACK}重命名当前主题 STR_5260 :大型截图 -STR_5261 :Filter -STR_5262 :Wacky Worlds -STR_5263 :Time Twister -STR_5264 :Custom -STR_5265 :{SMALLFONT}{BLACK}Select which content sources are visible +STR_5261 :过滤 +STR_5262 :怪诞世界 +STR_5263 :扭曲时间 +STR_5264 :自定义 +STR_5265 :{SMALLFONT}{BLACK}选择哪些内容为可视 STR_5266 :{SMALLFONT}{BLACK}显示 STR_5267 :{SMALLFONT}{BLACK}语言及单位 STR_5268 :{SMALLFONT}{BLACK}音效 @@ -2784,74 +2783,74 @@ STR_5276 :输入名称以搜索物件 STR_5277 :清除 STR_5278 :沙盒模式 STR_5279 :关闭沙盒模式 -STR_5280 :{SMALLFONT}{BLACK}Allow editing land ownership settings through the Map window and other options that are normally restricted to the Scenario Editor -STR_5281 :{SMALLFONT}{BLACK}Features -STR_5282 :RCT1 Ride Open/Close Lights -STR_5283 :RCT1 Park Open/Close Lights -STR_5284 :RCT1 Scenario Selection Font +STR_5280 :{SMALLFONT}{BLACK}允许从地图视窗中修改土地拥有权, 以及其他平常只适用于剧情编辑工具的选项 +STR_5281 :{SMALLFONT}{BLACK}功能 +STR_5282 :过山车大亨1式游乐设施开关灯 +STR_5283 :过山车大亨1式乐园开关开关灯 +STR_5284 :过山车大亨1式选择剧情字体 STR_5285 :爆炸!!! STR_5286 :{SMALLFONT}{BLACK}让一些游客爆炸 -STR_5287 :Ride is already broken down -STR_5288 :Ride is closed -STR_5289 :No breakdowns available for this ride -STR_5290 :Fix ride -STR_5291 :Can't force breakdown -STR_5292 :{SMALLFONT}{BLACK}Force a breakdown -STR_5293 :{SMALLFONT}{BLACK}Close ride/attraction -STR_5294 :{SMALLFONT}{BLACK}Test ride/attraction -STR_5295 :{SMALLFONT}{BLACK}Open ride/attraction -STR_5296 :{SMALLFONT}{BLACK}Close park -STR_5297 :{SMALLFONT}{BLACK}Open park +STR_5287 :游乐设施已经故障 +STR_5288 :游乐设施已关闭 +STR_5289 :没有故障适用于该设施 +STR_5290 :维修游乐设施 +STR_5291 :不能强制设施故障 +STR_5292 :{SMALLFONT}{BLACK}强制故障 +STR_5293 :{SMALLFONT}{BLACK}关闭游乐设施/店铺 +STR_5294 :{SMALLFONT}{BLACK}测试游乐设施/店铺 +STR_5295 :{SMALLFONT}{BLACK}开启游乐设施/店铺 +STR_5296 :{SMALLFONT}{BLACK}关闭乐园 +STR_5297 :{SMALLFONT}{BLACK}开启乐园 STR_5298 :{RED}{STRINGID} STR_5299 :{LIGHTPINK}{STRINGID} -STR_5300 :{SMALLFONT}{BLACK}Quick fire staff -STR_5301 :{MEDIUMFONT}{BLACK}Clear your loan -STR_5302 :Clear loan -STR_5303 :Allow building in pause mode +STR_5300 :{SMALLFONT}{BLACK}快速解雇员工 +STR_5301 :{MEDIUMFONT}{BLACK}清除你的贷款 +STR_5302 :清除贷款 +STR_5303 :允许在暂停模式下继续建造 STR_5304 :主菜单动画: STR_5305 :过山车大亨1(RCT1) STR_5306 :过山车大亨1 (+资料片1 - AA) STR_5307 :过山车大亨1 (+资料片1&2 - AA+LL) STR_5308 :过山车大亨2(RCT2) STR_5309 :OpenRCT2 -STR_5310 :Random +STR_5310 :随机 STR_5311 :{SMALLFONT}{BLACK}调试工具 STR_5312 :显示控制台 -STR_5313 :Show tile inspector -STR_5314 :Tile inspector +STR_5313 :显示地图网格检查工具 +STR_5314 :地图网格检查工具 STR_5315 :草地 STR_5316 :沙地 STR_5317 :泥地 STR_5318 :石地 -STR_5319 :火星地 -STR_5320 :Checkerboard -STR_5321 :Grass clumps -STR_5322 :Ice -STR_5323 :Grid (red) -STR_5324 :Grid (yellow) -STR_5325 :Grid (blue) -STR_5326 :Grid (green) -STR_5327 :Sand (dark) -STR_5328 :Sand (light) -STR_5329 :Checkerboard (inverted) -STR_5330 :Underground view -STR_5331 :Rock -STR_5332 :Wood (red) -STR_5333 :Wood (black) -STR_5334 :Ice -STR_5335 :Ride entrance -STR_5336 :Ride exit -STR_5337 :Park entrance -STR_5338 :Element type -STR_5339 :{SMALLFONT}{BLACK}Base height -STR_5340 :{SMALLFONT}{BLACK}Clearance height +STR_5319 :火星 +STR_5320 :棋盘 +STR_5321 :草丛 +STR_5322 :雪地 +STR_5323 :网格 (红) +STR_5324 :网格 (黄) +STR_5325 :网格 (蓝) +STR_5326 :网格 (绿) +STR_5327 :沙地 (较暗) +STR_5328 :沙地 (较浅) +STR_5329 :棋盘 (反转) +STR_5330 :地下视图 +STR_5331 :石地 +STR_5332 :森林 (红) +STR_5333 :森林 (黑) +STR_5334 :雪地 +STR_5335 :设施入口 +STR_5336 :设施出口 +STR_5337 :乐园入口 +STR_5338 :元素类型 +STR_5339 :{SMALLFONT}{BLACK}基础高度 +STR_5340 :{SMALLFONT}{BLACK}清理高度 STR_5343 :自动放置员工 STR_5344 :更新日志 -STR_5345 :Financial cheats -STR_5346 :Guest cheats -STR_5347 :Park cheats -STR_5348 :Ride cheats -STR_5349 :{SMALLFONT}{BLACK}All Rides +STR_5345 :财政秘籍 +STR_5346 :游客秘籍 +STR_5347 :乐园秘籍 +STR_5348 :设施秘籍 +STR_5349 :{SMALLFONT}{BLACK}全部游乐设施 STR_5350 :最大 STR_5351 :最小 STR_5352 :{BLACK}兴奋度: @@ -2879,50 +2878,50 @@ STR_5373 :名称 {STRINGID} STR_5374 :日期 {STRINGID} STR_5375 :▲ STR_5376 :▼ -STR_5377 :{SMALLFONT}{BLACK}Saves -STR_5378 :{SMALLFONT}{BLACK}Script -STR_5379 :{SMALLFONT}{BLACK}Skip to next wait command -STR_5380 :{SMALLFONT}{BLACK}Start playing title sequence -STR_5381 :{SMALLFONT}{BLACK}Stop playing title sequence -STR_5382 :{SMALLFONT}{BLACK}Restart title sequence -STR_5383 :{SMALLFONT}{BLACK}Create a new title sequence based on the current one -STR_5384 :{SMALLFONT}{BLACK}Delete the current title sequence -STR_5385 :{SMALLFONT}{BLACK}Rename the current title sequence -STR_5386 :{SMALLFONT}{BLACK}Insert a new command -STR_5387 :{SMALLFONT}{BLACK}Edit the selected command -STR_5388 :{SMALLFONT}{BLACK}Delete the selected command -STR_5389 :{SMALLFONT}{BLACK}Skip to the selected command in the title sequence -STR_5390 :{SMALLFONT}{BLACK}Move the selected command down -STR_5391 :{SMALLFONT}{BLACK}Move the selected command up -STR_5392 :{SMALLFONT}{BLACK}Add a save to the title sequence -STR_5393 :{SMALLFONT}{BLACK}Remove the selected save from the title sequence -STR_5394 :{SMALLFONT}{BLACK}Rename the selected save -STR_5395 :{SMALLFONT}{BLACK}Load the selected save in game -STR_5396 :{SMALLFONT}{BLACK}Reload the title sequence if changes have been made to it outside of the game -STR_5397 :Can only be used on the title screen -STR_5398 :Cannot edit title sequence while it's playing -STR_5399 :Press the stop button to continue editing -STR_5400 :Can't change this title sequence -STR_5401 :Create a new title sequence to make changes to -STR_5402 :Failed to load title sequence -STR_5403 :There may be no Load or Wait command or a save may be invalid -STR_5404 :Name already exists -STR_5405 :Enter a name for the save -STR_5406 :Enter a name for the title sequence -STR_5407 :Add -STR_5408 :Remove -STR_5409 :Insert -STR_5410 :Edit -STR_5411 :Reload -STR_5412 :Skip to -STR_5413 :Load -STR_5414 :Load{MOVE_X}{87}Six Flags Magic Mountain.SC6 -STR_5415 :Load{MOVE_X}{87}{STRING} -STR_5416 :Load{MOVE_X}{87}No save selected -STR_5417 :Location -STR_5418 :Location{MOVE_X}{87}{COMMA16} {COMMA16} -STR_5419 :Rotate -STR_5420 :Rotate{MOVE_X}{87}{COMMA16} +STR_5377 :{SMALLFONT}{BLACK}保存 +STR_5378 :{SMALLFONT}{BLACK}脚本 +STR_5379 :{SMALLFONT}{BLACK}跳到下一个等待命令 +STR_5380 :{SMALLFONT}{BLACK}开始播放此菜单动画 +STR_5381 :{SMALLFONT}{BLACK}停止播放此菜单动画 +STR_5382 :{SMALLFONT}{BLACK}重新开始播放此菜单动画 +STR_5383 :{SMALLFONT}{BLACK}基于当前菜单动画创建新菜单动画 +STR_5384 :{SMALLFONT}{BLACK}删除当前菜单动画 +STR_5385 :{SMALLFONT}{BLACK}重命名当前菜单动画 +STR_5386 :{SMALLFONT}{BLACK}插入新命令 +STR_5387 :{SMALLFONT}{BLACK}编辑选择的命令 +STR_5388 :{SMALLFONT}{BLACK}删除选择的命令 +STR_5389 :{SMALLFONT}{BLACK}跳过选择的命令 +STR_5390 :{SMALLFONT}{BLACK}向下移动选择的命令 +STR_5391 :{SMALLFONT}{BLACK}向上移动选择的命令 +STR_5392 :{SMALLFONT}{BLACK}在菜单动画上添加存档 +STR_5393 :{SMALLFONT}{BLACK}移除选中的存档 +STR_5394 :{SMALLFONT}{BLACK}重命名选择的存档 +STR_5395 :{SMALLFONT}{BLACK}在游戏中载入选中的存档 +STR_5396 :{SMALLFONT}{BLACK}重载菜单动画,以便显示游戏外所做的改动 +STR_5397 :只能用在主菜单中 +STR_5398 :不能在菜单动画播放时修改 +STR_5399 :点击停止按钮来继续编辑 +STR_5400 :不能修改改菜单动画 +STR_5401 :创建新的菜单动画来 +STR_5402 :载入菜单动画失败 +STR_5403 :无效存档或者没有“载入”或“等待”命令 +STR_5404 :名称已被使用 +STR_5405 :请输入存档名称: +STR_5406 :请输入菜单动画存档名称 +STR_5407 :添加 +STR_5408 :移除 +STR_5409 :插入 +STR_5410 :编辑 +STR_5411 :重载 +STR_5412 :转跳至 +STR_5413 :加载 +STR_5414 :加载{MOVE_X}{87}大型六旗游乐园.SC6 +STR_5415 :加载{MOVE_X}{87}{STRING} +STR_5416 :加载{MOVE_X}{87}无存档被选中 +STR_5417 :定位 +STR_5418 :定位{MOVE_X}{87}{COMMA16} {COMMA16} +STR_5419 :旋转 +STR_5420 :旋转{MOVE_X}{87}{COMMA16} STR_5421 :放大 STR_5422 :放大{MOVE_X}{87}{COMMA16} STR_5423 :等待 @@ -2935,16 +2934,16 @@ STR_5429 :放大级别: STR_5430 :等待毫秒数: STR_5431 :要读取的存档: STR_5432 :命令: -STR_5433 :Title Sequences +STR_5433 :菜单动画 STR_5434 :命令编辑器 STR_5435 :重命名存档 STR_5436 :编辑主菜单动画... STR_5437 :暂无存档被选中 STR_5438 :当命令编辑器打开时不能做出改变 -STR_5439 :A wait command with at least 4 seconds is required with a restart command +STR_5439 :重新开始(Restart)命令需要加上4秒以上的等待(Wait)命令 STR_5440 :当焦点失去时最小化全屏幕的游戏 STR_5441 :{SMALLFONT}{BLACK}根据轨道类型区分游乐设施{NEWLINE}使得车辆类型可以在建造后更改{NEWLINE}(过山车大亨1行为). -STR_5442 :Force park rating: +STR_5442 :强制乐园评价: STR_5443 :速度{MOVE_X}{87}{STRINGID} STR_5444 :速度: STR_5445 :速度 @@ -2977,7 +2976,7 @@ STR_5471 :向下滚动地图 STR_5472 :向右滚动地图 STR_5473 :日夜循环 STR_5474 :横幅上显示全大写英文 -STR_5475 :{COMMA16} weeks +STR_5475 :{COMMA16} 周 STR_5476 :硬件 STR_5477 :地图显示 STR_5478 :控制 @@ -3048,7 +3047,7 @@ STR_5544 :{SMALLFONT}{BLACK}亮红色 STR_5545 :{SMALLFONT}{BLACK}暗红色 STR_5546 :{SMALLFONT}{BLACK}亮粉色 STR_5547 :{SMALLFONT}{BLACK}浅粉色 -STR_5548 :Show all operating modes +STR_5548 :显示所有运行模式 STR_5549 :年/月/日 STR_5550 :{POP16}{POP16}{COMMA16}年, {PUSH16}{PUSH16}{MONTH} {PUSH16}{PUSH16}{STRINGID} STR_5551 :年/日/月 @@ -3067,10 +3066,10 @@ STR_5563 :此功能暂未稳定地使用,请多加留意. STR_5564 :插入已损坏元素 STR_5565 :{SMALLFONT}{BLACK}在一个网格的最上插入一个已损坏的地图元素.此插入会隐藏其他元素. STR_5566 :密码: -STR_5567 :Advertise +STR_5567 :广告 STR_5568 :需要密码 STR_5569 :此服务器需要密码 -STR_5570 :Fetch Servers +STR_5570 :获取服务器 STR_5571 :加入游戏 STR_5572 :添加至收藏 STR_5573 :从收藏中去除 @@ -3112,7 +3111,7 @@ STR_5608 :BH STR_5609 :CH STR_5610 :{SMALLFONT}{BLACK}移除选中的地图元素. 使用时请注意. 因为强制移除地图元素并不会对游戏中的现金做成影响. STR_5611 :G -STR_5612 :{SMALLFONT}{BLACK}Ghost flag +STR_5612 :{SMALLFONT}{BLACK}鬼旗 STR_5613 :B STR_5614 :{SMALLFONT}{BLACK}损坏标记 STR_5615 :L @@ -3135,8 +3134,8 @@ STR_5631 :原版DLC游乐园 STR_5632 :修建你自己的... STR_5633 :CMD + STR_5634 :OPTION + -STR_5635 :{WINDOW_COLOUR_2}Money spent: {BLACK}{CURRENCY2DP} -STR_5636 :{WINDOW_COLOUR_2}Commands ran: {BLACK}{COMMA16} +STR_5635 :{WINDOW_COLOUR_2}花费: {BLACK}{CURRENCY2DP} +STR_5636 :{WINDOW_COLOUR_2}命令运行: {BLACK}{COMMA16} STR_5637 :不能做此行为... STR_5638 :权限不够 STR_5639 :{SMALLFONT}{BLACK}显示玩家列表 @@ -3146,9 +3145,9 @@ STR_5642 :组 STR_5643 :添加组 STR_5644 :删除组 STR_5645 :聊天 -STR_5646 :Terraform +STR_5646 :修改地势 STR_5647 :切换暂停 -STR_5648 :Set Water Level +STR_5648 :设置水位 STR_5649 :创建游乐设施 STR_5650 :移除游乐设施 STR_5651 :修建游乐设施 @@ -3166,10 +3165,10 @@ STR_5661 :设置玩家组 STR_5662 :N/A STR_5663 :清除景物 STR_5664 :作弊 -STR_5665 :Toggle Scenery Cluster +STR_5665 :切换风景群集 STR_5666 :无密码登录 STR_5701 :{WINDOW_COLOUR_2}最近的行为: {BLACK}{STRINGID} -STR_5702 :{SMALLFONT}{BLACK}Locate player's most recent action +STR_5702 :{SMALLFONT}{BLACK}定位玩家最近的动作 STR_5703 :不能踢出创建者 STR_5704 :最近的行为 STR_5705 :不能将此玩家分配到该组 @@ -3207,10 +3206,10 @@ STR_5737 :已关闭, {COMMA16} 个游客仍在此游乐设施上 STR_5738 :已关闭, {COMMA16} 个游客仍在此游乐设施上 STR_5739 :{WINDOW_COLOUR_2}此游乐设施上的游客: {BLACK}{COMMA16} STR_5740 :永不结束的市场推广计划 -STR_5741 :{SMALLFONT}{BLACK}Marketing campaigns never end +STR_5741 :{SMALLFONT}{BLACK}推广计划将不断进行下去 STR_5742 :认证中 ... STR_5743 :连接中 ... -STR_5744 :Resolving ... +STR_5744 :解析中 ... STR_5745 :检测到网络不同步 STR_5746 :已断线 STR_5747 :已断线: {STRING} @@ -3413,7 +3412,7 @@ STR_5945 :{WINDOW_COLOUR_2}已连接的边缘: STR_5946 :{WINDOW_COLOUR_2}游乐设施种类:{BLACK}{STRINGID} STR_5947 :{WINDOW_COLOUR_2}游乐设施ID:{BLACK}{COMMA16} STR_5948 :{WINDOW_COLOUR_2}游乐设施名称:{BLACK}{STRINGID} -STR_5949 :{WINDOW_COLOUR_2}链条坡道 +STR_5949 :{WINDOW_COLOUR_2}提升坡 STR_5950 :{WINDOW_COLOUR_2}将改变应用于整个轨道组件中 STR_5951 :{WINDOW_COLOUR_2}轨道组件ID:{BLACK}{COMMA16} STR_5952 :{WINDOW_COLOUR_2}编号:{BLACK}{COMMA16} @@ -3569,12 +3568,12 @@ STR_6101 :游乐设施不会随时间流逝而贬值 STR_6102 :{SMALLFONT}{BLACK}游乐设施的价值不会随时间流逝而贬值, 因此游客不会产生玩此游乐设施太贵的想法. STR_6103 :{SMALLFONT}{BLACK}此选项在联网模式下被禁用. STR_6104 :螺旋过山车 -STR_6105 :Hypercoaster +STR_6105 :超级过山车 STR_6106 :轨道小车 -STR_6107 :Monster Trucks +STR_6107 :怪兽卡车 STR_6108 :钢架旋转式 STR_6109 :超旋转式 -STR_6110 :Junior过山车 +STR_6110 :儿童过山车 STR_6111 :经典迷你过山车 STR_6112 :一种紧凑的钢制轨道过山车, 其车厢穿越螺旋轨道和回旋轨道 STR_6113 :一种高大的非翻转的过山车, 拥有巨大降落, 高速和只有围栏限制的舒适的车厢 @@ -3594,8 +3593,8 @@ STR_6125 :对象类型 STR_6126 :未知类型 STR_6127 :文件: {STRING} STR_6128 :文件不能被加载, 因为其中一部分引用的对象丢失或错误. 以下是这些对象的列表. -STR_6129 :将选中的项目复制到剪贴板 -STR_6130 :将整个列表复制到剪贴板 +STR_6129 :复制 +STR_6130 :复制全部 STR_6131 :对象源 STR_6132 :忽略研究状态 STR_6133 :{SMALLFONT}{BLACK}使用还未被研究出的游乐设施和景观 @@ -3686,7 +3685,7 @@ STR_6217 :游乐设施 / 轨道可用性 STR_6218 :OpenRCT2官方 STR_6219 :高亮道路相关物 STR_6220 :使之可用 -STR_6221 :{SMALLFONT}{BLACK}This will set the ride's known entrance or exit location to the currently selected tile. 每个站台只能有一个入口或出口使之可用. +STR_6221 :{SMALLFONT}{BLACK}每个站台只能有一个入口或出口可用. STR_6222 :无法在此放置游客入口... STR_6223 :必须在游乐园边界外! STR_6224 :{STRING}放置一个游客入口. @@ -3739,8 +3738,39 @@ STR_6270 :地表 STR_6271 :地表边缘 STR_6272 :站台 STR_6273 :音乐 -STR_6274 :无法设置配色方案... +STR_6274 :无法设置配色主题... STR_6275 :{WINDOW_COLOUR_2}车站类型: +STR_6276 :{RED}{STRINGID} 有游客被困住了,这可能是因为无效的设施类型或者运行模式 +STR_6277 :{WINDOW_COLOUR_2}车站编号: {BLACK}{COMMA16} +STR_6278 :自动保存数量 +STR_6279 :{SMALLFONT}{BLACK}应该保留的自动保存数量 +STR_6284 :网络 +STR_6285 :网络信息 +STR_6286 :接收 +STR_6287 :发送 +STR_6288 :已接收 +STR_6289 :已发生 +STR_6290 :基础协议 +STR_6291 :指令 +STR_6292 :测绘 +STR_6293 :B +STR_6294 :KiB +STR_6295 :MiB +STR_6296 :GiB +STR_6297 :TiB +STR_6298 :{STRING}/秒 +STR_6280 :{SMALLFONT}{BLACK}聊天室 +STR_6281 :{SMALLFONT}{BLACK}在工具栏中加入激活聊天窗口的按钮 +STR_6282 :聊天室 +STR_6283 :暂时无法聊天。你连接到服务器了吗? +STR_6299 :下载全部 +STR_6300 :{SMALLFONT}{BLACK}从网络上下载可下载缺失文件 +STR_6301 :{SMALLFONT}{BLACK}将所选文件名称复制到剪贴板 +STR_6302 :{SMALLFONT}{BLACK}将整个缺失文件列表复制到剪贴板 +STR_6303 :下载文件 ({COMMA16} / {COMMA16}): [{STRING}] +STR_6304 :打开景物选择器 +STR_6305 :多线程 +STR_6306 :{SMALLFONT}{BLACK}使用多个线程进行渲染的实验选项可能会导致游戏不稳定 ############# diff --git a/debian/changelog b/debian/changelog index 664a7a3582..492ba600dc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -openrct2 (0.2.2-develop-1) unstable; urgency=medium +openrct2 (0.2.3-develop-1) unstable; urgency=medium - * Release 2019-03 (Closes: #XXXXXX) + * Release 2019-07 (Closes: #XXXXXX) -- Michał Janiszewski Sun, 10 Jan 2016 23:41:16 +0100 diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 71b0695a56..3c45c4fc19 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,3 +1,111 @@ +0.2.3+ (in development) +------------------------------------------------------------------------ +- Feature: [#9285] Remember current group in scenario list window. +- Feature: [#9918] Increase image list capacity by about 100k units. +- Change: [#1349] Increase the number of ride music played simultaneously from 2 to 32. +- Fix: [#4927] Giant screenshot cut off at bottom and top. +- Fix: [#7572] Queue paths connect to regular paths through fences. +- Fix: [#7690] Problem with guests freezing on certain tiles of path. +- Fix: [#7883] Headless server log is stored incorrectly if server name contains CJK in Ubuntu +- Fix: [#8136] Excessive lateral G penalty is too excessive. +- Fix: [#8584] Duck spawning function does not check tiles with x or y coordinate of 0..64 (Original bug) +- Fix: [#9179] Crash when modifying a ride occasionally. +- Fix: [#9533] Door sounds not playing. +- Fix: [#9574] Text overflow in scenario objective window when using CJK languages. +- Fix: [#9603] Don't render audio when master volume is turned off. +- Fix: [#9625] Show correct cost in scenery selection. +- Fix: [#9669] The tile inspector shortcut key does not work with debugging tools disabled. +- Fix: [#9675] Guest entry point limit can be bypassed in scenario editor. +- Fix: [#9683] Cannot raise water level if part of the tool's area of effect is off of the map. +- Fix: [#9684] Entering custom size for water/land tool allows confirmation with main enter key, but not numpad enter key. +- Fix: [#9690] The keyboard shortcut for rotating the game view can be set to Enter or KP Enter, but not both. +- Fix: [#9717] Scroll bars do not render correctly when using OpenGL renderer. +- Fix: [#9729] Peeps do not take into account height difference when deciding to pathfind to a ride entrance (original bug). +- Fix: [#9902] Doors/Portcullis do not check to make sure doors are open causing double opens. +- Fix: [#9926] Africa - Oasis park has wrong peep spawn (original bug). +- Fix: [#9953] Crash when hacked rides attempt to find the closest mechanic. +- Fix: [#9955] Resizing map in while pause mode does not work and may result in freezes. +- Fix: [#9957] When using 'no money' cheat, guests complain of running out of cash. +- Fix: [#9970] Wait for quarter load fails. +- Fix: [#9994] Game action tick collision during server connect and map load. +- Fix: [#10017] Ghost elements influencing ride excitement. +- Fix: [#10036] Do not allocate large chunks of memory for save file classification. +- Fix: [#10106] Ride circuits should not be used for modes that do not support it. +- Fix: [#10149] Desync in headless mode with rides that create smoke particles. +- Improved: [#9466] Add the rain weather effect to the OpenGL renderer. +- Improved: [#9987] Minimum load rounding. +- Improved: [#10125] Better support for high DPI screens. + +0.2.3 (2019-07-10) +------------------------------------------------------------------------ +- Feature: [#485] Rides can now be simulated with ghost trains during construction. +- Feature: [#1260] Option for making giant screenshots have a transparent background. +- Feature: [#2339] Find local servers automatically when fetching servers. +- Feature: [#7296] Allow assigning a keyboard shortcut for the scenery picker. +- Feature: [#8029] Add the Hungarian Forint (HUF) to the list of available currencies. +- Feature: [#8481] Multi-threaded rendering. +- Feature: [#8558] Guest debugging tab. +- Feature: [#8659] Banner and sign texts are now shown in tooltips. +- Feature: [#8687] New multiplayer toolbar icon showing network status with reconnect option. +- Feature: [#8791] Improved tile element flag manipulation in Tile Inspector. +- Feature: [#8919] Allow setting ride price from console. +- Feature: [#8963] Add missing Czech letters to sprite font, use sprite font for Czech. +- Feature: [#9154] Change map toolbar icon with current viewport rotation. +- Change: [#7877] Files are now sorted in logical rather than dictionary order. +- Change: [#8427] Ghost elements now show up as white on the mini-map. +- Change: [#8688] Move common actions from debug menu into cheats menu. +- Change: [#9428] Increase maximum height of the Hypercoaster to RCT1 limits. +- Fix: [#2294] Clients crashing the server with invalid object selection. +- Fix: [#4568, #5896] Incorrect fences removed when building a tracked ride through +- Fix: [#5103] OpenGL: ride track preview not rendered. +- Fix: [#5889] Giant screenshot does not work while using OpenGL renderer. +- Fix: [#5579] Network desync immediately after connecting. +- Fix: [#5893] Looking at guest window tabs other than the main tab eventually causes assertion. +- Fix: [#5905] Urban Park merry-go-round has entrance and exit swapped (original bug). +- Fix: [#6006] Objects higher than 6 metres are considered trees (original bug). +- Fix: [#7039] Map window not rendering properly when using OpenGL. +- Fix: [#7045] Theme window's colour pickers not drawn properly on OpenGL. +- Fix: [#7323] Tunnel entrances not rendering in 'highlight path issues' mode if they have benches inside. +- Fix: [#7729] Money Input Prompt breaks on certain values. +- Fix: [#7884] Unfinished preserved rides can be demolished with quick demolish. +- Fix: [#7913] RCT1/RCT2 title sequence timing is off. +- Fix: [#7700, #8079, #8969] Crash when unloading buggy custom rides. +- Fix: [#7829] Rotated information kiosk can cause 'unreachable' messages. +- Fix: [#7878] Scroll shortcut keys ignore SHIFT/CTRL/ALT modifiers. +- Fix: [#8219] Faulty folder recreation in "save" folder. +- Fix: [#8480, #8535] Crash when mirroring track design. +- Fix: [#8507] Incorrect change in vehicle rolling direction. +- Fix: [#8537] Imported RCT1 rides/shops are all numbered 1. +- Fix: [#8553] Scenery removal tool removes fences and paths while paused. +- Fix: [#8598] Taking screenshots fails with some park names. +- Fix: [#8602] Wall piece collision detection deviates from vanilla +- Fix: [#8649] Setting date does not work in multiplayer. +- Fix: [#8873] Potential crash when placing footpaths. +- Fix: [#8882] Submarine Ride does not count as indoors (original bug). +- Fix: [#8900] Peep tracking is not synchronized. +- Fix: [#8909] Potential crash when invoking game actions as server. +- Fix: [#8947] Detection of AVX2 support. +- Fix: [#8988] Character sprite lookup noticeably slows down drawing. +- Fix: [#9000] Show correct error message if not enough money available. +- Fix: [#9067] Land/water tools show prices when money is disabled. +- Fix: [#9124] Disconnected clients can crash the server. +- Fix: [#9132] System file browser cannot open SV4 files. +- Fix: [#9152] Spectators can modify ride colours. +- Fix: [#9202] Artefacts show when changing ride type as client or using in-game console. +- Fix: [#9240] Crash when passing directory instead of save file. +- Fix: [#9245] Headless servers apply Discord Rich Presence. +- Fix: [#9293] Issue with the native load/save dialog. +- Fix: [#9322] Peep crashing the game trying to find a ride to look at. +- Fix: [#9324] Crash trying to remove invalid footpath scenery. +- Fix: [#9402] Ad campaigns disappear when you save and load the game. +- Fix: [#9411] Ad campaigns end too soon. +- Fix: [#9476] Running `simulate` command on park yields `Completed: (null)`. +- Fix: [#9520] Time Twister object artdec29 conversion problem. +- Fix: Guests eating popcorn are drawn as if they're eating pizza. +- Fix: The arbitrary ride type and vehicle dropdown lists are ordered case-sensitively. +- Improved: [#6116] Expose colour scheme for track elements in the tile inspector. +- Improved: Allow the use of numpad enter key for console and chat. + 0.2.2 (2019-03-13) ------------------------------------------------------------------------ - Feature: [#4418] Allow steep slopes on the side-friction roller coaster. @@ -31,6 +139,7 @@ - Fix: [#6191] OpenRCT2 fails to run when the path has an emoji in it. - Fix: [#7439] Placement messages have mixed strings - Fix: [#7473] Disabling sound effects also disables "Disable audio on focus loss". +- Fix: [#7476] Trying to Change Park Name During MP Session Instantly Crashes Host Game. - Fix: [#7536] Android builds fail to start. - Fix: [#7689] Deleting 0-tile maze gives a MONEY32_UNDEFINED (negative) refund. - Fix: [#7828] Copied entrances and exits stay when demolishing ride. @@ -55,6 +164,7 @@ - Fix: [#8200] Incorrect behaviour when removing entrances and exits that are on the same tile. - Fix: [#8204] Crash when tile element has no surface elements. - Fix: [#8264] Rides and scenery placeable outside of map with ZC and Sandbox mode enabled. +- Fix: [#8300] Crash in UpdateRideMazePathfinding(). - Fix: [#8335] Rides with arbitrary ride types can crash the game when they break down. - Fix: [#8358] Infinite loop when changing vehicle count on stopped ride. - Fix: [#8402] Crash closing a window in some cases. @@ -71,9 +181,10 @@ - Fix: [#8585] Part of track missing on air powered vertical coaster. - Fix: [#8588] Guest list scrolling breaks above ~2000 guests. - Fix: [#8591] Game loop does not run at a consistent tick rate of 40 Hz. -- Fix: [#8647] Marketing campaigns check for entry fees below £1 (original bug). +- Fix: [#8647] Marketing campaigns check for entry fees below £1 (original bug). - Fix: [#8653] Crash when peeps attempt to enter a ride with no vehicles. - Fix: [#8720] Desync due to boats colliding with ghost pieces. +- Fix: [#8736] Incomplete warning when all ride slots are full. - Fix: [#8739] Savegame from original game crashes when cruising through map. - Fix: [#8742] Access violation in vehicle_update_sound_params. - Fix: [#8804] Raising water shows money effect at the bottom rather than new height. diff --git a/distribution/macos/Info.plist b/distribution/macos/Info.plist index 38af077c0f..ef0188664d 100644 --- a/distribution/macos/Info.plist +++ b/distribution/macos/Info.plist @@ -13,7 +13,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.2.2 + 0.2.3 CFBundleSignature ORCT LSMinimumSystemVersion diff --git a/distribution/readme.txt b/distribution/readme.txt index d711bad307..ef3bb42f6f 100644 --- a/distribution/readme.txt +++ b/distribution/readme.txt @@ -1,5 +1,5 @@ -Last updated: 2019-03-13 -Release version: 0.2.2 +Last updated: 2019-07-10 +Release version: 0.2.3 ------------------------------------------------------------------------ diff --git a/dockerfiles/ubuntu_amd64/Dockerfile b/dockerfiles/ubuntu_amd64/Dockerfile index 9e241e6aea..ab42979445 100644 --- a/dockerfiles/ubuntu_amd64/Dockerfile +++ b/dockerfiles/ubuntu_amd64/Dockerfile @@ -36,4 +36,4 @@ RUN \ RUN apt-get -y upgrade # clang and gcc already installed -RUN apt-get install --no-install-recommends -y ccache cmake libsdl2-dev libsdl2-ttf-dev pkg-config libjansson-dev libspeex-dev libspeexdsp-dev libcurl4-openssl-dev libcrypto++-dev libfontconfig1-dev libfreetype6-dev libpng-dev libzip-dev git libssl-dev ninja-build libicu-dev +RUN apt-get install --no-install-recommends -y ccache cmake libsdl2-dev libsdl2-ttf-dev pkg-config libjansson-dev libspeex-dev libspeexdsp-dev libcurl4-openssl-dev libcrypto++-dev libfontconfig1-dev libfreetype6-dev libpng-dev libzip-dev git libssl-dev ninja-build libicu-dev libgtest-dev diff --git a/openrct2.common.props b/openrct2.common.props index 49376b04f8..7437297cfa 100644 --- a/openrct2.common.props +++ b/openrct2.common.props @@ -9,7 +9,7 @@ $(DefaultPlatformToolset) - 10.0.14393.0 + 10.0.17763.0 MultiByte diff --git a/openrct2.proj b/openrct2.proj index af9d95b558..f40db1ec61 100644 --- a/openrct2.proj +++ b/openrct2.proj @@ -18,7 +18,7 @@ x64 $(GIT_COMMIT_SHA1.Substring(0, 7)) - 0.2.2 + 0.2.3 -$(GIT_BRANCH)-$(GIT_COMMIT_SHA1_SHORT) 16 @@ -68,10 +68,12 @@ 2fe3bd994b3189899d93f1d5a881e725e046fdc2 https://github.com/google/googletest/archive/$(GtestVersion).zip 058b9df80244c03f1633cb06e9f70471a29ebb8e - https://github.com/OpenRCT2/title-sequences/releases/download/v0.1.2/title-sequence-v0.1.2.zip - 1136ef92bfb05cd1cba9831ba6dc4a653d87a246 - https://github.com/OpenRCT2/objects/releases/download/v1.0.9/objects.zip - be0bcb454505e4f7c56d21d6804f81faf8a0a652 + https://github.com/OpenRCT2/title-sequences/releases/download/v0.1.2c/title-sequences.zip + 304d13a126c15bf2c86ff13b81a2f2cc1856ac8d + https://github.com/OpenRCT2/objects/releases/download/v1.0.12/objects.zip + 56b5d22ed7da0afa750b3dcb5ac22de61e3597c2 + https://github.com/OpenRCT2/replays/releases/download/v0.0.4/replays.zip + 6584368CD04EC42FDC2EB5DF26FECE9A964C27B7 @@ -226,6 +228,15 @@ OutputDirectory="$(TargetDir)data\object" /> + + + + + diff --git a/openrct2.targets b/openrct2.targets index a93c2c362e..9a82f91fc6 100644 --- a/openrct2.targets +++ b/openrct2.targets @@ -153,7 +153,7 @@ if (!String.IsNullOrEmpty(CheckFile)) { string checkSha1 = GetSha1FromCheckFile(CheckFile, Name); - if (String.Equals(checkSha1, Sha1, StringComparison.OrdinalIgnoreCase)) + if (String.Equals(checkSha1, Sha1, StringComparison.OrdinalIgnoreCase) && Directory.Exists(OutputDirectory)) { Log.LogMessage(MessageImportance.Normal, String.Format("{0} up to date", Name)); return true; diff --git a/readme.md b/readme.md index 67314da372..9d620fcc7e 100644 --- a/readme.md +++ b/readme.md @@ -10,8 +10,8 @@ An open-source re-implementation of RollerCoaster Tycoon 2. A construction and m ### Build Status | | Windows | Linux / Mac | Download | |-------------|---------|-------------|----------| -| **master** | [![AppVeyor](https://ci.appveyor.com/api/projects/status/7efnemxhon6i5n34/branch/master?svg=true)](https://ci.appveyor.com/project/IntelOrca/openrct2-ject9) | [![Travis CI](https://travis-ci.org/OpenRCT2/OpenRCT2.svg?branch=master)](https://travis-ci.org/OpenRCT2/OpenRCT2) | [![OpenRCT2.org](https://img.shields.io/badge/master-v0.2.2-green.svg)](https://openrct2.org/downloads/master/latest) | -| **develop** | [![AppVeyor](https://ci.appveyor.com/api/projects/status/7efnemxhon6i5n34/branch/develop?svg=true)](https://ci.appveyor.com/project/IntelOrca/openrct2-ject9) | [![Travis CI](https://travis-ci.org/OpenRCT2/OpenRCT2.svg?branch=develop)](https://travis-ci.org/OpenRCT2/OpenRCT2) | [![OpenRCT2.org](https://img.shields.io/badge/develop-v0.2.2+-blue.svg)](https://openrct2.org/downloads/develop/latest) | +| **master** | [![AppVeyor](https://ci.appveyor.com/api/projects/status/7efnemxhon6i5n34/branch/master?svg=true)](https://ci.appveyor.com/project/IntelOrca/openrct2-ject9) | [![Travis CI](https://travis-ci.org/OpenRCT2/OpenRCT2.svg?branch=master)](https://travis-ci.org/OpenRCT2/OpenRCT2) | [![OpenRCT2.org](https://img.shields.io/badge/master-v0.2.3-green.svg)](https://openrct2.org/downloads/master/latest) | +| **develop** | [![AppVeyor](https://ci.appveyor.com/api/projects/status/7efnemxhon6i5n34/branch/develop?svg=true)](https://ci.appveyor.com/project/IntelOrca/openrct2-ject9) | [![Travis CI](https://travis-ci.org/OpenRCT2/OpenRCT2.svg?branch=develop)](https://travis-ci.org/OpenRCT2/OpenRCT2) | [![OpenRCT2.org](https://img.shields.io/badge/develop-v0.2.3+-blue.svg)](https://openrct2.org/downloads/develop/latest) | --- @@ -66,7 +66,7 @@ OpenRCT2 requires original files of RollerCoaster Tycoon 2 to play. It can be bo Some Linux distributions offer native packages already. These packages are usually third-party, but we're trying to resolve issues they are facing. * ArchLinux AUR: [openrct2-git](https://aur.archlinux.org/packages/openrct2-git) and [openrct2](https://aur.archlinux.org/packages/openrct2) -* Ubuntu PPA: [`master` branch](https://launchpad.net/~openrct2/+archive/ubuntu/master) and [`develop` branch](https://launchpad.net/~openrct2/+archive/ubuntu/nightly) +* Ubuntu PPA: [`develop` branch](https://launchpad.net/~openrct2/+archive/ubuntu/nightly) (nightly builds) * openSUSE OBS: [games/openrct2](https://software.opensuse.org/download.html?project=games&package=openrct2) * Gentoo (main portage tree): [games-simulation/openrct2](https://packages.gentoo.org/packages/games-simulation/openrct2) * NixOS (`nixos-unstable` channel): [openrct2](https://github.com/NixOS/nixpkgs/blob/master/pkgs/games/openrct2/default.nix) @@ -123,10 +123,12 @@ The program can also be built as a command line program using CMake. This type o ### Windows: 1. Check out the repository. This can be done using [GitHub Desktop](https://desktop.github.com) or [other tools](https://help.github.com/articles/which-remote-url-should-i-use). 2. Open a new Developer Command Prompt for VS 2017, then navigate to the repository (e.g. `cd C:\GitHub\OpenRCT2`). -3. Run `msbuild openrct2.proj /t:build /p:platform=x64`. +3. To build the 64-bit version, use `msbuild openrct2.proj /t:build /p:platform=x64`. + + To build the 32-bit version, use `msbuild openrct2.proj /t:build /p:platform=Win32`. 4. Run the game, `bin\openrct2` -Once you have ran msbuild once, further development can be done within Visual Studio by opening `openrct2.sln`. +Once you have ran msbuild once, further development can be done within Visual Studio by opening `openrct2.sln`. Make sure to select the correct target platform for which you ran the build in point #3 (`Win32` for the 32-bit version, `x64` for the 64-bit version), otherwise the build will fail in Visual Studio. Other examples: ``` @@ -162,10 +164,13 @@ The standard CMake build procedure is to install the [required libraries](https: ``` mkdir build cd build -cmake ../ # set your standard cmake options, e.g. build type here +cmake ../ # set your standard cmake options, e.g. build type here - For example, -DCMAKE_BUILD_TYPE=RelWithDebInfo make # you can parallelise your build job with e.g. -j8 or consider using ninja DESTDIR=. make install # the install target creates all the necessary files in places we expect them ``` + +You can also use Ninja in place of Make, if you prefer, see Wiki for details. + Detailed instructions can be found on our [wiki](https://github.com/OpenRCT2/OpenRCT2/wiki/Building-OpenRCT2-on-Linux). --- diff --git a/resources/OpenRCT2.rc b/resources/OpenRCT2.rc index f9ccc2fd1f..18c7441445 100644 --- a/resources/OpenRCT2.rc +++ b/resources/OpenRCT2.rc @@ -1,6 +1,7 @@ // Microsoft Visual C++ generated resource script. // #include "resource.h" +#include "version.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -15,8 +16,45 @@ ///////////////////////////////////////////////////////////////////////////// // English (United Kingdom) resources -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) || defined(AFX_TARG_ENG) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(65001) + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION OPENRCT2_FILE_VERSION + PRODUCTVERSION OPENRCT2_FILE_VERSION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "CompanyName", "OpenRCT2 Team" + VALUE "FileDescription", "Main executable for OpenRCT2" + VALUE "FileVersion", OPENRCT2_PRODUCT_VERSION + VALUE "LegalCopyright", "Copyright (c) 2014-2019 OpenRCT2 developers" + VALUE "ProductName", "OpenRCT2" + VALUE "ProductVersion", OPENRCT2_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// @@ -67,3 +105,4 @@ IDI_ICON ICON "logo\\icon.ico" ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED + diff --git a/resources/g2/font/latin/n-caron-bold.png b/resources/g2/font/latin/n-caron-bold.png new file mode 100644 index 0000000000..438634bc25 Binary files /dev/null and b/resources/g2/font/latin/n-caron-bold.png differ diff --git a/resources/g2/font/latin/n-caron-small.png b/resources/g2/font/latin/n-caron-small.png new file mode 100644 index 0000000000..97630bad3d Binary files /dev/null and b/resources/g2/font/latin/n-caron-small.png differ diff --git a/resources/g2/font/latin/n-caron-tiny.png b/resources/g2/font/latin/n-caron-tiny.png new file mode 100644 index 0000000000..6d96a8774a Binary files /dev/null and b/resources/g2/font/latin/n-caron-tiny.png differ diff --git a/resources/g2/font/latin/n-caron-uc-bold.png b/resources/g2/font/latin/n-caron-uc-bold.png new file mode 100644 index 0000000000..ae2f8b5976 Binary files /dev/null and b/resources/g2/font/latin/n-caron-uc-bold.png differ diff --git a/resources/g2/font/latin/n-caron-uc-small.png b/resources/g2/font/latin/n-caron-uc-small.png new file mode 100644 index 0000000000..02a8059422 Binary files /dev/null and b/resources/g2/font/latin/n-caron-uc-small.png differ diff --git a/resources/g2/font/latin/n-caron-uc-tiny.png b/resources/g2/font/latin/n-caron-uc-tiny.png new file mode 100644 index 0000000000..5d61b21ac3 Binary files /dev/null and b/resources/g2/font/latin/n-caron-uc-tiny.png differ diff --git a/resources/g2/font/latin/r-caron-bold.png b/resources/g2/font/latin/r-caron-bold.png new file mode 100644 index 0000000000..65dbf1c4f8 Binary files /dev/null and b/resources/g2/font/latin/r-caron-bold.png differ diff --git a/resources/g2/font/latin/r-caron-small.png b/resources/g2/font/latin/r-caron-small.png new file mode 100644 index 0000000000..4877c59bac Binary files /dev/null and b/resources/g2/font/latin/r-caron-small.png differ diff --git a/resources/g2/font/latin/r-caron-tiny.png b/resources/g2/font/latin/r-caron-tiny.png new file mode 100644 index 0000000000..624312b791 Binary files /dev/null and b/resources/g2/font/latin/r-caron-tiny.png differ diff --git a/resources/g2/font/latin/r-caron-uc-bold.png b/resources/g2/font/latin/r-caron-uc-bold.png new file mode 100644 index 0000000000..c802499ed5 Binary files /dev/null and b/resources/g2/font/latin/r-caron-uc-bold.png differ diff --git a/resources/g2/font/latin/r-caron-uc-small.png b/resources/g2/font/latin/r-caron-uc-small.png new file mode 100644 index 0000000000..8bec8f344e Binary files /dev/null and b/resources/g2/font/latin/r-caron-uc-small.png differ diff --git a/resources/g2/font/latin/r-caron-uc-tiny.png b/resources/g2/font/latin/r-caron-uc-tiny.png new file mode 100644 index 0000000000..48c6632cf4 Binary files /dev/null and b/resources/g2/font/latin/r-caron-uc-tiny.png differ diff --git a/resources/g2/font/latin/s-caron-bold.png b/resources/g2/font/latin/s-caron-bold.png new file mode 100644 index 0000000000..14600a8dce Binary files /dev/null and b/resources/g2/font/latin/s-caron-bold.png differ diff --git a/resources/g2/font/latin/s-caron-small.png b/resources/g2/font/latin/s-caron-small.png new file mode 100644 index 0000000000..073d4d9c0f Binary files /dev/null and b/resources/g2/font/latin/s-caron-small.png differ diff --git a/resources/g2/font/latin/s-caron-tiny.png b/resources/g2/font/latin/s-caron-tiny.png new file mode 100644 index 0000000000..b6b358aeac Binary files /dev/null and b/resources/g2/font/latin/s-caron-tiny.png differ diff --git a/resources/g2/font/latin/s-caron-uc-bold.png b/resources/g2/font/latin/s-caron-uc-bold.png new file mode 100644 index 0000000000..e46b2b1bb2 Binary files /dev/null and b/resources/g2/font/latin/s-caron-uc-bold.png differ diff --git a/resources/g2/font/latin/s-caron-uc-small.png b/resources/g2/font/latin/s-caron-uc-small.png new file mode 100644 index 0000000000..aef1fb5e4d Binary files /dev/null and b/resources/g2/font/latin/s-caron-uc-small.png differ diff --git a/resources/g2/font/latin/s-caron-uc-tiny.png b/resources/g2/font/latin/s-caron-uc-tiny.png new file mode 100644 index 0000000000..51442f2b5e Binary files /dev/null and b/resources/g2/font/latin/s-caron-uc-tiny.png differ diff --git a/resources/g2/font/latin/t-caron-bold.png b/resources/g2/font/latin/t-caron-bold.png new file mode 100644 index 0000000000..ec6823604c Binary files /dev/null and b/resources/g2/font/latin/t-caron-bold.png differ diff --git a/resources/g2/font/latin/t-caron-small.png b/resources/g2/font/latin/t-caron-small.png new file mode 100644 index 0000000000..bbfd511a12 Binary files /dev/null and b/resources/g2/font/latin/t-caron-small.png differ diff --git a/resources/g2/font/latin/t-caron-tiny.png b/resources/g2/font/latin/t-caron-tiny.png new file mode 100644 index 0000000000..10ce135736 Binary files /dev/null and b/resources/g2/font/latin/t-caron-tiny.png differ diff --git a/resources/g2/font/latin/t-caron-uc-bold.png b/resources/g2/font/latin/t-caron-uc-bold.png new file mode 100644 index 0000000000..8ffebc1330 Binary files /dev/null and b/resources/g2/font/latin/t-caron-uc-bold.png differ diff --git a/resources/g2/font/latin/t-caron-uc-small.png b/resources/g2/font/latin/t-caron-uc-small.png new file mode 100644 index 0000000000..a2b3efa157 Binary files /dev/null and b/resources/g2/font/latin/t-caron-uc-small.png differ diff --git a/resources/g2/font/latin/t-caron-uc-tiny.png b/resources/g2/font/latin/t-caron-uc-tiny.png new file mode 100644 index 0000000000..9b3a021950 Binary files /dev/null and b/resources/g2/font/latin/t-caron-uc-tiny.png differ diff --git a/resources/g2/font/latin/u-ring-bold.png b/resources/g2/font/latin/u-ring-bold.png new file mode 100644 index 0000000000..d818014dcf Binary files /dev/null and b/resources/g2/font/latin/u-ring-bold.png differ diff --git a/resources/g2/font/latin/u-ring-small.png b/resources/g2/font/latin/u-ring-small.png new file mode 100644 index 0000000000..3f3afb4658 Binary files /dev/null and b/resources/g2/font/latin/u-ring-small.png differ diff --git a/resources/g2/font/latin/u-ring-tiny.png b/resources/g2/font/latin/u-ring-tiny.png new file mode 100644 index 0000000000..c17b58b991 Binary files /dev/null and b/resources/g2/font/latin/u-ring-tiny.png differ diff --git a/resources/g2/font/latin/u-ring-uc-bold.png b/resources/g2/font/latin/u-ring-uc-bold.png new file mode 100644 index 0000000000..2a921028cd Binary files /dev/null and b/resources/g2/font/latin/u-ring-uc-bold.png differ diff --git a/resources/g2/font/latin/u-ring-uc-small.png b/resources/g2/font/latin/u-ring-uc-small.png new file mode 100644 index 0000000000..581629045c Binary files /dev/null and b/resources/g2/font/latin/u-ring-uc-small.png differ diff --git a/resources/g2/font/latin/u-ring-uc-tiny.png b/resources/g2/font/latin/u-ring-uc-tiny.png new file mode 100644 index 0000000000..908bb60214 Binary files /dev/null and b/resources/g2/font/latin/u-ring-uc-tiny.png differ diff --git a/resources/g2/font/latin/z-caron-bold.png b/resources/g2/font/latin/z-caron-bold.png new file mode 100644 index 0000000000..f4a69f936d Binary files /dev/null and b/resources/g2/font/latin/z-caron-bold.png differ diff --git a/resources/g2/font/latin/z-caron-small.png b/resources/g2/font/latin/z-caron-small.png new file mode 100644 index 0000000000..e0ba0a8876 Binary files /dev/null and b/resources/g2/font/latin/z-caron-small.png differ diff --git a/resources/g2/font/latin/z-caron-tiny.png b/resources/g2/font/latin/z-caron-tiny.png new file mode 100644 index 0000000000..92dcaa982f Binary files /dev/null and b/resources/g2/font/latin/z-caron-tiny.png differ diff --git a/resources/g2/font/latin/z-caron-uc-bold.png b/resources/g2/font/latin/z-caron-uc-bold.png new file mode 100644 index 0000000000..9503ac8284 Binary files /dev/null and b/resources/g2/font/latin/z-caron-uc-bold.png differ diff --git a/resources/g2/font/latin/z-caron-uc-small.png b/resources/g2/font/latin/z-caron-uc-small.png new file mode 100644 index 0000000000..7221299be9 Binary files /dev/null and b/resources/g2/font/latin/z-caron-uc-small.png differ diff --git a/resources/g2/font/latin/z-caron-uc-tiny.png b/resources/g2/font/latin/z-caron-uc-tiny.png new file mode 100644 index 0000000000..5bff847a48 Binary files /dev/null and b/resources/g2/font/latin/z-caron-uc-tiny.png differ diff --git a/resources/g2/icons/map_east.png b/resources/g2/icons/map_east.png new file mode 100644 index 0000000000..2d078a88e8 Binary files /dev/null and b/resources/g2/icons/map_east.png differ diff --git a/resources/g2/icons/map_east_pressed.png b/resources/g2/icons/map_east_pressed.png new file mode 100644 index 0000000000..8979ef881c Binary files /dev/null and b/resources/g2/icons/map_east_pressed.png differ diff --git a/resources/g2/icons/map_north.png b/resources/g2/icons/map_north.png new file mode 100644 index 0000000000..d50f7dc4df Binary files /dev/null and b/resources/g2/icons/map_north.png differ diff --git a/resources/g2/icons/map_north_pressed.png b/resources/g2/icons/map_north_pressed.png new file mode 100644 index 0000000000..7ad1050742 Binary files /dev/null and b/resources/g2/icons/map_north_pressed.png differ diff --git a/resources/g2/icons/map_south.png b/resources/g2/icons/map_south.png new file mode 100644 index 0000000000..8e66136186 Binary files /dev/null and b/resources/g2/icons/map_south.png differ diff --git a/resources/g2/icons/map_south_pressed.png b/resources/g2/icons/map_south_pressed.png new file mode 100644 index 0000000000..a58d2d3b50 Binary files /dev/null and b/resources/g2/icons/map_south_pressed.png differ diff --git a/resources/g2/icons/map_west.png b/resources/g2/icons/map_west.png new file mode 100644 index 0000000000..0aa74611d7 Binary files /dev/null and b/resources/g2/icons/map_west.png differ diff --git a/resources/g2/icons/map_west_pressed.png b/resources/g2/icons/map_west_pressed.png new file mode 100644 index 0000000000..93bf7d0bf4 Binary files /dev/null and b/resources/g2/icons/map_west_pressed.png differ diff --git a/resources/g2/icons/multiplayer_desync.png b/resources/g2/icons/multiplayer_desync.png new file mode 100644 index 0000000000..43e989b945 Binary files /dev/null and b/resources/g2/icons/multiplayer_desync.png differ diff --git a/resources/g2/icons/multiplayer_sync.png b/resources/g2/icons/multiplayer_sync.png new file mode 100644 index 0000000000..596f876fa3 Binary files /dev/null and b/resources/g2/icons/multiplayer_sync.png differ diff --git a/resources/g2/icons/multiplayer_toolbar.png b/resources/g2/icons/multiplayer_toolbar.png new file mode 100644 index 0000000000..a2c9e734fa Binary files /dev/null and b/resources/g2/icons/multiplayer_toolbar.png differ diff --git a/resources/g2/icons/multiplayer_toolbar_pressed.png b/resources/g2/icons/multiplayer_toolbar_pressed.png new file mode 100644 index 0000000000..faaa02377f Binary files /dev/null and b/resources/g2/icons/multiplayer_toolbar_pressed.png differ diff --git a/resources/g2/icons/58.png b/resources/g2/icons/news_messages.png similarity index 100% rename from resources/g2/icons/58.png rename to resources/g2/icons/news_messages.png diff --git a/resources/g2/icons/rct1_simulate_off.png b/resources/g2/icons/rct1_simulate_off.png new file mode 100644 index 0000000000..c7a7c402a6 Binary files /dev/null and b/resources/g2/icons/rct1_simulate_off.png differ diff --git a/resources/g2/icons/rct1_simulate_off_pressed.png b/resources/g2/icons/rct1_simulate_off_pressed.png new file mode 100644 index 0000000000..0f5b7e05d1 Binary files /dev/null and b/resources/g2/icons/rct1_simulate_off_pressed.png differ diff --git a/resources/g2/icons/rct1_simulate_on.png b/resources/g2/icons/rct1_simulate_on.png new file mode 100644 index 0000000000..6e43186142 Binary files /dev/null and b/resources/g2/icons/rct1_simulate_on.png differ diff --git a/resources/g2/icons/rct1_simulate_on_pressed.png b/resources/g2/icons/rct1_simulate_on_pressed.png new file mode 100644 index 0000000000..4de2738e8e Binary files /dev/null and b/resources/g2/icons/rct1_simulate_on_pressed.png differ diff --git a/resources/g2/icons/simulate.png b/resources/g2/icons/simulate.png new file mode 100644 index 0000000000..173bc128e1 Binary files /dev/null and b/resources/g2/icons/simulate.png differ diff --git a/resources/g2/icons/31.png b/resources/g2/icons/title_play.png similarity index 100% rename from resources/g2/icons/31.png rename to resources/g2/icons/title_play.png diff --git a/resources/g2/icons/29.png b/resources/g2/icons/title_restart.png similarity index 100% rename from resources/g2/icons/29.png rename to resources/g2/icons/title_restart.png diff --git a/resources/g2/icons/32.png b/resources/g2/icons/title_skip.png similarity index 100% rename from resources/g2/icons/32.png rename to resources/g2/icons/title_skip.png diff --git a/resources/g2/icons/30.png b/resources/g2/icons/title_stop.png similarity index 100% rename from resources/g2/icons/30.png rename to resources/g2/icons/title_stop.png diff --git a/resources/g2/7.png b/resources/g2/placeholder.png similarity index 100% rename from resources/g2/7.png rename to resources/g2/placeholder.png diff --git a/resources/g2/sprites.json b/resources/g2/sprites.json index 66046226a4..df0cf72aba 100644 --- a/resources/g2/sprites.json +++ b/resources/g2/sprites.json @@ -21,7 +21,7 @@ "path": "icons/map_gen_land.png" }, { - "path": "7.png" + "path": "placeholder.png" }, { "path": "icons/zoom_in.png" @@ -87,16 +87,16 @@ "path": "icons/rct1_open_on_pressed.png" }, { - "path": "icons/29.png" + "path": "icons/title_restart.png" }, { - "path": "icons/30.png" + "path": "icons/title_stop.png" }, { - "path": "icons/31.png" + "path": "icons/title_play.png" }, { - "path": "icons/32.png" + "path": "icons/title_skip.png" }, { "path": "icons/cheats.png" @@ -174,7 +174,7 @@ "path": "track/junior/steep_to_flat_lift_3_2.png" }, { - "path": "icons/58.png" + "path": "icons/news_messages.png" }, { "path": "icons/server_password.png" @@ -394,6 +394,59 @@ "x_offset": 2, "y_offset": 1 }, + { + "path": "icons/map_north.png" + }, + { + "path": "icons/map_north_pressed.png" + }, + { + "path": "icons/map_west.png" + }, + { + "path": "icons/map_west_pressed.png" + }, + { + "path": "icons/map_south.png" + }, + { + "path": "icons/map_south_pressed.png" + }, + { + "path": "icons/map_east.png" + }, + { + "path": "icons/map_east_pressed.png" + }, + { + "path": "icons/multiplayer_toolbar.png" + }, + { + "path": "icons/multiplayer_toolbar_pressed.png" + }, + { + "path": "icons/multiplayer_sync.png" + }, + { + "path": "icons/multiplayer_desync.png" + }, + { + "path": "icons/simulate.png", + "x_offset": 2, + "y_offset": 2 + }, + { + "path": "icons/rct1_simulate_off.png" + }, + { + "path": "icons/rct1_simulate_off_pressed.png" + }, + { + "path": "icons/rct1_simulate_on.png" + }, + { + "path": "icons/rct1_simulate_on_pressed.png" + }, { "path": "font/latin/ae-uc-small.png", "y_offset": 0, @@ -796,6 +849,78 @@ "palette": "keep", "forceBmp": true }, + { + "path": "font/latin/n-caron-uc-small.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/n-caron-small.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/r-caron-uc-small.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/r-caron-small.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/s-caron-uc-small.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/s-caron-small.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/t-caron-uc-small.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/t-caron-small.png", + "y_offset": 1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/u-ring-uc-small.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/u-ring-small.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/z-caron-uc-small.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/z-caron-small.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, { "path": "font/rouble-small.png", "y_offset": 0, @@ -1205,6 +1330,78 @@ "palette": "keep", "forceBmp": true }, + { + "path": "font/latin/n-caron-uc-bold.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/n-caron-bold.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/r-caron-uc-bold.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/r-caron-bold.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/s-caron-uc-bold.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/s-caron-bold.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/t-caron-uc-bold.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/t-caron-bold.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/u-ring-uc-bold.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/u-ring-bold.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/z-caron-uc-bold.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/z-caron-bold.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, { "path": "font/rouble-bold.png", "y_offset": 0, @@ -1622,6 +1819,78 @@ "palette": "keep", "forceBmp": true }, + { + "path": "font/latin/n-caron-uc-tiny.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/n-caron-tiny.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/r-caron-uc-tiny.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/r-caron-tiny.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/s-caron-uc-tiny.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/s-caron-tiny.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/t-caron-uc-tiny.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/t-caron-tiny.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/u-ring-uc-tiny.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/u-ring-tiny.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/z-caron-uc-tiny.png", + "y_offset": -1, + "palette": "keep", + "forceBmp": true + }, + { + "path": "font/latin/z-caron-tiny.png", + "y_offset": 0, + "palette": "keep", + "forceBmp": true + }, { "path": "font/rouble-tiny.png", "y_offset": 0, diff --git a/resources/version.h b/resources/version.h new file mode 100644 index 0000000000..fcaebecce6 --- /dev/null +++ b/resources/version.h @@ -0,0 +1,2 @@ +#define OPENRCT2_FILE_VERSION 0, 0, 0, 0 +#define OPENRCT2_PRODUCT_VERSION "0.0.0.0-00000000" diff --git a/shell.nix b/shell.nix index 8350bae7c9..3259fd74e8 100644 --- a/shell.nix +++ b/shell.nix @@ -15,15 +15,15 @@ let objects-src = pkgs.fetchFromGitHub { owner = "OpenRCT2"; repo = "objects"; - rev = "v1.0.9"; - sha256 = "442b7da11b2b884559148ab9e7fdf781f50dd50feb69bfa569a78e52205a5709"; + rev = "v1.0.12"; + sha256 = "95b4648712f986e0219d43bb5400357b4bcf9e045b04dba561fc5be3e25e081b"; }; title-sequences-src = pkgs.fetchFromGitHub { owner = "OpenRCT2"; repo = "title-sequences"; - rev = "v0.1.2"; - sha256 = "1yb1ynkfmiankii3fngr9km5wbc07rp30nh0apkj6wryrhy7imgm"; + rev = "v0.1.2b"; + sha256 = "a220b4c3cd3180bebb261cc59141b7fb290b433631ba9c7587a4f2c9f7dc4e4c"; }; in pkgs.stdenv.mkDerivation { diff --git a/src/openrct2-android/app/build.gradle b/src/openrct2-android/app/build.gradle index 30dc262a4d..2e389666e3 100644 --- a/src/openrct2-android/app/build.gradle +++ b/src/openrct2-android/app/build.gradle @@ -10,7 +10,7 @@ android { targetSdkVersion 25 versionCode 2 - versionName '0.2.2' + versionName '0.2.3' externalNativeBuild { cmake { @@ -90,7 +90,7 @@ android.applicationVariants.all { variant -> dest "$variant.mergeAssets.outputDir/data" } download { - src 'https://github.com/OpenRCT2/title-sequences/releases/download/v0.1.2/title-sequence-v0.1.2.zip' + src 'https://github.com/OpenRCT2/title-sequences/releases/download/v0.1.2c/title-sequences.zip' dest new File(buildDir, 'title-sequence.zip') } copy { @@ -98,7 +98,7 @@ android.applicationVariants.all { variant -> into "$variant.mergeAssets.outputDir/data/title" } download { - src 'https://github.com/OpenRCT2/objects/releases/download/v1.0.9/objects.zip' + src 'https://github.com/OpenRCT2/objects/releases/download/v1.0.12/objects.zip' dest new File(buildDir, 'objects.zip') } copy { diff --git a/src/openrct2-cli/Cli.cpp b/src/openrct2-cli/Cli.cpp index 7587c39413..a063be9185 100644 --- a/src/openrct2-cli/Cli.cpp +++ b/src/openrct2-cli/Cli.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-dll/openrct2-dll.cpp b/src/openrct2-dll/openrct2-dll.cpp index 9fdf22ea51..24f2340fc8 100644 --- a/src/openrct2-dll/openrct2-dll.cpp +++ b/src/openrct2-dll/openrct2-dll.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -32,7 +32,7 @@ using namespace OpenRCT2::Ui; static char** GetCommandLineArgs(int argc, wchar_t** argvW); static void FreeCommandLineArgs(int argc, char** argv); -static char* ConvertUTF16toUTF8(const wchar_t* src); +static char* ConvertWideChartoUTF8(const wchar_t* src); DLLEXPORT int LaunchOpenRCT2(int argc, wchar_t** argvW) { @@ -43,6 +43,8 @@ DLLEXPORT int LaunchOpenRCT2(int argc, wchar_t** argvW) return -1; } + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); int exitCode = NormalisedMain(argc, const_cast(argv)); FreeCommandLineArgs(argc, argv); @@ -61,7 +63,7 @@ static char** GetCommandLineArgs(int argc, wchar_t** argvW) // Convert to UTF-8 for (int i = 0; i < argc; i++) { - argv[i] = ConvertUTF16toUTF8(argvW[i]); + argv[i] = ConvertWideChartoUTF8(argvW[i]); } return argv; @@ -77,7 +79,7 @@ static void FreeCommandLineArgs(int argc, char** argv) free(argv); } -static char* ConvertUTF16toUTF8(const wchar_t* src) +static char* ConvertWideChartoUTF8(const wchar_t* src) { int srcLen = lstrlenW(src); int sizeReq = WideCharToMultiByte(CP_UTF8, 0, src, srcLen, nullptr, 0, nullptr, nullptr); diff --git a/src/openrct2-ui/CursorData.cpp b/src/openrct2-ui/CursorData.cpp index 1b2d920ff8..4c24f24a64 100644 --- a/src/openrct2-ui/CursorData.cpp +++ b/src/openrct2-ui/CursorData.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/CursorRepository.cpp b/src/openrct2-ui/CursorRepository.cpp index 19a1e2f687..d8070612bd 100644 --- a/src/openrct2-ui/CursorRepository.cpp +++ b/src/openrct2-ui/CursorRepository.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/CursorRepository.h b/src/openrct2-ui/CursorRepository.h index 60c1705dab..3f415dac05 100644 --- a/src/openrct2-ui/CursorRepository.h +++ b/src/openrct2-ui/CursorRepository.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/SDLException.h b/src/openrct2-ui/SDLException.h index 9e16709b18..8bc3991e26 100644 --- a/src/openrct2-ui/SDLException.h +++ b/src/openrct2-ui/SDLException.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/TextComposition.cpp b/src/openrct2-ui/TextComposition.cpp index ec0cf6d0ea..6769858a53 100644 --- a/src/openrct2-ui/TextComposition.cpp +++ b/src/openrct2-ui/TextComposition.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -106,13 +106,15 @@ void TextComposition::HandleMessage(const SDL_Event* e) uint16_t modifier = e->key.keysym.mod; SDL_Keycode key = e->key.keysym.sym; + SDL_Scancode scancode = e->key.keysym.scancode; if (key == SDLK_KP_ENTER) { // Map Keypad enter to regular enter. key = SDLK_RETURN; + scancode = SDL_SCANCODE_RETURN; } - GetContext()->GetUiContext()->SetKeysPressed(key, e->key.keysym.scancode); + GetContext()->GetUiContext()->SetKeysPressed(key, scancode); // Text input if (_session.Buffer == nullptr) diff --git a/src/openrct2-ui/TextComposition.h b/src/openrct2-ui/TextComposition.h index ab656caa29..ce8dceb9f4 100644 --- a/src/openrct2-ui/TextComposition.h +++ b/src/openrct2-ui/TextComposition.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/Ui.cpp b/src/openrct2-ui/Ui.cpp index 3dbe526b61..22ebaeada3 100644 --- a/src/openrct2-ui/Ui.cpp +++ b/src/openrct2-ui/Ui.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/Ui.h b/src/openrct2-ui/Ui.h index 195411031d..991e0ef338 100644 --- a/src/openrct2-ui/Ui.h +++ b/src/openrct2-ui/Ui.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/UiContext.Android.cpp b/src/openrct2-ui/UiContext.Android.cpp index a5152e090f..a911a3bc49 100644 --- a/src/openrct2-ui/UiContext.Android.cpp +++ b/src/openrct2-ui/UiContext.Android.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/UiContext.Linux.cpp b/src/openrct2-ui/UiContext.Linux.cpp index 4c391581ed..b2e9913f8c 100644 --- a/src/openrct2-ui/UiContext.Linux.cpp +++ b/src/openrct2-ui/UiContext.Linux.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -265,7 +265,7 @@ namespace OpenRCT2::Ui static int32_t Execute(const std::string& command, std::string* output = nullptr) { # ifndef __EMSCRIPTEN__ - log_verbose("executing \"%s\"...\n", command.c_str()); + log_verbose("executing \"%s\"...", command.c_str()); FILE* fpipe = popen(command.c_str(), "r"); if (fpipe == nullptr) { diff --git a/src/openrct2-ui/UiContext.Win32.cpp b/src/openrct2-ui/UiContext.Win32.cpp index 621daa96a4..c19b3fcd98 100644 --- a/src/openrct2-ui/UiContext.Win32.cpp +++ b/src/openrct2-ui/UiContext.Win32.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -87,29 +87,28 @@ namespace OpenRCT2::Ui void ShowMessageBox(SDL_Window* window, const std::string& message) override { HWND hwnd = GetHWND(window); - std::wstring messageW = String::ToUtf16(message); + std::wstring messageW = String::ToWideChar(message); MessageBoxW(hwnd, messageW.c_str(), L"OpenRCT2", MB_OK); } void OpenFolder(const std::string& path) override { - std::wstring pathW = String::ToUtf16(path); + std::wstring pathW = String::ToWideChar(path); ShellExecuteW(NULL, L"open", pathW.c_str(), NULL, NULL, SW_SHOWNORMAL); } std::string ShowFileDialog(SDL_Window* window, const FileDialogDesc& desc) override { - std::wstring wcFilename = String::ToUtf16(desc.DefaultFilename); + std::wstring wcFilename = String::ToWideChar(desc.DefaultFilename); wcFilename.resize(std::max(wcFilename.size(), MAX_PATH)); - std::wstring wcTitle = String::ToUtf16(desc.Title); - std::wstring wcInitialDirectory = String::ToUtf16(desc.InitialDirectory); + std::wstring wcTitle = String::ToWideChar(desc.Title); + std::wstring wcInitialDirectory = String::ToWideChar(desc.InitialDirectory); std::wstring wcFilters = GetFilterString(desc.Filters); // Set open file name options OPENFILENAMEW openFileName = {}; openFileName.lStructSize = sizeof(OPENFILENAMEW); - openFileName.hwndOwner = GetHWND(window); openFileName.lpstrTitle = wcTitle.c_str(); openFileName.lpstrInitialDir = wcInitialDirectory.c_str(); openFileName.lpstrFilter = wcFilters.c_str(); @@ -163,7 +162,7 @@ namespace OpenRCT2::Ui LPMALLOC lpMalloc; if (SUCCEEDED(CoInitializeEx(0, COINIT_APARTMENTTHREADED)) && SUCCEEDED(SHGetMalloc(&lpMalloc))) { - std::wstring titleW = String::ToUtf16(title); + std::wstring titleW = String::ToWideChar(title); BROWSEINFOW bi = {}; bi.lpszTitle = titleW.c_str(); bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE | BIF_NONEWFOLDERBUTTON; @@ -212,7 +211,7 @@ namespace OpenRCT2::Ui std::wstringstream filtersb; for (auto filter : filters) { - filtersb << String::ToUtf16(filter.Name) << '\0' << String::ToUtf16(filter.Pattern) << '\0'; + filtersb << String::ToWideChar(filter.Name) << '\0' << String::ToWideChar(filter.Pattern) << '\0'; } return filtersb.str(); } diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index f15ff3e78d..6db70d3f73 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -387,7 +387,7 @@ public: switch (e.button.button) { case SDL_BUTTON_LEFT: - store_mouse_input(MOUSE_STATE_LEFT_PRESS, x, y); + store_mouse_input(MOUSE_STATE_LEFT_PRESS, ScreenCoordsXY(x, y)); _cursorState.left = CURSOR_PRESSED; _cursorState.old = 1; break; @@ -395,7 +395,7 @@ public: _cursorState.middle = CURSOR_PRESSED; break; case SDL_BUTTON_RIGHT: - store_mouse_input(MOUSE_STATE_RIGHT_PRESS, x, y); + store_mouse_input(MOUSE_STATE_RIGHT_PRESS, ScreenCoordsXY(x, y)); _cursorState.right = CURSOR_PRESSED; _cursorState.old = 2; break; @@ -414,7 +414,7 @@ public: switch (e.button.button) { case SDL_BUTTON_LEFT: - store_mouse_input(MOUSE_STATE_LEFT_RELEASE, x, y); + store_mouse_input(MOUSE_STATE_LEFT_RELEASE, ScreenCoordsXY(x, y)); _cursorState.left = CURSOR_RELEASED; _cursorState.old = 3; break; @@ -422,7 +422,7 @@ public: _cursorState.middle = CURSOR_RELEASED; break; case SDL_BUTTON_RIGHT: - store_mouse_input(MOUSE_STATE_RIGHT_RELEASE, x, y); + store_mouse_input(MOUSE_STATE_RIGHT_RELEASE, ScreenCoordsXY(x, y)); _cursorState.right = CURSOR_RELEASED; _cursorState.old = 4; break; @@ -447,13 +447,13 @@ public: if (_cursorState.touchIsDouble) { - store_mouse_input(MOUSE_STATE_RIGHT_PRESS, x, y); + store_mouse_input(MOUSE_STATE_RIGHT_PRESS, ScreenCoordsXY(x, y)); _cursorState.right = CURSOR_PRESSED; _cursorState.old = 2; } else { - store_mouse_input(MOUSE_STATE_LEFT_PRESS, x, y); + store_mouse_input(MOUSE_STATE_LEFT_PRESS, ScreenCoordsXY(x, y)); _cursorState.left = CURSOR_PRESSED; _cursorState.old = 1; } @@ -468,13 +468,13 @@ public: if (_cursorState.touchIsDouble) { - store_mouse_input(MOUSE_STATE_RIGHT_RELEASE, x, y); + store_mouse_input(MOUSE_STATE_RIGHT_RELEASE, ScreenCoordsXY(x, y)); _cursorState.right = CURSOR_RELEASED; _cursorState.old = 4; } else { - store_mouse_input(MOUSE_STATE_LEFT_RELEASE, x, y); + store_mouse_input(MOUSE_STATE_LEFT_RELEASE, ScreenCoordsXY(x, y)); _cursorState.left = CURSOR_RELEASED; _cursorState.old = 3; } @@ -639,7 +639,7 @@ private: height = 480; // Create window in window first rather than fullscreen so we have the display the window is on first - uint32_t flags = SDL_WINDOW_RESIZABLE; + uint32_t flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; if (gConfigGeneral.drawing_engine == DRAWING_ENGINE_OPENGL) { flags |= SDL_WINDOW_OPENGL; @@ -784,9 +784,10 @@ private: DrawRainFunc drawFunc) { rct_window* w{}; - for (auto i = window_get_index(original_w) + 1;; i++) + auto itStart = window_get_iterator(original_w); + for (auto it = std::next(itStart);; it++) { - if (i >= g_window_list.size()) + if (it == g_window_list.end()) { // Loop ended, draw rain for original_w auto vp = original_w->viewport; @@ -806,7 +807,7 @@ private: return; } - w = g_window_list[i].get(); + w = it->get(); if (right <= w->x || bottom <= w->y) { continue; diff --git a/src/openrct2-ui/UiContext.h b/src/openrct2-ui/UiContext.h index cbfb44b418..fae1ad6926 100644 --- a/src/openrct2-ui/UiContext.h +++ b/src/openrct2-ui/UiContext.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/UiContext.macOS.mm b/src/openrct2-ui/UiContext.macOS.mm index eb7b24a46b..01c76b7872 100644 --- a/src/openrct2-ui/UiContext.macOS.mm +++ b/src/openrct2-ui/UiContext.macOS.mm @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -143,7 +143,7 @@ namespace OpenRCT2::Ui private: static int32_t Execute(const std::string& command, std::string* output = nullptr) { - log_verbose("executing \"%s\"...\n", command.c_str()); + log_verbose("executing \"%s\"...", command.c_str()); FILE* fpipe = popen(command.c_str(), "r"); if (fpipe == nullptr) { diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp index edd6f12b19..e02e07127b 100644 --- a/src/openrct2-ui/WindowManager.cpp +++ b/src/openrct2-ui/WindowManager.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -100,10 +101,12 @@ public: return window_save_prompt_open(); case WC_SCENERY: return window_scenery_open(); +#ifndef DISABLE_NETWORK case WC_SERVER_LIST: return window_server_list_open(); case WC_SERVER_START: return window_server_start_open(); +#endif case WC_KEYBOARD_SHORTCUT_LIST: return window_shortcut_keys_open(); case WC_STAFF_LIST: @@ -220,7 +223,8 @@ public: uint32_t type = intent->GetUIntExtra(INTENT_EXTRA_LOADSAVE_TYPE); std::string defaultName = intent->GetStringExtra(INTENT_EXTRA_PATH); loadsave_callback callback = (loadsave_callback)intent->GetPointerExtra(INTENT_EXTRA_CALLBACK); - rct_window* w = window_loadsave_open(type, defaultName.c_str(), callback); + TrackDesign* trackDesign = static_cast(intent->GetPointerExtra(INTENT_EXTRA_TRACK_DESIGN)); + rct_window* w = window_loadsave_open(type, defaultName.c_str(), callback, trackDesign); return w; } @@ -244,7 +248,7 @@ public: case WC_RIDE: { auto ride = get_ride(intent->GetSIntExtra(INTENT_EXTRA_RIDE_ID)); - return window_ride_main_open(ride); + return ride == nullptr ? nullptr : window_ride_main_open(ride); } case WC_TRACK_DESIGN_PLACE: return window_track_place_open((track_design_file_ref*)intent->GetPointerExtra(INTENT_EXTRA_TRACK_DESIGN)); @@ -370,24 +374,20 @@ public: case INTENT_ACTION_INVALIDATE_VEHICLE_WINDOW: { - rct_vehicle* vehicle = static_cast(intent.GetPointerExtra(INTENT_EXTRA_VEHICLE)); - int32_t viewVehicleIndex; - Ride* ride; - rct_window* w; - - w = window_find_by_number(WC_RIDE, vehicle->ride); + auto vehicle = static_cast(intent.GetPointerExtra(INTENT_EXTRA_VEHICLE)); + auto w = window_find_by_number(WC_RIDE, vehicle->ride); if (w == nullptr) return; - ride = get_ride(vehicle->ride); - viewVehicleIndex = w->ride.view - 1; - if (viewVehicleIndex < 0 || viewVehicleIndex >= ride->num_vehicles) + auto ride = get_ride(vehicle->ride); + auto viewVehicleIndex = w->ride.view - 1; + if (ride == nullptr || viewVehicleIndex < 0 || viewVehicleIndex >= ride->num_vehicles) return; if (vehicle->sprite_index != ride->vehicles[viewVehicleIndex]) return; - window_invalidate(w); + w->Invalidate(); break; } @@ -401,7 +401,7 @@ public: { w->vehicleIndex = 0; } - window_invalidate(w); + w->Invalidate(); } break; } @@ -432,7 +432,7 @@ public: rct_window* w = window_find_by_number(WC_BANNER, bannerIndex); if (w != nullptr) { - window_invalidate(w); + w->Invalidate(); } break; } @@ -503,7 +503,7 @@ public: // Make sure the viewport has correct coordinates set. viewport_update_position(mainWindow); - window_invalidate(mainWindow); + mainWindow->Invalidate(); } } diff --git a/src/openrct2-ui/WindowManager.h b/src/openrct2-ui/WindowManager.h index 0f13c8579f..4e6c0286eb 100644 --- a/src/openrct2-ui/WindowManager.h +++ b/src/openrct2-ui/WindowManager.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/audio/AudioChannel.cpp b/src/openrct2-ui/audio/AudioChannel.cpp index 6aba2cfec7..be7c09d1c4 100644 --- a/src/openrct2-ui/audio/AudioChannel.cpp +++ b/src/openrct2-ui/audio/AudioChannel.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/src/openrct2-ui/audio/AudioContext.cpp b/src/openrct2-ui/audio/AudioContext.cpp index ecb9caf59e..c9e5833bc3 100644 --- a/src/openrct2-ui/audio/AudioContext.cpp +++ b/src/openrct2-ui/audio/AudioContext.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/audio/AudioContext.h b/src/openrct2-ui/audio/AudioContext.h index a0df39e572..7078e59923 100644 --- a/src/openrct2-ui/audio/AudioContext.h +++ b/src/openrct2-ui/audio/AudioContext.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/audio/AudioFormat.h b/src/openrct2-ui/audio/AudioFormat.h index 40341bfd85..bba2feb6ed 100644 --- a/src/openrct2-ui/audio/AudioFormat.h +++ b/src/openrct2-ui/audio/AudioFormat.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/audio/AudioMixer.cpp b/src/openrct2-ui/audio/AudioMixer.cpp index a4d4f859fb..3295cc7c50 100644 --- a/src/openrct2-ui/audio/AudioMixer.cpp +++ b/src/openrct2-ui/audio/AudioMixer.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -44,7 +44,7 @@ namespace OpenRCT2::Audio uint8_t _settingSoundVolume = 0xFF; uint8_t _settingMusicVolume = 0xFF; - IAudioSource* _css1Sources[SOUND_MAXID] = { nullptr }; + IAudioSource* _css1Sources[RCT2SoundCount] = { nullptr }; IAudioSource* _musicSources[PATH_ID_END] = { nullptr }; std::vector _channelBuffer; @@ -185,9 +185,9 @@ namespace OpenRCT2::Audio _volume = volume; } - IAudioSource* GetSoundSource(int32_t id) override + IAudioSource* GetSoundSource(SoundId id) override { - return _css1Sources[id]; + return _css1Sources[static_cast(id)]; } IAudioSource* GetMusicSource(int32_t id) override @@ -223,7 +223,8 @@ namespace OpenRCT2::Audio { auto channel = *it; int32_t group = channel->GetGroup(); - if (group != MIXER_GROUP_SOUND || gConfigSound.sound_enabled) + if ((group != MIXER_GROUP_SOUND || gConfigSound.sound_enabled) && gConfigSound.master_sound_enabled + && gConfigSound.master_volume != 0) { MixChannel(channel, dst, length); } diff --git a/src/openrct2-ui/audio/FileAudioSource.cpp b/src/openrct2-ui/audio/FileAudioSource.cpp index 51e54e91f3..f623cfcb22 100644 --- a/src/openrct2-ui/audio/FileAudioSource.cpp +++ b/src/openrct2-ui/audio/FileAudioSource.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/audio/MemoryAudioSource.cpp b/src/openrct2-ui/audio/MemoryAudioSource.cpp index 64b85ab71b..693f159275 100644 --- a/src/openrct2-ui/audio/MemoryAudioSource.cpp +++ b/src/openrct2-ui/audio/MemoryAudioSource.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/BitmapReader.cpp b/src/openrct2-ui/drawing/BitmapReader.cpp index ef76134898..820e782ae4 100644 --- a/src/openrct2-ui/drawing/BitmapReader.cpp +++ b/src/openrct2-ui/drawing/BitmapReader.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/BitmapReader.h b/src/openrct2-ui/drawing/BitmapReader.h index 4328b01c15..232fdc3491 100644 --- a/src/openrct2-ui/drawing/BitmapReader.h +++ b/src/openrct2-ui/drawing/BitmapReader.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/DrawingEngineFactory.hpp b/src/openrct2-ui/drawing/engines/DrawingEngineFactory.hpp index 64924a7999..f78083a076 100644 --- a/src/openrct2-ui/drawing/engines/DrawingEngineFactory.hpp +++ b/src/openrct2-ui/drawing/engines/DrawingEngineFactory.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/HardwareDisplayDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/HardwareDisplayDrawingEngine.cpp index cc1895738e..8365a9f789 100644 --- a/src/openrct2-ui/drawing/engines/HardwareDisplayDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/HardwareDisplayDrawingEngine.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -62,14 +62,6 @@ public: ~HardwareDisplayDrawingEngine() override { - if (_screenTexture != nullptr) - { - SDL_DestroyTexture(_screenTexture); - } - if (_scaledScreenTexture != nullptr) - { - SDL_DestroyTexture(_scaledScreenTexture); - } SDL_FreeFormat(_screenTextureFormat); SDL_DestroyRenderer(_sdlRenderer); } @@ -85,6 +77,8 @@ public: { _useVsync = vsync; SDL_DestroyRenderer(_sdlRenderer); + _screenTexture = nullptr; + _scaledScreenTexture = nullptr; Initialise(); Resize(_uiContext->GetWidth(), _uiContext->GetHeight()); } diff --git a/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp index 440a0bd3d2..4ef671c301 100644 --- a/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/ApplyPaletteShader.cpp b/src/openrct2-ui/drawing/engines/opengl/ApplyPaletteShader.cpp index fe7e15c3d1..6ee690e44a 100644 --- a/src/openrct2-ui/drawing/engines/opengl/ApplyPaletteShader.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/ApplyPaletteShader.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/ApplyPaletteShader.h b/src/openrct2-ui/drawing/engines/opengl/ApplyPaletteShader.h index 8312da39ad..e0677a667a 100644 --- a/src/openrct2-ui/drawing/engines/opengl/ApplyPaletteShader.h +++ b/src/openrct2-ui/drawing/engines/opengl/ApplyPaletteShader.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.cpp b/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.cpp index 983b36e856..5eb10a33bd 100644 --- a/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.h b/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.h index df740994c8..c61c33b3ef 100644 --- a/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.h +++ b/src/openrct2-ui/drawing/engines/opengl/ApplyTransparencyShader.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h b/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h index 3d78a49e27..464c37a0f3 100644 --- a/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h +++ b/src/openrct2-ui/drawing/engines/opengl/DrawCommands.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/DrawLineShader.cpp b/src/openrct2-ui/drawing/engines/opengl/DrawLineShader.cpp index 8a4104f975..19cba2d1e2 100644 --- a/src/openrct2-ui/drawing/engines/opengl/DrawLineShader.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/DrawLineShader.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/DrawLineShader.h b/src/openrct2-ui/drawing/engines/opengl/DrawLineShader.h index c7c79c1a9c..2ecb36f0ec 100644 --- a/src/openrct2-ui/drawing/engines/opengl/DrawLineShader.h +++ b/src/openrct2-ui/drawing/engines/opengl/DrawLineShader.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/DrawRectShader.cpp b/src/openrct2-ui/drawing/engines/opengl/DrawRectShader.cpp index 4486471162..c2bf52ec31 100644 --- a/src/openrct2-ui/drawing/engines/opengl/DrawRectShader.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/DrawRectShader.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/DrawRectShader.h b/src/openrct2-ui/drawing/engines/opengl/DrawRectShader.h index d335e84f35..1d5f2dc081 100644 --- a/src/openrct2-ui/drawing/engines/opengl/DrawRectShader.h +++ b/src/openrct2-ui/drawing/engines/opengl/DrawRectShader.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/GLSLTypes.h b/src/openrct2-ui/drawing/engines/opengl/GLSLTypes.h index 0f13b94039..8a730ae853 100644 --- a/src/openrct2-ui/drawing/engines/opengl/GLSLTypes.h +++ b/src/openrct2-ui/drawing/engines/opengl/GLSLTypes.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLAPI.cpp b/src/openrct2-ui/drawing/engines/opengl/OpenGLAPI.cpp index 54404f6489..6408a2217d 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLAPI.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLAPI.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLAPI.h b/src/openrct2-ui/drawing/engines/opengl/OpenGLAPI.h index 06918caf23..a426f2058c 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLAPI.h +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLAPI.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLAPIProc.h b/src/openrct2-ui/drawing/engines/opengl/OpenGLAPIProc.h index b5c894af35..f0e3fa3313 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLAPIProc.h +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLAPIProc.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp index f3279ebb04..85623819a4 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -56,7 +56,6 @@ class OpenGLDrawingContext final : public IDrawingContext private: OpenGLDrawingEngine* _engine = nullptr; rct_drawpixelinfo* _dpi = nullptr; - ApplyTransparencyShader* _applyTransparencyShader = nullptr; DrawLineShader* _drawLineShader = nullptr; DrawRectShader* _drawRectShader = nullptr; @@ -115,6 +114,62 @@ public: void HandleTransparency(); void SetDPI(rct_drawpixelinfo* dpi); + rct_drawpixelinfo* GetDPI() const + { + return _dpi; + } +}; + +class OpenGLRainDrawer final : public IRainDrawer +{ + OpenGLDrawingContext* _drawingContext; + +public: + explicit OpenGLRainDrawer(OpenGLDrawingContext* drawingContext) + : _drawingContext(drawingContext) + { + } + + virtual void Draw(int32_t x, int32_t y, int32_t width, int32_t height, int32_t xStart, int32_t yStart) + { + const uint8_t* pattern = RainPattern; + + uint8_t patternXSpace = *pattern++; + uint8_t patternYSpace = *pattern++; + + uint8_t patternStartXOffset = xStart % patternXSpace; + uint8_t patternStartYOffset = yStart % patternYSpace; + + const auto* dpi = _drawingContext->GetDPI(); + + uint32_t pixelOffset = (dpi->pitch + dpi->width) * y + x; + uint8_t patternYPos = patternStartYOffset % patternYSpace; + + for (; height != 0; height--) + { + uint8_t patternX = pattern[patternYPos * 2]; + if (patternX != 0xFF) + { + uint32_t finalPixelOffset = width + pixelOffset; + + uint32_t xPixelOffset = pixelOffset; + xPixelOffset += ((uint8_t)(patternX - patternStartXOffset)) % patternXSpace; + + uint8_t patternPixel = pattern[patternYPos * 2 + 1]; + for (; xPixelOffset < finalPixelOffset; xPixelOffset += patternXSpace) + { + int32_t pixelX = xPixelOffset % dpi->width; + int32_t pixelY = (xPixelOffset / dpi->width) % dpi->height; + + _drawingContext->DrawLine(patternPixel, pixelX, pixelY, pixelX + 1, pixelY + 1); + } + } + + pixelOffset += dpi->pitch + dpi->width; + patternYPos++; + patternYPos %= patternYSpace; + } + } }; class OpenGLDrawingEngine : public IDrawingEngine @@ -138,6 +193,7 @@ private: OpenGLFramebuffer* _screenFramebuffer = nullptr; OpenGLFramebuffer* _scaleFramebuffer = nullptr; OpenGLFramebuffer* _smoothScaleFramebuffer = nullptr; + OpenGLRainDrawer _rainDrawer; public: SDL_Color Palette[256]; @@ -145,9 +201,11 @@ public: explicit OpenGLDrawingEngine(const std::shared_ptr& uiContext) : _uiContext(uiContext) + , _drawingContext(new OpenGLDrawingContext(this)) + , _rainDrawer(_drawingContext) { _window = (SDL_Window*)_uiContext->GetWindow(); - _drawingContext = new OpenGLDrawingContext(this); + _bitsDPI.DrawingEngine = this; # ifdef __ENABLE_LIGHTFX__ lightfx_set_available(false); # endif @@ -283,7 +341,8 @@ public: void PaintRain() override { - // Not implemented + _drawingContext->SetDPI(&_bitsDPI); + DrawRain(&_bitsDPI, &_rainDrawer); } std::string Screenshot() override @@ -510,7 +569,7 @@ void OpenGLDrawingContext::FillRect(uint32_t colour, int32_t left, int32_t top, if (colour & 0x1000000) { // cross-pattern - command.flags = DrawRectCommand::FLAG_CROSS_HATCH; + command.flags |= DrawRectCommand::FLAG_CROSS_HATCH; } else if (colour & 0x2000000) { @@ -881,7 +940,7 @@ void OpenGLDrawingContext::FlushCommandBuffers() void OpenGLDrawingContext::FlushLines() { - if (_commandBuffers.lines.size() == 0) + if (_commandBuffers.lines.empty()) return; _drawLineShader->Use(); @@ -892,7 +951,7 @@ void OpenGLDrawingContext::FlushLines() void OpenGLDrawingContext::FlushRectangles() { - if (_commandBuffers.rects.size() == 0) + if (_commandBuffers.rects.empty()) return; OpenGLAPI::SetTexture(0, GL_TEXTURE_2D_ARRAY, _textureCache->GetAtlasesTexture()); diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.cpp b/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.cpp index 8b2b01583d..e8951c9404 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -23,7 +23,7 @@ OpenGLFramebuffer::OpenGLFramebuffer(SDL_Window* window) _id = BACKBUFFER_ID; _texture = 0; _depth = 0; - SDL_GetWindowSize(window, &_width, &_height); + SDL_GL_GetDrawableSize(window, &_width, &_height); } OpenGLFramebuffer::OpenGLFramebuffer(int32_t width, int32_t height, bool depth, bool integer) diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.h b/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.h index 180daa1d96..8e226911af 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.h +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLFramebuffer.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLShaderProgram.cpp b/src/openrct2-ui/drawing/engines/opengl/OpenGLShaderProgram.cpp index 0d2738ac55..44f4e92f18 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLShaderProgram.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLShaderProgram.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLShaderProgram.h b/src/openrct2-ui/drawing/engines/opengl/OpenGLShaderProgram.h index f974bdb33f..6eef387c8d 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLShaderProgram.h +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLShaderProgram.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.cpp b/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.cpp index dd902c6a21..9d113dfb49 100644 --- a/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.h b/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.h index bbbe0767ae..d76c53d6ab 100644 --- a/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.h +++ b/src/openrct2-ui/drawing/engines/opengl/SwapFramebuffer.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp b/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp index 211bfb0558..16dee7ba1e 100644 --- a/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -30,6 +30,8 @@ TextureCache::~TextureCache() void TextureCache::InvalidateImage(uint32_t image) { + unique_lock lock(_mutex); + uint32_t index = _indexMap[image]; if (index == UNUSED_INDEX) return; @@ -61,18 +63,28 @@ void TextureCache::InvalidateImage(uint32_t image) BasicTextureInfo TextureCache::GetOrLoadImageTexture(uint32_t image) { + uint32_t index; + image &= 0x7FFFF; - uint32_t index = _indexMap[image]; - if (index != UNUSED_INDEX) + // Try to read cached texture first. { - const auto& info = _textureCache[index]; - return { - info.index, - info.normalizedBounds, - }; + shared_lock lock(_mutex); + + index = _indexMap[image]; + if (index != UNUSED_INDEX) + { + const auto& info = _textureCache[index]; + return { + info.index, + info.normalizedBounds, + }; + } } + // Load new texture. + unique_lock lock(_mutex); + index = (uint32_t)_textureCache.size(); AtlasTextureInfo info = LoadImageTexture(image); @@ -87,18 +99,27 @@ BasicTextureInfo TextureCache::GetOrLoadGlyphTexture(uint32_t image, uint8_t* pa { GlyphId glyphId; glyphId.Image = image; - std::copy_n(palette, sizeof(glyphId.Palette), (uint8_t*)&glyphId.Palette); - auto kvp = _glyphTextureMap.find(glyphId); - if (kvp != _glyphTextureMap.end()) + // Try to read cached texture first. { - const auto& info = kvp->second; - return { - info.index, - info.normalizedBounds, - }; + shared_lock lock(_mutex); + + std::copy_n(palette, sizeof(glyphId.Palette), (uint8_t*)&glyphId.Palette); + + auto kvp = _glyphTextureMap.find(glyphId); + if (kvp != _glyphTextureMap.end()) + { + const auto& info = kvp->second; + return { + info.index, + info.normalizedBounds, + }; + } } + // Load new texture. + unique_lock lock(_mutex); + auto cacheInfo = LoadGlyphTexture(image, palette); auto it = _glyphTextureMap.insert(std::make_pair(glyphId, cacheInfo)); @@ -155,7 +176,7 @@ void TextureCache::GeneratePaletteTexture() GLint y = PaletteToY(i); uint16_t image = palette_to_g1_offset[i]; auto element = gfx_get_g1_element(image); - gfx_draw_sprite_software(&dpi, image, -element->x_offset, y - element->y_offset, 0); + gfx_draw_sprite_software(&dpi, ImageId(image), -element->x_offset, y - element->y_offset); } glBindTexture(GL_TEXTURE_RECTANGLE, _paletteTexture); @@ -176,7 +197,7 @@ void TextureCache::EnlargeAtlasesTexture(GLuint newEntries) { // Retrieve current array data, growing buffer. oldPixels.resize(_atlasesTextureDimensions * _atlasesTextureDimensions * _atlasesTextureCapacity); - if (oldPixels.size() > 0) + if (!oldPixels.empty()) { glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, oldPixels.data()); } @@ -191,7 +212,7 @@ void TextureCache::EnlargeAtlasesTexture(GLuint newEntries) GL_RED_INTEGER, GL_UNSIGNED_BYTE, nullptr); // Restore old data - if (oldPixels.size() > 0) + if (!oldPixels.empty()) { glTexSubImage3D( GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, _atlasesTextureDimensions, _atlasesTextureDimensions, _atlasesTextureIndices, @@ -258,7 +279,7 @@ AtlasTextureInfo TextureCache::AllocateImage(int32_t imageWidth, int32_t imageHe int32_t atlasSize = (int32_t)powf(2, (float)Atlas::CalculateImageSizeOrder(imageWidth, imageHeight)); # ifdef DEBUG - log_verbose("new texture atlas #%d (size %d) allocated\n", atlasIndex, atlasSize); + log_verbose("new texture atlas #%d (size %d) allocated", atlasIndex, atlasSize); # endif _atlases.emplace_back(atlasIndex, atlasSize); @@ -278,7 +299,7 @@ rct_drawpixelinfo TextureCache::GetImageAsDPI(uint32_t image, uint32_t tertiaryC int32_t height = g1Element->height; rct_drawpixelinfo dpi = CreateDPI(width, height); - gfx_draw_sprite_software(&dpi, image, -g1Element->x_offset, -g1Element->y_offset, tertiaryColour); + gfx_draw_sprite_software(&dpi, ImageId::FromUInt32(image, tertiaryColour), -g1Element->x_offset, -g1Element->y_offset); return dpi; } @@ -289,7 +310,8 @@ rct_drawpixelinfo TextureCache::GetGlyphAsDPI(uint32_t image, uint8_t* palette) int32_t height = g1Element->height; rct_drawpixelinfo dpi = CreateDPI(width, height); - gfx_draw_sprite_palette_set_software(&dpi, image, -g1Element->x_offset, -g1Element->y_offset, palette, nullptr); + gfx_draw_sprite_palette_set_software( + &dpi, ImageId::FromUInt32(image), -g1Element->x_offset, -g1Element->y_offset, palette, nullptr); return dpi; } diff --git a/src/openrct2-ui/drawing/engines/opengl/TextureCache.h b/src/openrct2-ui/drawing/engines/opengl/TextureCache.h index 430f9658af..c8fc9d584c 100644 --- a/src/openrct2-ui/drawing/engines/opengl/TextureCache.h +++ b/src/openrct2-ui/drawing/engines/opengl/TextureCache.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -15,7 +15,11 @@ #include #include #include +#include #include +#ifndef __MACOSX__ +# include +#endif #include #include @@ -199,6 +203,16 @@ private: GLuint _paletteTexture = 0; +#ifndef __MACOSX__ + std::shared_mutex _mutex; + typedef std::shared_lock shared_lock; + typedef std::unique_lock unique_lock; +#else + std::mutex _mutex; + typedef std::unique_lock shared_lock; + typedef std::unique_lock unique_lock; +#endif + public: TextureCache(); ~TextureCache(); diff --git a/src/openrct2-ui/drawing/engines/opengl/TransparencyDepth.cpp b/src/openrct2-ui/drawing/engines/opengl/TransparencyDepth.cpp index d134ee1256..ebfbb2739a 100644 --- a/src/openrct2-ui/drawing/engines/opengl/TransparencyDepth.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/TransparencyDepth.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/drawing/engines/opengl/TransparencyDepth.h b/src/openrct2-ui/drawing/engines/opengl/TransparencyDepth.h index 5b824ff43e..0db234995b 100644 --- a/src/openrct2-ui/drawing/engines/opengl/TransparencyDepth.h +++ b/src/openrct2-ui/drawing/engines/opengl/TransparencyDepth.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/input/Input.cpp b/src/openrct2-ui/input/Input.cpp index 080cc29f27..21d42288a2 100644 --- a/src/openrct2-ui/input/Input.cpp +++ b/src/openrct2-ui/input/Input.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -36,6 +36,7 @@ static void input_handle_console(int32_t key) input = CONSOLE_INPUT_LINE_CLEAR; break; case SDL_SCANCODE_RETURN: + case SDL_SCANCODE_KP_ENTER: input = CONSOLE_INPUT_LINE_EXECUTE; break; case SDL_SCANCODE_UP: @@ -67,6 +68,7 @@ static void input_handle_chat(int32_t key) input = CHAT_INPUT_CLOSE; break; case SDL_SCANCODE_RETURN: + case SDL_SCANCODE_KP_ENTER: input = CHAT_INPUT_SEND; break; } @@ -79,7 +81,6 @@ static void input_handle_chat(int32_t key) static void game_handle_key_scroll() { rct_window* mainWindow; - int32_t scrollX, scrollY; mainWindow = window_get_main(); if (mainWindow == nullptr) @@ -97,16 +98,14 @@ static void game_handle_key_scroll() if (gChatOpen) return; - scrollX = 0; - scrollY = 0; const uint8_t* keysState = context_get_keys_state(); - get_keyboard_map_scroll(keysState, &scrollX, &scrollY); + auto scrollCoords = get_keyboard_map_scroll(keysState); - if (scrollX != 0 || scrollY != 0) + if (scrollCoords.x != 0 || scrollCoords.y != 0) { window_unfollow_sprite(mainWindow); } - input_scroll_viewport(scrollX, scrollY); + input_scroll_viewport(scrollCoords); } static int32_t input_scancode_to_rct_keycode(int32_t sdl_key) diff --git a/src/openrct2-ui/input/Input.h b/src/openrct2-ui/input/Input.h index f5c593b634..d37e91a736 100644 --- a/src/openrct2-ui/input/Input.h +++ b/src/openrct2-ui/input/Input.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/input/KeyboardShortcut.cpp b/src/openrct2-ui/input/KeyboardShortcut.cpp index 291fc6cc0e..8872f00f07 100644 --- a/src/openrct2-ui/input/KeyboardShortcut.cpp +++ b/src/openrct2-ui/input/KeyboardShortcut.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -33,6 +33,7 @@ #include #include #include +#include uint8_t gKeyboardShortcutChangeId; @@ -78,7 +79,7 @@ static void toggle_view_flag(int32_t viewportFlag) if (window != nullptr) { window->viewport->flags ^= viewportFlag; - window_invalidate(window); + window->Invalidate(); } } @@ -114,7 +115,7 @@ static void shortcut_pause_game() rct_window* window = window_find_by_class(WC_TOP_TOOLBAR); if (window != nullptr) { - window_invalidate(window); + window->Invalidate(); window_event_mouse_up_call(window, WC_TOP_TOOLBAR__WIDX_PAUSE); } } @@ -352,7 +353,7 @@ static void shortcut_adjust_land() rct_window* window = window_find_by_class(WC_TOP_TOOLBAR); if (window != nullptr) { - window_invalidate(window); + window->Invalidate(); window_event_mouse_up_call(window, WC_TOP_TOOLBAR__WIDX_LAND); } } @@ -371,7 +372,7 @@ static void shortcut_adjust_water() rct_window* window = window_find_by_class(WC_TOP_TOOLBAR); if (window != nullptr) { - window_invalidate(window); + window->Invalidate(); window_event_mouse_up_call(window, WC_TOP_TOOLBAR__WIDX_WATER); } } @@ -390,7 +391,7 @@ static void shortcut_build_scenery() rct_window* window = window_find_by_class(WC_TOP_TOOLBAR); if (window != nullptr) { - window_invalidate(window); + window->Invalidate(); window_event_mouse_up_call(window, WC_TOP_TOOLBAR__WIDX_SCENERY); } } @@ -409,7 +410,7 @@ static void shortcut_build_paths() rct_window* window = window_find_by_class(WC_TOP_TOOLBAR); if (window != nullptr) { - window_invalidate(window); + window->Invalidate(); window_event_mouse_up_call(window, WC_TOP_TOOLBAR__WIDX_PATH); } } @@ -445,7 +446,7 @@ static void shortcut_show_research_information() if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) return; - if (!(gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))) + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) { context_open_window_view(WV_RIDE_RESEARCH); } @@ -456,7 +457,7 @@ static void shortcut_show_rides_list() if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) return; - if (!(gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))) + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) { context_open_window(WC_RIDE_LIST); } @@ -467,7 +468,7 @@ static void shortcut_show_park_information() if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) return; - if (!(gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))) + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) { context_open_window(WC_PARK_INFORMATION); } @@ -478,7 +479,7 @@ static void shortcut_show_guest_list() if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) return; - if (!(gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))) + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) { context_open_window(WC_GUEST_LIST); } @@ -489,7 +490,7 @@ static void shortcut_show_staff_list() if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) return; - if (!(gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))) + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) { context_open_window(WC_STAFF_LIST); } @@ -500,7 +501,7 @@ static void shortcut_show_recent_messages() if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) return; - if (!(gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))) + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) context_open_window(WC_RECENT_NEWS); } @@ -564,7 +565,7 @@ static void shortcut_clear_scenery() rct_window* window = window_find_by_class(WC_TOP_TOOLBAR); if (window != nullptr) { - window_invalidate(window); + window->Invalidate(); window_event_mouse_up_call(window, WC_TOP_TOOLBAR__WIDX_CLEAR_SCENERY); } } @@ -753,7 +754,7 @@ static void shortcut_highlight_path_issues_toggle() static void shortcut_open_tile_inspector() { - if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO || !gConfigGeneral.debugging_tools) + if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO || !gConfigInterface.toolbar_show_cheats) return; context_open_window(WC_TILE_INSPECTOR); @@ -767,6 +768,33 @@ static void shortcut_advance_to_next_tick() gDoSingleUpdate = true; } +static void shortcut_open_scenery_picker() +{ + if ((gScreenFlags & (SCREEN_FLAGS_TITLE_DEMO | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) + || (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR && gS6Info.editor_step != EDITOR_STEP_LANDSCAPE_EDITOR)) + return; + + rct_window* window_scenery = window_find_by_class(WC_SCENERY); + if (window_scenery == nullptr) + { + rct_window* window_toolbar = window_find_by_class(WC_TOP_TOOLBAR); + if (window_toolbar != nullptr) + { + window_toolbar->Invalidate(); + window_event_mouse_up_call(window_toolbar, WC_TOP_TOOLBAR__WIDX_SCENERY); + } + } + + window_scenery = window_find_by_class(WC_SCENERY); + if (window_scenery != nullptr && !widget_is_disabled(window_scenery, WC_SCENERY__WIDX_SCENERY_EYEDROPPER_BUTTON) + && window_scenery->widgets[WC_SCENERY__WIDX_SCENERY_EYEDROPPER_BUTTON].type != WWT_EMPTY + && !gWindowSceneryEyedropperEnabled) + { + window_event_mouse_up_call(window_scenery, WC_SCENERY__WIDX_SCENERY_EYEDROPPER_BUTTON); + return; + } +} + namespace { const shortcut_action shortcut_table[SHORTCUT_COUNT] = { @@ -841,6 +869,7 @@ namespace shortcut_highlight_path_issues_toggle, shortcut_open_tile_inspector, shortcut_advance_to_next_tick, + shortcut_open_scenery_picker, }; } // anonymous namespace diff --git a/src/openrct2-ui/input/KeyboardShortcuts.cpp b/src/openrct2-ui/input/KeyboardShortcuts.cpp index 79bffcb762..2108b26814 100644 --- a/src/openrct2-ui/input/KeyboardShortcuts.cpp +++ b/src/openrct2-ui/input/KeyboardShortcuts.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -157,8 +157,9 @@ std::string KeyboardShortcuts::GetShortcutString(int32_t shortcut) const return std::string(buffer); } -void KeyboardShortcuts::GetKeyboardMapScroll(const uint8_t* keysState, int32_t* x, int32_t* y) const +ScreenCoordsXY KeyboardShortcuts::GetKeyboardMapScroll(const uint8_t* keysState) const { + ScreenCoordsXY screenCoords; for (int32_t shortcutId = SHORTCUT_SCROLL_MAP_UP; shortcutId <= SHORTCUT_SCROLL_MAP_RIGHT; shortcutId++) { uint16_t shortcutKey = _keys[shortcutId]; @@ -169,46 +170,37 @@ void KeyboardShortcuts::GetKeyboardMapScroll(const uint8_t* keysState, int32_t* if (!keysState[scancode]) continue; - if (shortcutKey & SHIFT) - { - if (!keysState[SDL_SCANCODE_LSHIFT] && !keysState[SDL_SCANCODE_RSHIFT]) - continue; - } - if (shortcutKey & CTRL) - { - if (!keysState[SDL_SCANCODE_LCTRL] && !keysState[SDL_SCANCODE_RCTRL]) - continue; - } - if (shortcutKey & ALT) - { - if (!keysState[SDL_SCANCODE_LALT] && !keysState[SDL_SCANCODE_RALT]) - continue; - } + // Check if SHIFT is either set in the shortcut key and currently pressed, + // or not set in the shortcut key and not currently pressed (in other words: check if they match). + if ((bool)(shortcutKey & SHIFT) != (keysState[SDL_SCANCODE_LSHIFT] || keysState[SDL_SCANCODE_RSHIFT])) + continue; + if ((bool)(shortcutKey & CTRL) != (keysState[SDL_SCANCODE_LCTRL] || keysState[SDL_SCANCODE_RCTRL])) + continue; + if ((bool)(shortcutKey & ALT) != (keysState[SDL_SCANCODE_LALT] || keysState[SDL_SCANCODE_RALT])) + continue; #ifdef __MACOSX__ - if (shortcutKey & CMD) - { - if (!keysState[SDL_SCANCODE_LGUI] && !keysState[SDL_SCANCODE_RGUI]) - continue; - } + if ((bool)(shortcutKey & CMD) != (keysState[SDL_SCANCODE_LGUI] || keysState[SDL_SCANCODE_RGUI])) + continue; #endif switch (shortcutId) { case SHORTCUT_SCROLL_MAP_UP: - *y = -1; + screenCoords.y = -1; break; case SHORTCUT_SCROLL_MAP_LEFT: - *x = -1; + screenCoords.x = -1; break; case SHORTCUT_SCROLL_MAP_DOWN: - *y = 1; + screenCoords.y = 1; break; case SHORTCUT_SCROLL_MAP_RIGHT: - *x = 1; + screenCoords.x = 1; break; default: break; } } + return screenCoords; } void keyboard_shortcuts_reset() @@ -242,9 +234,9 @@ void keyboard_shortcuts_format_string(char* buffer, size_t bufferSize, int32_t s String::Set(buffer, bufferSize, str.c_str()); } -void get_keyboard_map_scroll(const uint8_t* keysState, int32_t* x, int32_t* y) +ScreenCoordsXY get_keyboard_map_scroll(const uint8_t* keysState) { - _instance->GetKeyboardMapScroll(keysState, x, y); + return _instance->GetKeyboardMapScroll(keysState); } // Default keyboard shortcuts @@ -317,4 +309,6 @@ const uint16_t KeyboardShortcuts::DefaultKeys[SHORTCUT_COUNT] = { SHORTCUT_UNDEFINED, // SHORTCUT_VIEW_CLIPPING SDL_SCANCODE_I, // SHORTCUT_HIGHLIGHT_PATH_ISSUES_TOGGLE SHORTCUT_UNDEFINED, // SHORTCUT_TILE_INSPECTOR + SHORTCUT_UNDEFINED, // SHORTCUT_ADVANCE_TO_NEXT_TICK + SHORTCUT_UNDEFINED, // SHORTCUT_SCENERY_PICKER }; diff --git a/src/openrct2-ui/input/KeyboardShortcuts.h b/src/openrct2-ui/input/KeyboardShortcuts.h index 9b546be59e..48090154c2 100644 --- a/src/openrct2-ui/input/KeyboardShortcuts.h +++ b/src/openrct2-ui/input/KeyboardShortcuts.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,6 +11,7 @@ #include #include +#include #define SHIFT 0x100 #define CTRL 0x200 @@ -95,6 +96,7 @@ enum SHORTCUT_HIGHLIGHT_PATH_ISSUES_TOGGLE, SHORTCUT_TILE_INSPECTOR, SHORTCUT_ADVANCE_TO_NEXT_TICK, + SHORTCUT_SCENERY_PICKER, SHORTCUT_COUNT, @@ -130,7 +132,7 @@ namespace OpenRCT2 void Set(int32_t key); int32_t GetFromKey(int32_t key); - void GetKeyboardMapScroll(const uint8_t* keysState, int32_t* x, int32_t* y) const; + ScreenCoordsXY GetKeyboardMapScroll(const uint8_t* keysState) const; }; } // namespace Input } // namespace OpenRCT2 @@ -150,4 +152,4 @@ void keyboard_shortcut_handle(int32_t key); void keyboard_shortcut_handle_command(int32_t shortcutIndex); void keyboard_shortcut_format_string(char* buffer, size_t size, uint16_t shortcutKey); -void get_keyboard_map_scroll(const uint8_t* keysState, int32_t* x, int32_t* y); +ScreenCoordsXY get_keyboard_map_scroll(const uint8_t* keysState); diff --git a/src/openrct2-ui/input/MouseInput.cpp b/src/openrct2-ui/input/MouseInput.cpp index 316645ccbb..e4183d3a42 100644 --- a/src/openrct2-ui/input/MouseInput.cpp +++ b/src/openrct2-ui/input/MouseInput.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -63,31 +63,31 @@ int32_t gTooltipCursorY; static int16_t _clickRepeatTicks; -static int32_t game_get_next_input(int32_t* x, int32_t* y); -static void input_widget_over(int32_t x, int32_t y, rct_window* w, rct_widgetindex widgetIndex); +static int32_t game_get_next_input(ScreenCoordsXY& screenCoords); +static void input_widget_over(ScreenCoordsXY screenCoords, rct_window* w, rct_widgetindex widgetIndex); static void input_widget_over_change_check( rct_windowclass windowClass, rct_windownumber windowNumber, rct_widgetindex widgetIndex); static void input_widget_over_flatbutton_invalidate(); -void process_mouse_over(int32_t x, int32_t y); -void process_mouse_tool(int32_t x, int32_t y); +void process_mouse_over(ScreenCoordsXY screenCoords); +void process_mouse_tool(ScreenCoordsXY screenCoords); void invalidate_scroll(); static rct_mouse_data* get_mouse_input(); -void tile_element_right_click(int32_t type, TileElement* tileElement, int32_t x, int32_t y); -static void game_handle_input_mouse(int32_t x, int32_t y, int32_t state); -static void input_widget_left(int32_t x, int32_t y, rct_window* w, rct_widgetindex widgetIndex); +void tile_element_right_click(int32_t type, TileElement* tileElement, ScreenCoordsXY screenCoords); +static void game_handle_input_mouse(ScreenCoordsXY screenCoords, int32_t state); +static void input_widget_left(ScreenCoordsXY screenCoords, rct_window* w, rct_widgetindex widgetIndex); void input_state_widget_pressed( - int32_t x, int32_t y, int32_t state, rct_widgetindex widgetIndex, rct_window* w, rct_widget* widget); + ScreenCoordsXY screenCoords, int32_t state, rct_widgetindex widgetIndex, rct_window* w, rct_widget* widget); void set_cursor(uint8_t cursor_id); -static void input_window_position_continue(rct_window* w, int32_t lastX, int32_t lastY, int32_t newX, int32_t newY); -static void input_window_position_end(rct_window* w, int32_t x, int32_t y); -static void input_window_resize_begin(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y); -static void input_window_resize_continue(rct_window* w, int32_t x, int32_t y); +static void input_window_position_continue(rct_window* w, ScreenCoordsXY lastScreenCoords, ScreenCoordsXY newScreenCoords); +static void input_window_position_end(rct_window* w, ScreenCoordsXY screenCoords); +static void input_window_resize_begin(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); +static void input_window_resize_continue(rct_window* w, ScreenCoordsXY screenCoords); static void input_window_resize_end(); static void input_viewport_drag_begin(rct_window* w); static void input_viewport_drag_continue(); static void input_viewport_drag_end(); -static void input_scroll_begin(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y); -static void input_scroll_continue(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y); +static void input_scroll_begin(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); +static void input_scroll_continue(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); static void input_scroll_end(); static void input_scroll_part_update_hthumb(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t scroll_id); static void input_scroll_part_update_hleft(rct_window* w, rct_widgetindex widgetIndex, int32_t scroll_id); @@ -95,7 +95,7 @@ static void input_scroll_part_update_hright(rct_window* w, rct_widgetindex widge static void input_scroll_part_update_vthumb(rct_window* w, rct_widgetindex widgetIndex, int32_t y, int32_t scroll_id); static void input_scroll_part_update_vtop(rct_window* w, rct_widgetindex widgetIndex, int32_t scroll_id); static void input_scroll_part_update_vbottom(rct_window* w, rct_widgetindex widgetIndex, int32_t scroll_id); -static void input_update_tooltip(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y); +static void input_update_tooltip(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); #pragma region Mouse input @@ -105,63 +105,54 @@ static void input_update_tooltip(rct_window* w, rct_widgetindex widgetIndex, int */ void game_handle_input() { - // NOTE: g_window_list may change during the event callbacks. - for (size_t i = g_window_list.size(); i > 0; i--) - { - auto& w = g_window_list[i - 1]; - window_event_periodic_update_call(w.get()); - } + window_visit_each([](rct_window* w) { window_event_periodic_update_call(w); }); invalidate_all_windows_after_input(); - int32_t x, y, state; - while ((state = game_get_next_input(&x, &y)) != MOUSE_STATE_RELEASED) + int32_t state; + ScreenCoordsXY screenCoords; + while ((state = game_get_next_input(screenCoords)) != MOUSE_STATE_RELEASED) { - game_handle_input_mouse(x, y, state & 0xFF); + game_handle_input_mouse(screenCoords, state & 0xFF); } if (_inputFlags & INPUT_FLAG_5) { - game_handle_input_mouse(x, y, state); + game_handle_input_mouse(screenCoords, state); } - else if (x != MONEY32_UNDEFINED) + else if (screenCoords.x != MONEY32_UNDEFINED) { int32_t screenWidth = context_get_width(); int32_t screenHeight = context_get_height(); - x = std::clamp(x, 0, screenWidth - 1); - y = std::clamp(y, 0, screenHeight - 1); + screenCoords.x = std::clamp(screenCoords.x, 0, screenWidth - 1); + screenCoords.y = std::clamp(screenCoords.y, 0, screenHeight - 1); - game_handle_input_mouse(x, y, state); - process_mouse_over(x, y); - process_mouse_tool(x, y); + game_handle_input_mouse(screenCoords, state); + process_mouse_over(screenCoords); + process_mouse_tool(screenCoords); } - // NOTE: g_window_list may change during the event callbacks. - for (size_t i = g_window_list.size(); i > 0; i--) - { - auto& w = g_window_list[i - 1]; - window_event_unknown_08_call(w.get()); - } + window_visit_each([](rct_window* w) { window_event_unknown_08_call(w); }); } /** * * rct2: 0x006E83C7 */ -static int32_t game_get_next_input(int32_t* x, int32_t* y) +static int32_t game_get_next_input(ScreenCoordsXY& screenCoords) { rct_mouse_data* input = get_mouse_input(); if (input == nullptr) { const CursorState* cursorState = context_get_cursor_state(); - *x = cursorState->x; - *y = cursorState->y; + screenCoords.x = cursorState->x; + screenCoords.y = cursorState->y; return 0; } else { - *x = input->x; - *y = input->y; + screenCoords.x = input->x; + screenCoords.y = input->y; return input->state; } } @@ -189,11 +180,11 @@ static rct_mouse_data* get_mouse_input() * * rct2: 0x006E957F */ -static void input_scroll_drag_begin(int32_t x, int32_t y, rct_window* w, rct_widgetindex widgetIndex) +static void input_scroll_drag_begin(ScreenCoordsXY screenCoords, rct_window* w, rct_widgetindex widgetIndex) { _inputState = INPUT_STATE_SCROLL_RIGHT; - gInputDragLastX = x; - gInputDragLastY = y; + gInputDragLastX = screenCoords.x; + gInputDragLastY = screenCoords.y; _dragWidget.window_classification = w->classification; _dragWidget.window_number = w->number; _dragWidget.widget_index = widgetIndex; @@ -207,7 +198,7 @@ static void input_scroll_drag_begin(int32_t x, int32_t y, rct_window* w, rct_wid * Based on (heavily changed) * rct2: 0x006E9E0E, 0x006E9ED0 */ -static void input_scroll_drag_continue(int32_t x, int32_t y, rct_window* w) +static void input_scroll_drag_continue(ScreenCoordsXY screenCoords, rct_window* w) { rct_widgetindex widgetIndex = _dragWidget.widget_index; uint8_t scrollIndex = _dragScrollIndex; @@ -216,8 +207,8 @@ static void input_scroll_drag_continue(int32_t x, int32_t y, rct_window* w) rct_scroll* scroll = &w->scrolls[scrollIndex]; int32_t dx, dy; - dx = x - gInputDragLastX; - dy = y - gInputDragLastY; + dx = screenCoords.x - gInputDragLastX; + dy = screenCoords.y - gInputDragLastY; if (scroll->flags & HSCROLLBAR_VISIBLE) { @@ -250,7 +241,7 @@ static void input_scroll_drag_continue(int32_t x, int32_t y, rct_window* w) * * rct2: 0x006E8ACB */ -static void input_scroll_right(int32_t x, int32_t y, int32_t state) +static void input_scroll_right(ScreenCoordsXY screenCoords, int32_t state) { rct_window* w = window_find_by_number(_dragWidget.window_classification, _dragWidget.window_number); if (w == nullptr) @@ -264,10 +255,10 @@ static void input_scroll_right(int32_t x, int32_t y, int32_t state) { case MOUSE_STATE_RELEASED: _ticksSinceDragStart += gCurrentDeltaTime; - if (x != 0 || y != 0) + if (screenCoords.x != 0 || screenCoords.y != 0) { _ticksSinceDragStart = 1000; - input_scroll_drag_continue(x, y, w); + input_scroll_drag_continue(screenCoords, w); } break; case MOUSE_STATE_RIGHT_RELEASE: @@ -281,30 +272,30 @@ static void input_scroll_right(int32_t x, int32_t y, int32_t state) * * rct2: 0x006E8655 */ -static void game_handle_input_mouse(int32_t x, int32_t y, int32_t state) +static void game_handle_input_mouse(ScreenCoordsXY screenCoords, int32_t state) { rct_window* w; rct_widget* widget; rct_widgetindex widgetIndex; // Get window and widget under cursor position - w = window_find_from_point(x, y); - widgetIndex = w == nullptr ? -1 : window_find_widget_from_point(w, x, y); + w = window_find_from_point(screenCoords); + widgetIndex = w == nullptr ? -1 : window_find_widget_from_point(w, screenCoords); widget = widgetIndex == -1 ? nullptr : &w->widgets[widgetIndex]; switch (_inputState) { case INPUT_STATE_RESET: - window_tooltip_reset(x, y); + window_tooltip_reset(screenCoords); // fall-through case INPUT_STATE_NORMAL: switch (state) { case MOUSE_STATE_RELEASED: - input_widget_over(x, y, w, widgetIndex); + input_widget_over(screenCoords, w, widgetIndex); break; case MOUSE_STATE_LEFT_PRESS: - input_widget_left(x, y, w, widgetIndex); + input_widget_left(screenCoords, w, widgetIndex); break; case MOUSE_STATE_RIGHT_PRESS: window_close_by_class(WC_TOOLTIP); @@ -325,7 +316,7 @@ static void game_handle_input_mouse(int32_t x, int32_t y, int32_t state) } break; case WWT_SCROLL: - input_scroll_drag_begin(x, y, w, widgetIndex); + input_scroll_drag_begin(screenCoords, w, widgetIndex); break; } } @@ -333,7 +324,7 @@ static void game_handle_input_mouse(int32_t x, int32_t y, int32_t state) } break; case INPUT_STATE_WIDGET_PRESSED: - input_state_widget_pressed(x, y, state, widgetIndex, w, widget); + input_state_widget_pressed(screenCoords, state, widgetIndex, w, widget); break; case INPUT_STATE_POSITIONING_WINDOW: w = window_find_by_number(_dragWidget.window_classification, _dragWidget.window_number); @@ -343,10 +334,10 @@ static void game_handle_input_mouse(int32_t x, int32_t y, int32_t state) } else { - input_window_position_continue(w, gInputDragLastX, gInputDragLastY, x, y); + input_window_position_continue(w, ScreenCoordsXY(gInputDragLastX, gInputDragLastY), screenCoords); if (state == MOUSE_STATE_LEFT_RELEASE) { - input_window_position_end(w, x, y); + input_window_position_end(w, screenCoords); } } break; @@ -361,12 +352,12 @@ static void game_handle_input_mouse(int32_t x, int32_t y, int32_t state) if (_ticksSinceDragStart < 500) { // If the user pressed the right mouse button for less than 500 ticks, interpret as right click - viewport_interaction_right_click(x, y); + viewport_interaction_right_click(screenCoords.x, screenCoords.y); } } break; case INPUT_STATE_DROPDOWN_ACTIVE: - input_state_widget_pressed(x, y, state, widgetIndex, w, widget); + input_state_widget_pressed(screenCoords, state, widgetIndex, w, widget); break; case INPUT_STATE_VIEWPORT_LEFT: w = window_find_by_number(_dragWidget.window_classification, _dragWidget.window_number); @@ -397,7 +388,7 @@ static void game_handle_input_mouse(int32_t x, int32_t y, int32_t state) break; } - window_event_tool_drag_call(w, gCurrentToolWidget.widget_index, x, y); + window_event_tool_drag_call(w, gCurrentToolWidget.widget_index, screenCoords); break; case MOUSE_STATE_LEFT_RELEASE: _inputState = INPUT_STATE_RESET; @@ -409,12 +400,12 @@ static void game_handle_input_mouse(int32_t x, int32_t y, int32_t state) gCurrentToolWidget.window_classification, gCurrentToolWidget.window_number); if (w != nullptr) { - window_event_tool_up_call(w, gCurrentToolWidget.widget_index, x, y); + window_event_tool_up_call(w, gCurrentToolWidget.widget_index, screenCoords); } } else if (!(_inputFlags & INPUT_FLAG_4)) { - viewport_interaction_left_click(x, y); + viewport_interaction_left_click(screenCoords.x, screenCoords.y); } } break; @@ -424,7 +415,7 @@ static void game_handle_input_mouse(int32_t x, int32_t y, int32_t state) switch (state) { case MOUSE_STATE_RELEASED: - input_scroll_continue(w, widgetIndex, x, y); + input_scroll_continue(w, widgetIndex, screenCoords); break; case MOUSE_STATE_LEFT_RELEASE: input_scroll_end(); @@ -445,49 +436,49 @@ static void game_handle_input_mouse(int32_t x, int32_t y, int32_t state) } if (state == MOUSE_STATE_RELEASED || state == MOUSE_STATE_LEFT_RELEASE) { - input_window_resize_continue(w, x, y); + input_window_resize_continue(w, screenCoords); } } break; case INPUT_STATE_SCROLL_RIGHT: - input_scroll_right(x, y, state); + input_scroll_right(screenCoords, state); break; } } #pragma region Window positioning / resizing -void input_window_position_begin(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) +void input_window_position_begin(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords) { _inputState = INPUT_STATE_POSITIONING_WINDOW; - gInputDragLastX = x - w->x; - gInputDragLastY = y - w->y; + gInputDragLastX = screenCoords.x - w->x; + gInputDragLastY = screenCoords.y - w->y; _dragWidget.window_classification = w->classification; _dragWidget.window_number = w->number; _dragWidget.widget_index = widgetIndex; } -static void input_window_position_continue(rct_window* w, int32_t lastX, int32_t lastY, int32_t newX, int32_t newY) +static void input_window_position_continue(rct_window* w, ScreenCoordsXY lastScreenCoords, ScreenCoordsXY newScreenCoords) { int32_t snapProximity; snapProximity = (w->flags & WF_NO_SNAPPING) ? 0 : gConfigGeneral.window_snap_proximity; - window_move_and_snap(w, newX - lastX, newY - lastY, snapProximity); + window_move_and_snap(w, newScreenCoords - lastScreenCoords, snapProximity); } -static void input_window_position_end(rct_window* w, int32_t x, int32_t y) +static void input_window_position_end(rct_window* w, ScreenCoordsXY screenCoords) { _inputState = INPUT_STATE_NORMAL; gTooltipTimeout = 0; gTooltipWidget = _dragWidget; - window_event_moved_call(w, x, y); + window_event_moved_call(w, screenCoords); } -static void input_window_resize_begin(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) +static void input_window_resize_begin(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords) { _inputState = INPUT_STATE_RESIZING; - gInputDragLastX = x; - gInputDragLastY = y; + gInputDragLastX = screenCoords.x; + gInputDragLastY = screenCoords.y; _dragWidget.window_classification = w->classification; _dragWidget.window_number = w->number; _dragWidget.widget_index = widgetIndex; @@ -495,13 +486,13 @@ static void input_window_resize_begin(rct_window* w, rct_widgetindex widgetIndex _originalWindowHeight = w->height; } -static void input_window_resize_continue(rct_window* w, int32_t x, int32_t y) +static void input_window_resize_continue(rct_window* w, ScreenCoordsXY screenCoords) { - if (y < (int32_t)context_get_height() - 2) + if (screenCoords.y < (int32_t)context_get_height() - 2) { int32_t dx, dy, targetWidth, targetHeight; - dx = x - gInputDragLastX; - dy = y - gInputDragLastY; + dx = screenCoords.x - gInputDragLastX; + dy = screenCoords.y - gInputDragLastY; targetWidth = _originalWindowWidth + dx; targetHeight = _originalWindowHeight + dy; @@ -608,7 +599,7 @@ static void input_viewport_drag_end() #pragma region Scroll bars -static void input_scroll_begin(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) +static void input_scroll_begin(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords) { rct_widget* widget; @@ -618,19 +609,19 @@ static void input_scroll_begin(rct_window* w, rct_widgetindex widgetIndex, int32 gPressedWidget.window_classification = w->classification; gPressedWidget.window_number = w->number; gPressedWidget.widget_index = widgetIndex; - gTooltipCursorX = x; - gTooltipCursorY = y; + gTooltipCursorX = screenCoords.x; + gTooltipCursorY = screenCoords.y; int32_t eax, ebx, scroll_area, scroll_id; scroll_id = 0; // safety - widget_scroll_get_part(w, widget, x, y, &eax, &ebx, &scroll_area, &scroll_id); + widget_scroll_get_part(w, widget, screenCoords.x, screenCoords.y, &eax, &ebx, &scroll_area, &scroll_id); _currentScrollArea = scroll_area; _currentScrollIndex = scroll_id; window_event_unknown_15_call(w, scroll_id, scroll_area); if (scroll_area == SCROLL_PART_VIEW) { - window_event_scroll_mousedown_call(w, scroll_id, eax, ebx); + window_event_scroll_mousedown_call(w, scroll_id, ScreenCoordsXY(eax, ebx)); return; } @@ -680,7 +671,7 @@ static void input_scroll_begin(rct_window* w, rct_widgetindex widgetIndex, int32 window_invalidate_by_number(widgetIndex, w->classification); } -static void input_scroll_continue(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) +static void input_scroll_continue(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords) { rct_widget* widget; int32_t scroll_part, scroll_id; @@ -696,26 +687,26 @@ static void input_scroll_continue(rct_window* w, rct_widgetindex widgetIndex, in return; } - widget_scroll_get_part(w, widget, x, y, &x2, &y2, &scroll_part, &scroll_id); + widget_scroll_get_part(w, widget, screenCoords.x, screenCoords.y, &x2, &y2, &scroll_part, &scroll_id); if (_currentScrollArea == SCROLL_PART_HSCROLLBAR_THUMB) { int32_t originalTooltipCursorX = gTooltipCursorX; - gTooltipCursorX = x; - input_scroll_part_update_hthumb(w, widgetIndex, x - originalTooltipCursorX, scroll_id); + gTooltipCursorX = screenCoords.x; + input_scroll_part_update_hthumb(w, widgetIndex, screenCoords.x - originalTooltipCursorX, scroll_id); return; } if (_currentScrollArea == SCROLL_PART_VSCROLLBAR_THUMB) { int32_t originalTooltipCursorY = gTooltipCursorY; - gTooltipCursorY = y; - input_scroll_part_update_vthumb(w, widgetIndex, y - originalTooltipCursorY, scroll_id); + gTooltipCursorY = screenCoords.y; + input_scroll_part_update_vthumb(w, widgetIndex, screenCoords.y - originalTooltipCursorY, scroll_id); return; } - x = x2; - y = y2; + screenCoords.x = x2; + screenCoords.y = y2; if (scroll_part != _currentScrollArea) { @@ -726,7 +717,7 @@ static void input_scroll_continue(rct_window* w, rct_widgetindex widgetIndex, in switch (scroll_part) { case SCROLL_PART_VIEW: - window_event_scroll_mousedrag_call(w, scroll_id, x, y); + window_event_scroll_mousedrag_call(w, scroll_id, screenCoords); break; case SCROLL_PART_HSCROLLBAR_LEFT: input_scroll_part_update_hleft(w, widgetIndex, scroll_id); @@ -920,7 +911,7 @@ static void input_scroll_part_update_vbottom(rct_window* w, rct_widgetindex widg * * rct2: 0x006E9253 */ -static void input_widget_over(int32_t x, int32_t y, rct_window* w, rct_widgetindex widgetIndex) +static void input_widget_over(ScreenCoordsXY screenCoords, rct_window* w, rct_widgetindex widgetIndex) { rct_windowclass windowClass = WC_NULL; rct_windownumber windowNumber = 0; @@ -938,24 +929,24 @@ static void input_widget_over(int32_t x, int32_t y, rct_window* w, rct_widgetind if (w != nullptr && widgetIndex != -1 && widget->type == WWT_SCROLL) { int32_t eax, ebx, scroll_part, edx; - widget_scroll_get_part(w, widget, x, y, &eax, &ebx, &scroll_part, &edx); + widget_scroll_get_part(w, widget, screenCoords.x, screenCoords.y, &eax, &ebx, &scroll_part, &edx); if (scroll_part != SCROLL_PART_VIEW) window_tooltip_close(); else { - window_event_scroll_mouseover_call(w, edx, eax, ebx); - input_update_tooltip(w, widgetIndex, x, y); + window_event_scroll_mouseover_call(w, edx, ScreenCoordsXY(eax, ebx)); + input_update_tooltip(w, widgetIndex, screenCoords); } } else { - input_update_tooltip(w, widgetIndex, x, y); + input_update_tooltip(w, widgetIndex, screenCoords); } gTooltipTimeout = 0; - gTooltipCursorX = x; - gTooltipCursorY = y; + gTooltipCursorX = screenCoords.x; + gTooltipCursorY = screenCoords.y; } /** @@ -1009,7 +1000,7 @@ static void input_widget_over_flatbutton_invalidate() * * rct2: 0x006E95F9 */ -static void input_widget_left(int32_t x, int32_t y, rct_window* w, rct_widgetindex widgetIndex) +static void input_widget_left(ScreenCoordsXY screenCoords, rct_window* w, rct_widgetindex widgetIndex) { rct_windowclass windowClass = WC_NULL; rct_windownumber windowNumber = 0; @@ -1045,13 +1036,13 @@ static void input_widget_left(int32_t x, int32_t y, rct_window* w, rct_widgetind { case WWT_FRAME: case WWT_RESIZE: - if (window_can_resize(w) && (x >= w->x + w->width - 19 && y >= w->y + w->height - 19)) - input_window_resize_begin(w, widgetIndex, x, y); + if (window_can_resize(w) && (screenCoords.x >= w->x + w->width - 19 && screenCoords.y >= w->y + w->height - 19)) + input_window_resize_begin(w, widgetIndex, screenCoords); break; case WWT_VIEWPORT: _inputState = INPUT_STATE_VIEWPORT_LEFT; - gInputDragLastX = x; - gInputDragLastY = y; + gInputDragLastX = screenCoords.x; + gInputDragLastY = screenCoords.y; _dragWidget.window_classification = windowClass; _dragWidget.window_number = windowNumber; if (_inputFlags & INPUT_FLAG_TOOL_ACTIVE) @@ -1059,21 +1050,21 @@ static void input_widget_left(int32_t x, int32_t y, rct_window* w, rct_widgetind w = window_find_by_number(gCurrentToolWidget.window_classification, gCurrentToolWidget.window_number); if (w != nullptr) { - window_event_tool_down_call(w, gCurrentToolWidget.widget_index, x, y); + window_event_tool_down_call(w, gCurrentToolWidget.widget_index, screenCoords); _inputFlags |= INPUT_FLAG_4; } } break; case WWT_CAPTION: - input_window_position_begin(w, widgetIndex, x, y); + input_window_position_begin(w, widgetIndex, screenCoords); break; case WWT_SCROLL: - input_scroll_begin(w, widgetIndex, x, y); + input_scroll_begin(w, widgetIndex, screenCoords); break; default: if (widget_is_enabled(w, widgetIndex) && !widget_is_disabled(w, widgetIndex)) { - audio_play_sound(SOUND_CLICK_1, 0, w->x + ((widget->left + widget->right) / 2)); + audio_play_sound(SoundId::Click1, 0, w->x + ((widget->left + widget->right) / 2)); // Set new cursor down widget gPressedWidget.window_classification = windowClass; @@ -1096,7 +1087,7 @@ static void input_widget_left(int32_t x, int32_t y, rct_window* w, rct_widgetind * * rct2: 0x006ED833 */ -void process_mouse_over(int32_t x, int32_t y) +void process_mouse_over(ScreenCoordsXY screenCoords) { rct_window* window; @@ -1104,13 +1095,13 @@ void process_mouse_over(int32_t x, int32_t y) cursorId = CURSOR_ARROW; set_map_tooltip_format_arg(0, rct_string_id, STR_NONE); - window = window_find_from_point(x, y); + window = window_find_from_point(screenCoords); if (window != nullptr) { int32_t ebx, edi; rct_window* subWindow; - rct_widgetindex widgetId = window_find_widget_from_point(window, x, y); + rct_widgetindex widgetId = window_find_widget_from_point(window, screenCoords); if (widgetId != -1) { switch (window->widgets[widgetId].type) @@ -1118,7 +1109,7 @@ void process_mouse_over(int32_t x, int32_t y) case WWT_VIEWPORT: if (!(_inputFlags & INPUT_FLAG_TOOL_ACTIVE)) { - if (viewport_interaction_left_over(x, y)) + if (viewport_interaction_left_over(screenCoords.x, screenCoords.y)) { set_cursor(CURSOR_HAND_POINT); return; @@ -1151,10 +1142,10 @@ void process_mouse_over(int32_t x, int32_t y) if (window->min_width == window->max_width && window->min_height == window->max_height) break; - if (x < window->x + window->width - 0x13) + if (screenCoords.x < window->x + window->width - 0x13) break; - if (y < window->y + window->height - 0x13) + if (screenCoords.y < window->y + window->height - 0x13) break; cursorId = CURSOR_DIAGONAL_ARROWS; @@ -1165,7 +1156,8 @@ void process_mouse_over(int32_t x, int32_t y) int32_t output_scroll_area, scroll_id; int32_t scroll_x, scroll_y; widget_scroll_get_part( - window, &window->widgets[widgetId], x, y, &scroll_x, &scroll_y, &output_scroll_area, &scroll_id); + window, &window->widgets[widgetId], screenCoords.x, screenCoords.y, &scroll_x, &scroll_y, + &output_scroll_area, &scroll_id); cursorId = scroll_id; if (output_scroll_area != SCROLL_PART_VIEW) { @@ -1173,13 +1165,13 @@ void process_mouse_over(int32_t x, int32_t y) break; } // Same as default but with scroll_x/y - cursorId = window_event_cursor_call(window, widgetId, scroll_x, scroll_y); + cursorId = window_event_cursor_call(window, widgetId, ScreenCoordsXY(scroll_x, scroll_y)); if (cursorId == -1) cursorId = CURSOR_ARROW; break; } default: - cursorId = window_event_cursor_call(window, widgetId, x, y); + cursorId = window_event_cursor_call(window, widgetId, screenCoords); if (cursorId == -1) cursorId = CURSOR_ARROW; break; @@ -1187,7 +1179,7 @@ void process_mouse_over(int32_t x, int32_t y) } } - viewport_interaction_right_over(x, y); + viewport_interaction_right_over(screenCoords.x, screenCoords.y); set_cursor(cursorId); } @@ -1195,7 +1187,7 @@ void process_mouse_over(int32_t x, int32_t y) * * rct2: 0x006ED801 */ -void process_mouse_tool(int32_t x, int32_t y) +void process_mouse_tool(ScreenCoordsXY screenCoords) { if (_inputFlags & INPUT_FLAG_TOOL_ACTIVE) { @@ -1204,7 +1196,7 @@ void process_mouse_tool(int32_t x, int32_t y) if (!w) tool_cancel(); else - window_event_tool_update_call(w, gCurrentToolWidget.widget_index, x, y); + window_event_tool_update_call(w, gCurrentToolWidget.widget_index, screenCoords); } } @@ -1213,7 +1205,7 @@ void process_mouse_tool(int32_t x, int32_t y) * rct2: 0x006E8DA7 */ void input_state_widget_pressed( - int32_t x, int32_t y, int32_t state, rct_widgetindex widgetIndex, rct_window* w, rct_widget* widget) + ScreenCoordsXY screenCoords, int32_t state, rct_widgetindex widgetIndex, rct_window* w, rct_widget* widget) { rct_windowclass cursor_w_class; rct_windownumber cursor_w_number; @@ -1276,7 +1268,7 @@ void input_state_widget_pressed( if (w->classification == WC_DROPDOWN) { - dropdown_index = dropdown_index_from_point(x, y, w); + dropdown_index = dropdown_index_from_point(screenCoords.x, screenCoords.y, w); if (dropdown_index == -1) { goto dropdown_cleanup; @@ -1355,7 +1347,7 @@ void input_state_widget_pressed( { int32_t mid_point_x = (widget->left + widget->right) / 2 + w->x; - audio_play_sound(SOUND_CLICK_2, 0, mid_point_x); + audio_play_sound(SoundId::Click2, 0, mid_point_x); } if (cursor_w_class != w->classification || cursor_w_number != w->number || widgetIndex != cursor_widgetIndex) break; @@ -1392,7 +1384,7 @@ void input_state_widget_pressed( if (w->classification == WC_DROPDOWN) { - int32_t dropdown_index = dropdown_index_from_point(x, y, w); + int32_t dropdown_index = dropdown_index_from_point(screenCoords.x, screenCoords.y, w); if (dropdown_index == -1) { return; @@ -1438,7 +1430,7 @@ void input_state_widget_pressed( STR_COLOUR_LIGHT_PINK_TIP, }; - window_tooltip_show(colourTooltips[dropdown_index], x, y); + window_tooltip_show(colourTooltips[dropdown_index], screenCoords); } if (dropdown_index < DROPDOWN_ITEMS_MAX_SIZE && dropdown_is_disabled(dropdown_index)) @@ -1461,17 +1453,17 @@ void input_state_widget_pressed( } } -static void input_update_tooltip(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) +static void input_update_tooltip(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords) { if (gTooltipWidget.window_classification == 255) { - if (gTooltipCursorX == x && gTooltipCursorY == y) + if (gTooltipCursorX == screenCoords.x && gTooltipCursorY == screenCoords.y) { _tooltipNotShownTicks++; if (_tooltipNotShownTicks > 50) { gTooltipTimeout = 0; - window_tooltip_open(w, widgetIndex, x, y); + window_tooltip_open(w, widgetIndex, screenCoords); } } } @@ -1549,7 +1541,7 @@ void invalidate_scroll() /** * rct2: 0x00406C96 */ -void store_mouse_input(int32_t state, int32_t x, int32_t y) +void store_mouse_input(int32_t state, ScreenCoordsXY screenCoords) { uint32_t writeIndex = _mouseInputQueueWriteIndex; uint32_t nextWriteIndex = (writeIndex + 1) % std::size(_mouseInputQueue); @@ -1558,8 +1550,8 @@ void store_mouse_input(int32_t state, int32_t x, int32_t y) if (nextWriteIndex != _mouseInputQueueReadIndex) { rct_mouse_data* item = &_mouseInputQueue[writeIndex]; - item->x = x; - item->y = y; + item->x = screenCoords.x; + item->y = screenCoords.y; item->state = state; _mouseInputQueueWriteIndex = nextWriteIndex; @@ -1597,7 +1589,7 @@ void game_handle_edge_scroll() else if (cursorState->y >= context_get_height() - 1) scrollY = 1; - input_scroll_viewport(scrollX, scrollY); + input_scroll_viewport(ScreenCoordsXY(scrollX, scrollY)); } bool input_test_place_object_modifier(PLACE_OBJECT_MODIFIER modifier) @@ -1605,17 +1597,17 @@ bool input_test_place_object_modifier(PLACE_OBJECT_MODIFIER modifier) return gInputPlaceObjectModifier & modifier; } -void input_scroll_viewport(int32_t scrollX, int32_t scrollY) +void input_scroll_viewport(ScreenCoordsXY scrollScreenCoords) { rct_window* mainWindow = window_get_main(); rct_viewport* viewport = mainWindow->viewport; const int32_t speed = gConfigGeneral.edge_scrolling_speed; - int32_t dx = scrollX * (speed << viewport->zoom); - int32_t dy = scrollY * (speed << viewport->zoom); + int32_t dx = scrollScreenCoords.x * (speed << viewport->zoom); + int32_t dy = scrollScreenCoords.y * (speed << viewport->zoom); - if (scrollX != 0) + if (scrollScreenCoords.x != 0) { // Speed up scrolling horizontally when at the edge of the map // so that the speed is consistent with vertical edge scrolling. @@ -1659,7 +1651,7 @@ void input_scroll_viewport(int32_t scrollX, int32_t scrollY) mainWindow->saved_view_x += dx; _inputFlags |= INPUT_FLAG_VIEWPORT_SCROLLING; } - if (scrollY != 0) + if (scrollScreenCoords.y != 0) { mainWindow->saved_view_y += dy; _inputFlags |= INPUT_FLAG_VIEWPORT_SCROLLING; diff --git a/src/openrct2-ui/interface/Dropdown.h b/src/openrct2-ui/interface/Dropdown.h index ff1002826a..b1c7e54303 100644 --- a/src/openrct2-ui/interface/Dropdown.h +++ b/src/openrct2-ui/interface/Dropdown.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/Graph.cpp b/src/openrct2-ui/interface/Graph.cpp index fd625524fe..8203908f44 100644 --- a/src/openrct2-ui/interface/Graph.cpp +++ b/src/openrct2-ui/interface/Graph.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/Graph.h b/src/openrct2-ui/interface/Graph.h index f818a6c059..288aa871cb 100644 --- a/src/openrct2-ui/interface/Graph.h +++ b/src/openrct2-ui/interface/Graph.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/InGameConsole.cpp b/src/openrct2-ui/interface/InGameConsole.cpp index 2f0c4c2c19..81ee2d3780 100644 --- a/src/openrct2-ui/interface/InGameConsole.cpp +++ b/src/openrct2-ui/interface/InGameConsole.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/InGameConsole.h b/src/openrct2-ui/interface/InGameConsole.h index faf91ab152..e84b48b155 100644 --- a/src/openrct2-ui/interface/InGameConsole.h +++ b/src/openrct2-ui/interface/InGameConsole.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/LandTool.cpp b/src/openrct2-ui/interface/LandTool.cpp index 6204676cdd..acfe9088c6 100644 --- a/src/openrct2-ui/interface/LandTool.cpp +++ b/src/openrct2-ui/interface/LandTool.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/LandTool.h b/src/openrct2-ui/interface/LandTool.h index 979ee421ff..24a2a3f3e4 100644 --- a/src/openrct2-ui/interface/LandTool.h +++ b/src/openrct2-ui/interface/LandTool.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/Theme.cpp b/src/openrct2-ui/interface/Theme.cpp index 8afd239d7d..9819ecd786 100644 --- a/src/openrct2-ui/interface/Theme.cpp +++ b/src/openrct2-ui/interface/Theme.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -866,10 +866,7 @@ rct_string_id theme_desc_get_name(rct_windowclass wc) void colour_scheme_update_all() { - for (auto& w : g_window_list) - { - colour_scheme_update(w.get()); - } + window_visit_each([](rct_window* w) { colour_scheme_update(w); }); } void colour_scheme_update(rct_window* window) diff --git a/src/openrct2-ui/interface/Theme.h b/src/openrct2-ui/interface/Theme.h index 9e9df952eb..4be5d8d5c9 100644 --- a/src/openrct2-ui/interface/Theme.h +++ b/src/openrct2-ui/interface/Theme.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/Viewport.h b/src/openrct2-ui/interface/Viewport.h index 1982da7c86..4618010908 100644 --- a/src/openrct2-ui/interface/Viewport.h +++ b/src/openrct2-ui/interface/Viewport.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/ViewportInteraction.cpp b/src/openrct2-ui/interface/ViewportInteraction.cpp index 0b9b40fb20..ab8a9b6c0a 100644 --- a/src/openrct2-ui/interface/ViewportInteraction.cpp +++ b/src/openrct2-ui/interface/ViewportInteraction.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,10 +14,13 @@ #include #include #include +#include #include #include +#include #include #include +#include #include #include #include @@ -92,9 +95,14 @@ int32_t viewport_interaction_get_item_left(int32_t x, int32_t y, viewport_intera ride_set_map_tooltip(tileElement); break; case VIEWPORT_INTERACTION_ITEM_PARK: - set_map_tooltip_format_arg(0, rct_string_id, gParkName); - set_map_tooltip_format_arg(2, uint32_t, gParkNameArgs); + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); + + set_map_tooltip_format_arg(0, rct_string_id, STR_STRING); + set_map_tooltip_format_arg(2, const char*, parkName); break; + } default: info->type = VIEWPORT_INTERACTION_ITEM_NONE; break; @@ -160,10 +168,11 @@ int32_t viewport_interaction_left_click(int32_t x, int32_t y) switch (info.sprite->generic.type) { case SPRITE_MISC_BALLOON: - game_do_command( - info.sprite->balloon.sprite_index, GAME_COMMAND_FLAG_APPLY, 0, 0, - GAME_COMMAND_BALLOON_PRESS, 0, 0); - break; + { + auto balloonPress = BalloonPressAction(info.sprite->AsBalloon()->sprite_index); + GameActions::Execute(&balloonPress); + } + break; case SPRITE_MISC_DUCK: duck_press(&info.sprite->duck); break; @@ -195,7 +204,6 @@ int32_t viewport_interaction_get_item_right(int32_t x, int32_t y, viewport_inter { TileElement* tileElement; rct_scenery_entry* sceneryEntry; - rct_banner* banner; Ride* ride; int32_t i, stationIndex; @@ -223,21 +231,24 @@ int32_t viewport_interaction_get_item_right(int32_t x, int32_t y, viewport_inter return info->type = VIEWPORT_INTERACTION_ITEM_NONE; ride = get_ride(sprite->vehicle.ride); - if (ride->status == RIDE_STATUS_CLOSED) + if (ride != nullptr && ride->status == RIDE_STATUS_CLOSED) { set_map_tooltip_format_arg(0, rct_string_id, STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY); - set_map_tooltip_format_arg(2, rct_string_id, ride->name); - set_map_tooltip_format_arg(4, uint32_t, ride->name_arguments); + ride->FormatNameTo(gMapTooltipFormatArgs + 2); } return info->type; case VIEWPORT_INTERACTION_ITEM_RIDE: + { if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) return info->type = VIEWPORT_INTERACTION_ITEM_NONE; if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH) return info->type = VIEWPORT_INTERACTION_ITEM_NONE; ride = get_ride(tile_element_get_ride_index(tileElement)); + if (ride == nullptr) + return info->type = VIEWPORT_INTERACTION_ITEM_NONE; + if (ride->status != RIDE_STATUS_CLOSED) return info->type; @@ -285,20 +296,18 @@ int32_t viewport_interaction_get_item_right(int32_t x, int32_t y, viewport_inter } else { - if (!gCheatsSandboxMode && !map_is_location_owned(info->x, info->y, tileElement->base_height << 4)) + if (!gCheatsSandboxMode && !map_is_location_owned({ info->x, info->y, tileElement->base_height << 4 })) { return info->type = VIEWPORT_INTERACTION_ITEM_NONE; } - set_map_tooltip_format_arg(2, rct_string_id, ride->name); - set_map_tooltip_format_arg(4, uint32_t, ride->name_arguments); + ride->FormatNameTo(gMapTooltipFormatArgs + 2); return info->type; } - set_map_tooltip_format_arg(4, rct_string_id, ride->name); - set_map_tooltip_format_arg(6, uint32_t, ride->name_arguments); + auto nameArgLen = ride->FormatNameTo(gMapTooltipFormatArgs + 4); set_map_tooltip_format_arg( - 10, rct_string_id, RideComponentNames[RideNameConvention[ride->type].station].capitalised); + 4 + nameArgLen, rct_string_id, RideComponentNames[RideNameConvention[ride->type].station].capitalised); if (tileElement->GetType() == TILE_ELEMENT_TYPE_ENTRANCE) stationIndex = tileElement->AsEntrance()->GetStationIndex(); @@ -309,15 +318,22 @@ int32_t viewport_interaction_get_item_right(int32_t x, int32_t y, viewport_inter if (ride->stations[i].Start.xy == RCT_XY8_UNDEFINED) stationIndex--; stationIndex++; - set_map_tooltip_format_arg(12, uint16_t, stationIndex); + set_map_tooltip_format_arg(4 + nameArgLen + 2, uint16_t, stationIndex); return info->type; - + } case VIEWPORT_INTERACTION_ITEM_WALL: sceneryEntry = tileElement->AsWall()->GetEntry(); if (sceneryEntry->wall.scrolling_mode != SCROLLING_MODE_NONE) { - set_map_tooltip_format_arg(0, rct_string_id, STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY); - set_map_tooltip_format_arg(2, rct_string_id, sceneryEntry->name); + auto banner = tileElement->AsWall()->GetBanner(); + + size_t argPos = 0; + set_map_tooltip_format_arg(argPos, rct_string_id, STR_MAP_TOOLTIP_BANNER_STRINGID_STRINGID); + argPos += sizeof(rct_string_id); + argPos += banner->FormatTextTo(gMapTooltipFormatArgs + argPos); + set_map_tooltip_format_arg(argPos, rct_string_id, STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY); + argPos += sizeof(rct_string_id); + set_map_tooltip_format_arg(argPos, rct_string_id, sceneryEntry->name); return info->type; } break; @@ -326,19 +342,33 @@ int32_t viewport_interaction_get_item_right(int32_t x, int32_t y, viewport_inter sceneryEntry = tileElement->AsLargeScenery()->GetEntry(); if (sceneryEntry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE) { - set_map_tooltip_format_arg(0, rct_string_id, STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY); - set_map_tooltip_format_arg(2, rct_string_id, sceneryEntry->name); + auto banner = tileElement->AsLargeScenery()->GetBanner(); + + size_t argPos = 0; + set_map_tooltip_format_arg(argPos, rct_string_id, STR_MAP_TOOLTIP_BANNER_STRINGID_STRINGID); + argPos += sizeof(rct_string_id); + argPos += banner->FormatTextTo(gMapTooltipFormatArgs + argPos); + set_map_tooltip_format_arg(argPos, rct_string_id, STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY); + argPos += sizeof(rct_string_id); + set_map_tooltip_format_arg(argPos, rct_string_id, sceneryEntry->name); return info->type; } break; case VIEWPORT_INTERACTION_ITEM_BANNER: - banner = &gBanners[tileElement->AsBanner()->GetIndex()]; + { + auto banner = tileElement->AsBanner()->GetBanner(); sceneryEntry = get_banner_entry(banner->type); - set_map_tooltip_format_arg(0, rct_string_id, STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY); - set_map_tooltip_format_arg(2, rct_string_id, sceneryEntry->name); + size_t argPos = 0; + set_map_tooltip_format_arg(argPos, rct_string_id, STR_MAP_TOOLTIP_BANNER_STRINGID_STRINGID); + argPos += sizeof(rct_string_id); + argPos += banner->FormatTextTo(gMapTooltipFormatArgs + argPos); + set_map_tooltip_format_arg(argPos, rct_string_id, STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY); + argPos += sizeof(rct_string_id); + set_map_tooltip_format_arg(argPos, rct_string_id, sceneryEntry->name); return info->type; + } } if (!(input_test_flag(INPUT_FLAG_6)) || !(input_test_flag(INPUT_FLAG_TOOL_ACTIVE))) @@ -428,7 +458,13 @@ int32_t viewport_interaction_right_click(int32_t x, int32_t y) case VIEWPORT_INTERACTION_ITEM_SPRITE: if (info.sprite->generic.sprite_identifier == SPRITE_IDENTIFIER_VEHICLE) - ride_construct(get_ride(info.sprite->vehicle.ride)); + { + auto ride = get_ride(info.sprite->vehicle.ride); + if (ride != nullptr) + { + ride_construct(ride); + } + } break; case VIEWPORT_INTERACTION_ITEM_RIDE: tileElement.x = info.x; @@ -469,7 +505,7 @@ int32_t viewport_interaction_right_click(int32_t x, int32_t y) static void viewport_interaction_remove_scenery(TileElement* tileElement, int32_t x, int32_t y) { auto removeSceneryAction = SmallSceneryRemoveAction( - x, y, tileElement->base_height, tileElement->AsSmallScenery()->GetSceneryQuadrant(), + { x, y, tileElement->base_height * 8 }, tileElement->AsSmallScenery()->GetSceneryQuadrant(), tileElement->AsSmallScenery()->GetEntryIndex()); GameActions::Execute(&removeSceneryAction); @@ -492,6 +528,8 @@ static void viewport_interaction_remove_footpath(TileElement* tileElement, int32 footpath_provisional_update(); tileElement2 = map_get_first_element_at(x / 32, y / 32); + if (tileElement2 == nullptr) + return; do { if (tileElement2->GetType() == TILE_ELEMENT_TYPE_PATH && tileElement2->base_height == z) @@ -530,8 +568,8 @@ void viewport_interaction_remove_park_entrance(TileElement* tileElement, int32_t y -= CoordsDirectionDelta[rotation].y; break; } - gGameCommandErrorTitle = STR_CANT_REMOVE_THIS; - game_do_command(x, GAME_COMMAND_FLAG_APPLY, y, tileElement->base_height / 2, GAME_COMMAND_REMOVE_PARK_ENTRANCE, 0, 0); + auto parkEntranceRemoveAction = ParkEntranceRemoveAction({ x, y, tileElement->base_height * 8 }); + GameActions::Execute(&parkEntranceRemoveAction); } /** @@ -547,7 +585,7 @@ static void viewport_interaction_remove_park_wall(TileElement* tileElement, int3 } else { - TileCoordsXYZD wallLocation = { x >> 5, y >> 5, tileElement->base_height, tileElement->GetDirection() }; + CoordsXYZD wallLocation = { x, y, tileElement->base_height * 8, tileElement->GetDirection() }; auto wallRemoveAction = WallRemoveAction(wallLocation); GameActions::Execute(&wallRemoveAction); } @@ -569,7 +607,8 @@ static void viewport_interaction_remove_large_scenery(TileElement* tileElement, else { auto removeSceneryAction = LargeSceneryRemoveAction( - x, y, tileElement->base_height, tileElement->GetDirection(), tileElement->AsLargeScenery()->GetSequenceIndex()); + { x, y, tileElement->base_height * 8, tileElement->GetDirection() }, + tileElement->AsLargeScenery()->GetSequenceIndex()); GameActions::Execute(&removeSceneryAction); } } @@ -582,7 +621,7 @@ static Peep* viewport_interaction_get_closest_peep(int32_t x, int32_t y, int32_t rct_viewport* viewport; Peep *peep, *closestPeep; - w = window_find_from_point(x, y); + w = window_find_from_point(ScreenCoordsXY(x, y)); if (w == nullptr) return nullptr; @@ -619,15 +658,18 @@ static Peep* viewport_interaction_get_closest_peep(int32_t x, int32_t y, int32_t * * rct2: 0x0068A15E */ -void sub_68A15E(int32_t screenX, int32_t screenY, int16_t* x, int16_t* y, int32_t* direction, TileElement** tileElement) +void sub_68A15E(int32_t screenX, int32_t screenY, int16_t* x, int16_t* y) { - int16_t my_x, my_y; + int16_t mapX, mapY; + CoordsXY initialPos{}; int32_t interactionType; - TileElement* myTileElement; + TileElement* tileElement; rct_viewport* viewport; get_map_coordinates_from_pos( - screenX, screenY, VIEWPORT_INTERACTION_MASK_TERRAIN & VIEWPORT_INTERACTION_MASK_WATER, &my_x, &my_y, &interactionType, - &myTileElement, &viewport); + screenX, screenY, VIEWPORT_INTERACTION_MASK_TERRAIN & VIEWPORT_INTERACTION_MASK_WATER, &mapX, &mapY, &interactionType, + &tileElement, &viewport); + initialPos.x = mapX; + initialPos.y = mapY; if (interactionType == VIEWPORT_INTERACTION_ITEM_NONE) { @@ -635,58 +677,27 @@ void sub_68A15E(int32_t screenX, int32_t screenY, int16_t* x, int16_t* y, int32_ return; } - int16_t originalZ = 0; + int16_t waterHeight = 0; if (interactionType == VIEWPORT_INTERACTION_ITEM_WATER) { - originalZ = myTileElement->AsSurface()->GetWaterHeight() << 4; + waterHeight = tileElement->AsSurface()->GetWaterHeight() << 4; } - LocationXY16 start_vp_pos = screen_coord_to_viewport_coord(viewport, screenX, screenY); - LocationXY16 map_pos = { (int16_t)(my_x + 16), (int16_t)(my_y + 16) }; + LocationXY16 initialVPPos = screen_coord_to_viewport_coord(viewport, screenX, screenY); + LocationXY16 mapPos = { (int16_t)(initialPos.x + 16), (int16_t)(initialPos.y + 16) }; for (int32_t i = 0; i < 5; i++) { - int16_t z = originalZ; + int16_t z = waterHeight; if (interactionType != VIEWPORT_INTERACTION_ITEM_WATER) { - z = tile_element_height(map_pos.x, map_pos.y); + z = tile_element_height({ mapPos.x, mapPos.y }); } - map_pos = viewport_coord_to_map_coord(start_vp_pos.x, start_vp_pos.y, z); - map_pos.x = std::clamp(map_pos.x, my_x, my_x + 31); - map_pos.y = std::clamp(map_pos.y, my_y, my_y + 31); + mapPos = viewport_coord_to_map_coord(initialVPPos.x, initialVPPos.y, z); + mapPos.x = std::clamp(mapPos.x, initialPos.x, initialPos.x + 31); + mapPos.y = std::clamp(mapPos.y, initialPos.y, initialPos.y + 31); } - // Determine to which edge the cursor is closest - int32_t myDirection; - int32_t mod_x = map_pos.x & 0x1F; - int32_t mod_y = map_pos.y & 0x1F; - if (mod_x < mod_y) - { - if (mod_x + mod_y < 32) - { - myDirection = 0; - } - else - { - myDirection = 1; - } - } - else - { - if (mod_x + mod_y < 32) - { - myDirection = 3; - } - else - { - myDirection = 2; - } - } - - *x = map_pos.x & ~0x1F; - *y = map_pos.y & ~0x1F; - if (direction != nullptr) - *direction = myDirection; - if (tileElement != nullptr) - *tileElement = myTileElement; + *x = mapPos.x & ~0x1F; + *y = mapPos.y & ~0x1F; } diff --git a/src/openrct2-ui/interface/Widget.cpp b/src/openrct2-ui/interface/Widget.cpp index 663a78ffdb..48a291ab54 100644 --- a/src/openrct2-ui/interface/Widget.cpp +++ b/src/openrct2-ui/interface/Widget.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/Widget.h b/src/openrct2-ui/interface/Widget.h index c107f94bc7..981aa980d7 100644 --- a/src/openrct2-ui/interface/Widget.h +++ b/src/openrct2-ui/interface/Widget.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/interface/Window.cpp b/src/openrct2-ui/interface/Window.cpp index d5cac3a33f..1069f7d771 100644 --- a/src/openrct2-ui/interface/Window.cpp +++ b/src/openrct2-ui/interface/Window.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -86,7 +86,7 @@ static bool window_fits_on_screen(int32_t x, int32_t y, int32_t width, int32_t h } rct_window* window_create( - int32_t x, int32_t y, int32_t width, int32_t height, rct_window_event_list* event_handlers, rct_windowclass cls, + ScreenCoordsXY screenCoords, int32_t width, int32_t height, rct_window_event_list* event_handlers, rct_windowclass cls, uint16_t flags) { // Check if there are any window slots left @@ -105,31 +105,31 @@ rct_window* window_create( } // Find right position to insert new window - auto dstIndex = g_window_list.size(); + auto itDestPos = g_window_list.end(); if (flags & WF_STICK_TO_BACK) { - for (size_t i = 0; i < g_window_list.size(); i++) + for (auto it = g_window_list.begin(); it != g_window_list.end(); it++) { - if (!(g_window_list[i]->flags & WF_STICK_TO_BACK)) + if (!((*it)->flags & WF_STICK_TO_BACK)) { - dstIndex = i; + itDestPos = it; } } } else if (!(flags & WF_STICK_TO_FRONT)) { - for (size_t i = g_window_list.size(); i > 0; i--) + for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) { - if (!(g_window_list[i - 1]->flags & WF_STICK_TO_FRONT)) + if (!((*it)->flags & WF_STICK_TO_FRONT)) { - dstIndex = i; + itDestPos = it.base(); break; } } } - g_window_list.insert(g_window_list.begin() + dstIndex, std::make_unique()); - auto w = g_window_list[dstIndex].get(); + auto itNew = g_window_list.insert(itDestPos, std::make_unique()); + auto w = itNew->get(); // Setup window w->classification = cls; @@ -141,12 +141,12 @@ rct_window* window_create( if (!(flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) { w->flags |= WF_WHITE_BORDER_MASK; - audio_play_sound(SOUND_WINDOW_OPEN, 0, x + (width / 2)); + audio_play_sound(SoundId::WindowOpen, 0, screenCoords.x + (width / 2)); } w->number = 0; - w->x = x; - w->y = y; + w->x = screenCoords.x; + w->y = screenCoords.y; w->width = width; w->height = height; w->viewport = nullptr; @@ -170,7 +170,7 @@ rct_window* window_create( w->viewport_smart_follow_sprite = SPRITE_INDEX_NULL; colour_scheme_update(w); - window_invalidate(w); + w->Invalidate(); return w; } @@ -314,7 +314,7 @@ foundSpace: if (x + width > screenWidth) x = screenWidth - width; - return window_create(x, y, width, height, event_handlers, cls, flags); + return window_create(ScreenCoordsXY(x, y), width, height, event_handlers, cls, flags); } rct_window* window_create_centred( @@ -326,7 +326,7 @@ rct_window* window_create_centred( int32_t x = (screenWidth - width) / 2; int32_t y = std::max(TOP_TOOLBAR_HEIGHT + 1, (screenHeight - height) / 2); - return window_create(x, y, width, height, event_handlers, cls, flags); + return window_create(ScreenCoordsXY(x, y), width, height, event_handlers, cls, flags); } static int32_t window_get_widget_index(rct_window* w, rct_widget* widget) @@ -545,7 +545,7 @@ void window_all_wheel_input() // Check window cursor is over if (!(input_test_flag(INPUT_FLAG_5))) { - rct_window* w = window_find_from_point(cursorState->x, cursorState->y); + rct_window* w = window_find_from_point(ScreenCoordsXY(cursorState->x, cursorState->y)); if (w != nullptr) { // Check if main window @@ -556,7 +556,7 @@ void window_all_wheel_input() } // Check scroll view, cursor is over - rct_widgetindex widgetIndex = window_find_widget_from_point(w, cursorState->x, cursorState->y); + rct_widgetindex widgetIndex = window_find_widget_from_point(w, ScreenCoordsXY(cursorState->x, cursorState->y)); if (widgetIndex != -1) { rct_widget* widget = &w->widgets[widgetIndex]; @@ -689,10 +689,9 @@ static void window_invalidate_pressed_image_buttons(rct_window* w) */ void invalidate_all_windows_after_input() { - for (auto& w : g_window_list) - { - window_update_scroll_widgets(w.get()); - window_invalidate_pressed_image_buttons(w.get()); - window_event_resize_call(w.get()); - } + window_visit_each([](rct_window* w) { + window_update_scroll_widgets(w); + window_invalidate_pressed_image_buttons(w); + window_event_resize_call(w); + }); } diff --git a/src/openrct2-ui/interface/Window.h b/src/openrct2-ui/interface/Window.h index d5c91ea3da..e107ceb0d5 100644 --- a/src/openrct2-ui/interface/Window.h +++ b/src/openrct2-ui/interface/Window.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/title/TitleSequencePlayer.cpp b/src/openrct2-ui/title/TitleSequencePlayer.cpp index 30aef26573..53c630f13b 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.cpp +++ b/src/openrct2-ui/title/TitleSequencePlayer.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -524,12 +524,12 @@ private: rct_window* w = window_get_main(); if (w != nullptr) { - int32_t z = tile_element_height(x, y); + int32_t z = tile_element_height({ x, y }); // Prevent scroll adjustment due to window placement when in-game auto oldScreenFlags = gScreenFlags; gScreenFlags = SCREEN_FLAGS_TITLE_DEMO; - window_set_location(w, x, y, z); + w->SetLocation(x, y, z); gScreenFlags = oldScreenFlags; viewport_update_position(w); diff --git a/src/openrct2-ui/title/TitleSequencePlayer.h b/src/openrct2-ui/title/TitleSequencePlayer.h index bcf9ae6e6a..ceead91e35 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.h +++ b/src/openrct2-ui/title/TitleSequencePlayer.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/windows/About.cpp b/src/openrct2-ui/windows/About.cpp index dc961cf33a..97f4fd5f4b 100644 --- a/src/openrct2-ui/windows/About.cpp +++ b/src/openrct2-ui/windows/About.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -333,5 +333,5 @@ static void window_about_set_page(rct_window* w, int32_t page) w->pressed_widgets |= (page == WINDOW_ABOUT_PAGE_RCT2) ? (1ULL << WIDX_TAB_ABOUT_RCT2) : (1ULL << WIDX_TAB_ABOUT_OPENRCT2); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); } diff --git a/src/openrct2-ui/windows/Banner.cpp b/src/openrct2-ui/windows/Banner.cpp index 44467e0d20..07044a6261 100644 --- a/src/openrct2-ui/windows/Banner.cpp +++ b/src/openrct2-ui/windows/Banner.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,7 +12,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -129,10 +132,13 @@ rct_window* window_banner_open(rct_windownumber number) w->number = number; window_init_scroll_widgets(w); - int32_t view_x = gBanners[w->number].x << 5; - int32_t view_y = gBanners[w->number].y << 5; + auto banner = GetBanner(w->number); + int32_t view_x = banner->position.x << 5; + int32_t view_y = banner->position.y << 5; TileElement* tile_element = map_get_first_element_at(view_x / 32, view_y / 32); + if (tile_element == nullptr) + return nullptr; while (1) { if ((tile_element->GetType() == TILE_ELEMENT_TYPE_BANNER) && (tile_element->AsBanner()->GetIndex() == w->number)) @@ -156,7 +162,7 @@ rct_window* window_banner_open(rct_windownumber number) (viewportWidget->bottom - viewportWidget->top) - 2, 0, view_x, view_y, view_z, 0, SPRITE_INDEX_NULL); w->viewport->flags = gConfigGeneral.always_show_gridlines ? VIEWPORT_FLAG_GRIDLINES : 0; - window_invalidate(w); + w->Invalidate(); return w; } @@ -167,11 +173,13 @@ rct_window* window_banner_open(rct_windownumber number) */ static void window_banner_mouseup(rct_window* w, rct_widgetindex widgetIndex) { - rct_banner* banner = &gBanners[w->number]; - int32_t x = banner->x << 5; - int32_t y = banner->y << 5; + auto banner = GetBanner(w->number); + int32_t x = banner->position.x << 5; + int32_t y = banner->position.y << 5; TileElement* tile_element = map_get_first_element_at(x / 32, y / 32); + if (tile_element == nullptr) + return; while (1) { @@ -186,20 +194,24 @@ static void window_banner_mouseup(rct_window* w, rct_widgetindex widgetIndex) window_close(w); break; case WIDX_BANNER_DEMOLISH: - game_do_command( - x, 1, y, tile_element->base_height | (tile_element->AsBanner()->GetPosition() << 8), GAME_COMMAND_REMOVE_BANNER, - 0, 0); + { + auto bannerRemoveAction = BannerRemoveAction( + { x, y, tile_element->base_height * 8, tile_element->AsBanner()->GetPosition() }); + GameActions::Execute(&bannerRemoveAction); break; + } case WIDX_BANNER_TEXT: - window_text_input_open( - w, WIDX_BANNER_TEXT, STR_BANNER_TEXT, STR_ENTER_BANNER_TEXT, gBanners[w->number].string_idx, 0, 32); + window_text_input_raw_open( + w, WIDX_BANNER_TEXT, STR_BANNER_TEXT, STR_ENTER_BANNER_TEXT, banner->GetText().c_str(), 32); break; case WIDX_BANNER_NO_ENTRY: + { textinput_cancel(); - game_do_command( - 1, GAME_COMMAND_FLAG_APPLY, w->number, banner->colour, GAME_COMMAND_SET_BANNER_STYLE, banner->text_colour, - banner->flags ^ BANNER_FLAG_NO_ENTRY); + auto bannerSetStyle = BannerSetStyleAction( + BannerSetStyleType::NoEntry, w->number, banner->flags ^ BANNER_FLAG_NO_ENTRY); + GameActions::Execute(&bannerSetStyle); break; + } } } @@ -209,7 +221,7 @@ static void window_banner_mouseup(rct_window* w, rct_widgetindex widgetIndex) */ static void window_banner_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { - rct_banner* banner = &gBanners[w->number]; + auto banner = GetBanner(w->number); switch (widgetIndex) { @@ -242,26 +254,25 @@ static void window_banner_mousedown(rct_window* w, rct_widgetindex widgetIndex, */ static void window_banner_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex) { - rct_banner* banner = &gBanners[w->number]; - switch (widgetIndex) { case WIDX_MAIN_COLOUR: + { if (dropdownIndex == -1) break; - game_do_command( - 1, GAME_COMMAND_FLAG_APPLY, w->number, dropdownIndex, GAME_COMMAND_SET_BANNER_STYLE, banner->text_colour, - banner->flags); + auto bannerSetStyle = BannerSetStyleAction(BannerSetStyleType::PrimaryColour, w->number, dropdownIndex); + GameActions::Execute(&bannerSetStyle); break; + } case WIDX_TEXT_COLOUR_DROPDOWN_BUTTON: + { if (dropdownIndex == -1) break; - - game_do_command( - 1, GAME_COMMAND_FLAG_APPLY, w->number, banner->colour, GAME_COMMAND_SET_BANNER_STYLE, dropdownIndex + 1, - banner->flags); + auto bannerSetStyle = BannerSetStyleAction(BannerSetStyleType::TextColour, w->number, dropdownIndex + 1); + GameActions::Execute(&bannerSetStyle); break; + } } } @@ -284,7 +295,7 @@ static void window_banner_textinput(rct_window* w, rct_widgetindex widgetIndex, */ static void window_banner_invalidate(rct_window* w) { - rct_banner* banner = &gBanners[w->number]; + auto banner = GetBanner(w->number); rct_widget* colour_btn = &window_banner_widgets[WIDX_MAIN_COLOUR]; colour_btn->type = WWT_EMPTY; @@ -335,10 +346,10 @@ static void window_banner_viewport_rotate(rct_window* w) view->width = 0; - rct_banner* banner = &gBanners[w->number]; + auto banner = GetBanner(w->number); - int32_t view_x = (banner->x << 5) + 16; - int32_t view_y = (banner->y << 5) + 16; + int32_t view_x = (banner->position.x << 5) + 16; + int32_t view_y = (banner->position.y << 5) + 16; int32_t view_z = w->frame_no; // Create viewport @@ -347,6 +358,7 @@ static void window_banner_viewport_rotate(rct_window* w) w, w->x + viewportWidget->left + 1, w->y + viewportWidget->top + 1, (viewportWidget->right - viewportWidget->left) - 1, (viewportWidget->bottom - viewportWidget->top) - 1, 0, view_x, view_y, view_z, 0, SPRITE_INDEX_NULL); - w->viewport->flags = gConfigGeneral.always_show_gridlines ? VIEWPORT_FLAG_GRIDLINES : 0; - window_invalidate(w); + if (w->viewport != nullptr) + w->viewport->flags = gConfigGeneral.always_show_gridlines ? VIEWPORT_FLAG_GRIDLINES : 0; + w->Invalidate(); } diff --git a/src/openrct2-ui/windows/Changelog.cpp b/src/openrct2-ui/windows/Changelog.cpp index 151e356ca6..5eac72354b 100644 --- a/src/openrct2-ui/windows/Changelog.cpp +++ b/src/openrct2-ui/windows/Changelog.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -147,12 +147,12 @@ static void window_changelog_resize(rct_window* w) w->min_height = MIN_WH; if (w->width < w->min_width) { - window_invalidate(w); + w->Invalidate(); w->width = w->min_width; } if (w->height < w->min_height) { - window_invalidate(w); + w->Invalidate(); w->height = w->min_height; } } @@ -213,7 +213,7 @@ static std::string GetChangelogText() { auto path = GetChangelogPath(); #if defined(_WIN32) && !defined(__MINGW32__) - auto pathW = String::ToUtf16(path); + auto pathW = String::ToWideChar(path); auto fs = std::ifstream(pathW, std::ios::in); #else auto fs = std::ifstream(path, std::ios::in); diff --git a/src/openrct2-ui/windows/Cheats.cpp b/src/openrct2-ui/windows/Cheats.cpp index d00b47db97..01e0f79552 100644 --- a/src/openrct2-ui/windows/Cheats.cpp +++ b/src/openrct2-ui/windows/Cheats.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -29,6 +31,10 @@ static utf8 _moneySpinnerText[MONEY_STRING_MAXLENGTH]; static money32 _moneySpinnerValue = CHEATS_MONEY_DEFAULT; static int32_t _selectedStaffSpeed = 1; +static int32_t _parkRatingSpinnerValue; +static int32_t _yearSpinnerValue = 1; +static int32_t _monthSpinnerValue = 1; +static int32_t _daySpinnerValue = 1; // clang-format off enum @@ -120,9 +126,9 @@ enum WINDOW_CHEATS_WIDGET_IDX WIDX_GENERAL_GROUP = WIDX_TAB_CONTENT, WIDX_OPEN_CLOSE_PARK, + WIDX_CREATE_DUCKS, WIDX_PARK_PARAMETERS, - WIDX_SANDBOX_MODE, - WIDX_RESET_DATE, + WIDX_REMOVE_DUCKS, WIDX_OWN_ALL_LAND, WIDX_FORCE_PARK_RATING, WIDX_PARK_RATING_SPINNER, @@ -265,9 +271,9 @@ static rct_widget window_cheats_misc_widgets[] = MAIN_CHEATS_WIDGETS, { WWT_GROUPBOX, 1, XPL(0) - GROUP_SPACE, WPL(1) + GROUP_SPACE, YPL(0), HPL(7.25), STR_CHEAT_GENERAL_GROUP, STR_NONE }, // General group { WWT_BUTTON, 1, XPL(0), WPL(0), YPL(1), HPL(1), STR_CHEAT_OPEN_PARK, STR_CHEAT_OPEN_PARK_TIP }, // open / close park + { WWT_BUTTON, 1, XPL(0), WPL(0), YPL(2), HPL(2), STR_CREATE_DUCKS, STR_CREATE_DUCKS }, // Create ducks { WWT_BUTTON, 1, XPL(1), WPL(1), YPL(1), HPL(1), STR_CHEAT_PARK_PARAMETERS, STR_CHEAT_PARK_PARAMETERS_TIP }, // Park parameters - { WWT_BUTTON, 1, XPL(0), WPL(0), YPL(2), HPL(2), STR_CHEAT_SANDBOX_MODE, STR_CHEAT_SANDBOX_MODE_TIP }, // Sandbox mode (edit land ownership in-game) - { WWT_BUTTON, 1, XPL(1), WPL(1), YPL(2), HPL(2), STR_CHEAT_RESET_DATE, STR_NONE }, // Reset date + { WWT_BUTTON, 1, XPL(1), WPL(1), YPL(2), HPL(2), STR_REMOVE_DUCKS, STR_REMOVE_DUCKS }, // Remove ducks { WWT_BUTTON, 1, XPL(0), WPL(0), YPL(3), HPL(3), STR_CHEAT_OWN_ALL_LAND, STR_CHEAT_OWN_ALL_LAND_TIP }, // Own all land { WWT_CHECKBOX, 1, XPL(0), WPL(0), YPL(5), HPL(5), STR_FORCE_PARK_RATING, STR_NONE }, // Force park rating SPINNER_WIDGETS (1, XPL(1), WPL(1) - 10, YPL(5) + 2, HPL(5) - 3, STR_NONE, STR_NONE), // park rating (3 widgets) @@ -533,6 +539,8 @@ static uint64_t window_cheats_page_enabled_widgets[] = { MAIN_CHEAT_ENABLED_WIDGETS | (1ULL << WIDX_FREEZE_WEATHER) | (1ULL << WIDX_OPEN_CLOSE_PARK) | + (1ULL << WIDX_CREATE_DUCKS) | + (1ULL << WIDX_REMOVE_DUCKS) | (1ULL << WIDX_WEATHER) | (1ULL << WIDX_WEATHER_DROPDOWN_BUTTON) | (1ULL << WIDX_CLEAR_GRASS) | @@ -545,8 +553,6 @@ static uint64_t window_cheats_page_enabled_widgets[] = { (1ULL << WIDX_HAVE_FUN) | (1ULL << WIDX_OWN_ALL_LAND) | (1ULL << WIDX_NEVERENDING_MARKETING) | - (1ULL << WIDX_SANDBOX_MODE) | - (1ULL << WIDX_RESET_DATE) | (1ULL << WIDX_STAFF_SPEED) | (1ULL << WIDX_STAFF_SPEED_DROPDOWN_BUTTON) | (1ULL << WIDX_PARK_PARAMETERS) | @@ -612,13 +618,13 @@ rct_window* window_cheats_open() if (window != nullptr) return window; - window = window_create(32, 32, WW, WH, &window_cheats_money_events, WC_CHEATS, 0); + window = window_create(ScreenCoordsXY(32, 32), WW, WH, &window_cheats_money_events, WC_CHEATS, 0); window->widgets = window_cheats_money_widgets; window->enabled_widgets = window_cheats_page_enabled_widgets[0]; window->hold_down_widgets = window_cheats_page_hold_down_widgets[0]; window_init_scroll_widgets(window); window_cheats_set_page(window, WINDOW_CHEATS_PAGE_MONEY); - park_rating_spinner_value = get_forced_park_rating() >= 0 ? get_forced_park_rating() : 999; + _parkRatingSpinnerValue = get_forced_park_rating() >= 0 ? get_forced_park_rating() : 999; return window; } @@ -638,53 +644,59 @@ static void window_cheats_money_mousedown(rct_window* w, rct_widgetindex widgetI widget_invalidate_by_class(WC_CHEATS, WIDX_MONEY_SPINNER); break; case WIDX_ADD_MONEY: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_ADDMONEY, _moneySpinnerValue, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::AddMoney, _moneySpinnerValue); break; case WIDX_YEAR_UP: - year_spinner_value++; - year_spinner_value = std::clamp(year_spinner_value, 1, 8192); + _yearSpinnerValue++; + _yearSpinnerValue = std::clamp(_yearSpinnerValue, 1, MAX_YEAR); widget_invalidate(w, WIDX_YEAR_BOX); break; case WIDX_YEAR_DOWN: - year_spinner_value--; - year_spinner_value = std::clamp(year_spinner_value, 1, 8192); + _yearSpinnerValue--; + _yearSpinnerValue = std::clamp(_yearSpinnerValue, 1, MAX_YEAR); widget_invalidate(w, WIDX_YEAR_BOX); break; case WIDX_MONTH_UP: - month_spinner_value++; - month_spinner_value = std::clamp(month_spinner_value, 1, (int)MONTH_COUNT); - day_spinner_value = std::clamp(day_spinner_value, 1, (int)days_in_month[month_spinner_value - 1]); + _monthSpinnerValue++; + _monthSpinnerValue = std::clamp(_monthSpinnerValue, 1, (int)MONTH_COUNT); + _daySpinnerValue = std::clamp(_daySpinnerValue, 1, (int)days_in_month[_monthSpinnerValue - 1]); widget_invalidate(w, WIDX_MONTH_BOX); widget_invalidate(w, WIDX_DAY_BOX); break; case WIDX_MONTH_DOWN: - month_spinner_value--; - month_spinner_value = std::clamp(month_spinner_value, 1, (int)MONTH_COUNT); - day_spinner_value = std::clamp(day_spinner_value, 1, (int)days_in_month[month_spinner_value - 1]); + _monthSpinnerValue--; + _monthSpinnerValue = std::clamp(_monthSpinnerValue, 1, (int)MONTH_COUNT); + _daySpinnerValue = std::clamp(_daySpinnerValue, 1, (int)days_in_month[_monthSpinnerValue - 1]); widget_invalidate(w, WIDX_MONTH_BOX); widget_invalidate(w, WIDX_DAY_BOX); break; case WIDX_DAY_UP: - day_spinner_value++; - day_spinner_value = std::clamp(day_spinner_value, 1, (int)days_in_month[month_spinner_value - 1]); + _daySpinnerValue++; + _daySpinnerValue = std::clamp(_daySpinnerValue, 1, (int)days_in_month[_monthSpinnerValue - 1]); widget_invalidate(w, WIDX_DAY_BOX); break; case WIDX_DAY_DOWN: - day_spinner_value--; - day_spinner_value = std::clamp(day_spinner_value, 1, (int)days_in_month[month_spinner_value - 1]); + _daySpinnerValue--; + _daySpinnerValue = std::clamp(_daySpinnerValue, 1, (int)days_in_month[_monthSpinnerValue - 1]); widget_invalidate(w, WIDX_DAY_BOX); break; case WIDX_DATE_SET: - date_set(year_spinner_value, month_spinner_value, day_spinner_value); + { + auto setDateAction = ParkSetDateAction(_yearSpinnerValue, _monthSpinnerValue, _daySpinnerValue); + GameActions::Execute(&setDateAction); window_invalidate_by_class(WC_BOTTOM_TOOLBAR); break; + } case WIDX_DATE_RESET: - date_set(1, 1, 1); + { + auto setDateAction = ParkSetDateAction(1, 1, 1); + GameActions::Execute(&setDateAction); window_invalidate_by_class(WC_BOTTOM_TOOLBAR); widget_invalidate(w, WIDX_YEAR_BOX); widget_invalidate(w, WIDX_MONTH_BOX); widget_invalidate(w, WIDX_DAY_BOX); break; + } } } @@ -693,18 +705,21 @@ static void window_cheats_misc_mousedown(rct_window* w, rct_widgetindex widgetIn switch (widgetIndex) { case WIDX_INCREASE_PARK_RATING: - park_rating_spinner_value = std::min(999, 10 * (park_rating_spinner_value / 10 + 1)); + _parkRatingSpinnerValue = std::min(999, 10 * (_parkRatingSpinnerValue / 10 + 1)); widget_invalidate_by_class(WC_CHEATS, WIDX_PARK_RATING_SPINNER); if (get_forced_park_rating() >= 0) - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETFORCEDPARKRATING, park_rating_spinner_value, GAME_COMMAND_CHEAT, 0, 0); + { + auto setCheatAction = SetCheatAction(CheatType::SetForcedParkRating, _parkRatingSpinnerValue); + GameActions::Execute(&setCheatAction); + } break; case WIDX_DECREASE_PARK_RATING: - park_rating_spinner_value = std::max(0, 10 * (park_rating_spinner_value / 10 - 1)); + _parkRatingSpinnerValue = std::max(0, 10 * (_parkRatingSpinnerValue / 10 - 1)); widget_invalidate_by_class(WC_CHEATS, WIDX_PARK_RATING_SPINNER); if (get_forced_park_rating() >= 0) - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETFORCEDPARKRATING, park_rating_spinner_value, GAME_COMMAND_CHEAT, 0, 0); + { + CheatsSet(CheatType::SetForcedParkRating, _parkRatingSpinnerValue); + } break; case WIDX_WEATHER_DROPDOWN_BUTTON: { @@ -754,7 +769,7 @@ static void window_cheats_misc_dropdown([[maybe_unused]] rct_window* w, rct_widg } else if (widgetIndex == WIDX_WEATHER_DROPDOWN_BUTTON) { - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_FORCEWEATHER, dropdownIndex, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::ForceWeather, dropdownIndex); } else if (widgetIndex == WIDX_STAFF_SPEED_DROPDOWN_BUTTON) { @@ -768,7 +783,7 @@ static void window_cheats_misc_dropdown([[maybe_unused]] rct_window* w, rct_widg speed = CHEATS_STAFF_NORMAL_SPEED; } - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETSTAFFSPEED, speed, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetStaffSpeed, speed); _selectedStaffSpeed = dropdownIndex; } } @@ -787,8 +802,7 @@ static void window_cheats_money_mouseup(rct_window* w, rct_widgetindex widgetInd window_cheats_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_NO_MONEY: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_NOMONEY, gParkFlags & PARK_FLAGS_NO_MONEY ? 0 : 1, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::NoMoney, gParkFlags & PARK_FLAGS_NO_MONEY ? 0 : 1); break; case WIDX_MONEY_SPINNER: money_to_string(_moneySpinnerValue, _moneySpinnerText, MONEY_STRING_MAXLENGTH, false); @@ -796,10 +810,10 @@ static void window_cheats_money_mouseup(rct_window* w, rct_widgetindex widgetInd w, WIDX_MONEY_SPINNER, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, _moneySpinnerText, MONEY_STRING_MAXLENGTH); break; case WIDX_SET_MONEY: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETMONEY, _moneySpinnerValue, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetMoney, _moneySpinnerValue); break; case WIDX_CLEAR_LOAN: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_CLEARLOAN, CHEATS_MONEY_DEFAULT, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::ClearLoan, CHEATS_MONEY_DEFAULT); break; } } @@ -818,108 +832,82 @@ static void window_cheats_guests_mouseup(rct_window* w, rct_widgetindex widgetIn window_cheats_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_GUEST_HAPPINESS_MAX: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_HAPPINESS, GAME_COMMAND_CHEAT, - PEEP_MAX_HAPPINESS, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HAPPINESS, PEEP_MAX_HAPPINESS); break; case WIDX_GUEST_HAPPINESS_MIN: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_HAPPINESS, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HAPPINESS, 0); break; case WIDX_GUEST_ENERGY_MAX: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_ENERGY, GAME_COMMAND_CHEAT, - PEEP_MAX_ENERGY, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_ENERGY, PEEP_MAX_ENERGY); break; case WIDX_GUEST_ENERGY_MIN: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_ENERGY, GAME_COMMAND_CHEAT, - PEEP_MIN_ENERGY, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_ENERGY, PEEP_MIN_ENERGY); break; case WIDX_GUEST_HUNGER_MAX: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_HUNGER, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HUNGER, 0); break; case WIDX_GUEST_HUNGER_MIN: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_HUNGER, GAME_COMMAND_CHEAT, 255, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_HUNGER, PEEP_MAX_HUNGER); break; case WIDX_GUEST_THIRST_MAX: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_THIRST, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_THIRST, 0); break; case WIDX_GUEST_THIRST_MIN: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_THIRST, GAME_COMMAND_CHEAT, 255, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_THIRST, PEEP_MAX_THIRST); break; case WIDX_GUEST_NAUSEA_MAX: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_NAUSEA, GAME_COMMAND_CHEAT, 255, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA, PEEP_MAX_NAUSEA); break; case WIDX_GUEST_NAUSEA_MIN: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_NAUSEA, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA, 0); break; case WIDX_GUEST_NAUSEA_TOLERANCE_MAX: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_NAUSEA_TOLERANCE, GAME_COMMAND_CHEAT, - PEEP_NAUSEA_TOLERANCE_HIGH, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA_TOLERANCE, PEEP_NAUSEA_TOLERANCE_HIGH); break; case WIDX_GUEST_NAUSEA_TOLERANCE_MIN: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_NAUSEA_TOLERANCE, GAME_COMMAND_CHEAT, - PEEP_NAUSEA_TOLERANCE_NONE, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_NAUSEA_TOLERANCE, PEEP_NAUSEA_TOLERANCE_NONE); break; case WIDX_GUEST_BATHROOM_MAX: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_BATHROOM, GAME_COMMAND_CHEAT, 255, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_BATHROOM, PEEP_MAX_BATHROOM); break; case WIDX_GUEST_BATHROOM_MIN: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_BATHROOM, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_BATHROOM, 0); break; case WIDX_GUEST_RIDE_INTENSITY_MORE_THAN_1: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY, - GAME_COMMAND_CHEAT, 1, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY, 1); break; case WIDX_GUEST_RIDE_INTENSITY_LESS_THAN_15: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGUESTPARAMETER, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY, - GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetGuestParameter, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY, 0); break; case WIDX_TRAM_GUESTS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_GENERATEGUESTS, CHEATS_TRAM_INCREMENT, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::GenerateGuests, CHEATS_TRAM_INCREMENT); break; case WIDX_REMOVE_ALL_GUESTS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_REMOVEALLGUESTS, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::RemoveAllGuests); break; case WIDX_EXPLODE_GUESTS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_EXPLODEGUESTS, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::ExplodeGuests); break; case WIDX_GIVE_GUESTS_MONEY: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_GIVEALLGUESTS, OBJECT_MONEY, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::GiveAllGuests, OBJECT_MONEY); break; case WIDX_GIVE_GUESTS_PARK_MAPS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_GIVEALLGUESTS, OBJECT_PARK_MAP, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::GiveAllGuests, OBJECT_PARK_MAP); break; case WIDX_GIVE_GUESTS_BALLOONS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_GIVEALLGUESTS, OBJECT_BALLOON, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::GiveAllGuests, OBJECT_BALLOON); break; case WIDX_GIVE_GUESTS_UMBRELLAS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_GIVEALLGUESTS, OBJECT_UMBRELLA, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::GiveAllGuests, OBJECT_UMBRELLA); break; case WIDX_GUEST_IGNORE_RIDE_INTENSITY: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_IGNORERIDEINTENSITY, !gCheatsIgnoreRideIntensity, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::IgnoreRideIntensity, !gCheatsIgnoreRideIntensity); break; case WIDX_DISABLE_VANDALISM: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLEVANDALISM, !gCheatsDisableVandalism, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::DisableVandalism, !gCheatsDisableVandalism); break; case WIDX_DISABLE_LITTERING: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLELITTERING, !gCheatsDisableLittering, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::DisableLittering, !gCheatsDisableLittering); break; } } @@ -938,50 +926,46 @@ static void window_cheats_misc_mouseup(rct_window* w, rct_widgetindex widgetInde window_cheats_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_FREEZE_WEATHER: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_FREEZEWEATHER, !gCheatsFreezeWeather, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::FreezeWeather, !gCheatsFreezeWeather); break; case WIDX_OPEN_CLOSE_PARK: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_OPENCLOSEPARK, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::OpenClosePark); + break; + case WIDX_CREATE_DUCKS: + CheatsSet(CheatType::CreateDucks, CHEATS_DUCK_INCREMENT); + break; + case WIDX_REMOVE_DUCKS: + CheatsSet(CheatType::RemoveDucks); break; case WIDX_CLEAR_GRASS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGRASSLENGTH, GRASS_LENGTH_CLEAR_0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetGrassLength, GRASS_LENGTH_CLEAR_0); break; case WIDX_MOWED_GRASS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGRASSLENGTH, GRASS_LENGTH_MOWED, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetGrassLength, GRASS_LENGTH_MOWED); break; case WIDX_WATER_PLANTS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_WATERPLANTS, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::WaterPlants); break; case WIDX_FIX_VANDALISM: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_FIXVANDALISM, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::FixVandalism); break; case WIDX_REMOVE_LITTER: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_REMOVELITTER, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::RemoveLitter); break; case WIDX_DISABLE_PLANT_AGING: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLEPLANTAGING, !gCheatsDisablePlantAging, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::DisablePlantAging, !gCheatsDisablePlantAging); break; case WIDX_WIN_SCENARIO: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_WINSCENARIO, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::WinScenario); break; case WIDX_HAVE_FUN: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_HAVEFUN, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::HaveFun); break; case WIDX_OWN_ALL_LAND: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_OWNALLLAND, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::OwnAllLand); break; case WIDX_NEVERENDING_MARKETING: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_NEVERENDINGMARKETING, !gCheatsNeverendingMarketing, GAME_COMMAND_CHEAT, 0, 0); - break; - case WIDX_SANDBOX_MODE: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_SANDBOXMODE, !gCheatsSandboxMode, GAME_COMMAND_CHEAT, 0, 0); - // To prevent tools from staying active after disabling cheat - // tool_cancel(); - break; - case WIDX_RESET_DATE: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_RESETDATE, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::NeverEndingMarketing, !gCheatsNeverendingMarketing); break; case WIDX_PARK_PARAMETERS: context_open_window(WC_EDITOR_SCENARIO_OPTIONS); @@ -989,12 +973,11 @@ static void window_cheats_misc_mouseup(rct_window* w, rct_widgetindex widgetInde case WIDX_FORCE_PARK_RATING: if (get_forced_park_rating() >= 0) { - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETFORCEDPARKRATING, -1, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetForcedParkRating, -1); } else { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETFORCEDPARKRATING, park_rating_spinner_value, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::SetForcedParkRating, _parkRatingSpinnerValue); } break; } @@ -1014,89 +997,80 @@ static void window_cheats_rides_mouseup(rct_window* w, rct_widgetindex widgetInd window_cheats_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_RENEW_RIDES: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_RENEWRIDES, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::RenewRides); break; case WIDX_MAKE_DESTRUCTIBLE: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_MAKEDESTRUCTIBLE, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::MakeDestructible); break; case WIDX_FIX_ALL: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_FIXRIDES, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::FixRides); break; case WIDX_FAST_LIFT_HILL: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_FASTLIFTHILL, !gCheatsFastLiftHill, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::FastLiftHill, !gCheatsFastLiftHill); + break; case WIDX_DISABLE_BRAKES_FAILURE: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLEBRAKESFAILURE, !gCheatsDisableBrakesFailure, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::DisableBrakesFailure, !gCheatsDisableBrakesFailure); break; case WIDX_DISABLE_ALL_BREAKDOWNS: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLEALLBREAKDOWNS, !gCheatsDisableAllBreakdowns, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::DisableAllBreakdowns, !gCheatsDisableAllBreakdowns); break; case WIDX_BUILD_IN_PAUSE_MODE: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_BUILDINPAUSEMODE, !gCheatsBuildInPauseMode, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::BuildInPauseMode, !gCheatsBuildInPauseMode); break; case WIDX_RESET_CRASH_STATUS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_RESETCRASHSTATUS, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::ResetCrashStatus); break; case WIDX_10_MINUTE_INSPECTIONS: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_10MINUTEINSPECTIONS, 0, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::TenMinuteInspections); break; case WIDX_SHOW_ALL_OPERATING_MODES: + { if (!gCheatsShowAllOperatingModes) { context_show_error(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE); } - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SHOWALLOPERATINGMODES, !gCheatsShowAllOperatingModes, GAME_COMMAND_CHEAT, 0, - 0); - break; + CheatsSet(CheatType::ShowAllOperatingModes, !gCheatsShowAllOperatingModes); + } + break; case WIDX_SHOW_VEHICLES_FROM_OTHER_TRACK_TYPES: + { if (!gCheatsShowVehiclesFromOtherTrackTypes) { context_show_error(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE); } - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SHOWVEHICLESFROMOTHERTRACKTYPES, !gCheatsShowVehiclesFromOtherTrackTypes, - GAME_COMMAND_CHEAT, 0, 0); - break; + CheatsSet(CheatType::ShowVehiclesFromOtherTrackTypes, !gCheatsShowVehiclesFromOtherTrackTypes); + } + break; case WIDX_DISABLE_TRAIN_LENGTH_LIMITS: + { if (!gCheatsDisableTrainLengthLimit) { context_show_error(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE); } - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLETRAINLENGTHLIMIT, !gCheatsDisableTrainLengthLimit, GAME_COMMAND_CHEAT, - 0, 0); - break; + CheatsSet(CheatType::DisableTrainLengthLimit, !gCheatsDisableTrainLengthLimit); + } + break; case WIDX_ENABLE_CHAIN_LIFT_ON_ALL_TRACK: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_ENABLECHAINLIFTONALLTRACK, !gCheatsEnableChainLiftOnAllTrack, - GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::EnableChainLiftOnAllTrack, !gCheatsEnableChainLiftOnAllTrack); break; case WIDX_ENABLE_ARBITRARY_RIDE_TYPE_CHANGES: + { if (!gCheatsAllowArbitraryRideTypeChanges) { context_show_error(STR_WARNING_IN_CAPS, STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE); } - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_ALLOW_ARBITRARY_RIDE_TYPE_CHANGES, !gCheatsAllowArbitraryRideTypeChanges, - GAME_COMMAND_CHEAT, 0, 0); - break; + CheatsSet(CheatType::AllowArbitraryRideTypeChanges, !gCheatsAllowArbitraryRideTypeChanges); + } + break; case WIDX_DISABLE_RIDE_VALUE_AGING: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLERIDEVALUEAGING, !gCheatsDisableRideValueAging, GAME_COMMAND_CHEAT, 0, - 0); + CheatsSet(CheatType::DisableRideValueAging, !gCheatsDisableRideValueAging); break; case WIDX_IGNORE_RESEARCH_STATUS: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_IGNORERESEARCHSTATUS, !gCheatsIgnoreResearchStatus, GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::IgnoreResearchStatus, !gCheatsIgnoreResearchStatus); break; case WIDX_ENABLE_ALL_DRAWABLE_TRACK_PIECES: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_ENABLEALLDRAWABLETRACKPIECES, !gCheatsEnableAllDrawableTrackPieces, - GAME_COMMAND_CHEAT, 0, 0); + CheatsSet(CheatType::EnableAllDrawableTrackPieces, !gCheatsEnableAllDrawableTrackPieces); break; } } @@ -1113,7 +1087,7 @@ static void window_cheats_text_input(rct_window* w, rct_widgetindex widgetIndex, { _moneySpinnerValue = val; } - window_invalidate(w); + w->Invalidate(); } } @@ -1174,7 +1148,6 @@ static void window_cheats_invalidate(rct_window* w) w->widgets[WIDX_OPEN_CLOSE_PARK].text = (gParkFlags & PARK_FLAGS_PARK_OPEN) ? STR_CHEAT_CLOSE_PARK : STR_CHEAT_OPEN_PARK; widget_set_checkbox_value(w, WIDX_FORCE_PARK_RATING, get_forced_park_rating() >= 0); - w->widgets[WIDX_SANDBOX_MODE].text = gCheatsSandboxMode ? STR_CHEAT_SANDBOX_MODE_DISABLE : STR_CHEAT_SANDBOX_MODE; w->widgets[WIDX_FREEZE_WEATHER].text = gCheatsFreezeWeather ? STR_CHEAT_UNFREEZE_WEATHER : STR_CHEAT_FREEZE_WEATHER; widget_set_checkbox_value(w, WIDX_NEVERENDING_MARKETING, gCheatsNeverendingMarketing); widget_set_checkbox_value(w, WIDX_DISABLE_PLANT_AGING, gCheatsDisablePlantAging); @@ -1215,26 +1188,25 @@ static void window_cheats_paint(rct_window* w, rct_drawpixelinfo* dpi) { colour |= COLOUR_FLAG_INSET; } - int32_t actual_month = month_spinner_value - 1; + int32_t actual_month = _monthSpinnerValue - 1; gfx_draw_string_left( dpi, STR_BOTTOM_TOOLBAR_CASH, gCommonFormatArgs, colour, w->x + XPL(0) + TXTO, w->y + YPL(2) + TXTO); gfx_draw_string_left(dpi, STR_YEAR, nullptr, COLOUR_BLACK, w->x + XPL(0) + TXTO, w->y + YPL(7) + TXTO); gfx_draw_string_left(dpi, STR_MONTH, nullptr, COLOUR_BLACK, w->x + XPL(0) + TXTO, w->y + YPL(8) + TXTO); gfx_draw_string_left(dpi, STR_DAY, nullptr, COLOUR_BLACK, w->x + XPL(0) + TXTO, w->y + YPL(9) + TXTO); gfx_draw_string_right( - dpi, STR_FORMAT_INTEGER, &year_spinner_value, w->colours[1], w->x + WPL(1) - 34 - TXTO, w->y + YPL(7) + TXTO); + dpi, STR_FORMAT_INTEGER, &_yearSpinnerValue, w->colours[1], w->x + WPL(1) - 34 - TXTO, w->y + YPL(7) + TXTO); gfx_draw_string_right( dpi, STR_FORMAT_MONTH, &actual_month, w->colours[1], w->x + WPL(1) - 34 - TXTO, w->y + YPL(8) + TXTO); gfx_draw_string_right( - dpi, STR_FORMAT_INTEGER, &day_spinner_value, w->colours[1], w->x + WPL(1) - 34 - TXTO, w->y + YPL(9) + TXTO); + dpi, STR_FORMAT_INTEGER, &_daySpinnerValue, w->colours[1], w->x + WPL(1) - 34 - TXTO, w->y + YPL(9) + TXTO); } else if (w->page == WINDOW_CHEATS_PAGE_MISC) { gfx_draw_string_left(dpi, STR_CHEAT_STAFF_SPEED, nullptr, COLOUR_BLACK, w->x + XPL(0) + TXTO, w->y + YPL(17) + TXTO); gfx_draw_string_left(dpi, STR_FORCE_WEATHER, nullptr, COLOUR_BLACK, w->x + XPL(0) + TXTO, w->y + YPL(10) + TXTO); gfx_draw_string_right( - dpi, STR_FORMAT_INTEGER, &park_rating_spinner_value, w->colours[1], w->x + WPL(1) - 34 - TXTO, - w->y + YPL(5) + TXTO); + dpi, STR_FORMAT_INTEGER, &_parkRatingSpinnerValue, w->colours[1], w->x + WPL(1) - 34 - TXTO, w->y + YPL(5) + TXTO); } else if (w->page == WINDOW_CHEATS_PAGE_GUESTS) { @@ -1311,9 +1283,9 @@ static void window_cheats_set_page(rct_window* w, int32_t page) } maxY += 6; - window_invalidate(w); + w->Invalidate(); w->height = maxY; w->widgets[WIDX_BACKGROUND].bottom = maxY - 1; w->widgets[WIDX_PAGE_BACKGROUND].bottom = maxY - 1; - window_invalidate(w); + w->Invalidate(); } diff --git a/src/openrct2-ui/windows/ClearScenery.cpp b/src/openrct2-ui/windows/ClearScenery.cpp index 24bb6e3a0c..c5702d2551 100644 --- a/src/openrct2-ui/windows/ClearScenery.cpp +++ b/src/openrct2-ui/windows/ClearScenery.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,7 @@ #include #include #include +#include #include // clang-format off @@ -95,7 +96,8 @@ rct_window* window_clear_scenery_open() if (window != nullptr) return window; - window = window_create(context_get_width() - 98, 29, 98, 94, &window_clear_scenery_events, WC_CLEAR_SCENERY, 0); + window = window_create( + ScreenCoordsXY(context_get_width() - 98, 29), 98, 94, &window_clear_scenery_events, WC_CLEAR_SCENERY, 0); window->widgets = window_clear_scenery_widgets; window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_INCREMENT) | (1 << WIDX_DECREMENT) | (1 << WIDX_PREVIEW) | (1 << WIDX_SMALL_SCENERY) | (1 << WIDX_LARGE_SCENERY) | (1 << WIDX_FOOTPATH); @@ -140,15 +142,15 @@ static void window_clear_scenery_mouseup(rct_window* w, rct_widgetindex widgetIn break; case WIDX_SMALL_SCENERY: gClearSmallScenery ^= 1; - window_invalidate(w); + w->Invalidate(); break; case WIDX_LARGE_SCENERY: gClearLargeScenery ^= 1; - window_invalidate(w); + w->Invalidate(); break; case WIDX_FOOTPATH: gClearFootpath ^= 1; - window_invalidate(w); + w->Invalidate(); break; } } @@ -162,14 +164,14 @@ static void window_clear_scenery_mousedown(rct_window* w, rct_widgetindex widget gLandToolSize = std::max(MINIMUM_TOOL_SIZE, gLandToolSize - 1); // Invalidate the window - window_invalidate(w); + w->Invalidate(); break; case WIDX_INCREMENT: // Increment land tool size, if it stays within the limit gLandToolSize = std::min(MAXIMUM_TOOL_SIZE, gLandToolSize + 1); // Invalidate the window - window_invalidate(w); + w->Invalidate(); break; } } @@ -188,7 +190,7 @@ static void window_clear_scenery_textinput(rct_window* w, rct_widgetindex widget size = std::max(MINIMUM_TOOL_SIZE, size); size = std::min(MAXIMUM_TOOL_SIZE, size); gLandToolSize = size; - window_invalidate(w); + w->Invalidate(); } } @@ -244,10 +246,10 @@ static void window_clear_scenery_paint(rct_window* w, rct_drawpixelinfo* dpi) } // Draw cost amount - x = (window_clear_scenery_widgets[WIDX_PREVIEW].left + window_clear_scenery_widgets[WIDX_PREVIEW].right) / 2 + w->x; - y = window_clear_scenery_widgets[WIDX_PREVIEW].bottom + w->y + 5 + 27; - if (gClearSceneryCost != MONEY32_UNDEFINED && gClearSceneryCost != 0) + if (gClearSceneryCost != MONEY32_UNDEFINED && gClearSceneryCost != 0 && !(gParkFlags & PARK_FLAGS_NO_MONEY)) { + x = (window_clear_scenery_widgets[WIDX_PREVIEW].left + window_clear_scenery_widgets[WIDX_PREVIEW].right) / 2 + w->x; + y = window_clear_scenery_widgets[WIDX_PREVIEW].bottom + w->y + 5 + 27; gfx_draw_string_centred(dpi, STR_COST_AMOUNT, x, y, COLOUR_BLACK, &gClearSceneryCost); } } diff --git a/src/openrct2-ui/windows/CustomCurrency.cpp b/src/openrct2-ui/windows/CustomCurrency.cpp index 9899710761..84f5008d3d 100644 --- a/src/openrct2-ui/windows/CustomCurrency.cpp +++ b/src/openrct2-ui/windows/CustomCurrency.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/windows/DebugPaint.cpp b/src/openrct2-ui/windows/DebugPaint.cpp index 182febca51..412ff6a5ba 100644 --- a/src/openrct2-ui/windows/DebugPaint.cpp +++ b/src/openrct2-ui/windows/DebugPaint.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -91,8 +91,8 @@ rct_window* window_debug_paint_open() return window; window = window_create( - 16, context_get_height() - 16 - 33 - WINDOW_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT, &window_debug_paint_events, - WC_DEBUG_PAINT, WF_STICK_TO_FRONT | WF_TRANSPARENT); + ScreenCoordsXY(16, context_get_height() - 16 - 33 - WINDOW_HEIGHT), WINDOW_WIDTH, WINDOW_HEIGHT, + &window_debug_paint_events, WC_DEBUG_PAINT, WF_STICK_TO_FRONT | WF_TRANSPARENT); window->widgets = window_debug_paint_widgets; window->enabled_widgets = (1 << WIDX_TOGGLE_SHOW_WIDE_PATHS) | (1 << WIDX_TOGGLE_SHOW_BLOCKED_TILES) @@ -145,7 +145,7 @@ static void window_debug_paint_invalidate(rct_window* w) if (ResizeLanguage != currentLanguage) { ResizeLanguage = currentLanguage; - window_invalidate(w); + w->Invalidate(); // Find the width of the longest string int16_t newWidth = 0; @@ -171,7 +171,7 @@ static void window_debug_paint_invalidate(rct_window* w) w->widgets[WIDX_TOGGLE_SHOW_BOUND_BOXES].right = newWidth - 8; w->widgets[WIDX_TOGGLE_SHOW_DIRTY_VISUALS].right = newWidth - 8; - window_invalidate(w); + w->Invalidate(); } widget_set_checkbox_value(w, WIDX_TOGGLE_SHOW_WIDE_PATHS, gPaintWidePathsAsGhost); diff --git a/src/openrct2-ui/windows/DemolishRidePrompt.cpp b/src/openrct2-ui/windows/DemolishRidePrompt.cpp index e0e48fc76d..8b652c69b6 100644 --- a/src/openrct2-ui/windows/DemolishRidePrompt.cpp +++ b/src/openrct2-ui/windows/DemolishRidePrompt.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -130,7 +130,7 @@ rct_window* window_ride_demolish_prompt_open(Ride* ride) int x = w->x; int y = w->y; window_close(w); - w = window_create(x, y, WW, WH, &window_ride_demolish_events, WC_DEMOLISH_RIDE_PROMPT, WF_TRANSPARENT); + w = window_create(ScreenCoordsXY(x, y), WW, WH, &window_ride_demolish_events, WC_DEMOLISH_RIDE_PROMPT, WF_TRANSPARENT); } else { @@ -156,7 +156,7 @@ rct_window* window_ride_refurbish_prompt_open(Ride* ride) int x = w->x; int y = w->y; window_close(w); - w = window_create(x, y, WW, WH, &window_ride_refurbish_events, WC_DEMOLISH_RIDE_PROMPT, WF_TRANSPARENT); + w = window_create(ScreenCoordsXY(x, y), WW, WH, &window_ride_refurbish_events, WC_DEMOLISH_RIDE_PROMPT, WF_TRANSPARENT); } else { @@ -218,22 +218,16 @@ static void window_ride_demolish_paint(rct_window* w, rct_drawpixelinfo* dpi) { window_draw_widgets(w, dpi); - Ride* ride = get_ride(w->number); - - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); - set_format_arg(6, money32, _demolishRideCost); - - int32_t x = w->x + WW / 2; - int32_t y = w->y + (WH / 2) - 3; - - if (gParkFlags & PARK_FLAGS_NO_MONEY) + auto ride = get_ride(w->number); + if (ride != nullptr) { - gfx_draw_string_centred_wrapped(dpi, gCommonFormatArgs, x, y, WW - 4, STR_DEMOLISH_RIDE_ID, COLOUR_BLACK); - } - else - { - gfx_draw_string_centred_wrapped(dpi, gCommonFormatArgs, x, y, WW - 4, STR_DEMOLISH_RIDE_ID_MONEY, COLOUR_BLACK); + auto stringId = (gParkFlags & PARK_FLAGS_NO_MONEY) ? STR_DEMOLISH_RIDE_ID : STR_DEMOLISH_RIDE_ID_MONEY; + auto nameArgLen = ride->FormatNameTo(gCommonFormatArgs); + set_format_arg(nameArgLen, money32, _demolishRideCost); + + int32_t x = w->x + WW / 2; + int32_t y = w->y + (WH / 2) - 3; + gfx_draw_string_centred_wrapped(dpi, gCommonFormatArgs, x, y, WW - 4, stringId, COLOUR_BLACK); } } @@ -241,21 +235,15 @@ static void window_ride_refurbish_paint(rct_window* w, rct_drawpixelinfo* dpi) { window_draw_widgets(w, dpi); - Ride* ride = get_ride(w->number); - - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); - set_format_arg(6, money32, _demolishRideCost / 2); - - int32_t x = w->x + WW / 2; - int32_t y = w->y + (WH / 2) - 3; - - if (gParkFlags & PARK_FLAGS_NO_MONEY) + auto ride = get_ride(w->number); + if (ride != nullptr) { - gfx_draw_string_centred_wrapped(dpi, gCommonFormatArgs, x, y, WW - 4, STR_REFURBISH_RIDE_ID_NO_MONEY, COLOUR_BLACK); - } - else - { - gfx_draw_string_centred_wrapped(dpi, gCommonFormatArgs, x, y, WW - 4, STR_REFURBISH_RIDE_ID_MONEY, COLOUR_BLACK); + auto stringId = (gParkFlags & PARK_FLAGS_NO_MONEY) ? STR_REFURBISH_RIDE_ID_NO_MONEY : STR_REFURBISH_RIDE_ID_MONEY; + auto nameArgLen = ride->FormatNameTo(gCommonFormatArgs); + set_format_arg(nameArgLen, money32, _demolishRideCost / 2); + + int32_t x = w->x + WW / 2; + int32_t y = w->y + (WH / 2) - 3; + gfx_draw_string_centred_wrapped(dpi, gCommonFormatArgs, x, y, WW - 4, stringId, COLOUR_BLACK); } } diff --git a/src/openrct2-ui/windows/Dropdown.cpp b/src/openrct2-ui/windows/Dropdown.cpp index a6f48a3ec3..d63e0fc712 100644 --- a/src/openrct2-ui/windows/Dropdown.cpp +++ b/src/openrct2-ui/windows/Dropdown.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -208,8 +208,8 @@ void window_dropdown_show_text_custom_width( // Create the window w = window_create( - x, y + extray, window_dropdown_widgets[WIDX_BACKGROUND].right + 1, window_dropdown_widgets[WIDX_BACKGROUND].bottom + 1, - &window_dropdown_events, WC_DROPDOWN, WF_STICK_TO_FRONT); + ScreenCoordsXY(x, y + extray), window_dropdown_widgets[WIDX_BACKGROUND].right + 1, + window_dropdown_widgets[WIDX_BACKGROUND].bottom + 1, &window_dropdown_events, WC_DROPDOWN, WF_STICK_TO_FRONT); w->widgets = window_dropdown_widgets; if (colour & COLOUR_FLAG_TRANSLUCENT) w->flags |= WF_TRANSPARENT; @@ -288,8 +288,8 @@ void window_dropdown_show_image( // Create the window w = window_create( - x, y + extray, window_dropdown_widgets[WIDX_BACKGROUND].right + 1, window_dropdown_widgets[WIDX_BACKGROUND].bottom + 1, - &window_dropdown_events, WC_DROPDOWN, WF_STICK_TO_FRONT); + ScreenCoordsXY(x, y + extray), window_dropdown_widgets[WIDX_BACKGROUND].right + 1, + window_dropdown_widgets[WIDX_BACKGROUND].bottom + 1, &window_dropdown_events, WC_DROPDOWN, WF_STICK_TO_FRONT); w->widgets = window_dropdown_widgets; if (colour & COLOUR_FLAG_TRANSLUCENT) w->flags |= WF_TRANSPARENT; diff --git a/src/openrct2-ui/windows/EditorBottomToolbar.cpp b/src/openrct2-ui/windows/EditorBottomToolbar.cpp index 89bea6e32d..87477ce2ab 100644 --- a/src/openrct2-ui/windows/EditorBottomToolbar.cpp +++ b/src/openrct2-ui/windows/EditorBottomToolbar.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -129,8 +129,8 @@ static constexpr const rct_string_id EditorStepNames[] = { rct_window* window_editor_bottom_toolbar_open() { rct_window* window = window_create( - 0, context_get_height() - 32, context_get_width(), 32, &window_editor_bottom_toolbar_events, WC_BOTTOM_TOOLBAR, - WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); + ScreenCoordsXY(0, context_get_height() - 32), context_get_width(), 32, &window_editor_bottom_toolbar_events, + WC_BOTTOM_TOOLBAR, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); window->widgets = window_editor_bottom_toolbar_widgets; window->enabled_widgets |= (1 << WIDX_PREVIOUS_STEP_BUTTON) | (1 << WIDX_NEXT_STEP_BUTTON) | (1 << WIDX_PREVIOUS_IMAGE) @@ -316,7 +316,7 @@ static void window_editor_bottom_toolbar_mouseup([[maybe_unused]] rct_window* w, if (widgetIndex == WIDX_PREVIOUS_STEP_BUTTON) { if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - || (gSpriteListCount[SPRITE_LIST_NULL] == MAX_SPRITES && !(gParkFlags & PARK_FLAGS_SPRITES_INITIALISED))) + || (gSpriteListCount[SPRITE_LIST_FREE] == MAX_SPRITES && !(gParkFlags & PARK_FLAGS_SPRITES_INITIALISED))) { previous_button_mouseup_events[gS6Info.editor_step](); } @@ -376,7 +376,7 @@ void window_editor_bottom_toolbar_invalidate(rct_window* w) } else if (!(gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER)) { - if (gSpriteListCount[SPRITE_LIST_NULL] != MAX_SPRITES || gParkFlags & PARK_FLAGS_SPRITES_INITIALISED) + if (gSpriteListCount[SPRITE_LIST_FREE] != MAX_SPRITES || gParkFlags & PARK_FLAGS_SPRITES_INITIALISED) { hide_previous_step_button(); } @@ -401,7 +401,7 @@ void window_editor_bottom_toolbar_paint(rct_window* w, rct_drawpixelinfo* dpi) { drawPreviousButton = true; } - else if (gSpriteListCount[SPRITE_LIST_NULL] != MAX_SPRITES) + else if (gSpriteListCount[SPRITE_LIST_FREE] != MAX_SPRITES) { drawNextButton = true; } diff --git a/src/openrct2-ui/windows/EditorInventionsList.cpp b/src/openrct2-ui/windows/EditorInventionsList.cpp index 5ee9337483..08fea90f05 100644 --- a/src/openrct2-ui/windows/EditorInventionsList.cpp +++ b/src/openrct2-ui/windows/EditorInventionsList.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -84,7 +84,7 @@ static void window_editor_inventions_list_drag_cursor(rct_window *w, rct_widgeti static void window_editor_inventions_list_drag_moved(rct_window* w, int32_t x, int32_t y); static void window_editor_inventions_list_drag_paint(rct_window *w, rct_drawpixelinfo *dpi); -static rct_string_id window_editor_inventions_list_prepare_name(const rct_research_item * researchItem, bool withGap); +static rct_string_id window_editor_inventions_list_prepare_name(const ResearchItem * researchItem, bool withGap); // 0x0098177C static rct_window_event_list window_editor_inventions_list_events = { @@ -152,7 +152,7 @@ static rct_window_event_list window_editor_inventions_list_drag_events = { #pragma endregion -static rct_research_item *_editorInventionsListDraggedItem; +static ResearchItem _editorInventionsListDraggedItem; static constexpr const rct_string_id EditorInventionsResearchCategories[] = { STR_RESEARCH_NEW_TRANSPORT_RIDES, @@ -165,8 +165,8 @@ static constexpr const rct_string_id EditorInventionsResearchCategories[] = { }; // clang-format on -static void window_editor_inventions_list_drag_open(rct_research_item* researchItem); -static void move_research_item(rct_research_item* beforeItem); +static void window_editor_inventions_list_drag_open(ResearchItem* researchItem); +static void move_research_item(ResearchItem* beforeItem, int32_t scrollIndex); /** * @@ -186,13 +186,9 @@ static void research_rides_setup() } // Set research required for rides in use - for (uint16_t rideIndex = 0; rideIndex < MAX_RIDES; rideIndex++) + for (const auto& ride : GetRideManager()) { - auto ride = get_ride(rideIndex); - if (ride->type != RIDE_TYPE_NULL) - { - Editor::SetSelectedObject(OBJECT_TYPE_RIDE, ride->subtype, OBJECT_SELECTION_FLAG_SELECTED); - } + Editor::SetSelectedObject(OBJECT_TYPE_RIDE, ride.subtype, OBJECT_SELECTION_FLAG_SELECTED); } } @@ -200,72 +196,47 @@ static void research_rides_setup() * * rct2: 0x006855E7 */ -static void move_research_item(rct_research_item* beforeItem) +static void move_research_item(ResearchItem* beforeItem, int32_t scrollIndex) { - rct_window* w; - rct_research_item *researchItem, draggedItem; - - if (_editorInventionsListDraggedItem + 1 == beforeItem) - return; - - // Back up the dragged item - draggedItem = *_editorInventionsListDraggedItem; - - // Remove dragged item from list - researchItem = _editorInventionsListDraggedItem; - do - { - *researchItem = *(researchItem + 1); - researchItem++; - } while (researchItem->rawValue != RESEARCHED_ITEMS_END_2); - // At end of this researchItem points to the end of the list - - if (beforeItem > _editorInventionsListDraggedItem) - beforeItem--; - - // Add dragged item to list - do - { - *researchItem = *(researchItem - 1); - researchItem--; - } while (researchItem != beforeItem); - - *researchItem = draggedItem; - - w = window_find_by_class(WC_EDITOR_INVENTION_LIST); + auto w = window_find_by_class(WC_EDITOR_INVENTION_LIST); if (w != nullptr) { w->research_item = nullptr; - window_invalidate(w); + w->Invalidate(); } + + research_remove(&_editorInventionsListDraggedItem); + + auto& researchList = scrollIndex == 0 ? gResearchItemsInvented : gResearchItemsUninvented; + if (beforeItem != nullptr) + { + for (size_t i = 0; i < researchList.size(); i++) + { + if (researchList[i].Equals(beforeItem)) + { + researchList.insert((researchList.begin() + i), _editorInventionsListDraggedItem); + return; + } + } + } + + // Still not found? Append to end of list. + researchList.push_back(_editorInventionsListDraggedItem); } /** * * rct2: 0x0068558E */ -static rct_research_item* window_editor_inventions_list_get_item_from_scroll_y(int32_t scrollIndex, int32_t y) +static ResearchItem* window_editor_inventions_list_get_item_from_scroll_y(int32_t scrollIndex, int32_t y) { - rct_research_item* researchItem; - - researchItem = gResearchItems; - - if (scrollIndex != 0) - { - // Skip pre-researched items - for (; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - } - researchItem++; - } - - for (; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR && researchItem->rawValue != RESEARCHED_ITEMS_END; - researchItem++) + auto& researchList = scrollIndex == 0 ? gResearchItemsInvented : gResearchItemsUninvented; + for (auto& researchItem : researchList) { y -= SCROLLABLE_ROW_HEIGHT; if (y < 0) { - return researchItem; + return &researchItem; } } @@ -276,56 +247,43 @@ static rct_research_item* window_editor_inventions_list_get_item_from_scroll_y(i * * rct2: 0x006855BB */ -static rct_research_item* window_editor_inventions_list_get_item_from_scroll_y_include_seps(int32_t scrollIndex, int32_t y) +static ResearchItem* window_editor_inventions_list_get_item_from_scroll_y_include_seps(int32_t scrollIndex, int32_t y) { - rct_research_item* researchItem; - - researchItem = gResearchItems; - - if (scrollIndex != 0) - { - // Skip pre-researched items - for (; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - } - researchItem++; - } - - for (; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR && researchItem->rawValue != RESEARCHED_ITEMS_END; - researchItem++) + auto& researchList = scrollIndex == 0 ? gResearchItemsInvented : gResearchItemsUninvented; + for (auto& researchItem : researchList) { y -= SCROLLABLE_ROW_HEIGHT; if (y < 0) { - return researchItem; + return &researchItem; } } - - return researchItem; + return nullptr; } -static rct_research_item* get_research_item_at(int32_t x, int32_t y) +static ResearchItem* get_research_item_at(int32_t x, int32_t y, int32_t* outScrollId) { rct_window* w = window_find_by_class(WC_EDITOR_INVENTION_LIST); if (w != nullptr && w->x <= x && w->y < y && w->x + w->width > x && w->y + w->height > y) { - rct_widgetindex widgetIndex = window_find_widget_from_point(w, x, y); + rct_widgetindex widgetIndex = window_find_widget_from_point(w, ScreenCoordsXY(x, y)); rct_widget* widget = &w->widgets[widgetIndex]; if (widgetIndex == WIDX_PRE_RESEARCHED_SCROLL || widgetIndex == WIDX_RESEARCH_ORDER_SCROLL) { gPressedWidget.widget_index = widgetIndex; - int32_t outX, outY, outScrollArea, outScrollId; - widget_scroll_get_part(w, widget, x, y, &outX, &outY, &outScrollArea, &outScrollId); + int32_t outX, outY, outScrollArea; + widget_scroll_get_part(w, widget, x, y, &outX, &outY, &outScrollArea, outScrollId); if (outScrollArea == SCROLL_PART_VIEW) { - outScrollId = outScrollId == 0 ? 0 : 1; + *outScrollId = *outScrollId == 0 ? 0 : 1; - int32_t scrollY = y - (w->y + widget->top) + w->scrolls[outScrollId].v_top + 5; - return window_editor_inventions_list_get_item_from_scroll_y_include_seps(outScrollId, scrollY); + int32_t scrollY = y - (w->y + widget->top) + w->scrolls[*outScrollId].v_top + 5; + return window_editor_inventions_list_get_item_from_scroll_y_include_seps(*outScrollId, scrollY); } } } + *outScrollId = -1; return nullptr; } @@ -352,7 +310,7 @@ rct_window* window_editor_inventions_list_open() w->var_4AE = 0; w->selected_tab = 0; w->research_item = nullptr; - _editorInventionsListDraggedItem = nullptr; + _editorInventionsListDraggedItem.rawValue = -1; w->min_width = WW; w->min_height = WH; @@ -392,17 +350,17 @@ static void window_editor_inventions_list_mouseup(rct_window* w, rct_widgetindex break; case WIDX_RANDOM_SHUFFLE: research_items_shuffle(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_MOVE_ITEMS_TO_TOP: research_items_make_all_researched(); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); break; case WIDX_MOVE_ITEMS_TO_BOTTOM: research_items_make_all_unresearched(); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); break; } } @@ -411,12 +369,12 @@ static void window_editor_inventions_list_resize(rct_window* w) { if (w->width < w->min_width) { - window_invalidate(w); + w->Invalidate(); w->width = w->min_width; } if (w->height < w->min_height) { - window_invalidate(w); + w->Invalidate(); w->height = w->min_height; } } @@ -431,14 +389,14 @@ static void window_editor_inventions_list_update(rct_window* w) window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_1); - if (_editorInventionsListDraggedItem == nullptr) + if (_editorInventionsListDraggedItem.IsInventedEndMarker()) return; if (window_find_by_class(WC_EDITOR_INVENTION_LIST_DRAG) != nullptr) return; - _editorInventionsListDraggedItem = nullptr; - window_invalidate(w); + _editorInventionsListDraggedItem.rawValue = -1; + w->Invalidate(); } /** @@ -447,22 +405,14 @@ static void window_editor_inventions_list_update(rct_window* w) */ static void window_editor_inventions_list_scrollgetheight(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height) { - rct_research_item* researchItem; - *height = 0; - - // Count / skip pre-researched items - for (researchItem = gResearchItems; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - *height += SCROLLABLE_ROW_HEIGHT; - - if (scrollIndex == 1) + if (scrollIndex == 0) { - researchItem++; - - // Count non pre-researched items - *height = 0; - for (; researchItem->rawValue != RESEARCHED_ITEMS_END; researchItem++) - *height += SCROLLABLE_ROW_HEIGHT; + *height += (int32_t)gResearchItemsInvented.size() * SCROLLABLE_ROW_HEIGHT; + } + else + { + *height += (int32_t)gResearchItemsUninvented.size() * SCROLLABLE_ROW_HEIGHT; } } @@ -472,17 +422,17 @@ static void window_editor_inventions_list_scrollgetheight(rct_window* w, int32_t */ static void window_editor_inventions_list_scrollmousedown(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) { - rct_research_item* researchItem; + ResearchItem* researchItem; researchItem = window_editor_inventions_list_get_item_from_scroll_y(scrollIndex, y); if (researchItem == nullptr) return; // Disallow picking up always-researched items - if (researchItem->rawValue < RESEARCHED_ITEMS_END_2 || research_item_is_always_researched(researchItem)) + if (research_item_is_always_researched(researchItem)) return; - window_invalidate(w); + w->Invalidate(); window_editor_inventions_list_drag_open(researchItem); } @@ -492,13 +442,13 @@ static void window_editor_inventions_list_scrollmousedown(rct_window* w, int32_t */ static void window_editor_inventions_list_scrollmouseover(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) { - rct_research_item* researchItem; + ResearchItem* researchItem; researchItem = window_editor_inventions_list_get_item_from_scroll_y(scrollIndex, y); if (researchItem != w->research_item) { w->research_item = researchItem; - window_invalidate(w); + w->Invalidate(); // Prevent always-researched items from being highlighted when hovered over if (researchItem != nullptr && research_item_is_always_researched(researchItem)) @@ -515,7 +465,7 @@ static void window_editor_inventions_list_scrollmouseover(rct_window* w, int32_t static void window_editor_inventions_list_cursor( rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y, int32_t* cursorId) { - rct_research_item* researchItem; + ResearchItem* researchItem; int32_t scrollIndex; switch (widgetIndex) @@ -532,8 +482,7 @@ static void window_editor_inventions_list_cursor( // Use the open hand as cursor for items that can be picked up researchItem = window_editor_inventions_list_get_item_from_scroll_y(scrollIndex, y); - if (researchItem != nullptr && researchItem->rawValue >= RESEARCHED_ITEMS_END_2 - && !research_item_is_always_researched(researchItem)) + if (researchItem != nullptr && !research_item_is_always_researched(researchItem)) { *cursorId = CURSOR_HAND_OPEN; } @@ -593,7 +542,7 @@ static void window_editor_inventions_list_invalidate(rct_window* w) static void window_editor_inventions_list_paint(rct_window* w, rct_drawpixelinfo* dpi) { rct_widget* widget; - rct_research_item* researchItem; + ResearchItem* researchItem; rct_string_id stringId; int32_t x, y, width; @@ -620,8 +569,8 @@ static void window_editor_inventions_list_paint(rct_window* w, rct_drawpixelinfo dpi, w->x + widget->left + 1, w->y + widget->top + 1, w->x + widget->right - 1, w->y + widget->bottom - 1, ColourMapA[w->colours[1]].darkest); - researchItem = _editorInventionsListDraggedItem; - if (researchItem == nullptr) + researchItem = &_editorInventionsListDraggedItem; + if (researchItem->IsInventedEndMarker()) researchItem = w->research_item; // If the research item is null or a list separator. if (researchItem == nullptr || researchItem->rawValue < 0) @@ -680,36 +629,21 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix uint8_t paletteIndex = ColourMapA[w->colours[1]].mid_light; gfx_clear(dpi, paletteIndex); - rct_research_item* researchItem = gResearchItems; - int32_t researchItemEndMarker; - - if (scrollIndex == 1) - { - // Skip pre-researched items - for (; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - } - researchItem++; - researchItemEndMarker = RESEARCHED_ITEMS_END; - } - else - { - researchItemEndMarker = RESEARCHED_ITEMS_SEPARATOR; - } - int16_t boxWidth = (w->widgets[WIDX_RESEARCH_ORDER_SCROLL].right - w->widgets[WIDX_RESEARCH_ORDER_SCROLL].left); int16_t columnSplitOffset = boxWidth / 2; int32_t itemY = -SCROLLABLE_ROW_HEIGHT; - do + + const auto& researchList = scrollIndex == 0 ? gResearchItemsInvented : gResearchItemsUninvented; + for (const auto& researchItem : researchList) { itemY += SCROLLABLE_ROW_HEIGHT; if (itemY + SCROLLABLE_ROW_HEIGHT < dpi->y || itemY >= dpi->y + dpi->height) continue; - if (w->research_item == researchItem) + if (w->research_item == &researchItem) { int32_t top, bottom; - if (_editorInventionsListDraggedItem == nullptr) + if (_editorInventionsListDraggedItem.IsInventedEndMarker()) { // Highlight top = itemY; @@ -725,10 +659,7 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix gfx_filter_rect(dpi, 0, top, boxWidth, bottom, PALETTE_DARKEN_1); } - if (researchItem->rawValue == RESEARCHED_ITEMS_SEPARATOR || researchItem->rawValue == RESEARCHED_ITEMS_END) - continue; - - if (researchItem == _editorInventionsListDraggedItem) + if (researchItem.Equals(&_editorInventionsListDraggedItem)) continue; utf8 groupNameBuffer[256], vehicleNameBuffer[256]; @@ -736,9 +667,9 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix utf8* vehicleNamePtr = vehicleNameBuffer; uint8_t colour; - if (research_item_is_always_researched(researchItem)) + if (research_item_is_always_researched(&researchItem)) { - if (w->research_item == researchItem && _editorInventionsListDraggedItem == nullptr) + if (w->research_item == &researchItem && _editorInventionsListDraggedItem.IsInventedEndMarker()) gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM_EXTRA_DARK; else gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM_DARK; @@ -754,13 +685,13 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix vehicleNamePtr = utf8_write_codepoint(vehicleNamePtr, colour); } - rct_string_id itemNameId = research_item_get_name(researchItem); + rct_string_id itemNameId = research_item_get_name(&researchItem); - if (researchItem->type == RESEARCH_ENTRY_TYPE_RIDE - && !RideGroupManager::RideTypeIsIndependent(researchItem->baseRideType)) + if (researchItem.type == RESEARCH_ENTRY_TYPE_RIDE + && !RideGroupManager::RideTypeIsIndependent(researchItem.baseRideType)) { - const auto rideEntry = get_ride_entry(researchItem->entryIndex); - const rct_string_id rideGroupName = get_ride_naming(researchItem->baseRideType, rideEntry).name; + const auto rideEntry = get_ride_entry(researchItem.entryIndex); + const rct_string_id rideGroupName = get_ride_naming(researchItem.baseRideType, rideEntry).name; format_string( groupNamePtr, std::size(groupNameBuffer), STR_INVENTIONS_LIST_RIDE_AND_VEHICLE_NAME, (void*)&rideGroupName); format_string(vehicleNamePtr, std::size(vehicleNameBuffer), itemNameId, nullptr); @@ -781,7 +712,7 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix gfx_clip_string(vehicleNameBuffer, columnSplitOffset - 11); gfx_draw_string(dpi, vehicleNameBuffer, colour, columnSplitOffset + 1, itemY); } - } while (researchItem++->rawValue != researchItemEndMarker); + } } #pragma region Drag item @@ -790,14 +721,14 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix * * rct2: 0x006852F4 */ -static void window_editor_inventions_list_drag_open(rct_research_item* researchItem) +static void window_editor_inventions_list_drag_open(ResearchItem* researchItem) { char buffer[256], *ptr; int32_t stringWidth; rct_window* w; window_close_by_class(WC_EDITOR_INVENTION_LIST_DRAG); - _editorInventionsListDraggedItem = researchItem; + _editorInventionsListDraggedItem = *researchItem; rct_string_id stringId = research_item_get_name(researchItem); ptr = buffer; @@ -820,11 +751,12 @@ static void window_editor_inventions_list_drag_open(rct_research_item* researchI window_editor_inventions_list_drag_widgets[0].right = stringWidth; w = window_create( - gTooltipCursorX - (stringWidth / 2), gTooltipCursorY - 7, stringWidth, 14, &window_editor_inventions_list_drag_events, - WC_EDITOR_INVENTION_LIST_DRAG, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_SNAPPING); + ScreenCoordsXY(gTooltipCursorX - (stringWidth / 2), gTooltipCursorY - 7), stringWidth, 14, + &window_editor_inventions_list_drag_events, WC_EDITOR_INVENTION_LIST_DRAG, + WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_SNAPPING); w->widgets = window_editor_inventions_list_drag_widgets; w->colours[1] = COLOUR_WHITE; - input_window_position_begin(w, 0, gTooltipCursorX, gTooltipCursorY); + input_window_position_begin(w, 0, ScreenCoordsXY(gTooltipCursorX, gTooltipCursorY)); } /** @@ -837,10 +769,11 @@ static void window_editor_inventions_list_drag_cursor( rct_window* inventionListWindow = window_find_by_class(WC_EDITOR_INVENTION_LIST); if (inventionListWindow != nullptr) { - rct_research_item* researchItem = get_research_item_at(x, y); + int32_t scrollId; + ResearchItem* researchItem = get_research_item_at(x, y, &scrollId); if (researchItem != inventionListWindow->research_item) { - window_invalidate(inventionListWindow); + inventionListWindow->Invalidate(); } } @@ -853,20 +786,23 @@ static void window_editor_inventions_list_drag_cursor( */ static void window_editor_inventions_list_drag_moved(rct_window* w, int32_t x, int32_t y) { - rct_research_item* researchItem; + ResearchItem* researchItem; + int32_t scrollId; // Skip always researched items, so that the dragged item gets placed underneath them do { - researchItem = get_research_item_at(x, y); + researchItem = get_research_item_at(x, y, &scrollId); y += LIST_ROW_HEIGHT; - } while (researchItem != nullptr && researchItem->rawValue >= 0 && research_item_is_always_researched(researchItem)); + } while (researchItem != nullptr && research_item_is_always_researched(researchItem)); - if (researchItem != nullptr) - move_research_item(researchItem); + if (scrollId != -1) + { + move_research_item(researchItem, scrollId); + } window_close(w); - _editorInventionsListDraggedItem = nullptr; + _editorInventionsListDraggedItem.rawValue = -1; window_invalidate_by_class(WC_EDITOR_INVENTION_LIST); } @@ -881,11 +817,11 @@ static void window_editor_inventions_list_drag_paint(rct_window* w, rct_drawpixe x = w->x; y = w->y + 2; - drawString = window_editor_inventions_list_prepare_name(_editorInventionsListDraggedItem, true); + drawString = window_editor_inventions_list_prepare_name(&_editorInventionsListDraggedItem, true); gfx_draw_string_left(dpi, drawString, gCommonFormatArgs, COLOUR_BLACK | COLOUR_FLAG_OUTLINE, x, y); } -static rct_string_id window_editor_inventions_list_prepare_name(const rct_research_item* researchItem, bool withGap) +static rct_string_id window_editor_inventions_list_prepare_name(const ResearchItem* researchItem, bool withGap) { rct_string_id drawString; rct_string_id stringId = research_item_get_name(researchItem); diff --git a/src/openrct2-ui/windows/EditorMain.cpp b/src/openrct2-ui/windows/EditorMain.cpp index ee97ea15ce..738dbe6913 100644 --- a/src/openrct2-ui/windows/EditorMain.cpp +++ b/src/openrct2-ui/windows/EditorMain.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -62,8 +62,8 @@ rct_window* window_editor_main_open() window_editor_main_widgets[0].right = context_get_width(); window_editor_main_widgets[0].bottom = context_get_height(); rct_window* window = window_create( - 0, 0, window_editor_main_widgets[0].right, window_editor_main_widgets[0].bottom, &window_editor_main_events, - WC_MAIN_WINDOW, WF_STICK_TO_BACK); + ScreenCoordsXY(0, 0), window_editor_main_widgets[0].right, window_editor_main_widgets[0].bottom, + &window_editor_main_events, WC_MAIN_WINDOW, WF_STICK_TO_BACK); window->widgets = window_editor_main_widgets; viewport_create(window, window->x, window->y, window->width, window->height, 0, 0x0FFF, 0x0FFF, 0, 0x1, SPRITE_INDEX_NULL); diff --git a/src/openrct2-ui/windows/EditorObjectSelection.cpp b/src/openrct2-ui/windows/EditorObjectSelection.cpp index a00d519cdf..873415afa9 100644 --- a/src/openrct2-ui/windows/EditorObjectSelection.cpp +++ b/src/openrct2-ui/windows/EditorObjectSelection.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -99,9 +99,6 @@ static constexpr const ObjectPageDesc ObjectSelectionPages[] = { { STR_OBJECT_SELECTION_PARK_ENTRANCE, SPR_TAB_PARK, false }, { STR_OBJECT_SELECTION_WATER, SPR_TAB_WATER, false }, - // No longer supported: - // { STR_OBJECT_SELECTION_SCENARIO_DESCRIPTION, SPR_TAB_STATS, false }, - // Currently hidden until new save format arrives: // { STR_OBJECT_SELECTION_TERRAIN_SURFACES, SPR_G2_TAB_LAND, false }, // { STR_OBJECT_SELECTION_TERRAIN_EDGES, SPR_G2_TAB_LAND, false }, @@ -327,7 +324,7 @@ static void visible_list_refresh(rct_window* w) } } - if (_listItems.size() == 0) + if (_listItems.empty()) { visible_list_dispose(); } @@ -355,7 +352,7 @@ static void visible_list_refresh(rct_window* w) } } } - window_invalidate(w); + w->Invalidate(); } static void window_editor_object_selection_init_widgets() @@ -488,7 +485,7 @@ static void window_editor_object_selection_mouseup(rct_window* w, rct_widgetinde w->selected_list_item = -1; w->object_entry = nullptr; w->scrolls[0].v_top = 0; - window_invalidate(w); + w->Invalidate(); break; case WIDX_FILTER_RIDE_TAB_TRANSPORT: case WIDX_FILTER_RIDE_TAB_GENTLE: @@ -508,12 +505,12 @@ static void window_editor_object_selection_mouseup(rct_window* w, rct_widgetinde w->object_entry = nullptr; w->scrolls[0].v_top = 0; w->frame_no = 0; - window_invalidate(w); + w->Invalidate(); break; case WIDX_ADVANCED: w->list_information_type ^= 1; - window_invalidate(w); + w->Invalidate(); break; case WIDX_INSTALL_TRACK: @@ -522,7 +519,7 @@ static void window_editor_object_selection_mouseup(rct_window* w, rct_widgetinde { w->selected_list_item = -1; } - window_invalidate(w); + w->Invalidate(); auto intent = Intent(WC_LOADSAVE); intent.putExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_TRACK); @@ -537,7 +534,7 @@ static void window_editor_object_selection_mouseup(rct_window* w, rct_widgetinde filter_update_counts(); w->scrolls->v_top = 0; visible_list_refresh(w); - window_invalidate(w); + w->Invalidate(); break; case WIDX_LIST_SORT_TYPE: if (_listSortType == RIDE_SORT_TYPE) @@ -665,7 +662,7 @@ static void window_editor_object_selection_dropdown(rct_window* w, rct_widgetind w->scrolls->v_top = 0; visible_list_refresh(w); - window_invalidate(w); + w->Invalidate(); break; } } @@ -698,10 +695,10 @@ static void window_editor_object_selection_scroll_mousedown(rct_window* w, int32 if (object_selection_flags & OBJECT_SELECTION_FLAG_6) return; - window_invalidate(w); + w->Invalidate(); const CursorState* state = context_get_cursor_state(); - audio_play_sound(SOUND_CLICK_1, 0, state->x); + audio_play_sound(SoundId::Click1, 0, state->x); if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { @@ -735,7 +732,7 @@ static void window_editor_object_selection_scroll_mousedown(rct_window* w, int32 { filter_update_counts(); visible_list_refresh(w); - window_invalidate(w); + w->Invalidate(); } if (_maxObjectsWasHit) @@ -778,7 +775,7 @@ static void window_editor_object_selection_scroll_mouseover(rct_window* w, int32 _loadedObject = object_repository_load_object(listItem->entry); } - window_invalidate(w); + w->Invalidate(); } } @@ -828,7 +825,7 @@ static void window_editor_object_selection_invalidate(rct_window* w) w->pressed_widgets &= ~(1 << WIDX_ADVANCED); // Set window title and buttons - set_format_arg(0, rct_string_id, ObjectSelectionPages[get_selected_object_type(w)].Caption); + set_format_arg(0, rct_string_id, ObjectSelectionPages[w->selected_tab].Caption); if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { w->widgets[WIDX_TITLE].text = STR_TRACK_DESIGNS_MANAGER_SELECT_RIDE_TYPE; @@ -1216,7 +1213,7 @@ static void window_editor_object_set_page(rct_window* w, int32_t page) } visible_list_refresh(w); - window_invalidate(w); + w->Invalidate(); } static void window_editor_object_selection_set_pressed_tab(rct_window* w) @@ -1349,7 +1346,7 @@ static void window_editor_object_selection_textinput(rct_window* w, rct_widgetin w->scrolls->v_top = 0; visible_list_refresh(w); - window_invalidate(w); + w->Invalidate(); } static bool filter_selected(uint8_t objectFlag) @@ -1463,10 +1460,7 @@ static bool filter_chunks(const ObjectRepositoryItem* item) break; } } - if (_filter_flags & (1 << (gRideCategories[rideType] + _numSourceGameItems))) - return true; - - return false; + return (_filter_flags & (1 << (gRideCategories[rideType] + _numSourceGameItems))) != 0; } return true; } @@ -1533,5 +1527,9 @@ static std::string object_get_description(const void* object) static int32_t get_selected_object_type(rct_window* w) { - return w->selected_tab; + auto tab = w->selected_tab; + if (tab >= OBJECT_TYPE_SCENARIO_TEXT) + return tab + 1; + else + return tab; } diff --git a/src/openrct2-ui/windows/EditorObjectiveOptions.cpp b/src/openrct2-ui/windows/EditorObjectiveOptions.cpp index c83f6af756..f8aecd0e00 100644 --- a/src/openrct2-ui/windows/EditorObjectiveOptions.cpp +++ b/src/openrct2-ui/windows/EditorObjectiveOptions.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,10 +12,13 @@ #include "../interface/Window.h" #include "Window.h" +#include #include #include +#include #include #include +#include #include #include #include @@ -332,12 +335,12 @@ static void window_editor_objective_options_set_page(rct_window* w, int32_t page w->hold_down_widgets = window_editor_objective_options_page_hold_down_widgets[page]; w->event_handlers = window_editor_objective_options_page_events[page]; w->widgets = window_editor_objective_options_widgets[page]; - window_invalidate(w); + w->Invalidate(); window_editor_objective_options_update_disabled_widgets(w); window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); } /** @@ -347,7 +350,7 @@ static void window_editor_objective_options_set_page(rct_window* w, int32_t page static void window_editor_objective_options_set_objective(rct_window* w, int32_t objective) { gScenarioObjectiveType = objective; - window_invalidate(w); + w->Invalidate(); // Set default objective arguments switch (objective) @@ -402,9 +405,11 @@ static void window_editor_objective_options_main_mouseup(rct_window* w, rct_widg window_editor_objective_options_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_PARK_NAME: - set_format_arg(16, uint32_t, gParkNameArgs); - window_text_input_open(w, WIDX_PARK_NAME, STR_PARK_NAME, STR_ENTER_PARK_NAME, gParkName, 0, 32); + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + window_text_input_raw_open(w, WIDX_PARK_NAME, STR_PARK_NAME, STR_ENTER_PARK_NAME, park.Name.c_str(), 32); break; + } case WIDX_SCENARIO_NAME: window_text_input_raw_open(w, WIDX_SCENARIO_NAME, STR_SCENARIO_NAME, STR_ENTER_SCENARIO_NAME, gS6Info.name, 64); break; @@ -526,7 +531,7 @@ static void window_editor_objective_options_arg_1_increase(rct_window* w) else { gScenarioObjectiveCurrency += MONEY(1000, 0); - window_invalidate(w); + w->Invalidate(); } break; case OBJECTIVE_MONTHLY_FOOD_INCOME: @@ -537,7 +542,7 @@ static void window_editor_objective_options_arg_1_increase(rct_window* w) else { gScenarioObjectiveCurrency += MONEY(100, 0); - window_invalidate(w); + w->Invalidate(); } break; case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: @@ -548,7 +553,7 @@ static void window_editor_objective_options_arg_1_increase(rct_window* w) else { gScenarioObjectiveNumGuests += 100; - window_invalidate(w); + w->Invalidate(); } break; case OBJECTIVE_FINISH_5_ROLLERCOASTERS: @@ -559,7 +564,7 @@ static void window_editor_objective_options_arg_1_increase(rct_window* w) else { gScenarioObjectiveCurrency += FIXED_2DP(0, 10); - window_invalidate(w); + w->Invalidate(); } break; default: @@ -570,7 +575,7 @@ static void window_editor_objective_options_arg_1_increase(rct_window* w) else { gScenarioObjectiveNumGuests += 50; - window_invalidate(w); + w->Invalidate(); } break; } @@ -590,7 +595,7 @@ static void window_editor_objective_options_arg_1_decrease(rct_window* w) else { gScenarioObjectiveCurrency -= MONEY(1000, 0); - window_invalidate(w); + w->Invalidate(); } break; case OBJECTIVE_MONTHLY_FOOD_INCOME: @@ -601,7 +606,7 @@ static void window_editor_objective_options_arg_1_decrease(rct_window* w) else { gScenarioObjectiveCurrency -= MONEY(100, 0); - window_invalidate(w); + w->Invalidate(); } break; case OBJECTIVE_10_ROLLERCOASTERS_LENGTH: @@ -612,7 +617,7 @@ static void window_editor_objective_options_arg_1_decrease(rct_window* w) else { gScenarioObjectiveNumGuests -= 100; - window_invalidate(w); + w->Invalidate(); } break; case OBJECTIVE_FINISH_5_ROLLERCOASTERS: @@ -623,7 +628,7 @@ static void window_editor_objective_options_arg_1_decrease(rct_window* w) else { gScenarioObjectiveCurrency -= FIXED_2DP(0, 10); - window_invalidate(w); + w->Invalidate(); } break; default: @@ -634,7 +639,7 @@ static void window_editor_objective_options_arg_1_decrease(rct_window* w) else { gScenarioObjectiveNumGuests -= 50; - window_invalidate(w); + w->Invalidate(); } break; } @@ -649,7 +654,7 @@ static void window_editor_objective_options_arg_2_increase(rct_window* w) else { gScenarioObjectiveYear++; - window_invalidate(w); + w->Invalidate(); } } @@ -662,7 +667,7 @@ static void window_editor_objective_options_arg_2_decrease(rct_window* w) else { gScenarioObjectiveYear--; - window_invalidate(w); + w->Invalidate(); } } @@ -718,7 +723,7 @@ static void window_editor_objective_options_main_dropdown(rct_window* w, rct_wid if (gS6Info.category != (uint8_t)dropdownIndex) { gS6Info.category = (uint8_t)dropdownIndex; - window_invalidate(w); + w->Invalidate(); } break; } @@ -771,16 +776,19 @@ static void window_editor_objective_options_main_textinput(rct_window* w, rct_wi GameActions::Execute(&action); if (gS6Info.name[0] == '\0') - format_string(gS6Info.name, 64, gParkName, &gParkNameArgs); + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + String::Set(gS6Info.name, sizeof(gS6Info.name), park.Name.c_str()); + } break; } case WIDX_SCENARIO_NAME: safe_strcpy(gS6Info.name, text, std::size(gS6Info.name)); - window_invalidate(w); + w->Invalidate(); break; case WIDX_DETAILS: safe_strcpy(gS6Info.details, text, std::size(gS6Info.details)); - window_invalidate(w); + w->Invalidate(); break; } } @@ -943,9 +951,14 @@ static void window_editor_objective_options_main_paint(rct_window* w, rct_drawpi y = w->y + w->widgets[WIDX_PARK_NAME].top; width = w->widgets[WIDX_PARK_NAME].left - 16; - set_format_arg(0, rct_string_id, gParkName); - set_format_arg(2, uint32_t, gParkNameArgs); - gfx_draw_string_left_clipped(dpi, STR_WINDOW_PARK_NAME, gCommonFormatArgs, COLOUR_BLACK, x, y, width); + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); + + set_format_arg(0, rct_string_id, STR_STRING); + set_format_arg(2, const char*, parkName); + gfx_draw_string_left_clipped(dpi, STR_WINDOW_PARK_NAME, gCommonFormatArgs, COLOUR_BLACK, x, y, width); + } // Scenario name x = w->x + 8; @@ -1017,20 +1030,17 @@ static void window_editor_objective_options_rides_resize(rct_window* w) */ static void window_editor_objective_options_rides_update(rct_window* w) { - int32_t i, numItems; - Ride* ride; - w->frame_no++; window_event_invalidate_call(w); window_event_resize_call(w); widget_invalidate(w, WIDX_TAB_2); - numItems = 0; - FOR_ALL_RIDES (i, ride) + auto numItems = 0; + for (auto& ride : GetRideManager()) { - if (gRideClassifications[ride->type] == RIDE_CLASS_RIDE) + if (ride.IsRide()) { - w->list_item_positions[numItems] = i; + w->list_item_positions[numItems] = ride.id; numItems++; } } @@ -1038,7 +1048,7 @@ static void window_editor_objective_options_rides_update(rct_window* w) if (w->no_list_items != numItems) { w->no_list_items = numItems; - window_invalidate(w); + w->Invalidate(); } } @@ -1058,16 +1068,16 @@ static void window_editor_objective_options_rides_scrollgetheight( */ static void window_editor_objective_options_rides_scrollmousedown(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) { - Ride* ride; - int32_t i; - - i = y / 12; + auto i = y / 12; if (i < 0 || i >= w->no_list_items) return; - ride = get_ride(w->list_item_positions[i]); - ride->lifecycle_flags ^= RIDE_LIFECYCLE_INDESTRUCTIBLE; - window_invalidate(w); + auto ride = get_ride(w->list_item_positions[i]); + if (ride != nullptr) + { + ride->lifecycle_flags ^= RIDE_LIFECYCLE_INDESTRUCTIBLE; + } + w->Invalidate(); } /** @@ -1085,7 +1095,7 @@ static void window_editor_objective_options_rides_scrollmouseover(rct_window* w, if (w->selected_list_item != i) { w->selected_list_item = i; - window_invalidate(w); + w->Invalidate(); } } @@ -1132,9 +1142,6 @@ static void window_editor_objective_options_rides_paint(rct_window* w, rct_drawp */ static void window_editor_objective_options_rides_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) { - rct_string_id stringId; - Ride* ride; - int32_t colour = ColourMapA[w->colours[1]].mid_light; gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, colour); @@ -1149,27 +1156,29 @@ static void window_editor_objective_options_rides_scrollpaint(rct_window* w, rct gfx_fill_rect_inset(dpi, 2, y, 11, y + 10, w->colours[1], INSET_RECT_F_E0); // Highlighted + auto stringId = STR_BLACK_STRING; if (i == w->selected_list_item) { stringId = STR_WINDOW_COLOUR_2_STRINGID; gfx_filter_rect(dpi, 0, y, w->width, y + 11, PALETTE_DARKEN_1); } - else - { - stringId = STR_BLACK_STRING; - } // Checkbox mark - ride = get_ride(w->list_item_positions[i]); - if (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE) + auto ride = get_ride(w->list_item_positions[i]); + if (ride != nullptr) { - gCurrentFontSpriteBase = stringId == STR_WINDOW_COLOUR_2_STRINGID ? FONT_SPRITE_BASE_MEDIUM_EXTRA_DARK - : FONT_SPRITE_BASE_MEDIUM_DARK; - gfx_draw_string(dpi, (char*)CheckBoxMarkString, w->colours[1] & 0x7F, 2, y); - } + if (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE) + { + gCurrentFontSpriteBase = stringId == STR_WINDOW_COLOUR_2_STRINGID ? FONT_SPRITE_BASE_MEDIUM_EXTRA_DARK + : FONT_SPRITE_BASE_MEDIUM_DARK; + gfx_draw_string(dpi, (char*)CheckBoxMarkString, w->colours[1] & 0x7F, 2, y); + } - // Ride name - gfx_draw_string_left(dpi, stringId, &ride->name, COLOUR_BLACK, 15, y); + // Ride name + uint32_t args[32]{}; + ride->FormatNameTo(args); + gfx_draw_string_left(dpi, stringId, args, COLOUR_BLACK, 15, y); + } } } @@ -1179,20 +1188,9 @@ static void window_editor_objective_options_rides_scrollpaint(rct_window* w, rct */ static void window_editor_objective_options_update_disabled_widgets(rct_window* w) { - Ride* ride; - int32_t i, numRides; - // Check if there are any rides (not shops or facilities) - numRides = 0; - FOR_ALL_RIDES (i, ride) - { - if (gRideClassifications[ride->type] == RIDE_CLASS_RIDE) - { - numRides++; - } - } - - if (numRides != 0) + const auto& rideManager = GetRideManager(); + if (std::any_of(rideManager.begin(), rideManager.end(), [](const Ride& ride) { return ride.IsRide(); })) { w->disabled_widgets &= ~(1 << WIDX_TAB_2); } diff --git a/src/openrct2-ui/windows/EditorScenarioOptions.cpp b/src/openrct2-ui/windows/EditorScenarioOptions.cpp index dfdcf13897..e134324c28 100644 --- a/src/openrct2-ui/windows/EditorScenarioOptions.cpp +++ b/src/openrct2-ui/windows/EditorScenarioOptions.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -470,11 +471,11 @@ static void window_editor_scenario_options_set_page(rct_window* w, int32_t page) w->hold_down_widgets = window_editor_scenario_options_page_hold_down_widgets[page]; w->event_handlers = window_editor_scenario_options_page_events[page]; w->widgets = window_editor_scenario_options_widgets[page]; - window_invalidate(w); + w->Invalidate(); window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); } #pragma region Financial @@ -508,18 +509,19 @@ static void window_editor_scenario_options_financial_mouseup(rct_window* w, rct_ newMoneySetting = (gParkFlags & PARK_FLAGS_NO_MONEY) ? 0 : 1; } - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETNOMONEY, newMoneySetting, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - window_invalidate(w); + auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::NoMoney, newMoneySetting); + GameActions::Execute(&scenarioSetSetting); + w->Invalidate(); break; } case WIDX_FORBID_MARKETING: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETFORBIDMARKETINGCAMPAIGNS, - gParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN ? 0 : 1, GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - window_invalidate(w); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidMarketingCampaigns, gParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + w->Invalidate(); break; + } } } @@ -561,115 +563,106 @@ static void window_editor_scenario_options_financial_mousedown(rct_window* w, rc case WIDX_INITIAL_CASH_INCREASE: if (gInitialCash < MONEY(1000000, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETINITIALCASH, gInitialCash + MONEY(500, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::InitialCash, gInitialCash + MONEY(500, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_CASH, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_INITIAL_CASH_DECREASE: if (gInitialCash > MONEY(0, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETINITIALCASH, gInitialCash - MONEY(500, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::InitialCash, gInitialCash - MONEY(500, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_CASH, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_INITIAL_LOAN_INCREASE: if (gBankLoan < MONEY(5000000, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETINITIALLOAN, gBankLoan + MONEY(1000, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::InitialLoan, gBankLoan + MONEY(1000, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_INIT_LOAN, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_INITIAL_LOAN_DECREASE: if (gBankLoan > MONEY(0, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETINITIALLOAN, gBankLoan - MONEY(1000, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::InitialLoan, gBankLoan - MONEY(1000, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_INIT_LOAN, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_MAXIMUM_LOAN_INCREASE: if (gMaxBankLoan < MONEY(5000000, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETMAXIMUMLOANSIZE, gMaxBankLoan + MONEY(1000, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::MaximumLoanSize, gMaxBankLoan + MONEY(1000, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_MAX_LOAN, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_MAXIMUM_LOAN_DECREASE: if (gMaxBankLoan > MONEY(0, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETMAXIMUMLOANSIZE, gMaxBankLoan - MONEY(1000, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::MaximumLoanSize, gMaxBankLoan - MONEY(1000, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_MAX_LOAN, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_INTEREST_RATE_INCREASE: if (gBankLoanInterestRate < 80) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETANNUALINTERESTRATE, gBankLoanInterestRate + 1, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::AnnualInterestRate, gBankLoanInterestRate + 1); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_INTEREST_RATE, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_INTEREST_RATE_DECREASE: if (gBankLoanInterestRate > 0) { - if (gBankLoanInterestRate > 80) - { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETANNUALINTERESTRATE, 80, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - } - else - { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETANNUALINTERESTRATE, gBankLoanInterestRate - 1, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - } + auto interest = std::min(80, gBankLoanInterestRate - 1); + auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::AnnualInterestRate, interest); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_INTEREST_RATE, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; } @@ -820,17 +813,21 @@ static void window_editor_scenario_options_guests_mouseup(rct_window* w, rct_wid window_editor_scenario_options_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_GUEST_PREFER_LESS_INTENSE_RIDES: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETGUESTSPREFERLESSINTENSERIDES, - gParkFlags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES ? 0 : 1, GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - window_invalidate(w); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestsPreferLessIntenseRides, gParkFlags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + w->Invalidate(); break; + } case WIDX_GUEST_PREFER_MORE_INTENSE_RIDES: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETGUESTSPREFERMOREINTENSERIDES, - gParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES ? 0 : 1, GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - window_invalidate(w); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestsPreferMoreIntenseRides, gParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + w->Invalidate(); break; + } } } @@ -854,106 +851,106 @@ static void window_editor_scenario_options_guests_mousedown(rct_window* w, rct_w case WIDX_CASH_PER_GUEST_INCREASE: if (gGuestInitialCash < MONEY(1000, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETAVERAGECASHPERGUEST, gGuestInitialCash + MONEY(1, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::AverageCashPerGuest, gGuestInitialCash + MONEY(1, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_CASH_PER_GUEST_DECREASE: if (gGuestInitialCash > MONEY(0, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETAVERAGECASHPERGUEST, gGuestInitialCash - MONEY(1, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::AverageCashPerGuest, gGuestInitialCash - MONEY(1, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_GUEST_INITIAL_HAPPINESS_INCREASE: if (gGuestInitialHappiness < 250) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETGUESTINITIALHAPPINESS, gGuestInitialHappiness + 4, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialHappiness, gGuestInitialHappiness + 4); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_GUEST_INITIAL_HAPPINESS_DECREASE: if (gGuestInitialHappiness > 40) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETGUESTINITIALHAPPINESS, gGuestInitialHappiness - 4, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialHappiness, gGuestInitialHappiness - 4); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_GUEST_INITIAL_HUNGER_INCREASE: if (gGuestInitialHunger > 40) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETGUESTINITIALHUNGER, gGuestInitialHunger - 4, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialHunger, gGuestInitialHunger - 4); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_GUEST_INITIAL_HUNGER_DECREASE: if (gGuestInitialHunger < 250) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETGUESTINITIALHUNGER, gGuestInitialHunger + 4, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialHunger, gGuestInitialHunger + 4); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_GUEST_INITIAL_THIRST_INCREASE: if (gGuestInitialThirst > 40) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETGUESTINITIALTHIRST, gGuestInitialThirst - 4, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialThirst, gGuestInitialThirst - 4); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_GUEST_INITIAL_THIRST_DECREASE: if (gGuestInitialThirst < 250) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETGUESTINITIALTHIRST, gGuestInitialThirst + 4, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestInitialThirst, gGuestInitialThirst + 4); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; } } @@ -1097,35 +1094,46 @@ static void window_editor_scenario_options_park_mouseup(rct_window* w, rct_widge window_editor_scenario_options_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_FORBID_TREE_REMOVAL: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETFORBIDTREEREMOVAL, - gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL ? 0 : 1, GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - window_invalidate(w); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidTreeRemoval, gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + w->Invalidate(); break; + } case WIDX_FORBID_LANDSCAPE_CHANGES: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETFORBIDLANDSCAPECHANGES, - gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES ? 0 : 1, GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - window_invalidate(w); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidLandscapeChanges, gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + w->Invalidate(); break; + } case WIDX_FORBID_HIGH_CONSTRUCTION: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETFORBIDHIGHCONSTRUCTION, - gParkFlags & PARK_FLAGS_FORBID_HIGH_CONSTRUCTION ? 0 : 1, GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - window_invalidate(w); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidHighConstruction, gParkFlags & PARK_FLAGS_FORBID_HIGH_CONSTRUCTION ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + w->Invalidate(); break; + } case WIDX_HARD_PARK_RATING: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETPARKRATINGHIGHERDIFFICULTLEVEL, - gParkFlags & PARK_FLAGS_DIFFICULT_PARK_RATING ? 0 : 1, GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - window_invalidate(w); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ParkRatingHigherDifficultyLevel, gParkFlags & PARK_FLAGS_DIFFICULT_PARK_RATING ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + w->Invalidate(); break; + } case WIDX_HARD_GUEST_GENERATION: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETGUESTGENERATIONHIGHERDIFFICULTLEVEL, - gParkFlags & PARK_FLAGS_DIFFICULT_GUEST_GENERATION ? 0 : 1, GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - window_invalidate(w); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestGenerationHigherDifficultyLevel, + gParkFlags & PARK_FLAGS_DIFFICULT_GUEST_GENERATION ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); + w->Invalidate(); break; + } } } @@ -1151,80 +1159,80 @@ static void window_editor_scenario_options_park_mousedown(rct_window* w, rct_wid case WIDX_LAND_COST_INCREASE: if (gLandPrice < MONEY(200, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETCOSTTOBUYLAND, gLandPrice + MONEY(1, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::CostToBuyLand, gLandPrice + MONEY(1, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_LAND_COST_DECREASE: if (gLandPrice > MONEY(5, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETCOSTTOBUYLAND, gLandPrice - MONEY(1, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::CostToBuyLand, gLandPrice - MONEY(1, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_CONSTRUCTION_RIGHTS_COST_INCREASE: if (gConstructionRightsPrice < MONEY(200, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETCOSTTOBUYCONSTRUCTIONRIGHTS, - gConstructionRightsPrice + MONEY(1, 00), GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::CostToBuyConstructionRights, gConstructionRightsPrice + MONEY(1, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_CONSTRUCTION_RIGHTS_COST_DECREASE: if (gConstructionRightsPrice > MONEY(5, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETCOSTTOBUYCONSTRUCTIONRIGHTS, - gConstructionRightsPrice - MONEY(1, 00), GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::CostToBuyConstructionRights, gConstructionRightsPrice - MONEY(1, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_ENTRY_PRICE_INCREASE: if (gParkEntranceFee < MAX_ENTRANCE_FEE) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETPARKCHARGEENTRYFEE, gParkEntranceFee + MONEY(1, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ParkChargeEntryFee, gParkEntranceFee + MONEY(1, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_INCREASE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_ENTRY_PRICE_DECREASE: if (gParkEntranceFee > MONEY(0, 00)) { - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETPARKCHARGEENTRYFEE, gParkEntranceFee - MONEY(1, 00), - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ParkChargeEntryFee, gParkEntranceFee - MONEY(1, 00)); + GameActions::Execute(&scenarioSetSetting); } else { context_show_error(STR_CANT_REDUCE_FURTHER, STR_NONE); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_PAY_FOR_PARK_OR_RIDES_DROPDOWN: dropdownWidget = widget - 1; @@ -1268,12 +1276,12 @@ static void window_editor_scenario_options_park_dropdown(rct_window* w, rct_widg switch (widgetIndex) { case WIDX_PAY_FOR_PARK_OR_RIDES_DROPDOWN: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, EDIT_SCENARIOOPTIONS_SETPARKCHARGEMETHOD, dropdownIndex, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, 0, 0); - window_invalidate(w); + { + auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::ParkChargeMethod, dropdownIndex); + GameActions::Execute(&scenarioSetSetting); + w->Invalidate(); break; - + } case WIDX_CLIMATE_DROPDOWN: if (gClimate != (uint8_t)dropdownIndex) { diff --git a/src/openrct2-ui/windows/Error.cpp b/src/openrct2-ui/windows/Error.cpp index 432c91cb5b..2f20f5dd7c 100644 --- a/src/openrct2-ui/windows/Error.cpp +++ b/src/openrct2-ui/windows/Error.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -138,12 +138,13 @@ rct_window* window_error_open(rct_string_id title, rct_string_id message) y = std::min(y, maxY); } - w = window_create(x, y, width, height, &window_error_events, WC_ERROR, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_RESIZABLE); + w = window_create( + ScreenCoordsXY(x, y), width, height, &window_error_events, WC_ERROR, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_RESIZABLE); w->widgets = window_error_widgets; w->error.var_480 = 0; if (!gDisableErrorWindowSound) { - audio_play_sound(SOUND_ERROR, 0, w->x + (w->width / 2)); + audio_play_sound(SoundId::Error, 0, w->x + (w->width / 2)); } return w; diff --git a/src/openrct2-ui/windows/Finances.cpp b/src/openrct2-ui/windows/Finances.cpp index 4f0cc9306e..d2da2adfa1 100644 --- a/src/openrct2-ui/windows/Finances.cpp +++ b/src/openrct2-ui/windows/Finances.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -535,10 +536,10 @@ rct_window* window_finances_open() } w->page = WINDOW_FINANCES_PAGE_SUMMARY; - window_invalidate(w); + w->Invalidate(); w->width = 530; w->height = 310; - window_invalidate(w); + w->Invalidate(); w->widgets = _windowFinancesPageWidgets[WINDOW_FINANCES_PAGE_SUMMARY]; w->enabled_widgets = WindowFinancesPageEnabledWidgets[WINDOW_FINANCES_PAGE_SUMMARY]; @@ -611,14 +612,14 @@ static void window_finances_summary_mousedown(rct_window* w, rct_widgetindex wid } } -static uint16_t summary_num_months_available() +static uint16_t summary_max_available_month() { - return std::min(gDateMonthsElapsed, EXPENDITURE_TABLE_MONTH_COUNT); + return std::min(gDateMonthsElapsed, EXPENDITURE_TABLE_MONTH_COUNT - 1); } static void window_finances_summary_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height) { - *width = EXPENDITURE_COLUMN_WIDTH * (summary_num_months_available() + 1); + *width = EXPENDITURE_COLUMN_WIDTH * (summary_max_available_month() + 1); } static void window_finances_summary_invertscroll(rct_window* w) @@ -737,7 +738,7 @@ static void window_finances_summary_scrollpaint(rct_window* w, rct_drawpixelinfo // Expenditure / Income values for each month int16_t currentMonthYear = gDateMonthsElapsed; - for (int32_t i = summary_num_months_available(); i >= 0; i--) + for (int32_t i = summary_max_available_month(); i >= 0; i--) { y = 0; @@ -1182,8 +1183,6 @@ static void window_finances_marketing_paint(rct_window* w, rct_drawpixelinfo* dp continue; noCampaignsActive = 0; - set_format_arg(0, rct_string_id, gParkName); - set_format_arg(2, uint32_t, gParkNameArgs); // Set special parameters switch (i) @@ -1192,13 +1191,26 @@ static void window_finances_marketing_paint(rct_window* w, rct_drawpixelinfo* dp case ADVERTISING_CAMPAIGN_RIDE: { auto ride = get_ride(campaign->RideId); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + if (ride != nullptr) + { + ride->FormatNameTo(gCommonFormatArgs); + } + else + { + set_format_arg(0, rct_string_id, STR_NONE); + } break; } case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE: - set_format_arg(0, rct_string_id, ShopItemStringIds[campaign->ShopItemType].plural); + set_format_arg(0, rct_string_id, ShopItems[campaign->ShopItemType].Naming.Plural); break; + default: + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); + set_format_arg(0, rct_string_id, STR_STRING); + set_format_arg(2, const char*, parkName); + } } // Advertisement @@ -1415,7 +1427,7 @@ static void window_finances_set_page(rct_window* w, int32_t page) w->disabled_widgets = 0; w->pressed_widgets = 0; - window_invalidate(w); + w->Invalidate(); if (w->page == WINDOW_FINANCES_PAGE_RESEARCH) { w->width = 320; @@ -1435,7 +1447,7 @@ static void window_finances_set_page(rct_window* w, int32_t page) window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); // Scroll summary all the way to the right, initially. if (w->page == WINDOW_FINANCES_PAGE_SUMMARY) diff --git a/src/openrct2-ui/windows/Footpath.cpp b/src/openrct2-ui/windows/Footpath.cpp index de3891bbeb..75068a0300 100644 --- a/src/openrct2-ui/windows/Footpath.cpp +++ b/src/openrct2-ui/windows/Footpath.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -216,7 +216,7 @@ rct_window* window_footpath_open() return window; } - window = window_create(0, 29, 106, 381, &window_footpath_events, WC_FOOTPATH, 0); + window = window_create(ScreenCoordsXY(0, 29), 106, 381, &window_footpath_events, WC_FOOTPATH, 0); window->widgets = window_footpath_widgets; window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_FOOTPATH_TYPE) | (1 << WIDX_QUEUELINE_TYPE) | (1 << WIDX_DIRECTION_NW) | (1 << WIDX_DIRECTION_NE) | (1 << WIDX_DIRECTION_SW) | (1 << WIDX_DIRECTION_SE) @@ -398,7 +398,7 @@ static void window_footpath_dropdown(rct_window* w, rct_widgetindex widgetIndex, gFootpathSelectedId = pathId; footpath_provisional_update(); _window_footpath_cost = MONEY32_UNDEFINED; - window_invalidate(w); + w->Invalidate(); } /** @@ -611,16 +611,9 @@ static void window_footpath_paint(rct_window* w, rct_drawpixelinfo* dpi) } int32_t image = ConstructionPreviewImages[slope][direction]; - int32_t selectedPath = gFootpathSelectedId; + int32_t selectedPath = gFootpathSelectedId + (MAX_PATH_OBJECTS * gFootpathSelectedType); PathSurfaceEntry* pathType = get_path_surface_entry(selectedPath); - if (gFootpathSelectedType == SELECTED_PATH_TYPE_NORMAL) - { - image += pathType->image; - } - else - { - image += pathType->queue_image; - } + image += pathType->image; // Draw construction image int32_t x = w->x + (window_footpath_widgets[WIDX_CONSTRUCT].left + window_footpath_widgets[WIDX_CONSTRUCT].right) / 2; @@ -902,7 +895,7 @@ static void window_footpath_place_path_at_point(int32_t x, int32_t y) // Don't play sound if it is no cost to prevent multiple sounds. TODO: make this work in no money scenarios if (result->Cost != 0) { - audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z); + audio_play_sound_at_location(SoundId::PlaceItem, result->Position); } } else @@ -990,7 +983,7 @@ static void window_footpath_construct() footpathPlaceAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) { if (result->Error == GA_ERROR::OK) { - audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z); + audio_play_sound_at_location(SoundId::PlaceItem, result->Position); if (gFootpathConstructSlope == 0) { @@ -1101,6 +1094,8 @@ static TileElement* footpath_get_tile_element_to_remove() tileElement = map_get_first_element_at(x, y); do { + if (tileElement == nullptr) + break; if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH) { if (tileElement->base_height == z) @@ -1172,9 +1167,9 @@ static void window_footpath_set_enabled_and_pressed_widgets() gMapSelectFlags |= MAP_SELECT_FLAG_GREEN; int32_t direction = gFootpathConstructDirection; - gMapSelectionTiles[0].x = gFootpathConstructFromPosition.x + CoordsDirectionDelta[direction].x; - gMapSelectionTiles[0].y = gFootpathConstructFromPosition.y + CoordsDirectionDelta[direction].y; - gMapSelectionTiles[1].x = -1; + gMapSelectionTiles.clear(); + gMapSelectionTiles.push_back({ gFootpathConstructFromPosition.x + CoordsDirectionDelta[direction].x, + gFootpathConstructFromPosition.y + CoordsDirectionDelta[direction].y }); map_invalidate_map_selection_tiles(); } @@ -1225,7 +1220,7 @@ static void window_footpath_set_enabled_and_pressed_widgets() w->pressed_widgets = pressedWidgets; w->disabled_widgets = disabledWidgets; - window_invalidate(w); + w->Invalidate(); } /** diff --git a/src/openrct2-ui/windows/GameBottomToolbar.cpp b/src/openrct2-ui/windows/GameBottomToolbar.cpp index c50653b43c..285efc300a 100644 --- a/src/openrct2-ui/windows/GameBottomToolbar.cpp +++ b/src/openrct2-ui/windows/GameBottomToolbar.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -134,8 +134,8 @@ rct_window* window_game_bottom_toolbar_open() uint32_t toolbar_height = line_height * 2 + 12; rct_window* window = window_create( - 0, screenHeight - toolbar_height, screenWidth, toolbar_height, &window_game_bottom_toolbar_events, WC_BOTTOM_TOOLBAR, - WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); + ScreenCoordsXY(0, screenHeight - toolbar_height), screenWidth, toolbar_height, &window_game_bottom_toolbar_events, + WC_BOTTOM_TOOLBAR, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); window->widgets = window_game_bottom_toolbar_widgets; window->enabled_widgets |= (1 << WIDX_LEFT_OUTSET) | (1 << WIDX_MONEY) | (1 << WIDX_GUESTS) | (1 << WIDX_PARK_RATING) | (1 << WIDX_MIDDLE_OUTSET) | (1 << WIDX_MIDDLE_INSET) | (1 << WIDX_NEWS_SUBJECT) | (1 << WIDX_NEWS_LOCATE) diff --git a/src/openrct2-ui/windows/Guest.cpp b/src/openrct2-ui/windows/Guest.cpp index 8eaa297c2f..a7bcc0b495 100644 --- a/src/openrct2-ui/windows/Guest.cpp +++ b/src/openrct2-ui/windows/Guest.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,7 +12,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -35,7 +38,8 @@ enum WINDOW_GUEST_PAGE { WINDOW_GUEST_RIDES, WINDOW_GUEST_FINANCE, WINDOW_GUEST_THOUGHTS, - WINDOW_GUEST_INVENTORY + WINDOW_GUEST_INVENTORY, + WINDOW_GUEST_DEBUG }; enum WINDOW_GUEST_WIDGET_IDX { @@ -49,8 +53,9 @@ enum WINDOW_GUEST_WIDGET_IDX { WIDX_TAB_4, WIDX_TAB_5, WIDX_TAB_6, + WIDX_TAB_7, - WIDX_MARQUEE = 10, + WIDX_MARQUEE = 11, WIDX_VIEWPORT, WIDX_ACTION_LBL, WIDX_PICKUP, @@ -58,102 +63,67 @@ enum WINDOW_GUEST_WIDGET_IDX { WIDX_LOCATE, WIDX_TRACK, - WIDX_RIDE_SCROLL = 10 + WIDX_RIDE_SCROLL = 11 }; -validate_global_widx(WC_PEEP, WIDX_ACTION_LBL); validate_global_widx(WC_PEEP, WIDX_PICKUP); +static constexpr int32_t TabWidth = 30; + +#define MAIN_GUEST_WIDGETS \ + { WWT_FRAME, 0, 0, 191, 0, 156, 0xFFFFFFFF, STR_NONE }, /* Panel / Background */ \ + { WWT_CAPTION, 0, 1, 190, 1, 14, STR_STRINGID, STR_WINDOW_TITLE_TIP }, /* Title */ \ + { WWT_CLOSEBOX, 0, 179, 189, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, /* Close x button */ \ + { WWT_RESIZE, 1, 0, 191, 43, 156, 0xFFFFFFFF, STR_NONE }, /* Resize */ \ + { WWT_TAB, 1, 3, TabWidth + 3, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VIEW_TIP }, /* Tab 1 */ \ + { WWT_TAB, 1, 34, TabWidth + 34, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_NEEDS_TIP }, /* Tab 2 */ \ + { WWT_TAB, 1, 65, TabWidth + 65, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VISITED_RIDES_TIP }, /* Tab 3 */ \ + { WWT_TAB, 1, 96, TabWidth + 96, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_FINANCE_TIP }, /* Tab 4 */ \ + { WWT_TAB, 1, 127, TabWidth + 127, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_THOUGHTS_TIP }, /* Tab 5 */ \ + { WWT_TAB, 1, 158, TabWidth + 158, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_ITEMS_TIP }, /* Tab 6 */ \ + { WWT_TAB, 1, 189, TabWidth + 189, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_DEBUG_TIP } /* Tab 7 */ + static rct_widget window_guest_overview_widgets[] = { - {WWT_FRAME, 0, 0, 191, 0, 156, 0xFFFFFFFF, STR_NONE}, // Panel / Background - {WWT_CAPTION, 0, 1, 190, 1, 14, STR_STRINGID, STR_WINDOW_TITLE_TIP}, // Title - {WWT_CLOSEBOX, 0, 179, 189, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP}, // Close x button - {WWT_RESIZE, 1, 0, 191, 43, 156, 0xFFFFFFFF, STR_NONE}, // Resize - {WWT_TAB, 1, 3, 33, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VIEW_TIP}, // Tab 1 - {WWT_TAB, 1, 73, 64, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_NEEDS_TIP}, // Tab 2 - {WWT_TAB, 1, 65, 95, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VISITED_RIDES_TIP}, // Tab 3 - {WWT_TAB, 1, 96, 126, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_FINANCE_TIP}, // Tab 4 - {WWT_TAB, 1, 127, 157, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_THOUGHTS_TIP}, // Tab 5 - {WWT_TAB, 1, 158, 188, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_ITEMS_TIP}, // Tab 6 - {WWT_LABEL_CENTRED, 1, 3, 166, 45, 56, 0xFFFFFFFF, STR_NONE}, // Label Thought marquee - {WWT_VIEWPORT, 1, 3, 166, 57, 143, 0xFFFFFFFF, STR_NONE}, // Viewport - {WWT_LABEL_CENTRED, 1, 3, 166, 144, 154, 0xFFFFFFFF, STR_NONE}, // Label Action - {WWT_FLATBTN, 1, 167, 190, 45, 68, SPR_PICKUP_BTN, STR_PICKUP_TIP}, // Pickup Button - {WWT_FLATBTN, 1, 167, 190, 69, 92, SPR_RENAME, STR_NAME_GUEST_TIP}, // Rename Button - {WWT_FLATBTN, 1, 167, 190, 93, 116, SPR_LOCATE, STR_LOCATE_SUBJECT_TIP}, // Locate Button - {WWT_FLATBTN, 1, 167, 190, 117, 140, SPR_TRACK_PEEP, STR_TOGGLE_GUEST_TRACKING_TIP}, // Track Button + MAIN_GUEST_WIDGETS, + { WWT_LABEL_CENTRED, 1, 3, 166, 45, 56, 0xFFFFFFFF, STR_NONE }, // Label Thought marquee + { WWT_VIEWPORT, 1, 3, 166, 57, 143, 0xFFFFFFFF, STR_NONE }, // Viewport + { WWT_LABEL_CENTRED, 1, 3, 166, 144, 154, 0xFFFFFFFF, STR_NONE }, // Label Action + { WWT_FLATBTN, 1, 167, 190, 45, 68, SPR_PICKUP_BTN, STR_PICKUP_TIP }, // Pickup Button + { WWT_FLATBTN, 1, 167, 190, 69, 92, SPR_RENAME, STR_NAME_GUEST_TIP }, // Rename Button + { WWT_FLATBTN, 1, 167, 190, 93, 116, SPR_LOCATE, STR_LOCATE_SUBJECT_TIP }, // Locate Button + { WWT_FLATBTN, 1, 167, 190, 117, 140, SPR_TRACK_PEEP, STR_TOGGLE_GUEST_TRACKING_TIP }, // Track Button { WIDGETS_END }, }; static rct_widget window_guest_stats_widgets[] = { - {WWT_FRAME, 0, 0, 191, 0, 156, STR_NONE, STR_NONE}, - {WWT_CAPTION, 0, 1, 190, 1, 14, STR_STRINGID, STR_WINDOW_TITLE_TIP}, - {WWT_CLOSEBOX, 0, 179, 189, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP}, - {WWT_RESIZE, 1, 0, 191, 43, 156, STR_NONE, STR_NONE}, - {WWT_TAB, 1, 3, 33, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VIEW_TIP}, - {WWT_TAB, 1, 34, 64, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_NEEDS_TIP}, - {WWT_TAB, 1, 65, 95, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VISITED_RIDES_TIP}, - {WWT_TAB, 1, 96, 126, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_FINANCE_TIP}, - {WWT_TAB, 1, 127, 157, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_THOUGHTS_TIP}, - {WWT_TAB, 1, 158, 188, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_ITEMS_TIP}, - {WIDGETS_END}, + MAIN_GUEST_WIDGETS, + { WIDGETS_END }, }; static rct_widget window_guest_rides_widgets[] = { - {WWT_FRAME, 0, 0, 191, 0, 156, STR_NONE, STR_NONE}, - {WWT_CAPTION, 0, 1, 190, 1, 14, STR_STRINGID, STR_WINDOW_TITLE_TIP}, - {WWT_CLOSEBOX, 0, 179, 189, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP}, - {WWT_RESIZE, 1, 0, 191, 43, 156, STR_NONE, STR_NONE}, - {WWT_TAB, 1, 3, 33, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VIEW_TIP}, - {WWT_TAB, 1, 34, 64, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_NEEDS_TIP}, - {WWT_TAB, 1, 65, 95, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VISITED_RIDES_TIP}, - {WWT_TAB, 1, 96, 126, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_FINANCE_TIP}, - {WWT_TAB, 1, 127, 157, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_THOUGHTS_TIP}, - {WWT_TAB, 1, 158, 188, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_ITEMS_TIP}, - {WWT_SCROLL, 1, 3, 188, 57, 143, SCROLL_VERTICAL, STR_NONE}, - {WIDGETS_END}, + MAIN_GUEST_WIDGETS, + { WWT_SCROLL, 1, 3, 188, 57, 143, SCROLL_VERTICAL, STR_NONE }, + { WIDGETS_END }, }; static rct_widget window_guest_finance_widgets[] = { - {WWT_FRAME, 0, 0, 191, 0, 156, STR_NONE, STR_NONE}, - {WWT_CAPTION, 0, 1, 190, 1, 14, STR_STRINGID, STR_WINDOW_TITLE_TIP}, - {WWT_CLOSEBOX, 0, 179, 189, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP}, - {WWT_RESIZE, 1, 0, 191, 43, 156, STR_NONE, STR_NONE}, - {WWT_TAB, 1, 3, 33, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VIEW_TIP}, - {WWT_TAB, 1, 34, 64, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_NEEDS_TIP}, - {WWT_TAB, 1, 65, 95, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VISITED_RIDES_TIP}, - {WWT_TAB, 1, 96, 126, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_FINANCE_TIP}, - {WWT_TAB, 1, 127, 157, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_THOUGHTS_TIP}, - {WWT_TAB, 1, 158, 188, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_ITEMS_TIP}, - {WIDGETS_END}, + MAIN_GUEST_WIDGETS, + { WIDGETS_END }, }; static rct_widget window_guest_thoughts_widgets[] = { - {WWT_FRAME, 0, 0, 191, 0, 156, STR_NONE, STR_NONE}, - {WWT_CAPTION, 0, 1, 190, 1, 14, STR_STRINGID, STR_WINDOW_TITLE_TIP}, - {WWT_CLOSEBOX, 0, 179, 189, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP}, - {WWT_RESIZE, 1, 0, 191, 43, 156, STR_NONE, STR_NONE}, - {WWT_TAB, 1, 3, 33, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VIEW_TIP}, - {WWT_TAB, 1, 34, 64, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_NEEDS_TIP}, - {WWT_TAB, 1, 65, 95, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VISITED_RIDES_TIP}, - {WWT_TAB, 1, 96, 126, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_FINANCE_TIP}, - {WWT_TAB, 1, 127, 157, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_THOUGHTS_TIP}, - {WWT_TAB, 1, 158, 188, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_ITEMS_TIP}, - {WIDGETS_END}, + MAIN_GUEST_WIDGETS, + { WIDGETS_END }, }; static rct_widget window_guest_inventory_widgets[] = { - {WWT_FRAME, 0, 0, 191, 0, 156, STR_NONE, STR_NONE}, - {WWT_CAPTION, 0, 1, 190, 1, 14, STR_STRINGID, STR_WINDOW_TITLE_TIP}, - {WWT_CLOSEBOX, 0, 179, 189, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP}, - {WWT_RESIZE, 1, 0, 191, 43, 156, STR_NONE, STR_NONE}, - {WWT_TAB, 1, 3, 33, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VIEW_TIP}, - {WWT_TAB, 1, 34, 64, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_NEEDS_TIP}, - {WWT_TAB, 1, 65, 95, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_VISITED_RIDES_TIP}, - {WWT_TAB, 1, 96, 126, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_FINANCE_TIP}, - {WWT_TAB, 1, 127, 157, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_THOUGHTS_TIP}, - {WWT_TAB, 1, 158, 188, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SHOW_GUEST_ITEMS_TIP}, - {WIDGETS_END}, + MAIN_GUEST_WIDGETS, + { WIDGETS_END }, +}; + +static rct_widget window_guest_debug_widgets[] = { + MAIN_GUEST_WIDGETS, + { WIDGETS_END }, }; // 0x981D0C @@ -163,12 +133,15 @@ static rct_widget *window_guest_page_widgets[] = { window_guest_rides_widgets, window_guest_finance_widgets, window_guest_thoughts_widgets, - window_guest_inventory_widgets + window_guest_inventory_widgets, + window_guest_debug_widgets }; static void window_guest_set_page(rct_window* w, int32_t page); static void window_guest_disable_widgets(rct_window* w); static void window_guest_viewport_init(rct_window* w); +static void window_guest_common_resize(rct_window* w); +static void window_guest_common_invalidate(rct_window* w); static void window_guest_overview_close(rct_window *w); static void window_guest_overview_resize(rct_window *w); @@ -183,14 +156,10 @@ static void window_guest_overview_tool_down(rct_window* w, rct_widgetindex widge static void window_guest_overview_tool_abort(rct_window *w, rct_widgetindex widgetIndex); static void window_guest_mouse_up(rct_window *w, rct_widgetindex widgetIndex); -static void window_guest_unknown_05(rct_window *w); -static void window_guest_stats_resize(rct_window *w); static void window_guest_stats_update(rct_window *w); -static void window_guest_stats_invalidate(rct_window *w); static void window_guest_stats_paint(rct_window *w, rct_drawpixelinfo *dpi); -static void window_guest_rides_resize(rct_window *w); static void window_guest_rides_update(rct_window *w); static void window_guest_rides_scroll_get_size(rct_window *w, int32_t scrollIndex, int32_t *width, int32_t *height); static void window_guest_rides_scroll_mouse_down(rct_window *w, int32_t scrollIndex, int32_t x, int32_t y); @@ -199,21 +168,18 @@ static void window_guest_rides_invalidate(rct_window *w); static void window_guest_rides_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_guest_rides_scroll_paint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex); -static void window_guest_finance_resize(rct_window *w); static void window_guest_finance_update(rct_window *w); -static void window_guest_finance_invalidate(rct_window *w); static void window_guest_finance_paint(rct_window *w, rct_drawpixelinfo *dpi); -static void window_guest_thoughts_resize(rct_window *w); static void window_guest_thoughts_update(rct_window *w); -static void window_guest_thoughts_invalidate(rct_window *w); static void window_guest_thoughts_paint(rct_window *w, rct_drawpixelinfo *dpi); -static void window_guest_inventory_resize(rct_window *w); static void window_guest_inventory_update(rct_window *w); -static void window_guest_inventory_invalidate(rct_window *w); static void window_guest_inventory_paint(rct_window *w, rct_drawpixelinfo *dpi); +static void window_guest_debug_update(rct_window *w); +static void window_guest_debug_paint(rct_window *w, rct_drawpixelinfo* dpi); + static rct_window_event_list window_guest_overview_events = { window_guest_overview_close, window_guest_overview_mouse_up, @@ -248,10 +214,10 @@ static rct_window_event_list window_guest_overview_events = { static rct_window_event_list window_guest_stats_events = { nullptr, window_guest_mouse_up, - window_guest_stats_resize, + window_guest_common_resize, + nullptr, nullptr, nullptr, - window_guest_unknown_05, window_guest_stats_update, nullptr, nullptr, @@ -271,7 +237,7 @@ static rct_window_event_list window_guest_stats_events = { nullptr, nullptr, nullptr, - window_guest_stats_invalidate, + window_guest_common_invalidate, window_guest_stats_paint, nullptr }; @@ -279,10 +245,10 @@ static rct_window_event_list window_guest_stats_events = { static rct_window_event_list window_guest_rides_events = { nullptr, window_guest_mouse_up, - window_guest_rides_resize, + window_guest_common_resize, + nullptr, nullptr, nullptr, - window_guest_unknown_05, window_guest_rides_update, nullptr, nullptr, @@ -310,10 +276,10 @@ static rct_window_event_list window_guest_rides_events = { static rct_window_event_list window_guest_finance_events = { nullptr, window_guest_mouse_up, - window_guest_finance_resize, + window_guest_common_resize, + nullptr, nullptr, nullptr, - window_guest_unknown_05, window_guest_finance_update, nullptr, nullptr, @@ -333,7 +299,7 @@ static rct_window_event_list window_guest_finance_events = { nullptr, nullptr, nullptr, - window_guest_finance_invalidate, + window_guest_common_invalidate, window_guest_finance_paint, nullptr }; @@ -341,10 +307,10 @@ static rct_window_event_list window_guest_finance_events = { static rct_window_event_list window_guest_thoughts_events = { nullptr, window_guest_mouse_up, - window_guest_thoughts_resize, + window_guest_common_resize, + nullptr, nullptr, nullptr, - window_guest_unknown_05, window_guest_thoughts_update, nullptr, nullptr, @@ -364,7 +330,7 @@ static rct_window_event_list window_guest_thoughts_events = { nullptr, nullptr, nullptr, - window_guest_thoughts_invalidate, + window_guest_common_invalidate, window_guest_thoughts_paint, nullptr }; @@ -372,10 +338,10 @@ static rct_window_event_list window_guest_thoughts_events = { static rct_window_event_list window_guest_inventory_events = { nullptr, window_guest_mouse_up, - window_guest_inventory_resize, + window_guest_common_resize, + nullptr, nullptr, nullptr, - window_guest_unknown_05, window_guest_inventory_update, nullptr, nullptr, @@ -395,11 +361,42 @@ static rct_window_event_list window_guest_inventory_events = { nullptr, nullptr, nullptr, - window_guest_inventory_invalidate, + window_guest_common_invalidate, window_guest_inventory_paint, nullptr }; +static rct_window_event_list window_guest_debug_events = { + nullptr, + window_guest_mouse_up, + window_guest_common_resize, + nullptr, + nullptr, + nullptr, + window_guest_debug_update, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + window_guest_common_invalidate, + window_guest_debug_paint, + nullptr +}; + // 0x981D24 static rct_window_event_list *window_guest_page_events[] = { &window_guest_overview_events, @@ -407,7 +404,8 @@ static rct_window_event_list *window_guest_page_events[] = { &window_guest_rides_events, &window_guest_finance_events, &window_guest_thoughts_events, - &window_guest_inventory_events + &window_guest_inventory_events, + &window_guest_debug_events }; void window_guest_set_colours(); @@ -421,6 +419,7 @@ static constexpr const uint32_t window_guest_page_enabled_widgets[] = { (1 << WIDX_TAB_4) | (1 << WIDX_TAB_5) | (1 << WIDX_TAB_6) | + (1 << WIDX_TAB_7) | (1 << WIDX_RENAME)| (1 << WIDX_PICKUP)| (1 << WIDX_LOCATE)| @@ -432,7 +431,8 @@ static constexpr const uint32_t window_guest_page_enabled_widgets[] = { (1 << WIDX_TAB_3) | (1 << WIDX_TAB_4) | (1 << WIDX_TAB_5) | - (1 << WIDX_TAB_6), + (1 << WIDX_TAB_6) | + (1 << WIDX_TAB_7), (1 << WIDX_CLOSE) | (1 << WIDX_TAB_1) | @@ -441,6 +441,7 @@ static constexpr const uint32_t window_guest_page_enabled_widgets[] = { (1 << WIDX_TAB_4) | (1 << WIDX_TAB_5) | (1 << WIDX_TAB_6) | + (1 << WIDX_TAB_7) | (1 << WIDX_RIDE_SCROLL), (1 << WIDX_CLOSE) | @@ -449,7 +450,8 @@ static constexpr const uint32_t window_guest_page_enabled_widgets[] = { (1 << WIDX_TAB_3) | (1 << WIDX_TAB_4) | (1 << WIDX_TAB_5) | - (1 << WIDX_TAB_6), + (1 << WIDX_TAB_6) | + (1 << WIDX_TAB_7), (1 << WIDX_CLOSE) | (1 << WIDX_TAB_1) | @@ -457,7 +459,8 @@ static constexpr const uint32_t window_guest_page_enabled_widgets[] = { (1 << WIDX_TAB_3) | (1 << WIDX_TAB_4) | (1 << WIDX_TAB_5) | - (1 << WIDX_TAB_6), + (1 << WIDX_TAB_6) | + (1 << WIDX_TAB_7), (1 << WIDX_CLOSE) | (1 << WIDX_TAB_1) | @@ -465,7 +468,27 @@ static constexpr const uint32_t window_guest_page_enabled_widgets[] = { (1 << WIDX_TAB_3) | (1 << WIDX_TAB_4) | (1 << WIDX_TAB_5) | - (1 << WIDX_TAB_6) + (1 << WIDX_TAB_6) | + (1 << WIDX_TAB_7), + + (1 << WIDX_CLOSE) | + (1 << WIDX_TAB_1) | + (1 << WIDX_TAB_2) | + (1 << WIDX_TAB_3) | + (1 << WIDX_TAB_4) | + (1 << WIDX_TAB_5) | + (1 << WIDX_TAB_6) | + (1 << WIDX_TAB_7) +}; + +static constexpr const rct_size16 window_guest_page_sizes[][2] = { + { 192, 159, 500, 450 }, // WINDOW_GUEST_OVERVIEW + { 192, 180, 192, 180 }, // WINDOW_GUEST_STATS + { 192, 180, 500, 400 }, // WINDOW_GUEST_RIDES + { 210, 148, 210, 148 }, // WINDOW_GUEST_FINANCE + { 192, 159, 500, 450 }, // WINDOW_GUEST_THOUGHTS + { 192, 159, 500, 450 }, // WINDOW_GUEST_INVENTORY + { 192, 159, 192, 171 } // WINDOW_GUEST_DEBUG }; // clang-format on @@ -486,7 +509,11 @@ rct_window* window_guest_open(Peep* peep) window = window_bring_to_front_by_number(WC_PEEP, peep->sprite_index); if (window == nullptr) { - window = window_create_auto_pos(192, 157, &window_guest_overview_events, WC_PEEP, WF_RESIZABLE); + int32_t windowWidth = 192; + if (gConfigGeneral.debugging_tools) + windowWidth += TabWidth; + + window = window_create_auto_pos(windowWidth, 157, &window_guest_overview_events, WC_PEEP, WF_RESIZABLE); window->widgets = window_guest_overview_widgets; window->enabled_widgets = window_guest_page_enabled_widgets[0]; window->number = peep->sprite_index; @@ -497,7 +524,7 @@ rct_window* window_guest_open(Peep* peep) window->picked_peep_frame = 0; window->highlighted_item = 0; window_guest_disable_widgets(window); - window->min_width = 192; + window->min_width = windowWidth; window->min_height = 157; window->max_width = 500; window->max_height = 450; @@ -508,7 +535,7 @@ rct_window* window_guest_open(Peep* peep) } window->page = 0; - window_invalidate(window); + window->Invalidate(); window->widgets = window_guest_page_widgets[WINDOW_GUEST_OVERVIEW]; window->enabled_widgets = window_guest_page_enabled_widgets[WINDOW_GUEST_OVERVIEW]; @@ -523,6 +550,51 @@ rct_window* window_guest_open(Peep* peep) return window; } +static void window_guest_common_resize(rct_window* w) +{ + // Get page specific min and max size + int32_t minWidth = window_guest_page_sizes[w->page][0].width; + int32_t minHeight = window_guest_page_sizes[w->page][0].height; + int32_t maxWidth = window_guest_page_sizes[w->page][1].width; + int32_t maxHeight = window_guest_page_sizes[w->page][1].height; + + // Ensure min size is large enough for all tabs to fit + for (int32_t i = WIDX_TAB_1; i <= WIDX_TAB_7; i++) + { + if (!(w->disabled_widgets & (1ULL << i))) + { + minWidth = std::max(minWidth, w->widgets[i].right + 3); + } + } + maxWidth = std::max(minWidth, maxWidth); + + window_set_resize(w, minWidth, minHeight, maxWidth, maxHeight); +} + +static void window_guest_common_invalidate(rct_window* w) +{ + if (window_guest_page_widgets[w->page] != w->widgets) + { + w->widgets = window_guest_page_widgets[w->page]; + window_init_scroll_widgets(w); + } + + w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1); + + auto peep = GET_PEEP(w->number); + peep->FormatNameTo(gCommonFormatArgs); + + w->widgets[WIDX_BACKGROUND].right = w->width - 1; + w->widgets[WIDX_BACKGROUND].bottom = w->height - 1; + w->widgets[WIDX_PAGE_BACKGROUND].right = w->width - 1; + w->widgets[WIDX_PAGE_BACKGROUND].bottom = w->height - 1; + w->widgets[WIDX_TITLE].right = w->width - 2; + w->widgets[WIDX_CLOSE].left = w->width - 13; + w->widgets[WIDX_CLOSE].right = w->width - 3; + + window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_7); +} + /** * Disables the finance tab when no money. * Disables peep pickup when in certain no pickup states. @@ -536,18 +608,22 @@ void window_guest_disable_widgets(rct_window* w) if (peep_can_be_picked_up(peep)) { if (w->disabled_widgets & (1 << WIDX_PICKUP)) - window_invalidate(w); + w->Invalidate(); } else { disabled_widgets = (1 << WIDX_PICKUP); if (!(w->disabled_widgets & (1 << WIDX_PICKUP))) - window_invalidate(w); + w->Invalidate(); } if (gParkFlags & PARK_FLAGS_NO_MONEY) { disabled_widgets |= (1 << WIDX_TAB_4); // Disable finance tab if no money } + if (!gConfigGeneral.debugging_tools) + { + disabled_widgets |= (1 << WIDX_TAB_7); // Disable debug tab when debug tools not turned on + } w->disabled_widgets = disabled_widgets; } @@ -575,25 +651,21 @@ void window_guest_overview_resize(rct_window* w) widget_invalidate(w, WIDX_MARQUEE); - window_set_resize(w, 192, 159, 500, 450); + window_guest_common_resize(w); - rct_viewport* view = w->viewport; - - if (view) + auto viewport = w->viewport; + if (viewport != nullptr) { - if ((w->width - 30) == view->width) + auto reqViewportWidth = w->width - 30; + auto reqViewportHeight = w->height - 72; + if (viewport->width != reqViewportWidth || viewport->height != reqViewportHeight) { - if ((w->height - 72) == view->height) - { - window_guest_viewport_init(w); - return; - } + uint8_t zoom_amount = 1 << viewport->zoom; + viewport->width = reqViewportWidth; + viewport->height = reqViewportHeight; + viewport->view_width = viewport->width / zoom_amount; + viewport->view_height = viewport->height / zoom_amount; } - uint8_t zoom_amount = 1 << view->zoom; - view->width = w->width - 30; - view->height = w->height - 72; - view->view_width = view->width / zoom_amount; - view->view_height = view->height / zoom_amount; } window_guest_viewport_init(w); } @@ -617,27 +689,46 @@ void window_guest_overview_mouse_up(rct_window* w, rct_widgetindex widgetIndex) case WIDX_TAB_4: case WIDX_TAB_5: case WIDX_TAB_6: + case WIDX_TAB_7: window_guest_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_PICKUP: + { if (!peep_can_be_picked_up(peep)) { return; } w->picked_peep_old_x = peep->x; - game_command_callback = game_command_callback_pickup_guest; - game_do_command(w->number, GAME_COMMAND_FLAG_APPLY, 0, 0, GAME_COMMAND_PICKUP_GUEST, 0, 0); - break; + PeepPickupAction pickupAction{ PeepPickupType::Pickup, w->number, {}, network_get_current_player_id() }; + pickupAction.SetCallback([peepnum = w->number](const GameAction* ga, const GameActionResult* result) { + if (result->Error != GA_ERROR::OK) + return; + rct_window* wind = window_find_by_number(WC_PEEP, peepnum); + if (wind) + { + tool_set(wind, WC_PEEP__WIDX_PICKUP, TOOL_PICKER); + } + }); + GameActions::Execute(&pickupAction); + } + break; case WIDX_RENAME: - window_text_input_open( - w, widgetIndex, STR_GUEST_RENAME_TITLE, STR_GUEST_RENAME_PROMPT, peep->name_string_idx, peep->id, 32); + { + auto peepName = peep->GetName(); + window_text_input_raw_open(w, widgetIndex, STR_GUEST_RENAME_TITLE, STR_GUEST_RENAME_PROMPT, peepName.c_str(), 32); break; + } case WIDX_LOCATE: - window_scroll_to_viewport(w); + w->ScrollToViewport(); break; case WIDX_TRACK: - get_sprite(w->number)->peep.peep_flags ^= PEEP_FLAGS_TRACKING; - break; + { + uint32_t flags = peep->peep_flags ^ PEEP_FLAGS_TRACKING; + + auto guestSetFlagsAction = GuestSetFlagsAction(w->number, flags); + GameActions::Execute(&guestSetFlagsAction); + } + break; } } @@ -677,11 +768,11 @@ void window_guest_set_page(rct_window* w, int32_t page) w->pressed_widgets = 0; w->widgets = window_guest_page_widgets[page]; window_guest_disable_widgets(w); - window_invalidate(w); + w->Invalidate(); window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); if (listen && w->viewport) w->viewport->flags |= VIEWPORT_FLAG_SOUND_ON; @@ -746,9 +837,9 @@ void window_guest_viewport_init(rct_window* w) w->viewport->flags = origViewportFlags; } w->flags |= WF_NO_SCROLLING; - window_invalidate(w); + w->Invalidate(); } - window_invalidate(w); + w->Invalidate(); } } @@ -945,6 +1036,24 @@ static void window_guest_inventory_tab_paint(rct_window* w, rct_drawpixelinfo* d gfx_draw_sprite(dpi, image_id, x, y, 0); } +static void window_guest_debug_tab_paint(rct_window* w, rct_drawpixelinfo* dpi) +{ + if (w->disabled_widgets & (1 << WIDX_TAB_7)) + return; + + rct_widget* widget = &w->widgets[WIDX_TAB_7]; + int32_t x = widget->left + w->x; + int32_t y = widget->top + w->y; + + int32_t image_id = SPR_TAB_GEARS_0; + if (w->page == WINDOW_GUEST_DEBUG) + { + image_id += (w->frame_no / 2) & 0x3; + } + + gfx_draw_sprite(dpi, image_id, x, y, 0); +} + /** * * rct2: 0x696887 @@ -958,6 +1067,7 @@ void window_guest_overview_paint(rct_window* w, rct_drawpixelinfo* dpi) window_guest_finance_tab_paint(w, dpi); window_guest_thoughts_tab_paint(w, dpi); window_guest_inventory_tab_paint(w, dpi); + window_guest_debug_tab_paint(w, dpi); // Draw the viewport no sound sprite if (w->viewport) @@ -971,11 +1081,8 @@ void window_guest_overview_paint(rct_window* w, rct_drawpixelinfo* dpi) } // Draw the centred label - uint32_t argument1, argument2; Peep* peep = GET_PEEP(w->number); - get_arguments_from_action(peep, &argument1, &argument2); - set_format_arg(0, uint32_t, argument1); - set_format_arg(4, uint32_t, argument2); + peep->FormatActionTo(gCommonFormatArgs); rct_widget* widget = &w->widgets[WIDX_ACTION_LBL]; int32_t x = (widget->left + widget->right) / 2 + w->x; int32_t y = w->y + widget->top - 1; @@ -1024,38 +1131,15 @@ void window_guest_overview_paint(rct_window* w, rct_drawpixelinfo* dpi) */ void window_guest_overview_invalidate(rct_window* w) { - if (window_guest_page_widgets[w->page] != w->widgets) - { - w->widgets = window_guest_page_widgets[w->page]; - window_init_scroll_widgets(w); - } - - w->pressed_widgets &= ~( - (1ULL << WIDX_TAB_1) | (1ULL << WIDX_TAB_2) | (1ULL << WIDX_TAB_3) | (1ULL << WIDX_TAB_4) | (1ULL << WIDX_TAB_5) - | (1ULL << WIDX_TAB_6)); - w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1); - - Peep* peep = GET_PEEP(w->number); - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); + window_guest_common_invalidate(w); + auto peep = GET_PEEP(w->number); w->pressed_widgets &= ~(1 << WIDX_TRACK); if (peep->peep_flags & PEEP_FLAGS_TRACKING) { w->pressed_widgets |= (1 << WIDX_TRACK); } - window_guest_overview_widgets[WIDX_BACKGROUND].right = w->width - 1; - window_guest_overview_widgets[WIDX_BACKGROUND].bottom = w->height - 1; - - window_guest_overview_widgets[WIDX_PAGE_BACKGROUND].right = w->width - 1; - window_guest_overview_widgets[WIDX_PAGE_BACKGROUND].bottom = w->height - 1; - - window_guest_overview_widgets[WIDX_TITLE].right = w->width - 2; - - window_guest_overview_widgets[WIDX_CLOSE].left = w->width - 13; - window_guest_overview_widgets[WIDX_CLOSE].right = w->width - 3; - window_guest_overview_widgets[WIDX_VIEWPORT].right = w->width - 26; window_guest_overview_widgets[WIDX_VIEWPORT].bottom = w->height - 14; @@ -1074,8 +1158,6 @@ void window_guest_overview_invalidate(rct_window* w) window_guest_overview_widgets[WIDX_RENAME].left = w->width - 25; window_guest_overview_widgets[WIDX_LOCATE].left = w->width - 25; window_guest_overview_widgets[WIDX_TRACK].left = w->width - 25; - - window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_6); } /** @@ -1092,6 +1174,13 @@ void window_guest_overview_update(rct_window* w) widget_invalidate(w, WIDX_TAB_1); widget_invalidate(w, WIDX_TAB_2); + Peep* peep = GET_PEEP(w->number); + if (peep != nullptr && peep->window_invalidate_flags & PEEP_INVALIDATE_PEEP_ACTION) + { + peep->window_invalidate_flags &= ~PEEP_INVALIDATE_PEEP_ACTION; + widget_invalidate(w, WIDX_ACTION_LBL); + } + w->list_information_type += 2; if ((w->highlighted_item & 0xFFFF) == 0xFFFF) @@ -1110,8 +1199,7 @@ void window_guest_overview_update(rct_window* w) int32_t random = util_rand() & 0xFFFF; if (random <= 0x2AAA) { - Peep* peep = GET_PEEP(w->number); - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_WATCHED, PEEP_THOUGHT_ITEM_NONE); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_WATCHED, PEEP_THOUGHT_ITEM_NONE); } } } @@ -1198,8 +1286,16 @@ void window_guest_overview_tool_down(rct_window* w, rct_widgetindex widgetIndex, if (dest_x == LOCATION_NULL) return; - game_command_callback = game_command_callback_pickup_guest; - game_do_command(w->number, GAME_COMMAND_FLAG_APPLY, 2, tileElement->base_height, GAME_COMMAND_PICKUP_GUEST, dest_x, dest_y); + PeepPickupAction pickupAction{ + PeepPickupType::Place, w->number, { dest_x, dest_y, tileElement->base_height }, network_get_current_player_id() + }; + pickupAction.SetCallback([](const GameAction* ga, const GameActionResult* result) { + if (result->Error != GA_ERROR::OK) + return; + tool_cancel(); + gPickupPeepImage = UINT32_MAX; + }); + GameActions::Execute(&pickupAction); } /** @@ -1211,7 +1307,10 @@ void window_guest_overview_tool_abort(rct_window* w, rct_widgetindex widgetIndex if (widgetIndex != WIDX_PICKUP) return; - game_do_command(w->number, GAME_COMMAND_FLAG_APPLY, 1, 0, GAME_COMMAND_PICKUP_GUEST, w->picked_peep_old_x, 0); + PeepPickupAction pickupAction{ + PeepPickupType::Cancel, w->number, { w->picked_peep_old_x, 0, 0 }, network_get_current_player_id() + }; + GameActions::Execute(&pickupAction); } /** @@ -1231,29 +1330,12 @@ void window_guest_mouse_up(rct_window* w, rct_widgetindex widgetIndex) case WIDX_TAB_4: case WIDX_TAB_5: case WIDX_TAB_6: + case WIDX_TAB_7: window_guest_set_page(w, widgetIndex - WIDX_TAB_1); break; } } -/** - * - * rct2: 0x697488 - */ -void window_guest_stats_resize(rct_window* w) -{ - window_set_resize(w, 192, 180, 192, 180); -} - -/** - * This is a combination of 5 functions that were identical - * rct2: 0x6974ED, 0x00697959, 0x00697C7B, 0x00697ED2, 0x00698333 - */ -void window_guest_unknown_05(rct_window* w) -{ - widget_invalidate(w, WIDX_TAB_1); -} - /** * * rct2: 0x69746A @@ -1264,39 +1346,7 @@ void window_guest_stats_update(rct_window* w) Peep* peep = GET_PEEP(w->number); peep->window_invalidate_flags &= ~PEEP_INVALIDATE_PEEP_STATS; - window_invalidate(w); -} - -/** - * - * rct2: 0x69707D - */ -void window_guest_stats_invalidate(rct_window* w) -{ - if (w->widgets != window_guest_page_widgets[w->page]) - { - w->widgets = window_guest_page_widgets[w->page]; - window_init_scroll_widgets(w); - } - - w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1); - - Peep* peep = GET_PEEP(w->number); - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); - - window_guest_stats_widgets[WIDX_BACKGROUND].right = w->width - 1; - window_guest_stats_widgets[WIDX_BACKGROUND].bottom = w->height - 1; - - window_guest_stats_widgets[WIDX_PAGE_BACKGROUND].right = w->width - 1; - window_guest_stats_widgets[WIDX_PAGE_BACKGROUND].bottom = w->height - 1; - - window_guest_stats_widgets[WIDX_TITLE].right = w->width - 2; - - window_guest_stats_widgets[WIDX_CLOSE].left = w->width - 13; - window_guest_stats_widgets[WIDX_CLOSE].right = w->width - 3; - - window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_6); + w->Invalidate(); } /** @@ -1343,6 +1393,7 @@ void window_guest_stats_paint(rct_window* w, rct_drawpixelinfo* dpi) window_guest_finance_tab_paint(w, dpi); window_guest_thoughts_tab_paint(w, dpi); window_guest_inventory_tab_paint(w, dpi); + window_guest_debug_tab_paint(w, dpi); // ebx Peep* peep = GET_PEEP(w->number); @@ -1509,15 +1560,6 @@ void window_guest_stats_paint(rct_window* w, rct_drawpixelinfo* dpi) gfx_draw_string_left(dpi, STR_GUEST_STAT_NAUSEA_TOLERANCE, gCommonFormatArgs, COLOUR_BLACK, x, y); } -/** - * - * rct2: 0x006978F4 - */ -void window_guest_rides_resize(rct_window* w) -{ - window_set_resize(w, 192, 128, 500, 400); -} - /** * * rct2: 0x6977B0 @@ -1529,34 +1571,31 @@ void window_guest_rides_update(rct_window* w) widget_invalidate(w, WIDX_TAB_2); widget_invalidate(w, WIDX_TAB_3); - Peep* peep = GET_PEEP(w->number); - - // Every 2048 ticks do a full window_invalidate - int32_t number_of_ticks = gScenarioTicks - peep->time_in_park; - if (!(number_of_ticks & 0x7FF)) - window_invalidate(w); - - uint8_t curr_list_position = 0; - for (ride_id_t ride_id = 0; ride_id < MAX_RIDES; ride_id++) + auto peep = GET_PEEP(w->number); + auto guest = peep->AsGuest(); + if (guest != nullptr) { - // Offset to the ride_id bit in peep_rides_been_on - uint8_t ride_id_bit = ride_id % 8; - uint8_t ride_id_offset = ride_id / 8; - if (peep->rides_been_on[ride_id_offset] & (1 << ride_id_bit)) + // Every 2048 ticks do a full window_invalidate + int32_t number_of_ticks = gScenarioTicks - peep->time_in_park; + if (!(number_of_ticks & 0x7FF)) + w->Invalidate(); + + uint8_t curr_list_position = 0; + for (const auto& ride : GetRideManager()) { - Ride* ride = get_ride(ride_id); - if (gRideClassifications[ride->type] == RIDE_CLASS_RIDE) + if (ride.IsRide() && guest->HasRidden(&ride)) { - w->list_item_positions[curr_list_position] = ride_id; + w->list_item_positions[curr_list_position] = ride.id; curr_list_position++; } } - } - // If there are new items - if (w->no_list_items != curr_list_position) - { - w->no_list_items = curr_list_position; - window_invalidate(w); + + // If there are new items + if (w->no_list_items != curr_list_position) + { + w->no_list_items = curr_list_position; + w->Invalidate(); + } } } @@ -1571,7 +1610,7 @@ void window_guest_rides_scroll_get_size(rct_window* w, int32_t scrollIndex, int3 if (w->selected_list_item != -1) { w->selected_list_item = -1; - window_invalidate(w); + w->Invalidate(); } int32_t visable_height = *height - window_guest_rides_widgets[WIDX_RIDE_SCROLL].bottom @@ -1583,7 +1622,7 @@ void window_guest_rides_scroll_get_size(rct_window* w, int32_t scrollIndex, int3 if (visable_height < w->scrolls[0].v_top) { w->scrolls[0].v_top = visable_height; - window_invalidate(w); + w->Invalidate(); } } @@ -1620,7 +1659,7 @@ void window_guest_rides_scroll_mouse_over(rct_window* w, int32_t scrollIndex, in return; w->selected_list_item = index; - window_invalidate(w); + w->Invalidate(); } /** @@ -1629,33 +1668,10 @@ void window_guest_rides_scroll_mouse_over(rct_window* w, int32_t scrollIndex, in */ void window_guest_rides_invalidate(rct_window* w) { - if (window_guest_page_widgets[w->page] != w->widgets) - { - w->widgets = window_guest_page_widgets[w->page]; - window_init_scroll_widgets(w); - } - - w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1); - - Peep* peep = GET_PEEP(w->number); - set_format_arg(0, uint16_t, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); - - window_guest_rides_widgets[WIDX_BACKGROUND].right = w->width - 1; - window_guest_rides_widgets[WIDX_BACKGROUND].bottom = w->height - 1; - - window_guest_rides_widgets[WIDX_PAGE_BACKGROUND].right = w->width - 1; - window_guest_rides_widgets[WIDX_PAGE_BACKGROUND].bottom = w->height - 1; - - window_guest_rides_widgets[WIDX_TITLE].right = w->width - 2; - - window_guest_rides_widgets[WIDX_CLOSE].left = w->width - 13; - window_guest_rides_widgets[WIDX_CLOSE].right = w->width - 3; + window_guest_common_invalidate(w); window_guest_rides_widgets[WIDX_RIDE_SCROLL].right = w->width - 4; window_guest_rides_widgets[WIDX_RIDE_SCROLL].bottom = w->height - 15; - - window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_6); } /** @@ -1671,6 +1687,7 @@ void window_guest_rides_paint(rct_window* w, rct_drawpixelinfo* dpi) window_guest_finance_tab_paint(w, dpi); window_guest_thoughts_tab_paint(w, dpi); window_guest_inventory_tab_paint(w, dpi); + window_guest_debug_tab_paint(w, dpi); Peep* peep = GET_PEEP(w->number); @@ -1683,20 +1700,15 @@ void window_guest_rides_paint(rct_window* w, rct_drawpixelinfo* dpi) y = w->y + window_guest_rides_widgets[WIDX_PAGE_BACKGROUND].bottom - 12; - rct_string_id ride_string_id = STR_PEEP_FAVOURITE_RIDE_NOT_AVAILABLE; - uint32_t ride_string_arguments = 0; - if (peep->favourite_ride != 0xFF) + set_format_arg(0, rct_string_id, STR_PEEP_FAVOURITE_RIDE_NOT_AVAILABLE); + if (peep->favourite_ride != RIDE_ID_NULL) { auto ride = get_ride(peep->favourite_ride); if (ride != nullptr) { - ride_string_arguments = ride->name_arguments; - ride_string_id = ride->name; + ride->FormatNameTo(gCommonFormatArgs); } } - set_format_arg(0, rct_string_id, ride_string_id); - set_format_arg(2, uint32_t, ride_string_arguments); - gfx_draw_string_left_clipped(dpi, STR_FAVOURITE_RIDE, gCommonFormatArgs, COLOUR_BLACK, x, y, w->width - 14); } @@ -1727,22 +1739,12 @@ void window_guest_rides_scroll_paint(rct_window* w, rct_drawpixelinfo* dpi, int3 auto ride = get_ride(w->list_item_positions[list_index]); if (ride != nullptr) { - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs); gfx_draw_string_left(dpi, stringId, gCommonFormatArgs, COLOUR_BLACK, 0, y - 1); } } } -/** - * - * rct2: 0x00697C16 - */ -void window_guest_finance_resize(rct_window* w) -{ - window_set_resize(w, 210, 148, 210, 148); -} - /** * * rct2: 0x00697BF8 @@ -1755,39 +1757,6 @@ void window_guest_finance_update(rct_window* w) widget_invalidate(w, WIDX_TAB_4); } -/** - * - * rct2: 0x00697968 - */ -void window_guest_finance_invalidate(rct_window* w) -{ - if (window_guest_page_widgets[w->page] != w->widgets) - { - w->widgets = window_guest_page_widgets[w->page]; - window_init_scroll_widgets(w); - } - - w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1); - - Peep* peep = GET_PEEP(w->number); - - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); - - window_guest_finance_widgets[WIDX_BACKGROUND].right = w->width - 1; - window_guest_finance_widgets[WIDX_BACKGROUND].bottom = w->height - 1; - - window_guest_finance_widgets[WIDX_PAGE_BACKGROUND].right = w->width - 1; - window_guest_finance_widgets[WIDX_PAGE_BACKGROUND].bottom = w->height - 1; - - window_guest_finance_widgets[WIDX_TITLE].right = w->width - 2; - - window_guest_finance_widgets[WIDX_CLOSE].left = w->width - 13; - window_guest_finance_widgets[WIDX_CLOSE].right = w->width - 3; - - window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_6); -} - /** * * rct2: 0x00697A08 @@ -1801,6 +1770,7 @@ void window_guest_finance_paint(rct_window* w, rct_drawpixelinfo* dpi) window_guest_finance_tab_paint(w, dpi); window_guest_thoughts_tab_paint(w, dpi); window_guest_inventory_tab_paint(w, dpi); + window_guest_debug_tab_paint(w, dpi); Peep* peep = GET_PEEP(w->number); @@ -1878,22 +1848,6 @@ void window_guest_finance_paint(rct_window* w, rct_drawpixelinfo* dpi) } } -/** - * - * rct2: 0x00697E33 - */ -void window_guest_thoughts_resize(rct_window* w) -{ - Peep* peep = GET_PEEP(w->number); - if (peep->window_invalidate_flags & PEEP_INVALIDATE_PEEP_THOUGHTS) - { - peep->window_invalidate_flags &= ~PEEP_INVALIDATE_PEEP_THOUGHTS; - window_invalidate(w); - } - - window_set_resize(w, 192, 159, 500, 450); -} - /** * * rct2: 0x00697EB4 @@ -1904,39 +1858,13 @@ void window_guest_thoughts_update(rct_window* w) widget_invalidate(w, WIDX_TAB_2); widget_invalidate(w, WIDX_TAB_5); -} -/** - * - * rct2: 0x00697C8A - */ -void window_guest_thoughts_invalidate(rct_window* w) -{ - if (window_guest_page_widgets[w->page] != w->widgets) + auto peep = GET_PEEP(w->number); + if (peep->window_invalidate_flags & PEEP_INVALIDATE_PEEP_THOUGHTS) { - w->widgets = window_guest_page_widgets[w->page]; - window_init_scroll_widgets(w); + peep->window_invalidate_flags &= ~PEEP_INVALIDATE_PEEP_THOUGHTS; + w->Invalidate(); } - - w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1); - - Peep* peep = GET_PEEP(w->number); - - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); - - window_guest_thoughts_widgets[WIDX_BACKGROUND].right = w->width - 1; - window_guest_thoughts_widgets[WIDX_BACKGROUND].bottom = w->height - 1; - - window_guest_thoughts_widgets[WIDX_PAGE_BACKGROUND].right = w->width - 1; - window_guest_thoughts_widgets[WIDX_PAGE_BACKGROUND].bottom = w->height - 1; - - window_guest_thoughts_widgets[WIDX_TITLE].right = w->width - 2; - - window_guest_thoughts_widgets[WIDX_CLOSE].left = w->width - 13; - window_guest_thoughts_widgets[WIDX_CLOSE].right = w->width - 3; - - window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_6); } /** @@ -1952,6 +1880,7 @@ void window_guest_thoughts_paint(rct_window* w, rct_drawpixelinfo* dpi) window_guest_finance_tab_paint(w, dpi); window_guest_thoughts_tab_paint(w, dpi); window_guest_inventory_tab_paint(w, dpi); + window_guest_debug_tab_paint(w, dpi); Peep* peep = GET_PEEP(w->number); @@ -1982,22 +1911,6 @@ void window_guest_thoughts_paint(rct_window* w, rct_drawpixelinfo* dpi) } } -/** - * - * rct2: 0x00698294 - */ -void window_guest_inventory_resize(rct_window* w) -{ - Peep* peep = GET_PEEP(w->number); - if (peep->window_invalidate_flags & PEEP_INVALIDATE_PEEP_INVENTORY) - { - peep->window_invalidate_flags &= ~PEEP_INVALIDATE_PEEP_INVENTORY; - window_invalidate(w); - } - - window_set_resize(w, 192, 159, 500, 450); -} - /** * * rct2: 0x00698315 @@ -2008,110 +1921,88 @@ void window_guest_inventory_update(rct_window* w) widget_invalidate(w, WIDX_TAB_2); widget_invalidate(w, WIDX_TAB_6); -} -/** - * - * rct2: 0x00697EE1 - */ -void window_guest_inventory_invalidate(rct_window* w) -{ - if (window_guest_page_widgets[w->page] != w->widgets) + auto peep = GET_PEEP(w->number); + if (peep->window_invalidate_flags & PEEP_INVALIDATE_PEEP_INVENTORY) { - w->widgets = window_guest_page_widgets[w->page]; - window_init_scroll_widgets(w); + peep->window_invalidate_flags &= ~PEEP_INVALIDATE_PEEP_INVENTORY; + w->Invalidate(); } - - w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1); - - Peep* peep = GET_PEEP(w->number); - - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); - - window_guest_inventory_widgets[WIDX_BACKGROUND].right = w->width - 1; - window_guest_inventory_widgets[WIDX_BACKGROUND].bottom = w->height - 1; - - window_guest_inventory_widgets[WIDX_PAGE_BACKGROUND].right = w->width - 1; - window_guest_inventory_widgets[WIDX_PAGE_BACKGROUND].bottom = w->height - 1; - - window_guest_inventory_widgets[WIDX_TITLE].right = w->width - 2; - - window_guest_inventory_widgets[WIDX_CLOSE].left = w->width - 13; - window_guest_inventory_widgets[WIDX_CLOSE].right = w->width - 3; - - window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_6); } static rct_string_id window_guest_inventory_format_item(Peep* peep, int32_t item) { - Ride* ride; + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); // Default arguments - set_format_arg(0, uint32_t, ShopItemImage[item]); - set_format_arg(4, rct_string_id, ShopItemStringIds[item].display); - set_format_arg(6, rct_string_id, gParkName); - set_format_arg(8, uint32_t, gParkNameArgs); + set_format_arg(0, uint32_t, ShopItems[item].Image); + set_format_arg(4, rct_string_id, ShopItems[item].Naming.Display); + set_format_arg(6, rct_string_id, STR_STRING); + set_format_arg(8, const char*, parkName); // Special overrides + Ride* ride{}; switch (item) { case SHOP_ITEM_BALLOON: - set_format_arg(0, uint32_t, SPRITE_ID_PALETTE_COLOUR_1(peep->balloon_colour) | ShopItemImage[item]); + set_format_arg(0, uint32_t, SPRITE_ID_PALETTE_COLOUR_1(peep->balloon_colour) | ShopItems[item].Image); break; case SHOP_ITEM_PHOTO: ride = get_ride(peep->photo1_ride_ref); - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + if (ride != nullptr) + ride->FormatNameTo(gCommonFormatArgs + 6); break; case SHOP_ITEM_UMBRELLA: - set_format_arg(0, uint32_t, SPRITE_ID_PALETTE_COLOUR_1(peep->umbrella_colour) | ShopItemImage[item]); + set_format_arg(0, uint32_t, SPRITE_ID_PALETTE_COLOUR_1(peep->umbrella_colour) | ShopItems[item].Image); break; case SHOP_ITEM_VOUCHER: switch (peep->voucher_type) { case VOUCHER_TYPE_PARK_ENTRY_FREE: set_format_arg(6, rct_string_id, STR_PEEP_INVENTORY_VOUCHER_PARK_ENTRY_FREE); - set_format_arg(8, rct_string_id, gParkName); - set_format_arg(10, uint32_t, gParkNameArgs); + set_format_arg(8, rct_string_id, STR_STRING); + set_format_arg(10, const char*, parkName); break; case VOUCHER_TYPE_RIDE_FREE: ride = get_ride(peep->voucher_arguments); - set_format_arg(6, rct_string_id, STR_PEEP_INVENTORY_VOUCHER_RIDE_FREE); - set_format_arg(8, rct_string_id, ride->name); - set_format_arg(10, uint32_t, ride->name_arguments); + if (ride != nullptr) + { + set_format_arg(6, rct_string_id, STR_PEEP_INVENTORY_VOUCHER_RIDE_FREE); + ride->FormatNameTo(gCommonFormatArgs + 8); + } break; case VOUCHER_TYPE_PARK_ENTRY_HALF_PRICE: set_format_arg(6, rct_string_id, STR_PEEP_INVENTORY_VOUCHER_PARK_ENTRY_HALF_PRICE); - set_format_arg(8, rct_string_id, gParkName); - set_format_arg(10, uint32_t, gParkNameArgs); + set_format_arg(8, rct_string_id, STR_STRING); + set_format_arg(10, const char*, parkName); break; case VOUCHER_TYPE_FOOD_OR_DRINK_FREE: set_format_arg(6, rct_string_id, STR_PEEP_INVENTORY_VOUCHER_FOOD_OR_DRINK_FREE); - set_format_arg(8, rct_string_id, ShopItemStringIds[peep->voucher_arguments].singular); + set_format_arg(8, rct_string_id, ShopItems[peep->voucher_arguments].Naming.Singular); break; } break; case SHOP_ITEM_HAT: - set_format_arg(0, uint32_t, SPRITE_ID_PALETTE_COLOUR_1(peep->hat_colour) | ShopItemImage[item]); + set_format_arg(0, uint32_t, SPRITE_ID_PALETTE_COLOUR_1(peep->hat_colour) | ShopItems[item].Image); break; case SHOP_ITEM_TSHIRT: - set_format_arg(0, uint32_t, SPRITE_ID_PALETTE_COLOUR_1(peep->tshirt_colour) | ShopItemImage[item]); + set_format_arg(0, uint32_t, SPRITE_ID_PALETTE_COLOUR_1(peep->tshirt_colour) | ShopItems[item].Image); break; case SHOP_ITEM_PHOTO2: ride = get_ride(peep->photo2_ride_ref); - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + if (ride != nullptr) + ride->FormatNameTo(gCommonFormatArgs + 6); break; case SHOP_ITEM_PHOTO3: ride = get_ride(peep->photo3_ride_ref); - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + if (ride != nullptr) + ride->FormatNameTo(gCommonFormatArgs + 6); break; case SHOP_ITEM_PHOTO4: ride = get_ride(peep->photo4_ride_ref); - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + if (ride != nullptr) + ride->FormatNameTo(gCommonFormatArgs + 6); break; } @@ -2131,6 +2022,7 @@ void window_guest_inventory_paint(rct_window* w, rct_drawpixelinfo* dpi) window_guest_finance_tab_paint(w, dpi); window_guest_thoughts_tab_paint(w, dpi); window_guest_inventory_tab_paint(w, dpi); + window_guest_debug_tab_paint(w, dpi); const auto guest = (GET_PEEP(w->number))->AsGuest(); if (guest != nullptr) @@ -2164,3 +2056,80 @@ void window_guest_inventory_paint(rct_window* w, rct_drawpixelinfo* dpi) } } } + +/** + * + * rct2: 0x00698315 + */ +void window_guest_debug_update(rct_window* w) +{ + w->frame_no++; + w->Invalidate(); +} + +void window_guest_debug_paint(rct_window* w, rct_drawpixelinfo* dpi) +{ + char buffer[512]{}; + char buffer2[512]{}; + + window_draw_widgets(w, dpi); + window_guest_overview_tab_paint(w, dpi); + window_guest_stats_tab_paint(w, dpi); + window_guest_rides_tab_paint(w, dpi); + window_guest_finance_tab_paint(w, dpi); + window_guest_thoughts_tab_paint(w, dpi); + window_guest_inventory_tab_paint(w, dpi); + window_guest_debug_tab_paint(w, dpi); + + auto peep = GET_PEEP(w->number); + auto x = w->x + window_guest_debug_widgets[WIDX_PAGE_BACKGROUND].left + 4; + auto y = w->y + window_guest_debug_widgets[WIDX_PAGE_BACKGROUND].top + 4; + { + set_format_arg(0, uint32_t, peep->sprite_index); + gfx_draw_string_left(dpi, STR_PEEP_DEBUG_SPRITE_INDEX, gCommonFormatArgs, 0, x, y); + } + y += LIST_ROW_HEIGHT; + { + int32_t args[] = { peep->x, peep->y, peep->x }; + gfx_draw_string_left(dpi, STR_PEEP_DEBUG_POSITION, args, 0, x, y); + } + y += LIST_ROW_HEIGHT; + { + int32_t args[] = { peep->next_x, peep->next_y, peep->next_z }; + format_string(buffer, sizeof(buffer), STR_PEEP_DEBUG_NEXT, args); + if (peep->GetNextIsSurface()) + { + format_string(buffer2, sizeof(buffer2), STR_PEEP_DEBUG_NEXT_SURFACE, nullptr); + safe_strcat(buffer, buffer2, sizeof(buffer)); + } + if (peep->GetNextIsSloped()) + { + int32_t args2[1] = { peep->GetNextDirection() }; + format_string(buffer2, sizeof(buffer2), STR_PEEP_DEBUG_NEXT_SLOPE, args2); + safe_strcat(buffer, buffer2, sizeof(buffer)); + } + gfx_draw_string(dpi, buffer, 0, x, y); + } + y += LIST_ROW_HEIGHT; + { + int32_t args[] = { peep->destination_x, peep->destination_y, peep->destination_tolerance }; + gfx_draw_string_left(dpi, STR_PEEP_DEBUG_DEST, args, 0, x, y); + } + y += LIST_ROW_HEIGHT; + { + int32_t args[] = { peep->pathfind_goal.x, peep->pathfind_goal.y, peep->pathfind_goal.z, peep->pathfind_goal.direction }; + gfx_draw_string_left(dpi, STR_PEEP_DEBUG_PATHFIND_GOAL, args, 0, x, y); + } + y += LIST_ROW_HEIGHT; + gfx_draw_string_left(dpi, STR_PEEP_DEBUG_PATHFIND_HISTORY, nullptr, 0, x, y); + y += LIST_ROW_HEIGHT; + + x += 10; + for (auto& point : peep->pathfind_history) + { + int32_t args[] = { point.x, point.y, point.z, point.direction }; + gfx_draw_string_left(dpi, STR_PEEP_DEBUG_PATHFIND_HISTORY_ITEM, args, 0, x, y); + y += LIST_ROW_HEIGHT; + } + x -= 10; +} diff --git a/src/openrct2-ui/windows/GuestList.cpp b/src/openrct2-ui/windows/GuestList.cpp index 997091c225..23e4260b4e 100644 --- a/src/openrct2-ui/windows/GuestList.cpp +++ b/src/openrct2-ui/windows/GuestList.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -136,6 +136,26 @@ static rct_window_event_list window_guest_list_events = { }; // clang-format on +struct FilterArguments +{ + uint8_t args[12]{}; + + rct_string_id GetFirstStringId() + { + rct_string_id firstStrId{}; + std::memcpy(&firstStrId, args, sizeof(firstStrId)); + return firstStrId; + } +}; +static bool operator==(const FilterArguments& l, const FilterArguments& r) +{ + return std::memcmp(l.args, r.args, sizeof(l.args)) == 0; +} +static bool operator!=(const FilterArguments& l, const FilterArguments& r) +{ + return !(l == r); +} + static uint32_t _window_guest_list_last_find_groups_tick; static uint32_t _window_guest_list_last_find_groups_selected_view; static uint32_t _window_guest_list_last_find_groups_wait; @@ -148,11 +168,10 @@ static uint32_t _window_guest_list_selected_view; // 0x00F1EE13 static int32_t _window_guest_list_num_pages; // 0x00F1EE08 static int32_t _window_guest_list_num_groups; // 0x00F1AF22 static bool _window_guest_list_tracking_only; -static uint16_t _window_guest_list_filter_arguments[4]; +static FilterArguments _window_guest_list_filter_arguments; static uint16_t _window_guest_list_groups_num_guests[240]; -static uint32_t _window_guest_list_groups_argument_1[240]; -static uint32_t _window_guest_list_groups_argument_2[240]; +static FilterArguments _window_guest_list_groups_arguments[240]; static uint8_t _window_guest_list_groups_guest_faces[240 * 58]; static uint8_t _window_guest_list_group_index[240]; @@ -161,7 +180,7 @@ static char _window_guest_list_filter_name[32]; static int32_t window_guest_list_is_peep_in_filter(Peep* peep); static void window_guest_list_find_groups(); -static void get_arguments_from_peep(Peep* peep, uint32_t* argument_1, uint32_t* argument_2); +static FilterArguments get_arguments_from_peep(const Peep* peep); static bool guest_should_be_visible(Peep* peep); @@ -235,61 +254,60 @@ rct_window* window_guest_list_open_with_filter(int32_t type, int32_t index) _window_guest_list_selected_page = 0; _window_guest_list_num_pages = 1; _window_guest_list_tracking_only = false; + _window_guest_list_filter_arguments = {}; switch (type) { case GLFT_GUESTS_ON_RIDE: { - Ride* ride = get_ride(index & 0x000000FF); - _window_guest_list_filter_arguments[0] = STR_ON_RIDE; - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE)) + auto ride = get_ride(index & 0xFF); + if (ride != nullptr) { - _window_guest_list_filter_arguments[0] = STR_IN_RIDE; - } - _window_guest_list_filter_arguments[1] = ride->name; - _window_guest_list_filter_arguments[2] = ride->name_arguments_type_name; - _window_guest_list_filter_arguments[3] = ride->name_arguments_number; + set_format_arg_on( + _window_guest_list_filter_arguments.args, 0, rct_string_id, + ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE) ? STR_IN_RIDE : STR_ON_RIDE); + ride->FormatNameTo(_window_guest_list_filter_arguments.args + 2); - _window_guest_list_selected_filter = 0; - _window_guest_list_highlighted_index = 0xFFFF; - _window_guest_list_selected_tab = 0; - _window_guest_list_selected_view = 0; + _window_guest_list_selected_filter = 0; + _window_guest_list_highlighted_index = 0xFFFF; + _window_guest_list_selected_tab = 0; + _window_guest_list_selected_view = 0; + } break; } case GLFT_GUESTS_IN_QUEUE: { - Ride* ride = get_ride(index & 0x000000FF); - _window_guest_list_filter_arguments[0] = STR_QUEUING_FOR; - _window_guest_list_filter_arguments[1] = ride->name; - _window_guest_list_filter_arguments[2] = ride->name_arguments_type_name; - _window_guest_list_filter_arguments[3] = ride->name_arguments_number; + auto ride = get_ride(index & 0xFF); + if (ride != nullptr) + { + set_format_arg_on(_window_guest_list_filter_arguments.args, 0, rct_string_id, STR_QUEUING_FOR); + ride->FormatNameTo(_window_guest_list_filter_arguments.args + 2); - _window_guest_list_selected_filter = 0; - _window_guest_list_highlighted_index = 0xFFFF; - _window_guest_list_selected_tab = 0; - _window_guest_list_selected_view = 0; + _window_guest_list_selected_filter = 0; + _window_guest_list_highlighted_index = 0xFFFF; + _window_guest_list_selected_tab = 0; + _window_guest_list_selected_view = 0; + } break; } case GLFT_GUESTS_THINKING_ABOUT_RIDE: { - Ride* ride = get_ride(index & 0x000000FF); - _window_guest_list_filter_arguments[0] = STR_NONE; - _window_guest_list_filter_arguments[1] = ride->name; - _window_guest_list_filter_arguments[2] = ride->name_arguments_type_name; - _window_guest_list_filter_arguments[3] = ride->name_arguments_number; + auto ride = get_ride(index & 0xFF); + if (ride != nullptr) + { + set_format_arg_on(_window_guest_list_filter_arguments.args, 0, rct_string_id, STR_NONE); + ride->FormatNameTo(_window_guest_list_filter_arguments.args + 2); - _window_guest_list_selected_filter = 1; - _window_guest_list_highlighted_index = 0xFFFF; - _window_guest_list_selected_tab = 0; - _window_guest_list_selected_view = 1; + _window_guest_list_selected_filter = 1; + _window_guest_list_highlighted_index = 0xFFFF; + _window_guest_list_selected_tab = 0; + _window_guest_list_selected_view = 1; + } break; } case GLFT_GUESTS_THINKING_X: { - _window_guest_list_filter_arguments[0] = PeepThoughts[(index & 0x000000FF)]; - _window_guest_list_filter_arguments[1] = 0; - _window_guest_list_filter_arguments[2] = 0; - _window_guest_list_filter_arguments[3] = 0; + set_format_arg_on(_window_guest_list_filter_arguments.args, 0, rct_string_id, PeepThoughts[index & 0xFF]); _window_guest_list_selected_filter = 1; _window_guest_list_highlighted_index = 0xFFFF; @@ -322,7 +340,7 @@ static void window_guest_list_mouseup(rct_window* w, rct_widgetindex widgetIndex w->pressed_widgets |= (1 << WIDX_TRACKING); else w->pressed_widgets &= ~(1 << WIDX_TRACKING); - window_invalidate(w); + w->Invalidate(); w->scrolls[0].v_top = 0; break; case WIDX_FILTER_BY_NAME: @@ -352,12 +370,12 @@ static void window_guest_list_resize(rct_window* w) w->min_height = 330; if (w->width < w->min_width) { - window_invalidate(w); + w->Invalidate(); w->width = w->min_width; } if (w->height < w->min_height) { - window_invalidate(w); + w->Invalidate(); w->height = w->min_height; } } @@ -390,7 +408,7 @@ static void window_guest_list_mousedown(rct_window* w, rct_widgetindex widgetInd window_guest_list_widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WWT_EMPTY; w->list_information_type = 0; _window_guest_list_selected_filter = -1; - window_invalidate(w); + w->Invalidate(); w->scrolls[0].v_top = 0; break; case WIDX_PAGE_DROPDOWN_BUTTON: @@ -437,13 +455,13 @@ static void window_guest_list_dropdown(rct_window* w, rct_widgetindex widgetInde if (dropdownIndex == -1) break; _window_guest_list_selected_page = dropdownIndex; - window_invalidate(w); + w->Invalidate(); break; case WIDX_INFO_TYPE_DROPDOWN_BUTTON: if (dropdownIndex == -1) break; _window_guest_list_selected_view = dropdownIndex; - window_invalidate(w); + w->Invalidate(); break; } } @@ -515,7 +533,7 @@ static void window_guest_list_scrollgetsize(rct_window* w, int32_t scrollIndex, if (_window_guest_list_highlighted_index != -1) { _window_guest_list_highlighted_index = -1; - window_invalidate(w); + w->Invalidate(); } i = y - window_guest_list_widgets[WIDX_GUEST_LIST].bottom + window_guest_list_widgets[WIDX_GUEST_LIST].top + 21; @@ -524,7 +542,7 @@ static void window_guest_list_scrollgetsize(rct_window* w, int32_t scrollIndex, if (i < w->scrolls[0].v_top) { w->scrolls[0].v_top = i; - window_invalidate(w); + w->Invalidate(); } *width = 447; @@ -572,12 +590,11 @@ static void window_guest_list_scrollmousedown(rct_window* w, int32_t scrollIndex i = y / SUMMARISED_GUEST_ROW_HEIGHT; if (i < _window_guest_list_num_groups) { - std::memcpy(_window_guest_list_filter_arguments + 0, &_window_guest_list_groups_argument_1[i], 4); - std::memcpy(_window_guest_list_filter_arguments + 2, &_window_guest_list_groups_argument_2[i], 4); + _window_guest_list_filter_arguments = _window_guest_list_groups_arguments[i]; _window_guest_list_selected_filter = _window_guest_list_selected_view; _window_guest_list_selected_tab = PAGE_INDIVIDUAL; window_guest_list_widgets[WIDX_TRACKING].type = WWT_FLATBTN; - window_invalidate(w); + w->Invalidate(); w->scrolls[0].v_top = 0; } break; @@ -597,7 +614,7 @@ static void window_guest_list_scrollmouseover(rct_window* w, int32_t scrollIndex if (i != _window_guest_list_highlighted_index) { _window_guest_list_highlighted_index = i; - window_invalidate(w); + w->Invalidate(); } } @@ -677,7 +694,7 @@ static void window_guest_list_paint(rct_window* w, rct_drawpixelinfo* dpi) { if (_window_guest_list_selected_filter != -1) { - if (_window_guest_list_filter_arguments[0] != 0xFFFF) + if (_window_guest_list_filter_arguments.GetFirstStringId() != STR_NONE) { format = filterNames[_window_guest_list_selected_filter]; // Not sure whether the index will ever be 2 } @@ -695,7 +712,7 @@ static void window_guest_list_paint(rct_window* w, rct_drawpixelinfo* dpi) { format = STR_ALL_GUESTS_SUMMARISED; } - gfx_draw_string_left_clipped(dpi, format, _window_guest_list_filter_arguments, COLOUR_BLACK, x, y, 310); + gfx_draw_string_left_clipped(dpi, format, _window_guest_list_filter_arguments.args, COLOUR_BLACK, x, y, 310); // Number of guests (list items) if (_window_guest_list_selected_tab == PAGE_INDIVIDUAL) @@ -719,7 +736,6 @@ static void window_guest_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, rct_string_id format; Peep* peep; rct_peep_thought* thought; - uint32_t argument_1, argument_2; // Background fill gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, ColourMapA[w->colours[1]].mid_light); @@ -759,8 +775,7 @@ static void window_guest_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, } // Guest name - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); + peep->FormatNameTo(gCommonFormatArgs); gfx_draw_string_left_clipped(dpi, format, gCommonFormatArgs, COLOUR_BLACK, 0, y, 113); switch (_window_guest_list_selected_view) @@ -774,11 +789,7 @@ static void window_guest_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, gfx_draw_sprite(dpi, STR_ENTER_SELECTION_SIZE, 112, y + 1, 0); // Action - - get_arguments_from_action(peep, &argument_1, &argument_2); - - set_format_arg(0, uint32_t, argument_1); - set_format_arg(4, uint32_t, argument_2); + peep->FormatActionTo(gCommonFormatArgs); gfx_draw_string_left_clipped(dpi, format, gCommonFormatArgs, COLOUR_BLACK, 133, y, 314); break; case VIEW_THOUGHTS: @@ -835,14 +846,15 @@ static void window_guest_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, j * 8, y + 12, 0); // Draw action - set_format_arg(0, uint32_t, _window_guest_list_groups_argument_1[i]); - set_format_arg(4, uint32_t, _window_guest_list_groups_argument_2[i]); - set_format_arg(10, uint32_t, numGuests); + std::memcpy( + gCommonFormatArgs, _window_guest_list_groups_arguments[i].args, + std::min(sizeof(gCommonFormatArgs), sizeof(_window_guest_list_groups_arguments[i].args))); gfx_draw_string_left_clipped(dpi, format, gCommonFormatArgs, COLOUR_BLACK, 0, y, 414); // Draw guest count - set_format_arg(8, rct_string_id, STR_GUESTS_COUNT_COMMA_SEP); - gfx_draw_string_right(dpi, format, gCommonFormatArgs + 8, COLOUR_BLACK, 326, y); + set_format_arg(0, rct_string_id, STR_GUESTS_COUNT_COMMA_SEP); + set_format_arg(2, uint32_t, numGuests); + gfx_draw_string_right(dpi, format, gCommonFormatArgs, COLOUR_BLACK, 326, y); } y += SUMMARISED_GUEST_ROW_HEIGHT; } @@ -865,21 +877,18 @@ static void window_guest_list_textinput(rct_window* w, rct_widgetindex widgetInd */ static int32_t window_guest_list_is_peep_in_filter(Peep* peep) { - char temp; - - temp = _window_guest_list_selected_view; + char temp = _window_guest_list_selected_view; _window_guest_list_selected_view = _window_guest_list_selected_filter; - uint32_t argument1, argument2; - get_arguments_from_peep(peep, &argument1, &argument2); + auto peepArgs = get_arguments_from_peep(peep); _window_guest_list_selected_view = temp; - if (_window_guest_list_filter_arguments[0] == 0xFFFF && _window_guest_list_selected_filter == 1) - argument1 |= 0xFFFF; + if (_window_guest_list_filter_arguments.GetFirstStringId() == STR_NONE && _window_guest_list_selected_filter == 1) + { + set_format_arg_on(peepArgs.args, 0, rct_string_id, STR_NONE); + } - uint32_t check1 = _window_guest_list_filter_arguments[0] | (_window_guest_list_filter_arguments[1] << 16); - uint32_t check2 = _window_guest_list_filter_arguments[2] | (_window_guest_list_filter_arguments[3] << 16); - if (argument1 == check1 && argument2 == check2) + if (_window_guest_list_filter_arguments == peepArgs) { return 0; } @@ -893,37 +902,27 @@ static int32_t window_guest_list_is_peep_in_filter(Peep* peep) * argument_1 (0x013CE952) gCommonFormatArgs * argument_2 (0x013CE954) gCommonFormatArgs + 2 */ -static void get_arguments_from_peep(Peep* peep, uint32_t* argument_1, uint32_t* argument_2) +static FilterArguments get_arguments_from_peep(const Peep* peep) { + FilterArguments result; switch (_window_guest_list_selected_view) { case VIEW_ACTIONS: - get_arguments_from_action(peep, argument_1, argument_2); + peep->FormatActionTo(result.args); break; case VIEW_THOUGHTS: { - rct_peep_thought* thought = &peep->thoughts[0]; + auto thought = &peep->thoughts[0]; if (thought->freshness <= 5 && thought->type != PEEP_THOUGHT_TYPE_NONE) { - // HACK The out arguments here are used to draw the group text so we just return - // gCommonFormatArgs as two uint32_ts. - std::memset(gCommonFormatArgs, 0, sizeof(*argument_1) + sizeof(*argument_2)); + std::memset(gCommonFormatArgs, 0, sizeof(gCommonFormatArgs)); peep_thought_set_format_args(thought); - std::memcpy(argument_1, gCommonFormatArgs, sizeof(*argument_1)); - std::memcpy(argument_2, gCommonFormatArgs + sizeof(*argument_1), sizeof(*argument_2)); - } - else - { - *argument_1 = 0; - *argument_2 = 0; + std::memcpy(result.args, gCommonFormatArgs, std::min(sizeof(gCommonFormatArgs), sizeof(result.args))); } break; } - default: - *argument_1 = 0; - *argument_2 = 0; - break; } + return result; } /** @@ -969,10 +968,8 @@ static void window_guest_list_find_groups() _window_guest_list_groups_num_guests[groupIndex] = 1; peep->flags &= ~(SPRITE_FLAGS_PEEP_VISIBLE); - get_arguments_from_peep( - peep, &_window_guest_list_groups_argument_1[groupIndex], &_window_guest_list_groups_argument_2[groupIndex]); - std::memcpy(_window_guest_list_filter_arguments + 0, &_window_guest_list_groups_argument_1[groupIndex], 4); - std::memcpy(_window_guest_list_filter_arguments + 2, &_window_guest_list_groups_argument_2[groupIndex], 4); + _window_guest_list_groups_arguments[groupIndex] = get_arguments_from_peep(peep); + _window_guest_list_filter_arguments = _window_guest_list_groups_arguments[groupIndex]; _window_guest_list_group_index[groupIndex] = groupIndex; faceIndex = groupIndex * 56; @@ -985,11 +982,9 @@ static void window_guest_list_find_groups() if (peep2->outside_of_park != 0 || !(peep2->flags & SPRITE_FLAGS_PEEP_VISIBLE)) continue; - uint32_t argument1, argument2; // Get and check if in same group - get_arguments_from_peep(peep2, &argument1, &argument2); - if (argument1 != _window_guest_list_groups_argument_1[groupIndex] - || argument2 != _window_guest_list_groups_argument_2[groupIndex]) + auto argument12 = get_arguments_from_peep(peep2); + if (argument12 != _window_guest_list_groups_arguments[groupIndex]) continue; // Assign guest @@ -1003,13 +998,13 @@ static void window_guest_list_find_groups() - SPR_PEEP_SMALL_FACE_VERY_VERY_UNHAPPY; } - if (_window_guest_list_filter_arguments[0] == 0) + if (_window_guest_list_filter_arguments.GetFirstStringId() == 0) { _window_guest_list_num_groups--; continue; } - int32_t curr_num_guests = _window_guest_list_groups_num_guests[groupIndex]; + auto curr_num_guests = _window_guest_list_groups_num_guests[groupIndex]; int32_t swap_position = 0; // This section places the groups in size order. bool gotoNextPeep = false; @@ -1029,23 +1024,13 @@ static void window_guest_list_find_groups() continue; } - int32_t argument_1 = _window_guest_list_groups_argument_1[groupIndex]; - int32_t argument_2 = _window_guest_list_groups_argument_2[groupIndex]; - int32_t bl = _window_guest_list_group_index[groupIndex]; + auto arguments12 = _window_guest_list_groups_arguments[groupIndex]; + auto bl = _window_guest_list_group_index[groupIndex]; do { - int32_t temp = curr_num_guests; - curr_num_guests = _window_guest_list_groups_num_guests[swap_position]; - _window_guest_list_groups_num_guests[swap_position] = temp; - - temp = argument_1; - argument_1 = _window_guest_list_groups_argument_1[swap_position]; - _window_guest_list_groups_argument_1[swap_position] = temp; - - temp = argument_2; - argument_2 = _window_guest_list_groups_argument_2[swap_position]; - _window_guest_list_groups_argument_2[swap_position] = temp; + std::swap(curr_num_guests, _window_guest_list_groups_num_guests[swap_position]); + std::swap(arguments12, _window_guest_list_groups_arguments[swap_position]); uint8_t temp_faces[56]; std::memcpy(temp_faces, &(_window_guest_list_groups_guest_faces[groupIndex * 56]), 56); @@ -1054,9 +1039,7 @@ static void window_guest_list_find_groups() &(_window_guest_list_groups_guest_faces[swap_position * 56]), 56); std::memcpy(&(_window_guest_list_groups_guest_faces[swap_position * 56]), temp_faces, 56); - temp = _window_guest_list_group_index[swap_position]; - _window_guest_list_group_index[swap_position] = bl; - bl = temp; + std::swap(bl, _window_guest_list_group_index[swap_position]); } while (++swap_position <= groupIndex); } } @@ -1068,14 +1051,14 @@ static bool guest_should_be_visible(Peep* peep) if (_window_guest_list_filter_name[0] != '\0') { - char formatted[256]; - - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); - format_string(formatted, sizeof(formatted), peep->name_string_idx, gCommonFormatArgs); - - if (strcasestr(formatted, _window_guest_list_filter_name) == nullptr) + char name[256]{}; + uint8_t args[32]{}; + peep->FormatNameTo(args); + format_string(name, sizeof(name), STR_STRINGID, args); + if (strcasestr(name, _window_guest_list_filter_name) == nullptr) + { return false; + } } return true; diff --git a/src/openrct2-ui/windows/InstallTrack.cpp b/src/openrct2-ui/windows/InstallTrack.cpp index 7abd2ad2b9..204dcc0569 100644 --- a/src/openrct2-ui/windows/InstallTrack.cpp +++ b/src/openrct2-ui/windows/InstallTrack.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -90,7 +90,7 @@ static rct_window_event_list window_install_track_events = { }; // clang-format on -static rct_track_td6* _trackDesign; +static std::unique_ptr _trackDesign; static std::string _trackPath; static std::string _trackName; static std::vector _trackDesignPreviewPixels; @@ -134,7 +134,7 @@ rct_window* window_install_track_open(const utf8* path) int32_t x = screenWidth / 2 - 201; int32_t y = std::max(TOP_TOOLBAR_HEIGHT + 1, screenHeight / 2 - 200); - rct_window* w = window_create(x, y, WW, WH, &window_install_track_events, WC_INSTALL_TRACK, 0); + rct_window* w = window_create(ScreenCoordsXY(x, y), WW, WH, &window_install_track_events, WC_INSTALL_TRACK, 0); w->widgets = window_install_track_widgets; w->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_ROTATE) | (1 << WIDX_TOGGLE_SCENERY) | (1 << WIDX_INSTALL) | (1 << WIDX_CANCEL); @@ -147,7 +147,7 @@ rct_window* window_install_track_open(const utf8* path) _trackDesignPreviewPixels.resize(4 * TRACK_PREVIEW_IMAGE_SIZE); window_install_track_update_preview(); - window_invalidate(w); + w->Invalidate(); return w; } @@ -162,7 +162,6 @@ static void window_install_track_close(rct_window* w) _trackName.clear(); _trackDesignPreviewPixels.clear(); _trackDesignPreviewPixels.shrink_to_fit(); - track_design_dispose(_trackDesign); _trackDesign = nullptr; } @@ -181,12 +180,12 @@ static void window_install_track_mouseup(rct_window* w, rct_widgetindex widgetIn case WIDX_ROTATE: _currentTrackPieceDirection++; _currentTrackPieceDirection %= 4; - window_invalidate(w); + w->Invalidate(); break; case WIDX_TOGGLE_SCENERY: gTrackDesignSceneryToggle = !gTrackDesignSceneryToggle; window_install_track_update_preview(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_INSTALL: window_install_track_design(w); @@ -238,13 +237,14 @@ static void window_install_track_paint(rct_window* w, rct_drawpixelinfo* dpi) g1temp.height = 217; g1temp.flags = G1_FLAG_BMP; gfx_set_g1_element(SPR_TEMP, &g1temp); + drawing_engine_invalidate_image(SPR_TEMP); gfx_draw_sprite(dpi, SPR_TEMP, x, y, 0); x = w->x + (widget->left + widget->right) / 2; y = w->y + widget->bottom - 12; // Warnings - rct_track_td6* td6 = _trackDesign; + const TrackDesign* td6 = _trackDesign.get(); if (td6->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) { if (!gTrackDesignSceneryToggle) @@ -344,16 +344,12 @@ static void window_install_track_paint(rct_window* w, rct_drawpixelinfo* dpi) gfx_draw_string_left(dpi, STR_MAX_LATERAL_G, &gForces, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; - // If .TD6 - if (td6->version_and_colour_scheme / 4 >= 2) + if (td6->total_air_time != 0) { - if (td6->total_air_time != 0) - { - // Total air time - int32_t airTime = td6->total_air_time * 25; - gfx_draw_string_left(dpi, STR_TOTAL_AIR_TIME, &airTime, COLOUR_BLACK, x, y); - y += LIST_ROW_HEIGHT; - } + // Total air time + int32_t airTime = td6->total_air_time * 25; + gfx_draw_string_left(dpi, STR_TOTAL_AIR_TIME, &airTime, COLOUR_BLACK, x, y); + y += LIST_ROW_HEIGHT; } } @@ -392,7 +388,8 @@ static void window_install_track_paint(rct_window* w, rct_drawpixelinfo* dpi) if (td6->cost != 0) { - gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, &td6->cost, COLOUR_BLACK, x, y); + set_format_arg(0, uint32_t, td6->cost); + gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, gCommonFormatArgs, COLOUR_BLACK, x, y); } } @@ -414,7 +411,7 @@ static void window_install_track_text_input(rct_window* w, rct_widgetindex widge static void window_install_track_update_preview() { - track_design_draw_preview(_trackDesign, _trackDesignPreviewPixels.data()); + track_design_draw_preview(_trackDesign.get(), _trackDesignPreviewPixels.data()); } static void window_install_track_design(rct_window* w) diff --git a/src/openrct2-ui/windows/Land.cpp b/src/openrct2-ui/windows/Land.cpp index 887f7f4cb1..341f00254e 100644 --- a/src/openrct2-ui/windows/Land.cpp +++ b/src/openrct2-ui/windows/Land.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -112,7 +112,7 @@ rct_window* window_land_open() if (window != nullptr) return window; - window = window_create(context_get_width() - 98, 29, 98, 160, &window_land_events, WC_LAND, 0); + window = window_create(ScreenCoordsXY(context_get_width() - 98, 29), 98, 160, &window_land_events, WC_LAND, 0); window->widgets = window_land_widgets; window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_DECREMENT) | (1 << WIDX_INCREMENT) | (1 << WIDX_FLOOR) | (1 << WIDX_WALL) | (1 << WIDX_MOUNTAINMODE) | (1 << WIDX_PAINTMODE) | (1 << WIDX_PREVIEW); @@ -158,12 +158,12 @@ static void window_land_mouseup(rct_window* w, rct_widgetindex widgetIndex) case WIDX_MOUNTAINMODE: gLandMountainMode ^= 1; gLandPaintMode = 0; - window_invalidate(w); + w->Invalidate(); break; case WIDX_PAINTMODE: gLandMountainMode = 0; gLandPaintMode ^= 1; - window_invalidate(w); + w->Invalidate(); break; case WIDX_PREVIEW: window_land_inputsize(w); @@ -193,14 +193,14 @@ static void window_land_mousedown(rct_window* w, rct_widgetindex widgetIndex, rc gLandToolSize = std::max(MINIMUM_TOOL_SIZE, gLandToolSize - 1); // Invalidate the window - window_invalidate(w); + w->Invalidate(); break; case WIDX_INCREMENT: // Increment land tool size gLandToolSize = std::min(MAXIMUM_TOOL_SIZE, gLandToolSize + 1); // Invalidate the window - window_invalidate(w); + w->Invalidate(); break; } } @@ -230,7 +230,7 @@ static void window_land_dropdown(rct_window* w, rct_widgetindex widgetIndex, int gLandToolTerrainSurface = type; _selectedFloorTexture = type; } - window_invalidate(w); + w->Invalidate(); break; case WIDX_WALL: if (dropdownIndex == -1) @@ -247,7 +247,7 @@ static void window_land_dropdown(rct_window* w, rct_widgetindex widgetIndex, int gLandToolTerrainEdge = type; _selectedWallTexture = type; } - window_invalidate(w); + w->Invalidate(); break; } } @@ -267,7 +267,7 @@ static void window_land_textinput(rct_window* w, rct_widgetindex widgetIndex, ch size = std::min(MAXIMUM_TOOL_SIZE, size); gLandToolSize = size; - window_invalidate(w); + w->Invalidate(); } } @@ -360,35 +360,39 @@ static void window_land_paint(rct_window* w, rct_drawpixelinfo* dpi) x = w->x + (previewWidget->left + previewWidget->right) / 2; y = w->y + previewWidget->bottom + 5; - // Draw raise cost amount - if (gLandToolRaiseCost != MONEY32_UNDEFINED && gLandToolRaiseCost != 0) - gfx_draw_string_centred(dpi, STR_RAISE_COST_AMOUNT, x, y, COLOUR_BLACK, &gLandToolRaiseCost); - y += 10; - - // Draw lower cost amount - if (gLandToolLowerCost != MONEY32_UNDEFINED && gLandToolLowerCost != 0) - gfx_draw_string_centred(dpi, STR_LOWER_COST_AMOUNT, x, y, COLOUR_BLACK, &gLandToolLowerCost); - y += 50; - - // Draw paint price - numTiles = gLandToolSize * gLandToolSize; - price = 0; - if (gLandToolTerrainSurface != 255) + if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) { - auto& objManager = GetContext()->GetObjectManager(); - const auto surfaceObj = static_cast( - objManager.GetLoadedObject(OBJECT_TYPE_TERRAIN_SURFACE, gLandToolTerrainSurface)); - if (surfaceObj != nullptr) + // Draw raise cost amount + if (gLandToolRaiseCost != MONEY32_UNDEFINED && gLandToolRaiseCost != 0) + gfx_draw_string_centred(dpi, STR_RAISE_COST_AMOUNT, x, y, COLOUR_BLACK, &gLandToolRaiseCost); + y += 10; + + // Draw lower cost amount + if (gLandToolLowerCost != MONEY32_UNDEFINED && gLandToolLowerCost != 0) + gfx_draw_string_centred(dpi, STR_LOWER_COST_AMOUNT, x, y, COLOUR_BLACK, &gLandToolLowerCost); + y += 50; + + // Draw paint price + numTiles = gLandToolSize * gLandToolSize; + price = 0; + if (gLandToolTerrainSurface != 255) { - price += numTiles * surfaceObj->Price; + auto& objManager = GetContext()->GetObjectManager(); + const auto surfaceObj = static_cast( + objManager.GetLoadedObject(OBJECT_TYPE_TERRAIN_SURFACE, gLandToolTerrainSurface)); + if (surfaceObj != nullptr) + { + price += numTiles * surfaceObj->Price; + } + } + + if (gLandToolTerrainEdge != 255) + price += numTiles * 100; + + if (price != 0) + { + set_format_arg(0, money32, price); + gfx_draw_string_centred(dpi, STR_COST_AMOUNT, x, y, COLOUR_BLACK, gCommonFormatArgs); } } - if (gLandToolTerrainEdge != 255) - price += numTiles * 100; - - if (price != 0 && !(gParkFlags & PARK_FLAGS_NO_MONEY)) - { - set_format_arg(0, money32, price); - gfx_draw_string_centred(dpi, STR_COST_AMOUNT, x, y, COLOUR_BLACK, gCommonFormatArgs); - } } diff --git a/src/openrct2-ui/windows/LandRights.cpp b/src/openrct2-ui/windows/LandRights.cpp index 253457130c..3e0e78aca9 100644 --- a/src/openrct2-ui/windows/LandRights.cpp +++ b/src/openrct2-ui/windows/LandRights.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -105,7 +106,7 @@ rct_window* window_land_rights_open() if (window != nullptr) return window; - window = window_create(context_get_width() - 98, 29, 98, 94, &window_land_rights_events, WC_LAND_RIGHTS, 0); + window = window_create(ScreenCoordsXY(context_get_width() - 98, 29), 98, 94, &window_land_rights_events, WC_LAND_RIGHTS, 0); window->widgets = window_land_rights_widgets; window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_DECREMENT) | (1 << WIDX_INCREMENT) | (1 << WIDX_PREVIEW) | (1 << WIDX_BUY_LAND_RIGHTS) | (1 << WIDX_BUY_CONSTRUCTION_RIGHTS); @@ -160,7 +161,7 @@ static void window_land_rights_mouseup(rct_window* w, rct_widgetindex widgetInde tool_set(w, WIDX_BUY_LAND_RIGHTS, TOOL_UP_ARROW); _landRightsMode = LAND_RIGHTS_MODE_BUY_LAND; show_land_rights(); - window_invalidate(w); + w->Invalidate(); } break; case WIDX_BUY_CONSTRUCTION_RIGHTS: @@ -169,7 +170,7 @@ static void window_land_rights_mouseup(rct_window* w, rct_widgetindex widgetInde tool_set(w, WIDX_BUY_CONSTRUCTION_RIGHTS, TOOL_UP_ARROW); _landRightsMode = LAND_RIGHTS_MODE_BUY_CONSTRUCTION_RIGHTS; show_construction_rights(); - window_invalidate(w); + w->Invalidate(); } break; } @@ -184,14 +185,14 @@ static void window_land_rights_mousedown(rct_window* w, rct_widgetindex widgetIn gLandToolSize = std::max(MINIMUM_TOOL_SIZE, gLandToolSize - 1); // Invalidate the window - window_invalidate(w); + w->Invalidate(); break; case WIDX_INCREMENT: // Decrement land rights tool size gLandToolSize = std::min(MAXIMUM_TOOL_SIZE, gLandToolSize + 1); // Invalidate the window - window_invalidate(w); + w->Invalidate(); break; } } @@ -210,7 +211,7 @@ static void window_land_rights_textinput(rct_window* w, rct_widgetindex widgetIn size = std::max(MINIMUM_TOOL_SIZE, size); size = std::min(MAXIMUM_TOOL_SIZE, size); gLandToolSize = size; - window_invalidate(w); + w->Invalidate(); } } @@ -281,10 +282,10 @@ static void window_land_rights_paint(rct_window* w, rct_drawpixelinfo* dpi) } // Draw cost amount - x = (window_land_rights_widgets[WIDX_PREVIEW].left + window_land_rights_widgets[WIDX_PREVIEW].right) / 2 + w->x; - y = window_land_rights_widgets[WIDX_PREVIEW].bottom + w->y + 32; - if (_landRightsCost != MONEY32_UNDEFINED && _landRightsCost != 0) + if (_landRightsCost != MONEY32_UNDEFINED && _landRightsCost != 0 && !(gParkFlags & PARK_FLAGS_NO_MONEY)) { + x = (window_land_rights_widgets[WIDX_PREVIEW].left + window_land_rights_widgets[WIDX_PREVIEW].right) / 2 + w->x; + y = window_land_rights_widgets[WIDX_PREVIEW].bottom + w->y + 32; gfx_draw_string_centred(dpi, STR_COST_AMOUNT, x, y, COLOUR_BLACK, &_landRightsCost); } } @@ -364,11 +365,13 @@ static void window_land_rights_tool_update_land_rights(int16_t x, int16_t y) if (!state_changed) return; - _landRightsCost = game_do_command( - gMapSelectPositionA.x, GAME_COMMAND_FLAG_2, gMapSelectPositionA.y, - (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) ? BUY_LAND_RIGHTS_FLAG_BUY_LAND - : BUY_LAND_RIGHTS_FLAG_BUY_CONSTRUCTION_RIGHTS, - GAME_COMMAND_BUY_LAND_RIGHTS, gMapSelectPositionB.x, gMapSelectPositionB.y); + auto landBuyRightsAction = LandBuyRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + (_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) ? LandBuyRightSetting::BuyLand + : LandBuyRightSetting::BuyConstructionRights); + auto res = GameActions::Query(&landBuyRightsAction); + + _landRightsCost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; } /** @@ -407,21 +410,20 @@ static void window_land_rights_tooldown(rct_window* w, rct_widgetindex widgetInd { if (x != LOCATION_NULL) { - gGameCommandErrorTitle = STR_CANT_BUY_LAND; - game_do_command( - gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, BUY_LAND_RIGHTS_FLAG_BUY_LAND, - GAME_COMMAND_BUY_LAND_RIGHTS, gMapSelectPositionB.x, gMapSelectPositionB.y); + auto landBuyRightsAction = LandBuyRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandBuyRightSetting::BuyLand); + GameActions::Execute(&landBuyRightsAction); } } else { if (x != LOCATION_NULL) { - gGameCommandErrorTitle = STR_CANT_BUY_CONSTRUCTION_RIGHTS_HERE; - game_do_command( - gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, - BUY_LAND_RIGHTS_FLAG_BUY_CONSTRUCTION_RIGHTS, GAME_COMMAND_BUY_LAND_RIGHTS, gMapSelectPositionB.x, - gMapSelectPositionB.y); + auto landBuyRightsAction = LandBuyRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandBuyRightSetting::BuyConstructionRights); + GameActions::Execute(&landBuyRightsAction); } } } @@ -436,21 +438,20 @@ static void window_land_rights_tooldrag(rct_window* w, rct_widgetindex widgetInd { if (x != LOCATION_NULL) { - gGameCommandErrorTitle = STR_CANT_BUY_LAND; - game_do_command( - gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, BUY_LAND_RIGHTS_FLAG_BUY_LAND, - GAME_COMMAND_BUY_LAND_RIGHTS, gMapSelectPositionB.x, gMapSelectPositionB.y); + auto landBuyRightsAction = LandBuyRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandBuyRightSetting::BuyLand); + GameActions::Execute(&landBuyRightsAction); } } else { if (x != LOCATION_NULL) { - gGameCommandErrorTitle = STR_CANT_BUY_CONSTRUCTION_RIGHTS_HERE; - game_do_command( - gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, - BUY_LAND_RIGHTS_FLAG_BUY_CONSTRUCTION_RIGHTS, GAME_COMMAND_BUY_LAND_RIGHTS, gMapSelectPositionB.x, - gMapSelectPositionB.y); + auto landBuyRightsAction = LandBuyRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandBuyRightSetting::BuyConstructionRights); + GameActions::Execute(&landBuyRightsAction); } } } diff --git a/src/openrct2-ui/windows/LoadSave.cpp b/src/openrct2-ui/windows/LoadSave.cpp index f0fabc2b90..d3a57e9206 100644 --- a/src/openrct2-ui/windows/LoadSave.cpp +++ b/src/openrct2-ui/windows/LoadSave.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +145,7 @@ struct LoadSaveListItem }; static loadsave_callback _loadSaveCallback; +static TrackDesign* _trackDesign; static std::vector _listItems; static char _directory[MAX_PATH]; @@ -240,25 +243,19 @@ static const char* getFilterPatternByType(const int32_t type, const bool isSave) static int32_t window_loadsave_get_dir(const int32_t type, char* path, size_t pathSize) { const char* last_save = getLastDirectoryByType(type); - if (last_save && platform_ensure_directory_exists(last_save)) + if (last_save != nullptr && platform_directory_exists(last_save)) safe_strcpy(path, last_save, pathSize); else getInitialDirectoryByType(type, path, pathSize); - - if (!platform_ensure_directory_exists(path)) - { - log_error("Unable to create save directory."); - return 0; - } - return 1; } static bool browse(bool isSave, char* path, size_t pathSize); -rct_window* window_loadsave_open(int32_t type, const char* defaultName, loadsave_callback callback) +rct_window* window_loadsave_open(int32_t type, const char* defaultName, loadsave_callback callback, TrackDesign* trackDesign) { _loadSaveCallback = callback; + _trackDesign = trackDesign; _type = type; _defaultName[0] = '\0'; @@ -346,12 +343,12 @@ static void window_loadsave_resize(rct_window* w) { if (w->width < w->min_width) { - window_invalidate(w); + w->Invalidate(); w->width = w->min_width; } if (w->height < w->min_height) { - window_invalidate(w); + w->Invalidate(); w->height = w->min_height; } } @@ -413,15 +410,14 @@ static bool browse(bool isSave, char* path, size_t pathSize) } else { - utf8 buffer[USER_STRING_MAX_LENGTH]{}; - if (gParkName != STR_NONE) - format_string(buffer, pathSize, gParkName, nullptr); - - // Use localized "Unnamed Park" if park name was empty - if (String::SizeOf(buffer) == 0) - format_string(buffer, pathSize, STR_UNNAMED_PARK, nullptr); - - safe_strcat_path(path, buffer, pathSize); + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto buffer = park.Name; + if (buffer.empty()) + { + // Use localised "Unnamed Park" if park name was empty. + buffer = format_string(STR_UNNAMED_PARK, nullptr); + } + safe_strcat_path(path, buffer.c_str(), pathSize); } } @@ -438,7 +434,7 @@ static bool browse(bool isSave, char* path, size_t pathSize) { // When the given save type was given, Windows still interprets a filename with a dot in its name as a custom extension, // meaning files like "My Coaster v1.2" will not get the .td6 extension by default. - if (get_file_extension_type(path) != fileType) + if (isSave && get_file_extension_type(path) != fileType) path_append_extension(path, extension, pathSize); return true; @@ -500,7 +496,7 @@ static void window_loadsave_mouseup(rct_window* w, rct_widgetindex widgetIndex) } config_save_default(); window_loadsave_sort_list(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SORT_DATE: @@ -514,7 +510,7 @@ static void window_loadsave_mouseup(rct_window* w, rct_widgetindex widgetIndex) } config_save_default(); window_loadsave_sort_list(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_DEFAULT: @@ -577,7 +573,7 @@ static void window_loadsave_scrollmouseover(rct_window* w, int32_t scrollIndex, w->selected_list_item = selectedItem; - window_invalidate(w); + w->Invalidate(); } static void window_loadsave_textinput(rct_window* w, rct_widgetindex widgetIndex, char* text) @@ -613,7 +609,7 @@ static void window_loadsave_textinput(rct_window* w, rct_widgetindex widgetIndex window_init_scroll_widgets(w); w->no_list_items = static_cast(_listItems.size()); - window_invalidate(w); + w->Invalidate(); break; case WIDX_NEW_FILE: @@ -793,15 +789,15 @@ static bool list_item_sort(LoadSaveListItem& a, LoadSaveListItem& b) switch (gConfigGeneral.load_save_sort) { case SORT_NAME_ASCENDING: - return strcicmp(a.name.c_str(), b.name.c_str()) < 0; + return strlogicalcmp(a.name.c_str(), b.name.c_str()) < 0; case SORT_NAME_DESCENDING: - return -strcicmp(a.name.c_str(), b.name.c_str()) < 0; + return -strlogicalcmp(a.name.c_str(), b.name.c_str()) < 0; case SORT_DATE_DESCENDING: return -difftime(a.date_modified, b.date_modified) < 0; case SORT_DATE_ASCENDING: return difftime(a.date_modified, b.date_modified) < 0; default: - return strcicmp(a.name.c_str(), b.name.c_str()) < 0; + return strlogicalcmp(a.name.c_str(), b.name.c_str()) < 0; } } @@ -946,7 +942,7 @@ static void window_loadsave_populate_list(rct_window* w, int32_t includeNewItem, window_loadsave_sort_list(); } - window_invalidate(w); + w->Invalidate(); } static void window_loadsave_invoke_callback(int32_t result, const utf8* path) @@ -1088,8 +1084,13 @@ static void window_loadsave_select(rct_window* w, const char* path) case (LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK): { + save_path(&gConfigGeneral.last_save_track_directory, pathBuffer); + path_set_extension(pathBuffer, "td6", sizeof(pathBuffer)); - int32_t success = track_design_save_to_file(pathBuffer); + + T6Exporter t6Export{ _trackDesign }; + + auto success = t6Export.SaveTrack(pathBuffer); if (success) { diff --git a/src/openrct2-ui/windows/Main.cpp b/src/openrct2-ui/windows/Main.cpp index 9464562608..a98f3acf21 100644 --- a/src/openrct2-ui/windows/Main.cpp +++ b/src/openrct2-ui/windows/Main.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -64,7 +64,7 @@ rct_window* window_main_open() window_main_widgets[0].right = context_get_width(); window_main_widgets[0].bottom = context_get_height(); rct_window* window = window_create( - 0, 0, window_main_widgets[0].right, window_main_widgets[0].bottom, &window_main_events, WC_MAIN_WINDOW, + ScreenCoordsXY(0, 0), window_main_widgets[0].right, window_main_widgets[0].bottom, &window_main_events, WC_MAIN_WINDOW, WF_STICK_TO_BACK); window->widgets = window_main_widgets; diff --git a/src/openrct2-ui/windows/Map.cpp b/src/openrct2-ui/windows/Map.cpp index 0b4b29c4e5..aa2a8c24f2 100644 --- a/src/openrct2-ui/windows/Map.cpp +++ b/src/openrct2-ui/windows/Map.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -290,7 +292,7 @@ static void window_map_mouseup(rct_window* w, rct_widgetindex widgetIndex) window_close(w); break; case WIDX_SET_LAND_RIGHTS: - window_invalidate(w); + w->Invalidate(); if (tool_set(w, widgetIndex, TOOL_UP_ARROW)) break; _activeTool = 2; @@ -306,7 +308,7 @@ static void window_map_mouseup(rct_window* w, rct_widgetindex widgetIndex) if (_activeTool & 2) _activeTool &= 0xF2; - window_invalidate(w); + w->Invalidate(); break; case WIDX_LAND_SALE_CHECKBOX: _activeTool ^= 8; @@ -314,7 +316,7 @@ static void window_map_mouseup(rct_window* w, rct_widgetindex widgetIndex) if (_activeTool & 8) _activeTool &= 0xF8; - window_invalidate(w); + w->Invalidate(); break; case WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX: _activeTool ^= 1; @@ -322,7 +324,7 @@ static void window_map_mouseup(rct_window* w, rct_widgetindex widgetIndex) if (_activeTool & 1) _activeTool &= 0xF1; - window_invalidate(w); + w->Invalidate(); break; case WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX: _activeTool ^= 4; @@ -330,10 +332,10 @@ static void window_map_mouseup(rct_window* w, rct_widgetindex widgetIndex) if (_activeTool & 4) _activeTool &= 0xF4; - window_invalidate(w); + w->Invalidate(); break; case WIDX_BUILD_PARK_ENTRANCE: - window_invalidate(w); + w->Invalidate(); if (tool_set(w, widgetIndex, TOOL_UP_ARROW)) break; @@ -408,13 +410,13 @@ static void window_map_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct // Decrement land rights tool size _landRightsToolSize = std::max(MINIMUM_TOOL_SIZE, _landRightsToolSize - 1); - window_invalidate(w); + w->Invalidate(); break; case WIDX_LAND_TOOL_LARGER: // Increment land rights tool size _landRightsToolSize = std::min(MAXIMUM_TOOL_SIZE, _landRightsToolSize + 1); - window_invalidate(w); + w->Invalidate(); break; } } @@ -435,7 +437,7 @@ static void window_map_update(rct_window* w) for (int32_t i = 0; i < 16; i++) map_window_set_pixels(w); - window_invalidate(w); + w->Invalidate(); // Update tab animations w->list_information_type++; @@ -504,10 +506,10 @@ static void window_map_tooldrag(rct_window* w, rct_widgetindex widgetIndex, int3 case WIDX_SET_LAND_RIGHTS: if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) { - gGameCommandErrorTitle = 0; - game_do_command( - gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, _activeTool, - GAME_COMMAND_SET_LAND_OWNERSHIP, gMapSelectPositionB.x, gMapSelectPositionB.y); + auto landSetRightsAction = LandSetRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandSetRightSetting::SetOwnershipWithChecks, _activeTool << 4); + GameActions::Execute(&landSetRightsAction); } break; } @@ -522,20 +524,20 @@ static void window_map_toolabort(rct_window* w, rct_widgetindex widgetIndex) switch (widgetIndex) { case WIDX_SET_LAND_RIGHTS: - window_invalidate(w); + w->Invalidate(); hide_gridlines(); hide_land_rights(); hide_construction_rights(); break; case WIDX_BUILD_PARK_ENTRANCE: park_entrance_remove_ghost(); - window_invalidate(w); + w->Invalidate(); hide_gridlines(); hide_land_rights(); hide_construction_rights(); break; case WIDX_PEOPLE_STARTING_POSITION: - window_invalidate(w); + w->Invalidate(); hide_gridlines(); hide_land_rights(); hide_construction_rights(); @@ -564,7 +566,7 @@ static void window_map_scrollmousedown(rct_window* w, int32_t scrollIndex, int32 CoordsXY c = map_window_screen_to_map(x, y); int32_t mapX = std::clamp(c.x, 0, MAXIMUM_MAP_SIZE_TECHNICAL * 32 - 1); int32_t mapY = std::clamp(c.y, 0, MAXIMUM_MAP_SIZE_TECHNICAL * 32 - 1); - int32_t mapZ = tile_element_height(x, y); + int32_t mapZ = tile_element_height({ x, y }); rct_window* mainWindow = window_get_main(); if (mainWindow != nullptr) @@ -590,11 +592,10 @@ static void window_map_scrollmousedown(rct_window* w, int32_t scrollIndex, int32 gMapSelectPositionB.y = mapY + size; map_invalidate_selection_rect(); - gGameCommandErrorTitle = STR_CANT_CHANGE_LAND_TYPE; - game_do_command( - gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, - gLandToolTerrainSurface | (gLandToolTerrainEdge << 8), GAME_COMMAND_CHANGE_SURFACE_STYLE, gMapSelectPositionB.x, - gMapSelectPositionB.y); + auto surfaceSetStyleAction = SurfaceSetStyleAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + gLandToolTerrainSurface, gLandToolTerrainEdge); + GameActions::Execute(&surfaceSetStyleAction); } else if (widget_is_active_tool(w, WIDX_SET_LAND_RIGHTS)) { @@ -614,10 +615,10 @@ static void window_map_scrollmousedown(rct_window* w, int32_t scrollIndex, int32 gMapSelectPositionB.y = mapY + size; map_invalidate_selection_rect(); - gGameCommandErrorTitle = 0; - game_do_command( - gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, _activeTool, GAME_COMMAND_SET_LAND_OWNERSHIP, - gMapSelectPositionB.x, gMapSelectPositionB.y); + auto landSetRightsAction = LandSetRightsAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + LandSetRightSetting::SetOwnershipWithChecks, _activeTool << 4); + GameActions::Execute(&landSetRightsAction); } } @@ -637,7 +638,7 @@ static void window_map_textinput(rct_window* w, rct_widgetindex widgetIndex, cha { size = std::clamp(size, MINIMUM_TOOL_SIZE, MAXIMUM_TOOL_SIZE); _landRightsToolSize = size; - window_invalidate(w); + w->Invalidate(); } break; case WIDX_MAP_SIZE_SPINNER: @@ -659,7 +660,7 @@ static void window_map_textinput(rct_window* w, rct_widgetindex widgetIndex, cha map_window_increase_map_size(); currentSize++; } - window_invalidate(w); + w->Invalidate(); } break; } @@ -877,6 +878,7 @@ static void window_map_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_ g1temp.x_offset = -8; g1temp.y_offset = -8; gfx_set_g1_element(SPR_TEMP, &g1temp); + drawing_engine_invalidate_image(SPR_TEMP); gfx_draw_sprite(dpi, SPR_TEMP, 0, 0, 0); if (w->selected_tab == PAGE_PEEPS) @@ -1095,7 +1097,7 @@ static void window_map_paint_train_overlay(rct_drawpixelinfo* dpi) rct_vehicle *train, *vehicle; uint16_t train_index, vehicle_index; - for (train_index = gSpriteListHead[SPRITE_LIST_TRAIN]; train_index != SPRITE_INDEX_NULL; train_index = train->next) + for (train_index = gSpriteListHead[SPRITE_LIST_VEHICLE_HEAD]; train_index != SPRITE_INDEX_NULL; train_index = train->next) { train = GET_VEHICLE(train_index); for (vehicle_index = train_index; vehicle_index != SPRITE_INDEX_NULL; vehicle_index = vehicle->next_vehicle_on_train) @@ -1190,21 +1192,19 @@ static void window_map_set_land_rights_tool_update(int32_t x, int32_t y) static void place_park_entrance_get_map_position( int32_t x, int32_t y, int16_t* mapX, int16_t* mapY, int16_t* mapZ, int32_t* direction) { - TileElement* tileElement; - - sub_68A15E(x, y, mapX, mapY, direction, &tileElement); + sub_68A15E(x, y, mapX, mapY); if (*mapX == LOCATION_NULL) return; - tileElement = map_get_surface_element_at(*mapX >> 5, *mapY >> 5); - *mapZ = tileElement->AsSurface()->GetWaterHeight(); + auto surfaceElement = map_get_surface_element_at(*mapX >> 5, *mapY >> 5); + *mapZ = surfaceElement->GetWaterHeight(); if (*mapZ == 0) { - *mapZ = tileElement->base_height / 2; - if ((tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != 0) + *mapZ = surfaceElement->base_height / 2; + if ((surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != 0) { (*mapZ)++; - if (tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) { (*mapZ)++; } @@ -1235,14 +1235,12 @@ static void window_map_place_park_entrance_tool_update(int32_t x, int32_t y) } sideDirection = (direction + 1) & 3; - gMapSelectionTiles[0].x = mapX; - gMapSelectionTiles[0].y = mapY; - gMapSelectionTiles[1].x = mapX + CoordsDirectionDelta[sideDirection].x; - gMapSelectionTiles[1].y = mapY + CoordsDirectionDelta[sideDirection].y; - gMapSelectionTiles[2].x = mapX - CoordsDirectionDelta[sideDirection].x; - gMapSelectionTiles[2].y = mapY - CoordsDirectionDelta[sideDirection].y; - gMapSelectionTiles[3].x = -1; - gMapSelectionTiles[3].y = -1; + gMapSelectionTiles.clear(); + gMapSelectionTiles.push_back({ mapX, mapY }); + gMapSelectionTiles.push_back( + { mapX + CoordsDirectionDelta[sideDirection].x, mapY + CoordsDirectionDelta[sideDirection].y }); + gMapSelectionTiles.push_back( + { mapX - CoordsDirectionDelta[sideDirection].x, mapY - CoordsDirectionDelta[sideDirection].y }); gMapSelectArrowPosition.x = mapX; gMapSelectArrowPosition.y = mapY; @@ -1316,7 +1314,7 @@ static void window_map_place_park_entrance_tool_down(int32_t x, int32_t y) money32 price = place_park_entrance(mapX, mapY, mapZ, direction); if (price != MONEY32_UNDEFINED) { - audio_play_sound_at_location(SOUND_PLACE_ITEM, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z); + audio_play_sound_at_location(SoundId::PlaceItem, { gCommandPosition.x, gCommandPosition.y, gCommandPosition.z }); } } } @@ -1340,7 +1338,7 @@ static void window_map_set_peep_spawn_tool_down(int32_t x, int32_t y) bool result = place_peep_spawn({ mapX, mapY, mapZ, (uint8_t)direction }); if (result) { - audio_play_sound_at_location(SOUND_PLACE_ITEM, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z); + audio_play_sound_at_location(SoundId::PlaceItem, { gCommandPosition.x, gCommandPosition.y, gCommandPosition.z }); } } @@ -1541,17 +1539,24 @@ static constexpr const uint8_t RideColourKey[] = { static uint16_t map_window_get_pixel_colour_peep(CoordsXY c) { - TileElement* tileElement = map_get_surface_element_at(c); - uint16_t colour = TerrainColour[tileElement->AsSurface()->GetSurfaceStyle()]; - if (tileElement->AsSurface()->GetWaterHeight() > 0) + auto* surfaceElement = map_get_surface_element_at(c); + uint16_t colour = TerrainColour[surfaceElement->GetSurfaceStyle()]; + if (surfaceElement->GetWaterHeight() > 0) colour = WaterColour; - if (!(tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED)) + if (!(surfaceElement->GetOwnership() & OWNERSHIP_OWNED)) colour = MAP_COLOUR_UNOWNED(colour); const int32_t maxSupportedTileElementType = (int32_t)std::size(ElementTypeAddColour); + auto tileElement = reinterpret_cast(surfaceElement); while (!(tileElement++)->IsLastForTile()) { + if (tileElement->IsGhost()) + { + colour = MAP_COLOUR(PALETTE_INDEX_21); + break; + } + int32_t tileElementType = tileElement->GetType() >> 2; if (tileElementType >= maxSupportedTileElementType) { @@ -1571,9 +1576,15 @@ static uint16_t map_window_get_pixel_colour_ride(CoordsXY c) uint16_t colourB = MAP_COLOUR(PALETTE_INDEX_13); // surface colour (dark grey) // as an improvement we could use first_element to show underground stuff? - TileElement* tileElement = map_get_surface_element_at(c); + TileElement* tileElement = reinterpret_cast(map_get_surface_element_at(c)); do { + if (tileElement->IsGhost()) + { + colourA = MAP_COLOUR(PALETTE_INDEX_21); + break; + } + switch (tileElement->GetType()) { case TILE_ELEMENT_TYPE_SURFACE: @@ -1590,11 +1601,13 @@ static uint16_t map_window_get_pixel_colour_ride(CoordsXY c) if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) break; ride = get_ride(tileElement->AsEntrance()->GetRideIndex()); - colourA = RideKeyColours[RideColourKey[ride->type]]; + if (ride != nullptr) + colourA = RideKeyColours[RideColourKey[ride->type]]; break; case TILE_ELEMENT_TYPE_TRACK: ride = get_ride(tileElement->AsTrack()->GetRideIndex()); - colourA = RideKeyColours[RideColourKey[ride->type]]; + if (ride != nullptr) + colourA = RideKeyColours[RideColourKey[ride->type]]; break; } } while (!(tileElement++)->IsLastForTile()); diff --git a/src/openrct2-ui/windows/MapGen.cpp b/src/openrct2-ui/windows/MapGen.cpp index 39ee2ffa29..4f8e39edd1 100644 --- a/src/openrct2-ui/windows/MapGen.cpp +++ b/src/openrct2-ui/windows/MapGen.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -534,7 +534,7 @@ rct_window* window_mapgen_open() w->frame_no = 0; w->page = WINDOW_MAPGEN_PAGE_BASE; - window_invalidate(w); + w->Invalidate(); w->widgets = PageWidgets[WINDOW_MAPGEN_PAGE_BASE]; w->enabled_widgets = PageEnabledWidgets[WINDOW_MAPGEN_PAGE_BASE]; w->hold_down_widgets = HoldDownWidgets[WINDOW_MAPGEN_PAGE_BASE]; @@ -616,27 +616,27 @@ static void window_mapgen_base_mousedown(rct_window* w, rct_widgetindex widgetIn { case WIDX_MAP_SIZE_UP: _mapSize = std::min(_mapSize + 1, MAXIMUM_MAP_SIZE_TECHNICAL); - window_invalidate(w); + w->Invalidate(); break; case WIDX_MAP_SIZE_DOWN: _mapSize = std::max(_mapSize - 1, MINIMUM_MAP_SIZE_TECHNICAL); - window_invalidate(w); + w->Invalidate(); break; case WIDX_BASE_HEIGHT_UP: _baseHeight = std::min(_baseHeight + 2, BASESIZE_MAX); - window_invalidate(w); + w->Invalidate(); break; case WIDX_BASE_HEIGHT_DOWN: _baseHeight = std::max(_baseHeight - 2, BASESIZE_MIN); - window_invalidate(w); + w->Invalidate(); break; case WIDX_WATER_LEVEL_UP: _waterLevel = std::min(_waterLevel + 2, WATERLEVEL_MAX); - window_invalidate(w); + w->Invalidate(); break; case WIDX_WATER_LEVEL_DOWN: _waterLevel = std::max(_waterLevel - 2, WATERLEVEL_MIN); - window_invalidate(w); + w->Invalidate(); break; case WIDX_FLOOR_TEXTURE: land_tool_show_surface_style_dropdown(w, widget, _floorTexture); @@ -668,7 +668,7 @@ static void window_mapgen_base_dropdown(rct_window* w, rct_widgetindex widgetInd gLandToolTerrainSurface = type; _floorTexture = type; } - window_invalidate(w); + w->Invalidate(); break; case WIDX_WALL_TEXTURE: if (dropdownIndex == -1) @@ -685,7 +685,7 @@ static void window_mapgen_base_dropdown(rct_window* w, rct_widgetindex widgetInd gLandToolTerrainEdge = type; _wallTexture = type; } - window_invalidate(w); + w->Invalidate(); break; } } @@ -729,7 +729,7 @@ static void window_mapgen_textinput(rct_window* w, rct_widgetindex widgetIndex, break; } - window_invalidate(w); + w->Invalidate(); } static void window_mapgen_base_invalidate(rct_window* w) @@ -916,55 +916,55 @@ static void window_mapgen_simplex_mousedown(rct_window* w, rct_widgetindex widge { case WIDX_SIMPLEX_LOW_UP: _simplex_low = std::min(_simplex_low + 1, 24); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_LOW_DOWN: _simplex_low = std::max(_simplex_low - 1, 0); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_HIGH_UP: _simplex_high = std::min(_simplex_high + 1, 36); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_HIGH_DOWN: _simplex_high = std::max(_simplex_high - 1, 0); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_BASE_FREQ_UP: _simplex_base_freq = std::min(_simplex_base_freq + 5, 1000); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_BASE_FREQ_DOWN: _simplex_base_freq = std::max(_simplex_base_freq - 5, 0); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_OCTAVES_UP: _simplex_octaves = std::min(_simplex_octaves + 1, 10); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_OCTAVES_DOWN: _simplex_octaves = std::max(_simplex_octaves - 1, 1); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_MAP_SIZE_UP: _mapSize = std::min(_mapSize + 1, MAXIMUM_MAP_SIZE_TECHNICAL); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_MAP_SIZE_DOWN: _mapSize = std::max(_mapSize - 1, MINIMUM_MAP_SIZE_TECHNICAL); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_WATER_LEVEL_UP: _waterLevel = std::min(_waterLevel + 2, 54); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_WATER_LEVEL_DOWN: _waterLevel = std::max(_waterLevel - 2, 0); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_RANDOM_TERRAIN_CHECKBOX: _randomTerrain = !_randomTerrain; - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_FLOOR_TEXTURE: land_tool_show_surface_style_dropdown(w, widget, _floorTexture); @@ -974,7 +974,7 @@ static void window_mapgen_simplex_mousedown(rct_window* w, rct_widgetindex widge break; case WIDX_SIMPLEX_PLACE_TREES_CHECKBOX: _placeTrees ^= 1; - window_invalidate(w); + w->Invalidate(); break; } } @@ -1000,7 +1000,7 @@ static void window_mapgen_simplex_dropdown(rct_window* w, rct_widgetindex widget gLandToolTerrainSurface = type; _floorTexture = type; } - window_invalidate(w); + w->Invalidate(); break; case WIDX_SIMPLEX_WALL_TEXTURE: if (dropdownIndex == -1) @@ -1017,7 +1017,7 @@ static void window_mapgen_simplex_dropdown(rct_window* w, rct_widgetindex widget gLandToolTerrainEdge = type; _wallTexture = type; } - window_invalidate(w); + w->Invalidate(); break; } } @@ -1356,7 +1356,7 @@ static void window_mapgen_set_page(rct_window* w, int32_t page) } window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); } static void window_mapgen_set_pressed_tab(rct_window* w) diff --git a/src/openrct2-ui/windows/MapTooltip.cpp b/src/openrct2-ui/windows/MapTooltip.cpp index 0963afc765..30ea592bd1 100644 --- a/src/openrct2-ui/windows/MapTooltip.cpp +++ b/src/openrct2-ui/windows/MapTooltip.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -128,13 +128,13 @@ static void window_map_tooltip_open() if (w == nullptr) { w = window_create( - x, y, width, height, &window_map_tooltip_events, WC_MAP_TOOLTIP, + ScreenCoordsXY(x, y), width, height, &window_map_tooltip_events, WC_MAP_TOOLTIP, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); w->widgets = window_map_tooltip_widgets; } else { - window_invalidate(w); + w->Invalidate(); w->x = x; w->y = y; w->width = width; @@ -148,7 +148,7 @@ static void window_map_tooltip_open() */ static void window_map_tooltip_update(rct_window* w) { - window_invalidate(w); + w->Invalidate(); } /** diff --git a/src/openrct2-ui/windows/MazeConstruction.cpp b/src/openrct2-ui/windows/MazeConstruction.cpp index 9f60d6146f..f11d2bc231 100644 --- a/src/openrct2-ui/windows/MazeConstruction.cpp +++ b/src/openrct2-ui/windows/MazeConstruction.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -135,7 +135,8 @@ static void window_maze_construction_construct(int32_t direction); */ rct_window* window_maze_construction_open() { - rct_window* w = window_create(0, 29, 166, 200, &window_maze_construction_events, WC_RIDE_CONSTRUCTION, WF_NO_AUTO_CLOSE); + rct_window* w = window_create( + ScreenCoordsXY(0, 29), 166, 200, &window_maze_construction_events, WC_RIDE_CONSTRUCTION, WF_NO_AUTO_CLOSE); w->widgets = window_maze_construction_widgets; w->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_MAZE_BUILD_MODE) | (1ULL << WIDX_MAZE_MOVE_MODE) | (1ULL << WIDX_MAZE_FILL_MODE) | (1ULL << WIDX_MAZE_DIRECTION_NW) | (1ULL << WIDX_MAZE_DIRECTION_NE) @@ -170,18 +171,21 @@ static void window_maze_construction_close(rct_window* w) hide_gridlines(); auto ride = get_ride(_currentRideIndex); - if (ride->overall_view.xy == RCT_XY8_UNDEFINED) + if (ride != nullptr) { - int32_t savedPausedState = gGamePaused; - gGamePaused = 0; - ride_action_modify(ride, RIDE_MODIFY_DEMOLISH, GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); - gGamePaused = savedPausedState; - } - else - { - auto intent = Intent(WC_RIDE); - intent.putExtra(INTENT_EXTRA_RIDE_ID, ride->id); - context_open_intent(&intent); + if (ride->overall_view.xy == RCT_XY8_UNDEFINED) + { + int32_t savedPausedState = gGamePaused; + gGamePaused = 0; + ride_action_modify(ride, RIDE_MODIFY_DEMOLISH, GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); + gGamePaused = savedPausedState; + } + else + { + auto intent = Intent(WC_RIDE); + intent.putExtra(INTENT_EXTRA_RIDE_ID, ride->id); + context_open_intent(&intent); + } } } @@ -288,7 +292,7 @@ static void window_maze_construction_mousedown(rct_window* w, rct_widgetindex wi */ static void window_maze_construction_update(rct_window* w) { - Ride* ride = get_ride(_currentRideIndex); + auto ride = get_ride(_currentRideIndex); if (ride == nullptr || ride->status != RIDE_STATUS_CLOSED) { window_close(w); @@ -336,11 +340,11 @@ static void window_maze_construction_toolupdate(rct_window* w, rct_widgetindex w switch (widgetIndex) { case WIDX_MAZE_DIRECTION_GROUPBOX: - ride_construction_toolupdate_construct(x, y); + ride_construction_toolupdate_construct(ScreenCoordsXY(x, y)); break; case WIDX_MAZE_ENTRANCE: case WIDX_MAZE_EXIT: - ride_construction_toolupdate_entrance_exit(x, y); + ride_construction_toolupdate_entrance_exit(ScreenCoordsXY(x, y)); break; } } @@ -374,10 +378,10 @@ static void window_maze_construction_entrance_tooldown(int32_t x, int32_t y, rct if (result->Error != GA_ERROR::OK) return; - audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z); + audio_play_sound_at_location(SoundId::PlaceItem, result->Position); - Ride* ride = get_ride(rideIndex); - if (ride_are_all_possible_entrances_and_exits_built(ride)) + auto ride = get_ride(rideIndex); + if (ride != nullptr && ride_are_all_possible_entrances_and_exits_built(ride)) { tool_cancel(); if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_NO_TRACK)) @@ -402,7 +406,7 @@ static void window_maze_construction_tooldown(rct_window* w, rct_widgetindex wid switch (widgetIndex) { case WIDX_MAZE_DIRECTION_GROUPBOX: - ride_construction_tooldown_construct(x, y); + ride_construction_tooldown_construct(ScreenCoordsXY(x, y)); break; case WIDX_MAZE_ENTRANCE: case WIDX_MAZE_EXIT: @@ -417,11 +421,15 @@ static void window_maze_construction_tooldown(rct_window* w, rct_widgetindex wid */ static void window_maze_construction_invalidate(rct_window* w) { - Ride* ride = get_ride(_currentRideIndex); - - // Set window title arguments - set_format_arg(4, rct_string_id, ride->name); - set_format_arg(6, uint32_t, ride->name_arguments); + auto ride = get_ride(_currentRideIndex); + if (ride != nullptr) + { + ride->FormatNameTo(gCommonFormatArgs + 4); + } + else + { + set_format_arg(4, rct_string_id, STR_NONE); + } } /** @@ -464,7 +472,7 @@ void window_maze_construction_update_pressed_widgets() } w->pressed_widgets = pressedWidgets; - window_invalidate(w); + w->Invalidate(); } /** @@ -507,6 +515,6 @@ static void window_maze_construction_construct(int32_t direction) _currentTrackBegin.y = y; if (_rideConstructionState != RIDE_CONSTRUCTION_STATE_MAZE_MOVE) { - audio_play_sound_at_location(SOUND_PLACE_ITEM, x, y, z); + audio_play_sound_at_location(SoundId::PlaceItem, { x, y, z }); } } diff --git a/src/openrct2-ui/windows/Multiplayer.cpp b/src/openrct2-ui/windows/Multiplayer.cpp index c9cbfc04c5..f33b7167d5 100644 --- a/src/openrct2-ui/windows/Multiplayer.cpp +++ b/src/openrct2-ui/windows/Multiplayer.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -333,7 +334,7 @@ static void window_multiplayer_set_page(rct_window* w, int32_t page) window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); } static void window_multiplayer_anchor_border_widgets(rct_window* w) @@ -560,7 +561,7 @@ static void window_multiplayer_players_resize(rct_window* w) w->widgets[WIDX_HEADER_PING].right = w->width - 5; w->selected_list_item = -1; - window_invalidate(w); + w->Invalidate(); } static void window_multiplayer_players_update(rct_window* w) @@ -576,7 +577,7 @@ static void window_multiplayer_players_scrollgetsize(rct_window* w, int32_t scro if (w->selected_list_item != -1) { w->selected_list_item = -1; - window_invalidate(w); + w->Invalidate(); } *height = network_get_num_players() * SCROLLABLE_ROW_HEIGHT; @@ -586,7 +587,7 @@ static void window_multiplayer_players_scrollgetsize(rct_window* w, int32_t scro if (i < w->scrolls[0].v_top) { w->scrolls[0].v_top = i; - window_invalidate(w); + w->Invalidate(); } } @@ -599,7 +600,7 @@ static void window_multiplayer_players_scrollmousedown(rct_window* w, int32_t sc return; w->selected_list_item = index; - window_invalidate(w); + w->Invalidate(); window_player_open(network_get_player_id(index)); } @@ -613,7 +614,7 @@ static void window_multiplayer_players_scrollmouseover(rct_window* w, int32_t sc return; w->selected_list_item = index; - window_invalidate(w); + w->Invalidate(); } static void window_multiplayer_players_invalidate(rct_window* w) @@ -741,11 +742,17 @@ static void window_multiplayer_groups_mouseup(rct_window* w, rct_widgetindex wid } break; case WIDX_ADD_GROUP: - game_do_command(0, GAME_COMMAND_FLAG_APPLY, 0, 0, GAME_COMMAND_MODIFY_GROUPS, 0, 0); - break; + { + auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::AddGroup); + GameActions::Execute(&networkModifyGroup); + } + break; case WIDX_REMOVE_GROUP: - game_do_command(1 | (_selectedGroup << 8), GAME_COMMAND_FLAG_APPLY, 0, 0, GAME_COMMAND_MODIFY_GROUPS, 0, 0); - break; + { + auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::RemoveGroup, _selectedGroup); + GameActions::Execute(&networkModifyGroup); + } + break; case WIDX_RENAME_GROUP:; int32_t groupIndex = network_get_group_index(_selectedGroup); const utf8* groupName = network_get_group_name(groupIndex); @@ -762,7 +769,7 @@ static void window_multiplayer_groups_resize(rct_window* w) w->list_item_positions[0] = 0; w->selected_list_item = -1; - window_invalidate(w); + w->Invalidate(); } static void window_multiplayer_groups_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) @@ -788,16 +795,18 @@ static void window_multiplayer_groups_dropdown(rct_window* w, rct_widgetindex wi switch (widgetIndex) { case WIDX_DEFAULT_GROUP_DROPDOWN: - game_do_command( - 4 | (network_get_group_id(dropdownIndex) << 8), GAME_COMMAND_FLAG_APPLY, 0, 0, GAME_COMMAND_MODIFY_GROUPS, 0, - 0); - break; + { + auto networkModifyGroup = NetworkModifyGroupAction( + ModifyGroupType::SetDefault, network_get_group_id(dropdownIndex)); + GameActions::Execute(&networkModifyGroup); + } + break; case WIDX_SELECTED_GROUP_DROPDOWN: _selectedGroup = network_get_group_id(dropdownIndex); break; } - window_invalidate(w); + w->Invalidate(); } static void window_multiplayer_groups_update(rct_window* w) @@ -813,7 +822,7 @@ static void window_multiplayer_groups_scrollgetsize(rct_window* w, int32_t scrol if (w->selected_list_item != -1) { w->selected_list_item = -1; - window_invalidate(w); + w->Invalidate(); } *height = network_get_num_actions() * SCROLLABLE_ROW_HEIGHT; @@ -823,7 +832,7 @@ static void window_multiplayer_groups_scrollgetsize(rct_window* w, int32_t scrol if (i < w->scrolls[0].v_top) { w->scrolls[0].v_top = i; - window_invalidate(w); + w->Invalidate(); } } @@ -836,9 +845,11 @@ static void window_multiplayer_groups_scrollmousedown(rct_window* w, int32_t scr return; w->selected_list_item = index; - window_invalidate(w); + w->Invalidate(); - game_do_command(2 | (_selectedGroup << 8), GAME_COMMAND_FLAG_APPLY, index, 0, GAME_COMMAND_MODIFY_GROUPS, 0, 0); + auto networkModifyGroup = NetworkModifyGroupAction( + ModifyGroupType::SetPermissions, _selectedGroup, "", index, PermissionState::Toggle); + GameActions::Execute(&networkModifyGroup); } static void window_multiplayer_groups_scrollmouseover(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) @@ -850,7 +861,7 @@ static void window_multiplayer_groups_scrollmouseover(rct_window* w, int32_t scr return; w->selected_list_item = index; - window_invalidate(w); + w->Invalidate(); } static void window_multiplayer_groups_text_input(rct_window* w, rct_widgetindex widgetIndex, char* text) @@ -861,15 +872,8 @@ static void window_multiplayer_groups_text_input(rct_window* w, rct_widgetindex if (text == nullptr) return; - game_do_command( - 3 | (_selectedGroup << 8) | (1 << 16), GAME_COMMAND_FLAG_APPLY, w->number, *((int32_t*)(text + 0)), - GAME_COMMAND_MODIFY_GROUPS, *((int32_t*)(text + 8)), *((int32_t*)(text + 4))); - game_do_command( - 3 | (_selectedGroup << 8) | (2 << 16), GAME_COMMAND_FLAG_APPLY, w->number, *((int32_t*)(text + 12)), - GAME_COMMAND_MODIFY_GROUPS, *((int32_t*)(text + 20)), *((int32_t*)(text + 16))); - game_do_command( - 3 | (_selectedGroup << 8) | (0 << 16), GAME_COMMAND_FLAG_APPLY, w->number, *((int32_t*)(text + 24)), - GAME_COMMAND_MODIFY_GROUPS, *((int32_t*)(text + 32)), *((int32_t*)(text + 28))); + auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::SetName, _selectedGroup, text); + GameActions::Execute(&networkModifyGroup); } static void window_multiplayer_groups_invalidate(rct_window* w) diff --git a/src/openrct2-ui/windows/MusicCredits.cpp b/src/openrct2-ui/windows/MusicCredits.cpp index a24e7a58cb..0b4907f75e 100644 --- a/src/openrct2-ui/windows/MusicCredits.cpp +++ b/src/openrct2-ui/windows/MusicCredits.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/windows/Network.cpp b/src/openrct2-ui/windows/Network.cpp index 37b689da6e..9371195c70 100644 --- a/src/openrct2-ui/windows/Network.cpp +++ b/src/openrct2-ui/windows/Network.cpp @@ -186,7 +186,7 @@ static void window_network_set_page(rct_window* w, int32_t page) window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); } static void window_network_anchor_border_widgets(rct_window* w) @@ -237,7 +237,7 @@ static void window_network_information_update(rct_window* w) { w->frame_no++; widget_invalidate(w, WIDX_TAB1 + w->page); - window_invalidate(w); + w->Invalidate(); NetworkStats_t curStats = network_get_stats(); diff --git a/src/openrct2-ui/windows/NetworkStatus.cpp b/src/openrct2-ui/windows/NetworkStatus.cpp index c78a2ecfb9..22d55383f5 100644 --- a/src/openrct2-ui/windows/NetworkStatus.cpp +++ b/src/openrct2-ui/windows/NetworkStatus.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/windows/NewCampaign.cpp b/src/openrct2-ui/windows/NewCampaign.cpp index 9c2ca6339f..9397f80000 100644 --- a/src/openrct2-ui/windows/NewCampaign.cpp +++ b/src/openrct2-ui/windows/NewCampaign.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -89,30 +89,37 @@ static rct_window_event_list window_new_campaign_events = { }; // clang-format on -static uint8_t window_new_campaign_rides[MAX_RIDES]; +static std::vector window_new_campaign_rides; static uint8_t window_new_campaign_shop_items[64]; static int32_t ride_value_compare(const void* a, const void* b) { - Ride *rideA, *rideB; + auto valueA = 0; + auto rideA = get_ride(*((uint8_t*)a)); + if (rideA != nullptr) + valueA = rideA->value; - rideA = get_ride(*((uint8_t*)a)); - rideB = get_ride(*((uint8_t*)b)); - return rideB->value - rideA->value; + auto valueB = 0; + auto rideB = get_ride(*((uint8_t*)b)); + if (rideB != nullptr) + valueB = rideB->value; + + return valueB - valueA; } static int32_t ride_name_compare(const void* a, const void* b) { - char rideAName[256], rideBName[256]; - Ride *rideA, *rideB; + std::string rideAName; + auto rideA = get_ride(*((uint8_t*)a)); + if (rideA != nullptr) + rideAName = rideA->GetName(); - rideA = get_ride(*((uint8_t*)a)); - rideB = get_ride(*((uint8_t*)b)); + std::string rideBName; + auto rideB = get_ride(*((uint8_t*)b)); + if (rideB != nullptr) + rideBName = rideB->GetName(); - format_string(rideAName, 256, rideA->name, &rideA->name_arguments); - format_string(rideBName, 256, rideB->name, &rideB->name_arguments); - - return _strcmpi(rideAName, rideBName); + return _strcmpi(rideAName.c_str(), rideBName.c_str()); } /** @@ -121,11 +128,7 @@ static int32_t ride_name_compare(const void* a, const void* b) */ rct_window* window_new_campaign_open(int16_t campaignType) { - rct_window* w; - Ride* ride; - int32_t i, numApplicableRides; - - w = window_bring_to_front_by_class(WC_NEW_CAMPAIGN); + auto w = window_bring_to_front_by_class(WC_NEW_CAMPAIGN); if (w != nullptr) { if (w->campaign.campaign_type == campaignType) @@ -153,36 +156,30 @@ rct_window* window_new_campaign_open(int16_t campaignType) w->campaign.ride_id = SELECTED_RIDE_UNDEFINED; // Get all applicable rides - numApplicableRides = 0; - window_new_campaign_rides[0] = RIDE_ID_NULL; - FOR_ALL_RIDES (i, ride) + window_new_campaign_rides.clear(); + for (const auto& ride : GetRideManager()) { - if (ride->status != RIDE_STATUS_OPEN) + if (ride.status == RIDE_STATUS_OPEN) { - continue; + if (!ride_type_has_flag( + ride.type, + RIDE_TYPE_FLAG_IS_SHOP | RIDE_TYPE_FLAG_SELLS_FOOD | RIDE_TYPE_FLAG_SELLS_DRINKS + | RIDE_TYPE_FLAG_IS_BATHROOM)) + { + window_new_campaign_rides.push_back(ride.id); + } } - if (ride_type_has_flag( - ride->type, - RIDE_TYPE_FLAG_IS_SHOP | RIDE_TYPE_FLAG_SELLS_FOOD | RIDE_TYPE_FLAG_SELLS_DRINKS | RIDE_TYPE_FLAG_IS_BATHROOM)) - { - continue; - } - - window_new_campaign_rides[numApplicableRides++] = i; } // Take top 128 most valuable rides - if (numApplicableRides > DROPDOWN_ITEMS_MAX_SIZE) + if (window_new_campaign_rides.size() > DROPDOWN_ITEMS_MAX_SIZE) { - qsort(window_new_campaign_rides, numApplicableRides, sizeof(uint8_t), ride_value_compare); - numApplicableRides = DROPDOWN_ITEMS_MAX_SIZE; + qsort(window_new_campaign_rides.data(), window_new_campaign_rides.size(), sizeof(ride_id_t), ride_value_compare); + window_new_campaign_rides.resize(DROPDOWN_ITEMS_MAX_SIZE); } // Sort rides by name - qsort(window_new_campaign_rides, numApplicableRides, sizeof(uint8_t), ride_name_compare); - - window_new_campaign_rides[numApplicableRides] = RIDE_ID_NULL; - + qsort(window_new_campaign_rides.data(), window_new_campaign_rides.size(), sizeof(ride_id_t), ride_name_compare); return w; } @@ -192,25 +189,23 @@ rct_window* window_new_campaign_open(int16_t campaignType) */ static void window_new_campaign_get_shop_items() { - int32_t i, numItems; - Ride* ride; - uint64_t items = 0; - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - if (rideEntry == nullptr) + auto rideEntry = ride.GetRideEntry(); + if (rideEntry != nullptr) { - continue; + auto itemType = rideEntry->shop_item; + if (itemType != SHOP_ITEM_NONE && shop_item_is_food_or_drink(itemType)) + { + items |= 1ULL << itemType; + } } - uint8_t itemType = rideEntry->shop_item; - if (itemType != SHOP_ITEM_NONE && shop_item_is_food_or_drink(itemType)) - items |= 1ULL << itemType; } // - numItems = 0; - for (i = 0; i < 64; i++) + auto numItems = 0; + for (auto i = 0; i < 64; i++) { if (items & (1ULL << i)) { @@ -271,7 +266,7 @@ static void window_new_campaign_mousedown(rct_window* w, rct_widgetindex widgetI break; gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = ShopItemStringIds[window_new_campaign_shop_items[i]].plural; + gDropdownItemsArgs[i] = ShopItems[window_new_campaign_shop_items[i]].Naming.Plural; numItems++; } @@ -284,15 +279,25 @@ static void window_new_campaign_mousedown(rct_window* w, rct_widgetindex widgetI else { int32_t numItems = 0; - for (int32_t i = 0; i < DROPDOWN_ITEMS_MAX_SIZE; i++) + for (auto rideId : window_new_campaign_rides) { - if (window_new_campaign_rides[i] == RIDE_ID_NULL) - break; - - Ride* ride = get_ride(window_new_campaign_rides[i]); - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = ((uint64_t)ride->name_arguments << 16ULL) | ride->name; - numItems++; + auto ride = get_ride(rideId); + if (ride != nullptr) + { + // HACK until dropdown items have longer argument buffers + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + if (ride->custom_name.empty()) + { + ride->FormatNameTo(&gDropdownItemsArgs[numItems]); + } + else + { + gDropdownItemsFormat[numItems] = STR_OPTIONS_DROPDOWN_ITEM; + set_format_arg_on( + (uint8_t*)&gDropdownItemsArgs[numItems], 0, const char*, ride->custom_name.c_str()); + } + numItems++; + } } window_dropdown_show_text_custom_width( @@ -303,11 +308,11 @@ static void window_new_campaign_mousedown(rct_window* w, rct_widgetindex widgetI // In RCT2, the maximum was 6 weeks case WIDX_WEEKS_INCREASE_BUTTON: w->campaign.no_weeks = std::min(w->campaign.no_weeks + 1, 12); - window_invalidate(w); + w->Invalidate(); break; case WIDX_WEEKS_DECREASE_BUTTON: w->campaign.no_weeks = std::max(w->campaign.no_weeks - 1, 2); - window_invalidate(w); + w->Invalidate(); break; } } @@ -321,7 +326,7 @@ static void window_new_campaign_dropdown(rct_window* w, rct_widgetindex widgetIn if (widgetIndex != WIDX_RIDE_DROPDOWN_BUTTON) return; - if (dropdownIndex == -1) + if (dropdownIndex < 0 || (size_t)dropdownIndex >= window_new_campaign_rides.size()) return; if (w->campaign.campaign_type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE) @@ -333,7 +338,7 @@ static void window_new_campaign_dropdown(rct_window* w, rct_widgetindex widgetIn w->campaign.ride_id = window_new_campaign_rides[dropdownIndex]; } - window_invalidate(w); + w->Invalidate(); } /** @@ -356,9 +361,12 @@ static void window_new_campaign_invalidate(rct_window* w) window_new_campaign_widgets[WIDX_RIDE_LABEL].text = STR_MARKETING_RIDE; if (w->campaign.ride_id != SELECTED_RIDE_UNDEFINED) { - Ride* ride = get_ride(w->campaign.ride_id); - window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].text = ride->name; - set_format_arg(0, uint32_t, ride->name_arguments); + auto ride = get_ride(w->campaign.ride_id); + if (ride != nullptr) + { + window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].text = STR_STRINGID; + ride->FormatNameTo(gCommonFormatArgs); + } } break; case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE: @@ -368,7 +376,7 @@ static void window_new_campaign_invalidate(rct_window* w) window_new_campaign_widgets[WIDX_RIDE_LABEL].text = STR_MARKETING_ITEM; if (w->campaign.ride_id != SELECTED_RIDE_UNDEFINED) { - window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].text = ShopItemStringIds[w->campaign.ride_id].plural; + window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].text = ShopItems[w->campaign.ride_id].Naming.Plural; } break; } diff --git a/src/openrct2-ui/windows/NewRide.cpp b/src/openrct2-ui/windows/NewRide.cpp index 35ea8c477f..4867a9368f 100644 --- a/src/openrct2-ui/windows/NewRide.cpp +++ b/src/openrct2-ui/windows/NewRide.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -589,7 +589,7 @@ static void window_new_ride_set_page(rct_window* w, int32_t page) } window_new_ride_refresh_widget_sizing(w); - window_invalidate(w); + w->Invalidate(); if (page < WINDOW_NEW_RIDE_PAGE_RESEARCH) { @@ -633,7 +633,7 @@ static void window_new_ride_refresh_widget_sizing(rct_window* w) // Handle new window size if (w->width != width || w->height != height) { - window_invalidate(w); + w->Invalidate(); // Resize widgets to new window size window_new_ride_widgets[WIDX_BACKGROUND].right = width - 1; @@ -646,7 +646,7 @@ static void window_new_ride_refresh_widget_sizing(rct_window* w) w->width = width; w->height = height; - window_invalidate(w); + w->Invalidate(); } window_init_scroll_widgets(w); @@ -774,9 +774,9 @@ static void window_new_ride_scrollmousedown(rct_window* w, int32_t scrollIndex, _windowNewRideHighlightedItem[_windowNewRideCurrentTab] = item; w->new_ride.selected_ride_id = item.ride_type_and_entry; - audio_play_sound(SOUND_CLICK_1, 0, w->x + (w->width / 2)); + audio_play_sound(SoundId::Click1, 0, w->x + (w->width / 2)); w->new_ride.selected_ride_countdown = 8; - window_invalidate(w); + w->Invalidate(); } /** @@ -797,7 +797,7 @@ static void window_new_ride_scrollmouseover(rct_window* w, int32_t scrollIndex, w->new_ride.highlighted_ride_id = item.ride_type_and_entry; _windowNewRideHighlightedItem[_windowNewRideCurrentTab] = item; - window_invalidate(w); + w->Invalidate(); } /** @@ -932,8 +932,7 @@ static ride_list_item window_new_ride_scroll_get_ride_list_item_at(rct_window* w static int32_t get_num_track_designs(ride_list_item item) { - char entry[DAT_NAME_LENGTH + 1]; - const char* entryPtr = nullptr; + std::string entryName; rct_ride_entry* rideEntry = nullptr; if (item.type < 0x80) @@ -941,22 +940,20 @@ static int32_t get_num_track_designs(ride_list_item item) rideEntry = get_ride_entry(item.entry_index); if (RideGroupManager::RideTypeIsIndependent(item.type)) { - get_ride_entry_name(entry, item.entry_index); - entryPtr = entry; + entryName = get_ride_entry_name(item.entry_index); } } - ITrackDesignRepository* repo = OpenRCT2::GetContext()->GetTrackDesignRepository(); - + auto repo = OpenRCT2::GetContext()->GetTrackDesignRepository(); if (rideEntry != nullptr && RideGroupManager::RideTypeHasRideGroups(item.type)) { - const RideGroup* rideGroup = RideGroupManager::GetRideGroup(item.type, rideEntry); - return (int32_t)repo->GetCountForRideGroup(item.type, rideGroup); - } - else - { - return (int32_t)repo->GetCountForObjectEntry(item.type, String::ToStd(entryPtr)); + auto rideGroup = RideGroupManager::GetRideGroup(item.type, rideEntry); + if (rideGroup != nullptr) + { + return (int32_t)repo->GetCountForRideGroup(item.type, rideGroup); + } } + return (int32_t)repo->GetCountForObjectEntry(item.type, entryName); } /** diff --git a/src/openrct2-ui/windows/News.cpp b/src/openrct2-ui/windows/News.cpp index e642ec077e..7eb4eb35b2 100644 --- a/src/openrct2-ui/windows/News.cpp +++ b/src/openrct2-ui/windows/News.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -143,8 +143,8 @@ static void window_news_update(rct_window* w) return; } - window_invalidate(w); - audio_play_sound(SOUND_CLICK_2, 0, w->x + (w->width / 2)); + w->Invalidate(); + audio_play_sound(SoundId::Click2, 0, w->x + (w->width / 2)); j = w->news.var_480; w->news.var_480 = -1; @@ -237,8 +237,8 @@ static void window_news_scrollmousedown(rct_window* w, int32_t scrollIndex, int3 w->news.var_480 = i - 11; w->news.var_482 = buttonIndex; w->news.var_484 = 4; - window_invalidate(w); - audio_play_sound(SOUND_CLICK_1, 0, w->x + (w->width / 2)); + w->Invalidate(); + audio_play_sound(SoundId::Click1, 0, w->x + (w->width / 2)); } } diff --git a/src/openrct2-ui/windows/NewsOptions.cpp b/src/openrct2-ui/windows/NewsOptions.cpp index ec47cbd4a7..bbd8dcbfbb 100644 --- a/src/openrct2-ui/windows/NewsOptions.cpp +++ b/src/openrct2-ui/windows/NewsOptions.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -244,11 +244,11 @@ static void window_news_options_invalidate(rct_window* w) if (w->height != y) { - window_invalidate(w); + w->Invalidate(); w->height = y; w->widgets[WIDX_BACKGROUND].bottom = y - 1; w->widgets[WIDX_TAB_CONTENT_PANEL].bottom = y - 1; - window_invalidate(w); + w->Invalidate(); } } @@ -264,7 +264,7 @@ static void window_news_options_set_page(rct_window* w, int32_t page) { w->page = page; w->frame_no = 0; - window_invalidate(w); + w->Invalidate(); } } diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index 3624587123..fb742d845a 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -30,18 +30,44 @@ class ObjectDownloader private: static constexpr auto OPENRCT2_API_LEGACY_OBJECT_URL = "https://api.openrct2.io/objects/legacy/"; + struct DownloadStatusInfo + { + std::string Name; + std::string Source; + size_t Count{}; + size_t Total{}; + + bool operator==(const DownloadStatusInfo& rhs) + { + return Name == rhs.Name && Source == rhs.Source && Count == rhs.Count && Total == rhs.Total; + } + bool operator!=(const DownloadStatusInfo& rhs) + { + return !(*this == rhs); + } + }; + std::vector _entries; std::vector _downloadedEntries; size_t _currentDownloadIndex{}; std::mutex _downloadedEntriesMutex; + std::mutex _queueMutex; bool _nextDownloadQueued{}; + DownloadStatusInfo _lastDownloadStatusInfo; + DownloadStatusInfo _downloadStatusInfo; + std::mutex _downloadStatusInfoMutex; + std::string _lastDownloadSource; + // TODO static due to INTENT_EXTRA_CALLBACK not allowing a std::function inline static bool _downloadingObjects; public: void Begin(const std::vector& entries) { + _lastDownloadStatusInfo = {}; + _downloadStatusInfo = {}; + _lastDownloadSource = {}; _entries = entries; _currentDownloadIndex = 0; _downloadingObjects = true; @@ -61,40 +87,74 @@ public: void Update() { + std::lock_guard guard(_queueMutex); if (_nextDownloadQueued) { _nextDownloadQueued = false; NextDownload(); } + UpdateStatusBox(); } private: - void UpdateProgress(const std::string& name, size_t count, size_t total) + void UpdateStatusBox() { - char str_downloading_objects[256]{}; - uint8_t args[32]{}; - set_format_arg_on(args, 0, int16_t, count); - set_format_arg_on(args, 2, int16_t, total); - set_format_arg_on(args, 4, char*, name.c_str()); + std::lock_guard guard(_downloadStatusInfoMutex); + if (_lastDownloadStatusInfo != _downloadStatusInfo) + { + _lastDownloadStatusInfo = _downloadStatusInfo; - format_string(str_downloading_objects, sizeof(str_downloading_objects), STR_DOWNLOADING_OBJECTS, args); + if (_downloadStatusInfo == DownloadStatusInfo()) + { + context_force_close_window_by_class(WC_NETWORK_STATUS); + } + else + { + char str_downloading_objects[256]{}; + uint8_t args[32]{}; + if (_downloadStatusInfo.Source.empty()) + { + set_format_arg_on(args, 0, int16_t, (int16_t)_downloadStatusInfo.Count); + set_format_arg_on(args, 2, int16_t, (int16_t)_downloadStatusInfo.Total); + set_format_arg_on(args, 4, char*, _downloadStatusInfo.Name.c_str()); + format_string(str_downloading_objects, sizeof(str_downloading_objects), STR_DOWNLOADING_OBJECTS, args); + } + else + { + set_format_arg_on(args, 0, char*, _downloadStatusInfo.Name.c_str()); + set_format_arg_on(args, sizeof(char*), char*, _downloadStatusInfo.Source.c_str()); + set_format_arg_on(args, sizeof(char*) + sizeof(char*), int16_t, (int16_t)_downloadStatusInfo.Count); + set_format_arg_on( + args, sizeof(char*) + sizeof(char*) + sizeof(int16_t), int16_t, (int16_t)_downloadStatusInfo.Total); + format_string(str_downloading_objects, sizeof(str_downloading_objects), STR_DOWNLOADING_OBJECTS_FROM, args); + } - auto intent = Intent(WC_NETWORK_STATUS); - intent.putExtra(INTENT_EXTRA_MESSAGE, std::string(str_downloading_objects)); - intent.putExtra(INTENT_EXTRA_CALLBACK, []() -> void { _downloadingObjects = false; }); - context_open_intent(&intent); + auto intent = Intent(WC_NETWORK_STATUS); + intent.putExtra(INTENT_EXTRA_MESSAGE, std::string(str_downloading_objects)); + intent.putExtra(INTENT_EXTRA_CALLBACK, []() -> void { _downloadingObjects = false; }); + context_open_intent(&intent); + } + } + } + + void UpdateProgress(const DownloadStatusInfo& info) + { + std::lock_guard guard(_downloadStatusInfoMutex); + _downloadStatusInfo = info; } void QueueNextDownload() { + std::lock_guard guard(_queueMutex); _nextDownloadQueued = true; } - void DownloadObject(const rct_object_entry& entry, const std::string name, const std::string_view url) + void DownloadObject(const rct_object_entry& entry, const std::string name, const std::string url) { - using namespace OpenRCT2::Network; + using namespace OpenRCT2::Networking; try { + std::printf("Downloading %s\n", url.c_str()); Http::Request req; req.method = Http::Method::GET; req.url = url; @@ -116,7 +176,7 @@ private: } else { - throw std::runtime_error("Non 200 status"); + std::printf(" Failed to download %s\n", name.c_str()); } QueueNextDownload(); }); @@ -130,21 +190,21 @@ private: void NextDownload() { - using namespace OpenRCT2::Network; + using namespace OpenRCT2::Networking; if (!_downloadingObjects || _currentDownloadIndex >= _entries.size()) { // Finished... _downloadingObjects = false; - context_force_close_window_by_class(WC_NETWORK_STATUS); + UpdateProgress({}); return; } auto& entry = _entries[_currentDownloadIndex]; auto name = String::Trim(std::string(entry.name, sizeof(entry.name))); - UpdateProgress(name, _currentDownloadIndex + 1, (int32_t)_entries.size()); - std::printf("Downloading %s...\n", name.c_str()); + log_verbose("Downloading object: [%s]:", name.c_str()); _currentDownloadIndex++; + UpdateProgress({ name, _lastDownloadSource, _currentDownloadIndex, _entries.size() }); try { Http::Request req; @@ -157,9 +217,12 @@ private: if (jresponse != nullptr) { auto objName = json_string_value(json_object_get(jresponse, "name")); + auto source = json_string_value(json_object_get(jresponse, "source")); auto downloadLink = json_string_value(json_object_get(jresponse, "download")); if (downloadLink != nullptr) { + _lastDownloadSource = source; + UpdateProgress({ name, source, _currentDownloadIndex, _entries.size() }); DownloadObject(entry, objName, downloadLink); } json_decref(jresponse); @@ -386,7 +449,7 @@ rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, window->no_list_items = (uint16_t)numMissingObjects; file_path = path; - window_invalidate(window); + window->Invalidate(); return window; } diff --git a/src/openrct2-ui/windows/Options.cpp b/src/openrct2-ui/windows/Options.cpp index e45f8d35ce..20411fa7db 100644 --- a/src/openrct2-ui/windows/Options.cpp +++ b/src/openrct2-ui/windows/Options.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -87,6 +87,7 @@ enum WINDOW_OPTIONS_WIDGET_IDX { WIDX_STEAM_OVERLAY_PAUSE, WIDX_UNCAP_FPS_CHECKBOX, WIDX_SHOW_FPS_CHECKBOX, + WIDX_MULTITHREADING_CHECKBOX, WIDX_USE_VSYNC_CHECKBOX, WIDX_MINIMIZE_FOCUS_LOSS, @@ -96,6 +97,7 @@ enum WINDOW_OPTIONS_WIDGET_IDX { WIDX_GRIDLINES_CHECKBOX, WIDX_UPPER_CASE_BANNERS_CHECKBOX, WIDX_SHOW_GUEST_PURCHASES_CHECKBOX, + WIDX_TRANSPARENT_SCREENSHOTS_CHECKBOX, WIDX_VIRTUAL_FLOOR_LABEL, WIDX_VIRTUAL_FLOOR, WIDX_VIRTUAL_FLOOR_DROPDOWN, @@ -172,7 +174,6 @@ enum WINDOW_OPTIONS_WIDGET_IDX { // Advanced WIDX_DEBUGGING_TOOLS = WIDX_PAGE_START, - WIDX_TEST_UNFINISHED_TRACKS, WIDX_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM, WIDX_SAVE_PLUGIN_DATA_CHECKBOX, WIDX_STAY_CONNECTED_AFTER_DESYNC, @@ -236,7 +237,8 @@ static rct_widget window_options_display_widgets[] = { { WWT_CHECKBOX, 1, 25, 290, 144, 155, STR_STEAM_OVERLAY_PAUSE, STR_STEAM_OVERLAY_PAUSE_TIP }, // Pause on steam overlay { WWT_CHECKBOX, 1, 11, 153, 161, 172, STR_UNCAP_FPS, STR_UNCAP_FPS_TIP }, // Uncap fps { WWT_CHECKBOX, 1, 155, 290, 161, 172, STR_SHOW_FPS, STR_SHOW_FPS_TIP }, // Show fps - { WWT_CHECKBOX, 1, 11, 290, 176, 187, STR_USE_VSYNC, STR_USE_VSYNC_TIP }, // Use vsync + { WWT_CHECKBOX, 1, 155, 290, 176, 187, STR_MULTITHREADING, STR_MULTITHREADING_TIP }, // Multithreading + { WWT_CHECKBOX, 1, 11, 153, 176, 187, STR_USE_VSYNC, STR_USE_VSYNC_TIP }, // Use vsync { WWT_CHECKBOX, 1, 11, 290, 191, 202, STR_MINIMISE_FULLSCREEN_ON_FOCUS_LOSS, STR_MINIMISE_FULLSCREEN_ON_FOCUS_LOSS_TIP }, // Minimise fullscreen focus loss { WIDGETS_END }, }; @@ -244,16 +246,17 @@ static rct_widget window_options_display_widgets[] = { static rct_widget window_options_rendering_widgets[] = { MAIN_OPTIONS_WIDGETS, #define FRAME_RENDERING_START 53 - { WWT_GROUPBOX, 1, 5, 304, FRAME_RENDERING_START + 0, FRAME_RENDERING_START + 92, STR_RENDERING_GROUP, STR_NONE }, // Rendering group + { WWT_GROUPBOX, 1, 5, 304, FRAME_RENDERING_START + 0, FRAME_RENDERING_START + 107, STR_RENDERING_GROUP, STR_NONE }, // Rendering group { WWT_CHECKBOX, 1, 10, 290, FRAME_RENDERING_START + 15, FRAME_RENDERING_START + 26, STR_TILE_SMOOTHING, STR_TILE_SMOOTHING_TIP }, // Landscape smoothing { WWT_CHECKBOX, 1, 10, 290, FRAME_RENDERING_START + 30, FRAME_RENDERING_START + 41, STR_GRIDLINES, STR_GRIDLINES_TIP }, // Gridlines { WWT_CHECKBOX, 1, 10, 290, FRAME_RENDERING_START + 45, FRAME_RENDERING_START + 56, STR_UPPERCASE_BANNERS, STR_UPPERCASE_BANNERS_TIP }, // Uppercase banners { WWT_CHECKBOX, 1, 10, 290, FRAME_RENDERING_START + 60, FRAME_RENDERING_START + 71, STR_SHOW_GUEST_PURCHASES, STR_SHOW_GUEST_PURCHASES_TIP }, // Guest purchases - { WWT_LABEL, 1, 10, 290, FRAME_RENDERING_START + 75, FRAME_RENDERING_START + 86, STR_VIRTUAL_FLOOR_STYLE, STR_NONE }, // Virtual floor - { WWT_DROPDOWN, 1, 155, 299, FRAME_RENDERING_START + 75, FRAME_RENDERING_START + 86, STR_NONE, STR_VIRTUAL_FLOOR_STYLE_TIP }, // Virtual floor dropdown - { WWT_BUTTON, 1, 288, 298, FRAME_RENDERING_START + 76, FRAME_RENDERING_START + 85, STR_DROPDOWN_GLYPH, STR_VIRTUAL_FLOOR_STYLE_TIP }, // Virtual floor dropdown + { WWT_CHECKBOX, 1, 10, 290, FRAME_RENDERING_START + 75, FRAME_RENDERING_START + 86, STR_TRANSPARENT_SCREENSHOT, STR_TRANSPARENT_SCREENSHOT_TIP }, // Transparent screenshot + { WWT_LABEL, 1, 10, 290, FRAME_RENDERING_START + 90, FRAME_RENDERING_START + 101, STR_VIRTUAL_FLOOR_STYLE, STR_NONE }, // Virtual floor + { WWT_DROPDOWN, 1, 155, 299, FRAME_RENDERING_START + 90, FRAME_RENDERING_START + 101, STR_NONE, STR_VIRTUAL_FLOOR_STYLE_TIP }, // Virtual floor dropdown + { WWT_BUTTON, 1, 288, 298, FRAME_RENDERING_START + 91, FRAME_RENDERING_START + 100, STR_DROPDOWN_GLYPH, STR_VIRTUAL_FLOOR_STYLE_TIP }, // Virtual floor dropdown #undef FRAME_RENDERING_START -#define FRAME_EFFECTS_START 148 +#define FRAME_EFFECTS_START 163 { WWT_GROUPBOX, 1, 5, 304, FRAME_EFFECTS_START + 0, FRAME_EFFECTS_START + 78, STR_EFFECTS_GROUP, STR_NONE }, // Rendering group { WWT_CHECKBOX, 1, 10, 290, FRAME_EFFECTS_START + 15, FRAME_EFFECTS_START + 26, STR_CYCLE_DAY_NIGHT, STR_CYCLE_DAY_NIGHT_TIP }, // Cycle day-night { WWT_CHECKBOX, 1, 25, 290, FRAME_EFFECTS_START + 30, FRAME_EFFECTS_START + 41, STR_ENABLE_LIGHTING_EFFECTS, STR_ENABLE_LIGHTING_EFFECTS_TIP }, // Enable light fx @@ -357,17 +360,16 @@ static rct_widget window_options_misc_widgets[] = { static rct_widget window_options_advanced_widgets[] = { MAIN_OPTIONS_WIDGETS, { WWT_CHECKBOX, 2, 10, 299, 54, 65, STR_ENABLE_DEBUGGING_TOOLS, STR_ENABLE_DEBUGGING_TOOLS_TIP }, // Enable debugging tools - { WWT_CHECKBOX, 2, 10, 299, 69, 80, STR_TEST_UNFINISHED_TRACKS, STR_TEST_UNFINISHED_TRACKS_TIP }, // Test unfinished tracks - { WWT_CHECKBOX, 2, 10, 299, 84, 95, STR_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM, STR_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM_TIP }, // Allow loading with incorrect checksum - { WWT_CHECKBOX, 2, 10, 299, 99, 110, STR_SAVE_PLUGIN_DATA, STR_SAVE_PLUGIN_DATA_TIP }, // Export plug-in objects with saved games - { WWT_CHECKBOX, 2, 10, 299, 114, 125, STR_STAY_CONNECTED_AFTER_DESYNC, STR_STAY_CONNECTED_AFTER_DESYNC_TIP }, // Do not disconnect after the client desynchronises with the server - { WWT_CHECKBOX, 1, 10, 299, 129, 140, STR_ALWAYS_NATIVE_LOADSAVE, STR_ALWAYS_NATIVE_LOADSAVE_TIP }, // Use native load/save window - { WWT_DROPDOWN, 1, 165, 299, 145, 157, STR_NONE, STR_NONE }, // Autosave dropdown - { WWT_BUTTON, 1, 288, 298, 146, 156, STR_DROPDOWN_GLYPH, STR_AUTOSAVE_FREQUENCY_TIP }, // Autosave dropdown button - SPINNER_WIDGETS (1, 165, 299, 165, 176, STR_NONE, STR_AUTOSAVE_AMOUNT_TIP ), // Autosave amount spinner - { WWT_LABEL, 1, 23, 298, 184, 195, STR_PATH_TO_RCT1, STR_PATH_TO_RCT1_TIP }, // RCT 1 path text - { WWT_BUTTON, 1, 24, 289, 199, 212, STR_NONE, STR_STRING_TOOLTIP }, // RCT 1 path button - { WWT_BUTTON, 1, 289, 299, 199, 212, STR_CLOSE_X, STR_PATH_TO_RCT1_CLEAR_TIP }, // RCT 1 path clear button + { WWT_CHECKBOX, 2, 10, 299, 69, 80, STR_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM, STR_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM_TIP }, // Allow loading with incorrect checksum + { WWT_CHECKBOX, 2, 10, 299, 84, 95, STR_SAVE_PLUGIN_DATA, STR_SAVE_PLUGIN_DATA_TIP }, // Export plug-in objects with saved games + { WWT_CHECKBOX, 2, 10, 299, 99, 110, STR_STAY_CONNECTED_AFTER_DESYNC, STR_STAY_CONNECTED_AFTER_DESYNC_TIP }, // Do not disconnect after the client desynchronises with the server + { WWT_CHECKBOX, 1, 10, 299, 114, 125, STR_ALWAYS_NATIVE_LOADSAVE, STR_ALWAYS_NATIVE_LOADSAVE_TIP }, // Use native load/save window + { WWT_DROPDOWN, 1, 165, 299, 130, 142, STR_NONE, STR_NONE }, // Autosave dropdown + { WWT_BUTTON, 1, 288, 298, 131, 141, STR_DROPDOWN_GLYPH, STR_AUTOSAVE_FREQUENCY_TIP }, // Autosave dropdown button + SPINNER_WIDGETS (1, 165, 299, 150, 161, STR_NONE, STR_AUTOSAVE_AMOUNT_TIP ), // Autosave amount spinner + { WWT_LABEL, 1, 23, 298, 169, 180, STR_PATH_TO_RCT1, STR_PATH_TO_RCT1_TIP }, // RCT 1 path text + { WWT_BUTTON, 1, 24, 289, 184, 197, STR_NONE, STR_STRING_TOOLTIP }, // RCT 1 path button + { WWT_BUTTON, 1, 289, 299, 184, 197, STR_CLOSE_X, STR_PATH_TO_RCT1_CLEAR_TIP }, // RCT 1 path clear button { WIDGETS_END }, }; @@ -522,6 +524,7 @@ static uint64_t window_options_page_enabled_widgets[] = { (1 << WIDX_UNCAP_FPS_CHECKBOX) | (1 << WIDX_USE_VSYNC_CHECKBOX) | (1 << WIDX_SHOW_FPS_CHECKBOX) | + (1 << WIDX_MULTITHREADING_CHECKBOX) | (1 << WIDX_MINIMIZE_FOCUS_LOSS) | (1 << WIDX_STEAM_OVERLAY_PAUSE) | (1 << WIDX_SCALE) | @@ -535,6 +538,7 @@ static uint64_t window_options_page_enabled_widgets[] = { (1 << WIDX_GRIDLINES_CHECKBOX) | (1 << WIDX_UPPER_CASE_BANNERS_CHECKBOX) | (1 << WIDX_SHOW_GUEST_PURCHASES_CHECKBOX) | + (1 << WIDX_TRANSPARENT_SCREENSHOTS_CHECKBOX) | (1 << WIDX_VIRTUAL_FLOOR) | (1 << WIDX_VIRTUAL_FLOOR_DROPDOWN) | (1 << WIDX_DAY_NIGHT_CHECKBOX) | @@ -598,7 +602,6 @@ static uint64_t window_options_page_enabled_widgets[] = { MAIN_OPTIONS_ENABLED_WIDGETS | (1 << WIDX_DEBUGGING_TOOLS) | - (1 << WIDX_TEST_UNFINISHED_TRACKS) | (1 << WIDX_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM) | (1 << WIDX_SAVE_PLUGIN_DATA_CHECKBOX) | (1 << WIDX_STAY_CONNECTED_AFTER_DESYNC) | @@ -680,29 +683,34 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) gConfigGeneral.uncap_fps ^= 1; drawing_engine_set_vsync(gConfigGeneral.use_vsync); config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_USE_VSYNC_CHECKBOX: gConfigGeneral.use_vsync ^= 1; drawing_engine_set_vsync(gConfigGeneral.use_vsync); config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SHOW_FPS_CHECKBOX: gConfigGeneral.show_fps ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); + break; + case WIDX_MULTITHREADING_CHECKBOX: + gConfigGeneral.multithreading ^= 1; + config_save_default(); + w->Invalidate(); break; case WIDX_MINIMIZE_FOCUS_LOSS: gConfigGeneral.minimize_fullscreen_focus_loss ^= 1; platform_refresh_video(false); config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_STEAM_OVERLAY_PAUSE: gConfigGeneral.steam_overlay_pause ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; } break; @@ -730,34 +738,39 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) case WIDX_DAY_NIGHT_CHECKBOX: gConfigGeneral.day_night_cycle ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_ENABLE_LIGHT_FX_CHECKBOX: gConfigGeneral.enable_light_fx ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_UPPER_CASE_BANNERS_CHECKBOX: gConfigGeneral.upper_case_banners ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_DISABLE_LIGHTNING_EFFECT_CHECKBOX: gConfigGeneral.disable_lightning_effect ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_RENDER_WEATHER_EFFECTS_CHECKBOX: gConfigGeneral.render_weather_effects ^= 1; gConfigGeneral.render_weather_gloom = gConfigGeneral.render_weather_effects; config_save_default(); - window_invalidate(w); + w->Invalidate(); gfx_invalidate_screen(); break; case WIDX_SHOW_GUEST_PURCHASES_CHECKBOX: gConfigGeneral.show_guest_purchases ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); + break; + case WIDX_TRANSPARENT_SCREENSHOTS_CHECKBOX: + gConfigGeneral.transparent_screenshot ^= 1; + config_save_default(); + w->Invalidate(); break; } break; @@ -771,7 +784,7 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) case WIDX_SOUND_CHECKBOX: gConfigSound.sound_enabled = !gConfigSound.sound_enabled; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_MASTER_SOUND_CHECKBOX: @@ -782,7 +795,7 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) audio_unpause_sounds(); window_invalidate_by_class(WC_TOP_TOOLBAR); config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_MUSIC_CHECKBOX: @@ -792,13 +805,13 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) audio_stop_ride_music(); } config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_AUDIO_FOCUS_CHECKBOX: gConfigSound.audio_focus = !gConfigSound.audio_focus; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; } break; @@ -812,63 +825,63 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) case WIDX_SCREEN_EDGE_SCROLLING: gConfigGeneral.edge_scrolling ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_TRAP_CURSOR: gConfigGeneral.trap_cursor ^= 1; config_save_default(); context_set_cursor_trap(gConfigGeneral.trap_cursor); - window_invalidate(w); + w->Invalidate(); break; case WIDX_ZOOM_TO_CURSOR: gConfigGeneral.zoom_to_cursor ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_TOOLBAR_SHOW_FINANCES: gConfigInterface.toolbar_show_finances ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); window_invalidate_by_class(WC_TOP_TOOLBAR); break; case WIDX_TOOLBAR_SHOW_RESEARCH: gConfigInterface.toolbar_show_research ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); window_invalidate_by_class(WC_TOP_TOOLBAR); break; case WIDX_TOOLBAR_SHOW_CHEATS: gConfigInterface.toolbar_show_cheats ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); window_invalidate_by_class(WC_TOP_TOOLBAR); break; case WIDX_TOOLBAR_SHOW_NEWS: gConfigInterface.toolbar_show_news ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); window_invalidate_by_class(WC_TOP_TOOLBAR); break; case WIDX_TOOLBAR_SHOW_MUTE: gConfigInterface.toolbar_show_mute ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); window_invalidate_by_class(WC_TOP_TOOLBAR); break; case WIDX_TOOLBAR_SHOW_CHAT: gConfigInterface.toolbar_show_chat ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); window_invalidate_by_class(WC_TOP_TOOLBAR); break; case WIDX_INVERT_DRAG: gConfigGeneral.invert_viewport_drag ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_THEMES_BUTTON: context_open_window(WC_THEMES); - window_invalidate(w); + w->Invalidate(); break; } break; @@ -879,13 +892,13 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) case WIDX_REAL_NAME_CHECKBOX: gConfigGeneral.show_real_names_of_guests ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); peep_update_names(gConfigGeneral.show_real_names_of_guests); break; case WIDX_AUTO_STAFF_PLACEMENT: gConfigGeneral.auto_staff_placement ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_TITLE_SEQUENCE_BUTTON: window_title_editor_open(0); @@ -898,12 +911,12 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) case WIDX_AUTO_OPEN_SHOPS: gConfigGeneral.auto_open_shops = !gConfigGeneral.auto_open_shops; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_ALLOW_EARLY_COMPLETION: gConfigGeneral.allow_early_completion ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; } break; @@ -916,31 +929,26 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) config_save_default(); gfx_invalidate_screen(); break; - case WIDX_TEST_UNFINISHED_TRACKS: - gConfigGeneral.test_unfinished_tracks ^= 1; - config_save_default(); - window_invalidate(w); - break; case WIDX_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM: gConfigGeneral.allow_loading_with_incorrect_checksum = !gConfigGeneral .allow_loading_with_incorrect_checksum; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SAVE_PLUGIN_DATA_CHECKBOX: gConfigGeneral.save_plugin_data ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_STAY_CONNECTED_AFTER_DESYNC: gConfigNetwork.stay_connected = !gConfigNetwork.stay_connected; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_ALWAYS_NATIVE_LOADSAVE: gConfigGeneral.use_native_browse_dialog = !gConfigGeneral.use_native_browse_dialog; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_PATH_TO_RCT1_BUTTON: { @@ -952,6 +960,7 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) { SafeFree(gConfigGeneral.rct1_path); gConfigGeneral.rct1_path = rct1path; + gConfigInterface.scenarioselect_last_tab = 0; config_save_default(); context_show_error(STR_RESTART_REQUIRED, STR_NONE); } @@ -961,7 +970,7 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) context_show_error(STR_PATH_TO_RCT1_WRONG_ERROR, STR_NONE); } } - window_invalidate(w); + w->Invalidate(); break; } case WIDX_PATH_TO_RCT1_CLEAR: @@ -970,7 +979,7 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) SafeFree(gConfigGeneral.rct1_path); config_save_default(); } - window_invalidate(w); + w->Invalidate(); break; } break; @@ -985,27 +994,27 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) case WIDX_FOLLOWER_PEEP_NAMES_CHECKBOX: gConfigTwitch.enable_follower_peep_names ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_FOLLOWER_PEEP_TRACKING_CHECKBOX: gConfigTwitch.enable_follower_peep_tracking ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_CHAT_PEEP_NAMES_CHECKBOX: gConfigTwitch.enable_chat_peep_names ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_CHAT_PEEP_TRACKING_CHECKBOX: gConfigTwitch.enable_chat_peep_tracking ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_NEWS_CHECKBOX: gConfigTwitch.enable_news ^= 1; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_API_URL_BUTTON: window_text_input_raw_open( @@ -1411,7 +1420,7 @@ static void window_options_dropdown(rct_window* w, rct_widgetindex widgetIndex, bool recreate_window = drawing_engine_requires_new_window(srcEngine, dstEngine); platform_refresh_video(recreate_window); config_save_default(); - window_invalidate(w); + w->Invalidate(); } break; case WIDX_SCALE_QUALITY_DROPDOWN: @@ -1537,7 +1546,7 @@ static void window_options_dropdown(rct_window* w, rct_widgetindex widgetIndex, config_save_default(); audio_start_title_music(); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_TITLE_MUSIC_DROPDOWN: if ((dropdownIndex == 1 || dropdownIndex == 3) @@ -1549,7 +1558,7 @@ static void window_options_dropdown(rct_window* w, rct_widgetindex widgetIndex, { gConfigSound.title_music = (int8_t)dropdownIndex; config_save_default(); - window_invalidate(w); + w->Invalidate(); } audio_stop_title_music(); @@ -1580,7 +1589,7 @@ static void window_options_dropdown(rct_window* w, rct_widgetindex widgetIndex, { title_sequence_change_preset((size_t)dropdownIndex); config_save_default(); - window_invalidate(w); + w->Invalidate(); } break; case WIDX_DEFAULT_INSPECTION_INTERVAL_DROPDOWN: @@ -1588,15 +1597,16 @@ static void window_options_dropdown(rct_window* w, rct_widgetindex widgetIndex, { gConfigGeneral.default_inspection_interval = (uint8_t)dropdownIndex; config_save_default(); - window_invalidate(w); + w->Invalidate(); } break; case WIDX_SCENARIO_GROUPING_DROPDOWN: if (dropdownIndex != gConfigGeneral.scenario_select_mode) { gConfigGeneral.scenario_select_mode = dropdownIndex; + gConfigInterface.scenarioselect_last_tab = 0; config_save_default(); - window_invalidate(w); + w->Invalidate(); window_close_by_class(WC_SCENARIO_SELECT); } break; @@ -1611,7 +1621,7 @@ static void window_options_dropdown(rct_window* w, rct_widgetindex widgetIndex, { gConfigGeneral.autosave_frequency = (uint8_t)dropdownIndex; config_save_default(); - window_invalidate(w); + w->Invalidate(); } break; } @@ -1711,6 +1721,7 @@ static void window_options_invalidate(rct_window* w) widget_set_checkbox_value(w, WIDX_UNCAP_FPS_CHECKBOX, gConfigGeneral.uncap_fps); widget_set_checkbox_value(w, WIDX_USE_VSYNC_CHECKBOX, gConfigGeneral.use_vsync); widget_set_checkbox_value(w, WIDX_SHOW_FPS_CHECKBOX, gConfigGeneral.show_fps); + widget_set_checkbox_value(w, WIDX_MULTITHREADING_CHECKBOX, gConfigGeneral.multithreading); widget_set_checkbox_value(w, WIDX_MINIMIZE_FOCUS_LOSS, gConfigGeneral.minimize_fullscreen_focus_loss); widget_set_checkbox_value(w, WIDX_STEAM_OVERLAY_PAUSE, gConfigGeneral.steam_overlay_pause); @@ -1730,6 +1741,7 @@ static void window_options_invalidate(rct_window* w) widget_set_checkbox_value(w, WIDX_GRIDLINES_CHECKBOX, gConfigGeneral.always_show_gridlines); widget_set_checkbox_value(w, WIDX_DAY_NIGHT_CHECKBOX, gConfigGeneral.day_night_cycle); widget_set_checkbox_value(w, WIDX_SHOW_GUEST_PURCHASES_CHECKBOX, gConfigGeneral.show_guest_purchases); + widget_set_checkbox_value(w, WIDX_TRANSPARENT_SCREENSHOTS_CHECKBOX, gConfigGeneral.transparent_screenshot); widget_set_checkbox_value(w, WIDX_UPPER_CASE_BANNERS_CHECKBOX, gConfigGeneral.upper_case_banners); rct_string_id VirtualFloorStyleStrings[] = { STR_VIRTUAL_FLOOR_STYLE_DISABLED, STR_VIRTUAL_FLOOR_STYLE_TRANSPARENT, @@ -1917,7 +1929,6 @@ static void window_options_invalidate(rct_window* w) case WINDOW_OPTIONS_PAGE_ADVANCED: widget_set_checkbox_value(w, WIDX_DEBUGGING_TOOLS, gConfigGeneral.debugging_tools); - widget_set_checkbox_value(w, WIDX_TEST_UNFINISHED_TRACKS, gConfigGeneral.test_unfinished_tracks); widget_set_checkbox_value( w, WIDX_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM, gConfigGeneral.allow_loading_with_incorrect_checksum); widget_set_checkbox_value(w, WIDX_SAVE_PLUGIN_DATA_CHECKBOX, gConfigGeneral.save_plugin_data); @@ -2199,11 +2210,11 @@ static void window_options_set_page(rct_window* w, int32_t page) w->pressed_widgets = 0; w->widgets = window_options_page_widgets[page]; - window_invalidate(w); + w->Invalidate(); window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); } static void window_options_set_pressed_tab(rct_window* w) diff --git a/src/openrct2-ui/windows/Park.cpp b/src/openrct2-ui/windows/Park.cpp index ebd224fc6d..9bd1f8a1a3 100644 --- a/src/openrct2-ui/windows/Park.cpp +++ b/src/openrct2-ui/windows/Park.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -112,7 +113,7 @@ static rct_widget window_park_guests_widgets[] = { static rct_widget window_park_price_widgets[] = { MAIN_PARK_WIDGETS, { WWT_LABEL, 1, 21, 146, 50, 61, STR_ADMISSION_PRICE, STR_NONE }, // - SPINNER_WIDGETS (1, 147, 222, 50, 61, STR_ARG_6_CURRENCY2DP, STR_NONE), // Price (3 widgets) + SPINNER_WIDGETS (1, 147, 222, 50, 61, STR_NONE, STR_NONE), // Price (3 widgets) { WIDGETS_END }, }; @@ -123,7 +124,7 @@ static rct_widget window_park_stats_widgets[] = { static rct_widget window_park_objective_widgets[] = { MAIN_PARK_WIDGETS, - { WWT_BUTTON, 1, 7, 222, 209, 220, STR_ENTER_NAME_INTO_SCENARIO_CHART, STR_NONE }, // enter name + { WWT_BUTTON, 1, 7, 222, 207, 220, STR_ENTER_NAME_INTO_SCENARIO_CHART, STR_NONE }, // enter name { WIDGETS_END }, }; @@ -582,8 +583,11 @@ static void window_park_set_disabled_tabs(rct_window* w) static void window_park_prepare_window_title_text() { - set_format_arg(0, rct_string_id, gParkName); - set_format_arg(2, uint32_t, gParkNameArgs); + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); + + set_format_arg(0, rct_string_id, STR_STRING); + set_format_arg(2, const char*, parkName); } #pragma region Entrance page @@ -605,7 +609,7 @@ rct_window* window_park_entrance_open() } window->page = WINDOW_PARK_PAGE_ENTRANCE; - window_invalidate(window); + window->Invalidate(); window->widgets = window_park_entrance_widgets; window->enabled_widgets = window_park_page_enabled_widgets[WINDOW_PARK_PAGE_ENTRANCE]; window->event_handlers = &window_park_entrance_events; @@ -651,17 +655,20 @@ static void window_park_entrance_mouseup(rct_window* w, rct_widgetindex widgetIn context_open_window(WC_LAND_RIGHTS); break; case WIDX_LOCATE: - window_scroll_to_viewport(w); + w->ScrollToViewport(); break; case WIDX_RENAME: - set_format_arg(16, uint32_t, gParkNameArgs); - window_text_input_open(w, WIDX_RENAME, STR_PARK_NAME, STR_ENTER_PARK_NAME, gParkName, 0, USER_STRING_MAX_LENGTH); + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + window_text_input_raw_open( + w, WIDX_RENAME, STR_PARK_NAME, STR_ENTER_PARK_NAME, park.Name.c_str(), USER_STRING_MAX_LENGTH); break; + } case WIDX_CLOSE_LIGHT: - park_set_open(0); + park_set_open(false); break; case WIDX_OPEN_LIGHT: - park_set_open(1); + park_set_open(true); break; } } @@ -718,13 +725,11 @@ static void window_park_entrance_dropdown(rct_window* w, rct_widgetindex widgetI if (dropdownIndex != 0) { - gGameCommandErrorTitle = STR_CANT_CLOSE_PARK; - park_set_open(1); + park_set_open(true); } else { - gGameCommandErrorTitle = STR_CANT_OPEN_PARK; - park_set_open(0); + park_set_open(false); } } } @@ -766,8 +771,13 @@ static void window_park_entrance_invalidate(rct_window* w) window_park_set_pressed_tab(w); // Set open / close park button state - set_format_arg(0, rct_string_id, gParkName); - set_format_arg(2, uint32_t, gParkNameArgs); + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); + + set_format_arg(0, rct_string_id, STR_STRING); + set_format_arg(2, const char*, parkName); + } window_park_entrance_widgets[WIDX_OPEN_OR_CLOSE].image = park_is_open() ? SPR_OPEN : SPR_CLOSED; window_park_entrance_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + !park_is_open() * 2 + widget_is_pressed(w, WIDX_CLOSE_LIGHT); @@ -878,7 +888,7 @@ static void window_park_init_viewport(rct_window* w) if (w->page != WINDOW_PARK_PAGE_ENTRANCE) return; - if (gParkEntrances.size() > 0) + if (!gParkEntrances.empty()) { const auto& entrance = gParkEntrances[0]; x = entrance.x + 16; @@ -925,13 +935,13 @@ static void window_park_init_viewport(rct_window* w) (viewportWidget->right - viewportWidget->left) - 1, (viewportWidget->bottom - viewportWidget->top) - 1, 0, x, y, z, w->viewport_focus_sprite.type & VIEWPORT_FOCUS_TYPE_MASK, SPRITE_INDEX_NULL); w->flags |= (1 << 2); - window_invalidate(w); + w->Invalidate(); } } if (w->viewport != nullptr) w->viewport->flags = viewportFlags; - window_invalidate(w); + w->Invalidate(); } #pragma endregion @@ -961,7 +971,7 @@ rct_window* window_park_rating_open() window->viewport = nullptr; window->page = WINDOW_PARK_PAGE_RATING; - window_invalidate(window); + window->Invalidate(); window->widgets = window_park_rating_widgets; window->enabled_widgets = window_park_page_enabled_widgets[WINDOW_PARK_PAGE_RATING]; window->hold_down_widgets = window_park_page_hold_down_widgets[WINDOW_PARK_PAGE_RATING]; @@ -1082,7 +1092,7 @@ rct_window* window_park_guests_open() window->viewport = nullptr; window->page = WINDOW_PARK_PAGE_GUESTS; - window_invalidate(window); + window->Invalidate(); window->widgets = window_park_guests_widgets; window->enabled_widgets = window_park_page_enabled_widgets[WINDOW_PARK_PAGE_GUESTS]; window->hold_down_widgets = window_park_page_hold_down_widgets[WINDOW_PARK_PAGE_GUESTS]; @@ -1279,10 +1289,6 @@ static void window_park_price_invalidate(rct_window* w) window_park_price_widgets[WIDX_DECREASE_PRICE].type = WWT_BUTTON; } - money16 parkEntranceFee = park_get_entrance_fee(); - set_format_arg(6, uint32_t, parkEntranceFee); - window_park_price_widgets[WIDX_PRICE].text = parkEntranceFee == 0 ? STR_FREE : STR_ARG_6_CURRENCY2DP; - window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_7); window_park_anchor_border_widgets(w); } @@ -1293,15 +1299,18 @@ static void window_park_price_invalidate(rct_window* w) */ static void window_park_price_paint(rct_window* w, rct_drawpixelinfo* dpi) { - int32_t x, y; - window_draw_widgets(w, dpi); window_park_draw_tab_images(dpi, w); - x = w->x + window_park_price_widgets[WIDX_PAGE_BACKGROUND].left + 4; - y = w->y + window_park_price_widgets[WIDX_PAGE_BACKGROUND].top + 30; - + auto x = w->x + w->widgets[WIDX_PAGE_BACKGROUND].left + 4; + auto y = w->y + w->widgets[WIDX_PAGE_BACKGROUND].top + 30; gfx_draw_string_left(dpi, STR_INCOME_FROM_ADMISSIONS, &gTotalIncomeFromAdmissions, COLOUR_BLACK, x, y); + + money32 parkEntranceFee = park_get_entrance_fee(); + auto stringId = parkEntranceFee == 0 ? STR_FREE : STR_BOTTOM_TOOLBAR_CASH; + x = w->x + w->widgets[WIDX_PRICE].left + 1; + y = w->y + w->widgets[WIDX_PRICE].top + 1; + gfx_draw_string_left(dpi, stringId, &parkEntranceFee, w->colours[1], x, y); } #pragma endregion @@ -1454,7 +1463,7 @@ rct_window* window_park_objective_open() window->viewport = nullptr; window->page = WINDOW_PARK_PAGE_OBJECTIVE; - window_invalidate(window); + window->Invalidate(); window->widgets = window_park_objective_widgets; window->enabled_widgets = window_park_page_enabled_widgets[WINDOW_PARK_PAGE_OBJECTIVE]; window->hold_down_widgets = window_park_page_hold_down_widgets[WINDOW_PARK_PAGE_OBJECTIVE]; @@ -1462,7 +1471,7 @@ rct_window* window_park_objective_open() window_init_scroll_widgets(window); window->x = context_get_width() / 2 - 115; window->y = context_get_height() / 2 - 87; - window_invalidate(window); + window->Invalidate(); return window; } @@ -1500,7 +1509,12 @@ static void window_park_objective_mouseup(rct_window* w, rct_widgetindex widgetI */ static void window_park_objective_resize(rct_window* w) { - window_set_resize(w, 230, 226, 230, 226); +#ifndef NO_TTF + if (gCurrentTTFFontSet != nullptr) + window_set_resize(w, 230, 270, 230, 270); + else +#endif + window_set_resize(w, 230, 226, 230, 226); } /** @@ -1522,7 +1536,7 @@ static void window_park_objective_textinput(rct_window* w, rct_widgetindex widge if (widgetIndex == WIDX_ENTER_NAME && text != nullptr && text[0] != 0) { scenario_success_submit_name(text); - window_invalidate(w); + w->Invalidate(); } } @@ -1535,9 +1549,13 @@ static void window_park_objective_invalidate(rct_window* w) window_park_set_pressed_tab(w); window_park_prepare_window_title_text(); - // + // Show name input button on scenario completion. if (gParkFlags & PARK_FLAGS_SCENARIO_COMPLETE_NAME_INPUT) + { window_park_objective_widgets[WIDX_ENTER_NAME].type = WWT_BUTTON; + window_park_objective_widgets[WIDX_ENTER_NAME].top = w->height - 19; + window_park_objective_widgets[WIDX_ENTER_NAME].bottom = w->height - 6; + } else window_park_objective_widgets[WIDX_ENTER_NAME].type = WWT_EMPTY; @@ -1620,7 +1638,7 @@ rct_window* window_park_awards_open() window->viewport = nullptr; window->page = WINDOW_PARK_PAGE_AWARDS; - window_invalidate(window); + window->Invalidate(); window->widgets = window_park_awards_widgets; window->enabled_widgets = window_park_page_enabled_widgets[WINDOW_PARK_PAGE_AWARDS]; window->hold_down_widgets = window_park_page_hold_down_widgets[WINDOW_PARK_PAGE_AWARDS]; @@ -1748,7 +1766,7 @@ static void window_park_set_page(rct_window* w, int32_t page) w->event_handlers = window_park_page_events[page]; w->widgets = window_park_page_widgets[page]; window_park_set_disabled_tabs(w); - window_invalidate(w); + w->Invalidate(); window_event_resize_call(w); window_event_invalidate_call(w); diff --git a/src/openrct2-ui/windows/Player.cpp b/src/openrct2-ui/windows/Player.cpp index 88316cbdff..45aae78737 100644 --- a/src/openrct2-ui/windows/Player.cpp +++ b/src/openrct2-ui/windows/Player.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -208,7 +210,7 @@ rct_window* window_player_open(uint8_t id) } window->page = 0; - window_invalidate(window); + window->Invalidate(); window->widgets = window_player_page_widgets[WINDOW_PLAYER_PAGE_OVERVIEW]; window->enabled_widgets = window_player_page_enabled_widgets[WINDOW_PLAYER_PAGE_OVERVIEW]; @@ -283,8 +285,11 @@ void window_player_overview_mouse_up(rct_window* w, rct_widgetindex widgetIndex) } break; case WIDX_KICK: - game_do_command(w->number, GAME_COMMAND_FLAG_APPLY, 0, 0, GAME_COMMAND_KICK_PLAYER, 0, 0); - break; + { + auto kickPlayerAction = PlayerKickAction(w->number); + GameActions::Execute(&kickPlayerAction); + } + break; } } @@ -310,8 +315,14 @@ void window_player_overview_dropdown(rct_window* w, rct_widgetindex widgetIndex, return; } int32_t group = network_get_group_id(dropdownIndex); - game_do_command(0, GAME_COMMAND_FLAG_APPLY, w->number, group, GAME_COMMAND_SET_PLAYER_GROUP, 0, 0); - window_invalidate(w); + auto playerSetGroupAction = PlayerSetGroupAction(w->number, group); + playerSetGroupAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) { + if (result->Error == GA_ERROR::OK) + { + w->Invalidate(); + } + }); + GameActions::Execute(&playerSetGroupAction); } void window_player_overview_resize(rct_window* w) @@ -400,6 +411,12 @@ void window_player_overview_paint(rct_window* w, rct_drawpixelinfo* dpi) void window_player_overview_invalidate(rct_window* w) { + int32_t playerIndex = network_get_player_index((uint8_t)w->number); + if (playerIndex == -1) + { + return; + } + if (window_player_page_widgets[w->page] != w->widgets) { w->widgets = window_player_page_widgets[w->page]; @@ -449,7 +466,7 @@ void window_player_overview_invalidate(rct_window* w) // Only enable kick button for other players const bool canKick = network_can_perform_action(network_get_current_player_group_index(), NETWORK_PERMISSION_KICK_PLAYER); - const bool isServer = network_get_player_flags(w->number) & NETWORK_PLAYER_FLAG_ISSERVER; + const bool isServer = network_get_player_flags(playerIndex) & NETWORK_PLAYER_FLAG_ISSERVER; const bool isOwnWindow = (network_get_current_player_id() == w->number); widget_set_enabled(w, WIDX_KICK, canKick && !isOwnWindow && !isServer); } @@ -458,7 +475,6 @@ void window_player_statistics_close(rct_window* w) { if (w->error.var_480) { - user_string_free(w->error.var_480); w->error.var_480 = 0; } } @@ -555,11 +571,11 @@ static void window_player_set_page(rct_window* w, int32_t page) w->event_handlers = window_player_page_events[page]; w->pressed_widgets = 0; w->widgets = window_player_page_widgets[page]; - window_invalidate(w); + w->Invalidate(); window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); if (page == WINDOW_PLAYER_PAGE_OVERVIEW) { diff --git a/src/openrct2-ui/windows/Research.cpp b/src/openrct2-ui/windows/Research.cpp index b64e2c5f6c..8ec9b30273 100644 --- a/src/openrct2-ui/windows/Research.cpp +++ b/src/openrct2-ui/windows/Research.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -246,10 +246,10 @@ rct_window* window_research_open() } w->page = 0; - window_invalidate(w); + w->Invalidate(); w->width = 300; w->height = 196; - window_invalidate(w); + w->Invalidate(); w->widgets = window_research_page_widgets[0]; w->enabled_widgets = window_research_page_enabled_widgets[0]; @@ -598,7 +598,7 @@ static void window_research_set_page(rct_window* w, int32_t page) w->disabled_widgets = 0; w->pressed_widgets = 0; - window_invalidate(w); + w->Invalidate(); if (w->page == WINDOW_RESEARCH_PAGE_DEVELOPMENT) { w->width = 300; @@ -613,7 +613,7 @@ static void window_research_set_page(rct_window* w, int32_t page) window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); } static void window_research_set_pressed_tab(rct_window* w) diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 01d596c6b6..045b05c02a 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -23,12 +23,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -39,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +99,7 @@ enum { WIDX_LOCATE, WIDX_DEMOLISH, WIDX_CLOSE_LIGHT, + WIDX_SIMULATE_LIGHT, WIDX_TEST_LIGHT, WIDX_OPEN_LIGHT, WIDX_RIDE_TYPE, @@ -228,6 +233,7 @@ static rct_widget window_ride_main_widgets[] = { { WWT_FLATBTN, 1, 291, 314, 118, 141, SPR_LOCATE, STR_LOCATE_SUBJECT_TIP }, { WWT_FLATBTN, 1, 291, 314, 142, 165, SPR_DEMOLISH, STR_DEMOLISH_RIDE_TIP }, { WWT_IMGBTN, 1, 296, 309, 48, 61, SPR_G2_RCT1_CLOSE_BUTTON_0, STR_CLOSE_RIDE_TIP }, + { WWT_IMGBTN, 1, 296, 309, 62, 75, SPR_G2_RCT1_TEST_BUTTON_0, STR_SIMULATE_RIDE_TIP }, { WWT_IMGBTN, 1, 296, 309, 62, 75, SPR_G2_RCT1_TEST_BUTTON_0, STR_TEST_RIDE_TIP }, { WWT_IMGBTN, 1, 296, 309, 76, 89, SPR_G2_RCT1_OPEN_BUTTON_0, STR_OPEN_RIDE_TIP }, { WWT_DROPDOWN, 1, 3, 307, 180, 191, STR_ARG_6_STRINGID, STR_NONE }, @@ -394,6 +400,7 @@ static constexpr const uint64_t window_ride_page_enabled_widgets[] = { (1ULL << WIDX_LOCATE) | (1ULL << WIDX_DEMOLISH) | (1ULL << WIDX_CLOSE_LIGHT) | + (1ULL << WIDX_SIMULATE_LIGHT) | (1ULL << WIDX_TEST_LIGHT) | (1ULL << WIDX_OPEN_LIGHT) | (1ULL << WIDX_RIDE_TYPE) | @@ -944,6 +951,7 @@ static rct_window_event_list *window_ride_page_events[] = { static bool _collectTrackDesignScenery = false; static int32_t _lastSceneryX = 0; static int32_t _lastSceneryY = 0; +static std::unique_ptr _trackDesign; // Cached overall view for each ride // (Re)calculated when the ride window is opened @@ -952,7 +960,7 @@ struct ride_overall_view { uint8_t zoom; }; -static ride_overall_view ride_overall_views[MAX_RIDES] = {}; +static std::vector ride_overall_views = {}; static constexpr const int32_t window_ride_tab_animation_divisor[] = { 0, 0, 2, 2, 4, 2, 8, 8, 2, 0 }; static constexpr const int32_t window_ride_tab_animation_frames[] = { 0, 0, 4, 16, 8, 16, 8, 8, 8, 0 }; @@ -977,7 +985,7 @@ static constexpr const rct_string_id RideBreakdownReasonNames[] = { STR_RIDE_BREAKDOWN_CONTROL_FAILURE }; -static constexpr const rct_string_id ColourSchemeNames[] = { +const rct_string_id ColourSchemeNames[4] = { STR_MAIN_COLOUR_SCHEME, STR_ALTERNATIVE_COLOUR_SCHEME_1, STR_ALTERNATIVE_COLOUR_SCHEME_2, @@ -1258,32 +1266,32 @@ static void window_ride_draw_tab_image(rct_drawpixelinfo* dpi, rct_window* w, in static void window_ride_draw_tab_main(rct_drawpixelinfo* dpi, rct_window* w) { rct_widgetindex widgetIndex = WIDX_TAB_1 + WINDOW_RIDE_PAGE_MAIN; - if (!(w->disabled_widgets & (1LL << widgetIndex))) { - int32_t spriteIndex = 0; - int32_t rideType = get_ride(w->number)->type; - - switch (gRideClassifications[rideType]) + auto ride = get_ride(w->number); + if (ride != nullptr) { - case RIDE_CLASS_RIDE: - spriteIndex = SPR_TAB_RIDE_0; - if (w->page == WINDOW_RIDE_PAGE_MAIN) - spriteIndex += (w->frame_no / 4) % 16; - break; - case RIDE_CLASS_SHOP_OR_STALL: - spriteIndex = SPR_TAB_SHOPS_AND_STALLS_0; - if (w->page == WINDOW_RIDE_PAGE_MAIN) - spriteIndex += (w->frame_no / 4) % 16; - break; - case RIDE_CLASS_KIOSK_OR_FACILITY: - spriteIndex = SPR_TAB_KIOSKS_AND_FACILITIES_0; - if (w->page == WINDOW_RIDE_PAGE_MAIN) - spriteIndex += (w->frame_no / 4) % 8; - break; + int32_t spriteIndex = 0; + switch (ride->GetClassification()) + { + case RideClassification::Ride: + spriteIndex = SPR_TAB_RIDE_0; + if (w->page == WINDOW_RIDE_PAGE_MAIN) + spriteIndex += (w->frame_no / 4) % 16; + break; + case RideClassification::ShopOrStall: + spriteIndex = SPR_TAB_SHOPS_AND_STALLS_0; + if (w->page == WINDOW_RIDE_PAGE_MAIN) + spriteIndex += (w->frame_no / 4) % 16; + break; + case RideClassification::KioskOrFacility: + spriteIndex = SPR_TAB_KIOSKS_AND_FACILITIES_0; + if (w->page == WINDOW_RIDE_PAGE_MAIN) + spriteIndex += (w->frame_no / 4) % 8; + break; + } + gfx_draw_sprite(dpi, spriteIndex, w->x + w->widgets[widgetIndex].left, w->y + w->widgets[widgetIndex].top, 0); } - - gfx_draw_sprite(dpi, spriteIndex, w->x + w->widgets[widgetIndex].left, w->y + w->widgets[widgetIndex].top, 0); } } @@ -1317,9 +1325,14 @@ static void window_ride_draw_tab_vehicle(rct_drawpixelinfo* dpi, rct_window* w) x = (widget->right - widget->left) / 2; y = (widget->bottom - widget->top) - 12; - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; - rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride); if (rideEntry->flags & RIDE_ENTRY_FLAG_VEHICLE_TAB_SCALE_HALF) { clipDPI.zoom_level = 1; @@ -1407,7 +1420,9 @@ static void window_ride_draw_tab_images(rct_drawpixelinfo* dpi, rct_window* w) static void window_ride_disable_tabs(rct_window* w) { uint32_t disabled_tabs = 0; - Ride* ride = get_ride(w->number & 0xFF); + auto ride = get_ride(w->number & 0xFF); + if (ride == nullptr) + return; uint8_t ride_type = ride->type; // ecx @@ -1492,10 +1507,15 @@ static void window_ride_update_overall_view(Ride* ride) maxz = std::max(maxz, z2); } - auto view = &ride_overall_views[ride->id]; - view->x = (minx + maxx) / 2 + 16; - view->y = (miny + maxy) / 2 + 16; - view->z = (minz + maxz) / 2 - 8; + if (ride->id >= ride_overall_views.size()) + { + ride_overall_views.resize(ride->id + 1); + } + + auto& view = ride_overall_views[ride->id]; + view.x = (minx + maxx) / 2 + 16; + view.y = (miny + maxy) / 2 + 16; + view.z = (minz + maxz) / 2 - 8; // Calculate size to determine from how far away to view the ride int32_t dx = maxx - minx; @@ -1508,12 +1528,12 @@ static void window_ride_update_overall_view(Ride* ride) { // Each farther zoom level shows twice as many tiles (log) // Appropriate zoom is lowered by one to fill the entire view with the ride - view->zoom = std::clamp(std::ceil(std::log(size / 80)) - 1, 0, 3); + view.zoom = std::clamp(std::ceil(std::log(size / 80)) - 1, 0, 3); } else { // Small rides or stalls are zoomed in all the way. - view->zoom = 0; + view.zoom = 0; } } @@ -1606,7 +1626,7 @@ static rct_window* window_ride_open_station(Ride* ride, int32_t stationIndex) w->page = WINDOW_RIDE_PAGE_MAIN; w->width = 316; w->height = 180; - window_invalidate(w); + w->Invalidate(); w->widgets = window_ride_page_widgets[w->page]; w->enabled_widgets = window_ride_page_enabled_widgets[w->page]; @@ -1678,6 +1698,8 @@ rct_window* window_ride_open_vehicle(rct_vehicle* vehicle) rct_vehicle* headVehicle = vehicle_get_head(vehicle); uint16_t headVehicleSpriteIndex = headVehicle->sprite_index; auto ride = get_ride(headVehicle->ride); + if (ride == nullptr) + return nullptr; // Get view index int32_t view = 1; @@ -1692,7 +1714,7 @@ rct_window* window_ride_open_vehicle(rct_vehicle* vehicle) rct_window* w = window_find_by_number(WC_RIDE, ride->id); if (w != nullptr) { - window_invalidate(w); + w->Invalidate(); if (input_test_flag(INPUT_FLAG_TOOL_ACTIVE) && gCurrentToolWidget.window_classification == w->classification && gCurrentToolWidget.window_number == w->number) @@ -1737,7 +1759,7 @@ rct_window* window_ride_open_vehicle(rct_vehicle* vehicle) w->page = WINDOW_RIDE_PAGE_MAIN; w->width = 316; w->height = 180; - window_invalidate(w); + w->Invalidate(); w->widgets = window_ride_page_widgets[w->page]; w->enabled_widgets = window_ride_page_enabled_widgets[w->page]; @@ -1749,7 +1771,7 @@ rct_window* window_ride_open_vehicle(rct_vehicle* vehicle) w->ride.view = view; window_ride_init_viewport(w); - window_invalidate(w); + w->Invalidate(); return w; } @@ -1792,12 +1814,12 @@ static void window_ride_set_page(rct_window* w, int32_t page) w->pressed_widgets = 0; w->widgets = window_ride_page_widgets[page]; window_ride_disable_tabs(w); - window_invalidate(w); + w->Invalidate(); window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); if (listen != 0 && w->viewport != nullptr) w->viewport->flags |= VIEWPORT_FLAG_SOUND_ON; @@ -1833,7 +1855,10 @@ static void window_ride_init_viewport(rct_window* w) if (w->page != WINDOW_RIDE_PAGE_MAIN) return; - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + int32_t eax = w->viewport_focus_coordinates.var_480 - 1; union @@ -1855,11 +1880,15 @@ static void window_ride_init_viewport(rct_window* w) { focus.sprite.sprite_id = ride->vehicles[eax]; - rct_ride_entry* ride_entry = get_ride_entry_by_ride(ride); + rct_ride_entry* ride_entry = ride->GetRideEntry(); if (ride_entry && ride_entry->tab_vehicle != 0) { rct_vehicle* vehicle = GET_VEHICLE(focus.sprite.sprite_id); - if (vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) + if (vehicle == nullptr) + { + focus.sprite.sprite_id = SPRITE_INDEX_NULL; + } + else if (vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) { focus.sprite.sprite_id = vehicle->next_vehicle_on_train; } @@ -1896,14 +1925,15 @@ static void window_ride_init_viewport(rct_window* w) w->viewport_focus_coordinates.var_480 = 0; } - ride_overall_view* view = &ride_overall_views[w->number]; - - focus.coordinate.x = view->x; - focus.coordinate.y = view->y; - focus.coordinate.z = view->z; - focus.coordinate.zoom = view->zoom; - - focus.sprite.type |= VIEWPORT_FOCUS_TYPE_COORDINATE; + if (w->number < ride_overall_views.size()) + { + const auto& view = ride_overall_views[w->number]; + focus.coordinate.x = view.x; + focus.coordinate.y = view.y; + focus.coordinate.z = view.z; + focus.coordinate.zoom = view.zoom; + focus.sprite.type |= VIEWPORT_FOCUS_TYPE_COORDINATE; + } } focus.coordinate.var_480 = w->viewport_focus_coordinates.var_480; @@ -1953,12 +1983,12 @@ static void window_ride_init_viewport(rct_window* w) focus.coordinate.z, focus.sprite.type & VIEWPORT_FOCUS_TYPE_MASK, focus.sprite.sprite_id); w->flags |= WF_NO_SCROLLING; - window_invalidate(w); + w->Invalidate(); } if (w->viewport) { w->viewport->flags = viewport_flags; - window_invalidate(w); + w->Invalidate(); } } @@ -1968,13 +1998,13 @@ static void window_ride_init_viewport(rct_window* w) */ static void window_ride_rename(rct_window* w) { - Ride* ride; - - ride = get_ride(w->number); - set_format_arg(16, uint32_t, ride->name_arguments); - window_text_input_open( - w, WIDX_RENAME, STR_RIDE_ATTRACTION_NAME, STR_ENTER_NEW_NAME_FOR_THIS_RIDE_ATTRACTION, ride->name, ride->name_arguments, - 32); + auto ride = get_ride(w->number); + if (ride != nullptr) + { + auto rideName = ride->GetName(); + window_text_input_raw_open( + w, WIDX_RENAME, STR_RIDE_ATTRACTION_NAME, STR_ENTER_NEW_NAME_FOR_THIS_RIDE_ATTRACTION, rideName.c_str(), 32); + } } /** @@ -1983,9 +2013,6 @@ static void window_ride_rename(rct_window* w) */ static void window_ride_main_mouseup(rct_window* w, rct_widgetindex widgetIndex) { - ride_id_t rideIndex; - int32_t status; - switch (widgetIndex) { case WIDX_CLOSE: @@ -2004,45 +2031,56 @@ static void window_ride_main_mouseup(rct_window* w, rct_widgetindex widgetIndex) window_ride_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_CONSTRUCTION: - rideIndex = (uint8_t)w->number; - ride_construct(get_ride(rideIndex)); - if (window_find_by_number(WC_RIDE_CONSTRUCTION, rideIndex) != nullptr) + { + auto ride = get_ride(w->number); + if (ride != nullptr) { - window_close(w); + ride_construct(ride); + if (window_find_by_number(WC_RIDE_CONSTRUCTION, ride->id) != nullptr) + { + window_close(w); + } } break; + } case WIDX_RENAME: window_ride_rename(w); break; case WIDX_LOCATE: - window_scroll_to_viewport(w); + w->ScrollToViewport(); break; case WIDX_DEMOLISH: context_open_detail_window(WD_DEMOLISH_RIDE, w->number); break; case WIDX_CLOSE_LIGHT: + case WIDX_SIMULATE_LIGHT: case WIDX_TEST_LIGHT: case WIDX_OPEN_LIGHT: - switch (widgetIndex) - { - default: - case WIDX_CLOSE_LIGHT: - status = RIDE_STATUS_CLOSED; - break; - case WIDX_TEST_LIGHT: - status = RIDE_STATUS_TESTING; - break; - case WIDX_OPEN_LIGHT: - status = RIDE_STATUS_OPEN; - break; - } - + { auto ride = get_ride(w->number); if (ride != nullptr) { + int32_t status; + switch (widgetIndex) + { + default: + case WIDX_CLOSE_LIGHT: + status = RIDE_STATUS_CLOSED; + break; + case WIDX_SIMULATE_LIGHT: + status = RIDE_STATUS_SIMULATING; + break; + case WIDX_TEST_LIGHT: + status = RIDE_STATUS_TESTING; + break; + case WIDX_OPEN_LIGHT: + status = RIDE_STATUS_OPEN; + break; + } ride_set_status(ride, status); } break; + } } } @@ -2052,12 +2090,32 @@ static void window_ride_main_mouseup(rct_window* w, rct_widgetindex widgetIndex) */ static void window_ride_main_resize(rct_window* w) { - const int32_t offset = gCheatsAllowArbitraryRideTypeChanges ? 15 : 0; - w->flags |= WF_RESIZABLE; - int32_t minHeight = 180 + offset; + int32_t minHeight = 180; if (theme_get_flags() & UITHEME_FLAG_USE_LIGHTS_RIDE) - minHeight = 200 + offset + RCT1_LIGHT_OFFSET - - (ride_type_has_flag(get_ride(w->number)->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? 14 : 0); + { + minHeight += 20 + RCT1_LIGHT_OFFSET; + + auto ride = get_ride(w->number); + if (ride != nullptr) + { +#ifdef __SIMULATE_IN_RIDE_WINDOW__ + if (ride->SupportsStatus(RIDE_STATUS_SIMULATING)) + { + minHeight += 14; + } +#endif + if (ride->SupportsStatus(RIDE_STATUS_TESTING)) + { + minHeight += 14; + } + } + } + if (gCheatsAllowArbitraryRideTypeChanges) + { + minHeight += 15; + } + + w->flags |= WF_RESIZABLE; window_set_resize(w, 316, minHeight, 500, 450); window_ride_init_viewport(w); } @@ -2069,7 +2127,9 @@ static void window_ride_main_resize(rct_window* w) static void window_ride_show_view_dropdown(rct_window* w, rct_widget* widget) { rct_widget* dropdownWidget = widget - 1; - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; int32_t numItems = 1; if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_VEHICLES)) @@ -2120,78 +2180,83 @@ static void window_ride_show_view_dropdown(rct_window* w, rct_widget* widget) dropdown_set_checked(w->ride.view, true); } -/** - * - * rct2: 0x006AF64C - */ -static void window_ride_show_open_dropdown(rct_window* w, rct_widget* widget) +static uint8_t window_ride_get_next_default_status(const Ride* ride) { - Ride* ride; - int32_t numItems, highlightedIndex = 0, checkedIndex; - - ride = get_ride(w->number); - - numItems = 0; - gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[numItems] = STR_CLOSE_RIDE; - numItems++; - - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE)) - { - gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[numItems] = STR_TEST_RIDE; - numItems++; - } - - gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[numItems] = STR_OPEN_RIDE; - numItems++; - - window_dropdown_show_text( - w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[1], 0, numItems); - - checkedIndex = ride->status; switch (ride->status) { + default: case RIDE_STATUS_CLOSED: - highlightedIndex = 0; if ((ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) || (ride->lifecycle_flags & RIDE_LIFECYCLE_HAS_STALLED_VEHICLE)) - break; - - highlightedIndex = 2; - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE)) - break; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) - break; - - highlightedIndex = 1; - break; + { + return RIDE_STATUS_CLOSED; + } + else if (ride->SupportsStatus(RIDE_STATUS_TESTING) && !(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) + { + return RIDE_STATUS_TESTING; + } + else + { + return RIDE_STATUS_OPEN; + } + case RIDE_STATUS_SIMULATING: + return RIDE_STATUS_TESTING; case RIDE_STATUS_TESTING: - highlightedIndex = 2; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) - break; - - highlightedIndex = 0; - break; + return (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) ? RIDE_STATUS_OPEN : RIDE_STATUS_CLOSED; case RIDE_STATUS_OPEN: - highlightedIndex = 0; - break; + return RIDE_STATUS_CLOSED; } +} - if (checkedIndex != RIDE_STATUS_CLOSED) - checkedIndex = 3 - checkedIndex; +struct RideStatusDropdownInfo +{ + struct Ride* Ride{}; + uint8_t CurrentStatus{}; + uint8_t DefaultStatus{}; - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE)) + int32_t NumItems{}; + int32_t CheckedIndex = -1; + int32_t DefaultIndex = -1; +}; + +static void window_ride_set_dropdown(RideStatusDropdownInfo& info, uint8_t status, rct_string_id text) +{ + if (info.Ride->SupportsStatus(status)) { - if (checkedIndex != 0) - checkedIndex--; - if (highlightedIndex != 0) - highlightedIndex--; + auto index = info.NumItems; + gDropdownItemsFormat[index] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[index] = text; + if (info.CurrentStatus == status) + { + info.CheckedIndex = index; + } + if (info.DefaultStatus == status) + { + info.DefaultIndex = index; + } + info.NumItems++; } +} - dropdown_set_checked(checkedIndex, true); - gDropdownDefaultIndex = highlightedIndex; +static void window_ride_show_open_dropdown(rct_window* w, rct_widget* widget) +{ + RideStatusDropdownInfo info; + info.Ride = get_ride(w->number); + if (info.Ride == nullptr) + return; + + info.CurrentStatus = info.Ride->status; + info.DefaultStatus = window_ride_get_next_default_status(info.Ride); + window_ride_set_dropdown(info, RIDE_STATUS_CLOSED, STR_CLOSE_RIDE); +#ifdef __SIMULATE_IN_RIDE_WINDOW__ + window_ride_set_dropdown(info, RIDE_STATUS_SIMULATING, STR_SIMULATE_RIDE); +#endif + window_ride_set_dropdown(info, RIDE_STATUS_TESTING, STR_TEST_RIDE); + window_ride_set_dropdown(info, RIDE_STATUS_OPEN, STR_OPEN_RIDE); + window_dropdown_show_text( + w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[1], 0, info.NumItems); + dropdown_set_checked(info.CheckedIndex, true); + gDropdownDefaultIndex = info.DefaultIndex; } static void populate_ride_type_dropdown() @@ -2209,7 +2274,7 @@ static void populate_ride_type_dropdown() } std::sort(RideDropdownData.begin(), RideDropdownData.end(), [](auto& a, auto& b) { - return std::strcmp(a.label_string, b.label_string) < 0; + return String::Compare(a.label_string, b.label_string, true) < 0; }); RideDropdownDataLanguage = ls.GetCurrentLanguage(); @@ -2217,7 +2282,10 @@ static void populate_ride_type_dropdown() static void window_ride_show_ride_type_dropdown(rct_window* w, rct_widget* widget) { - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + populate_ride_type_dropdown(); for (size_t i = 0; i < RideDropdownData.size(); i++) @@ -2249,7 +2317,7 @@ static void window_ride_show_ride_type_dropdown(rct_window* w, rct_widget* widge static void populate_vehicle_type_dropdown(Ride* ride) { - rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride); + rct_ride_entry* rideEntry = ride->GetRideEntry(); bool selectionShouldBeExpanded; int32_t rideTypeIterator, rideTypeIteratorMax; @@ -2312,7 +2380,7 @@ static void populate_vehicle_type_dropdown(Ride* ride) } std::sort(VehicleDropdownData.begin(), VehicleDropdownData.end(), [](auto& a, auto& b) { - return std::strcmp(a.label_string, b.label_string) < 0; + return String::Compare(a.label_string, b.label_string, true) < 0; }); VehicleDropdownExpanded = selectionShouldBeExpanded; @@ -2322,7 +2390,10 @@ static void populate_vehicle_type_dropdown(Ride* ride) static void window_ride_show_vehicle_type_dropdown(rct_window* w, rct_widget* widget) { - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + populate_vehicle_type_dropdown(ride); size_t numItems = std::min(VehicleDropdownData.size(), DROPDOWN_ITEMS_MAX_SIZE); @@ -2380,51 +2451,63 @@ static void window_ride_main_mousedown(rct_window* w, rct_widgetindex widgetInde */ static void window_ride_main_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex) { - Ride* ride; - int32_t status = 0; - switch (widgetIndex) { case WIDX_VIEW_DROPDOWN: if (dropdownIndex == -1) { - dropdownIndex = w->ride.view; - ride = get_ride(w->number); - dropdownIndex++; - if (dropdownIndex != 0 && dropdownIndex <= ride->num_vehicles - && !(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) - dropdownIndex = ride->num_vehicles + 1; - - if (dropdownIndex >= gDropdownNumItems) - dropdownIndex = 0; + dropdownIndex = w->ride.view + 1; + auto ride = get_ride(w->number); + if (ride != nullptr) + { + if (dropdownIndex != 0 && dropdownIndex <= ride->num_vehicles + && !(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) + { + dropdownIndex = ride->num_vehicles + 1; + } + if (dropdownIndex >= gDropdownNumItems) + { + dropdownIndex = 0; + } + } } w->ride.view = dropdownIndex; window_ride_init_viewport(w); - window_invalidate(w); + w->Invalidate(); break; case WIDX_OPEN: - if (dropdownIndex == -1) - dropdownIndex = gDropdownHighlightedIndex; - - ride = get_ride(w->number); - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE) && dropdownIndex != 0) - dropdownIndex++; - - switch (dropdownIndex) + { + auto ride = get_ride(w->number); + if (ride != nullptr) { - case 0: - status = RIDE_STATUS_CLOSED; - break; - case 1: - status = RIDE_STATUS_TESTING; - break; - case 2: - status = RIDE_STATUS_OPEN; - break; + auto status = RIDE_STATUS_CLOSED; + if (dropdownIndex < 0) + { + dropdownIndex = gDropdownHighlightedIndex; + } + if (dropdownIndex < (int32_t)std::size(gDropdownItemsArgs)) + { + switch (gDropdownItemsArgs[dropdownIndex]) + { + case STR_CLOSE_RIDE: + status = RIDE_STATUS_CLOSED; + break; + case STR_SIMULATE_RIDE: + status = RIDE_STATUS_SIMULATING; + break; + case STR_TEST_RIDE: + status = RIDE_STATUS_TESTING; + break; + case STR_OPEN_RIDE: + status = RIDE_STATUS_OPEN; + break; + } + } + ride_set_status(ride, status); } - ride_set_status(ride, status); break; + } case WIDX_RIDE_TYPE_DROPDOWN: if (dropdownIndex != -1 && dropdownIndex < RIDE_TYPE_COUNT) { @@ -2434,7 +2517,6 @@ static void window_ride_main_dropdown(rct_window* w, rct_widgetindex widgetIndex { set_operating_setting(w->number, RideSetSetting::RideType, rideType); } - window_invalidate_all(); } } } @@ -2451,28 +2533,32 @@ static void window_ride_main_update(rct_window* w) widget_invalidate(w, WIDX_TAB_1); // Update status - Ride* ride = get_ride(w->number); - if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN)) + auto ride = get_ride(w->number); + if (ride != nullptr) { - if (w->ride.view == 0) - return; - - if (w->ride.view <= ride->num_vehicles) + if (!(ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAIN)) { - int32_t vehicleIndex = w->ride.view - 1; - uint16_t vehicleSpriteIndex = ride->vehicles[vehicleIndex]; - if (vehicleSpriteIndex == SPRITE_INDEX_NULL) + if (w->ride.view == 0) return; - rct_vehicle* vehicle = &(get_sprite(vehicleSpriteIndex)->vehicle); - if (vehicle->status != 4 && vehicle->status != 22 && vehicle->status != 10 && vehicle->status != 7) + if (w->ride.view <= ride->num_vehicles) { - return; + int32_t vehicleIndex = w->ride.view - 1; + uint16_t vehicleSpriteIndex = ride->vehicles[vehicleIndex]; + if (vehicleSpriteIndex == SPRITE_INDEX_NULL) + return; + + rct_vehicle* vehicle = &(get_sprite(vehicleSpriteIndex)->vehicle); + if (vehicle->status != VEHICLE_STATUS_TRAVELLING && vehicle->status != VEHICLE_STATUS_TRAVELLING_CABLE_LIFT + && vehicle->status != VEHICLE_STATUS_TRAVELLING_DODGEMS + && vehicle->status != VEHICLE_STATUS_TRAVELLING_BOAT) + { + return; + } } } + ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAIN; } - - ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAIN; widget_invalidate(w, WIDX_STATUS); } @@ -2519,25 +2605,40 @@ static void window_ride_main_invalidate(rct_window* w) window_ride_set_pressed_tab(w); - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + w->disabled_widgets &= ~((1 << WIDX_DEMOLISH) | (1 << WIDX_CONSTRUCTION)); if (ride->lifecycle_flags & (RIDE_LIFECYCLE_INDESTRUCTIBLE | RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK)) w->disabled_widgets |= (1 << WIDX_DEMOLISH); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); - set_format_arg(6, uint16_t, RideNaming[ride->type].name); + ride->FormatNameTo(gCommonFormatArgs); + uint32_t spriteIds[] = { SPR_CLOSED, SPR_OPEN, SPR_TESTING, + SPR_G2_SIMULATE, }; window_ride_main_widgets[WIDX_OPEN].image = spriteIds[ride->status]; +#ifdef __SIMULATE_IN_RIDE_WINDOW__ window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RIDE_STATUS_CLOSED) * 2 + widget_is_pressed(w, WIDX_CLOSE_LIGHT); + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].image = SPR_G2_RCT1_SIMULATE_BUTTON_0 + + (ride->status == RIDE_STATUS_SIMULATING) * 2 + widget_is_pressed(w, WIDX_SIMULATE_LIGHT); window_ride_main_widgets[WIDX_TEST_LIGHT].image = SPR_G2_RCT1_TEST_BUTTON_0 + (ride->status == RIDE_STATUS_TESTING) * 2 + widget_is_pressed(w, WIDX_TEST_LIGHT); +#else + window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RIDE_STATUS_CLOSED) * 2 + + widget_is_pressed(w, WIDX_CLOSE_LIGHT); + + auto baseSprite = ride->status == RIDE_STATUS_SIMULATING ? SPR_G2_RCT1_SIMULATE_BUTTON_0 : SPR_G2_RCT1_TEST_BUTTON_0; + window_ride_main_widgets[WIDX_TEST_LIGHT].image = baseSprite + + (ride->status == RIDE_STATUS_TESTING || ride->status == RIDE_STATUS_SIMULATING) * 2 + + widget_is_pressed(w, WIDX_TEST_LIGHT); +#endif window_ride_main_widgets[WIDX_OPEN_LIGHT].image = SPR_G2_RCT1_OPEN_BUTTON_0 + (ride->status == RIDE_STATUS_OPEN) * 2 + widget_is_pressed(w, WIDX_OPEN_LIGHT); @@ -2569,6 +2670,7 @@ static void window_ride_main_invalidate(rct_window* w) else { window_ride_main_widgets[WIDX_RIDE_TYPE].type = WWT_DROPDOWN; + window_ride_main_widgets[WIDX_RIDE_TYPE].text = RideNaming[ride->type].name; window_ride_main_widgets[WIDX_RIDE_TYPE_DROPDOWN].type = WWT_BUTTON; } @@ -2578,11 +2680,21 @@ static void window_ride_main_invalidate(rct_window* w) { window_ride_main_widgets[WIDX_OPEN].type = WWT_EMPTY; window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WWT_IMGBTN; - window_ride_main_widgets[WIDX_TEST_LIGHT].type - = (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? WWT_EMPTY : WWT_IMGBTN); + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WWT_EMPTY; +#ifdef __SIMULATE_IN_RIDE_WINDOW__ + if (ride->SupportsStatus(RIDE_STATUS_SIMULATING)) + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WWT_IMGBTN; +#endif + window_ride_main_widgets[WIDX_TEST_LIGHT].type = ride->SupportsStatus(RIDE_STATUS_TESTING) ? WWT_IMGBTN : WWT_EMPTY; window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WWT_IMGBTN; height = 62; + if (window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type != WWT_EMPTY) + { + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].top = height; + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].bottom = height + 13; + height += 14; + } if (window_ride_main_widgets[WIDX_TEST_LIGHT].type != WWT_EMPTY) { window_ride_main_widgets[WIDX_TEST_LIGHT].top = height; @@ -2592,15 +2704,12 @@ static void window_ride_main_invalidate(rct_window* w) window_ride_main_widgets[WIDX_OPEN_LIGHT].top = height; window_ride_main_widgets[WIDX_OPEN_LIGHT].bottom = height + 13; height += 14 - 24 + RCT1_LIGHT_OFFSET; - - w->min_height = 200 + RCT1_LIGHT_OFFSET - (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? 14 : 0); - if (w->height < w->min_height) - window_event_resize_call(w); } else { window_ride_main_widgets[WIDX_OPEN].type = WWT_FLATBTN; window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WWT_EMPTY; + window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WWT_EMPTY; window_ride_main_widgets[WIDX_TEST_LIGHT].type = WWT_EMPTY; window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WWT_EMPTY; height = 46; @@ -2629,15 +2738,11 @@ static rct_string_id window_ride_get_status_overall_view(rct_window* w, void* ar auto ride = get_ride(w->number); if (ride != nullptr) { - rct_string_id formatSecondary; - int32_t argument; - ride_get_status(ride, &formatSecondary, &argument); - std::memcpy(arguments, &formatSecondary, sizeof(formatSecondary)); - std::memcpy((void*)((uintptr_t)arguments + 2), &argument, sizeof(argument)); - stringId = STR_RED_OUTLINED_STRING; - if (formatSecondary != STR_BROKEN_DOWN && formatSecondary != STR_CRASHED) + ride->FormatStatusTo(arguments); + stringId = STR_BLACK_STRING; + if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { - stringId = STR_BLACK_STRING; + stringId = STR_RED_OUTLINED_STRING; } } return stringId; @@ -2649,13 +2754,14 @@ static rct_string_id window_ride_get_status_overall_view(rct_window* w, void* ar */ static rct_string_id window_ride_get_status_vehicle(rct_window* w, void* arguments) { - Ride* ride; rct_vehicle* vehicle; int32_t vehicleIndex; uint16_t vehicleSpriteIndex; rct_string_id stringId; - ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return 0; vehicleIndex = w->ride.view - 1; vehicleSpriteIndex = ride->vehicles[vehicleIndex]; @@ -2705,7 +2811,10 @@ static rct_string_id window_ride_get_status_vehicle(rct_window* w, void* argumen */ static rct_string_id window_ride_get_status_station(rct_window* w, void* arguments) { - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return STR_NONE; + int32_t count = w->ride.view - ride->num_vehicles - 1; int32_t stationIndex = -1; rct_string_id stringId = 0; @@ -2753,13 +2862,12 @@ static rct_string_id window_ride_get_status_station(rct_window* w, void* argumen */ static rct_string_id window_ride_get_status(rct_window* w, void* arguments) { - Ride* ride = get_ride(w->number); - + auto ride = get_ride(w->number); if (w->ride.view == 0) return window_ride_get_status_overall_view(w, arguments); - if (w->ride.view <= ride->num_vehicles) + if (ride != nullptr && w->ride.view <= ride->num_vehicles) return window_ride_get_status_vehicle(w, arguments); - if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + if (ride != nullptr && ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) return window_ride_get_status_overall_view(w, arguments); return window_ride_get_status_station(w, arguments); } @@ -2770,7 +2878,6 @@ static rct_string_id window_ride_get_status(rct_window* w, void* arguments) */ static void window_ride_main_paint(rct_window* w, rct_drawpixelinfo* dpi) { - Ride* ride; rct_widget* widget; rct_string_id stringId; @@ -2786,7 +2893,10 @@ static void window_ride_main_paint(rct_window* w, rct_drawpixelinfo* dpi) } // View dropdown - ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + stringId = STR_OVERALL_VIEW; if (w->ride.view != 0) { @@ -2861,7 +2971,9 @@ static void window_ride_vehicle_resize(rct_window* w) */ static void window_ride_vehicle_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; switch (widgetIndex) { @@ -2870,20 +2982,20 @@ static void window_ride_vehicle_mousedown(rct_window* w, rct_widgetindex widgetI break; case WIDX_VEHICLE_TRAINS_INCREASE: if (ride->num_vehicles < 32) - ride_set_num_vehicles(ride, ride->num_vehicles + 1); + ride->SetNumVehicles(ride->num_vehicles + 1); break; case WIDX_VEHICLE_TRAINS_DECREASE: if (ride->num_vehicles > 1) - ride_set_num_vehicles(ride, ride->num_vehicles - 1); + ride->SetNumVehicles(ride->num_vehicles - 1); break; case WIDX_VEHICLE_CARS_PER_TRAIN_INCREASE: if (ride->num_cars_per_train < 255) - ride_set_num_cars_per_vehicle(ride, ride->num_cars_per_train + 1); + ride->SetNumCarsPerVehicle(ride->num_cars_per_train + 1); break; case WIDX_VEHICLE_CARS_PER_TRAIN_DECREASE: - rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride); + rct_ride_entry* rideEntry = ride->GetRideEntry(); if (ride->num_cars_per_train > rideEntry->zero_cars + 1) - ride_set_num_cars_per_vehicle(ride, ride->num_cars_per_train - 1); + ride->SetNumCarsPerVehicle(ride->num_cars_per_train - 1); break; } } @@ -2906,7 +3018,7 @@ static void window_ride_vehicle_dropdown(rct_window* w, rct_widgetindex widgetIn if (ride != nullptr) { auto newRideType = VehicleDropdownData[dropdownIndex].subtype_id; - ride_set_ride_entry(ride, newRideType); + ride->SetRideEntry(newRideType); } } break; @@ -2931,7 +3043,6 @@ static void window_ride_vehicle_update(rct_window* w) static void window_ride_vehicle_invalidate(rct_window* w) { rct_widget* widgets; - Ride* ride; rct_ride_entry* rideEntry; rct_string_id stringId; int32_t carsPerTrain; @@ -2945,11 +3056,14 @@ static void window_ride_vehicle_invalidate(rct_window* w) window_ride_set_pressed_tab(w); - ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + rideEntry = ride->GetRideEntry(); + + w->widgets[WIDX_TITLE].text = STR_ARG_20_STRINGID; + ride->FormatNameTo(gCommonFormatArgs + 20); // Widget setup carsPerTrain = ride->num_cars_per_train - rideEntry->zero_cars; @@ -3032,19 +3146,19 @@ static void window_ride_vehicle_invalidate(rct_window* w) */ static void window_ride_vehicle_paint(rct_window* w, rct_drawpixelinfo* dpi) { - Ride* ride; - rct_ride_entry* rideEntry; - int32_t x, y; - int16_t factor; - window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); - ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; - x = w->x + 8; - y = w->y + 64; + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + int32_t x = w->x + 8; + int32_t y = w->y + 64; // Description y += gfx_draw_string_left_wrapped(dpi, &rideEntry->naming.description, x, y, 300, STR_BLACK_STRING, COLOUR_BLACK); @@ -3054,7 +3168,7 @@ static void window_ride_vehicle_paint(rct_window* w, rct_drawpixelinfo* dpi) gfx_draw_string_left(dpi, STR_CAPACITY, &rideEntry->capacity, COLOUR_BLACK, x, y); // Excitement Factor - factor = rideEntry->excitement_multiplier; + auto factor = (int16_t)rideEntry->excitement_multiplier; if (factor > 0) { y += LIST_ROW_HEIGHT; @@ -3102,8 +3216,11 @@ static rct_vehicle_paintinfo _sprites_to_draw[144]; */ static void window_ride_vehicle_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) { - Ride* ride = get_ride(w->number); - rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + + rct_ride_entry* rideEntry = ride->GetRideEntry(); // Background gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width, dpi->y + dpi->height, PALETTE_INDEX_12); @@ -3192,7 +3309,9 @@ static void window_ride_vehicle_scrollpaint(rct_window* w, rct_drawpixelinfo* dp */ static void window_ride_mode_tweak_increase(rct_window* w) { - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; uint8_t maxValue = RideProperties[ride->type].max_value; uint8_t minValue = gCheatsFastLiftHill ? 0 : RideProperties[ride->type].min_value; @@ -3214,7 +3333,9 @@ static void window_ride_mode_tweak_increase(rct_window* w) */ static void window_ride_mode_tweak_decrease(rct_window* w) { - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; uint8_t maxValue = RideProperties[ride->type].max_value; uint8_t minValue = gCheatsFastLiftHill ? 0 : RideProperties[ride->type].min_value; @@ -3236,12 +3357,13 @@ static void window_ride_mode_tweak_decrease(rct_window* w) static void window_ride_mode_dropdown(rct_window* w, rct_widget* widget) { rct_widget* dropdownWidget; - Ride* ride; const uint8_t *availableModes, *mode; int32_t i, numAvailableModes; dropdownWidget = widget - 1; - ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; // Seek to available modes for this ride availableModes = ride_seek_available_modes(ride); @@ -3280,13 +3402,12 @@ static void window_ride_mode_dropdown(rct_window* w, rct_widget* widget) */ static void window_ride_load_dropdown(rct_window* w, rct_widget* widget) { - rct_widget* dropdownWidget; - int32_t i; + auto ride = get_ride(w->number); + if (ride == nullptr) + return; - dropdownWidget = widget - 1; - Ride* ride = get_ride(w->number); - - for (i = 0; i < 5; i++) + auto dropdownWidget = widget - 1; + for (auto i = 0; i < 5; i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = VehicleLoadNames[i]; @@ -3304,9 +3425,9 @@ static void window_ride_load_dropdown(rct_window* w, rct_widget* widget) */ static void window_ride_operating_mouseup(rct_window* w, rct_widgetindex widgetIndex) { - Ride* ride; - - ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; switch (widgetIndex) { @@ -3362,9 +3483,11 @@ static void window_ride_operating_resize(rct_window* w) */ static void window_ride_operating_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { - Ride* ride = get_ride(w->number); - uint8_t upper_bound, lower_bound; + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + uint8_t upper_bound, lower_bound; switch (widgetIndex) { case WIDX_MODE_TWEAK_INCREASE: @@ -3442,22 +3565,23 @@ static void window_ride_operating_mousedown(rct_window* w, rct_widgetindex widge */ static void window_ride_operating_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex) { - Ride* ride; - const uint8_t* availableModes; - if (dropdownIndex == -1) return; - ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; switch (widgetIndex) { case WIDX_MODE_DROPDOWN: + { // Seek to available modes for this ride - availableModes = ride_seek_available_modes(ride); - - set_operating_setting(w->number, RideSetSetting::Mode, availableModes[dropdownIndex]); + auto availableModes = ride_seek_available_modes(ride); + if (availableModes != nullptr) + set_operating_setting(w->number, RideSetSetting::Mode, availableModes[dropdownIndex]); break; + } case WIDX_LOAD_DROPDOWN: set_operating_setting( w->number, RideSetSetting::Departure, (ride->depart_flags & ~RIDE_DEPART_WAIT_FOR_LOAD_MASK) | dropdownIndex); @@ -3471,17 +3595,15 @@ static void window_ride_operating_dropdown(rct_window* w, rct_widgetindex widget */ static void window_ride_operating_update(rct_window* w) { - Ride* ride; - w->frame_no++; window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_3); - ride = get_ride(w->number); - if (ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_OPERATING) + auto ride = get_ride(w->number); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_OPERATING) { ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_OPERATING; - window_invalidate(w); + w->Invalidate(); } } @@ -3492,7 +3614,6 @@ static void window_ride_operating_update(rct_window* w) static void window_ride_operating_invalidate(rct_window* w) { rct_widget* widgets; - Ride* ride; rct_string_id format, caption, tooltip; widgets = window_ride_page_widgets[w->page]; @@ -3504,10 +3625,11 @@ static void window_ride_operating_invalidate(rct_window* w) window_ride_set_pressed_tab(w); - ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs); // Widget setup w->pressed_widgets &= ~( @@ -3533,7 +3655,7 @@ static void window_ride_operating_invalidate(rct_window* w) } // Number of circuits - if (ride_can_have_multiple_circuits(ride)) + if (ride->CanHaveMultipleCircuits()) { window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS_LABEL].type = WWT_LABEL; window_ride_operating_widgets[WIDX_OPERATE_NUMBER_OF_CIRCUITS].type = WWT_SPINNER; @@ -3723,13 +3845,12 @@ static void window_ride_operating_invalidate(rct_window* w) */ static void window_ride_operating_paint(rct_window* w, rct_drawpixelinfo* dpi) { - Ride* ride; - uint16_t blockSections; - window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); - ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; // Horizontal rule between mode settings and depart settings gfx_fill_rect_inset( @@ -3740,7 +3861,7 @@ static void window_ride_operating_paint(rct_window* w, rct_drawpixelinfo* dpi) // Number of block sections if (ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) { - blockSections = ride->num_block_brakes + ride->num_stations; + auto blockSections = ride->num_block_brakes + ride->num_stations; gfx_draw_string_left( dpi, STR_BLOCK_SECTIONS, &blockSections, COLOUR_BLACK, w->x + 21, ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED ? w->y + 89 : w->y + 61); @@ -3757,13 +3878,12 @@ static void window_ride_operating_paint(rct_window* w, rct_drawpixelinfo* dpi) */ static void window_ride_locate_mechanic(rct_window* w) { - Ride* ride; - Peep* mechanic; - - ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; // First check if there is a mechanic assigned - mechanic = ride_get_assigned_mechanic(ride); + Peep* mechanic = ride_get_assigned_mechanic(ride); // Otherwise find the closest mechanic if (mechanic == nullptr) @@ -3848,12 +3968,13 @@ static void window_ride_maintenance_resize(rct_window* w) */ static void window_ride_maintenance_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { - Ride* ride = get_ride(w->number); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - if (rideEntry == nullptr) - { + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) return; - } rct_widget* dropdownWidget = widget; int32_t j, num_items; @@ -3955,9 +4076,13 @@ static void window_ride_maintenance_dropdown(rct_window* w, rct_widgetindex widg if (dropdownIndex == -1) return; - rct_vehicle* vehicle; - Ride* ride = get_ride(w->number); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; switch (widgetIndex) { @@ -3968,6 +4093,7 @@ static void window_ride_maintenance_dropdown(rct_window* w, rct_widgetindex widg case WIDX_FORCE_BREAKDOWN: if (dropdownIndex == 0) { + rct_vehicle* vehicle; switch (ride->breakdown_reason_pending) { case BREAKDOWN_SAFETY_CUT_OUT: @@ -4050,17 +4176,15 @@ static void window_ride_maintenance_dropdown(rct_window* w, rct_widgetindex widg */ static void window_ride_maintenance_update(rct_window* w) { - Ride* ride; - w->frame_no++; window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_4); - ride = get_ride(w->number); - if (ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAINTENANCE) + auto ride = get_ride(w->number); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_MAINTENANCE) { ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_MAINTENANCE; - window_invalidate(w); + w->Invalidate(); } } @@ -4070,9 +4194,7 @@ static void window_ride_maintenance_update(rct_window* w) */ static void window_ride_maintenance_invalidate(rct_window* w) { - rct_widget* widgets; - - widgets = window_ride_page_widgets[w->page]; + auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; @@ -4081,9 +4203,11 @@ static void window_ride_maintenance_invalidate(rct_window* w) window_ride_set_pressed_tab(w); - Ride* ride = get_ride(w->number); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + + ride->FormatNameTo(gCommonFormatArgs); window_ride_maintenance_widgets[WIDX_INSPECTION_INTERVAL].text = RideInspectionIntervalNames[ride->inspection_interval]; @@ -4120,7 +4244,9 @@ static void window_ride_maintenance_paint(rct_window* w, rct_drawpixelinfo* dpi) window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; // Locate mechanic button image rct_widget* widget = &window_ride_maintenance_widgets[WIDX_LOCATE_MECHANIC]; @@ -4217,8 +4343,7 @@ static void window_ride_maintenance_paint(rct_window* w, rct_drawpixelinfo* dpi) auto peep = (&(get_sprite(ride->mechanic)->peep))->AsStaff(); if (peep != nullptr && peep->IsMechanic()) { - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); + peep->FormatNameTo(gCommonFormatArgs); gfx_draw_string_left_wrapped(dpi, gCommonFormatArgs, x + 4, y, 280, stringId, COLOUR_BLACK); } } @@ -4288,7 +4413,8 @@ static void window_ride_set_track_colour_scheme(rct_window* w, int32_t x, int32_ z = tileElement->base_height * 8; direction = tileElement->GetDirection(); - auto gameAction = RideSetColourSchemeAction(x, y, z, direction, tileElement->AsTrack()->GetTrackType(), newColourScheme); + auto gameAction = RideSetColourSchemeAction( + CoordsXYZD{ x, y, z, static_cast(direction) }, tileElement->AsTrack()->GetTrackType(), newColourScheme); GameActions::Execute(&gameAction); } @@ -4354,18 +4480,20 @@ static void window_ride_colour_resize(rct_window* w) */ static void window_ride_colour_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { - Ride* ride; - uint16_t colourSchemeIndex; vehicle_colour vehicleColour; - rct_widget* dropdownWidget; - rct_ride_entry* rideEntry; int32_t i, numItems; rct_string_id stringId; - ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); - colourSchemeIndex = w->ride_colour; - dropdownWidget = widget - 1; + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + auto colourSchemeIndex = w->ride_colour; + auto dropdownWidget = widget - 1; switch (widgetIndex) { @@ -4491,7 +4619,7 @@ static void window_ride_colour_dropdown(rct_window* w, rct_widgetindex widgetInd { case WIDX_TRACK_COLOUR_SCHEME_DROPDOWN: w->ride_colour = (uint16_t)dropdownIndex; - window_invalidate(w); + w->Invalidate(); break; case WIDX_TRACK_MAIN_COLOUR: { @@ -4552,7 +4680,7 @@ static void window_ride_colour_dropdown(rct_window* w, rct_widgetindex widgetInd break; case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN: w->vehicleIndex = dropdownIndex; - window_invalidate(w); + w->Invalidate(); break; case WIDX_VEHICLE_MAIN_COLOUR: { @@ -4616,13 +4744,10 @@ static void window_ride_colour_tooldrag(rct_window* w, rct_widgetindex widgetInd */ static void window_ride_colour_invalidate(rct_window* w) { - rct_widget* widgets; - rct_ride_entry* rideEntry; - Ride* ride; TrackColour trackColour; vehicle_colour vehicleColour; - widgets = window_ride_page_widgets[w->page]; + auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; @@ -4631,11 +4756,16 @@ static void window_ride_colour_invalidate(rct_window* w) window_ride_set_pressed_tab(w); - ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + w->widgets[WIDX_TITLE].text = STR_ARG_16_STRINGID; + ride->FormatNameTo(gCommonFormatArgs + 16); // Track colours int32_t colourScheme = w->ride_colour; @@ -4844,11 +4974,10 @@ static void window_ride_colour_paint(rct_window* w, rct_drawpixelinfo* dpi) // TODO: This should use lists and identified sprites rct_drawpixelinfo clippedDpi; rct_widget* widget; - Ride* ride; - rct_ride_entry* rideEntry; - ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); @@ -4863,7 +4992,8 @@ static void window_ride_colour_paint(rct_window* w, rct_drawpixelinfo* dpi) auto trackColour = ride_get_track_colour(ride, w->ride_colour); // - if (rideEntry->shop_item == SHOP_ITEM_NONE) + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr || rideEntry->shop_item == SHOP_ITEM_NONE) { int32_t x = w->x + widget->left; int32_t y = w->y + widget->top; @@ -4899,7 +5029,7 @@ static void window_ride_colour_paint(rct_window* w, rct_drawpixelinfo* dpi) uint8_t shopItem = rideEntry->shop_item_secondary == SHOP_ITEM_NONE ? rideEntry->shop_item : rideEntry->shop_item_secondary; - int32_t spriteIndex = ShopItemImage[shopItem]; + int32_t spriteIndex = ShopItems[shopItem].Image; spriteIndex |= SPRITE_ID_PALETTE_COLOUR_1(ride->track_colour[0].main); gfx_draw_sprite(dpi, spriteIndex, x, y, 0); @@ -4950,27 +5080,27 @@ static void window_ride_colour_paint(rct_window* w, rct_drawpixelinfo* dpi) */ static void window_ride_colour_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) { - Ride* ride; - rct_ride_entry* rideEntry; - rct_widget* vehiclePreviewWidget; - int32_t trainCarIndex, x, y, spriteIndex; - vehicle_colour vehicleColour; + auto ride = get_ride(w->number); + if (ride == nullptr) + return; - ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); - vehiclePreviewWidget = &window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW]; - vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex); + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + auto vehiclePreviewWidget = &window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW]; + auto vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex); // Background colour gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, PALETTE_INDEX_12); // ? - x = (vehiclePreviewWidget->right - vehiclePreviewWidget->left) / 2; - y = vehiclePreviewWidget->bottom - vehiclePreviewWidget->top - 15; + int32_t x = (vehiclePreviewWidget->right - vehiclePreviewWidget->left) / 2; + int32_t y = vehiclePreviewWidget->bottom - vehiclePreviewWidget->top - 15; // ? - trainCarIndex = (ride->colour_scheme_type & 3) == RIDE_COLOUR_SCHEME_DIFFERENT_PER_CAR ? w->vehicleIndex - : rideEntry->tab_vehicle; + auto trainCarIndex = (ride->colour_scheme_type & 3) == RIDE_COLOUR_SCHEME_DIFFERENT_PER_CAR ? w->vehicleIndex + : rideEntry->tab_vehicle; rct_ride_entry_vehicle* rideVehicleEntry = &rideEntry->vehicles[ride_entry_get_vehicle_at_position( ride->subtype, ride->num_cars_per_train, trainCarIndex)]; @@ -4978,7 +5108,7 @@ static void window_ride_colour_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi y += rideVehicleEntry->tab_height; // Draw the coloured spinning vehicle - spriteIndex = (rideVehicleEntry->flags & VEHICLE_ENTRY_FLAG_11) ? w->frame_no / 4 : w->frame_no / 2; + uint32_t spriteIndex = (rideVehicleEntry->flags & VEHICLE_ENTRY_FLAG_11) ? w->frame_no / 4 : w->frame_no / 2; spriteIndex &= rideVehicleEntry->rotation_frame_mask; spriteIndex *= rideVehicleEntry->base_num_frames; spriteIndex += rideVehicleEntry->base_image_id; @@ -5008,10 +5138,12 @@ static uint8_t window_ride_current_music_style_order[42]; */ static void window_ride_toggle_music(rct_window* w) { - Ride* ride = get_ride(w->number); - - int32_t activateMusic = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) ? 0 : 1; - set_operating_setting(w->number, RideSetSetting::Music, activateMusic); + auto ride = get_ride(w->number); + if (ride != nullptr) + { + int32_t activateMusic = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) ? 0 : 1; + set_operating_setting(w->number, RideSetSetting::Music, activateMusic); + } } /** @@ -5059,14 +5191,13 @@ static void window_ride_music_resize(rct_window* w) */ static void window_ride_music_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { - rct_widget* dropdownWidget; - int32_t i; - if (widgetIndex != WIDX_MUSIC_DROPDOWN) return; - dropdownWidget = widget - 1; - Ride* ride = get_ride(w->number); + auto dropdownWidget = widget - 1; + auto ride = get_ride(w->number); + if (ride == nullptr) + return; int32_t numItems = 0; if (ride->type == RIDE_TYPE_MERRY_GO_ROUND) @@ -5084,7 +5215,7 @@ static void window_ride_music_mousedown(rct_window* w, rct_widgetindex widgetInd window_ride_current_music_style_order[numItems++] = MUSIC_STYLE_CUSTOM_MUSIC_2; } - for (i = 0; i < numItems; i++) + for (auto i = 0; i < numItems; i++) { gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; gDropdownItemsArgs[i] = MusicStyleNames[window_ride_current_music_style_order[i]]; @@ -5094,7 +5225,7 @@ static void window_ride_music_mousedown(rct_window* w, rct_widgetindex widgetInd w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], 0, DROPDOWN_FLAG_STAY_OPEN, numItems, widget->right - dropdownWidget->left); - for (i = 0; i < numItems; i++) + for (auto i = 0; i < numItems; i++) { if (window_ride_current_music_style_order[i] == ride->music) { @@ -5135,10 +5266,7 @@ static void window_ride_music_update(rct_window* w) */ static void window_ride_music_invalidate(rct_window* w) { - rct_widget* widgets; - int32_t isMusicActivated; - - widgets = window_ride_page_widgets[w->page]; + auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; @@ -5147,15 +5275,17 @@ static void window_ride_music_invalidate(rct_window* w) window_ride_set_pressed_tab(w); - Ride* ride = get_ride(w->number); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + + ride->FormatNameTo(gCommonFormatArgs); // Set selected music window_ride_music_widgets[WIDX_MUSIC].text = MusicStyleNames[ride->music]; // Set music activated - isMusicActivated = ride->lifecycle_flags & (RIDE_LIFECYCLE_MUSIC); + auto isMusicActivated = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) != 0; if (isMusicActivated) { w->pressed_widgets |= (1 << WIDX_PLAY_MUSIC); @@ -5276,13 +5406,46 @@ void window_ride_measurements_design_cancel() } } +static void TrackDesignCallback(int32_t result, [[maybe_unused]] const utf8* path) +{ + if (result == MODAL_RESULT_OK) + { + track_repository_scan(); + } + gfx_invalidate_screen(); +}; + /** * * rct2: 0x006AD4CD */ static void window_ride_measurements_design_save(rct_window* w) { - track_design_save((uint8_t)w->number); + Ride* ride = get_ride(w->number); + _trackDesign = ride->SaveToTrackDesign(); + if (!_trackDesign) + { + return; + } + + if (gTrackDesignSaveMode) + { + auto errMessage = _trackDesign->CreateTrackDesignScenery(); + if (errMessage != STR_NONE) + { + context_show_error(STR_CANT_SAVE_TRACK_DESIGN, errMessage); + return; + } + } + + auto trackName = ride->GetName(); + auto intent = Intent(WC_LOADSAVE); + intent.putExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK); + intent.putExtra(INTENT_EXTRA_TRACK_DESIGN, _trackDesign.get()); + intent.putExtra(INTENT_EXTRA_PATH, trackName); + intent.putExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast(&TrackDesignCallback)); + + context_open_intent(&intent); } /** @@ -5350,7 +5513,9 @@ static void window_ride_measurements_mousedown(rct_window* w, rct_widgetindex wi if (widgetIndex != WIDX_SAVE_TRACK_DESIGN) return; - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; gDropdownItemsFormat[0] = STR_SAVE_TRACK_DESIGN_ITEM; gDropdownItemsFormat[1] = STR_SAVE_TRACK_DESIGN_WITH_SCENERY_ITEM; @@ -5383,7 +5548,9 @@ static void window_ride_measurements_dropdown(rct_window* w, rct_widgetindex wid dropdownIndex = gDropdownHighlightedIndex; if (dropdownIndex == 0) - track_design_save((uint8_t)w->number); + { + window_ride_measurements_design_save(w); + } else setup_scenery_selection(w); } @@ -5421,7 +5588,7 @@ static void window_ride_measurements_tooldown(rct_window* w, rct_widgetindex wid case VIEWPORT_INTERACTION_ITEM_WALL: case VIEWPORT_INTERACTION_ITEM_FOOTPATH: _collectTrackDesignScenery = !track_design_save_contains_tile_element(tileElement); - track_design_save_select_tile_element(interactionType, mapX, mapY, tileElement, _collectTrackDesignScenery); + track_design_save_select_tile_element(interactionType, { mapX, mapY }, tileElement, _collectTrackDesignScenery); break; } } @@ -5444,7 +5611,7 @@ static void window_ride_measurements_tooldrag(rct_window* w, rct_widgetindex wid case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: case VIEWPORT_INTERACTION_ITEM_WALL: case VIEWPORT_INTERACTION_ITEM_FOOTPATH: - track_design_save_select_tile_element(interactionType, mapX, mapY, tileElement, _collectTrackDesignScenery); + track_design_save_select_tile_element(interactionType, { mapX, mapY }, tileElement, _collectTrackDesignScenery); break; } } @@ -5464,9 +5631,7 @@ static void window_ride_measurements_toolabort(rct_window* w, rct_widgetindex wi */ static void window_ride_measurements_invalidate(rct_window* w) { - rct_widget* widgets; - - widgets = window_ride_page_widgets[w->page]; + auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; @@ -5475,9 +5640,11 @@ static void window_ride_measurements_invalidate(rct_window* w) window_ride_set_pressed_tab(w); - Ride* ride = get_ride(w->number); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + + ride->FormatNameTo(gCommonFormatArgs); window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].tooltip = STR_SAVE_TRACK_DESIGN_NOT_POSSIBLE; window_ride_measurements_widgets[WIDX_SAVE_TRACK_DESIGN].type = WWT_EMPTY; @@ -5537,7 +5704,10 @@ static void window_ride_measurements_paint(rct_window* w, rct_drawpixelinfo* dpi } else { - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + int32_t x = w->x + window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].left + 4; int32_t y = w->y + window_ride_measurements_widgets[WIDX_PAGE_BACKGROUND].top + 4; @@ -5582,7 +5752,7 @@ static void window_ride_measurements_paint(rct_window* w, rct_drawpixelinfo* dpi if (ride->type == RIDE_TYPE_MINI_GOLF) { // Holes - holes = ride->holes & 0x1F; + holes = ride->holes; gfx_draw_string_left(dpi, STR_HOLES, &holes, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } @@ -5706,7 +5876,7 @@ static void window_ride_measurements_paint(rct_window* w, rct_drawpixelinfo* dpi if (ride->type != RIDE_TYPE_MINI_GOLF) { // Inversions - inversions = ride->inversions & 0x1F; + inversions = ride->inversions; if (inversions != 0) { gfx_draw_string_left(dpi, STR_INVERSIONS, &inversions, COLOUR_BLACK, x, y); @@ -5749,7 +5919,7 @@ static void window_ride_set_graph(rct_window* w, int32_t type) w->list_information_type &= 0xFF00; w->list_information_type |= type; } - window_invalidate(w); + w->Invalidate(); } /** @@ -5817,7 +5987,6 @@ static void window_ride_graphs_mousedown(rct_window* w, rct_widgetindex widgetIn static void window_ride_graphs_update(rct_window* w) { rct_widget* widget; - rct_ride_measurement* measurement; int32_t x; w->frame_no++; @@ -5833,7 +6002,8 @@ static void window_ride_graphs_update(rct_window* w) auto ride = get_ride(w->number); if (ride != nullptr) { - measurement = ride_get_measurement(ride, nullptr); + RideMeasurement* measurement{}; + std::tie(measurement, std::ignore) = ride_get_measurement(ride); x = measurement == nullptr ? 0 : measurement->current_item - (((widget->right - widget->left) / 4) * 3); } } @@ -5848,8 +6018,6 @@ static void window_ride_graphs_update(rct_window* w) */ static void window_ride_graphs_scrollgetheight(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height) { - rct_ride_measurement* measurement; - window_event_invalidate_call(w); // Set minimum size @@ -5859,7 +6027,8 @@ static void window_ride_graphs_scrollgetheight(rct_window* w, int32_t scrollInde auto ride = get_ride(w->number); if (ride != nullptr) { - measurement = ride_get_measurement(ride, nullptr); + RideMeasurement* measurement{}; + std::tie(measurement, std::ignore) = ride_get_measurement(ride); if (measurement != nullptr) { *width = std::max(*width, measurement->num_items); @@ -5887,8 +6056,7 @@ static void window_ride_graphs_tooltip(rct_window* w, rct_widgetindex widgetInde auto ride = get_ride(w->number); if (ride != nullptr) { - rct_string_id message; - auto measurement = ride_get_measurement(ride, &message); + auto [measurement, message] = ride_get_measurement(ride); if (measurement != nullptr && (measurement->flags & RIDE_MEASUREMENT_FLAG_RUNNING)) { set_format_arg(4, uint16_t, measurement->vehicle_index + 1); @@ -5912,11 +6080,7 @@ static void window_ride_graphs_tooltip(rct_window* w, rct_widgetindex widgetInde */ static void window_ride_graphs_invalidate(rct_window* w) { - rct_widget* widgets; - Ride* ride; - int32_t x, y; - - widgets = window_ride_page_widgets[w->page]; + auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; @@ -5925,10 +6089,11 @@ static void window_ride_graphs_invalidate(rct_window* w) window_ride_set_pressed_tab(w); - ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs); // Set pressed graph button type w->pressed_widgets &= ~(1 << WIDX_GRAPH_VELOCITY); @@ -5950,8 +6115,8 @@ static void window_ride_graphs_invalidate(rct_window* w) } // Anchor graph widget - x = w->width - 4; - y = w->height - 18; + auto x = w->width - 4; + auto y = w->height - 18; window_ride_graphs_widgets[WIDX_GRAPH].right = x; window_ride_graphs_widgets[WIDX_GRAPH].bottom = y; @@ -5990,11 +6155,11 @@ static void window_ride_graphs_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi auto widget = &window_ride_graphs_widgets[WIDX_GRAPH]; auto stringId = STR_NONE; - rct_ride_measurement* measurement{}; + RideMeasurement* measurement{}; auto ride = get_ride(w->number); if (ride != nullptr) { - measurement = ride_get_measurement(ride, &stringId); + std::tie(measurement, stringId) = ride_get_measurement(ride); } if (measurement == nullptr) { @@ -6151,32 +6316,22 @@ static utf8 _moneyInputText[MONEY_STRING_MAXLENGTH]; static void update_same_price_throughout_flags(uint32_t shop_item) { - uint32_t newFlags; + uint64_t newFlags; if (shop_item_is_photo(shop_item)) { - newFlags = gSamePriceThroughoutParkA; - newFlags ^= (1 << SHOP_ITEM_PHOTO); - game_do_command(0, 1, 0, (0x2 << 8), GAME_COMMAND_SET_PARK_OPEN, newFlags, shop_item); - - newFlags = gSamePriceThroughoutParkB; - newFlags ^= (1 << (SHOP_ITEM_PHOTO2 - 32)) | (1 << (SHOP_ITEM_PHOTO3 - 32)) | (1 << (SHOP_ITEM_PHOTO4 - 32)); - game_do_command(0, 1, 0, (0x3 << 8), GAME_COMMAND_SET_PARK_OPEN, newFlags, shop_item); + newFlags = gSamePriceThroughoutPark; + newFlags ^= (1ULL << SHOP_ITEM_PHOTO) | (1ULL << SHOP_ITEM_PHOTO2) | (1ULL << SHOP_ITEM_PHOTO3) + | (1ULL << SHOP_ITEM_PHOTO4); + auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags); + GameActions::Execute(&parkSetParameter); } else { - if (shop_item < 32) - { - newFlags = gSamePriceThroughoutParkA; - newFlags ^= (1u << shop_item); - game_do_command(0, 1, 0, (0x2 << 8), GAME_COMMAND_SET_PARK_OPEN, newFlags, shop_item); - } - else - { - newFlags = gSamePriceThroughoutParkB; - newFlags ^= (1u << (shop_item - 32)); - game_do_command(0, 1, 0, (0x3 << 8), GAME_COMMAND_SET_PARK_OPEN, newFlags, shop_item); - } + newFlags = gSamePriceThroughoutPark; + newFlags ^= (1ULL << shop_item); + auto parkSetParameter = ParkSetParameterAction(ParkParameter::SamePriceInPark, newFlags); + GameActions::Execute(&parkSetParameter); } } @@ -6186,22 +6341,28 @@ static void update_same_price_throughout_flags(uint32_t shop_item) */ static void window_ride_income_toggle_primary_price(rct_window* w) { - Ride* ride; - rct_ride_entry* rideEntry; + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + uint32_t shop_item; - - ride = get_ride(w->number); - rideEntry = get_ride_entry(ride->subtype); - if (ride->type == RIDE_TYPE_TOILETS) { shop_item = SHOP_ITEM_ADMISSION; } else { - shop_item = rideEntry->shop_item; - if (shop_item == 0xFFFF) + auto rideEntry = get_ride_entry(ride->subtype); + if (rideEntry != nullptr) + { + shop_item = rideEntry->shop_item; + if (shop_item == 0xFFFF) + return; + } + else + { return; + } } update_same_price_throughout_flags(shop_item); @@ -6216,14 +6377,15 @@ static void window_ride_income_toggle_primary_price(rct_window* w) */ static void window_ride_income_toggle_secondary_price(rct_window* w) { - Ride* ride; - rct_ride_entry* rideEntry; - uint32_t shop_item; + auto ride = get_ride(w->number); + if (ride == nullptr) + return; - ride = get_ride(w->number); - rideEntry = get_ride_entry(ride->subtype); + auto rideEntry = get_ride_entry(ride->subtype); + if (rideEntry == nullptr) + return; - shop_item = rideEntry->shop_item_secondary; + auto shop_item = rideEntry->shop_item_secondary; if (shop_item == SHOP_ITEM_NONE) shop_item = RidePhotoItems[ride->type]; @@ -6248,7 +6410,10 @@ static void window_ride_income_increase_primary_price(rct_window* w) if (!window_ride_income_can_modify_primary_price(w)) return; - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + money16 price = ride->price; if (price < MONEY(20, 00)) price++; @@ -6265,7 +6430,10 @@ static void window_ride_income_decrease_primary_price(rct_window* w) if (!window_ride_income_can_modify_primary_price(w)) return; - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + money16 price = ride->price; if (price > MONEY(0, 00)) price--; @@ -6275,7 +6443,10 @@ static void window_ride_income_decrease_primary_price(rct_window* w) static money16 window_ride_income_get_secondary_price(rct_window* w) { - Ride* ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return 0; + money16 price = ride->price_secondary; return price; } @@ -6288,10 +6459,13 @@ static void window_ride_income_set_secondary_price(rct_window* w, money16 price) static bool window_ride_income_can_modify_primary_price(rct_window* w) { - Ride* ride = get_ride(w->number); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); + auto ride = get_ride(w->number); + if (ride == nullptr) + return false; - return park_ride_prices_unlocked() || ride->type == RIDE_TYPE_TOILETS || rideEntry->shop_item != SHOP_ITEM_NONE; + auto rideEntry = ride->GetRideEntry(); + return park_ride_prices_unlocked() || ride->type == RIDE_TYPE_TOILETS + || (rideEntry != nullptr && rideEntry->shop_item != SHOP_ITEM_NONE); } /** @@ -6350,11 +6524,13 @@ static void window_ride_income_mouseup(rct_window* w, rct_widgetindex widgetInde if (!window_ride_income_can_modify_primary_price(w)) return; - Ride* ride = get_ride(w->number); - - money_to_string((money32)ride->price, _moneyInputText, MONEY_STRING_MAXLENGTH, true); - window_text_input_raw_open( - w, WIDX_PRIMARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, _moneyInputText, MONEY_STRING_MAXLENGTH); + auto ride = get_ride(w->number); + if (ride != nullptr) + { + money_to_string((money32)ride->price, _moneyInputText, MONEY_STRING_MAXLENGTH, true); + window_text_input_raw_open( + w, WIDX_PRIMARY_PRICE, STR_ENTER_NEW_VALUE, STR_ENTER_NEW_VALUE, _moneyInputText, MONEY_STRING_MAXLENGTH); + } break; } case WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK: @@ -6413,17 +6589,15 @@ static void window_ride_income_mousedown(rct_window* w, rct_widgetindex widgetIn */ static void window_ride_income_update(rct_window* w) { - Ride* ride; - w->frame_no++; window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_9); - ride = get_ride(w->number); - if (ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_INCOME) + auto ride = get_ride(w->number); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_INCOME) { ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_INCOME; - window_invalidate(w); + w->Invalidate(); } } @@ -6457,11 +6631,7 @@ static void window_ride_income_textinput(rct_window* w, rct_widgetindex widgetIn */ static void window_ride_income_invalidate(rct_window* w) { - rct_widget* widgets; - rct_ride_entry* rideEntry; - int32_t primaryItem, secondaryItem; - - widgets = window_ride_page_widgets[w->page]; + auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; @@ -6470,11 +6640,16 @@ static void window_ride_income_invalidate(rct_window* w) window_ride_set_pressed_tab(w); - Ride* ride = get_ride(w->number); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; - rideEntry = get_ride_entry_by_ride(ride); + w->widgets[WIDX_TITLE].text = STR_ARG_14_STRINGID; + ride->FormatNameTo(gCommonFormatArgs + 14); + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; // Primary item w->pressed_widgets &= ~(1 << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); @@ -6501,7 +6676,7 @@ static void window_ride_income_invalidate(rct_window* w) if (ridePrimaryPrice == 0) window_ride_income_widgets[WIDX_PRIMARY_PRICE].text = STR_FREE; - primaryItem = SHOP_ITEM_ADMISSION; + uint8_t primaryItem = SHOP_ITEM_ADMISSION; if (ride->type == RIDE_TYPE_TOILETS || ((primaryItem = rideEntry->shop_item) != SHOP_ITEM_NONE)) { window_ride_income_widgets[WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK].type = WWT_CHECKBOX; @@ -6509,16 +6684,16 @@ static void window_ride_income_invalidate(rct_window* w) if (shop_item_has_common_price(primaryItem)) w->pressed_widgets |= (1 << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); - window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].text = ShopItemStringIds[primaryItem].price_label; + window_ride_income_widgets[WIDX_PRIMARY_PRICE_LABEL].text = ShopItems[primaryItem].Naming.PriceLabel; } // Get secondary item - secondaryItem = RidePhotoItems[ride->type]; + auto secondaryItem = RidePhotoItems[ride->type]; if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO)) { if ((secondaryItem = rideEntry->shop_item_secondary) != SHOP_ITEM_NONE) { - window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].text = ShopItemStringIds[secondaryItem].price_label; + window_ride_income_widgets[WIDX_SECONDARY_PRICE_LABEL].text = ShopItems[secondaryItem].Naming.PriceLabel; } } @@ -6562,8 +6737,6 @@ static void window_ride_income_invalidate(rct_window* w) */ static void window_ride_income_paint(rct_window* w, rct_drawpixelinfo* dpi) { - Ride* ride; - rct_ride_entry* rideEntry; rct_string_id stringId; money32 profit, costPerHour; int32_t x, y, primaryItem, secondaryItem; @@ -6571,8 +6744,13 @@ static void window_ride_income_paint(rct_window* w, rct_drawpixelinfo* dpi) window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); - ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; x = w->x + window_ride_income_widgets[WIDX_PAGE_BACKGROUND].left + 4; y = w->y + window_ride_income_widgets[WIDX_PAGE_BACKGROUND].top + 33; @@ -6584,7 +6762,7 @@ static void window_ride_income_paint(rct_window* w, rct_drawpixelinfo* dpi) profit = ride->price; stringId = STR_PROFIT_PER_ITEM_SOLD; - profit -= get_shop_item_cost(primaryItem); + profit -= ShopItems[primaryItem].Cost; if (profit < 0) { profit *= -1; @@ -6605,7 +6783,7 @@ static void window_ride_income_paint(rct_window* w, rct_drawpixelinfo* dpi) profit = ride->price_secondary; stringId = STR_PROFIT_PER_ITEM_SOLD; - profit -= get_shop_item_cost(secondaryItem); + profit -= ShopItems[secondaryItem].Cost; if (profit < 0) { profit *= -1; @@ -6711,8 +6889,6 @@ static void window_ride_customer_resize(rct_window* w) */ static void window_ride_customer_update(rct_window* w) { - Ride* ride; - w->var_492++; if (w->var_492 >= 24) w->var_492 = 0; @@ -6720,11 +6896,11 @@ static void window_ride_customer_update(rct_window* w) window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_10); - ride = get_ride(w->number); - if (ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_CUSTOMER) + auto ride = get_ride(w->number); + if (ride != nullptr && ride->window_invalidate_flags & RIDE_INVALIDATE_RIDE_CUSTOMER) { ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_CUSTOMER; - window_invalidate(w); + w->Invalidate(); } } @@ -6734,9 +6910,7 @@ static void window_ride_customer_update(rct_window* w) */ static void window_ride_customer_invalidate(rct_window* w) { - rct_widget* widgets; - - widgets = window_ride_page_widgets[w->page]; + auto widgets = window_ride_page_widgets[w->page]; if (w->widgets != widgets) { w->widgets = widgets; @@ -6745,24 +6919,26 @@ static void window_ride_customer_invalidate(rct_window* w) window_ride_set_pressed_tab(w); - Ride* ride = get_ride(w->number); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); - - window_ride_customer_widgets[WIDX_SHOW_GUESTS_THOUGHTS].type = WWT_FLATBTN; - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) + auto ride = get_ride(w->number); + if (ride != nullptr) { - window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WWT_EMPTY; - window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WWT_EMPTY; - } - else - { - window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WWT_FLATBTN; - window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WWT_FLATBTN; - } + ride->FormatNameTo(gCommonFormatArgs); - window_ride_anchor_border_widgets(w); - window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10); + window_ride_customer_widgets[WIDX_SHOW_GUESTS_THOUGHTS].type = WWT_FLATBTN; + if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) + { + window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WWT_EMPTY; + window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WWT_EMPTY; + } + else + { + window_ride_customer_widgets[WIDX_SHOW_GUESTS_ON_RIDE].type = WWT_FLATBTN; + window_ride_customer_widgets[WIDX_SHOW_GUESTS_QUEUING].type = WWT_FLATBTN; + } + + window_ride_anchor_border_widgets(w); + window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_10); + } } /** @@ -6771,7 +6947,6 @@ static void window_ride_customer_invalidate(rct_window* w) */ static void window_ride_customer_paint(rct_window* w, rct_drawpixelinfo* dpi) { - Ride* ride; int32_t x, y; uint8_t shopItem; int16_t popularity, satisfaction, queueTime, age; @@ -6781,12 +6956,15 @@ static void window_ride_customer_paint(rct_window* w, rct_drawpixelinfo* dpi) window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); - ride = get_ride(w->number); + auto ride = get_ride(w->number); + if (ride == nullptr) + return; + x = w->x + window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].left + 4; y = w->y + window_ride_customer_widgets[WIDX_PAGE_BACKGROUND].top + 4; // Customers currently on ride - if (gRideClassifications[ride->type] == RIDE_CLASS_RIDE) + if (ride->IsRide()) { int16_t customersOnRide = ride->num_riders; gfx_draw_string_left(dpi, STR_CUSTOMERS_ON_RIDE, &customersOnRide, COLOUR_BLACK, x, y); @@ -6827,19 +7005,19 @@ static void window_ride_customer_paint(rct_window* w, rct_drawpixelinfo* dpi) y += LIST_ROW_HEIGHT; // Queue time - if (gRideClassifications[ride->type] == RIDE_CLASS_RIDE) + if (ride->IsRide()) { - queueTime = ride_get_max_queue_time(ride); + queueTime = ride->GetMaxQueueTime(); stringId = queueTime == 1 ? STR_QUEUE_TIME_MINUTE : STR_QUEUE_TIME_MINUTES; y += gfx_draw_string_left_wrapped(dpi, &queueTime, x, y, 308, stringId, COLOUR_BLACK); y += 5; } // Primary shop items sold - shopItem = get_ride_entry_by_ride(ride)->shop_item; + shopItem = ride->GetRideEntry()->shop_item; if (shopItem != SHOP_ITEM_NONE) { - set_format_arg(0, rct_string_id, ShopItemStringIds[shopItem].plural); + set_format_arg(0, rct_string_id, ShopItems[shopItem].Naming.Plural); set_format_arg(2, uint32_t, ride->no_primary_items_sold); gfx_draw_string_left(dpi, STR_ITEMS_SOLD, gCommonFormatArgs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; @@ -6847,10 +7025,10 @@ static void window_ride_customer_paint(rct_window* w, rct_drawpixelinfo* dpi) // Secondary shop items sold / on-ride photos sold shopItem = (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) ? RidePhotoItems[ride->type] - : get_ride_entry_by_ride(ride)->shop_item_secondary; + : ride->GetRideEntry()->shop_item_secondary; if (shopItem != SHOP_ITEM_NONE) { - set_format_arg(0, rct_string_id, ShopItemStringIds[shopItem].plural); + set_format_arg(0, rct_string_id, ShopItems[shopItem].Naming.Plural); set_format_arg(2, uint32_t, ride->no_secondary_items_sold); gfx_draw_string_left(dpi, STR_ITEMS_SOLD, gCommonFormatArgs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; @@ -6861,7 +7039,7 @@ static void window_ride_customer_paint(rct_window* w, rct_drawpixelinfo* dpi) y += LIST_ROW_HEIGHT; // Guests favourite - if (gRideClassifications[ride->type] == RIDE_CLASS_RIDE) + if (ride->IsRide()) { stringId = ride->guests_favourite == 1 ? STR_FAVOURITE_RIDE_OF_GUEST : STR_FAVOURITE_RIDE_OF_GUESTS; gfx_draw_string_left(dpi, stringId, &ride->guests_favourite, COLOUR_BLACK, x, y); diff --git a/src/openrct2-ui/windows/RideConstruction.cpp b/src/openrct2-ui/windows/RideConstruction.cpp index 9e11e4bee9..d9bcdfbacc 100644 --- a/src/openrct2-ui/windows/RideConstruction.cpp +++ b/src/openrct2-ui/windows/RideConstruction.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,7 @@ enum { WIDX_SEAT_ROTATION_ANGLE_SPINNER, WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP, WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN, + WIDX_SIMULATE, }; validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_CONSTRUCT); @@ -112,8 +114,8 @@ static rct_widget window_ride_construction_widgets[] = { { WWT_IMGBTN, 1, 3, 162, 164, 333, 0xFFFFFFFF, STR_RIDE_CONSTRUCTION_CONSTRUCT_SELECTED_SECTION_TIP }, { WWT_FLATBTN, 1, 60, 105, 338, 361, SPR_DEMOLISH_CURRENT_SECTION, STR_RIDE_CONSTRUCTION_REMOVE_HIGHLIGHTED_SECTION_TIP }, { WWT_FLATBTN, 1, 50, 71, 29, 52, SPR_RIDE_CONSTRUCTION_LEFT_CURVE_LARGE, STR_RIDE_CONSTRUCTION_LEFT_CURVE_LARGE_TIP }, - { WWT_FLATBTN, 1, 20, 43, 338, 361, SPR_PREVIOUS, STR_RIDE_CONSTRUCTION_MOVE_TO_PREVIOUS_SECTION_TIP }, - { WWT_FLATBTN, 1, 122, 145, 338, 361, SPR_NEXT, STR_RIDE_CONSTRUCTION_MOVE_TO_NEXT_SECTION_TIP }, + { WWT_FLATBTN, 1, 30, 53, 338, 361, SPR_PREVIOUS, STR_RIDE_CONSTRUCTION_MOVE_TO_PREVIOUS_SECTION_TIP }, + { WWT_FLATBTN, 1, 112, 135, 338, 361, SPR_NEXT, STR_RIDE_CONSTRUCTION_MOVE_TO_NEXT_SECTION_TIP }, { WWT_GROUPBOX, 0, 3, 162, 362, 389, 0xFFFFFFFF, STR_NONE }, { WWT_BUTTON, 1, 9, 78, 372, 383, STR_RIDE_CONSTRUCTION_ENTRANCE, STR_RIDE_CONSTRUCTION_ENTRANCE_TIP }, { WWT_BUTTON, 1, 87, 156, 372, 383, STR_RIDE_CONSTRUCTION_EXIT, STR_RIDE_CONSTRUCTION_EXIT_TIP }, @@ -123,6 +125,7 @@ static rct_widget window_ride_construction_widgets[] = { { WWT_FLATBTN, 1, 123, 146, 132, 155, SPR_RIDE_CONSTRUCTION_O_SHAPED_TRACK, STR_RIDE_CONSTRUCTION_O_SHAPED_ENCLOSED_TRACK_TIP }, { WWT_GROUPBOX, 0, 96, 162, 120, 160, STR_RIDE_CONSTRUCTION_SEAT_ROT, STR_NONE }, SPINNER_WIDGETS (1, 101, 158, 138, 149, 0, STR_RIDE_CONSTRUCTION_SELECT_SEAT_ROTATION_ANGLE_TIP), + { WWT_FLATBTN, 1, 139, 162, 338, 361, SPR_G2_SIMULATE, STR_SIMULATE_RIDE_TIP }, { WIDGETS_END } }; @@ -477,7 +480,7 @@ static void window_ride_construction_show_special_track_dropdown(rct_window* w, static void ride_selected_track_set_seat_rotation(int32_t seatRotation); static void loc_6C7502(int32_t al); static void ride_construction_set_brakes_speed(int32_t brakesSpeed); -static void ride_construction_tooldown_entrance_exit(int32_t screenX, int32_t screenY); +static void ride_construction_tooldown_entrance_exit(ScreenCoordsXY screenCoords); static uint8_t _currentPossibleRideConfigurations[32]; @@ -519,8 +522,9 @@ rct_window* window_ride_construction_open() ride_id_t rideIndex = _currentRideIndex; close_ride_window_for_construction(rideIndex); - rct_window* w; - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return nullptr; _stationConstructed = ride->num_stations != 0; _deferClose = false; @@ -530,7 +534,8 @@ rct_window* window_ride_construction_open() return context_open_window_view(WV_MAZE_CONSTRUCTION); } - w = window_create(0, 29, 166, 394, &window_ride_construction_events, WC_RIDE_CONSTRUCTION, WF_NO_AUTO_CLOSE); + auto w = window_create( + ScreenCoordsXY(0, 29), 166, 394, &window_ride_construction_events, WC_RIDE_CONSTRUCTION, WF_NO_AUTO_CLOSE); w->widgets = window_ride_construction_widgets; w->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_LEFT_CURVE_VERY_SMALL) | (1ULL << WIDX_LEFT_CURVE_SMALL) @@ -539,8 +544,8 @@ rct_window* window_ride_construction_open() | (1ULL << WIDX_SLOPE_DOWN) | (1ULL << WIDX_LEVEL) | (1ULL << WIDX_SLOPE_UP) | (1ULL << WIDX_SLOPE_UP_STEEP) | (1ULL << WIDX_CHAIN_LIFT) | (1ULL << WIDX_BANK_LEFT) | (1ULL << WIDX_BANK_STRAIGHT) | (1ULL << WIDX_BANK_RIGHT) | (1ULL << WIDX_CONSTRUCT) | (1ULL << WIDX_DEMOLISH) | (1ULL << WIDX_LEFT_CURVE_LARGE) | (1ULL << WIDX_PREVIOUS_SECTION) - | (1ULL << WIDX_NEXT_SECTION) | (1ULL << WIDX_ENTRANCE) | (1ULL << WIDX_EXIT) | (1ULL << WIDX_RIGHT_CURVE_LARGE) - | (1ULL << WIDX_ROTATE) | (1ULL << WIDX_U_TRACK) | (1ULL << WIDX_O_TRACK) + | (1ULL << WIDX_NEXT_SECTION) | (1ULL << WIDX_SIMULATE) | (1ULL << WIDX_ENTRANCE) | (1ULL << WIDX_EXIT) + | (1ULL << WIDX_RIGHT_CURVE_LARGE) | (1ULL << WIDX_ROTATE) | (1ULL << WIDX_U_TRACK) | (1ULL << WIDX_O_TRACK) | (1ULL << WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP) | (1ULL << WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN); window_init_scroll_widgets(w); @@ -604,7 +609,7 @@ static void window_ride_construction_close(rct_window* w) // If we demolish a ride all windows will be closed including the construction window, // the ride at this point is already gone. auto ride = get_ride(_currentRideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + if (ride == nullptr) { return; } @@ -624,7 +629,7 @@ static void window_ride_construction_close(rct_window* w) } } - ride_set_to_default_inspection_interval(ride); + ride->SetToDefaultInspectionInterval(); auto intent = Intent(WC_RIDE); intent.putExtra(INTENT_EXTRA_RIDE_ID, ride->id); context_open_intent(&intent); @@ -673,6 +678,16 @@ static void window_ride_construction_mouseup(rct_window* w, rct_widgetindex widg case WIDX_EXIT: window_ride_construction_exit_click(w); break; + case WIDX_SIMULATE: + { + auto ride = get_ride(_currentRideIndex); + if (ride != nullptr) + { + auto status = ride->status == RIDE_STATUS_SIMULATING ? RIDE_STATUS_CLOSED : RIDE_STATUS_SIMULATING; + ride_set_status(ride, status); + } + break; + } } } @@ -689,7 +704,10 @@ static void window_ride_construction_resize(rct_window* w) w->enabled_widgets |= (1 << WIDX_CONSTRUCT); } - Ride* ride = get_ride(_currentRideIndex); + auto ride = get_ride(_currentRideIndex); + if (ride == nullptr) + return; + int32_t rideType = ride_get_alternative_type(ride); uint64_t disabledWidgets = 0; @@ -1232,7 +1250,9 @@ static void window_ride_construction_resize(rct_window* w) */ static void window_ride_construction_mousedown(rct_window* w, rct_widgetindex widgetIndex, rct_widget* widget) { - Ride* ride = get_ride(_currentRideIndex); + auto ride = get_ride(_currentRideIndex); + if (ride == nullptr) + return; window_ride_construction_update_enabled_track_pieces(); switch (widgetIndex) @@ -1653,8 +1673,8 @@ static void window_ride_construction_dropdown(rct_window* w, rct_widgetindex wid _currentTrackCurve = trackPiece | 0x100; window_ride_construction_update_active_elements(); } -static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, const GameActionResult* result); -static void RideConstructPlacedBackwardGameActionCallback(const GameAction* ga, const GameActionResult* result); +static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, const TrackPlaceActionResult* result); +static void RideConstructPlacedBackwardGameActionCallback(const GameAction* ga, const TrackPlaceActionResult* result); static void CloseConstructWindowOnCompletion(Ride* ride); static void CloseConstructWindowOnCompletion(Ride* ride) @@ -1676,7 +1696,7 @@ static void CloseConstructWindowOnCompletion(Ride* ride) } } -static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, const GameActionResult* result) +static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, const TrackPlaceActionResult* result) { if (result->Error != GA_ERROR::OK) { @@ -1723,7 +1743,7 @@ static void RideConstructPlacedForwardGameActionCallback(const GameAction* ga, c CloseConstructWindowOnCompletion(ride); } -static void RideConstructPlacedBackwardGameActionCallback(const GameAction* ga, const GameActionResult* result) +static void RideConstructPlacedBackwardGameActionCallback(const GameAction* ga, const TrackPlaceActionResult* result) { if (result->Error != GA_ERROR::OK) { @@ -1790,7 +1810,6 @@ static void window_ride_construction_construct(rct_window* w) auto trackPlaceAction = TrackPlaceAction( rideIndex, trackType, { x, y, z, static_cast(trackDirection) }, (properties)&0xFF, (properties >> 8) & 0x0F, (properties >> 12) & 0x0F, liftHillAndAlternativeState); - if (_rideConstructionState == RIDE_CONSTRUCTION_STATE_BACK) { trackPlaceAction.SetCallback(RideConstructPlacedBackwardGameActionCallback); @@ -1801,20 +1820,29 @@ static void window_ride_construction_construct(rct_window* w) } auto res = GameActions::Execute(&trackPlaceAction); // Used by some functions - _trackPlaceCost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; + if (res->Error != GA_ERROR::OK) + { + gGameCommandErrorText = res->ErrorMessage; + _trackPlaceCost = MONEY32_UNDEFINED; + } + else + { + gGameCommandErrorText = STR_NONE; + _trackPlaceCost = res->Cost; + } if (res->Error != GA_ERROR::OK) { return; } - audio_play_sound_at_location(SOUND_PLACE_ITEM, x, y, z); + audio_play_sound_at_location(SoundId::PlaceItem, { x, y, z }); if (network_get_mode() != NETWORK_MODE_NONE) { _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_TRACK_PLACE_ACTION_QUEUED; } - if (gTrackGroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) + if (dynamic_cast(res.get())->GroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) { viewport_set_visibility(1); } @@ -1923,12 +1951,19 @@ static void window_ride_construction_mouseup_demolish(rct_window* w) { _currentTrackBegin.x, _currentTrackBegin.y, _currentTrackBegin.z, _currentTrackPieceDirection }); trackRemoveAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) { - _stationConstructed = get_ride(w->number)->num_stations != 0; - window_ride_construction_mouseup_demolish_next_piece(x, y, z, direction, type); if (result->Error != GA_ERROR::OK) { window_ride_construction_update_active_elements(); } + else + { + auto ride = get_ride(w->number); + if (ride != nullptr) + { + _stationConstructed = ride->num_stations != 0; + window_ride_construction_mouseup_demolish_next_piece(x, y, z, direction, type); + } + } }); GameActions::Execute(&trackRemoveAction); @@ -1956,7 +1991,7 @@ static void window_ride_construction_entrance_click(rct_window* w) if (tool_set(w, WIDX_ENTRANCE, TOOL_CROSSHAIR)) { auto ride = get_ride(_currentRideIndex); - if (!ride_try_get_origin_element(ride, nullptr)) + if (ride != nullptr && !ride_try_get_origin_element(ride, nullptr)) { ride_initialise_construction_window(ride); } @@ -2013,11 +2048,13 @@ static void window_ride_construction_exit_click(rct_window* w) */ static void window_ride_construction_update(rct_window* w) { - Ride* ride = get_ride(_currentRideIndex); + auto ride = get_ride(_currentRideIndex); + if (ride == nullptr) + return; // Close construction window if ride is not closed, // editing ride while open will cause many issues until properly handled - if (ride->status != RIDE_STATUS_CLOSED) + if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING) { window_close(w); return; @@ -2073,14 +2110,14 @@ static void window_ride_construction_update(rct_window* w) static bool ride_get_place_position_from_screen_position(int32_t screenX, int32_t screenY, int32_t* outX, int32_t* outY) { int16_t mapX, mapY, mapZ; - int32_t interactionType, direction; - TileElement* tileElement; - rct_viewport* viewport; + int32_t interactionType; + rct_viewport* viewport = nullptr; if (!_trackPlaceCtrlState) { if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_COPY_Z) { + TileElement* tileElement; get_map_coordinates_from_pos(screenX, screenY, 0xFCCA, &mapX, &mapY, &interactionType, &tileElement, &viewport); if (interactionType != 0) { @@ -2137,15 +2174,15 @@ static bool ride_get_place_position_from_screen_position(int32_t screenX, int32_ if (!_trackPlaceCtrlState) { - sub_68A15E(screenX, screenY, &mapX, &mapY, &direction, &tileElement); + sub_68A15E(screenX, screenY, &mapX, &mapY); if (mapX == LOCATION_NULL) return false; _trackPlaceZ = 0; if (_trackPlaceShiftState) { - tileElement = map_get_surface_element_at(mapX >> 5, mapY >> 5); - mapZ = floor2(tileElement->base_height * 8, 16); + auto surfaceElement = map_get_surface_element_at(mapX >> 5, mapY >> 5); + mapZ = floor2(surfaceElement->base_height * 8, 16); mapZ += _trackPlaceShiftZ; mapZ = std::max(mapZ, 16); _trackPlaceZ = mapZ; @@ -2179,11 +2216,11 @@ static void window_ride_construction_toolupdate(rct_window* w, rct_widgetindex w switch (widgetIndex) { case WIDX_CONSTRUCT: - ride_construction_toolupdate_construct(x, y); + ride_construction_toolupdate_construct(ScreenCoordsXY(x, y)); break; case WIDX_ENTRANCE: case WIDX_EXIT: - ride_construction_toolupdate_entrance_exit(x, y); + ride_construction_toolupdate_entrance_exit(ScreenCoordsXY(x, y)); break; } } @@ -2197,11 +2234,11 @@ static void window_ride_construction_tooldown(rct_window* w, rct_widgetindex wid switch (widgetIndex) { case WIDX_CONSTRUCT: - ride_construction_tooldown_construct(x, y); + ride_construction_tooldown_construct(ScreenCoordsXY(x, y)); break; case WIDX_ENTRANCE: case WIDX_EXIT: - ride_construction_tooldown_entrance_exit(x, y); + ride_construction_tooldown_entrance_exit(ScreenCoordsXY(x, y)); break; } } @@ -2212,12 +2249,13 @@ static void window_ride_construction_tooldown(rct_window* w, rct_widgetindex wid */ static void window_ride_construction_invalidate(rct_window* w) { - Ride* ride; - rct_string_id stringId; + auto ride = get_ride(_currentRideIndex); + if (ride == nullptr) + { + return; + } - ride = get_ride(_currentRideIndex); - - stringId = STR_RIDE_CONSTRUCTION_SPECIAL; + rct_string_id stringId = STR_RIDE_CONSTRUCTION_SPECIAL; if (_currentTrackCurve & 0x100) { stringId = RideConfigurationStringIds[_currentTrackCurve & 0xFF]; @@ -2243,9 +2281,24 @@ static void window_ride_construction_invalidate(rct_window* w) window_ride_construction_widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].text = RideConstructionSeatAngleRotationStrings [_currentSeatRotationAngle]; + // Simulate button + auto& simulateWidget = w->widgets[WIDX_SIMULATE]; + simulateWidget.type = WWT_EMPTY; + if (ride->SupportsStatus(RIDE_STATUS_SIMULATING)) + { + simulateWidget.type = WWT_FLATBTN; + if (ride->status == RIDE_STATUS_SIMULATING) + { + w->pressed_widgets |= (1ULL << WIDX_SIMULATE); + } + else + { + w->pressed_widgets &= ~(1ULL << WIDX_SIMULATE); + } + } + // Set window title arguments - set_format_arg(4, rct_string_id, ride->name); - set_format_arg(6, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs + 4); } /** @@ -2298,12 +2351,11 @@ static void window_ride_construction_draw_track_piece( rct_window* w, rct_drawpixelinfo* dpi, ride_id_t rideIndex, int32_t trackType, int32_t trackDirection, int32_t unknown, int32_t width, int32_t height) { - const rct_preview_track* trackBlock; - Ride* ride; + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; - ride = get_ride(rideIndex); - - trackBlock = get_track_def_from_ride(ride, trackType); + auto trackBlock = get_track_def_from_ride(ride, trackType); while ((trackBlock + 1)->index != 0xFF) trackBlock++; @@ -2316,26 +2368,7 @@ static void window_ride_construction_draw_track_piece( y = 0; } - int16_t tmp; - switch (trackDirection & 3) - { - case 1: - tmp = x; - x = y; - y = -tmp; - break; - case 2: - x = -x; - y = -y; - break; - case 3: - tmp = x; - x = -y; - y = tmp; - break; - case 0: - break; - } + rotate_map_coordinates(&x, &y, trackDirection & 3); // this is actually case 0, but the other cases all jump to it x = 4112 + (x / 2); y = 4112 + (y / 2); @@ -2360,7 +2393,7 @@ static void window_ride_construction_draw_track_piece( } static TileElement _tempTrackTileElement; -static TileElement _tempSideTrackTileElement = { 0x80, 0x8F, 128, 128, 0, 0, 0, 0 }; +static TileElement _tempSideTrackTileElement = { 0x80, 0x8F, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static TileElement* _backupTileElementArrays[5]; /** @@ -2374,14 +2407,12 @@ static void sub_6CBCE2( rct_drawpixelinfo* dpi, ride_id_t rideIndex, int32_t trackType, int32_t trackDirection, int32_t edx, int32_t originX, int32_t originY, int32_t originZ) { - Ride* ride; - const rct_preview_track* trackBlock; - int32_t offsetX, offsetY; - paint_session* session = paint_session_alloc(dpi, 0); trackDirection &= 3; - ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; int16_t preserveMapSizeUnits = gMapSizeUnits; int16_t preserveMapSizeMinus2 = gMapSizeMinus2; @@ -2393,37 +2424,19 @@ static void sub_6CBCE2( gMapSize = 256; gMapSizeMaxXY = (256 * 32) - 1; - trackBlock = get_track_def_from_ride(ride, trackType); + auto trackBlock = get_track_def_from_ride(ride, trackType); while (trackBlock->index != 255) { auto quarterTile = trackBlock->var_08.Rotate(trackDirection); - switch (trackDirection) - { - default: - case 0: - offsetX = trackBlock->x; - offsetY = trackBlock->y; - break; - case 1: - offsetX = trackBlock->y; - offsetY = -trackBlock->x; - break; - case 2: - offsetX = -trackBlock->x; - offsetY = -trackBlock->y; - break; - case 3: - offsetX = -trackBlock->y; - offsetY = trackBlock->x; - break; - } - int32_t x = originX + offsetX; - int32_t y = originY + offsetY; + CoordsXY coords = { originX, originY }; + CoordsXY offsets = { trackBlock->x, trackBlock->y }; + coords += offsets.Rotate(trackDirection); + int32_t baseZ = (originZ + trackBlock->z) >> 3; int32_t clearanceZ = ((trackBlock->var_07 + RideData5[ride->type].clearance_height) >> 3) + baseZ + 4; - int32_t tileX = x >> 5; - int32_t tileY = y >> 5; + int32_t tileX = coords.x >> 5; + int32_t tileY = coords.y >> 5; // Replace map elements with temporary ones containing track _backupTileElementArrays[0] = map_get_first_element_at(tileX + 0, tileY + 0); @@ -2440,20 +2453,21 @@ static void sub_6CBCE2( // Set the temporary track element _tempTrackTileElement.SetType(TILE_ELEMENT_TYPE_TRACK); _tempTrackTileElement.SetDirection(trackDirection); - _tempTrackTileElement.AsTrack()->SetHasChain((edx & 0x10000) ? true : false); - _tempTrackTileElement.flags = quarterTile.GetBaseQuarterOccupied() | TILE_ELEMENT_FLAG_LAST_TILE; + _tempTrackTileElement.AsTrack()->SetHasChain((edx & 0x10000) != 0); + _tempTrackTileElement.SetOccupiedQuadrants(quarterTile.GetBaseQuarterOccupied()); + _tempTrackTileElement.SetLastForTile(true); _tempTrackTileElement.base_height = baseZ; _tempTrackTileElement.clearance_height = clearanceZ; _tempTrackTileElement.AsTrack()->SetTrackType(trackType); _tempTrackTileElement.AsTrack()->SetSequenceIndex(trackBlock->index); _tempTrackTileElement.AsTrack()->SetHasCableLift(false); - _tempTrackTileElement.AsTrack()->SetInverted((edx & 0x20000) ? true : false); + _tempTrackTileElement.AsTrack()->SetInverted((edx & 0x20000) != 0); _tempTrackTileElement.AsTrack()->SetColourScheme(RIDE_COLOUR_SCHEME_MAIN); // Skipping seat rotation, should not be necessary for a temporary piece. _tempTrackTileElement.AsTrack()->SetRideIndex(rideIndex); // Draw this map tile - sub_68B2B7(session, x, y); + sub_68B2B7(session, coords.x, coords.y); // Restore map elements map_set_tile_elements(tileX + 0, tileY + 0, _backupTileElementArrays[0]); @@ -2516,14 +2530,16 @@ void window_ride_construction_update_active_elements_impl() */ void window_ride_construction_update_enabled_track_pieces() { - Ride* ride = get_ride(_currentRideIndex); - rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride); - int32_t rideType = (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_TYPE) ? RideData4[ride->type].alternate_type - : ride->type; + auto ride = get_ride(_currentRideIndex); + if (ride == nullptr) + return; + auto rideEntry = ride->GetRideEntry(); if (rideEntry == nullptr) return; + int32_t rideType = (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_TYPE) ? RideData4[ride->type].alternate_type + : ride->type; if (gCheatsEnableAllDrawableTrackPieces) { _enabledRidePieces = RideTypePossibleTrackConfigurations[rideType]; @@ -2664,7 +2680,6 @@ void sub_6C94D8() */ static void window_ride_construction_update_map_selection() { - Ride* ride; int32_t trackType, trackDirection, x, y; map_invalidate_map_selection_tiles(); @@ -2696,9 +2711,12 @@ static void window_ride_construction_update_map_selection() break; } - ride = get_ride(_currentRideIndex); - window_ride_construction_select_map_tiles(ride, trackType, trackDirection, x, y); - map_invalidate_map_selection_tiles(); + auto ride = get_ride(_currentRideIndex); + if (ride != nullptr) + { + window_ride_construction_select_map_tiles(ride, trackType, trackDirection, x, y); + map_invalidate_map_selection_tiles(); + } } /** @@ -2707,11 +2725,12 @@ static void window_ride_construction_update_map_selection() */ static void window_ride_construction_update_possible_ride_configurations() { - Ride* ride; int32_t trackType; int32_t edi; - ride = get_ride(_currentRideIndex); + auto ride = get_ride(_currentRideIndex); + if (ride == nullptr) + return; _currentlyShowingBrakeOrBoosterSpeed = false; if (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_TYPE) @@ -2809,8 +2828,10 @@ static void window_ride_construction_update_possible_ride_configurations() */ static void window_ride_construction_update_widgets(rct_window* w) { - ride_id_t rideIndex = _currentRideIndex; - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(_currentRideIndex); + if (ride == nullptr) + return; + int32_t rideType = ride_get_alternative_type(ride); w->hold_down_widgets = 0; @@ -3224,7 +3245,7 @@ static void window_ride_construction_update_widgets(rct_window* w) break; default: w->pressed_widgets = pressedWidgets; - window_invalidate(w); + w->Invalidate(); return; } @@ -3318,7 +3339,7 @@ static void window_ride_construction_update_widgets(rct_window* w) pressedWidgets |= (1 << WIDX_CHAIN_LIFT); w->pressed_widgets = pressedWidgets; - window_invalidate(w); + w->Invalidate(); } static void window_ride_construction_select_map_tiles( @@ -3332,40 +3353,19 @@ static void window_ride_construction_select_map_tiles( } const rct_preview_track* trackBlock; - int32_t offsetX, offsetY; trackBlock = get_track_def_from_ride(ride, trackType); trackDirection &= 3; - int32_t selectionTileIndex = 0; + gMapSelectionTiles.clear(); while (trackBlock->index != 255) { - switch (trackDirection) - { - default: - case 0: - offsetX = trackBlock->x; - offsetY = trackBlock->y; - break; - case 1: - offsetX = trackBlock->y; - offsetY = -trackBlock->x; - break; - case 2: - offsetX = -trackBlock->x; - offsetY = -trackBlock->y; - break; - case 3: - offsetX = -trackBlock->y; - offsetY = trackBlock->x; - break; - } - gMapSelectionTiles[selectionTileIndex].x = x + offsetX; - gMapSelectionTiles[selectionTileIndex].y = y + offsetY; - selectionTileIndex++; + CoordsXY coords = { x, y }; + CoordsXY offsets = { trackBlock->x, trackBlock->y }; + coords += offsets.Rotate(trackDirection); + + gMapSelectionTiles.push_back(coords); trackBlock++; } - gMapSelectionTiles[selectionTileIndex].x = -1; - gMapSelectionTiles[selectionTileIndex].y = -1; } /** @@ -3381,14 +3381,14 @@ static void window_ride_construction_show_special_track_dropdown(rct_window* w, rct_string_id trackPieceStringId = RideConfigurationStringIds[trackPiece]; if (trackPieceStringId == STR_RAPIDS) { - Ride* ride = get_ride(_currentRideIndex); - if (ride->type == RIDE_TYPE_CAR_RIDE) + auto ride = get_ride(_currentRideIndex); + if (ride != nullptr && ride->type == RIDE_TYPE_CAR_RIDE) trackPieceStringId = STR_LOG_BUMPS; } if (trackPieceStringId == STR_SPINNING_CONTROL_TOGGLE_TRACK) { - Ride* ride = get_ride(_currentRideIndex); - if (ride->type != RIDE_TYPE_STEEL_WILD_MOUSE) + auto ride = get_ride(_currentRideIndex); + if (ride != nullptr && ride->type != RIDE_TYPE_STEEL_WILD_MOUSE) trackPieceStringId = STR_BOOSTER; } gDropdownItemsFormat[i] = trackPieceStringId; @@ -3458,9 +3458,13 @@ static void ride_construction_set_brakes_speed(int32_t brakesSpeed) z = _currentTrackBegin.z; if (!sub_6C683D(&x, &y, &z, _currentTrackPieceDirection & 3, _currentTrackPieceType, 0, &tileElement, 0)) { - game_do_command( - _currentTrackBegin.x, GAME_COMMAND_FLAG_APPLY | ((brakesSpeed) << 8), _currentTrackBegin.y, - tileElement->AsTrack()->GetTrackType(), GAME_COMMAND_SET_BRAKES_SPEED, _currentTrackBegin.z, 0); + auto trackSetBrakeSpeed = TrackSetBrakeSpeedAction( + { _currentTrackBegin.x, _currentTrackBegin.y, _currentTrackBegin.z }, tileElement->AsTrack()->GetTrackType(), + brakesSpeed); + trackSetBrakeSpeed.SetCallback( + [](const GameAction* ga, const GameActionResult* result) { window_ride_construction_update_active_elements(); }); + GameActions::Execute(&trackSetBrakeSpeed); + return; } window_ride_construction_update_active_elements(); } @@ -3469,17 +3473,16 @@ static void ride_construction_set_brakes_speed(int32_t brakesSpeed) * * rct2: 0x006CC6A8 */ -void ride_construction_toolupdate_construct(int32_t screenX, int32_t screenY) +void ride_construction_toolupdate_construct(ScreenCoordsXY screenCoords) { int32_t x, y, z; - Ride* ride; const rct_preview_track* trackBlock; map_invalidate_map_selection_tiles(); gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - if (!ride_get_place_position_from_screen_position(screenX, screenY, &x, &y)) + if (!ride_get_place_position_from_screen_position(screenCoords.x, screenCoords.y, &x, &y)) { ride_construction_invalidate_current_track(); map_invalidate_map_selection_tiles(); @@ -3488,7 +3491,7 @@ void ride_construction_toolupdate_construct(int32_t screenX, int32_t screenY) z = _trackPlaceZ; if (z == 0) - z = map_get_highest_z(x >> 5, y >> 5); + z = map_get_highest_z({ x, y }); gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_ARROW; @@ -3497,10 +3500,8 @@ void ride_construction_toolupdate_construct(int32_t screenX, int32_t screenY) gMapSelectArrowPosition.x = x; gMapSelectArrowPosition.y = y; gMapSelectArrowPosition.z = z; - gMapSelectionTiles[0].x = x; - gMapSelectionTiles[0].y = y; - gMapSelectionTiles[1].x = -1; - gMapSelectionTiles[1].y = -1; + gMapSelectionTiles.clear(); + gMapSelectionTiles.push_back({ x, y }); ride_id_t rideIndex; int32_t trackType, trackDirection, liftHillAndAlternativeState; @@ -3512,7 +3513,9 @@ void ride_construction_toolupdate_construct(int32_t screenX, int32_t screenY) return; } _currentTrackPieceType = trackType; - ride = get_ride(_currentRideIndex); + auto ride = get_ride(_currentRideIndex); + if (ride == nullptr) + return; // Re-using this other code, very slight difference from original // - Original code checks for MSB mask instead of 255 on trackPart->var_00 @@ -3526,16 +3529,14 @@ void ride_construction_toolupdate_construct(int32_t screenX, int32_t screenY) if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT) { int32_t highestZ = 0; - LocationXY16* selectedTile = gMapSelectionTiles; - while (selectedTile->x != -1) + for (const auto& selectedTile : gMapSelectionTiles) { - if (selectedTile->x < (256 * 32) && selectedTile->y < (256 * 32)) + if (selectedTile.x < (256 * 32) && selectedTile.y < (256 * 32)) { - z = map_get_highest_z(selectedTile->x >> 5, selectedTile->y >> 5); + z = map_get_highest_z(selectedTile); if (z > highestZ) highestZ = z; } - selectedTile++; } } // loc_6CC8BF: @@ -3683,7 +3684,7 @@ void ride_construction_toolupdate_construct(int32_t screenX, int32_t screenY) * * rct2: 0x006CD354 */ -void ride_construction_toolupdate_entrance_exit(int32_t screenX, int32_t screenY) +void ride_construction_toolupdate_entrance_exit(ScreenCoordsXY screenCoords) { int32_t x, y, direction; uint8_t stationNum; @@ -3693,7 +3694,7 @@ void ride_construction_toolupdate_entrance_exit(int32_t screenX, int32_t screenY gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - ride_get_entrance_or_exit_position_from_screen_position(screenX, screenY, &x, &y, &direction); + ride_get_entrance_or_exit_position_from_screen_position(screenCoords.x, screenCoords.y, &x, &y, &direction); if (gRideEntranceExitPlaceDirection == 255) { ride_construction_invalidate_current_track(); @@ -3731,7 +3732,7 @@ void ride_construction_toolupdate_entrance_exit(int32_t screenX, int32_t screenY * * rct2: 0x006CCA73 */ -void ride_construction_tooldown_construct(int32_t screenX, int32_t screenY) +void ride_construction_tooldown_construct(ScreenCoordsXY screenCoords) { const CursorState* state = context_get_cursor_state(); ride_id_t rideIndex; @@ -3751,33 +3752,33 @@ void ride_construction_tooldown_construct(int32_t screenX, int32_t screenY) highestZ = 0; if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT) { - LocationXY16* selectedTile = gMapSelectionTiles; - while (selectedTile->x != -1) + for (const auto& selectedTile : gMapSelectionTiles) { - if (selectedTile->x >= (256 * 32) || selectedTile->y >= (256 * 32)) + if (selectedTile.x >= (256 * 32) || selectedTile.y >= (256 * 32)) continue; - z = map_get_highest_z(selectedTile->x >> 5, selectedTile->y >> 5); + z = map_get_highest_z(selectedTile); if (z > highestZ) highestZ = z; - - selectedTile++; } } gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - if (!ride_get_place_position_from_screen_position(screenX, screenY, &x, &y)) + if (!ride_get_place_position_from_screen_position(screenCoords.x, screenCoords.y, &x, &y)) return; z = _trackPlaceZ; if (z == 0) - z = map_get_highest_z(x >> 5, y >> 5); + z = map_get_highest_z({ x, y }); tool_cancel(); - Ride* ride = get_ride(_currentRideIndex); + auto ride = get_ride(_currentRideIndex); + if (ride == nullptr) + return; + if (_trackPlaceZ == 0) { const rct_preview_track* trackBlock = get_track_def_from_ride(ride, _currentTrackPieceType); @@ -3834,7 +3835,7 @@ void ride_construction_tooldown_construct(int32_t screenX, int32_t screenY) || errorText == STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND || errorText == STR_TOO_HIGH_FOR_SUPPORTS || zAttempts == 0 || z < 0) { - audio_play_sound(SOUND_ERROR, 0, state->x); + audio_play_sound(SoundId::Error, 0, state->x); w = window_find_by_class(WC_RIDE_CONSTRUCTION); if (w != nullptr) { @@ -3855,8 +3856,7 @@ void ride_construction_tooldown_construct(int32_t screenX, int32_t screenY) else { window_close_by_class(WC_ERROR); - audio_play_sound_at_location( - SOUND_PLACE_ITEM, _currentTrackBegin.x, _currentTrackBegin.y, _currentTrackBegin.z); + audio_play_sound_at_location(SoundId::PlaceItem, _currentTrackBegin); break; } } @@ -3908,7 +3908,7 @@ void ride_construction_tooldown_construct(int32_t screenX, int32_t screenY) _currentTrackAlternative = saveCurrentTrackAlternative; _currentTrackLiftHill = saveCurrentTrackLiftHill; - audio_play_sound(SOUND_ERROR, 0, state->x); + audio_play_sound(SoundId::Error, 0, state->x); break; } else if (zAttempts >= 0) @@ -3927,7 +3927,7 @@ void ride_construction_tooldown_construct(int32_t screenX, int32_t screenY) * * rct2: 0x006CCA73 */ -static void ride_construction_tooldown_entrance_exit(int32_t screenX, int32_t screenY) +static void ride_construction_tooldown_entrance_exit(ScreenCoordsXY screenCoords) { ride_construction_invalidate_current_track(); map_invalidate_selection_rect(); @@ -3935,7 +3935,7 @@ static void ride_construction_tooldown_entrance_exit(int32_t screenX, int32_t sc gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; int32_t mapX, mapY, direction; - ride_get_entrance_or_exit_position_from_screen_position(screenX, screenY, &mapX, &mapY, &direction); + ride_get_entrance_or_exit_position_from_screen_position(screenCoords.x, screenCoords.y, &mapX, &mapY, &direction); if (gRideEntranceExitPlaceDirection == 255) return; @@ -3947,10 +3947,10 @@ static void ride_construction_tooldown_entrance_exit(int32_t screenX, int32_t sc if (result->Error != GA_ERROR::OK) return; - audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z); + audio_play_sound_at_location(SoundId::PlaceItem, result->Position); - Ride* ride = get_ride(gRideEntranceExitPlaceRideIndex); - if (ride_are_all_possible_entrances_and_exits_built(ride)) + auto ride = get_ride(gRideEntranceExitPlaceRideIndex); + if (ride != nullptr && ride_are_all_possible_entrances_and_exits_built(ride)) { tool_cancel(); if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_NO_TRACK)) diff --git a/src/openrct2-ui/windows/RideList.cpp b/src/openrct2-ui/windows/RideList.cpp index f66b137540..08e341fe75 100644 --- a/src/openrct2-ui/windows/RideList.cpp +++ b/src/openrct2-ui/windows/RideList.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -275,7 +275,7 @@ static void window_ride_list_mouseup(rct_window* w, rct_widgetindex widgetIndex) { _quickDemolishMode = false; } - window_invalidate(w); + w->Invalidate(); break; } } @@ -290,12 +290,12 @@ static void window_ride_list_resize(rct_window* w) w->min_height = 124; if (w->width < w->min_width) { - window_invalidate(w); + w->Invalidate(); w->width = w->min_width; } if (w->height < w->min_height) { - window_invalidate(w); + w->Invalidate(); w->height = w->min_height; } @@ -379,7 +379,7 @@ static void window_ride_list_dropdown(rct_window* w, rct_widgetindex widgetIndex window_ride_list_open_all(w); } - window_invalidate(w); + w->Invalidate(); } else if (widgetIndex == WIDX_INFORMATION_TYPE_DROPDOWN) { @@ -397,7 +397,7 @@ static void window_ride_list_dropdown(rct_window* w, rct_widgetindex widgetIndex } _window_ride_list_information_type = informationType; - window_invalidate(w); + w->Invalidate(); } } @@ -410,7 +410,7 @@ static void window_ride_list_update(rct_window* w) w->frame_no = (w->frame_no + 1) % 64; widget_invalidate(w, WIDX_TAB_1 + w->page); if (_window_ride_list_information_type != INFORMATION_TYPE_STATUS) - window_invalidate(w); + w->Invalidate(); } /** @@ -425,7 +425,7 @@ static void window_ride_list_scrollgetsize(rct_window* w, int32_t scrollIndex, i if (w->selected_list_item != -1) { w->selected_list_item = -1; - window_invalidate(w); + w->Invalidate(); } top = *height - window_ride_list_widgets[WIDX_LIST].bottom + window_ride_list_widgets[WIDX_LIST].top + 21; @@ -434,7 +434,7 @@ static void window_ride_list_scrollgetsize(rct_window* w, int32_t scrollIndex, i if (top < w->scrolls[0].v_top) { w->scrolls[0].v_top = top; - window_invalidate(w); + w->Invalidate(); } } @@ -479,7 +479,7 @@ static void window_ride_list_scrollmouseover(rct_window* w, int32_t scrollIndex, return; w->selected_list_item = index; - window_invalidate(w); + w->Invalidate(); } /** @@ -527,30 +527,23 @@ static void window_ride_list_invalidate(rct_window* w) w->widgets[WIDX_CLOSE_LIGHT].type = WWT_IMGBTN; w->widgets[WIDX_OPEN_LIGHT].type = WWT_IMGBTN; - int8_t allClosed = -1; - int8_t allOpen = -1; - int32_t i; - Ride* ride; - FOR_ALL_RIDES (i, ride) + const auto& rideManager = GetRideManager(); + auto allClosed = false; + auto allOpen = false; + if (std::size(rideManager) != 0) { - if (w->page != gRideClassifications[ride->type]) - continue; - if (ride->status == RIDE_STATUS_OPEN) - { - if (allOpen == -1) - allOpen = true; - allClosed = false; - } - else - { - if (allClosed == -1) - allClosed = true; - allOpen = false; - } + auto c = (RideClassification)w->page; + allClosed = std::none_of(rideManager.begin(), rideManager.end(), [c](const Ride& ride) { + return ride.GetClassification() == c && ride.status == RIDE_STATUS_OPEN; + }); + allOpen = std::none_of(rideManager.begin(), rideManager.end(), [c](const Ride& ride) { + return ride.GetClassification() == c && ride.status != RIDE_STATUS_OPEN; + }); } - w->widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (allClosed == 1) * 2 + + w->widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (allClosed ? 1 : 0) * 2 + widget_is_pressed(w, WIDX_CLOSE_LIGHT); - w->widgets[WIDX_OPEN_LIGHT].image = SPR_G2_RCT1_OPEN_BUTTON_0 + (allOpen == 1) * 2 + w->widgets[WIDX_OPEN_LIGHT].image = SPR_G2_RCT1_OPEN_BUTTON_0 + (allOpen ? 1 : 0) * 2 + widget_is_pressed(w, WIDX_OPEN_LIGHT); w->widgets[WIDX_QUICK_DEMOLISH].top = w->widgets[WIDX_OPEN_LIGHT].bottom + 3; } @@ -586,39 +579,42 @@ static void window_ride_list_paint(rct_window* w, rct_drawpixelinfo* dpi) */ static void window_ride_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) { - int32_t i, y, argument; - rct_string_id format, formatSecondary; - Ride* ride; - gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width, dpi->y + dpi->height, ColourMapA[w->colours[1]].mid_light); - y = 0; - for (i = 0; i < w->no_list_items; i++) + auto y = 0; + for (auto i = 0; i < w->no_list_items; i++) { - format = (_quickDemolishMode ? STR_RED_STRINGID : STR_BLACK_STRING); - - // Background highlight + rct_string_id format = (_quickDemolishMode ? STR_RED_STRINGID : STR_BLACK_STRING); if (i == w->selected_list_item) { + // Background highlight gfx_filter_rect(dpi, 0, y, 800, y + SCROLLABLE_ROW_HEIGHT - 1, PALETTE_DARKEN_1); format = (_quickDemolishMode ? STR_LIGHTPINK_STRINGID : STR_WINDOW_COLOUR_2_STRINGID); } // Get ride - ride = get_ride(w->list_item_positions[i]); + auto ride = get_ride(w->list_item_positions[i]); + if (ride == nullptr) + continue; // Ride name - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs); gfx_draw_string_left_clipped(dpi, format, gCommonFormatArgs, COLOUR_BLACK, 0, y - 1, 159); // Ride information - formatSecondary = 0; + auto formatSecondaryEnabled = true; + rct_string_id formatSecondary = 0; switch (_window_ride_list_information_type) { case INFORMATION_TYPE_STATUS: - ride_get_status(ride, &formatSecondary, &argument); - set_format_arg(2, int32_t, argument); + formatSecondaryEnabled = false; + ride->FormatStatusTo(gCommonFormatArgs); + + // Make test red and bold if broken down or crashed + if ((ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) || (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) + { + format = STR_RED_OUTLINED_STRING; + } break; case INFORMATION_TYPE_POPULARITY: formatSecondary = STR_POPULARITY_UNKNOWN_LABEL; @@ -695,7 +691,7 @@ static void window_ride_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, } break; case INFORMATION_TYPE_QUEUE_LENGTH: - set_format_arg(2, uint16_t, ride_get_total_queue_length(ride)); + set_format_arg(2, uint16_t, ride->GetTotalQueueLength()); formatSecondary = STR_QUEUE_EMPTY; { uint16_t arg; @@ -708,7 +704,7 @@ static void window_ride_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, } break; case INFORMATION_TYPE_QUEUE_TIME: - set_format_arg(2, uint16_t, ride_get_max_queue_time(ride)); + set_format_arg(2, uint16_t, ride->GetMaxQueueTime()); formatSecondary = STR_QUEUE_TIME_LABEL; { uint16_t arg; @@ -728,7 +724,7 @@ static void window_ride_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, break; case INFORMATION_TYPE_GUESTS_FAVOURITE: formatSecondary = 0; - if (gRideClassifications[ride->type] == RIDE_CLASS_RIDE) + if (ride->IsRide()) { set_format_arg(2, uint16_t, ride->guests_favourite); formatSecondary = ride->guests_favourite == 1 ? STR_GUESTS_FAVOURITE_LABEL @@ -737,11 +733,10 @@ static void window_ride_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, break; } - // Make test red and bold if broken down or crashed - if (formatSecondary == STR_BROKEN_DOWN || formatSecondary == STR_CRASHED) - format = STR_RED_OUTLINED_STRING; - - set_format_arg(0, rct_string_id, formatSecondary); + if (formatSecondaryEnabled) + { + set_format_arg(0, rct_string_id, formatSecondary); + } gfx_draw_string_left_clipped(dpi, format, gCommonFormatArgs, COLOUR_BLACK, 160, y - 1, 157); y += SCROLLABLE_ROW_HEIGHT; } @@ -780,14 +775,11 @@ static void window_ride_list_draw_tab_images(rct_drawpixelinfo* dpi, rct_window* */ void window_ride_list_refresh_list(rct_window* w) { - int32_t i; - Ride *ride, *otherRide; - char bufferA[128], bufferB[128]; int32_t list_index = 0; - - FOR_ALL_RIDES (i, ride) + for (auto& ridec : GetRideManager()) { - if (w->page != gRideClassifications[ride->type] + auto ride = &ridec; + if (ride->GetClassification() != (RideClassification)w->page || (ride->status == RIDE_STATUS_CLOSED && !ride_has_any_track_elements(ride))) continue; @@ -796,160 +788,207 @@ void window_ride_list_refresh_list(rct_window* w) ride->window_invalidate_flags &= ~RIDE_INVALIDATE_RIDE_LIST; } - w->list_item_positions[list_index] = i; + w->list_item_positions[list_index] = ride->id; int32_t current_list_position = list_index; switch (w->list_information_type) { case INFORMATION_TYPE_STATUS: - format_string_to_upper(bufferA, 128, ride->name, &ride->name_arguments); + { + auto strA = ride->GetName(); while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - format_string_to_upper(bufferB, 128, otherRide->name, &otherRide->name_arguments); - if (strcmp(bufferA, bufferB) >= 0) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + auto strB = otherRide->GetName(); + if (_strcmpi(strA.c_str(), strB.c_str()) >= 0) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; + } case INFORMATION_TYPE_POPULARITY: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->popularity * 4 <= otherRide->popularity * 4) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->popularity * 4 <= otherRide->popularity * 4) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_SATISFACTION: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->satisfaction * 5 <= otherRide->satisfaction * 5) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->satisfaction * 5 <= otherRide->satisfaction * 5) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_PROFIT: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->profit <= otherRide->profit) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->profit <= otherRide->profit) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_TOTAL_CUSTOMERS: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->total_customers <= otherRide->total_customers) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->total_customers <= otherRide->total_customers) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_TOTAL_PROFIT: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->total_profit <= otherRide->total_profit) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->total_profit <= otherRide->total_profit) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_CUSTOMERS: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride_customers_per_hour(ride) <= ride_customers_per_hour(otherRide)) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride_customers_per_hour(ride) <= ride_customers_per_hour(otherRide)) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_AGE: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->build_date <= otherRide->build_date) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->build_date <= otherRide->build_date) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_INCOME: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->income_per_hour <= otherRide->income_per_hour) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->income_per_hour <= otherRide->income_per_hour) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_RUNNING_COST: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->upkeep_cost <= otherRide->upkeep_cost) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->upkeep_cost <= otherRide->upkeep_cost) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_QUEUE_LENGTH: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride_get_total_queue_length(ride) <= ride_get_total_queue_length(otherRide)) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->GetTotalQueueLength() <= otherRide->GetTotalQueueLength()) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_QUEUE_TIME: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride_get_max_queue_time(ride) <= ride_get_max_queue_time(otherRide)) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->GetMaxQueueTime() <= otherRide->GetMaxQueueTime()) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_RELIABILITY: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->reliability_percentage <= otherRide->reliability_percentage) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->reliability_percentage <= otherRide->reliability_percentage) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_DOWN_TIME: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->downtime <= otherRide->downtime) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->downtime <= otherRide->downtime) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; case INFORMATION_TYPE_GUESTS_FAVOURITE: while (--current_list_position >= 0) { - otherRide = get_ride(w->list_item_positions[current_list_position]); - if (ride->guests_favourite <= otherRide->guests_favourite) - break; + auto otherRide = get_ride(w->list_item_positions[current_list_position]); + if (otherRide != nullptr) + { + if (ride->guests_favourite <= otherRide->guests_favourite) + break; - window_bubble_list_item(w, current_list_position); + window_bubble_list_item(w, current_list_position); + } } break; } @@ -959,35 +998,27 @@ void window_ride_list_refresh_list(rct_window* w) w->no_list_items = list_index; w->selected_list_item = -1; - window_invalidate(w); + w->Invalidate(); } static void window_ride_list_close_all(rct_window* w) { - int32_t i; - Ride* ride; - - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (w->page != gRideClassifications[ride->type]) - continue; - if (ride->status == RIDE_STATUS_CLOSED) - continue; - ride_set_status(ride, RIDE_STATUS_CLOSED); + if (ride.status != RIDE_STATUS_CLOSED && ride.GetClassification() == (RideClassification)w->page) + { + ride_set_status(&ride, RIDE_STATUS_CLOSED); + } } } static void window_ride_list_open_all(rct_window* w) { - int32_t i; - Ride* ride; - - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (w->page != gRideClassifications[ride->type]) - continue; - if (ride->status == RIDE_STATUS_OPEN) - continue; - ride_set_status(ride, RIDE_STATUS_OPEN); + if (ride.status != RIDE_STATUS_OPEN && ride.GetClassification() == (RideClassification)w->page) + { + ride_set_status(&ride, RIDE_STATUS_OPEN); + } } } diff --git a/src/openrct2-ui/windows/SavePrompt.cpp b/src/openrct2-ui/windows/SavePrompt.cpp index 92fa50696e..5770edb24e 100644 --- a/src/openrct2-ui/windows/SavePrompt.cpp +++ b/src/openrct2-ui/windows/SavePrompt.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -134,7 +135,7 @@ rct_window* window_save_prompt_open() * and game_load_or_quit() are not called by the original binary anymore. */ - if (gScreenAge < 3840) + if (gScreenAge < 3840 && network_get_mode() == NETWORK_MODE_NONE) { game_load_or_quit_no_save_prompt(); return nullptr; @@ -175,9 +176,13 @@ rct_window* window_save_prompt_open() window->enabled_widgets = enabled_widgets; window_init_scroll_widgets(window); - // Pause the game - gGamePaused |= GAME_PAUSED_MODAL; - audio_stop_all_music_and_sounds(); + // Pause the game if not network play. + if (network_get_mode() == NETWORK_MODE_NONE) + { + gGamePaused |= GAME_PAUSED_MODAL; + audio_stop_all_music_and_sounds(); + } + window_invalidate_by_class(WC_TOP_TOOLBAR); stringId = window_save_prompt_labels[prompt_mode][0]; @@ -198,8 +203,12 @@ rct_window* window_save_prompt_open() static void window_save_prompt_close(rct_window* w) { // Unpause the game - gGamePaused &= ~GAME_PAUSED_MODAL; - audio_unpause_sounds(); + if (network_get_mode() == NETWORK_MODE_NONE) + { + gGamePaused &= ~GAME_PAUSED_MODAL; + audio_unpause_sounds(); + } + window_invalidate_by_class(WC_TOP_TOOLBAR); } diff --git a/src/openrct2-ui/windows/Scenery.cpp b/src/openrct2-ui/windows/Scenery.cpp index d20843848e..ecce651315 100644 --- a/src/openrct2-ui/windows/Scenery.cpp +++ b/src/openrct2-ui/windows/Scenery.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -140,6 +140,7 @@ enum WINDOW_SCENERY_LIST_WIDGET_IDX { validate_global_widx(WC_SCENERY, WIDX_SCENERY_TAB_1); validate_global_widx(WC_SCENERY, WIDX_SCENERY_ROTATE_OBJECTS_BUTTON); +validate_global_widx(WC_SCENERY, WIDX_SCENERY_EYEDROPPER_BUTTON); static rct_widget window_scenery_widgets[] = { { WWT_FRAME, 0, 0, 633, 0, 141, 0xFFFFFFFF, STR_NONE }, // 1 0x009DE298 @@ -447,8 +448,8 @@ rct_window* window_scenery_open() window_scenery_init(); window = window_create( - context_get_width() - WINDOW_SCENERY_WIDTH, 0x1D, WINDOW_SCENERY_WIDTH, WINDOW_SCENERY_HEIGHT, &window_scenery_events, - WC_SCENERY, WF_NO_SCROLLING); + ScreenCoordsXY(context_get_width() - WINDOW_SCENERY_WIDTH, 0x1D), WINDOW_SCENERY_WIDTH, WINDOW_SCENERY_HEIGHT, + &window_scenery_events, WC_SCENERY, WF_NO_SCROLLING); window->widgets = window_scenery_widgets; window->enabled_widgets = (1 << WIDX_SCENERY_CLOSE) | (1 << WIDX_SCENERY_ROTATE_OBJECTS_BUTTON) | (1 << WIDX_SCENERY_TAB_1) @@ -567,19 +568,20 @@ static void window_scenery_mouseup(rct_window* w, rct_widgetindex widgetIndex) gWindowSceneryRotation++; gWindowSceneryRotation = gWindowSceneryRotation % 4; scenery_remove_ghost_tool_placement(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_SCENERY_REPAINT_SCENERY_BUTTON: gWindowSceneryPaintEnabled ^= 1; gWindowSceneryClusterEnabled = 0; gWindowSceneryEyedropperEnabled = false; - window_invalidate(w); + w->Invalidate(); break; case WIDX_SCENERY_EYEDROPPER_BUTTON: gWindowSceneryPaintEnabled = 0; gWindowSceneryClusterEnabled = 0; gWindowSceneryEyedropperEnabled = !gWindowSceneryEyedropperEnabled; - window_invalidate(w); + scenery_remove_ghost_tool_placement(); + w->Invalidate(); break; case WIDX_SCENERY_BUILD_CLUSTER_BUTTON: gWindowSceneryPaintEnabled = 0; @@ -598,7 +600,7 @@ static void window_scenery_mouseup(rct_window* w, rct_widgetindex widgetIndex) { context_show_error(STR_CANT_DO_THIS, STR_PERMISSION_DENIED); } - window_invalidate(w); + w->Invalidate(); break; } } @@ -639,23 +641,23 @@ static void window_scenery_resize(rct_window* w) { if (w->width < w->min_width) { - window_invalidate(w); + w->Invalidate(); w->width = w->min_width; - window_invalidate(w); + w->Invalidate(); } if (w->width > w->max_width) { - window_invalidate(w); + w->Invalidate(); w->width = w->max_width; - window_invalidate(w); + w->Invalidate(); } if (w->height < w->min_height) { - window_invalidate(w); + w->Invalidate(); w->height = w->min_height; - window_invalidate(w); + w->Invalidate(); // HACK: For some reason invalidate has not been called window_event_invalidate_call(w); window_scenery_update_scroll(w); @@ -663,9 +665,9 @@ static void window_scenery_resize(rct_window* w) if (w->height > w->max_height) { - window_invalidate(w); + w->Invalidate(); w->height = w->max_height; - window_invalidate(w); + w->Invalidate(); // HACK: For some reason invalidate has not been called window_event_invalidate_call(w); window_scenery_update_scroll(w); @@ -694,7 +696,7 @@ static void window_scenery_mousedown(rct_window* w, rct_widgetindex widgetIndex, if (widgetIndex >= WIDX_SCENERY_TAB_1 && widgetIndex <= WIDX_SCENERY_TAB_20) { gWindowSceneryActiveTabIndex = widgetIndex - WIDX_SCENERY_TAB_1; - window_invalidate(w); + w->Invalidate(); gSceneryPlaceCost = MONEY32_UNDEFINED; // HACK: for 3210 Ensures that window_scenery_update_scroll gets called one time @@ -724,7 +726,7 @@ static void window_scenery_dropdown(rct_window* w, rct_widgetindex widgetIndex, gWindowSceneryTertiaryColour = (uint8_t)dropdownIndex; } - window_invalidate(w); + w->Invalidate(); } /** @@ -746,7 +748,7 @@ static void window_scenery_periodic_update(rct_window* w) static void window_scenery_update(rct_window* w) { const CursorState* state = context_get_cursor_state(); - rct_window* other = window_find_from_point(state->x, state->y); + rct_window* other = window_find_from_point(ScreenCoordsXY(state->x, state->y)); if (other == w) { int32_t window_x = state->x - w->x + 26; @@ -754,7 +756,7 @@ static void window_scenery_update(rct_window* w) if (window_y < 44 || window_x <= w->width) { - rct_widgetindex widgetIndex = window_find_widget_from_point(w, state->x, state->y); + rct_widgetindex widgetIndex = window_find_widget_from_point(w, ScreenCoordsXY(state->x, state->y)); if (widgetIndex >= WIDX_SCENERY_TAB_CONTENT_PANEL) { w->scenery.hover_counter++; @@ -795,7 +797,7 @@ static void window_scenery_update(rct_window* w) } } - window_invalidate(w); + w->Invalidate(); if (!scenery_tool_is_active()) { @@ -887,10 +889,10 @@ void window_scenery_scrollmousedown(rct_window* w, int32_t scrollIndex, int32_t gWindowSceneryPaintEnabled &= 0xFE; gWindowSceneryEyedropperEnabled = false; - audio_play_sound(4, 0, w->x + (w->width / 2)); + audio_play_sound(SoundId::Click1, 0, w->x + (w->width / 2)); w->scenery.hover_counter = -16; gSceneryPlaceCost = MONEY32_UNDEFINED; - window_invalidate(w); + w->Invalidate(); } /** @@ -903,7 +905,7 @@ void window_scenery_scrollmouseover(rct_window* w, int32_t scrollIndex, int32_t if (sceneryId != WINDOW_SCENERY_TAB_SELECTION_UNDEFINED) { w->scenery.selected_scenery_id = sceneryId; - window_invalidate(w); + w->Invalidate(); } } @@ -968,7 +970,7 @@ void window_scenery_invalidate(rct_window* w) if (gWindowSceneryEyedropperEnabled) w->pressed_widgets |= (1 << WIDX_SCENERY_EYEDROPPER_BUTTON); if (gWindowSceneryClusterEnabled == 1) - w->pressed_widgets |= (1 << WIDX_SCENERY_BUILD_CLUSTER_BUTTON); + w->pressed_widgets |= (1ULL << WIDX_SCENERY_BUILD_CLUSTER_BUTTON); window_scenery_widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WWT_EMPTY; window_scenery_widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WWT_EMPTY; @@ -1373,10 +1375,10 @@ bool window_scenery_set_selected_item(int32_t sceneryId) gWindowSceneryActiveTabIndex = tabIndex; gWindowSceneryTabSelections[tabIndex] = sceneryId; - audio_play_sound(SOUND_CLICK_1, 0, context_get_width() / 2); + audio_play_sound(SoundId::Click1, 0, context_get_width() / 2); w->scenery.hover_counter = -16; gSceneryPlaceCost = MONEY32_UNDEFINED; - window_invalidate(w); + w->Invalidate(); result = true; } } diff --git a/src/openrct2-ui/windows/ServerList.cpp b/src/openrct2-ui/windows/ServerList.cpp index 18256723c6..85149d150a 100644 --- a/src/openrct2-ui/windows/ServerList.cpp +++ b/src/openrct2-ui/windows/ServerList.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -7,52 +7,39 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifndef DISABLE_NETWORK -#ifndef DISABLE_HTTP -using namespace OpenRCT2::Network; -#endif +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include -#define WWIDTH_MIN 500 -#define WHEIGHT_MIN 300 -#define WWIDTH_MAX 1200 -#define WHEIGHT_MAX 800 -#define ITEM_HEIGHT (3 + 9 + 3) - -class MasterServerException : public std::exception -{ -public: - rct_string_id StatusText; - - MasterServerException(rct_string_id statusText) - : StatusText(statusText) - { - } -}; +# define WWIDTH_MIN 500 +# define WHEIGHT_MIN 300 +# define WWIDTH_MAX 1200 +# define WHEIGHT_MAX 800 +# define ITEM_HEIGHT (3 + 9 + 3) static char _playerName[32 + 1]; -static std::vector _serverEntries; -static std::mutex _mutex; +static ServerList _serverList; +static std::future, rct_string_id>> _fetchFuture; static uint32_t _numPlayersOnline = 0; -static rct_string_id status_text = STR_SERVER_LIST_CONNECTING; +static rct_string_id _statusText = STR_SERVER_LIST_CONNECTING; // clang-format off enum { @@ -138,18 +125,9 @@ static int32_t _hoverButtonIndex = -1; static std::string _version; static void server_list_get_item_button(int32_t buttonIndex, int32_t x, int32_t y, int32_t width, int32_t* outX, int32_t* outY); -static void server_list_load_server_entries(); -static void server_list_save_server_entries(); -static void dispose_server_entry_list(); -static server_entry& add_server_entry(const std::string& address); -static void sort_servers(); static void join_server(std::string address); -#ifndef DISABLE_HTTP -static void fetch_servers(); -static void fetch_servers_callback(Http::Response& response); -static void RefreshServersFromJson(const json_t* jsonServers); -#endif -static bool is_version_valid(const std::string& version); +static void server_list_fetch_servers_begin(); +static void server_list_fetch_servers_check(rct_window* w); rct_window* window_server_list_open() { @@ -183,20 +161,18 @@ rct_window* window_server_list_open() safe_strcpy(_playerName, gConfigNetwork.player_name.c_str(), sizeof(_playerName)); - server_list_load_server_entries(); - window->no_list_items = (uint16_t)_serverEntries.size(); + _serverList.ReadAndAddFavourites(); + window->no_list_items = (uint16_t)_serverList.GetCount(); -#ifndef DISABLE_HTTP - fetch_servers(); -#endif + server_list_fetch_servers_begin(); return window; } static void window_server_list_close(rct_window* w) { - std::lock_guard guard(_mutex); - dispose_server_entry_list(); + _serverList = {}; + _fetchFuture = {}; } static void window_server_list_mouseup(rct_window* w, rct_widgetindex widgetIndex) @@ -212,10 +188,10 @@ static void window_server_list_mouseup(rct_window* w, rct_widgetindex widgetInde case WIDX_LIST: { int32_t serverIndex = w->selected_list_item; - if (serverIndex >= 0 && serverIndex < (int32_t)_serverEntries.size()) + if (serverIndex >= 0 && serverIndex < (int32_t)_serverList.GetCount()) { - const auto& server = _serverEntries[serverIndex]; - if (is_version_valid(server.version)) + const auto& server = _serverList.GetServer(serverIndex); + if (server.IsVersionValid()) { join_server(server.address); } @@ -228,9 +204,7 @@ static void window_server_list_mouseup(rct_window* w, rct_widgetindex widgetInde break; } case WIDX_FETCH_SERVERS: -#ifndef DISABLE_HTTP - fetch_servers(); -#endif + server_list_fetch_servers_begin(); break; case WIDX_ADD_SERVER: window_text_input_open(w, widgetIndex, STR_ADD_SERVER, STR_ENTER_HOSTNAME_OR_IP_ADDRESS, STR_NONE, 0, 128); @@ -249,27 +223,26 @@ static void window_server_list_resize(rct_window* w) static void window_server_list_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex) { auto serverIndex = w->selected_list_item; - if (serverIndex >= 0 && serverIndex < (int32_t)_serverEntries.size()) + if (serverIndex >= 0 && serverIndex < (int32_t)_serverList.GetCount()) { - auto& server = _serverEntries[serverIndex]; + auto& server = _serverList.GetServer(serverIndex); switch (dropdownIndex) { case DDIDX_JOIN: - if (is_version_valid(server.version)) + if (server.IsVersionValid()) { join_server(server.address); } else { - set_format_arg(0, void*, _serverEntries[serverIndex].version.c_str()); + set_format_arg(0, void*, server.version.c_str()); context_show_error(STR_UNABLE_TO_CONNECT_TO_SERVER, STR_MULTIPLAYER_INCORRECT_SOFTWARE_VERSION); } break; case DDIDX_FAVOURITE: { - std::lock_guard guard(_mutex); server.favourite = !server.favourite; - server_list_save_server_entries(); + _serverList.WriteFavourites(); } break; } @@ -283,6 +256,7 @@ static void window_server_list_update(rct_window* w) window_update_textbox_caret(); widget_invalidate(w, WIDX_PLAYER_NAME_INPUT); } + server_list_fetch_servers_check(w); } static void window_server_list_scroll_getsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height) @@ -294,25 +268,25 @@ static void window_server_list_scroll_getsize(rct_window* w, int32_t scrollIndex static void window_server_list_scroll_mousedown(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) { int32_t serverIndex = w->selected_list_item; - if (serverIndex < 0) - return; - if (serverIndex >= (int32_t)_serverEntries.size()) - return; - - rct_widget* listWidget = &w->widgets[WIDX_LIST]; - int32_t ddx = w->x + listWidget->left + x + 2 - w->scrolls[0].h_left; - int32_t ddy = w->y + listWidget->top + y + 2 - w->scrolls[0].v_top; - - gDropdownItemsFormat[0] = STR_JOIN_GAME; - if (_serverEntries[serverIndex].favourite) + if (serverIndex >= 0 && serverIndex < (int32_t)_serverList.GetCount()) { - gDropdownItemsFormat[1] = STR_REMOVE_FROM_FAVOURITES; + const auto& server = _serverList.GetServer(serverIndex); + + auto listWidget = &w->widgets[WIDX_LIST]; + int32_t ddx = w->x + listWidget->left + x + 2 - w->scrolls[0].h_left; + int32_t ddy = w->y + listWidget->top + y + 2 - w->scrolls[0].v_top; + + gDropdownItemsFormat[0] = STR_JOIN_GAME; + if (server.favourite) + { + gDropdownItemsFormat[1] = STR_REMOVE_FROM_FAVOURITES; + } + else + { + gDropdownItemsFormat[1] = STR_ADD_TO_FAVOURITES; + } + window_dropdown_show_text(ddx, ddy, 0, COLOUR_GREY, 0, 2); } - else - { - gDropdownItemsFormat[1] = STR_ADD_TO_FAVOURITES; - } - window_dropdown_show_text(ddx, ddy, 0, COLOUR_GREY, 0, 2); } static void window_server_list_scroll_mouseover(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) @@ -355,7 +329,7 @@ static void window_server_list_scroll_mouseover(rct_window* w, int32_t scrollInd w->selected_list_item = index; _hoverButtonIndex = hoverButtonIndex; window_tooltip_close(); - window_invalidate(w); + w->Invalidate(); } } @@ -387,14 +361,15 @@ static void window_server_list_textinput(rct_window* w, rct_widgetindex widgetIn case WIDX_ADD_SERVER: { - std::lock_guard guard(_mutex); - auto& entry = add_server_entry(text); + ServerListEntry entry; + entry.address = text; + entry.name = text; entry.favourite = true; - sort_servers(); - server_list_save_server_entries(); - } - window_invalidate(w); + _serverList.Add(entry); + _serverList.WriteFavourites(); + w->Invalidate(); break; + } } } @@ -424,7 +399,7 @@ static void window_server_list_invalidate(rct_window* w) window_server_list_widgets[WIDX_START_SERVER].top = buttonTop; window_server_list_widgets[WIDX_START_SERVER].bottom = buttonBottom; - w->no_list_items = (uint16_t)_serverEntries.size(); + w->no_list_items = (uint16_t)_serverList.GetCount(); } static void window_server_list_paint(rct_window* w, rct_drawpixelinfo* dpi) @@ -439,13 +414,11 @@ static void window_server_list_paint(rct_window* w, rct_drawpixelinfo* dpi) gfx_draw_string_left( dpi, STR_NETWORK_VERSION, (void*)&versionCStr, COLOUR_WHITE, w->x + 324, w->y + w->widgets[WIDX_START_SERVER].top + 1); - gfx_draw_string_left(dpi, status_text, (void*)&_numPlayersOnline, COLOUR_WHITE, w->x + 8, w->y + w->height - 15); + gfx_draw_string_left(dpi, _statusText, (void*)&_numPlayersOnline, COLOUR_WHITE, w->x + 8, w->y + w->height - 15); } static void window_server_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) { - std::lock_guard guard(_mutex); - uint8_t paletteIndex = ColourMapA[w->colours[1]].mid_light; gfx_clear(dpi, paletteIndex); @@ -459,31 +432,35 @@ static void window_server_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi continue; // if (y + ITEM_HEIGHT < dpi->y) continue; - server_entry* serverDetails = &_serverEntries[i]; + const auto& serverDetails = _serverList.GetServer(i); bool highlighted = i == w->selected_list_item; // Draw hover highlight if (highlighted) { gfx_filter_rect(dpi, 0, y, width, y + ITEM_HEIGHT, PALETTE_DARKEN_1); - _version = serverDetails->version; + _version = serverDetails.version; w->widgets[WIDX_LIST].tooltip = STR_NETWORK_VERSION_TIP; } int32_t colour = w->colours[1]; - if (serverDetails->favourite) + if (serverDetails.favourite) { colour = COLOUR_YELLOW; } + else if (serverDetails.local) + { + colour = COLOUR_MOSS_GREEN; + } // Draw server information - if (highlighted && !serverDetails->description.empty()) + if (highlighted && !serverDetails.description.empty()) { - gfx_draw_string(dpi, serverDetails->description.c_str(), colour, 3, y + 3); + gfx_draw_string(dpi, serverDetails.description.c_str(), colour, 3, y + 3); } else { - gfx_draw_string(dpi, serverDetails->name.c_str(), colour, 3, y + 3); + gfx_draw_string(dpi, serverDetails.name.c_str(), colour, 3, y + 3); } int32_t right = width - 3 - 14; @@ -491,7 +468,7 @@ static void window_server_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi // Draw compatibility icon right -= 10; int32_t compatibilitySpriteId; - if (serverDetails->version.empty()) + if (serverDetails.version.empty()) { // Server not online... compatibilitySpriteId = SPR_G2_RCT1_CLOSE_BUTTON_0; @@ -499,7 +476,7 @@ static void window_server_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi else { // Server online... check version - bool correctVersion = serverDetails->version == network_get_version(); + bool correctVersion = serverDetails.version == network_get_version(); compatibilitySpriteId = correctVersion ? SPR_G2_RCT1_OPEN_BUTTON_2 : SPR_G2_RCT1_CLOSE_BUTTON_2; } gfx_draw_sprite(dpi, compatibilitySpriteId, right, y + 1, 0); @@ -507,7 +484,7 @@ static void window_server_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi // Draw lock icon right -= 8; - if (serverDetails->requiresPassword) + if (serverDetails.requiresPassword) { gfx_draw_sprite(dpi, SPR_G2_LOCKED, right, y + 4, 0); } @@ -516,9 +493,9 @@ static void window_server_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi // Draw number of players char players[32]; players[0] = 0; - if (serverDetails->maxplayers > 0) + if (serverDetails.maxplayers > 0) { - snprintf(players, 32, "%d/%d", serverDetails->players, serverDetails->maxplayers); + snprintf(players, 32, "%d/%d", serverDetails.players, serverDetails.maxplayers); } int32_t numPlayersStringWidth = gfx_get_string_width(players); gfx_draw_string(dpi, players, w->colours[1], right - numPlayersStringWidth, y + 3); @@ -533,81 +510,6 @@ static void server_list_get_item_button(int32_t buttonIndex, int32_t x, int32_t *outY = y + 2; } -static void server_list_load_server_entries() -{ - auto entries = server_list_read(); - { - std::lock_guard guard(_mutex); - dispose_server_entry_list(); - _serverEntries = entries; - sort_servers(); - } -} - -static void server_list_save_server_entries() -{ - // Save just favourite servers - std::vector favouriteServers; - std::copy_if( - _serverEntries.begin(), _serverEntries.end(), std::back_inserter(favouriteServers), - [](const server_entry& entry) { return entry.favourite; }); - server_list_write(favouriteServers); -} - -static void dispose_server_entry_list() -{ - _serverEntries.clear(); - _serverEntries.shrink_to_fit(); -} - -static server_entry& add_server_entry(const std::string& address) -{ - auto entry = std::find_if(std::begin(_serverEntries), std::end(_serverEntries), [address](const server_entry& e) { - return e.address == address; - }); - if (entry != _serverEntries.end()) - { - return *entry; - } - - server_entry newserver; - newserver.address = address; - newserver.name = address; - _serverEntries.push_back(newserver); - return _serverEntries.back(); -} - -static bool server_compare(const server_entry& a, const server_entry& b) -{ - // Order by favourite - if (a.favourite != b.favourite) - { - return a.favourite; - } - - // Then by version - bool serverACompatible = a.version == network_get_version(); - bool serverBCompatible = b.version == network_get_version(); - if (serverACompatible != serverBCompatible) - { - return serverACompatible; - } - - // Then by password protection - if (a.requiresPassword != b.requiresPassword) - { - return !a.requiresPassword; - } - - // Then by name - return String::Compare(a.name, b.name, true) < 0; -} - -static void sort_servers() -{ - std::sort(_serverEntries.begin(), _serverEntries.end(), server_compare); -} - static void join_server(std::string address) { int32_t port = gConfigNetwork.default_port; @@ -635,140 +537,83 @@ static void join_server(std::string address) } } -#ifndef DISABLE_HTTP -static void fetch_servers() +static void server_list_fetch_servers_begin() { - std::string masterServerUrl = OPENRCT2_MASTER_SERVER_URL; - if (gConfigNetwork.master_server_url.empty() == false) + if (_fetchFuture.valid()) { - masterServerUrl = gConfigNetwork.master_server_url; + // A fetch is already in progress + return; } - { - std::lock_guard guard(_mutex); - _serverEntries.erase( - std::remove_if( - _serverEntries.begin(), _serverEntries.end(), [](const server_entry& server) { return !server.favourite; }), - _serverEntries.end()); - sort_servers(); - } + _serverList.Clear(); + _serverList.ReadAndAddFavourites(); + _statusText = STR_SERVER_LIST_CONNECTING; - Http::Request request; - request.url = masterServerUrl; - request.method = Http::Method::GET; - request.header["Accept"] = "application/json"; - status_text = STR_SERVER_LIST_CONNECTING; - Http::DoAsync(request, fetch_servers_callback); -} + _fetchFuture = std::async(std::launch::async, [] { + // Spin off background fetches + auto lanF = _serverList.FetchLocalServerListAsync(); + auto wanF = _serverList.FetchOnlineServerListAsync(); -static uint32_t get_total_player_count() -{ - return std::accumulate(_serverEntries.begin(), _serverEntries.end(), 0, [](uint32_t acc, const server_entry& entry) { - return acc + entry.players; + // Merge or deal with errors + std::vector allEntries; + try + { + auto entries = lanF.get(); + allEntries.insert(allEntries.end(), entries.begin(), entries.end()); + } + catch (...) + { + } + + auto status = STR_NONE; + try + { + auto entries = wanF.get(); + allEntries.insert(allEntries.end(), entries.begin(), entries.end()); + } + catch (const MasterServerException& e) + { + status = e.StatusText; + } + catch (...) + { + status = STR_SERVER_LIST_NO_CONNECTION; + } + return std::make_tuple(allEntries, status); }); } -static void fetch_servers_callback(Http::Response& response) +static void server_list_fetch_servers_check(rct_window* w) { - json_t* root = nullptr; - try + if (_fetchFuture.valid()) { - if (response.status != Http::Status::OK) + auto status = _fetchFuture.wait_for(std::chrono::seconds::zero()); + if (status == std::future_status::ready) { - throw MasterServerException(STR_SERVER_LIST_NO_CONNECTION); - } - - root = Json::FromString(response.body); - auto jsonStatus = json_object_get(root, "status"); - if (!json_is_number(jsonStatus)) - { - throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_NUMBER); - } - - auto status = (int32_t)json_integer_value(jsonStatus); - if (status != 200) - { - throw MasterServerException(STR_SERVER_LIST_MASTER_SERVER_FAILED); - } - - auto jsonServers = json_object_get(root, "servers"); - if (!json_is_array(jsonServers)) - { - throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_ARRAY); - } - - RefreshServersFromJson(jsonServers); - } - catch (const MasterServerException& e) - { - status_text = e.StatusText; - window_invalidate_by_class(WC_SERVER_LIST); - } - catch (const std::exception& e) - { - status_text = STR_SERVER_LIST_NO_CONNECTION; - window_invalidate_by_class(WC_SERVER_LIST); - log_warning("Unable to connect to master server: %s", e.what()); - } - - if (root != nullptr) - { - json_decref(root); - root = nullptr; - } -} - -static void RefreshServersFromJson(const json_t* jsonServers) -{ - auto count = (int32_t)json_array_size(jsonServers); - for (int32_t i = 0; i < count; i++) - { - auto server = json_array_get(jsonServers, i); - if (!json_is_object(server)) - { - continue; - } - - auto port = json_object_get(server, "port"); - auto name = json_object_get(server, "name"); - auto description = json_object_get(server, "description"); - auto requiresPassword = json_object_get(server, "requiresPassword"); - auto version = json_object_get(server, "version"); - auto players = json_object_get(server, "players"); - auto maxPlayers = json_object_get(server, "maxPlayers"); - auto ip = json_object_get(server, "ip"); - auto ip4 = json_object_get(ip, "v4"); - auto addressIp = json_array_get(ip4, 0); - - if (name == nullptr || version == nullptr) - { - log_verbose("Cowardly refusing to add server without name or version specified."); - continue; - } - - auto address = String::StdFormat("%s:%d", json_string_value(addressIp), (int32_t)json_integer_value(port)); - { - std::lock_guard guard(_mutex); - auto& newserver = add_server_entry(address); - newserver.name = json_string_value(name); - newserver.requiresPassword = json_is_true(requiresPassword); - newserver.description = (description == nullptr ? "" : json_string_value(description)); - newserver.version = json_string_value(version); - newserver.players = (uint8_t)json_integer_value(players); - newserver.maxplayers = (uint8_t)json_integer_value(maxPlayers); + try + { + auto [entries, statusText] = _fetchFuture.get(); + _serverList.AddRange(entries); + _numPlayersOnline = _serverList.GetTotalPlayerCount(); + _statusText = STR_X_PLAYERS_ONLINE; + if (statusText != STR_NONE) + { + _statusText = statusText; + } + } + catch (const MasterServerException& e) + { + _statusText = e.StatusText; + } + catch (const std::exception& e) + { + _statusText = STR_SERVER_LIST_NO_CONNECTION; + log_warning("Unable to connect to master server: %s", e.what()); + } + _fetchFuture = {}; + w->Invalidate(); } } - - sort_servers(); - _numPlayersOnline = get_total_player_count(); - - status_text = STR_X_PLAYERS_ONLINE; - window_invalidate_by_class(WC_SERVER_LIST); } #endif - -static bool is_version_valid(const std::string& version) -{ - return version.empty() || version == network_get_version(); -} diff --git a/src/openrct2-ui/windows/ServerStart.cpp b/src/openrct2-ui/windows/ServerStart.cpp index 207ab3f05e..b08a5f8dd1 100644 --- a/src/openrct2-ui/windows/ServerStart.cpp +++ b/src/openrct2-ui/windows/ServerStart.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -7,18 +7,20 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ -#include "../interface/Theme.h" +#ifndef DISABLE_NETWORK -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +# include "../interface/Theme.h" + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include static char _port[7]; static char _name[65]; @@ -193,7 +195,7 @@ static void window_server_start_mouseup(rct_window* w, rct_widgetindex widgetInd gConfigNetwork.maxplayers++; } config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_MAXPLAYERS_DECREASE: if (gConfigNetwork.maxplayers > 1) @@ -201,12 +203,12 @@ static void window_server_start_mouseup(rct_window* w, rct_widgetindex widgetInd gConfigNetwork.maxplayers--; } config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_ADVERTISE_CHECKBOX: gConfigNetwork.advertise = !gConfigNetwork.advertise; config_save_default(); - window_invalidate(w); + w->Invalidate(); break; case WIDX_START_SERVER: window_scenarioselect_open(window_server_start_scenarioselect_callback, false); @@ -345,3 +347,5 @@ static void window_server_start_paint(rct_window* w, rct_drawpixelinfo* dpi) gfx_draw_string_left(dpi, STR_PASSWORD, nullptr, w->colours[1], w->x + 6, w->y + w->widgets[WIDX_PASSWORD_INPUT].top); gfx_draw_string_left(dpi, STR_MAX_PLAYERS, nullptr, w->colours[1], w->x + 6, w->y + w->widgets[WIDX_MAXPLAYERS].top); } + +#endif diff --git a/src/openrct2-ui/windows/ShortcutKeyChange.cpp b/src/openrct2-ui/windows/ShortcutKeyChange.cpp index 2a1a6f6bf9..9989b1c0b7 100644 --- a/src/openrct2-ui/windows/ShortcutKeyChange.cpp +++ b/src/openrct2-ui/windows/ShortcutKeyChange.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2-ui/windows/ShortcutKeys.cpp b/src/openrct2-ui/windows/ShortcutKeys.cpp index a0db7fb12c..47aae89092 100644 --- a/src/openrct2-ui/windows/ShortcutKeys.cpp +++ b/src/openrct2-ui/windows/ShortcutKeys.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -150,6 +150,7 @@ const rct_string_id ShortcutStringIds[SHORTCUT_COUNT] = { STR_SHORTCUT_HIGHLIGHT_PATH_ISSUES_TOGGLE, STR_SHORTCUT_OPEN_TILE_INSPECTOR, STR_ADVANCE_TO_NEXT_TICK, + STR_SHORTCUT_OPEN_SCENERY_PICKER, }; // clang-format on @@ -192,7 +193,7 @@ static void window_shortcut_mouseup(rct_window* w, rct_widgetindex widgetIndex) case WIDX_RESET: keyboard_shortcuts_reset(); keyboard_shortcuts_save(); - window_invalidate(w); + w->Invalidate(); break; } } @@ -258,7 +259,7 @@ static void window_shortcut_scrollmouseover(rct_window* w, int32_t scrollIndex, w->selected_list_item = selected_item; - window_invalidate(w); + w->Invalidate(); } /** diff --git a/src/openrct2-ui/windows/Sign.cpp b/src/openrct2-ui/windows/Sign.cpp index 5c02229952..6eb04610c6 100644 --- a/src/openrct2-ui/windows/Sign.cpp +++ b/src/openrct2-ui/windows/Sign.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -131,6 +131,8 @@ static rct_window_event_list window_sign_small_events = { }; // clang-format on +static void window_sign_show_text_input(rct_window* w); + /** * * rct2: 0x006BA305 @@ -153,10 +155,13 @@ rct_window* window_sign_open(rct_windownumber number) w->number = number; window_init_scroll_widgets(w); - int32_t view_x = gBanners[w->number].x << 5; - int32_t view_y = gBanners[w->number].y << 5; + auto banner = GetBanner(w->number); + int32_t view_x = banner->position.x << 5; + int32_t view_y = banner->position.y << 5; TileElement* tile_element = map_get_first_element_at(view_x / 32, view_y / 32); + if (tile_element == nullptr) + return nullptr; while (1) { @@ -191,7 +196,7 @@ rct_window* window_sign_open(rct_windownumber number) (viewportWidget->bottom - viewportWidget->top) - 1, 0, view_x, view_y, view_z, 0, SPRITE_INDEX_NULL); w->viewport->flags = gConfigGeneral.always_show_gridlines ? VIEWPORT_FLAG_GRIDLINES : 0; - window_invalidate(w); + w->Invalidate(); return w; } @@ -202,14 +207,6 @@ rct_window* window_sign_open(rct_windownumber number) */ static void window_sign_mouseup(rct_window* w, rct_widgetindex widgetIndex) { - rct_banner* banner = &gBanners[w->number]; - int32_t x = banner->x << 5; - int32_t y = banner->y << 5; - - rct_string_id string_id; - - TileElement* tile_element = map_get_first_element_at(x / 32, y / 32); - switch (widgetIndex) { case WIDX_CLOSE: @@ -217,6 +214,12 @@ static void window_sign_mouseup(rct_window* w, rct_widgetindex widgetIndex) break; case WIDX_SIGN_DEMOLISH: { + auto banner = GetBanner(w->number); + int32_t x = banner->position.x << 5; + int32_t y = banner->position.y << 5; + auto tile_element = map_get_first_element_at(x / 32, y / 32); + if (tile_element == nullptr) + return; while (1) { if (tile_element->GetType() == TILE_ELEMENT_TYPE_LARGE_SCENERY) @@ -233,24 +236,13 @@ static void window_sign_mouseup(rct_window* w, rct_widgetindex widgetIndex) } auto sceneryRemoveAction = LargeSceneryRemoveAction( - x, y, tile_element->base_height, tile_element->GetDirection(), + { x, y, tile_element->base_height * 8, tile_element->GetDirection() }, tile_element->AsLargeScenery()->GetSequenceIndex()); GameActions::Execute(&sceneryRemoveAction); - break; } case WIDX_SIGN_TEXT: - if (banner->flags & BANNER_FLAG_LINKED_TO_RIDE) - { - Ride* ride = get_ride(banner->ride_index); - set_format_arg(16, uint32_t, ride->name_arguments); - string_id = ride->name; - } - else - { - string_id = gBanners[w->number].string_idx; - } - window_text_input_open(w, WIDX_SIGN_TEXT, STR_SIGN_TEXT_TITLE, STR_SIGN_TEXT_PROMPT, string_id, 0, 32); + window_sign_show_text_input(w); break; } } @@ -302,7 +294,7 @@ static void window_sign_dropdown(rct_window* w, rct_widgetindex widgetIndex, int return; } - window_invalidate(w); + w->Invalidate(); } /** @@ -371,10 +363,10 @@ static void window_sign_viewport_rotate(rct_window* w) view->width = 0; - rct_banner* banner = &gBanners[w->number]; + auto banner = GetBanner(w->number); - int32_t view_x = (banner->x << 5) + 16; - int32_t view_y = (banner->y << 5) + 16; + int32_t view_x = (banner->position.x << 5) + 16; + int32_t view_y = (banner->position.y << 5) + 16; int32_t view_z = w->frame_no; // Create viewport @@ -382,9 +374,9 @@ static void window_sign_viewport_rotate(rct_window* w) viewport_create( w, w->x + viewportWidget->left + 1, w->y + viewportWidget->top + 1, (viewportWidget->right - viewportWidget->left) - 1, (viewportWidget->bottom - viewportWidget->top) - 1, 0, view_x, view_y, view_z, 0, SPRITE_INDEX_NULL); - - w->viewport->flags = gConfigGeneral.always_show_gridlines ? VIEWPORT_FLAG_GRIDLINES : 0; - window_invalidate(w); + if (w->viewport != nullptr) + w->viewport->flags = gConfigGeneral.always_show_gridlines ? VIEWPORT_FLAG_GRIDLINES : 0; + w->Invalidate(); } /** @@ -412,10 +404,13 @@ rct_window* window_sign_small_open(rct_windownumber number) w->colours[1] = COLOUR_DARK_BROWN; w->colours[2] = COLOUR_DARK_BROWN; - int32_t view_x = gBanners[w->number].x << 5; - int32_t view_y = gBanners[w->number].y << 5; + auto banner = GetBanner(w->number); + int32_t view_x = banner->position.x << 5; + int32_t view_y = banner->position.y << 5; TileElement* tile_element = map_get_first_element_at(view_x / 32, view_y / 32); + if (tile_element == nullptr) + return nullptr; while (1) { @@ -449,7 +444,7 @@ rct_window* window_sign_small_open(rct_windownumber number) w->viewport->flags = gConfigGeneral.always_show_gridlines ? VIEWPORT_FLAG_GRIDLINES : 0; w->flags |= WF_NO_SCROLLING; - window_invalidate(w); + w->Invalidate(); return w; } @@ -460,14 +455,6 @@ rct_window* window_sign_small_open(rct_windownumber number) */ static void window_sign_small_mouseup(rct_window* w, rct_widgetindex widgetIndex) { - rct_banner* banner = &gBanners[w->number]; - int32_t x = banner->x << 5; - int32_t y = banner->y << 5; - - rct_string_id string_id; - - TileElement* tile_element = map_get_first_element_at(x / 32, y / 32); - switch (widgetIndex) { case WIDX_CLOSE: @@ -475,6 +462,12 @@ static void window_sign_small_mouseup(rct_window* w, rct_widgetindex widgetIndex break; case WIDX_SIGN_DEMOLISH: { + auto banner = GetBanner(w->number); + int32_t x = banner->position.x << 5; + int32_t y = banner->position.y << 5; + auto tile_element = map_get_first_element_at(x / 32, y / 32); + if (tile_element == nullptr) + return; while (true) { if (tile_element->GetType() == TILE_ELEMENT_TYPE_WALL) @@ -488,23 +481,13 @@ static void window_sign_small_mouseup(rct_window* w, rct_widgetindex widgetIndex } tile_element++; } - TileCoordsXYZD wallLocation = { x >> 5, y >> 5, tile_element->base_height, tile_element->GetDirection() }; + CoordsXYZD wallLocation = { x, y, tile_element->base_height * 8, tile_element->GetDirection() }; auto wallRemoveAction = WallRemoveAction(wallLocation); GameActions::Execute(&wallRemoveAction); + break; } - break; case WIDX_SIGN_TEXT: - if (banner->flags & BANNER_FLAG_LINKED_TO_RIDE) - { - Ride* ride = get_ride(banner->ride_index); - set_format_arg(16, uint32_t, ride->name_arguments); - string_id = ride->name; - } - else - { - string_id = gBanners[w->number].string_idx; - } - window_text_input_open(w, WIDX_SIGN_TEXT, STR_SIGN_TEXT_TITLE, STR_SIGN_TEXT_PROMPT, string_id, 0, 32); + window_sign_show_text_input(w); break; } } @@ -539,7 +522,7 @@ static void window_sign_small_dropdown(rct_window* w, rct_widgetindex widgetInde return; } - window_invalidate(w); + w->Invalidate(); } /** @@ -568,3 +551,13 @@ static void window_sign_small_invalidate(rct_window* w) main_colour_btn->image = SPRITE_ID_PALETTE_COLOUR_1(w->list_information_type) | IMAGE_TYPE_TRANSPARENT | SPR_PALETTE_BTN; text_colour_btn->image = SPRITE_ID_PALETTE_COLOUR_1(w->var_492) | IMAGE_TYPE_TRANSPARENT | SPR_PALETTE_BTN; } + +static void window_sign_show_text_input(rct_window* w) +{ + auto banner = GetBanner(w->number); + if (banner != nullptr) + { + auto bannerText = banner->GetText(); + window_text_input_raw_open(w, WIDX_SIGN_TEXT, STR_SIGN_TEXT_TITLE, STR_SIGN_TEXT_PROMPT, bannerText.c_str(), 32); + } +} diff --git a/src/openrct2-ui/windows/Staff.cpp b/src/openrct2-ui/windows/Staff.cpp index c31fc30c34..c89e26de20 100644 --- a/src/openrct2-ui/windows/Staff.cpp +++ b/src/openrct2-ui/windows/Staff.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -16,8 +16,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -329,7 +331,7 @@ rct_window* window_staff_open(Peep* peep) w->max_height = 450; } w->page = 0; - window_invalidate(w); + w->Invalidate(); w->widgets = window_staff_overview_widgets; w->enabled_widgets = window_staff_page_enabled_widgets[0]; @@ -364,13 +366,13 @@ void window_staff_disable_widgets(rct_window* w) if (peep_can_be_picked_up(peep)) { if (w->disabled_widgets & (1 << WIDX_PICKUP)) - window_invalidate(w); + w->Invalidate(); } else { disabled_widgets |= (1 << WIDX_PICKUP); if (!(w->disabled_widgets & (1 << WIDX_PICKUP))) - window_invalidate(w); + w->Invalidate(); } } @@ -426,13 +428,13 @@ void window_staff_set_page(rct_window* w, int32_t page) w->widgets = window_staff_page_widgets[page]; window_staff_disable_widgets(w); - window_invalidate(w); + w->Invalidate(); window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); if (listen && w->viewport) w->viewport->flags |= VIEWPORT_FLAG_SOUND_ON; @@ -457,17 +459,23 @@ void window_staff_overview_mouseup(rct_window* w, rct_widgetindex widgetIndex) window_staff_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_LOCATE: - window_scroll_to_viewport(w); + w->ScrollToViewport(); break; case WIDX_PICKUP: { - // this is called in callback when hiring staff, setting nestlevel to 0 so that command is sent separately - int32_t oldNestLevel = gGameCommandNestLevel; - gGameCommandNestLevel = 0; - game_command_callback = game_command_callback_pickup_staff; w->picked_peep_old_x = peep->x; - game_do_command(w->number, GAME_COMMAND_FLAG_APPLY, 0, 0, GAME_COMMAND_PICKUP_STAFF, 0, 0); - gGameCommandNestLevel = oldNestLevel; + + PeepPickupAction pickupAction{ PeepPickupType::Pickup, w->number, {}, network_get_current_player_id() }; + pickupAction.SetCallback([peepnum = w->number](const GameAction* ga, const GameActionResult* result) { + if (result->Error != GA_ERROR::OK) + return; + rct_window* wind = window_find_by_number(WC_PEEP, peepnum); + if (wind) + { + tool_set(wind, WC_STAFF__WIDX_PICKUP, TOOL_PICKER); + } + }); + GameActions::Execute(&pickupAction); } break; case WIDX_FIRE: @@ -478,10 +486,12 @@ void window_staff_overview_mouseup(rct_window* w, rct_widgetindex widgetIndex) break; } case WIDX_RENAME: - window_text_input_open( - w, widgetIndex, STR_STAFF_TITLE_STAFF_MEMBER_NAME, STR_STAFF_PROMPT_ENTER_NAME, peep->name_string_idx, peep->id, - 32); + { + auto peepName = peep->GetName(); + window_text_input_raw_open( + w, widgetIndex, STR_STAFF_TITLE_STAFF_MEMBER_NAME, STR_STAFF_PROMPT_ENTER_NAME, peepName.c_str(), 32); break; + } } } @@ -501,24 +511,24 @@ void window_staff_overview_resize(rct_window* w) if (w->width < w->min_width) { w->width = w->min_width; - window_invalidate(w); + w->Invalidate(); } if (w->width > w->max_width) { - window_invalidate(w); + w->Invalidate(); w->width = w->max_width; } if (w->height < w->min_height) { w->height = w->min_height; - window_invalidate(w); + w->Invalidate(); } if (w->height > w->max_height) { - window_invalidate(w); + w->Invalidate(); w->height = w->max_height; } @@ -704,24 +714,24 @@ void window_staff_stats_resize(rct_window* w) if (w->width < w->min_width) { w->width = w->min_width; - window_invalidate(w); + w->Invalidate(); } if (w->width > w->max_width) { - window_invalidate(w); + w->Invalidate(); w->width = w->max_width; } if (w->height < w->min_height) { w->height = w->min_height; - window_invalidate(w); + w->Invalidate(); } if (w->height > w->max_height) { - window_invalidate(w); + w->Invalidate(); w->height = w->max_height; } } @@ -739,7 +749,7 @@ void window_staff_stats_update(rct_window* w) if (peep->window_invalidate_flags & PEEP_INVALIDATE_STAFF_STATS) { peep->window_invalidate_flags &= ~PEEP_INVALIDATE_STAFF_STATS; - window_invalidate(w); + w->Invalidate(); } } @@ -770,8 +780,7 @@ void window_staff_stats_invalidate(rct_window* w) Peep* peep = GET_PEEP(w->number); - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); + peep->FormatNameTo(gCommonFormatArgs); window_staff_stats_widgets[WIDX_BACKGROUND].right = w->width - 1; window_staff_stats_widgets[WIDX_BACKGROUND].bottom = w->height - 1; @@ -805,8 +814,7 @@ void window_staff_options_invalidate(rct_window* w) Peep* peep = GET_PEEP(w->number); - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); + peep->FormatNameTo(gCommonFormatArgs); switch (peep->staff_type) { @@ -883,8 +891,7 @@ void window_staff_overview_invalidate(rct_window* w) Peep* peep = GET_PEEP(w->number); - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); + peep->FormatNameTo(gCommonFormatArgs); window_staff_overview_widgets[WIDX_BACKGROUND].right = w->width - 1; window_staff_overview_widgets[WIDX_BACKGROUND].bottom = w->height - 1; @@ -945,11 +952,8 @@ void window_staff_overview_paint(rct_window* w, rct_drawpixelinfo* dpi) } // Draw the centred label - uint32_t argument1, argument2; Peep* peep = GET_PEEP(w->number); - get_arguments_from_action(peep, &argument1, &argument2); - set_format_arg(0, uint32_t, argument1); - set_format_arg(4, uint32_t, argument2); + peep->FormatActionTo(gCommonFormatArgs); rct_widget* widget = &w->widgets[WIDX_BTM_LABEL]; int32_t x = (widget->left + widget->right) / 2 + w->x; int32_t y = w->y + widget->top; @@ -1197,9 +1201,16 @@ void window_staff_overview_tool_down(rct_window* w, rct_widgetindex widgetIndex, if (dest_x == LOCATION_NULL) return; - game_command_callback = game_command_callback_pickup_staff; - game_do_command( - w->number, GAME_COMMAND_FLAG_APPLY, 2, tileElement->base_height, GAME_COMMAND_PICKUP_STAFF, dest_x, dest_y); + PeepPickupAction pickupAction{ + PeepPickupType::Place, w->number, { dest_x, dest_y, tileElement->base_height }, network_get_current_player_id() + }; + pickupAction.SetCallback([](const GameAction* ga, const GameActionResult* result) { + if (result->Error != GA_ERROR::OK) + return; + tool_cancel(); + gPickupPeepImage = UINT32_MAX; + }); + GameActions::Execute(&pickupAction); } else if (widgetIndex == WIDX_PATROL) { @@ -1210,14 +1221,14 @@ void window_staff_overview_tool_down(rct_window* w, rct_widgetindex widgetIndex, return; rct_sprite* sprite = try_get_sprite(w->number); - if (sprite == nullptr || sprite->IsPeep() == false) + if (sprite == nullptr || !sprite->IsPeep()) return; Peep& peep = sprite->peep; if (peep.type != PEEP_TYPE_STAFF) return; - if (staff_is_patrol_area_set(peep.staff_id, dest_x, dest_y) == true) + if (staff_is_patrol_area_set(peep.staff_id, dest_x, dest_y)) { _staffPatrolAreaPaintValue = PatrolAreaValue::UNSET; } @@ -1225,7 +1236,8 @@ void window_staff_overview_tool_down(rct_window* w, rct_widgetindex widgetIndex, { _staffPatrolAreaPaintValue = PatrolAreaValue::SET; } - game_do_command(dest_x, 1, dest_y, w->number, GAME_COMMAND_SET_STAFF_PATROL, 0, 0); + auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(w->number, { dest_x, dest_y }); + GameActions::Execute(&staffSetPatrolAreaAction); } } @@ -1250,7 +1262,7 @@ void window_staff_overview_tool_drag(rct_window* w, rct_widgetindex widgetIndex, return; rct_sprite* sprite = try_get_sprite(w->number); - if (sprite == nullptr || sprite->IsPeep() == false) + if (sprite == nullptr || !sprite->IsPeep()) return; Peep& peep = sprite->peep; @@ -1258,12 +1270,13 @@ void window_staff_overview_tool_drag(rct_window* w, rct_widgetindex widgetIndex, return; bool patrolAreaValue = staff_is_patrol_area_set(peep.staff_id, dest_x, dest_y); - if (_staffPatrolAreaPaintValue == PatrolAreaValue::SET && patrolAreaValue == true) + if (_staffPatrolAreaPaintValue == PatrolAreaValue::SET && patrolAreaValue) return; // Since area is already the value we want, skip... - if (_staffPatrolAreaPaintValue == PatrolAreaValue::UNSET && patrolAreaValue == false) + if (_staffPatrolAreaPaintValue == PatrolAreaValue::UNSET && !patrolAreaValue) return; // Since area is already the value we want, skip... - game_do_command(dest_x, 1, dest_y, w->number, GAME_COMMAND_SET_STAFF_PATROL, 0, 0); + auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(w->number, { dest_x, dest_y }); + GameActions::Execute(&staffSetPatrolAreaAction); } void window_staff_overview_tool_up(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) @@ -1282,7 +1295,10 @@ void window_staff_overview_tool_abort(rct_window* w, rct_widgetindex widgetIndex { if (widgetIndex == WIDX_PICKUP) { - game_do_command(w->number, GAME_COMMAND_FLAG_APPLY, 1, 0, GAME_COMMAND_PICKUP_STAFF, w->picked_peep_old_x, 0); + PeepPickupAction pickupAction{ + PeepPickupType::Cancel, w->number, { w->picked_peep_old_x, 0, 0 }, network_get_current_player_id() + }; + GameActions::Execute(&pickupAction); } else if (widgetIndex == WIDX_PATROL) { @@ -1376,13 +1392,13 @@ void window_staff_viewport_init(rct_window* w) viewport_create(w, x, y, width, height, 0, 0, 0, 0, focus.type & VIEWPORT_FOCUS_TYPE_MASK, focus.sprite_id); w->flags |= WF_NO_SCROLLING; - window_invalidate(w); + w->Invalidate(); } } if (w->viewport) w->viewport->flags = viewport_flags; - window_invalidate(w); + w->Invalidate(); } /** diff --git a/src/openrct2-ui/windows/StaffFirePrompt.cpp b/src/openrct2-ui/windows/StaffFirePrompt.cpp index 31acc4e44d..439e6c2a48 100644 --- a/src/openrct2-ui/windows/StaffFirePrompt.cpp +++ b/src/openrct2-ui/windows/StaffFirePrompt.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -103,12 +104,13 @@ rct_window* window_staff_fire_prompt_open(Peep* peep) */ static void window_staff_fire_mouseup(rct_window *w, rct_widgetindex widgetIndex) { - Peep* peep = &get_sprite(w->number)->peep; - switch (widgetIndex){ case WIDX_YES: - game_do_command(peep->x, 1, peep->y, w->number, GAME_COMMAND_FIRE_STAFF_MEMBER, 0, 0); + { + auto staffFireAction = StaffFireAction(w->number); + GameActions::Execute(&staffFireAction); break; + } case WIDX_CANCEL: case WIDX_CLOSE: window_close(w); @@ -125,8 +127,7 @@ static void window_staff_fire_paint(rct_window *w, rct_drawpixelinfo *dpi) Peep* peep = &get_sprite(w->number)->peep; - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); + peep->FormatNameTo(gCommonFormatArgs); int32_t x = w->x + WW / 2; int32_t y = w->y + (WH / 2) - 3; diff --git a/src/openrct2-ui/windows/StaffList.cpp b/src/openrct2-ui/windows/StaffList.cpp index d969fcb494..739b3ea386 100644 --- a/src/openrct2-ui/windows/StaffList.cpp +++ b/src/openrct2-ui/windows/StaffList.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -206,13 +207,13 @@ static void window_staff_list_mouseup(rct_window* w, rct_widgetindex widgetIndex break; case WIDX_STAFF_LIST_HIRE_BUTTON: { - int32_t staffType = _windowStaffListSelectedTab; + STAFF_TYPE staffType = static_cast(_windowStaffListSelectedTab); + ENTERTAINER_COSTUME costume = ENTERTAINER_COSTUME_COUNT; if (staffType == STAFF_TYPE_ENTERTAINER) { - uint8_t costume = window_staff_list_get_random_entertainer_costume(); - staffType += costume; + costume = static_cast(window_staff_list_get_random_entertainer_costume()); } - hire_new_staff_member(staffType); + staff_hire_new_member(staffType, costume); break; } case WIDX_STAFF_LIST_SHOW_PATROL_AREA_BUTTON: @@ -228,7 +229,7 @@ static void window_staff_list_mouseup(rct_window* w, rct_widgetindex widgetIndex break; case WIDX_STAFF_LIST_QUICK_FIRE: _quick_fire_mode ^= 1; - window_invalidate(w); + w->Invalidate(); break; } } @@ -244,12 +245,12 @@ static void window_staff_list_resize(rct_window* w) if (w->width < w->min_width) { w->width = w->min_width; - window_invalidate(w); + w->Invalidate(); } if (w->height < w->min_height) { w->height = w->min_height; - window_invalidate(w); + w->Invalidate(); } } @@ -272,7 +273,7 @@ static void window_staff_list_mousedown(rct_window* w, rct_widgetindex widgetInd break; _windowStaffListSelectedTab = (uint8_t)newSelectedTab; - window_invalidate(w); + w->Invalidate(); w->scrolls[0].v_top = 0; window_staff_list_cancel_tools(w); break; @@ -430,7 +431,7 @@ void window_staff_list_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t if (_windowStaffListHighlightedIndex != -1) { _windowStaffListHighlightedIndex = -1; - window_invalidate(w); + w->Invalidate(); } *height = staffCount * SCROLLABLE_ROW_HEIGHT; @@ -441,7 +442,7 @@ void window_staff_list_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t if (i < w->scrolls[0].v_top) { w->scrolls[0].v_top = i; - window_invalidate(w); + w->Invalidate(); } *width = w->widgets[WIDX_STAFF_LIST_LIST].right - w->widgets[WIDX_STAFF_LIST_LIST].left - 15; @@ -465,7 +466,10 @@ void window_staff_list_scrollmousedown(rct_window* w, int32_t scrollIndex, int32 if (i == 0) { if (_quick_fire_mode) - game_do_command(peep->x, 1, peep->y, spriteIndex, GAME_COMMAND_FIRE_STAFF_MEMBER, 0, 0); + { + auto staffFireAction = StaffFireAction(spriteIndex); + GameActions::Execute(&staffFireAction); + } else { auto intent = Intent(WC_PEEP); @@ -491,7 +495,7 @@ void window_staff_list_scrollmouseover(rct_window* w, int32_t scrollIndex, int32 if (i != _windowStaffListHighlightedIndex) { _windowStaffListHighlightedIndex = i; - window_invalidate(w); + w->Invalidate(); } } @@ -656,7 +660,6 @@ static constexpr const uint32_t staffCostumeSprites[] = { void window_staff_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) { int32_t spriteIndex, y, i, staffOrderIcon_x, staffOrders, staffOrderSprite; - uint32_t argument_1, argument_2; uint8_t selectedTab; Peep* peep; @@ -690,13 +693,10 @@ void window_staff_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_ format = (_quick_fire_mode ? STR_LIGHTPINK_STRINGID : STR_WINDOW_COLOUR_2_STRINGID); } - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); + peep->FormatNameTo(gCommonFormatArgs); gfx_draw_string_left_clipped(dpi, format, gCommonFormatArgs, COLOUR_BLACK, 0, y, nameColumnSize); - get_arguments_from_action(peep, &argument_1, &argument_2); - set_format_arg(0, uint32_t, argument_1); - set_format_arg(4, uint32_t, argument_2); + peep->FormatActionTo(gCommonFormatArgs); gfx_draw_string_left_clipped(dpi, format, gCommonFormatArgs, COLOUR_BLACK, actionOffset, y, actionColumnSize); // True if a patrol path is set for the worker diff --git a/src/openrct2-ui/windows/TextInput.cpp b/src/openrct2-ui/windows/TextInput.cpp index 6ac78dcec5..be30cbfefb 100644 --- a/src/openrct2-ui/windows/TextInput.cpp +++ b/src/openrct2-ui/windows/TextInput.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -298,7 +298,7 @@ void window_text_input_key(rct_window* w, char keychar) } if (w) - window_invalidate(w); + w->Invalidate(); } void window_text_input_periodic_update(rct_window* w) @@ -317,7 +317,7 @@ void window_text_input_periodic_update(rct_window* w) if (w->frame_no > 30) w->frame_no = 0; - window_invalidate(w); + w->Invalidate(); } static void window_text_input_close(rct_window* w) @@ -344,7 +344,7 @@ static void window_text_input_invalidate(rct_window* w) // Change window size if required. if (height != w->height) { - window_invalidate(w); + w->Invalidate(); window_set_resize(w, WW, height, WW, height); } diff --git a/src/openrct2-ui/windows/Themes.cpp b/src/openrct2-ui/windows/Themes.cpp index 85f41a37ea..23ca6433e3 100644 --- a/src/openrct2-ui/windows/Themes.cpp +++ b/src/openrct2-ui/windows/Themes.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -465,22 +465,22 @@ static void window_themes_resize(rct_window* w) if (w->width < w->min_width) { w->width = w->min_width; - window_invalidate(w); + w->Invalidate(); } if (w->height < w->min_height) { w->height = w->min_height; - window_invalidate(w); + w->Invalidate(); } if (w->width > w->max_width) { w->width = w->max_width; - window_invalidate(w); + w->Invalidate(); } if (w->height > w->max_height) { w->height = w->max_height; - window_invalidate(w); + w->Invalidate(); } } } @@ -508,7 +508,7 @@ static void window_themes_mousedown(rct_window* w, rct_widgetindex widgetIndex, w->scrolls[0].v_top = 0; w->frame_no = 0; window_event_resize_call(w); - window_invalidate(w); + w->Invalidate(); break; case WIDX_THEMES_PRESETS_DROPDOWN: theme_manager_load_available_themes(); @@ -628,7 +628,7 @@ void window_themes_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* wi if (i < w->scrolls[0].v_top) { w->scrolls[0].v_top = i; - window_invalidate(w); + w->Invalidate(); } *width = 420; @@ -727,7 +727,7 @@ static void window_themes_textinput(rct_window* w, rct_widgetindex widgetIndex, { theme_rename(text); } - window_invalidate(w); + w->Invalidate(); } else { @@ -904,12 +904,10 @@ void window_themes_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t sc gfx_draw_string_left(dpi, theme_desc_get_name(wc), nullptr, w->colours[1], 2, y + 4); uint8_t colour = theme_get_colour(wc, j); - uint32_t image = SPRITE_ID_PALETTE_COLOUR_1(colour & ~COLOUR_FLAG_TRANSLUCENT) | IMAGE_TYPE_TRANSPARENT - | SPR_PALETTE_BTN; + uint32_t image = SPRITE_ID_PALETTE_COLOUR_1(colour & ~COLOUR_FLAG_TRANSLUCENT) | SPR_PALETTE_BTN; if (i == _colour_index_1 && j == _colour_index_2) { - image = SPRITE_ID_PALETTE_COLOUR_1(colour & ~COLOUR_FLAG_TRANSLUCENT) | IMAGE_TYPE_TRANSPARENT - | SPR_PALETTE_BTN_PRESSED; + image = SPRITE_ID_PALETTE_COLOUR_1(colour & ~COLOUR_FLAG_TRANSLUCENT) | SPR_PALETTE_BTN_PRESSED; } gfx_draw_sprite(dpi, image, _button_offset_x + 12 * j, y + _button_offset_y, 0); diff --git a/src/openrct2-ui/windows/TileInspector.cpp b/src/openrct2-ui/windows/TileInspector.cpp index 7bee7f47db..d1f249fba8 100644 --- a/src/openrct2-ui/windows/TileInspector.cpp +++ b/src/openrct2-ui/windows/TileInspector.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,6 @@ #include #include #include -#include // clang-format off static constexpr const rct_string_id TerrainTypeStringIds[] = { @@ -100,8 +100,8 @@ enum WINDOW_TILE_INSPECTOR_WIDGET_IDX { WIDX_SPINNER_Y_DECREASE, WIDX_BUTTON_CORRUPT, WIDX_BUTTON_REMOVE, - WIDX_BUTTON_MOVE_DOWN, WIDX_BUTTON_MOVE_UP, + WIDX_BUTTON_MOVE_DOWN, WIDX_BUTTON_ROTATE, WIDX_BUTTON_SORT, WIDX_BUTTON_COPY, @@ -110,7 +110,6 @@ enum WINDOW_TILE_INSPECTOR_WIDGET_IDX { WIDX_COLUMN_BASEHEIGHT, WIDX_COLUMN_CLEARANCEHEIGHT, WIDX_COLUMN_GHOSTFLAG, - WIDX_COLUMN_BROKENFLAG, WIDX_COLUMN_LASTFLAG, WIDX_GROUPBOX_DETAILS, WIDX_GROUPBOX_PROPERTIES, @@ -133,6 +132,7 @@ enum WINDOW_TILE_INSPECTOR_WIDGET_IDX { WIDX_PATH_SPINNER_HEIGHT = PAGE_WIDGETS, WIDX_PATH_SPINNER_HEIGHT_INCREASE, WIDX_PATH_SPINNER_HEIGHT_DECREASE, + WIDX_PATH_CHECK_BROKEN, WIDX_PATH_CHECK_SLOPED, WIDX_PATH_CHECK_EDGE_NE, // Note: This is NOT named after the world orientation, but after the way WIDX_PATH_CHECK_EDGE_E, // it looks in the window (top corner is north). Their order is important, @@ -149,6 +149,8 @@ enum WINDOW_TILE_INSPECTOR_WIDGET_IDX { WIDX_TRACK_SPINNER_HEIGHT_INCREASE, WIDX_TRACK_SPINNER_HEIGHT_DECREASE, WIDX_TRACK_CHECK_CHAIN_LIFT, + WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED, + WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE, // Scenery WIDX_SCENERY_SPINNER_HEIGHT = PAGE_WIDGETS, @@ -214,11 +216,10 @@ enum WINDOW_TILE_INSPECTOR_WIDGET_IDX { // Column offsets for the table headers #define COL_X_TYPE 3 // Type -#define COL_X_BH (COL_X_TYPE + 300) // Base height +#define COL_X_BH (COL_X_TYPE + 312) // Base height #define COL_X_CH (COL_X_BH + 20) // Clearance height #define COL_X_GF (COL_X_CH + 20) // Ghost flag -#define COL_X_BF (COL_X_GF + 12) // Broken flag -#define COL_X_LF (COL_X_BF + 12) // Last for tile flag +#define COL_X_LF (COL_X_GF + 12) // Last for tile flag #define PADDING_BOTTOM 15 #define GROUPBOX_PADDING 6 @@ -253,8 +254,8 @@ enum WINDOW_TILE_INSPECTOR_WIDGET_IDX { /* Buttons */ \ { WWT_FLATBTN, 1, BX, BW, BY, BH, SPR_MAP, STR_INSERT_CORRUPT_TIP }, /* Insert corrupt button */ \ { WWT_FLATBTN, 1, BX - BS * 1, BW - BS * 1, BY, BH, SPR_DEMOLISH, STR_REMOVE_SELECTED_ELEMENT_TIP }, /* Remove button */ \ - { WWT_BUTTON, 1, BX - BS * 2, BW - BS * 2, BY, BY + 11, STR_UP, STR_MOVE_SELECTED_ELEMENT_UP_TIP }, /* Move down */ \ - { WWT_BUTTON, 1, BX - BS * 2, BW - BS * 2, BH - 11, BH, STR_DOWN, STR_MOVE_SELECTED_ELEMENT_DOWN_TIP }, /* Move up */ \ + { WWT_BUTTON, 1, BX - BS * 2, BW - BS * 2, BY, BY + 11, STR_UP, STR_MOVE_SELECTED_ELEMENT_UP_TIP }, /* Move up */ \ + { WWT_BUTTON, 1, BX - BS * 2, BW - BS * 2, BH - 11, BH, STR_DOWN, STR_MOVE_SELECTED_ELEMENT_DOWN_TIP }, /* Move down */ \ { WWT_FLATBTN, 1, BX - BS * 3, BW - BS * 3, BY, BH, SPR_ROTATE_ARROW, STR_ROTATE_SELECTED_ELEMENT_TIP }, /* Rotate button */ \ { WWT_FLATBTN, 1, BX - BS * 4, BW - BS * 4, BY, BH, SPR_G2_SORT, STR_TILE_INSPECTOR_SORT_TIP }, /* Sort button */ \ { WWT_FLATBTN, 1, BX - BS * 5, BW - BS * 5, BY, BH, SPR_G2_COPY, STR_TILE_INSPECTOR_COPY_TIP }, /* Copy button */ \ @@ -263,8 +264,7 @@ enum WINDOW_TILE_INSPECTOR_WIDGET_IDX { { WWT_TABLE_HEADER, 1, COL_X_TYPE, COL_X_BH - 1, 42, 42 + 13, STR_NONE, STR_NONE }, /* Type */ \ { WWT_TABLE_HEADER, 1, COL_X_BH, COL_X_CH - 1, 42, 42 + 13, STR_NONE, STR_TILE_INSPECTOR_BASE_HEIGHT }, /* Base height */ \ { WWT_TABLE_HEADER, 1, COL_X_CH, COL_X_GF - 1, 42, 42 + 13, STR_NONE, STR_TILE_INSPECTOR_CLEARANCE_HEIGHT }, /* Clearance height */ \ - { WWT_TABLE_HEADER, 1, COL_X_GF, COL_X_BF - 1, 42, 42 + 13, STR_NONE, STR_TILE_INSPECTOR_FLAG_GHOST }, /* Ghost flag */ \ - { WWT_TABLE_HEADER, 1, COL_X_BF, COL_X_LF - 1, 42, 42 + 13, STR_NONE, STR_TILE_INSPECTOR_FLAG_BROKEN }, /* Broken flag */ \ + { WWT_TABLE_HEADER, 1, COL_X_GF, COL_X_LF - 1, 42, 42 + 13, STR_NONE, STR_TILE_INSPECTOR_FLAG_GHOST }, /* Ghost flag */ \ { WWT_TABLE_HEADER, 1, COL_X_LF, WW - 3, 42, 42 + 13, STR_NONE, STR_TILE_INSPECTOR_FLAG_LAST }, /* Last of tile flag */ \ { WWT_GROUPBOX, 1, 6, WW - 6, -1, -1, STR_NONE, STR_NONE }, /* Details group box */ \ { WWT_GROUPBOX, 1, 6, WW - 6, -1, -1, STR_TILE_INSPECTOR_GROUPBOX_PROPERTIES, STR_NONE } /* Properties group box */ @@ -293,13 +293,14 @@ static rct_widget SurfaceWidgets[] = { }; #define PAT_GBPB PADDING_BOTTOM // Path group box properties bottom -#define PAT_GBPT (PAT_GBPB + 16 + 4 * 21) // Path group box properties top +#define PAT_GBPT (PAT_GBPB + 16 + 5 * 21) // Path group box properties top #define PAT_GBDB (PAT_GBPT + GROUPBOX_PADDING) // Path group box info bottom #define PAT_GBDT (PAT_GBDB + 20 + 2 * 11) // Path group box info top static rct_widget PathWidgets[] = { MAIN_TILE_INSPECTOR_WIDGETS, SPINNER_WIDGETS (1, GBBL(1), GBBR(1), GBBT(WH - PAT_GBPT, 0) + 3, GBBB(WH - PAT_GBPT, 0) - 3, STR_NONE, STR_NONE), // WIDX_PATH_SPINNER_HEIGHT{,_INCREASE,_DECREASE} - { WWT_CHECKBOX, 1, GBBF(WH - PAT_GBPT, 0, 1), STR_TILE_INSPECTOR_PATH_SLOPED, STR_NONE }, // WIDX_PATH_CHECK_SLOPED + { WWT_CHECKBOX, 1, GBBF(WH - PAT_GBPT, 0, 1), STR_TILE_INSPECTOR_PATH_BROKEN, STR_NONE }, // WIDX_PATH_CHECK_BROKEN + { WWT_CHECKBOX, 1, GBBF(WH - PAT_GBPT, 0, 2), STR_TILE_INSPECTOR_PATH_SLOPED, STR_NONE }, // WIDX_PATH_CHECK_SLOPED { WWT_CHECKBOX, 1, CHK(GBBL(1) + 14 * 3, GBBT(WH - PAT_GBPT, 2) + 7 * 1), STR_NONE, STR_NONE }, // WIDX_PATH_CHECK_EDGE_NE { WWT_CHECKBOX, 1, CHK(GBBL(1) + 14 * 4, GBBT(WH - PAT_GBPT, 2) + 7 * 2), STR_NONE, STR_NONE }, // WIDX_PATH_CHECK_EDGE_E { WWT_CHECKBOX, 1, CHK(GBBL(1) + 14 * 3, GBBT(WH - PAT_GBPT, 2) + 7 * 3), STR_NONE, STR_NONE }, // WIDX_PATH_CHECK_EDGE_SE @@ -312,14 +313,16 @@ static rct_widget PathWidgets[] = { }; #define TRA_GBPB PADDING_BOTTOM // Track group box properties bottom -#define TRA_GBPT (TRA_GBPB + 16 + 3 * 21) // Track group box properties top +#define TRA_GBPT (TRA_GBPB + 16 + 5 * 21) // Track group box properties top #define TRA_GBDB (TRA_GBPT + GROUPBOX_PADDING) // Track group box info bottom -#define TRA_GBDT (TRA_GBDB + 20 + 6 * 11) // Track group box info top +#define TRA_GBDT (TRA_GBDB + 20 + 7 * 11) // Track group box info top static rct_widget TrackWidgets[] = { MAIN_TILE_INSPECTOR_WIDGETS, { WWT_CHECKBOX, 1, GBBF(WH - TRA_GBPT, 0, 0), STR_TILE_INSPECTOR_TRACK_ENTIRE_TRACK_PIECE, STR_NONE }, // WIDX_TRACK_CHECK_APPLY_TO_ALL SPINNER_WIDGETS (1, GBBL(1), GBBR(1), GBBT(WH - TRA_GBPT, 1) + 3, GBBB(WH - TRA_GBPT, 1) - 3, STR_NONE, STR_NONE), // WIDX_TRACK_SPINNER_HEIGHT{,_INCREASE,_DECREASE} { WWT_CHECKBOX, 1, GBBF(WH - TRA_GBPT, 0, 2), STR_TILE_INSPECTOR_TRACK_CHAIN_LIFT, STR_NONE }, // WIDX_TRACK_CHECK_CHAIN_LIFT + { WWT_CHECKBOX, 1, GBBF(WH - TRA_GBPT, 0, 3), STR_TILE_INSPECTOR_TRACK_BLOCK_BRAKE, STR_NONE }, // WIDX_PATH_CHECK_BLOCK_BRAKE_CLOSED + { WWT_CHECKBOX, 1, GBBF(WH - TRA_GBPT, 0, 4), STR_TILE_INSPECTOR_TRACK_IS_INDESTRUCTIBLE, STR_NONE }, // WIDX_PATH_CHECK_IS_INDESTRUCTIBLE { WIDGETS_END }, }; @@ -443,8 +446,7 @@ static bool windowTileInspectorTileSelected = false; static int32_t windowTileInspectorToolMouseX = 0; static int32_t windowTileInspectorToolMouseY = 0; static bool windowTileInspectorToolCtrlDown = false; -static int32_t windowTileInspectorToolMapX = 0; -static int32_t windowTileInspectorToolMapY = 0; +static CoordsXY windowTileInspectorToolMap = {}; static bool windowTileInspectorApplyToAll = false; static bool windowTileInspectorElementCopied = false; static TileElement tileInspectorCopiedElement; @@ -501,8 +503,8 @@ static rct_window_event_list TileInspectorWindowEvents = { static uint64_t PageEnabledWidgets[] = { (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT), (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_SURFACE_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_SURFACE_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_SURFACE_BUTTON_REMOVE_FENCES) | (1ULL << WIDX_SURFACE_BUTTON_RESTORE_FENCES) | (1ULL << WIDX_SURFACE_CHECK_CORNER_N) | (1ULL << WIDX_SURFACE_CHECK_CORNER_E) | (1ULL << WIDX_SURFACE_CHECK_CORNER_S) | (1ULL << WIDX_SURFACE_CHECK_CORNER_W) | (1ULL << WIDX_SURFACE_CHECK_DIAGONAL), - (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_PATH_CHECK_SLOPED) | (1ULL << WIDX_PATH_CHECK_EDGE_N) | (1ULL << WIDX_PATH_CHECK_EDGE_NE) | (1ULL << WIDX_PATH_CHECK_EDGE_E) | (1ULL << WIDX_PATH_CHECK_EDGE_SE) | (1ULL << WIDX_PATH_CHECK_EDGE_S) | (1ULL << WIDX_PATH_CHECK_EDGE_SW) | (1ULL << WIDX_PATH_CHECK_EDGE_W) | (1ULL << WIDX_PATH_CHECK_EDGE_NW), - (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_TRACK_CHECK_APPLY_TO_ALL) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_TRACK_CHECK_CHAIN_LIFT), + (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_PATH_CHECK_SLOPED) | (1ULL << WIDX_PATH_CHECK_BROKEN) | (1ULL << WIDX_PATH_CHECK_EDGE_N) | (1ULL << WIDX_PATH_CHECK_EDGE_NE) | (1ULL << WIDX_PATH_CHECK_EDGE_E) | (1ULL << WIDX_PATH_CHECK_EDGE_SE) | (1ULL << WIDX_PATH_CHECK_EDGE_S) | (1ULL << WIDX_PATH_CHECK_EDGE_SW) | (1ULL << WIDX_PATH_CHECK_EDGE_W) | (1ULL << WIDX_PATH_CHECK_EDGE_NW), + (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_TRACK_CHECK_APPLY_TO_ALL) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_TRACK_CHECK_CHAIN_LIFT) | (1ULL << WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED) | (1ULL << WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE), (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_SCENERY_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_SCENERY_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_N) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_E) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_S) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_W) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_N) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_E) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_S) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_W), (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_ENTRANCE_BUTTON_MAKE_USABLE), (1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_WALL_DROPDOWN_SLOPE) | (1ULL << WIDX_WALL_DROPDOWN_SLOPE_BUTTON), @@ -547,7 +549,7 @@ rct_window* window_tile_inspector_open() if (window != nullptr) return window; - window = window_create(0, 29, WW, WH, &TileInspectorWindowEvents, WC_TILE_INSPECTOR, WF_RESIZABLE); + window = window_create(ScreenCoordsXY(0, 29), WW, WH, &TileInspectorWindowEvents, WC_TILE_INSPECTOR, WF_RESIZABLE); window_tile_inspector_set_page(window, TILE_INSPECTOR_PAGE_DEFAULT); window->min_width = MIN_WW; @@ -574,7 +576,8 @@ static TileElement* window_tile_inspector_get_selected_element(rct_window* w) openrct2_assert( windowTileInspectorSelectedIndex >= 0 && windowTileInspectorSelectedIndex < windowTileInspectorElementCount, "Selected list item out of range"); - return map_get_first_element_at(windowTileInspectorTileX, windowTileInspectorTileY) + windowTileInspectorSelectedIndex; + return map_get_first_element_at(windowTileInspectorToolMap.x / 32, windowTileInspectorToolMap.y / 32) + + windowTileInspectorSelectedIndex; } static void window_tile_inspector_select_element_from_list(rct_window* w, int32_t index) @@ -588,7 +591,7 @@ static void window_tile_inspector_select_element_from_list(rct_window* w, int32_ windowTileInspectorSelectedIndex = index; } - window_invalidate(w); + w->Invalidate(); } static void window_tile_inspector_load_tile(rct_window* w, TileElement* elementToSelect) @@ -596,10 +599,12 @@ static void window_tile_inspector_load_tile(rct_window* w, TileElement* elementT windowTileInspectorSelectedIndex = -1; w->scrolls[0].v_top = 0; - TileElement* element = map_get_first_element_at(windowTileInspectorTileX, windowTileInspectorTileY); + TileElement* element = map_get_first_element_at(windowTileInspectorToolMap.x / 32, windowTileInspectorToolMap.y / 32); int16_t numItems = 0; do { + if (element == nullptr) + break; if (element == elementToSelect) { windowTileInspectorSelectedIndex = numItems; @@ -610,31 +615,28 @@ static void window_tile_inspector_load_tile(rct_window* w, TileElement* elementT windowTileInspectorElementCount = numItems; - window_invalidate(w); + w->Invalidate(); } static void window_tile_inspector_insert_corrupt_element(int32_t elementIndex) { openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - game_do_command( - TILE_INSPECTOR_ANY_INSERT_CORRUPT, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), - elementIndex, GAME_COMMAND_MODIFY_TILE, 0, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyInsertCorrupt, elementIndex); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_remove_element(int32_t elementIndex) { openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - game_do_command( - TILE_INSPECTOR_ANY_REMOVE, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), - elementIndex, GAME_COMMAND_MODIFY_TILE, 0, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyRemove, elementIndex); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_rotate_element(int32_t elementIndex) { openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - game_do_command( - TILE_INSPECTOR_ANY_ROTATE, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), - elementIndex, GAME_COMMAND_MODIFY_TILE, 0, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyRotate, elementIndex); + GameActions::Execute(&modifyTile); } // Swap element with its parent @@ -642,17 +644,15 @@ static void window_tile_inspector_swap_elements(int16_t first, int16_t second) { openrct2_assert(first >= 0 && first < windowTileInspectorElementCount, "first out of range"); openrct2_assert(second >= 0 && second < windowTileInspectorElementCount, "second out of range"); - game_do_command( - TILE_INSPECTOR_ANY_SWAP, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), first, - GAME_COMMAND_MODIFY_TILE, second, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnySwap, first, second); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_sort_elements() { openrct2_assert(windowTileInspectorTileSelected, "No tile selected"); - game_do_command( - TILE_INSPECTOR_ANY_SORT, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), 0, - GAME_COMMAND_MODIFY_TILE, 0, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnySort); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_copy_element(rct_window* w) @@ -660,114 +660,120 @@ static void window_tile_inspector_copy_element(rct_window* w) // Copy value, in case the element gets moved tileInspectorCopiedElement = *window_tile_inspector_get_selected_element(w); windowTileInspectorElementCopied = true; - window_invalidate(w); + w->Invalidate(); } static void window_tile_inspector_paste_element(rct_window* w) { - // Construct the data to send using the surface's properties - int32_t data[2]; - std::memcpy(&data[0], &tileInspectorCopiedElement, 8); - assert_struct_size(data, sizeof(tileInspectorCopiedElement)); - - game_do_command( - TILE_INSPECTOR_ANY_PASTE, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), data[0], - GAME_COMMAND_MODIFY_TILE, data[1], 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyPaste, 0, 0, tileInspectorCopiedElement); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_base_height_offset(int16_t elementIndex, int8_t heightOffset) { - game_do_command( - TILE_INSPECTOR_ANY_BASE_HEIGHT_OFFSET, GAME_COMMAND_FLAG_APPLY, - windowTileInspectorTileX | (windowTileInspectorTileY << 8), elementIndex, GAME_COMMAND_MODIFY_TILE, heightOffset, 0); + auto modifyTile = TileModifyAction( + windowTileInspectorToolMap, TileModifyType::AnyBaseHeightOffset, elementIndex, heightOffset); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_surface_show_park_fences(bool showFences) { - game_do_command( - TILE_INSPECTOR_SURFACE_SHOW_PARK_FENCES, GAME_COMMAND_FLAG_APPLY, - windowTileInspectorTileX | (windowTileInspectorTileY << 8), showFences, GAME_COMMAND_MODIFY_TILE, 0, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::SurfaceShowParkFences, showFences); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_surface_toggle_corner(int32_t cornerIndex) { - game_do_command( - TILE_INSPECTOR_SURFACE_TOGGLE_CORNER, GAME_COMMAND_FLAG_APPLY, - windowTileInspectorTileX | (windowTileInspectorTileY << 8), cornerIndex, GAME_COMMAND_MODIFY_TILE, 0, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::SurfaceToggleCorner, cornerIndex); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_surface_toggle_diagonal() { - game_do_command( - TILE_INSPECTOR_SURFACE_TOGGLE_DIAGONAL, GAME_COMMAND_FLAG_APPLY, - windowTileInspectorTileX | (windowTileInspectorTileY << 8), 0, GAME_COMMAND_MODIFY_TILE, 0, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::SurfaceToggleDiagonal); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_path_set_sloped(int32_t elementIndex, bool sloped) { - game_do_command( - TILE_INSPECTOR_PATH_SET_SLOPE, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), - elementIndex, GAME_COMMAND_MODIFY_TILE, sloped, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::PathSetSlope, elementIndex, sloped); + GameActions::Execute(&modifyTile); +} + +static void window_tile_inspector_path_set_broken(int32_t elementIndex, bool broken) +{ + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::PathSetBroken, elementIndex, broken); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_path_toggle_edge(int32_t elementIndex, int32_t cornerIndex) { openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); openrct2_assert(cornerIndex >= 0 && cornerIndex < 8, "cornerIndex out of range"); - game_do_command( - TILE_INSPECTOR_PATH_TOGGLE_EDGE, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), - elementIndex, GAME_COMMAND_MODIFY_TILE, cornerIndex, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::PathToggleEdge, elementIndex, cornerIndex); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_entrance_make_usable(int32_t elementIndex) { Guard::ArgumentInRange(elementIndex, 0, windowTileInspectorElementCount - 1); - game_do_command( - TILE_INSPECTOR_ENTRANCE_MAKE_USABLE, GAME_COMMAND_FLAG_APPLY, - windowTileInspectorTileX | (windowTileInspectorTileY << 8), elementIndex, GAME_COMMAND_MODIFY_TILE, 0, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::EntranceMakeUsable, elementIndex); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_wall_set_slope(int32_t elementIndex, int32_t slopeValue) { // Make sure only the correct bits are set openrct2_assert((slopeValue & 3) == slopeValue, "slopeValue doesn't match its mask"); - - game_do_command( - TILE_INSPECTOR_WALL_SET_SLOPE, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), - elementIndex, GAME_COMMAND_MODIFY_TILE, slopeValue, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::WallSetSlope, elementIndex, slopeValue); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_track_block_height_offset(int32_t elementIndex, int8_t heightOffset) { - game_do_command( - TILE_INSPECTOR_TRACK_BASE_HEIGHT_OFFSET, GAME_COMMAND_FLAG_APPLY, - windowTileInspectorTileX | (windowTileInspectorTileY << 8), elementIndex, GAME_COMMAND_MODIFY_TILE, heightOffset, 0); + auto modifyTile = TileModifyAction( + windowTileInspectorToolMap, TileModifyType::TrackBaseHeightOffset, elementIndex, heightOffset); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_track_block_set_lift(int32_t elementIndex, bool entireTrackBlock, bool chain) { - game_do_command( - TILE_INSPECTOR_TRACK_SET_CHAIN, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), - elementIndex, GAME_COMMAND_MODIFY_TILE, entireTrackBlock, chain); + auto modifyTile = TileModifyAction( + windowTileInspectorToolMap, entireTrackBlock ? TileModifyType::TrackSetChainBlock : TileModifyType::TrackSetChain, + elementIndex, chain); + GameActions::Execute(&modifyTile); +} + +static void window_tile_inspector_track_set_block_brake(int32_t elementIndex, bool blockBrake) +{ + auto modifyTile = TileModifyAction( + windowTileInspectorToolMap, TileModifyType::TrackSetBlockBrake, elementIndex, blockBrake); + GameActions::Execute(&modifyTile); +} + +static void window_tile_inspector_track_set_indestructible(int32_t elementIndex, bool isIndestructible) +{ + auto modifyTile = TileModifyAction( + windowTileInspectorToolMap, TileModifyType::TrackSetIndestructible, elementIndex, isIndestructible); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_quarter_tile_set(int32_t elementIndex, const int32_t quarterIndex) { // quarterIndex is widget index relative to WIDX_SCENERY_CHECK_QUARTER_N, so a value from 0-3 openrct2_assert(quarterIndex >= 0 && quarterIndex < 4, "quarterIndex out of range"); - - game_do_command( - TILE_INSPECTOR_SCENERY_SET_QUARTER_LOCATION, GAME_COMMAND_FLAG_APPLY, - windowTileInspectorTileX | (windowTileInspectorTileY << 8), elementIndex, GAME_COMMAND_MODIFY_TILE, - (quarterIndex - get_current_rotation()) & 3, 0); + auto modifyTile = TileModifyAction( + windowTileInspectorToolMap, TileModifyType::ScenerySetQuarterLocation, elementIndex, + (quarterIndex - get_current_rotation()) & 3); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_toggle_quadrant_collosion(int32_t elementIndex, const int32_t quadrantIndex) { - game_do_command( - TILE_INSPECTOR_SCENERY_SET_QUARTER_COLLISION, GAME_COMMAND_FLAG_APPLY, - windowTileInspectorTileX | (windowTileInspectorTileY << 8), elementIndex, GAME_COMMAND_MODIFY_TILE, - (quadrantIndex + 2 - get_current_rotation()) & 3, 0); + auto modifyTile = TileModifyAction( + windowTileInspectorToolMap, TileModifyType::ScenerySetQuarterCollision, elementIndex, + (quadrantIndex + 2 - get_current_rotation()) & 3); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_banner_toggle_block(int32_t elementIndex, int32_t edgeIndex) @@ -776,17 +782,15 @@ static void window_tile_inspector_banner_toggle_block(int32_t elementIndex, int3 // Make edgeIndex abstract edgeIndex = (edgeIndex - get_current_rotation()) & 3; - - game_do_command( - TILE_INSPECTOR_BANNER_TOGGLE_BLOCKING_EDGE, GAME_COMMAND_FLAG_APPLY, - windowTileInspectorTileX | (windowTileInspectorTileY << 8), elementIndex, GAME_COMMAND_MODIFY_TILE, edgeIndex, 0); + auto modifyTile = TileModifyAction( + windowTileInspectorToolMap, TileModifyType::BannerToggleBlockingEdge, elementIndex, edgeIndex); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_clamp_corrupt(int32_t elementIndex) { - game_do_command( - TILE_INSPECTOR_CORRUPT_CLAMP, GAME_COMMAND_FLAG_APPLY, windowTileInspectorTileX | (windowTileInspectorTileY << 8), - elementIndex, GAME_COMMAND_MODIFY_TILE, 0, 0); + auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::CorruptClamp, elementIndex); + GameActions::Execute(&modifyTile); } static void window_tile_inspector_mouseup(rct_window* w, rct_widgetindex widgetIndex) @@ -819,10 +823,10 @@ static void window_tile_inspector_mouseup(rct_window* w, rct_widgetindex widgetI case WIDX_BUTTON_PASTE: window_tile_inspector_paste_element(w); break; - case WIDX_BUTTON_MOVE_DOWN: + case WIDX_BUTTON_MOVE_UP: window_tile_inspector_swap_elements(windowTileInspectorSelectedIndex, windowTileInspectorSelectedIndex + 1); break; - case WIDX_BUTTON_MOVE_UP: + case WIDX_BUTTON_MOVE_DOWN: window_tile_inspector_swap_elements(windowTileInspectorSelectedIndex - 1, windowTileInspectorSelectedIndex); break; } @@ -867,6 +871,9 @@ static void window_tile_inspector_mouseup(rct_window* w, rct_widgetindex widgetI case WIDX_PATH_CHECK_SLOPED: window_tile_inspector_path_set_sloped(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsSloped()); break; + case WIDX_PATH_CHECK_BROKEN: + window_tile_inspector_path_set_broken(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsBroken()); + break; case WIDX_PATH_CHECK_EDGE_E: case WIDX_PATH_CHECK_EDGE_S: case WIDX_PATH_CHECK_EDGE_W: @@ -910,6 +917,14 @@ static void window_tile_inspector_mouseup(rct_window* w, rct_widgetindex widgetI window_tile_inspector_track_block_set_lift(windowTileInspectorSelectedIndex, entireTrackBlock, newLift); break; } + case WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED: + window_tile_inspector_track_set_block_brake( + windowTileInspectorSelectedIndex, !tileElement->AsTrack()->BlockBrakeClosed()); + break; + case WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE: + window_tile_inspector_track_set_indestructible( + windowTileInspectorSelectedIndex, !tileElement->AsTrack()->IsIndestructible()); + break; } // switch widget index break; @@ -972,12 +987,12 @@ static void window_tile_inspector_resize(rct_window* w) w->min_height = MIN_WH; if (w->width < w->min_width) { - window_invalidate(w); + w->Invalidate(); w->width = w->min_width; } if (w->height < w->min_height) { - window_invalidate(w); + w->Invalidate(); w->height = w->min_height; } } @@ -988,18 +1003,24 @@ static void window_tile_inspector_mousedown(rct_window* w, rct_widgetindex widge { case WIDX_SPINNER_X_INCREASE: windowTileInspectorTileX = std::min(windowTileInspectorTileX + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1); + windowTileInspectorToolMap.x = std::min( + windowTileInspectorToolMap.x + 32, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); window_tile_inspector_load_tile(w, nullptr); break; case WIDX_SPINNER_X_DECREASE: windowTileInspectorTileX = std::max(windowTileInspectorTileX - 1, 0); + windowTileInspectorToolMap.x = std::max(windowTileInspectorToolMap.x - 32, 0); window_tile_inspector_load_tile(w, nullptr); break; case WIDX_SPINNER_Y_INCREASE: windowTileInspectorTileY = std::min(windowTileInspectorTileY + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1); + windowTileInspectorToolMap.y = std::min( + windowTileInspectorToolMap.y + 32, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); window_tile_inspector_load_tile(w, nullptr); break; case WIDX_SPINNER_Y_DECREASE: windowTileInspectorTileY = std::max(windowTileInspectorTileY - 1, 0); + windowTileInspectorToolMap.y = std::max(windowTileInspectorToolMap.y - 32, 0); window_tile_inspector_load_tile(w, nullptr); break; } // switch widget index @@ -1222,8 +1243,8 @@ static void window_tile_inspector_tool_update(rct_window* w, rct_widgetindex wid } else if (windowTileInspectorTileSelected) { - gMapSelectPositionA.x = gMapSelectPositionB.x = windowTileInspectorTileX << 5; - gMapSelectPositionA.y = gMapSelectPositionB.y = windowTileInspectorTileY << 5; + gMapSelectPositionA.x = gMapSelectPositionB.x = windowTileInspectorToolMap.x; + gMapSelectPositionA.y = gMapSelectPositionB.y = windowTileInspectorToolMap.y; } else { @@ -1268,15 +1289,15 @@ static void window_tile_inspector_update_selected_tile(rct_window* w, int32_t x, } // Tile is already selected - if (windowTileInspectorTileSelected && mapX == windowTileInspectorToolMapX && mapY == windowTileInspectorToolMapY) + if (windowTileInspectorTileSelected && mapX == windowTileInspectorToolMap.x && mapY == windowTileInspectorToolMap.y) { return; } } windowTileInspectorTileSelected = true; - windowTileInspectorToolMapX = mapX; - windowTileInspectorToolMapY = mapY; + windowTileInspectorToolMap.x = mapX; + windowTileInspectorToolMap.y = mapY; windowTileInspectorTileX = mapX >> 5; windowTileInspectorTileY = mapY >> 5; @@ -1302,7 +1323,7 @@ static void window_tile_inspector_scrollgetsize(rct_window* w, int32_t scrollInd static void window_tile_inspector_set_page(rct_window* w, const TILE_INSPECTOR_PAGE page) { // Invalidate the window already, because the size may change - window_invalidate(w); + w->Invalidate(); // subtract current page height, then add new page height if (w->page != TILE_INSPECTOR_PAGE_DEFAULT) @@ -1385,30 +1406,32 @@ static void window_tile_inspector_invalidate(rct_window* w) if (w->page != page) { window_tile_inspector_set_page(w, page); - window_invalidate(w); + w->Invalidate(); } // X and Y spinners widget_set_enabled( w, WIDX_SPINNER_X_INCREASE, - (windowTileInspectorTileSelected && (windowTileInspectorTileX < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); - widget_set_enabled(w, WIDX_SPINNER_X_DECREASE, (windowTileInspectorTileSelected && (windowTileInspectorTileX > 0))); + (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.x / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); + widget_set_enabled( + w, WIDX_SPINNER_X_DECREASE, (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.x / 32) > 0))); widget_set_enabled( w, WIDX_SPINNER_Y_INCREASE, - (windowTileInspectorTileSelected && (windowTileInspectorTileY < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); - widget_set_enabled(w, WIDX_SPINNER_Y_DECREASE, (windowTileInspectorTileSelected && (windowTileInspectorTileY > 0))); + (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.y / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); + widget_set_enabled( + w, WIDX_SPINNER_Y_DECREASE, (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.y / 32) > 0))); // Sort buttons widget_set_enabled(w, WIDX_BUTTON_SORT, (windowTileInspectorTileSelected && windowTileInspectorElementCount > 1)); // Move Up button - widget_set_enabled(w, WIDX_BUTTON_MOVE_UP, (windowTileInspectorSelectedIndex > 0)); + widget_set_enabled( + w, WIDX_BUTTON_MOVE_UP, + (windowTileInspectorSelectedIndex != -1 && windowTileInspectorSelectedIndex < windowTileInspectorElementCount - 1)); widget_invalidate(w, WIDX_BUTTON_MOVE_UP); // Move Down button - widget_set_enabled( - w, WIDX_BUTTON_MOVE_DOWN, - (windowTileInspectorSelectedIndex != -1 && windowTileInspectorSelectedIndex < windowTileInspectorElementCount - 1)); + widget_set_enabled(w, WIDX_BUTTON_MOVE_DOWN, (windowTileInspectorSelectedIndex > 0)); widget_invalidate(w, WIDX_BUTTON_MOVE_DOWN); // Copy button @@ -1495,25 +1518,28 @@ static void window_tile_inspector_invalidate(rct_window* w) w->widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; w->widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; w->widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_PATH_CHECK_SLOPED].top = GBBT(propertiesAnchor, 1) + 2; - w->widgets[WIDX_PATH_CHECK_SLOPED].bottom = GBBT(propertiesAnchor, 1) + 15; - w->widgets[WIDX_PATH_CHECK_EDGE_N].top = GBBT(propertiesAnchor, 2) + 7 * 0; + w->widgets[WIDX_PATH_CHECK_BROKEN].top = GBBT(propertiesAnchor, 1); + w->widgets[WIDX_PATH_CHECK_BROKEN].bottom = GBBB(propertiesAnchor, 1); + w->widgets[WIDX_PATH_CHECK_SLOPED].top = GBBT(propertiesAnchor, 2); + w->widgets[WIDX_PATH_CHECK_SLOPED].bottom = GBBB(propertiesAnchor, 2); + w->widgets[WIDX_PATH_CHECK_EDGE_N].top = GBBT(propertiesAnchor, 3) + 7 * 0; w->widgets[WIDX_PATH_CHECK_EDGE_N].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_N].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_NE].top = GBBT(propertiesAnchor, 2) + 7 * 1; + w->widgets[WIDX_PATH_CHECK_EDGE_NE].top = GBBT(propertiesAnchor, 3) + 7 * 1; w->widgets[WIDX_PATH_CHECK_EDGE_NE].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_NE].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_E].top = GBBT(propertiesAnchor, 2) + 7 * 2; + w->widgets[WIDX_PATH_CHECK_EDGE_E].top = GBBT(propertiesAnchor, 3) + 7 * 2; w->widgets[WIDX_PATH_CHECK_EDGE_E].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_E].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_SE].top = GBBT(propertiesAnchor, 2) + 7 * 3; + w->widgets[WIDX_PATH_CHECK_EDGE_SE].top = GBBT(propertiesAnchor, 3) + 7 * 3; w->widgets[WIDX_PATH_CHECK_EDGE_SE].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_SE].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_S].top = GBBT(propertiesAnchor, 2) + 7 * 4; + w->widgets[WIDX_PATH_CHECK_EDGE_S].top = GBBT(propertiesAnchor, 3) + 7 * 4; w->widgets[WIDX_PATH_CHECK_EDGE_S].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_S].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_SW].top = GBBT(propertiesAnchor, 2) + 7 * 3; + w->widgets[WIDX_PATH_CHECK_EDGE_SW].top = GBBT(propertiesAnchor, 3) + 7 * 3; w->widgets[WIDX_PATH_CHECK_EDGE_SW].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_SW].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_W].top = GBBT(propertiesAnchor, 2) + 7 * 2; + w->widgets[WIDX_PATH_CHECK_EDGE_W].top = GBBT(propertiesAnchor, 3) + 7 * 2; w->widgets[WIDX_PATH_CHECK_EDGE_W].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_W].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_NW].top = GBBT(propertiesAnchor, 2) + 7 * 1; + w->widgets[WIDX_PATH_CHECK_EDGE_NW].top = GBBT(propertiesAnchor, 3) + 7 * 1; w->widgets[WIDX_PATH_CHECK_EDGE_NW].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_NW].top + 13; widget_set_checkbox_value(w, WIDX_PATH_CHECK_SLOPED, tileElement->AsPath()->IsSloped()); + widget_set_checkbox_value(w, WIDX_PATH_CHECK_BROKEN, tileElement->AsPath()->IsBroken()); widget_set_checkbox_value( w, WIDX_PATH_CHECK_EDGE_NE, tileElement->AsPath()->GetEdges() & (1 << ((0 - get_current_rotation()) & 3))); widget_set_checkbox_value( @@ -1542,8 +1568,14 @@ static void window_tile_inspector_invalidate(rct_window* w) w->widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 1) - 4; w->widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].top = GBBT(propertiesAnchor, 2); w->widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].bottom = GBBB(propertiesAnchor, 2); + w->widgets[WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED].top = GBBT(propertiesAnchor, 3); + w->widgets[WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED].bottom = GBBB(propertiesAnchor, 3); + w->widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].top = GBBT(propertiesAnchor, 4); + w->widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].bottom = GBBB(propertiesAnchor, 4); widget_set_checkbox_value(w, WIDX_TRACK_CHECK_APPLY_TO_ALL, windowTileInspectorApplyToAll); widget_set_checkbox_value(w, WIDX_TRACK_CHECK_CHAIN_LIFT, tileElement->AsTrack()->HasChain()); + widget_set_checkbox_value(w, WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED, tileElement->AsTrack()->BlockBrakeClosed()); + widget_set_checkbox_value(w, WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE, tileElement->AsTrack()->IsIndestructible()); break; case TILE_INSPECTOR_PAGE_SCENERY: { @@ -1583,10 +1615,11 @@ static void window_tile_inspector_invalidate(rct_window* w) w->widgets[WIDX_SCENERY_CHECK_COLLISION_S].bottom = w->widgets[WIDX_SCENERY_CHECK_COLLISION_S].top + 13; w->widgets[WIDX_SCENERY_CHECK_COLLISION_W].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1; w->widgets[WIDX_SCENERY_CHECK_COLLISION_W].bottom = w->widgets[WIDX_SCENERY_CHECK_COLLISION_W].top + 13; - N = (tileElement->flags & (1 << ((2 - get_current_rotation()) & 3))) != 0; - E = (tileElement->flags & (1 << ((3 - get_current_rotation()) & 3))) != 0; - S = (tileElement->flags & (1 << ((0 - get_current_rotation()) & 3))) != 0; - W = (tileElement->flags & (1 << ((1 - get_current_rotation()) & 3))) != 0; + auto occupiedQuadrants = tileElement->GetOccupiedQuadrants(); + N = (occupiedQuadrants & (1 << ((2 - get_current_rotation()) & 3))) != 0; + E = (occupiedQuadrants & (1 << ((3 - get_current_rotation()) & 3))) != 0; + S = (occupiedQuadrants & (1 << ((0 - get_current_rotation()) & 3))) != 0; + W = (occupiedQuadrants & (1 << ((1 - get_current_rotation()) & 3))) != 0; widget_set_checkbox_value(w, WIDX_SCENERY_CHECK_COLLISION_N, N); widget_set_checkbox_value(w, WIDX_SCENERY_CHECK_COLLISION_E, E); widget_set_checkbox_value(w, WIDX_SCENERY_CHECK_COLLISION_S, S); @@ -1711,12 +1744,6 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) dpi, STR_TILE_INSPECTOR_FLAG_GHOST_SHORT, gCommonFormatArgs, w->colours[1], w->x + widget->left + 1, w->y + widget->top + 1, widget->right - widget->left); } - if ((widget = &w->widgets[WIDX_COLUMN_BROKENFLAG])->type != WWT_EMPTY) - { - gfx_draw_string_left_clipped( - dpi, STR_TILE_INSPECTOR_FLAG_BROKEN_SHORT, gCommonFormatArgs, w->colours[1], w->x + widget->left + 1, - w->y + widget->top + 1, widget->right - widget->left); - } if ((widget = &w->widgets[WIDX_COLUMN_LASTFLAG])->type != WWT_EMPTY) { gfx_draw_string_left_clipped( @@ -1725,17 +1752,18 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) } // Draw coordinates - gfx_draw_string(dpi, (char*)"X:", COLOUR_DARK_GREEN, w->x + 5, w->y + 24); - gfx_draw_string(dpi, (char*)"Y:", COLOUR_DARK_GREEN, w->x + 74, w->y + 24); + gfx_draw_string(dpi, (char*)"X:", COLOUR_WHITE, w->x + 5, w->y + 24); + gfx_draw_string(dpi, (char*)"Y:", COLOUR_WHITE, w->x + 74, w->y + 24); if (windowTileInspectorTileSelected) { - gfx_draw_string_right(dpi, STR_FORMAT_INTEGER, &windowTileInspectorTileX, COLOUR_DARK_GREEN, w->x + 43, w->y + 24); - gfx_draw_string_right(dpi, STR_FORMAT_INTEGER, &windowTileInspectorTileY, COLOUR_DARK_GREEN, w->x + 113, w->y + 24); + auto tileCoords = TileCoordsXY{ windowTileInspectorToolMap }; + gfx_draw_string_right(dpi, STR_FORMAT_INTEGER, &tileCoords.x, COLOUR_WHITE, w->x + 43, w->y + 24); + gfx_draw_string_right(dpi, STR_FORMAT_INTEGER, &tileCoords.y, COLOUR_WHITE, w->x + 113, w->y + 24); } else { - gfx_draw_string(dpi, (char*)"-", COLOUR_DARK_GREEN, w->x + 43 - 7, w->y + 24); - gfx_draw_string(dpi, (char*)"-", COLOUR_DARK_GREEN, w->x + 113 - 7, w->y + 24); + gfx_draw_string(dpi, (char*)"-", COLOUR_WHITE, w->x + 43 - 7, w->y + 24); + gfx_draw_string(dpi, (char*)"-", COLOUR_WHITE, w->x + 113 - 7, w->y + 24); } if (windowTileInspectorSelectedIndex != -1) @@ -1754,7 +1782,7 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) // Details // Terrain texture name rct_string_id terrainNameId = TerrainTypeStringIds[tileElement->AsSurface()->GetSurfaceStyle()]; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SURFACE_TERAIN, &terrainNameId, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SURFACE_TERAIN, &terrainNameId, COLOUR_WHITE, x, y); // Edge texture name uint32_t idx = tileElement->AsSurface()->GetEdgeStyle(); @@ -1762,7 +1790,7 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) (uint32_t)idx < std::size(TerrainEdgeTypeStringIds), "Tried accessing invalid entry %d in terrainEdgeTypeStringIds", idx); rct_string_id terrainEdgeNameId = TerrainEdgeTypeStringIds[tileElement->AsSurface()->GetEdgeStyle()]; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SURFACE_EDGE, &terrainEdgeNameId, COLOUR_DARK_GREEN, x, y + 11); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SURFACE_EDGE, &terrainEdgeNameId, COLOUR_WHITE, x, y + 11); // Land ownership rct_string_id landOwnership; @@ -1776,27 +1804,27 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) landOwnership = STR_CONSTRUCTION_RIGHTS_SALE; else landOwnership = STR_TILE_INSPECTOR_LAND_NOT_OWNED_AND_NOT_AVAILABLE; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SURFACE_OWNERSHIP, &landOwnership, COLOUR_DARK_GREEN, x, y + 22); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SURFACE_OWNERSHIP, &landOwnership, COLOUR_WHITE, x, y + 22); // Water level uint32_t waterLevel = tileElement->AsSurface()->GetWaterHeight(); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SURFACE_WATER_LEVEL, &waterLevel, COLOUR_DARK_GREEN, x, y + 33); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SURFACE_WATER_LEVEL, &waterLevel, COLOUR_WHITE, x, y + 33); // Properties // Raise / lower label x = w->x + w->widgets[WIDX_GROUPBOX_DETAILS].left + 7; y = w->y + w->widgets[WIDX_SURFACE_SPINNER_HEIGHT].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_WHITE, x, y); // Current base height x = w->x + w->widgets[WIDX_SURFACE_SPINNER_HEIGHT].left + 3; int32_t baseHeight = tileElement->base_height; - gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_WHITE, x, y); // Raised corners x = w->x + w->widgets[WIDX_GROUPBOX_DETAILS].left + 7; y = w->y + w->widgets[WIDX_SURFACE_CHECK_CORNER_E].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SURFACE_CORNERS, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SURFACE_CORNERS, nullptr, COLOUR_WHITE, x, y); break; } @@ -1805,7 +1833,7 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) // Details // Path name rct_string_id pathNameId = tileElement->AsPath()->GetPathEntry()->string_idx; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_PATH_NAME, &pathNameId, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_PATH_NAME, &pathNameId, COLOUR_WHITE, x, y); // Path addition if (tileElement->AsPath()->HasAddition()) @@ -1814,26 +1842,26 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) const auto* sceneryElement = get_footpath_item_entry(pathAdditionType); rct_string_id additionNameId = sceneryElement != nullptr ? sceneryElement->name : (rct_string_id)STR_UNKNOWN_OBJECT_TYPE; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_PATH_ADDITIONS, &additionNameId, COLOUR_DARK_GREEN, x, y + 11); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_PATH_ADDITIONS, &additionNameId, COLOUR_WHITE, x, y + 11); } else - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_PATH_ADDITIONS_NONE, nullptr, COLOUR_DARK_GREEN, x, y + 11); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_PATH_ADDITIONS_NONE, nullptr, COLOUR_WHITE, x, y + 11); // Properties // Raise / lower label x = w->x + w->widgets[WIDX_GROUPBOX_DETAILS].left + 7; y = w->y + w->widgets[WIDX_PATH_SPINNER_HEIGHT].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_WHITE, x, y); // Current base height x = w->x + w->widgets[WIDX_PATH_SPINNER_HEIGHT].left + 3; int32_t baseHeight = tileElement->base_height; - gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_WHITE, x, y); // Path connections x = w->x + w->widgets[WIDX_GROUPBOX_DETAILS].left + 7; y = w->y + w->widgets[WIDX_PATH_CHECK_EDGE_W].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_PATH_CONNECTED_EDGES, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_PATH_CONNECTED_EDGES, nullptr, COLOUR_WHITE, x, y); break; } @@ -1843,33 +1871,50 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) // Ride auto trackElement = tileElement->AsTrack(); int16_t rideId = trackElement->GetRideIndex(); - Ride* ride = get_ride(rideId); - rct_string_id rideType = RideNaming[ride->type].name; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_TRACK_RIDE_TYPE, &rideType, COLOUR_DARK_GREEN, x, y); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_TRACK_RIDE_ID, &rideId, COLOUR_DARK_GREEN, x, y + 11); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(0 + sizeof(rct_string_id), uint32_t, ride->name_arguments); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_TRACK_RIDE_NAME, gCommonFormatArgs, COLOUR_DARK_GREEN, x, y + 22); + auto ride = get_ride(rideId); + if (ride != nullptr) + { + auto rideType = RideNaming[ride->type].name; + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_TRACK_RIDE_TYPE, &rideType, COLOUR_WHITE, x, y); + } + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_TRACK_RIDE_ID, &rideId, COLOUR_WHITE, x, y + 11); + if (ride != nullptr) + { + ride->FormatNameTo(gCommonFormatArgs); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_TRACK_RIDE_NAME, gCommonFormatArgs, COLOUR_WHITE, x, y + 22); + } // Track int16_t trackType = trackElement->GetTrackType(); int16_t sequenceNumber = trackElement->GetSequenceIndex(); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_TRACK_PIECE_ID, &trackType, COLOUR_DARK_GREEN, x, y + 33); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_TRACK_SEQUENCE, &sequenceNumber, COLOUR_DARK_GREEN, x, y + 44); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_TRACK_PIECE_ID, &trackType, COLOUR_WHITE, x, y + 33); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_TRACK_SEQUENCE, &sequenceNumber, COLOUR_WHITE, x, y + 44); if (track_element_is_station(tileElement)) { int16_t stationIndex = trackElement->GetStationIndex(); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_STATION_INDEX, &stationIndex, COLOUR_DARK_GREEN, x, y + 55); + set_format_arg(0, rct_string_id, STR_COMMA16); + set_format_arg(2, int16_t, stationIndex); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_STATION_INDEX, gCommonFormatArgs, COLOUR_WHITE, x, y + 55); } + else + { + const char* stationNone = "-"; + set_format_arg(0, rct_string_id, STR_STRING); + set_format_arg(2, char*, stationNone); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_STATION_INDEX, gCommonFormatArgs, COLOUR_WHITE, x, y + 55); + } + + rct_string_id colourScheme = ColourSchemeNames[trackElement->GetColourScheme()]; + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_COLOUR_SCHEME, &colourScheme, COLOUR_WHITE, x, y + 66); // Properties // Raise / lower label y = w->y + w->widgets[WIDX_TRACK_SPINNER_HEIGHT].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_WHITE, x, y); // Current base height x = w->x + w->widgets[WIDX_TRACK_SPINNER_HEIGHT].left + 3; int32_t baseHeight = tileElement->base_height; - gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_WHITE, x, y); break; } @@ -1878,7 +1923,7 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) // Details // Age int16_t age = tileElement->AsSmallScenery()->GetAge(); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SCENERY_AGE, &age, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SCENERY_AGE, &age, COLOUR_WHITE, x, y); // Quadrant value const rct_scenery_entry* sceneryEntry = get_small_scenery_entry(tileElement->AsSmallScenery()->GetEntryIndex()); @@ -1890,31 +1935,31 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) STR_TILE_INSPECTOR_SCENERY_QUADRANT_NE, STR_TILE_INSPECTOR_SCENERY_QUADRANT_SE }; gfx_draw_string_left( - dpi, STR_TILE_INSPECTOR_SCENERY_QUADRANT, &quadrant_string_idx[quadrant], COLOUR_DARK_GREEN, x, y + 11); + dpi, STR_TILE_INSPECTOR_SCENERY_QUADRANT, &quadrant_string_idx[quadrant], COLOUR_WHITE, x, y + 11); } // Scenery ID int16_t idx = tileElement->AsSmallScenery()->GetEntryIndex(); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SCENERY_ENTRY_IDX, &idx, COLOUR_DARK_GREEN, x, y + 22); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SCENERY_ENTRY_IDX, &idx, COLOUR_WHITE, x, y + 22); // Properties // Raise / Lower y = w->y + w->widgets[WIDX_SCENERY_SPINNER_HEIGHT].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_WHITE, x, y); // Current base height x = w->x + w->widgets[WIDX_SCENERY_SPINNER_HEIGHT].left + 3; int32_t baseHeight = tileElement->base_height; - gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_WHITE, x, y); // Quarter tile x = w->x + w->widgets[WIDX_GROUPBOX_DETAILS].left + 7; y = w->y + w->widgets[WIDX_SCENERY_CHECK_QUARTER_E].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SCENERY_QUADRANT_LABEL, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_SCENERY_QUADRANT_LABEL, nullptr, COLOUR_WHITE, x, y); // Collision y = w->y + w->widgets[WIDX_SCENERY_CHECK_COLLISION_E].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_COLLISSION, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_COLLISSION, nullptr, COLOUR_WHITE, x, y); break; } @@ -1923,17 +1968,15 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) // Details // Entrance type rct_string_id entranceType = EntranceTypeStringIds[tileElement->AsEntrance()->GetEntranceType()]; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRANCE_TYPE, &entranceType, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRANCE_TYPE, &entranceType, COLOUR_WHITE, x, y); if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) { - // Park entrance ID - int32_t middleX = windowTileInspectorTileX << 5; - int32_t middleY = windowTileInspectorTileY << 5; // TODO: Make this work with Left/Right park entrance parts - int16_t parkEntranceIndex = park_entrance_get_index(middleX, middleY, tileElement->base_height * 8); + int16_t parkEntranceIndex = park_entrance_get_index( + windowTileInspectorToolMap.x, windowTileInspectorToolMap.y, tileElement->base_height * 8); gfx_draw_string_left( - dpi, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, &parkEntranceIndex, COLOUR_DARK_GREEN, x, y + 11); + dpi, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, &parkEntranceIndex, COLOUR_WHITE, x, y + 11); } else { @@ -1942,13 +1985,13 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) { // Ride entrance ID gfx_draw_string_left( - dpi, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, &rideEntranceIndex, COLOUR_DARK_GREEN, x, y + 11); + dpi, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, &rideEntranceIndex, COLOUR_WHITE, x, y + 11); } else { // Ride exit ID gfx_draw_string_left( - dpi, STR_TILE_INSPECTOR_ENTRANCE_EXIT_ID, &rideEntranceIndex, COLOUR_DARK_GREEN, x, y + 11); + dpi, STR_TILE_INSPECTOR_ENTRANCE_EXIT_ID, &rideEntranceIndex, COLOUR_WHITE, x, y + 11); } } @@ -1956,27 +1999,27 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) { // Entrance part rct_string_id entrancePart = ParkEntrancePartStringIds[tileElement->AsEntrance()->GetSequenceIndex()]; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRANCE_PART, &entrancePart, COLOUR_DARK_GREEN, x, y + 22); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRANCE_PART, &entrancePart, COLOUR_WHITE, x, y + 22); } else { // Ride ID int16_t rideId = tileElement->AsEntrance()->GetRideIndex(); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRANCE_RIDE_ID, &rideId, COLOUR_DARK_GREEN, x, y + 22); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRANCE_RIDE_ID, &rideId, COLOUR_WHITE, x, y + 22); // Station index int16_t stationIndex = tileElement->AsEntrance()->GetStationIndex(); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_STATION_INDEX, &stationIndex, COLOUR_DARK_GREEN, x, y + 33); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_STATION_INDEX, &stationIndex, COLOUR_WHITE, x, y + 33); } // Properties // Raise / Lower y = w->y + w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_WHITE, x, y); // Current base height x = w->x + w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].left + 3; int32_t baseHeight = tileElement->base_height; - gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_WHITE, x, y); break; } @@ -1985,35 +2028,39 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) // Details // Type int16_t wallType = tileElement->AsWall()->GetEntryIndex(); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_WALL_TYPE, &wallType, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_WALL_TYPE, &wallType, COLOUR_WHITE, x, y); // Banner info rct_wall_scenery_entry wallEntry = get_wall_entry(wallType)->wall; if (wallEntry.flags & WALL_SCENERY_IS_BANNER) { - gfx_draw_string_left( - dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, - &gBanners[tileElement->AsWall()->GetBannerIndex()].string_idx, COLOUR_DARK_GREEN, x, y + 11); + auto banner = tileElement->AsWall()->GetBanner(); + if (banner != nullptr && !banner->IsNull()) + { + uint8_t args[32]{}; + banner->FormatTextTo(args); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, args, COLOUR_WHITE, x, y + 11); + } } else { - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, nullptr, COLOUR_DARK_GREEN, x, y + 11); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, nullptr, COLOUR_WHITE, x, y + 11); } // Properties // Raise / lower label y = w->y + w->widgets[WIDX_WALL_SPINNER_HEIGHT].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_WHITE, x, y); // Current base height x = w->x + w->widgets[WIDX_WALL_SPINNER_HEIGHT].left + 3; int32_t baseHeight = tileElement->base_height; - gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_WHITE, x, y); // Slope label x = w->x + w->widgets[WIDX_GROUPBOX_DETAILS].left + 7; y = w->y + w->widgets[WIDX_WALL_DROPDOWN_SLOPE].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_WALL_SLOPE, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_WALL_SLOPE, nullptr, COLOUR_WHITE, x, y); break; } @@ -2023,34 +2070,38 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) // Type auto sceneryElement = tileElement->AsLargeScenery(); int16_t largeSceneryType = sceneryElement->GetEntryIndex(); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_LARGE_SCENERY_TYPE, &largeSceneryType, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_LARGE_SCENERY_TYPE, &largeSceneryType, COLOUR_WHITE, x, y); // Part ID int16_t pieceID = sceneryElement->GetSequenceIndex(); - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_LARGE_SCENERY_PIECE_ID, &pieceID, COLOUR_DARK_GREEN, x, y + 11); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_LARGE_SCENERY_PIECE_ID, &pieceID, COLOUR_WHITE, x, y + 11); // Banner info rct_scenery_entry* largeSceneryEntry = get_large_scenery_entry(largeSceneryType); if (largeSceneryEntry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE) { - const BannerIndex bannerIndex = sceneryElement->GetBannerIndex(); - rct_string_id* string = &gBanners[bannerIndex].string_idx; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, string, COLOUR_DARK_GREEN, x, y + 22); + auto banner = sceneryElement->GetBanner(); + if (banner != nullptr && !banner->IsNull()) + { + uint8_t args[32]{}; + banner->FormatTextTo(args); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, args, COLOUR_WHITE, x, y + 22); + } } else { - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, nullptr, COLOUR_DARK_GREEN, x, y + 22); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, nullptr, COLOUR_WHITE, x, y + 22); } // Properties // Raise / lower label y = w->y + w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_WHITE, x, y); // Current base height x = w->x + w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].left + 3; int32_t baseHeight = tileElement->base_height; - gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_WHITE, x, y); break; } @@ -2058,32 +2109,28 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) { // Details // Banner info - const uint8_t bannerIndex = tileElement->AsBanner()->GetIndex(); - if (gBanners[bannerIndex].flags & BANNER_FLAG_NO_ENTRY) + auto banner = tileElement->AsBanner()->GetBanner(); + if (banner != nullptr && !banner->IsNull()) { - rct_string_id noEntryStringIdx = STR_NO_ENTRY; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, &noEntryStringIdx, COLOUR_DARK_GREEN, x, y); - } - else - { - gfx_draw_string_left( - dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, &gBanners[bannerIndex].string_idx, COLOUR_DARK_GREEN, x, y); + uint8_t args[32]{}; + banner->FormatTextTo(args); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, args, COLOUR_WHITE, x, y + 22); } // Properties // Raise / lower label y = w->y + w->widgets[WIDX_BANNER_SPINNER_HEIGHT].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_WHITE, x, y); // Current base height x = w->x + w->widgets[WIDX_BANNER_SPINNER_HEIGHT].left + 3; int32_t baseHeight = tileElement->base_height; - gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_WHITE, x, y); // Blocked paths y += 28; x = w->x + w->widgets[WIDX_GROUPBOX_DETAILS].left + 7; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BANNER_BLOCKED_PATHS, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BANNER_BLOCKED_PATHS, nullptr, COLOUR_WHITE, x, y); break; } @@ -2092,12 +2139,12 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) // Properties // Raise / lower label y = w->y + w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT].top; - gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, nullptr, COLOUR_WHITE, x, y); // Current base height x = w->x + w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT].left + 3; int32_t baseHeight = tileElement->base_height; - gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_DARK_GREEN, x, y); + gfx_draw_string_left(dpi, STR_FORMAT_INTEGER, &baseHeight, COLOUR_WHITE, x, y); break; } } // switch page @@ -2116,11 +2163,14 @@ static void window_tile_inspector_scrollpaint(rct_window* w, rct_drawpixelinfo* if (!windowTileInspectorTileSelected) return; - const TileElement* tileElement = map_get_first_element_at(windowTileInspectorTileX, windowTileInspectorTileY); + const TileElement* tileElement = map_get_first_element_at( + windowTileInspectorToolMap.x / 32, windowTileInspectorToolMap.y / 32); gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM; do { + if (tileElement == nullptr) + break; const bool selectedRow = i == windowTileInspectorSelectedIndex; const bool hoveredRow = i == windowTileInspectorHighlightedIndex; int32_t type = tileElement->GetType(); @@ -2186,7 +2236,6 @@ static void window_tile_inspector_scrollpaint(rct_window* w, rct_drawpixelinfo* const int32_t baseHeight = tileElement->base_height; const int32_t clearanceHeight = tileElement->clearance_height; const bool ghost = tileElement->IsGhost(); - const bool broken = tileElement->AsPath() == nullptr ? false : tileElement->AsPath()->IsBroken(); const bool last = tileElement->IsLastForTile(); const rct_string_id stringFormat = (selectedRow || hoveredRow) ? STR_WHITE_STRING : STR_WINDOW_COLOUR_2_STRINGID; @@ -2208,17 +2257,13 @@ static void window_tile_inspector_scrollpaint(rct_window* w, rct_drawpixelinfo* set_format_arg(2, int32_t, clearanceHeight); gfx_draw_string_left(dpi, stringFormat, gCommonFormatArgs, COLOUR_BLACK, x + COL_X_CH, y); - // Checkmarks for ghost, broken en last for tile + // Checkmarks for ghost and last for tile set_format_arg(0, rct_string_id, STR_STRING); set_format_arg(2, char*, CheckBoxMarkString); if (ghost) { gfx_draw_string_left(dpi, stringFormat, gCommonFormatArgs, COLOUR_BLACK, x + COL_X_GF, y); } - if (broken) - { - gfx_draw_string_left(dpi, stringFormat, gCommonFormatArgs, COLOUR_BLACK, x + COL_X_BF, y); - } if (last) { gfx_draw_string_left(dpi, stringFormat, gCommonFormatArgs, COLOUR_BLACK, x + COL_X_LF, y); diff --git a/src/openrct2-ui/windows/TitleCommandEditor.cpp b/src/openrct2-ui/windows/TitleCommandEditor.cpp index 4acf7ec8d3..0382dd747d 100644 --- a/src/openrct2-ui/windows/TitleCommandEditor.cpp +++ b/src/openrct2-ui/windows/TitleCommandEditor.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -336,7 +336,7 @@ static void window_title_command_editor_mouseup(rct_window* w, rct_widgetindex w command.Zoom = zoom; snprintf(textbox1Buffer, BUF_SIZE, "%d", command.Zoom); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_SELECT_SCENARIO: window_scenarioselect_open(scenario_select_callback, true); @@ -501,7 +501,7 @@ static void window_title_command_editor_dropdown(rct_window* w, rct_widgetindex case TITLE_SCRIPT_LOADSC: command.Scenario[0] = '\0'; } - window_invalidate(w); + w->Invalidate(); break; case WIDX_INPUT_DROPDOWN: switch (command.Type) @@ -510,14 +510,14 @@ static void window_title_command_editor_dropdown(rct_window* w, rct_widgetindex if (dropdownIndex != command.Speed - 1) { command.Speed = (uint8_t)(dropdownIndex + 1); - window_invalidate(w); + w->Invalidate(); } break; case TITLE_SCRIPT_LOAD: if (dropdownIndex != command.SaveIndex) { command.SaveIndex = (uint8_t)dropdownIndex; - window_invalidate(w); + w->Invalidate(); } break; } @@ -561,7 +561,7 @@ static void window_title_command_editor_textinput(rct_window* w, rct_widgetindex snprintf(textbox1Buffer, BUF_SIZE, "%d", command.Rotations); } } - window_invalidate(w); + w->Invalidate(); } else { @@ -576,7 +576,7 @@ static void window_title_command_editor_textinput(rct_window* w, rct_widgetindex command.X = (uint8_t)value; } snprintf(textbox1Buffer, BUF_SIZE, "%d", command.X); - window_invalidate(w); + w->Invalidate(); } else { @@ -591,7 +591,7 @@ static void window_title_command_editor_textinput(rct_window* w, rct_widgetindex command.Y = (uint8_t)value; } snprintf(textbox2Buffer, BUF_SIZE, "%d", command.Y); - window_invalidate(w); + w->Invalidate(); } else { @@ -623,16 +623,28 @@ static void window_title_command_editor_tool_down(rct_window* w, rct_widgetindex if (spriteIdentifier == SPRITE_IDENTIFIER_PEEP) { validSprite = true; - Peep* peep = GET_PEEP(spriteIndex); - format_string(command.SpriteName, USER_STRING_MAX_LENGTH, peep->name_string_idx, &peep->id); + auto peep = GET_PEEP(spriteIndex); + if (peep != nullptr) + { + uint8_t formatArgs[32]{}; + peep->FormatNameTo(formatArgs); + format_string(command.SpriteName, USER_STRING_MAX_LENGTH, STR_STRINGID, &peep->id); + } } else if (spriteIdentifier == SPRITE_IDENTIFIER_VEHICLE) { validSprite = true; - rct_vehicle* vehicle = GET_VEHICLE(spriteIndex); - Ride* ride = get_ride(vehicle->ride); - set_format_arg(16, uint32_t, ride->name_arguments); - format_string(command.SpriteName, USER_STRING_MAX_LENGTH, ride->name, &ride->name_arguments); + auto vehicle = GET_VEHICLE(spriteIndex); + if (vehicle != nullptr) + { + auto ride = get_ride(vehicle->ride); + if (ride != nullptr) + { + uint8_t formatArgs[32]{}; + ride->FormatNameTo(formatArgs); + format_string(command.SpriteName, USER_STRING_MAX_LENGTH, STR_STRINGID, formatArgs); + } + } } else if (spriteIdentifier == SPRITE_IDENTIFIER_LITTER) { @@ -661,7 +673,7 @@ static void window_title_command_editor_tool_down(rct_window* w, rct_widgetindex command.SpriteIndex = spriteIndex; window_follow_sprite(w, (size_t)command.SpriteIndex); tool_cancel(); - window_invalidate(w); + w->Invalidate(); } } } diff --git a/src/openrct2-ui/windows/TitleEditor.cpp b/src/openrct2-ui/windows/TitleEditor.cpp index 022d605fae..0baa7a3550 100644 --- a/src/openrct2-ui/windows/TitleEditor.cpp +++ b/src/openrct2-ui/windows/TitleEditor.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -535,7 +535,7 @@ static void window_title_editor_mousedown(rct_window* w, rct_widgetindex widgetI w->scrolls[0].v_top = 0; w->frame_no = 0; window_event_resize_call(w); - window_invalidate(w); + w->Invalidate(); } break; } @@ -571,7 +571,7 @@ static void window_title_editor_dropdown(rct_window* w, rct_widgetindex widgetIn if (widgetIndex == WIDX_TITLE_EDITOR_PRESETS_DROPDOWN) { window_title_editor_load_sequence(dropdownIndex); - window_invalidate(w); + w->Invalidate(); } } @@ -610,7 +610,7 @@ static void window_title_editor_scrollgetsize(rct_window* w, int32_t scrollIndex if (i < w->scrolls[0].v_top) { w->scrolls[0].v_top = i; - window_invalidate(w); + w->Invalidate(); } *width = SCROLL_WIDTH; @@ -688,7 +688,7 @@ static void window_title_editor_textinput(rct_window* w, rct_widgetindex widgetI window_title_editor_load_sequence(newIndex); } config_save_default(); - window_invalidate(w); + w->Invalidate(); } else { @@ -1001,7 +1001,7 @@ static void window_title_editor_scrollpaint_commands(rct_window* w, rct_drawpixe commandName = STR_TITLE_EDITOR_COMMAND_LOAD_FILE; const char* name = ""; source_desc desc; - if (scenario_get_source_desc_by_id(command->SaveIndex, &desc)) + if (ScenarioSources::TryGetById(command->SaveIndex, &desc)) { name = desc.title; } diff --git a/src/openrct2-ui/windows/TitleExit.cpp b/src/openrct2-ui/windows/TitleExit.cpp index a8da964a7f..46ae7585ce 100644 --- a/src/openrct2-ui/windows/TitleExit.cpp +++ b/src/openrct2-ui/windows/TitleExit.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -69,7 +69,7 @@ rct_window* window_title_exit_open() rct_window* window; window = window_create( - context_get_width() - 40, context_get_height() - 64, 40, 64, &window_title_exit_events, WC_TITLE_EXIT, + ScreenCoordsXY(context_get_width() - 40, context_get_height() - 64), 40, 64, &window_title_exit_events, WC_TITLE_EXIT, WF_STICK_TO_BACK | WF_TRANSPARENT); window->widgets = window_title_exit_widgets; window->enabled_widgets |= (1ULL << WIDX_EXIT); diff --git a/src/openrct2-ui/windows/TitleLogo.cpp b/src/openrct2-ui/windows/TitleLogo.cpp index d9cf99afa1..c2957c89f9 100644 --- a/src/openrct2-ui/windows/TitleLogo.cpp +++ b/src/openrct2-ui/windows/TitleLogo.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -60,7 +60,7 @@ static rct_window_event_list window_title_logo_events = { rct_window* window_title_logo_open() { rct_window* window = window_create( - 0, 0, 232, 136, &window_title_logo_events, WC_TITLE_LOGO, WF_STICK_TO_BACK | WF_TRANSPARENT); + ScreenCoordsXY(0, 0), 232, 136, &window_title_logo_events, WC_TITLE_LOGO, WF_STICK_TO_BACK | WF_TRANSPARENT); window->widgets = window_title_logo_widgets; window_init_scroll_widgets(window); window->colours[0] = TRANSLUCENT(COLOUR_GREY); diff --git a/src/openrct2-ui/windows/TitleMenu.cpp b/src/openrct2-ui/windows/TitleMenu.cpp index 54ed089a81..9a5bbc31dc 100644 --- a/src/openrct2-ui/windows/TitleMenu.cpp +++ b/src/openrct2-ui/windows/TitleMenu.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -85,7 +85,7 @@ rct_window* window_title_menu_open() rct_window* window; window = window_create( - 0, context_get_height() - 154, 0, 100, &window_title_menu_events, WC_TITLE_MENU, + ScreenCoordsXY(0, context_get_height() - 154), 0, 100, &window_title_menu_events, WC_TITLE_MENU, WF_STICK_TO_BACK | WF_TRANSPARENT | WF_NO_BACKGROUND); window->widgets = window_title_menu_widgets; window->enabled_widgets diff --git a/src/openrct2-ui/windows/TitleOptions.cpp b/src/openrct2-ui/windows/TitleOptions.cpp index dc4ec747fa..ce648ae169 100644 --- a/src/openrct2-ui/windows/TitleOptions.cpp +++ b/src/openrct2-ui/windows/TitleOptions.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -65,7 +65,8 @@ static rct_window_event_list window_title_options_events = { rct_window* window_title_options_open() { rct_window* window = window_create( - context_get_width() - 80, 0, 80, 15, &window_title_options_events, WC_TITLE_OPTIONS, WF_STICK_TO_BACK | WF_TRANSPARENT); + ScreenCoordsXY(context_get_width() - 80, 0), 80, 15, &window_title_options_events, WC_TITLE_OPTIONS, + WF_STICK_TO_BACK | WF_TRANSPARENT); window->widgets = window_title_options_widgets; window->enabled_widgets |= (1ULL << WIDX_OPTIONS); window_init_scroll_widgets(window); diff --git a/src/openrct2-ui/windows/TitleScenarioSelect.cpp b/src/openrct2-ui/windows/TitleScenarioSelect.cpp index 6be6a7113d..74d4162631 100644 --- a/src/openrct2-ui/windows/TitleScenarioSelect.cpp +++ b/src/openrct2-ui/windows/TitleScenarioSelect.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -226,10 +226,17 @@ static void window_scenarioselect_init_tabs(rct_window* w) } } - int32_t firstPage = bitscanforward(showPages); - if (firstPage != -1) + if (showPages & (1 << gConfigInterface.scenarioselect_last_tab)) { - w->selected_tab = firstPage; + w->selected_tab = gConfigInterface.scenarioselect_last_tab; + } + else + { + int32_t firstPage = bitscanforward(showPages); + if (firstPage != -1) + { + w->selected_tab = firstPage; + } } int32_t x = 3; @@ -269,12 +276,14 @@ static void window_scenarioselect_mousedown(rct_window* w, rct_widgetindex widge { w->selected_tab = widgetIndex - 4; w->highlighted_scenario = nullptr; + gConfigInterface.scenarioselect_last_tab = w->selected_tab; + config_save_default(); initialise_list_items(w); - window_invalidate(w); + w->Invalidate(); window_event_resize_call(w); window_event_invalidate_call(w); window_init_scroll_widgets(w); - window_invalidate(w); + w->Invalidate(); } } @@ -331,7 +340,7 @@ static void window_scenarioselect_scrollmousedown(rct_window* w, int32_t scrollI y -= scenarioItemHeight; if (y < 0 && !listItem.scenario.is_locked) { - audio_play_sound(SOUND_CLICK_1, 0, w->x + (w->width / 2)); + audio_play_sound(SoundId::Click1, 0, w->x + (w->width / 2)); gFirstTimeSaving = true; _callback(listItem.scenario.scenario->path); if (_titleEditor) @@ -390,11 +399,11 @@ static void window_scenarioselect_scrollmouseover(rct_window* w, int32_t scrollI if (w->highlighted_scenario != selected) { w->highlighted_scenario = selected; - window_invalidate(w); + w->Invalidate(); } else if (_showLockedInformation != originalShowLockedInformation) { - window_invalidate(w); + w->Invalidate(); } } diff --git a/src/openrct2-ui/windows/Tooltip.cpp b/src/openrct2-ui/windows/Tooltip.cpp index 876ee10024..4ef403917f 100644 --- a/src/openrct2-ui/windows/Tooltip.cpp +++ b/src/openrct2-ui/windows/Tooltip.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -63,17 +63,17 @@ static rct_window_event_list window_tooltip_events = { static utf8 _tooltipText[sizeof(gCommonStringFormatBuffer)]; static int16_t _tooltipNumLines; -void window_tooltip_reset(int32_t x, int32_t y) +void window_tooltip_reset(ScreenCoordsXY screenCoords) { - gTooltipCursorX = x; - gTooltipCursorY = y; + gTooltipCursorX = screenCoords.x; + gTooltipCursorY = screenCoords.y; gTooltipTimeout = 0; gTooltipWidget.window_classification = 255; input_set_state(INPUT_STATE_NORMAL); input_set_flag(INPUT_FLAG_4, false); } -void window_tooltip_show(rct_string_id id, int32_t x, int32_t y) +void window_tooltip_show(rct_string_id id, ScreenCoordsXY screenCoords) { rct_window* w; int32_t width, height; @@ -107,20 +107,20 @@ void window_tooltip_show(rct_string_id id, int32_t x, int32_t y) int32_t screenWidth = context_get_width(); int32_t screenHeight = context_get_height(); - x = std::clamp(x - (width / 2), 0, screenWidth - width); + screenCoords.x = std::clamp(screenCoords.x - (width / 2), 0, screenWidth - width); // TODO The cursor size will be relative to the window DPI. // The amount to offset the y should be adjusted. int32_t max_y = screenHeight - height; - y += 26; // Normally, we'd display the tooltip 26 lower - if (y > max_y) + screenCoords.y += 26; // Normally, we'd display the tooltip 26 lower + if (screenCoords.y > max_y) // If y is too large, the tooltip could be forced below the cursor if we'd just clamped y, // so we'll subtract a bit more - y -= height + 40; - y = std::clamp(y, 22, max_y); + screenCoords.y -= height + 40; + screenCoords.y = std::clamp(screenCoords.y, 22, max_y); - w = window_create(x, y, width, height, &window_tooltip_events, WC_TOOLTIP, WF_TRANSPARENT | WF_STICK_TO_FRONT); + w = window_create(screenCoords, width, height, &window_tooltip_events, WC_TOOLTIP, WF_TRANSPARENT | WF_STICK_TO_FRONT); w->widgets = window_tooltip_widgets; reset_tooltip_not_shown(); @@ -130,7 +130,7 @@ void window_tooltip_show(rct_string_id id, int32_t x, int32_t y) * * rct2: 0x006EA10D */ -void window_tooltip_open(rct_window* widgetWindow, rct_widgetindex widgetIndex, int32_t x, int32_t y) +void window_tooltip_open(rct_window* widgetWindow, rct_widgetindex widgetIndex, ScreenCoordsXY screenCords) { rct_widget* widget; @@ -149,7 +149,7 @@ void window_tooltip_open(rct_window* widgetWindow, rct_widgetindex widgetIndex, if (window_event_tooltip_call(widgetWindow, widgetIndex) == STR_NONE) return; - window_tooltip_show(widget->tooltip, x, y); + window_tooltip_show(widget->tooltip, screenCords); } /** diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index d28c609c2c..588dd0d004 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -25,18 +25,32 @@ #include #include #include +#include +#include #include #include +#include +#include +#include +#include +#include #include #include +#include #include +#include +#include +#include +#include +#include +#include #include #include #include #include #include +#include #include -#include #include #include #include @@ -132,11 +146,7 @@ enum TOP_TOOLBAR_VIEW_MENU_DDIDX { enum TOP_TOOLBAR_DEBUG_DDIDX { DDIDX_CONSOLE = 0, - DDIDX_TILE_INSPECTOR = 1, - DDIDX_OBJECT_SELECTION = 2, - DDIDX_INVENTIONS_LIST = 3, - DDIDX_SCENARIO_OPTIONS = 4, - DDIDX_DEBUG_PAINT = 5, + DDIDX_DEBUG_PAINT = 1, TOP_TOOLBAR_DEBUG_COUNT }; @@ -144,13 +154,23 @@ enum TOP_TOOLBAR_DEBUG_DDIDX { enum TOP_TOOLBAR_NETWORK_DDIDX { DDIDX_MULTIPLAYER = 0, DDIDX_NETWORK = 1, + DDIDX_MULTIPLAYER_RECONNECT = 2, + + TOP_TOOLBAR_NETWORK_COUNT }; enum { DDIDX_CHEATS, - DDIDX_ENABLE_SANDBOX_MODE = 2, - DDIDX_DISABLE_CLEARANCE_CHECKS, - DDIDX_DISABLE_SUPPORT_LIMITS + DDIDX_TILE_INSPECTOR = 1, + DDIDX_OBJECT_SELECTION = 2, + DDIDX_INVENTIONS_LIST = 3, + DDIDX_SCENARIO_OPTIONS = 4, + // 5 is a separator + DDIDX_ENABLE_SANDBOX_MODE = 6, + DDIDX_DISABLE_CLEARANCE_CHECKS = 7, + DDIDX_DISABLE_SUPPORT_LIMITS = 8, + + TOP_TOOLBAR_CHEATS_COUNT }; enum { @@ -233,7 +253,7 @@ static rct_widget window_top_toolbar_widgets[] = { { WWT_TRNBTN, 3, 0x001E, 0x003B, 0, TOP_TOOLBAR_HEIGHT, IMAGE_TYPE_REMAP | SPR_TAB_TOOLBAR, STR_SCENARIO_OPTIONS_FINANCIAL_TIP },// Finances { WWT_TRNBTN, 3, 0x001E, 0x003B, 0, TOP_TOOLBAR_HEIGHT, IMAGE_TYPE_REMAP | SPR_TAB_TOOLBAR, STR_FINANCES_RESEARCH_TIP }, // Research { WWT_TRNBTN, 3, 0x001E, 0x003B, 0, TOP_TOOLBAR_HEIGHT, IMAGE_TYPE_REMAP | SPR_TAB_TOOLBAR, STR_SHOW_RECENT_MESSAGES_TIP }, // News - { WWT_TRNBTN, 0, 0x001E, 0x003B, 0, TOP_TOOLBAR_HEIGHT, IMAGE_TYPE_REMAP | SPR_TAB_TOOLBAR, STR_SHOW_MULTIPLAYER_STATUS_TIP }, // Network + { WWT_TRNBTN, 0, 0x001E, 0x003B, 0, TOP_TOOLBAR_HEIGHT, IMAGE_TYPE_REMAP | SPR_G2_TOOLBAR_MULTIPLAYER, STR_SHOW_MULTIPLAYER_STATUS_TIP }, // Network { WWT_TRNBTN, 0, 0x001E, 0x003B, 0, TOP_TOOLBAR_HEIGHT, IMAGE_TYPE_REMAP | SPR_TAB_TOOLBAR, STR_TOOLBAR_CHAT_TIP }, // Chat { WWT_EMPTY, 0, 0, 10-1, 0, 0, 0xFFFFFFFF, STR_NONE }, // Artificial widget separator @@ -289,6 +309,8 @@ static void top_toolbar_init_fastforward_menu(rct_window* window, rct_widget* wi static void top_toolbar_fastforward_menu_dropdown(int16_t dropdownIndex); static void top_toolbar_init_rotate_menu(rct_window* window, rct_widget* widget); static void top_toolbar_rotate_menu_dropdown(int16_t dropdownIndex); +static void top_toolbar_init_cheats_menu(rct_window* window, rct_widget* widget); +static void top_toolbar_cheats_menu_dropdown(int16_t dropdownIndex); static void top_toolbar_init_debug_menu(rct_window* window, rct_widget* widget); static void top_toolbar_debug_menu_dropdown(int16_t dropdownIndex); static void top_toolbar_init_network_menu(rct_window* window, rct_widget* widget); @@ -319,7 +341,7 @@ static colour_t _tertiaryColour; rct_window* window_top_toolbar_open() { rct_window* window = window_create( - 0, 0, context_get_width(), TOP_TOOLBAR_HEIGHT + 1, &window_top_toolbar_events, WC_TOP_TOOLBAR, + ScreenCoordsXY(0, 0), context_get_width(), TOP_TOOLBAR_HEIGHT + 1, &window_top_toolbar_events, WC_TOP_TOOLBAR, WF_STICK_TO_FRONT | WF_TRANSPARENT | WF_NO_BACKGROUND); window->widgets = window_top_toolbar_widgets; @@ -492,30 +514,7 @@ static void window_top_toolbar_mousedown(rct_window* w, rct_widgetindex widgetIn #endif break; case WIDX_CHEATS: - gDropdownItemsFormat[0] = STR_TOGGLE_OPTION; - gDropdownItemsFormat[1] = STR_EMPTY; - gDropdownItemsFormat[2] = STR_TOGGLE_OPTION; - gDropdownItemsFormat[3] = STR_TOGGLE_OPTION; - gDropdownItemsFormat[4] = STR_TOGGLE_OPTION; - gDropdownItemsArgs[0] = STR_CHEAT_TITLE; - gDropdownItemsArgs[2] = STR_ENABLE_SANDBOX_MODE; - gDropdownItemsArgs[3] = STR_DISABLE_CLEARANCE_CHECKS; - gDropdownItemsArgs[4] = STR_DISABLE_SUPPORT_LIMITS; - window_dropdown_show_text( - w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[0] | 0x80, 0, 5); - if (gCheatsSandboxMode) - { - dropdown_set_checked(DDIDX_ENABLE_SANDBOX_MODE, true); - } - if (gCheatsDisableClearanceChecks) - { - dropdown_set_checked(DDIDX_DISABLE_CLEARANCE_CHECKS, true); - } - if (gCheatsDisableSupportLimits) - { - dropdown_set_checked(DDIDX_DISABLE_SUPPORT_LIMITS, true); - } - gDropdownDefaultIndex = DDIDX_CHEATS; + top_toolbar_init_cheats_menu(w, widget); break; case WIDX_VIEW_MENU: top_toolbar_init_view_menu(w, widget); @@ -644,26 +643,7 @@ static void window_top_toolbar_dropdown(rct_window* w, rct_widgetindex widgetInd } break; case WIDX_CHEATS: - switch (dropdownIndex) - { - case DDIDX_CHEATS: - context_open_window(WC_CHEATS); - break; - case DDIDX_ENABLE_SANDBOX_MODE: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_SANDBOXMODE, !gCheatsSandboxMode, GAME_COMMAND_CHEAT, 0, 0); - break; - case DDIDX_DISABLE_CLEARANCE_CHECKS: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLECLEARANCECHECKS, !gCheatsDisableClearanceChecks, - GAME_COMMAND_CHEAT, 0, 0); - break; - case DDIDX_DISABLE_SUPPORT_LIMITS: - game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLESUPPORTLIMITS, !gCheatsDisableSupportLimits, - GAME_COMMAND_CHEAT, 0, 0); - break; - } + top_toolbar_cheats_menu_dropdown(dropdownIndex); break; case WIDX_VIEW_MENU: top_toolbar_view_menu_dropdown(dropdownIndex); @@ -744,7 +724,7 @@ static void window_top_toolbar_invalidate(rct_window* w) window_top_toolbar_widgets[WIDX_CHAT].type = WWT_EMPTY; } - if (gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) + if (gScreenFlags & SCREEN_FLAGS_EDITOR) { window_top_toolbar_widgets[WIDX_PAUSE].type = WWT_EMPTY; window_top_toolbar_widgets[WIDX_RIDES].type = WWT_EMPTY; @@ -753,7 +733,6 @@ static void window_top_toolbar_invalidate(rct_window* w) window_top_toolbar_widgets[WIDX_GUESTS].type = WWT_EMPTY; window_top_toolbar_widgets[WIDX_FINANCES].type = WWT_EMPTY; window_top_toolbar_widgets[WIDX_RESEARCH].type = WWT_EMPTY; - window_top_toolbar_widgets[WIDX_CHEATS].type = WWT_EMPTY; window_top_toolbar_widgets[WIDX_NEWS].type = WWT_EMPTY; window_top_toolbar_widgets[WIDX_NETWORK].type = WWT_EMPTY; @@ -875,6 +854,20 @@ static void window_top_toolbar_invalidate(rct_window* w) else window_top_toolbar_widgets[WIDX_MUTE].image = IMAGE_TYPE_REMAP | SPR_G2_TOOLBAR_UNMUTE; + // Set map button to the right image. + if (window_top_toolbar_widgets[WIDX_MAP].type != WWT_EMPTY) + { + static constexpr uint32_t imageIdByRotation[] = { + SPR_G2_MAP_NORTH, + SPR_G2_MAP_WEST, + SPR_G2_MAP_SOUTH, + SPR_G2_MAP_EAST, + }; + + uint32_t mapImageId = imageIdByRotation[get_current_rotation()]; + window_top_toolbar_widgets[WIDX_MAP].image = IMAGE_TYPE_REMAP | mapImageId; + } + // Zoomed out/in disable. Not sure where this code is in the original. if (window_get_main()->viewport->zoom == 0) { @@ -1005,8 +998,15 @@ static void window_top_toolbar_paint(rct_window* w, rct_drawpixelinfo* dpi) y = w->y + window_top_toolbar_widgets[WIDX_NETWORK].top + 0; if (widget_is_pressed(w, WIDX_NETWORK)) y++; - imgId = SPR_SHOW_GUESTS_ON_THIS_RIDE_ATTRACTION; - gfx_draw_sprite(dpi, imgId, x, y, 0); + + // Draw (de)sync icon. + imgId = (network_is_desynchronised() ? SPR_G2_MULTIPLAYER_DESYNC : SPR_G2_MULTIPLAYER_SYNC); + gfx_draw_sprite(dpi, imgId, x + 3, y + 11, 0); + + // Draw number of players. + int32_t player_count = network_get_num_players(); + gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM; + gfx_draw_string_right(dpi, STR_COMMA16, &player_count, COLOUR_WHITE | COLOUR_FLAG_OUTLINE, x + 23, y + 1); } } @@ -1041,11 +1041,11 @@ static void repaint_scenery_tool_down(int16_t x, int16_t y, rct_widgetindex widg return; uint8_t quadrant = tile_element->AsSmallScenery()->GetSceneryQuadrant(); - gGameCommandErrorTitle = STR_CANT_REPAINT_THIS; - game_do_command( - grid_x, GAME_COMMAND_FLAG_APPLY | quadrant << 8, grid_y, - tile_element->base_height | (tile_element->AsSmallScenery()->GetEntryIndex() << 8), - GAME_COMMAND_SET_SCENERY_COLOUR, 0, gWindowSceneryPrimaryColour | (gWindowScenerySecondaryColour << 8)); + auto repaintScenery = SmallScenerySetColourAction( + { grid_x, grid_y, tile_element->base_height * 8 }, quadrant, tile_element->AsSmallScenery()->GetEntryIndex(), + gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour); + + GameActions::Execute(&repaintScenery); break; } case VIEWPORT_INTERACTION_ITEM_WALL: @@ -1056,11 +1056,11 @@ static void repaint_scenery_tool_down(int16_t x, int16_t y, rct_widgetindex widg if (!(scenery_entry->wall.flags & (WALL_SCENERY_HAS_PRIMARY_COLOUR | WALL_SCENERY_HAS_GLASS))) return; - gGameCommandErrorTitle = STR_CANT_REPAINT_THIS; - game_do_command( - grid_x, 1 | (gWindowSceneryPrimaryColour << 8), grid_y, - tile_element->GetDirection() | (tile_element->base_height << 8), GAME_COMMAND_SET_WALL_COLOUR, 0, - gWindowScenerySecondaryColour | (gWindowSceneryTertiaryColour << 8)); + auto repaintScenery = WallSetColourAction( + { grid_x, grid_y, tile_element->base_height * 8, tile_element->GetDirection() }, gWindowSceneryPrimaryColour, + gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); + + GameActions::Execute(&repaintScenery); break; } case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: @@ -1071,23 +1071,27 @@ static void repaint_scenery_tool_down(int16_t x, int16_t y, rct_widgetindex widg if (!(scenery_entry->large_scenery.flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) return; - gGameCommandErrorTitle = STR_CANT_REPAINT_THIS; - game_do_command( - grid_x, 1 | (tile_element->GetDirection() << 8), grid_y, - tile_element->base_height | (tile_element->AsLargeScenery()->GetSequenceIndex() << 8), - GAME_COMMAND_SET_LARGE_SCENERY_COLOUR, 0, gWindowSceneryPrimaryColour | (gWindowScenerySecondaryColour << 8)); + auto repaintScenery = LargeScenerySetColourAction( + { grid_x, grid_y, tile_element->base_height * 8, tile_element->GetDirection() }, + tile_element->AsLargeScenery()->GetSequenceIndex(), gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour); + + GameActions::Execute(&repaintScenery); break; } case VIEWPORT_INTERACTION_ITEM_BANNER: { - rct_banner* banner = &gBanners[tile_element->AsBanner()->GetIndex()]; - rct_scenery_entry* scenery_entry = get_banner_entry(banner->type); - if (scenery_entry->banner.flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR) + auto banner = tile_element->AsBanner()->GetBanner(); + if (banner != nullptr) { - gGameCommandErrorTitle = STR_CANT_REPAINT_THIS; - game_do_command( - grid_x, 1, grid_y, tile_element->base_height | ((tile_element->AsBanner()->GetPosition() & 0x3) << 8), - GAME_COMMAND_SET_BANNER_COLOUR, 0, gWindowSceneryPrimaryColour | (gWindowScenerySecondaryColour << 8)); + auto scenery_entry = get_banner_entry(banner->type); + if (scenery_entry->banner.flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR) + { + auto repaintScenery = BannerSetColourAction( + { grid_x, grid_y, tile_element->base_height * 8, tile_element->AsBanner()->GetPosition() }, + gWindowSceneryPrimaryColour); + + GameActions::Execute(&repaintScenery); + } } break; } @@ -1163,15 +1167,17 @@ static void scenery_eyedropper_tool_down(int16_t x, int16_t y, rct_widgetindex w } case VIEWPORT_INTERACTION_ITEM_BANNER: { - int32_t bannerIndex = tileElement->AsBanner()->GetIndex(); - rct_banner* banner = &gBanners[bannerIndex]; - rct_scenery_entry* sceneryEntry = get_banner_entry(banner->type); - if (sceneryEntry != nullptr) + auto banner = tileElement->AsBanner()->GetBanner(); + if (banner != nullptr) { - int32_t sceneryId = get_scenery_id_from_entry_index(OBJECT_TYPE_BANNERS, banner->type); - if (sceneryId != -1 && window_scenery_set_selected_item(sceneryId)) + auto sceneryEntry = get_banner_entry(banner->type); + if (sceneryEntry != nullptr) { - gWindowSceneryEyedropperEnabled = false; + int32_t sceneryId = get_scenery_id_from_entry_index(OBJECT_TYPE_BANNERS, banner->type); + if (sceneryId != -1 && window_scenery_set_selected_item(sceneryId)) + { + gWindowSceneryEyedropperEnabled = false; + } } } break; @@ -1349,15 +1355,15 @@ static void sub_6E1F34( // If SHIFT pressed if (gSceneryShiftPressed) { - TileElement* tile_element = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); + auto* surfaceElement = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); - if (tile_element == nullptr) + if (surfaceElement == nullptr) { *grid_x = LOCATION_NULL; return; } - int16_t z = (tile_element->base_height * 8) & 0xFFF0; + int16_t z = (surfaceElement->base_height * 8) & 0xFFF0; z += gSceneryShiftPressZOffset; z = std::clamp(z, 16, maxPossibleHeight); @@ -1423,25 +1429,21 @@ static void sub_6E1F34( return; } + // If CTRL and SHIFT not pressed gSceneryPlaceZ = 0; - uint16_t water_height = tile_element->AsSurface()->GetWaterHeight(); - if (water_height != 0) - { - gSceneryPlaceZ = water_height * 16; - } // If SHIFT pressed if (gSceneryShiftPressed) { - tile_element = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); + auto surfaceElement = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); - if (tile_element == nullptr) + if (surfaceElement == nullptr) { *grid_x = LOCATION_NULL; return; } - int16_t z = (tile_element->base_height * 8) & 0xFFF0; + int16_t z = (surfaceElement->base_height * 8) & 0xFFF0; z += gSceneryShiftPressZOffset; z = std::clamp(z, 16, maxPossibleHeight); @@ -1530,15 +1532,15 @@ static void sub_6E1F34( // If SHIFT pressed if (gSceneryShiftPressed) { - TileElement* tile_element = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); + auto* surfaceElement = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); - if (tile_element == nullptr) + if (surfaceElement == nullptr) { *grid_x = LOCATION_NULL; return; } - int16_t z = (tile_element->base_height * 8) & 0xFFF0; + int16_t z = (surfaceElement->base_height * 8) & 0xFFF0; z += gSceneryShiftPressZOffset; z = std::clamp(z, 16, maxPossibleHeight); @@ -1579,7 +1581,7 @@ static void sub_6E1F34( // If CTRL not pressed if (!gSceneryCtrlPressed) { - sub_68A15E(x, y, grid_x, grid_y, nullptr, nullptr); + sub_68A15E(x, y, grid_x, grid_y); if (*grid_x == LOCATION_NULL) return; @@ -1589,15 +1591,15 @@ static void sub_6E1F34( // If SHIFT pressed if (gSceneryShiftPressed) { - TileElement* tile_element = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); + auto* surfaceElement = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); - if (tile_element == nullptr) + if (surfaceElement == nullptr) { *grid_x = LOCATION_NULL; return; } - int16_t z = (tile_element->base_height * 8) & 0xFFF0; + int16_t z = (surfaceElement->base_height * 8) & 0xFFF0; z += gSceneryShiftPressZOffset; z = std::clamp(z, 16, maxPossibleHeight); @@ -1787,7 +1789,7 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo } // Actually place - if (success == GA_ERROR::OK || ((q + 1 == quantity) && (forceError == true))) + if (success == GA_ERROR::OK || ((q + 1 == quantity) && forceError)) { auto smallSceneryPlaceAction = SmallSceneryPlaceAction( { cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, type, primaryColour, @@ -1796,8 +1798,7 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo smallSceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) { if (result->Error == GA_ERROR::OK) { - audio_play_sound_at_location( - SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z); + audio_play_sound_at_location(SoundId::PlaceItem, result->Position); } }); auto res = GameActions::Execute(&smallSceneryPlaceAction); @@ -1826,7 +1827,7 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo { return; } - audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z); + audio_play_sound_at_location(SoundId::PlaceItem, result->Position); }); auto res = GameActions::Execute(&footpathSceneryPlaceAction); break; @@ -1841,32 +1842,42 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo for (; zAttemptRange != 0; zAttemptRange--) { - int32_t flags = (parameter_1 & 0xFF00) | GAME_COMMAND_FLAG_APPLY; + auto primaryColour = (parameter_2 >> 8) & 0xFF; + auto edges = parameter_2 & 0xFF; + auto type = (parameter_1 >> 8) & 0xFF; + auto wallPlaceAction = WallPlaceAction( + type, { gridX, gridY, gSceneryPlaceZ }, edges, primaryColour, _secondaryColour, _tertiaryColour); - gDisableErrorWindowSound = true; - gGameCommandErrorTitle = STR_CANT_BUILD_PARK_ENTRANCE_HERE; - int32_t cost = game_do_command( - gridX, flags, gridY, parameter_2, GAME_COMMAND_PLACE_WALL, gSceneryPlaceZ, - _secondaryColour | (_tertiaryColour << 8)); - gDisableErrorWindowSound = false; - - if (cost != MONEY32_UNDEFINED) - { - window_close_by_class(WC_ERROR); - audio_play_sound_at_location(SOUND_PLACE_ITEM, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z); - return; - } - - if (gGameCommandErrorText == STR_NOT_ENOUGH_CASH_REQUIRES - || gGameCommandErrorText == STR_CAN_ONLY_BUILD_THIS_ON_WATER) + auto res = GameActions::Query(&wallPlaceAction); + if (res->Error == GA_ERROR::OK) { break; } - gSceneryPlaceZ += 8; + if (res->ErrorMessage == STR_NOT_ENOUGH_CASH_REQUIRES || res->ErrorMessage == STR_CAN_ONLY_BUILD_THIS_ON_WATER) + { + break; + } + + if (zAttemptRange != 1) + { + gSceneryPlaceZ += 8; + } } - audio_play_sound_at_location(SOUND_ERROR, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z); + auto primaryColour = (parameter_2 >> 8) & 0xFF; + auto edges = parameter_2 & 0xFF; + auto type = (parameter_1 >> 8) & 0xFF; + auto wallPlaceAction = WallPlaceAction( + type, { gridX, gridY, gSceneryPlaceZ }, edges, primaryColour, _secondaryColour, _tertiaryColour); + + wallPlaceAction.SetCallback([](const GameAction* ga, const GameActionResult* result) { + if (result->Error == GA_ERROR::OK) + { + audio_play_sound_at_location(SoundId::PlaceItem, result->Position); + } + }); + auto res = GameActions::Execute(&wallPlaceAction); break; } case SCENERY_TYPE_LARGE: @@ -1879,52 +1890,81 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo for (; zAttemptRange != 0; zAttemptRange--) { - int32_t flags = (parameter_1 & 0xFF00) | GAME_COMMAND_FLAG_APPLY; + auto primaryColour = parameter_2 & 0xFF; + auto secondaryColour = (parameter_2 >> 8) & 0xFF; + auto largeSceneryType = parameter_3 & 0xFF; + uint8_t direction = (parameter_1 & 0xFF00) >> 8; + CoordsXYZD loc = { gridX, gridY, gSceneryPlaceZ, direction }; - gDisableErrorWindowSound = true; - gGameCommandErrorTitle = STR_CANT_POSITION_THIS_HERE; - int32_t cost = game_do_command( - gridX, flags, gridY, parameter_2, GAME_COMMAND_PLACE_LARGE_SCENERY, parameter_3, gSceneryPlaceZ); - gDisableErrorWindowSound = false; + auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, largeSceneryType, primaryColour, secondaryColour); - if (cost != MONEY32_UNDEFINED) - { - window_close_by_class(WC_ERROR); - audio_play_sound_at_location(SOUND_PLACE_ITEM, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z); - return; - } - - if (gGameCommandErrorText == STR_NOT_ENOUGH_CASH_REQUIRES - || gGameCommandErrorText == STR_CAN_ONLY_BUILD_THIS_ON_WATER) + auto res = GameActions::Query(&sceneryPlaceAction); + if (res->Error == GA_ERROR::OK) { break; } - gSceneryPlaceZ += 8; - } + if (res->ErrorMessage == STR_NOT_ENOUGH_CASH_REQUIRES || res->ErrorMessage == STR_CAN_ONLY_BUILD_THIS_ON_WATER) + { + break; + } - audio_play_sound_at_location(SOUND_ERROR, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z); + if (zAttemptRange != 1) + { + gSceneryPlaceZ += 8; + } + } + auto primaryColour = parameter_2 & 0xFF; + auto secondaryColour = (parameter_2 >> 8) & 0xFF; + auto largeSceneryType = parameter_3 & 0xFF; + uint8_t direction = (parameter_1 & 0xFF00) >> 8; + CoordsXYZD loc = { gridX, gridY, gSceneryPlaceZ, direction }; + + auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, largeSceneryType, primaryColour, secondaryColour); + sceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) { + if (result->Error == GA_ERROR::OK) + { + audio_play_sound_at_location(SoundId::PlaceItem, result->Position); + } + else + { + audio_play_sound_at_location(SoundId::Error, { loc.x, loc.y, gSceneryPlaceZ }); + } + }); + auto res = GameActions::Execute(&sceneryPlaceAction); break; } case SCENERY_TYPE_BANNER: { - int32_t flags = (parameter_1 & 0xFF00) | GAME_COMMAND_FLAG_APPLY; - - gGameCommandErrorTitle = STR_CANT_POSITION_THIS_HERE; - game_command_callback = game_command_callback_place_banner; - game_do_command( - gridX, flags, gridY, parameter_2, GAME_COMMAND_PLACE_BANNER, parameter_3, gWindowSceneryPrimaryColour); + uint8_t direction = (parameter_2 >> 8) & 0xFF; + int32_t z = (parameter_2 & 0xFF) * 16; + CoordsXYZD loc{ gridX, gridY, z, direction }; + auto primaryColour = gWindowSceneryPrimaryColour; + auto bannerType = (parameter_1 & 0xFF00) >> 8; + auto bannerIndex = create_new_banner(0); + if (bannerIndex == BANNER_INDEX_NULL) + { + context_show_error(STR_CANT_POSITION_THIS_HERE, STR_TOO_MANY_BANNERS_IN_GAME); + break; + } + auto bannerPlaceAction = BannerPlaceAction(loc, bannerType, bannerIndex, primaryColour); + bannerPlaceAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) { + if (result->Error == GA_ERROR::OK) + { + audio_play_sound_at_location(SoundId::PlaceItem, result->Position); + context_open_detail_window(WD_BANNER, bannerIndex); + } + }); + GameActions::Execute(&bannerPlaceAction); break; } } } -/** - * - * rct2: 0x0068E213 - */ -static void top_toolbar_tool_update_scenery_clear(int16_t x, int16_t y) +static uint8_t top_toolbar_tool_update_land_paint(int16_t x, int16_t y) { + uint8_t state_changed = 0; + map_invalidate_selection_rect(); gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; @@ -1938,11 +1978,9 @@ static void top_toolbar_tool_update_scenery_clear(int16_t x, int16_t y) gClearSceneryCost = MONEY32_UNDEFINED; window_invalidate_by_class(WC_CLEAR_SCENERY); } - return; + return state_changed; } - uint8_t state_changed = 0; - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) { gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; @@ -1992,7 +2030,16 @@ static void top_toolbar_tool_update_scenery_clear(int16_t x, int16_t y) } map_invalidate_selection_rect(); - if (!state_changed) + return state_changed; +} + +/** + * + * rct2: 0x0068E213 + */ +static void top_toolbar_tool_update_scenery_clear(int16_t x, int16_t y) +{ + if (!top_toolbar_tool_update_land_paint(x, y)) return; auto action = GetClearAction(); @@ -2005,79 +2052,6 @@ static void top_toolbar_tool_update_scenery_clear(int16_t x, int16_t y) } } -static void top_toolbar_tool_update_land_paint(int16_t x, int16_t y) -{ - map_invalidate_selection_rect(); - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - - LocationXY16 mapTile = {}; - screen_get_map_xy(x, y, &mapTile.x, &mapTile.y, nullptr); - - if (mapTile.x == LOCATION_NULL) - { - if (gClearSceneryCost != MONEY32_UNDEFINED) - { - gClearSceneryCost = MONEY32_UNDEFINED; - window_invalidate_by_class(WC_CLEAR_SCENERY); - } - return; - } - - uint8_t state_changed = 0; - - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) - { - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - state_changed++; - } - - if (gMapSelectType != MAP_SELECT_TYPE_FULL) - { - gMapSelectType = MAP_SELECT_TYPE_FULL; - state_changed++; - } - - int16_t tool_size = std::max(1, gLandToolSize); - int16_t tool_length = (tool_size - 1) * 32; - - // Move to tool bottom left - mapTile.x -= (tool_size - 1) * 16; - mapTile.y -= (tool_size - 1) * 16; - mapTile.x &= 0xFFE0; - mapTile.y &= 0xFFE0; - - if (gMapSelectPositionA.x != mapTile.x) - { - gMapSelectPositionA.x = mapTile.x; - state_changed++; - } - - if (gMapSelectPositionA.y != mapTile.y) - { - gMapSelectPositionA.y = mapTile.y; - state_changed++; - } - - mapTile.x += tool_length; - mapTile.y += tool_length; - - if (gMapSelectPositionB.x != mapTile.x) - { - gMapSelectPositionB.x = mapTile.x; - state_changed++; - } - - if (gMapSelectPositionB.y != mapTile.y) - { - gMapSelectPositionB.y = mapTile.y; - state_changed++; - } - - map_invalidate_selection_rect(); - if (!state_changed) - return; -} - /** * * rct2: 0x00664280 @@ -2331,16 +2305,21 @@ static void top_toolbar_tool_update_water(int16_t x, int16_t y) if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) return; - money32 lower_cost = lower_water( - gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y, 0); + auto waterLowerAction = WaterLowerAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); + auto waterRaiseAction = WaterRaiseAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); - money32 raise_cost = raise_water( - gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y, 0); + auto res = GameActions::Query(&waterLowerAction); + money32 lowerCost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; - if (gWaterToolRaiseCost != raise_cost || gWaterToolLowerCost != lower_cost) + res = GameActions::Query(&waterRaiseAction); + money32 raiseCost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; + + if (gWaterToolRaiseCost != raiseCost || gWaterToolLowerCost != lowerCost) { - gWaterToolRaiseCost = raise_cost; - gWaterToolLowerCost = lower_cost; + gWaterToolRaiseCost = raiseCost; + gWaterToolLowerCost = lowerCost; window_invalidate_by_class(WC_WATER); } return; @@ -2422,16 +2401,21 @@ static void top_toolbar_tool_update_water(int16_t x, int16_t y) if (!state_changed) return; - money32 lower_cost = lower_water( - gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y, 0); + auto waterLowerAction = WaterLowerAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); + auto waterRaiseAction = WaterRaiseAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); - money32 raise_cost = raise_water( - gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y, 0); + auto res = GameActions::Query(&waterLowerAction); + money32 lowerCost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; - if (gWaterToolRaiseCost != raise_cost || gWaterToolLowerCost != lower_cost) + res = GameActions::Query(&waterRaiseAction); + money32 raiseCost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; + + if (gWaterToolRaiseCost != raiseCost || gWaterToolLowerCost != lowerCost) { - gWaterToolRaiseCost = raise_cost; - gWaterToolLowerCost = lower_cost; + gWaterToolRaiseCost = raiseCost; + gWaterToolLowerCost = lowerCost; window_invalidate_by_class(WC_WATER); } } @@ -2466,8 +2450,6 @@ static money32 try_place_ghost_scenery( { map_tile.x, map_tile.y, gSceneryPlaceZ, rotation }, quadrant, type, primaryColour, secondaryColour); smallSceneryPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); auto res = GameActions::Execute(&smallSceneryPlaceAction); - - cost = res->Cost; if (res->Error != GA_ERROR::OK) return MONEY32_UNDEFINED; @@ -2476,10 +2458,10 @@ static money32 try_place_ghost_scenery( gSceneryPlaceRotation = (uint16_t)(parameter_3 & 0xFF); gSceneryPlaceObject = selected_tab; - tileElement = gSceneryTileElement; + tileElement = dynamic_cast(res.get())->tileElement; gSceneryGhostPosition.z = tileElement->base_height; gSceneryQuadrant = tileElement->AsSmallScenery()->GetSceneryQuadrant(); - if (gSceneryGroundFlags & ELEMENT_IS_UNDERGROUND) + if (dynamic_cast(res.get())->GroundFlags & ELEMENT_IS_UNDERGROUND) { // Set underground on viewport_set_visibility(4); @@ -2491,6 +2473,7 @@ static money32 try_place_ghost_scenery( } gSceneryGhostType |= SCENERY_GHOST_FLAG_0; + cost = res->Cost; break; } case 1: @@ -2518,45 +2501,64 @@ static money32 try_place_ghost_scenery( auto res = GameActions::Execute(&footpathSceneryPlaceAction); if (res->Error != GA_ERROR::OK) return MONEY32_UNDEFINED; + + cost = res->Cost; break; } case 2: + { // Walls // 6e26b0 - cost = game_do_command( - map_tile.x, parameter_1 | 0x69, map_tile.y, parameter_2, GAME_COMMAND_PLACE_WALL, gSceneryPlaceZ, - _secondaryColour | (_tertiaryColour << 8)); + auto primaryColour = (parameter_2 >> 8) & 0xFF; + auto edges = parameter_2 & 0xFF; + auto type = (parameter_1 >> 8) & 0xFF; + auto wallPlaceAction = WallPlaceAction( + type, { map_tile.x, map_tile.y, gSceneryPlaceZ }, edges, primaryColour, _secondaryColour, _tertiaryColour); + wallPlaceAction.SetFlags( + GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); + wallPlaceAction.SetCallback([=](const GameAction* ga, const WallPlaceActionResult* result) { + if (result->Error != GA_ERROR::OK) + return; - if (cost == MONEY32_UNDEFINED) - return cost; + gSceneryGhostPosition.x = map_tile.x; + gSceneryGhostPosition.y = map_tile.y; + gSceneryGhostWallRotation = edges; + gSceneryGhostPosition.z = result->tileElement->base_height; - gSceneryGhostPosition.x = map_tile.x; - gSceneryGhostPosition.y = map_tile.y; - gSceneryGhostWallRotation = (parameter_2 & 0xFF); + gSceneryGhostType |= SCENERY_GHOST_FLAG_2; + }); - tileElement = gSceneryTileElement; - gSceneryGhostPosition.z = tileElement->base_height; - - gSceneryGhostType |= SCENERY_GHOST_FLAG_2; + auto res = GameActions::Execute(&wallPlaceAction); + if (res->Error != GA_ERROR::OK) + return MONEY32_UNDEFINED; + cost = res->Cost; break; + } case 3: + { // Large Scenery // 6e25a7 - cost = game_do_command( - map_tile.x, parameter_1 | 0x69, map_tile.y, parameter_2, GAME_COMMAND_PLACE_LARGE_SCENERY, parameter_3, - gSceneryPlaceZ); + auto primaryColour = parameter_2 & 0xFF; + auto secondaryColour = (parameter_2 >> 8) & 0xFF; + auto sceneryType = parameter_3 & 0xFF; + uint8_t direction = (parameter_1 & 0xFF00) >> 8; + CoordsXYZD loc = { map_tile.x, map_tile.y, gSceneryPlaceZ, direction }; - if (cost == MONEY32_UNDEFINED) - return cost; + auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, sceneryType, primaryColour, secondaryColour); + sceneryPlaceAction.SetFlags( + GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); + auto res = GameActions::Execute(&sceneryPlaceAction); + if (res->Error != GA_ERROR::OK) + return MONEY32_UNDEFINED; gSceneryGhostPosition.x = map_tile.x; gSceneryGhostPosition.y = map_tile.y; - gSceneryPlaceRotation = ((parameter_1 >> 8) & 0xFF); + gSceneryPlaceRotation = loc.direction; - tileElement = gSceneryTileElement; + tileElement = dynamic_cast(res.get())->tileElement; gSceneryGhostPosition.z = tileElement->base_height; - if (gSceneryGroundFlags & ELEMENT_IS_UNDERGROUND) + if (dynamic_cast(res.get())->GroundFlags & ELEMENT_IS_UNDERGROUND) { // Set underground on viewport_set_visibility(4); @@ -2568,22 +2570,39 @@ static money32 try_place_ghost_scenery( } gSceneryGhostType |= SCENERY_GHOST_FLAG_3; + cost = res->Cost; break; + } case 4: + { // Banners // 6e2612 - cost = game_do_command( - map_tile.x, parameter_1 | 0x69, map_tile.y, parameter_2, GAME_COMMAND_PLACE_BANNER, parameter_3, 0); + uint8_t direction = (parameter_2 >> 8) & 0xFF; + int32_t z = (parameter_2 & 0xFF) * 16; + CoordsXYZD loc{ map_tile.x, map_tile.y, z, direction }; + auto primaryColour = gWindowSceneryPrimaryColour; + auto bannerType = (parameter_1 & 0xFF00) >> 8; + auto bannerIndex = create_new_banner(0); + if (bannerIndex == BANNER_INDEX_NULL) + { + // Silently fail as this is just for the ghost + break; + } + auto bannerPlaceAction = BannerPlaceAction(loc, bannerType, bannerIndex, primaryColour); + bannerPlaceAction.SetFlags( + GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); + auto res = GameActions::Execute(&bannerPlaceAction); + if (res->Error != GA_ERROR::OK) + return MONEY32_UNDEFINED; - if (cost == MONEY32_UNDEFINED) - return cost; - - gSceneryGhostPosition.x = map_tile.x; - gSceneryGhostPosition.y = map_tile.y; - gSceneryGhostPosition.z = (parameter_2 & 0xFF) * 2 + 2; - gSceneryPlaceRotation = ((parameter_2 >> 8) & 0xFF); + gSceneryGhostPosition.x = loc.x; + gSceneryGhostPosition.y = loc.y; + gSceneryGhostPosition.z = loc.z / 8 + 2; + gSceneryPlaceRotation = direction; gSceneryGhostType |= SCENERY_GHOST_FLAG_4; + cost = res->Cost; break; + } } return cost; @@ -2762,7 +2781,7 @@ static void top_toolbar_tool_update_scenery(int16_t x, int16_t y) case SCENERY_TYPE_LARGE: { scenery = get_large_scenery_entry(selected_scenery); - LocationXY16* selectedTile = gMapSelectionTiles; + gMapSelectionTiles.clear(); for (rct_large_scenery_tile* tile = scenery->large_scenery.tiles; tile->x_offset != (int16_t)(uint16_t)0xFFFF; tile++) @@ -2774,11 +2793,8 @@ static void top_toolbar_tool_update_scenery(int16_t x, int16_t y) tileLocation.x += mapTile.x; tileLocation.y += mapTile.y; - selectedTile->x = tileLocation.x; - selectedTile->y = tileLocation.y; - selectedTile++; + gMapSelectionTiles.push_back({ tileLocation.x, tileLocation.y }); } - selectedTile->x = -1; gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT; map_invalidate_map_selection_tiles(); @@ -2887,10 +2903,12 @@ static void window_top_toolbar_tool_down(rct_window* w, rct_widgetindex widgetIn case WIDX_LAND: if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) { - gGameCommandErrorTitle = STR_CANT_CHANGE_LAND_TYPE; - game_do_command( - gMapSelectPositionA.x, 1, gMapSelectPositionA.y, gLandToolTerrainSurface | (gLandToolTerrainEdge << 8), - GAME_COMMAND_CHANGE_SURFACE_STYLE, gMapSelectPositionB.x, gMapSelectPositionB.y); + auto surfaceSetStyleAction = SurfaceSetStyleAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + gLandToolTerrainSurface, gLandToolTerrainEdge); + + GameActions::Execute(&surfaceSetStyleAction); + gCurrentToolId = TOOL_UP_DOWN_ARROW; } break; @@ -2917,17 +2935,25 @@ static money32 selection_raise_land(uint8_t flags) centreX += 16; centreY += 16; - uint32_t xBounds = (gMapSelectPositionA.x & 0xFFFF) | (gMapSelectPositionB.x << 16); - uint32_t yBounds = (gMapSelectPositionA.y & 0xFFFF) | (gMapSelectPositionB.y << 16); - - gGameCommandErrorTitle = STR_CANT_RAISE_LAND_HERE; if (gLandMountainMode) { - return game_do_command(centreX, flags, centreY, xBounds, GAME_COMMAND_EDIT_LAND_SMOOTH, gMapSelectType, yBounds); + auto landSmoothAction = LandSmoothAction( + { centreX, centreY }, + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType, + false); + auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landSmoothAction) + : GameActions::Query(&landSmoothAction); + return res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; } else { - return game_do_command(centreX, flags, centreY, xBounds, GAME_COMMAND_RAISE_LAND, gMapSelectType, yBounds); + auto landRaiseAction = LandRaiseAction( + { centreX, centreY }, + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType); + auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landRaiseAction) + : GameActions::Query(&landRaiseAction); + + return res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; } } @@ -2942,18 +2968,25 @@ static money32 selection_lower_land(uint8_t flags) centreX += 16; centreY += 16; - uint32_t xBounds = (gMapSelectPositionA.x & 0xFFFF) | (gMapSelectPositionB.x << 16); - uint32_t yBounds = (gMapSelectPositionA.y & 0xFFFF) | (gMapSelectPositionB.y << 16); - - gGameCommandErrorTitle = STR_CANT_LOWER_LAND_HERE; if (gLandMountainMode) { - return game_do_command( - centreX, flags, centreY, xBounds, GAME_COMMAND_EDIT_LAND_SMOOTH, 0x8000 + gMapSelectType, yBounds); + auto landSmoothAction = LandSmoothAction( + { centreX, centreY }, + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType, + true); + auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landSmoothAction) + : GameActions::Query(&landSmoothAction); + return res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; } else { - return game_do_command(centreX, flags, centreY, xBounds, GAME_COMMAND_LOWER_LAND, gMapSelectType, yBounds); + auto landLowerAction = LandLowerAction( + { centreX, centreY }, + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType); + auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landLowerAction) + : GameActions::Query(&landLowerAction); + + return res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; } } @@ -2963,10 +2996,10 @@ static money32 selection_lower_land(uint8_t flags) */ static void window_top_toolbar_land_tool_drag(int16_t x, int16_t y) { - rct_window* window = window_find_from_point(x, y); + rct_window* window = window_find_from_point(ScreenCoordsXY(x, y)); if (!window) return; - rct_widgetindex widget_index = window_find_widget_from_point(window, x, y); + rct_widgetindex widget_index = window_find_widget_from_point(window, ScreenCoordsXY(x, y)); if (widget_index == -1) return; rct_widget* widget = &window->widgets[widget_index]; @@ -3006,10 +3039,10 @@ static void window_top_toolbar_land_tool_drag(int16_t x, int16_t y) */ static void window_top_toolbar_water_tool_drag(int16_t x, int16_t y) { - rct_window* window = window_find_from_point(x, y); + rct_window* window = window_find_from_point(ScreenCoordsXY(x, y)); if (!window) return; - rct_widgetindex widget_index = window_find_widget_from_point(window, x, y); + rct_widgetindex widget_index = window_find_widget_from_point(window, ScreenCoordsXY(x, y)); if (widget_index == -1) return; rct_widget* widget = &window->widgets[widget_index]; @@ -3028,11 +3061,10 @@ static void window_top_toolbar_water_tool_drag(int16_t x, int16_t y) { gInputDragLastY += dx; - gGameCommandErrorTitle = STR_CANT_RAISE_WATER_LEVEL_HERE; + auto waterRaiseAction = WaterRaiseAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); + GameActions::Execute(&waterRaiseAction); - game_do_command( - gMapSelectPositionA.x, 1, gMapSelectPositionA.y, dx, GAME_COMMAND_RAISE_WATER, gMapSelectPositionB.x, - gMapSelectPositionB.y); gWaterToolRaiseCost = MONEY32_UNDEFINED; gWaterToolLowerCost = MONEY32_UNDEFINED; @@ -3045,11 +3077,9 @@ static void window_top_toolbar_water_tool_drag(int16_t x, int16_t y) { gInputDragLastY += dx; - gGameCommandErrorTitle = STR_CANT_LOWER_WATER_LEVEL_HERE; - - game_do_command( - gMapSelectPositionA.x, 1, gMapSelectPositionA.y, dx, GAME_COMMAND_LOWER_WATER, gMapSelectPositionB.x, - gMapSelectPositionB.y); + auto waterLowerAction = WaterLowerAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }); + GameActions::Execute(&waterLowerAction); gWaterToolRaiseCost = MONEY32_UNDEFINED; gWaterToolLowerCost = MONEY32_UNDEFINED; @@ -3079,10 +3109,12 @@ static void window_top_toolbar_tool_drag(rct_window* w, rct_widgetindex widgetIn { if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE) { - gGameCommandErrorTitle = STR_CANT_CHANGE_LAND_TYPE; - game_do_command( - gMapSelectPositionA.x, 1, gMapSelectPositionA.y, gLandToolTerrainSurface | (gLandToolTerrainEdge << 8), - GAME_COMMAND_CHANGE_SURFACE_STYLE, gMapSelectPositionB.x, gMapSelectPositionB.y); + auto surfaceSetStyleAction = SurfaceSetStyleAction( + { gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, + gLandToolTerrainSurface, gLandToolTerrainEdge); + + GameActions::Execute(&surfaceSetStyleAction); + // The tool is set to 12 here instead of 3 so that the dragging cursor is not the elevation change // cursor gCurrentToolId = TOOL_CROSSHAIR; @@ -3204,7 +3236,7 @@ static void top_toolbar_fastforward_menu_dropdown(int16_t dropdownIndex) gGameSpeed = dropdownIndex + 1; if (gGameSpeed >= 5) gGameSpeed = 8; - window_invalidate(w); + w->Invalidate(); } } } @@ -3228,34 +3260,47 @@ static void top_toolbar_rotate_menu_dropdown(int16_t dropdownIndex) if (dropdownIndex == 0) { window_rotate_camera(w, 1); - window_invalidate(w); + w->Invalidate(); } else if (dropdownIndex == 1) { window_rotate_camera(w, -1); - window_invalidate(w); + w->Invalidate(); } } } -static void top_toolbar_init_debug_menu(rct_window* w, rct_widget* widget) +static void top_toolbar_init_cheats_menu(rct_window* w, rct_widget* widget) { - gDropdownItemsFormat[DDIDX_CONSOLE] = STR_TOGGLE_OPTION; - gDropdownItemsArgs[DDIDX_CONSOLE] = STR_DEBUG_DROPDOWN_CONSOLE; + gDropdownItemsFormat[DDIDX_CHEATS] = STR_TOGGLE_OPTION; + gDropdownItemsArgs[DDIDX_CHEATS] = STR_CHEAT_TITLE; + gDropdownItemsFormat[DDIDX_TILE_INSPECTOR] = STR_TOGGLE_OPTION; gDropdownItemsArgs[DDIDX_TILE_INSPECTOR] = STR_DEBUG_DROPDOWN_TILE_INSPECTOR; + gDropdownItemsFormat[DDIDX_OBJECT_SELECTION] = STR_TOGGLE_OPTION; gDropdownItemsArgs[DDIDX_OBJECT_SELECTION] = STR_DEBUG_DROPDOWN_OBJECT_SELECTION; + gDropdownItemsFormat[DDIDX_INVENTIONS_LIST] = STR_TOGGLE_OPTION; gDropdownItemsArgs[DDIDX_INVENTIONS_LIST] = STR_DEBUG_DROPDOWN_INVENTIONS_LIST; + gDropdownItemsFormat[DDIDX_SCENARIO_OPTIONS] = STR_TOGGLE_OPTION; gDropdownItemsArgs[DDIDX_SCENARIO_OPTIONS] = STR_DEBUG_DROPDOWN_SCENARIO_OPTIONS; - gDropdownItemsFormat[DDIDX_DEBUG_PAINT] = STR_TOGGLE_OPTION; - gDropdownItemsArgs[DDIDX_DEBUG_PAINT] = STR_DEBUG_DROPDOWN_DEBUG_PAINT; + + gDropdownItemsFormat[5] = STR_EMPTY; + + gDropdownItemsFormat[DDIDX_ENABLE_SANDBOX_MODE] = STR_TOGGLE_OPTION; + gDropdownItemsArgs[DDIDX_ENABLE_SANDBOX_MODE] = STR_ENABLE_SANDBOX_MODE; + + gDropdownItemsFormat[DDIDX_DISABLE_CLEARANCE_CHECKS] = STR_TOGGLE_OPTION; + gDropdownItemsArgs[DDIDX_DISABLE_CLEARANCE_CHECKS] = STR_DISABLE_CLEARANCE_CHECKS; + + gDropdownItemsFormat[DDIDX_DISABLE_SUPPORT_LIMITS] = STR_TOGGLE_OPTION; + gDropdownItemsArgs[DDIDX_DISABLE_SUPPORT_LIMITS] = STR_DISABLE_SUPPORT_LIMITS; window_dropdown_show_text( - w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[0] | 0x80, - DROPDOWN_FLAG_STAY_OPEN, TOP_TOOLBAR_DEBUG_COUNT); + w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[0] | 0x80, 0, + TOP_TOOLBAR_CHEATS_COUNT); // Disable items that are not yet available in multiplayer if (network_get_mode() != NETWORK_MODE_NONE) @@ -3264,18 +3309,91 @@ static void top_toolbar_init_debug_menu(rct_window* w, rct_widget* widget) dropdown_set_disabled(DDIDX_INVENTIONS_LIST, true); } + if (gScreenFlags & SCREEN_FLAGS_EDITOR) + { + dropdown_set_disabled(DDIDX_CHEATS, true); + dropdown_set_disabled(DDIDX_OBJECT_SELECTION, true); + dropdown_set_disabled(DDIDX_INVENTIONS_LIST, true); + dropdown_set_disabled(DDIDX_SCENARIO_OPTIONS, true); + dropdown_set_disabled(DDIDX_ENABLE_SANDBOX_MODE, true); + } + + if (gCheatsSandboxMode) + { + dropdown_set_checked(DDIDX_ENABLE_SANDBOX_MODE, true); + } + if (gCheatsDisableClearanceChecks) + { + dropdown_set_checked(DDIDX_DISABLE_CLEARANCE_CHECKS, true); + } + if (gCheatsDisableSupportLimits) + { + dropdown_set_checked(DDIDX_DISABLE_SUPPORT_LIMITS, true); + } + + if (!dropdown_is_disabled(DDIDX_CHEATS)) + gDropdownDefaultIndex = DDIDX_CHEATS; + else + gDropdownDefaultIndex = DDIDX_TILE_INSPECTOR; +} + +static void top_toolbar_cheats_menu_dropdown(int16_t dropdownIndex) +{ + switch (dropdownIndex) + { + case DDIDX_CHEATS: + context_open_window(WC_CHEATS); + break; + case DDIDX_TILE_INSPECTOR: + context_open_window(WC_TILE_INSPECTOR); + break; + case DDIDX_OBJECT_SELECTION: + window_close_all(); + context_open_window(WC_EDITOR_OBJECT_SELECTION); + break; + case DDIDX_INVENTIONS_LIST: + context_open_window(WC_EDITOR_INVENTION_LIST); + break; + case DDIDX_SCENARIO_OPTIONS: + context_open_window(WC_EDITOR_SCENARIO_OPTIONS); + break; + case DDIDX_ENABLE_SANDBOX_MODE: + CheatsSet(CheatType::SandboxMode, !gCheatsSandboxMode); + break; + case DDIDX_DISABLE_CLEARANCE_CHECKS: + CheatsSet(CheatType::DisableClearanceChecks, !gCheatsDisableClearanceChecks); + break; + case DDIDX_DISABLE_SUPPORT_LIMITS: + CheatsSet(CheatType::DisableSupportLimits, !gCheatsDisableSupportLimits); + break; + } +} + +static void top_toolbar_init_debug_menu(rct_window* w, rct_widget* widget) +{ + gDropdownItemsFormat[DDIDX_CONSOLE] = STR_TOGGLE_OPTION; + gDropdownItemsArgs[DDIDX_CONSOLE] = STR_DEBUG_DROPDOWN_CONSOLE; + gDropdownItemsFormat[DDIDX_DEBUG_PAINT] = STR_TOGGLE_OPTION; + gDropdownItemsArgs[DDIDX_DEBUG_PAINT] = STR_DEBUG_DROPDOWN_DEBUG_PAINT; + + window_dropdown_show_text( + w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[0] | 0x80, + DROPDOWN_FLAG_STAY_OPEN, TOP_TOOLBAR_DEBUG_COUNT); + dropdown_set_checked(DDIDX_DEBUG_PAINT, window_find_by_class(WC_DEBUG_PAINT) != nullptr); } static void top_toolbar_init_network_menu(rct_window* w, rct_widget* widget) { - gDropdownItemsFormat[0] = STR_MULTIPLAYER; - gDropdownItemsFormat[DDIDX_MULTIPLAYER] = STR_MULTIPLAYER; gDropdownItemsFormat[DDIDX_NETWORK] = STR_NETWORK; + gDropdownItemsFormat[DDIDX_MULTIPLAYER_RECONNECT] = STR_MULTIPLAYER_RECONNECT; window_dropdown_show_text( - w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[0] | 0x80, 0, 2); + w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[0] | 0x80, 0, + TOP_TOOLBAR_NETWORK_COUNT); + + dropdown_set_disabled(DDIDX_MULTIPLAYER_RECONNECT, !network_is_desynchronised()); gDropdownDefaultIndex = DDIDX_MULTIPLAYER; } @@ -3293,19 +3411,6 @@ static void top_toolbar_debug_menu_dropdown(int16_t dropdownIndex) console.Open(); break; } - case DDIDX_TILE_INSPECTOR: - context_open_window(WC_TILE_INSPECTOR); - break; - case DDIDX_OBJECT_SELECTION: - window_close_all(); - context_open_window(WC_EDITOR_OBJECT_SELECTION); - break; - case DDIDX_INVENTIONS_LIST: - context_open_window(WC_EDITOR_INVENTION_LIST); - break; - case DDIDX_SCENARIO_OPTIONS: - context_open_window(WC_EDITOR_SCENARIO_OPTIONS); - break; case DDIDX_DEBUG_PAINT: if (window_find_by_class(WC_DEBUG_PAINT) == nullptr) { @@ -3333,6 +3438,9 @@ static void top_toolbar_network_menu_dropdown(int16_t dropdownIndex) case DDIDX_NETWORK: context_open_window(WC_NETWORK); break; + case DDIDX_MULTIPLAYER_RECONNECT: + network_reconnect(); + break; } } } @@ -3471,7 +3579,7 @@ static void top_toolbar_view_menu_dropdown(int16_t dropdownIndex) default: return; } - window_invalidate(w); + w->Invalidate(); } } diff --git a/src/openrct2-ui/windows/TrackDesignManage.cpp b/src/openrct2-ui/windows/TrackDesignManage.cpp index b74e679e0d..1518a7bbc1 100644 --- a/src/openrct2-ui/windows/TrackDesignManage.cpp +++ b/src/openrct2-ui/windows/TrackDesignManage.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -248,7 +248,7 @@ static void window_track_delete_prompt_open() int32_t screenWidth = context_get_width(); int32_t screenHeight = context_get_height(); rct_window* w = window_create( - std::max(TOP_TOOLBAR_HEIGHT + 1, (screenWidth - 250) / 2), (screenHeight - 44) / 2, 250, 74, + ScreenCoordsXY(std::max(TOP_TOOLBAR_HEIGHT + 1, (screenWidth - 250) / 2), (screenHeight - 44) / 2), 250, 74, &window_track_delete_prompt_events, WC_TRACK_DELETE_PROMPT, WF_STICK_TO_FRONT); w->widgets = window_track_delete_prompt_widgets; w->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_RENAME) | (1 << WIDX_DELETE); diff --git a/src/openrct2-ui/windows/TrackDesignPlace.cpp b/src/openrct2-ui/windows/TrackDesignPlace.cpp index 34aad6ab1c..6de0600b54 100644 --- a/src/openrct2-ui/windows/TrackDesignPlace.cpp +++ b/src/openrct2-ui/windows/TrackDesignPlace.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -115,19 +115,19 @@ static int16_t _window_track_place_last_valid_y; static int16_t _window_track_place_last_valid_z; static money32 _window_track_place_last_cost; -static rct_track_td6* _trackDesign; +static std::unique_ptr _trackDesign; static void window_track_place_clear_provisional(); static int32_t window_track_place_get_base_z(int32_t x, int32_t y); static void window_track_place_attempt_placement( - rct_track_td6* td6, int32_t x, int32_t y, int32_t z, int32_t bl, money32* cost, ride_id_t* rideIndex); + TrackDesign* td6, int32_t x, int32_t y, int32_t z, int32_t bl, money32* cost, ride_id_t* rideIndex); static void window_track_place_clear_mini_preview(); -static void window_track_place_draw_mini_preview(rct_track_td6* td6); +static void window_track_place_draw_mini_preview(TrackDesign* td6); static void window_track_place_draw_mini_preview_track( - rct_track_td6* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max); + TrackDesign* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max); static void window_track_place_draw_mini_preview_maze( - rct_track_td6* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max); + TrackDesign* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max); static LocationXY16 draw_mini_preview_get_pixel_position(int16_t x, int16_t y); static bool draw_mini_preview_is_pixel_in_bounds(LocationXY16 pixel); static uint8_t* draw_mini_preview_get_pixel_ptr(LocationXY16 pixel); @@ -148,8 +148,8 @@ static void window_track_place_clear_mini_preview() */ rct_window* window_track_place_open(const track_design_file_ref* tdFileRef) { - rct_track_td6* td6 = track_design_open(tdFileRef->path); - if (td6 == nullptr) + _trackDesign = track_design_open(tdFileRef->path); + if (_trackDesign == nullptr) { return nullptr; } @@ -158,7 +158,7 @@ rct_window* window_track_place_open(const track_design_file_ref* tdFileRef) _window_track_place_mini_preview.resize(TRACK_MINI_PREVIEW_SIZE); - rct_window* w = window_create(0, 29, 200, 124, &window_track_place_events, WC_TRACK_DESIGN_PLACE, 0); + rct_window* w = window_create(ScreenCoordsXY(0, 29), 200, 124, &window_track_place_events, WC_TRACK_DESIGN_PLACE, 0); w->widgets = window_track_place_widgets; w->enabled_widgets = 1 << WIDX_CLOSE | 1 << WIDX_ROTATE | 1 << WIDX_MIRROR | 1 << WIDX_SELECT_DIFFERENT_DESIGN; window_init_scroll_widgets(w); @@ -171,9 +171,7 @@ rct_window* window_track_place_open(const track_design_file_ref* tdFileRef) _currentTrackPieceDirection = (2 - get_current_rotation()) & 3; window_track_place_clear_mini_preview(); - window_track_place_draw_mini_preview(td6); - - _trackDesign = td6; + window_track_place_draw_mini_preview(_trackDesign.get()); return w; } @@ -192,7 +190,6 @@ static void window_track_place_close(rct_window* w) hide_gridlines(); _window_track_place_mini_preview.clear(); _window_track_place_mini_preview.shrink_to_fit(); - track_design_dispose(_trackDesign); _trackDesign = nullptr; } @@ -210,16 +207,16 @@ static void window_track_place_mouseup(rct_window* w, rct_widgetindex widgetInde case WIDX_ROTATE: window_track_place_clear_provisional(); _currentTrackPieceDirection = (_currentTrackPieceDirection + 1) & 3; - window_invalidate(w); + w->Invalidate(); _window_track_place_last_x = -1; - window_track_place_draw_mini_preview(_trackDesign); + window_track_place_draw_mini_preview(_trackDesign.get()); break; case WIDX_MIRROR: - track_design_mirror(_trackDesign); + track_design_mirror(_trackDesign.get()); _currentTrackPieceDirection = (0 - _currentTrackPieceDirection) & 3; - window_invalidate(w); + w->Invalidate(); _window_track_place_last_x = -1; - window_track_place_draw_mini_preview(_trackDesign); + window_track_place_draw_mini_preview(_trackDesign.get()); break; case WIDX_SELECT_DIFFERENT_DESIGN: window_close(w); @@ -257,7 +254,7 @@ static void window_track_place_toolupdate(rct_window* w, rct_widgetindex widgetI gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; // Get the tool map position - sub_68A15E(x, y, &mapX, &mapY, nullptr, nullptr); + sub_68A15E(x, y, &mapX, &mapY); if (mapX == LOCATION_NULL) { window_track_place_clear_provisional(); @@ -267,7 +264,7 @@ static void window_track_place_toolupdate(rct_window* w, rct_widgetindex widgetI // Check if tool map position has changed since last update if (mapX == _window_track_place_last_x && mapY == _window_track_place_last_y) { - place_virtual_track(_trackDesign, PTD_OPERATION_DRAW_OUTLINES, true, get_ride(0), mapX, mapY, 0); + place_virtual_track(_trackDesign.get(), PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), mapX, mapY, 0); return; } @@ -283,8 +280,8 @@ static void window_track_place_toolupdate(rct_window* w, rct_widgetindex widgetI for (int32_t i = 0; i < 7; i++) { ride_id_t rideIndex; - uint16_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_GHOST; - window_track_place_attempt_placement(_trackDesign, mapX, mapY, mapZ, flags, &cost, &rideIndex); + uint16_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; + window_track_place_attempt_placement(_trackDesign.get(), mapX, mapY, mapZ, flags, &cost, &rideIndex); if (cost != MONEY32_UNDEFINED) { _window_track_place_ride_index = rideIndex; @@ -306,7 +303,7 @@ static void window_track_place_toolupdate(rct_window* w, rct_widgetindex widgetI widget_invalidate(w, WIDX_PRICE); } - place_virtual_track(_trackDesign, PTD_OPERATION_DRAW_OUTLINES, true, get_ride(0), mapX, mapY, mapZ); + place_virtual_track(_trackDesign.get(), PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), mapX, mapY, mapZ); } /** @@ -315,47 +312,49 @@ static void window_track_place_toolupdate(rct_window* w, rct_widgetindex widgetI */ static void window_track_place_tooldown(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) { - int32_t i; - int16_t mapX, mapY, mapZ; - money32 cost; - ride_id_t rideIndex; - window_track_place_clear_provisional(); map_invalidate_map_selection_tiles(); gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - sub_68A15E(x, y, &mapX, &mapY, nullptr, nullptr); + int16_t mapX, mapY; + sub_68A15E(x, y, &mapX, &mapY); if (mapX == LOCATION_NULL) return; // Try increasing Z until a feasible placement is found - mapZ = window_track_place_get_base_z(mapX, mapY); - for (i = 0; i < 7; i++) + int16_t mapZ = window_track_place_get_base_z(mapX, mapY); + for (int32_t i = 0; i < 7; i++) { gDisableErrorWindowSound = true; - window_track_place_attempt_placement(_trackDesign, mapX, mapY, mapZ, 1, &cost, &rideIndex); + money32 cost = MONEY32_UNDEFINED; + ride_id_t rideIndex = RIDE_ID_NULL; + window_track_place_attempt_placement(_trackDesign.get(), mapX, mapY, mapZ, 1, &cost, &rideIndex); gDisableErrorWindowSound = false; if (cost != MONEY32_UNDEFINED) { - window_close_by_class(WC_ERROR); - audio_play_sound_at_location(SOUND_PLACE_ITEM, mapX, mapY, mapZ); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + window_close_by_class(WC_ERROR); + audio_play_sound_at_location(SoundId::PlaceItem, { mapX, mapY, mapZ }); - _currentRideIndex = rideIndex; - if (track_design_are_entrance_and_exit_placed()) - { - auto intent = Intent(WC_RIDE); - intent.putExtra(INTENT_EXTRA_RIDE_ID, rideIndex); - context_open_intent(&intent); - window_close(w); - } - else - { - ride_initialise_construction_window(get_ride(rideIndex)); - w = window_find_by_class(WC_RIDE_CONSTRUCTION); - window_event_mouse_up_call(w, WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE); + _currentRideIndex = rideIndex; + if (track_design_are_entrance_and_exit_placed()) + { + auto intent = Intent(WC_RIDE); + intent.putExtra(INTENT_EXTRA_RIDE_ID, rideIndex); + context_open_intent(&intent); + window_close(w); + } + else + { + ride_initialise_construction_window(ride); + w = window_find_by_class(WC_RIDE_CONSTRUCTION); + window_event_mouse_up_call(w, WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE); + } } return; } @@ -368,7 +367,7 @@ static void window_track_place_tooldown(rct_window* w, rct_widgetindex widgetInd } // Unable to build track - audio_play_sound_at_location(SOUND_ERROR, mapX, mapY, mapZ); + audio_play_sound_at_location(SoundId::Error, { mapX, mapY, mapZ }); } /** @@ -386,12 +385,12 @@ static void window_track_place_toolabort(rct_window* w, rct_widgetindex widgetIn */ static void window_track_place_unknown14(rct_window* w) { - window_track_place_draw_mini_preview(_trackDesign); + window_track_place_draw_mini_preview(_trackDesign.get()); } static void window_track_place_invalidate(rct_window* w) { - window_track_place_draw_mini_preview(_trackDesign); + window_track_place_draw_mini_preview(_trackDesign.get()); } /** @@ -403,10 +402,13 @@ static void window_track_place_clear_provisional() if (_window_track_place_last_was_valid) { auto ride = get_ride(_window_track_place_ride_index); - place_virtual_track( - _trackDesign, PTD_OPERATION_CLEAR_OUTLINES, true, ride, _window_track_place_last_valid_x, - _window_track_place_last_valid_y, _window_track_place_last_valid_z); - _window_track_place_last_was_valid = false; + if (ride != nullptr) + { + place_virtual_track( + _trackDesign.get(), PTD_OPERATION_REMOVE_GHOST, true, ride, _window_track_place_last_valid_x, + _window_track_place_last_valid_y, _window_track_place_last_valid_z); + _window_track_place_last_was_valid = false; + } } } @@ -416,31 +418,30 @@ static void window_track_place_clear_provisional() */ static int32_t window_track_place_get_base_z(int32_t x, int32_t y) { - TileElement* tileElement; uint32_t z; - tileElement = map_get_surface_element_at(x >> 5, y >> 5); - z = tileElement->base_height * 8; + auto surfaceElement = map_get_surface_element_at(x >> 5, y >> 5); + z = surfaceElement->base_height * 8; // Increase Z above slope - if (tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) { z += 16; // Increase Z above double slope - if (tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) z += 16; } // Increase Z above water - if (tileElement->AsSurface()->GetWaterHeight() > 0) - z = std::max(z, tileElement->AsSurface()->GetWaterHeight() << 4); + if (surfaceElement->GetWaterHeight() > 0) + z = std::max(z, surfaceElement->GetWaterHeight() << 4); - return z + place_virtual_track(_trackDesign, PTD_OPERATION_GET_PLACE_Z, true, get_ride(0), x, y, z); + return z + place_virtual_track(_trackDesign.get(), PTD_OPERATION_GET_PLACE_Z, true, GetOrAllocateRide(0), x, y, z); } static void window_track_place_attempt_placement( - rct_track_td6* td6, int32_t x, int32_t y, int32_t z, int32_t bl, money32* cost, ride_id_t* rideIndex) + TrackDesign* td6, int32_t x, int32_t y, int32_t z, int32_t bl, money32* cost, ride_id_t* rideIndex) { int32_t eax, ebx, ecx, edx, esi, edi, ebp; money32 result; @@ -451,7 +452,7 @@ static void window_track_place_attempt_placement( ecx = y; edi = z; - gActiveTrackDesign = _trackDesign; + gActiveTrackDesign = _trackDesign.get(); result = game_do_command_p(GAME_COMMAND_PLACE_TRACK_DESIGN, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); gActiveTrackDesign = nullptr; @@ -467,7 +468,7 @@ static void window_track_place_attempt_placement( */ static void window_track_place_paint(rct_window* w, rct_drawpixelinfo* dpi) { - set_format_arg(0, char*, _trackDesign->name); + set_format_arg(0, char*, _trackDesign->name.c_str()); window_draw_widgets(w, dpi); // Draw mini tile preview @@ -493,7 +494,7 @@ static void window_track_place_paint(rct_window* w, rct_drawpixelinfo* dpi) * * rct2: 0x006D1845 */ -static void window_track_place_draw_mini_preview(rct_track_td6* td6) +static void window_track_place_draw_mini_preview(TrackDesign* td6) { window_track_place_clear_mini_preview(); @@ -521,19 +522,18 @@ static void window_track_place_draw_mini_preview(rct_track_td6* td6) } static void window_track_place_draw_mini_preview_track( - rct_track_td6* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max) + TrackDesign* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max) { uint8_t rotation = (_currentTrackPieceDirection + get_current_rotation()) & 3; - rct_td6_track_element* trackElement = td6->track_elements; const rct_preview_track** trackBlockArray = (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_TRACK)) ? TrackBlocks : FlatRideTrackBlocks; - while (trackElement->type != 255) + for (const auto& trackElement : td6->track_elements) { - int32_t trackType = trackElement->type; - if (trackType == TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP) + int32_t trackType = trackElement.type; + if (trackType == TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP_ALIAS) { - trackType = 255; + trackType = TRACK_ELEM_MULTIDIM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP; } // Follow a single track piece shape @@ -598,16 +598,14 @@ static void window_track_place_draw_mini_preview_track( origin.x += CoordsDirectionDelta[rotation].x; origin.y += CoordsDirectionDelta[rotation].y; } - trackElement++; } // Draw entrance and exit preview. - rct_td6_entrance_element* entrance = td6->entrance_elements; - for (; entrance->z != -1; entrance++) + for (const auto& entrance : td6->entrance_elements) { int16_t x = origin.x; int16_t y = origin.y; - map_offset_with_rotation(&x, &y, entrance->x, entrance->y, rotation); + map_offset_with_rotation(&x, &y, entrance.x, entrance.y, rotation); if (pass == 0) { @@ -622,14 +620,7 @@ static void window_track_place_draw_mini_preview_track( if (draw_mini_preview_is_pixel_in_bounds(pixelPosition)) { uint8_t* pixel = draw_mini_preview_get_pixel_ptr(pixelPosition); - - bool isExit = false; - if (entrance->direction & (1 << 7)) - { - isExit = true; - } - - uint8_t colour = isExit ? _PaletteIndexColourExit : _PaletteIndexColourEntrance; + uint8_t colour = entrance.isExit ? _PaletteIndexColourExit : _PaletteIndexColourEntrance; for (int32_t i = 0; i < 4; i++) { pixel[338 + i] = colour; // x + 2, y + 2 @@ -643,14 +634,13 @@ static void window_track_place_draw_mini_preview_track( } static void window_track_place_draw_mini_preview_maze( - rct_track_td6* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max) + TrackDesign* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max) { uint8_t rotation = (_currentTrackPieceDirection + get_current_rotation()) & 3; - rct_td6_maze_element* mazeElement = td6->maze_elements; - while (mazeElement->all != 0) + for (const auto& mazeElement : td6->maze_elements) { - int16_t x = mazeElement->x * 32; - int16_t y = mazeElement->y * 32; + int16_t x = mazeElement.x * 32; + int16_t y = mazeElement.y * 32; rotate_map_coordinates(&x, &y, rotation); x += origin.x; @@ -673,9 +663,9 @@ static void window_track_place_draw_mini_preview_maze( uint8_t colour = _PaletteIndexColourTrack; // Draw entrance and exit with different colours. - if (mazeElement->type == MAZE_ELEMENT_TYPE_ENTRANCE) + if (mazeElement.type == MAZE_ELEMENT_TYPE_ENTRANCE) colour = _PaletteIndexColourEntrance; - else if (mazeElement->type == MAZE_ELEMENT_TYPE_EXIT) + else if (mazeElement.type == MAZE_ELEMENT_TYPE_EXIT) colour = _PaletteIndexColourExit; for (int32_t i = 0; i < 4; i++) @@ -687,7 +677,6 @@ static void window_track_place_draw_mini_preview_maze( } } } - mazeElement++; } } diff --git a/src/openrct2-ui/windows/TrackList.cpp b/src/openrct2-ui/windows/TrackList.cpp index b81e0111da..1cf6d4d08d 100644 --- a/src/openrct2-ui/windows/TrackList.cpp +++ b/src/openrct2-ui/windows/TrackList.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -105,7 +105,7 @@ static std::vector _trackDesigns; static utf8 _filterString[USER_STRING_MAX_LENGTH]; static std::vector _filteredTrackIds; static uint16_t _loadedTrackDesignIndex; -static rct_track_td6* _loadedTrackDesign; +static std::unique_ptr _loadedTrackDesign; static std::vector _trackDesignPreviewPixels; static void track_list_load_designs(ride_list_item item); @@ -137,7 +137,7 @@ rct_window* window_track_list_open(ride_list_item item) y = TOP_TOOLBAR_HEIGHT + 2; } - rct_window* w = window_create(x, y, 600, 432, &window_track_list_events, WC_TRACK_DESIGN_LIST, 0); + rct_window* w = window_create(ScreenCoordsXY(x, y), 600, 432, &window_track_list_events, WC_TRACK_DESIGN_LIST, 0); window_track_list_widgets[WIDX_FILTER_STRING].string = _filterString; w->widgets = window_track_list_widgets; @@ -208,7 +208,6 @@ static void window_track_list_filter_list() static void window_track_list_close(rct_window* w) { // Dispose track design and preview - track_design_dispose(_loadedTrackDesign); _loadedTrackDesign = nullptr; _trackDesignPreviewPixels.clear(); _trackDesignPreviewPixels.shrink_to_fit(); @@ -247,7 +246,7 @@ static void window_track_list_select(rct_window* w, int32_t listIndex) return; } - audio_play_sound(SOUND_CLICK_1, 0, w->x + (w->width / 2)); + audio_play_sound(SoundId::Click1, 0, w->x + (w->width / 2)); if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) { if (listIndex == 0) @@ -259,8 +258,7 @@ static void window_track_list_select(rct_window* w, int32_t listIndex) listIndex--; } - rct_track_td6* td6 = _loadedTrackDesign; - if (td6 != nullptr && (td6->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE)) + if (_loadedTrackDesign && (_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE)) { gTrackDesignSceneryToggle = true; } @@ -318,12 +316,12 @@ static void window_track_list_mouseup(rct_window* w, rct_widgetindex widgetIndex case WIDX_ROTATE: _currentTrackPieceDirection++; _currentTrackPieceDirection %= 4; - window_invalidate(w); + w->Invalidate(); break; case WIDX_TOGGLE_SCENERY: gTrackDesignSceneryToggle = !gTrackDesignSceneryToggle; _loadedTrackDesignIndex = TRACK_DESIGN_INDEX_UNLOADED; - window_invalidate(w); + w->Invalidate(); break; case WIDX_BACK: window_close(w); @@ -349,7 +347,7 @@ static void window_track_list_mouseup(rct_window* w, rct_widgetindex widgetIndex String::Set(_filterString, sizeof(_filterString), ""); window_track_list_filter_list(); - window_invalidate(w); + w->Invalidate(); break; } } @@ -398,7 +396,7 @@ static void window_track_list_scrollmouseover(rct_window* w, int32_t scrollIndex if (i != -1 && w->selected_list_item != i) { w->selected_list_item = i; - window_invalidate(w); + w->Invalidate(); } } } @@ -417,7 +415,7 @@ static void window_track_list_textinput(rct_window* w, rct_widgetindex widgetInd w->scrolls->v_top = 0; - window_invalidate(w); + w->Invalidate(); } static void window_track_list_update(rct_window* w) @@ -432,7 +430,7 @@ static void window_track_list_update(rct_window* w) { track_list_load_designs(_window_track_list_item); w->selected_list_item = 0; - window_invalidate(w); + w->Invalidate(); w->track_list.reload_track_designs = false; } } @@ -499,7 +497,7 @@ static void window_track_list_paint(rct_window* w, rct_drawpixelinfo* dpi) int32_t listItemIndex = w->selected_list_item; if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { - if (_trackDesigns.size() == 0 || listItemIndex == -1) + if (_trackDesigns.empty() || listItemIndex == -1) return; } else @@ -534,8 +532,7 @@ static void window_track_list_paint(rct_window* w, rct_drawpixelinfo* dpi) } } - rct_track_td6* td6 = _loadedTrackDesign; - if (td6 == nullptr) + if (!_loadedTrackDesign) { return; } @@ -544,32 +541,27 @@ static void window_track_list_paint(rct_window* w, rct_drawpixelinfo* dpi) x = w->x + (widget->left + widget->right) / 2; y = w->y + (widget->top + widget->bottom) / 2; - if (drawing_engine_get_type() != DRAWING_ENGINE_OPENGL) - { - rct_g1_element g1temp = {}; - g1temp.offset = _trackDesignPreviewPixels.data() + (_currentTrackPieceDirection * TRACK_PREVIEW_IMAGE_SIZE); - g1temp.width = 370; - g1temp.height = 217; - g1temp.flags = G1_FLAG_BMP; - gfx_set_g1_element(SPR_TEMP, &g1temp); - gfx_draw_sprite(dpi, SPR_TEMP, trackPreviewX, trackPreviewY, 0); - } - else - { - gfx_draw_string_centred_clipped(dpi, STR_NOT_SUPPPORTED_IN_OPENGL, nullptr, COLOUR_BLACK, x, y, 368); - } + rct_g1_element g1temp = {}; + g1temp.offset = _trackDesignPreviewPixels.data() + (_currentTrackPieceDirection * TRACK_PREVIEW_IMAGE_SIZE); + g1temp.width = 370; + g1temp.height = 217; + g1temp.flags = G1_FLAG_BMP; + gfx_set_g1_element(SPR_TEMP, &g1temp); + drawing_engine_invalidate_image(SPR_TEMP); + gfx_draw_sprite(dpi, SPR_TEMP, trackPreviewX, trackPreviewY, 0); y = w->y + widget->bottom - 12; // Warnings - if ((td6->track_flags & TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE) && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) + if ((_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE) + && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) { // Vehicle design not available gfx_draw_string_centred_clipped(dpi, STR_VEHICLE_DESIGN_UNAVAILABLE, nullptr, COLOUR_BLACK, x, y, 368); y -= SCROLLABLE_ROW_HEIGHT; } - if (td6->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) + if (_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) { if (!gTrackDesignSceneryToggle) { @@ -589,96 +581,92 @@ static void window_track_list_paint(rct_window* w, rct_drawpixelinfo* dpi) y = w->y + widget->bottom + 2; // Stats - fixed32_2dp rating = td6->excitement * 10; + fixed32_2dp rating = _loadedTrackDesign->excitement * 10; gfx_draw_string_left(dpi, STR_TRACK_LIST_EXCITEMENT_RATING, &rating, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; - rating = td6->intensity * 10; + rating = _loadedTrackDesign->intensity * 10; gfx_draw_string_left(dpi, STR_TRACK_LIST_INTENSITY_RATING, &rating, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; - rating = td6->nausea * 10; + rating = _loadedTrackDesign->nausea * 10; gfx_draw_string_left(dpi, STR_TRACK_LIST_NAUSEA_RATING, &rating, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT + 4; // Information for tracked rides. - if (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_TRACK)) + if (ride_type_has_flag(_loadedTrackDesign->type, RIDE_TYPE_FLAG_HAS_TRACK)) { - if (td6->type != RIDE_TYPE_MAZE) + if (_loadedTrackDesign->type != RIDE_TYPE_MAZE) { - if (td6->type == RIDE_TYPE_MINI_GOLF) + if (_loadedTrackDesign->type == RIDE_TYPE_MINI_GOLF) { // Holes - uint16_t holes = td6->holes & 0x1F; + uint16_t holes = _loadedTrackDesign->holes & 0x1F; gfx_draw_string_left(dpi, STR_HOLES, &holes, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } else { // Maximum speed - uint16_t speed = ((td6->max_speed << 16) * 9) >> 18; + uint16_t speed = ((_loadedTrackDesign->max_speed << 16) * 9) >> 18; gfx_draw_string_left(dpi, STR_MAX_SPEED, &speed, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Average speed - speed = ((td6->average_speed << 16) * 9) >> 18; + speed = ((_loadedTrackDesign->average_speed << 16) * 9) >> 18; gfx_draw_string_left(dpi, STR_AVERAGE_SPEED, &speed, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } // Ride length set_format_arg(0, rct_string_id, STR_RIDE_LENGTH_ENTRY); - set_format_arg(2, uint16_t, td6->ride_length); + set_format_arg(2, uint16_t, _loadedTrackDesign->ride_length); gfx_draw_string_left_clipped(dpi, STR_TRACK_LIST_RIDE_LENGTH, gCommonFormatArgs, COLOUR_BLACK, x, y, 214); y += LIST_ROW_HEIGHT; } - if (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) + if (ride_type_has_flag(_loadedTrackDesign->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) { // Maximum positive vertical Gs - int32_t gForces = td6->max_positive_vertical_g * 32; + int32_t gForces = _loadedTrackDesign->max_positive_vertical_g * 32; gfx_draw_string_left(dpi, STR_MAX_POSITIVE_VERTICAL_G, &gForces, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Maximum negative vertical Gs - gForces = td6->max_negative_vertical_g * 32; + gForces = _loadedTrackDesign->max_negative_vertical_g * 32; gfx_draw_string_left(dpi, STR_MAX_NEGATIVE_VERTICAL_G, &gForces, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Maximum lateral Gs - gForces = td6->max_lateral_g * 32; + gForces = _loadedTrackDesign->max_lateral_g * 32; gfx_draw_string_left(dpi, STR_MAX_LATERAL_G, &gForces, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; - // If .TD6 - if (td6->version_and_colour_scheme / 4 >= 2) + if (_loadedTrackDesign->total_air_time != 0) { - if (td6->total_air_time != 0) - { - // Total air time - int32_t airTime = td6->total_air_time * 25; - gfx_draw_string_left(dpi, STR_TOTAL_AIR_TIME, &airTime, COLOUR_BLACK, x, y); - y += LIST_ROW_HEIGHT; - } + // Total air time + int32_t airTime = _loadedTrackDesign->total_air_time * 25; + gfx_draw_string_left(dpi, STR_TOTAL_AIR_TIME, &airTime, COLOUR_BLACK, x, y); + y += LIST_ROW_HEIGHT; } } - if (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_DROPS)) + if (ride_type_has_flag(_loadedTrackDesign->type, RIDE_TYPE_FLAG_HAS_DROPS)) { // Drops - uint16_t drops = td6->drops & 0x3F; + uint16_t drops = _loadedTrackDesign->drops & 0x3F; gfx_draw_string_left(dpi, STR_DROPS, &drops, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Drop height is multiplied by 0.75 - uint16_t highestDropHeight = (td6->highest_drop_height * 3) / 4; + uint16_t highestDropHeight = (_loadedTrackDesign->highest_drop_height * 3) / 4; gfx_draw_string_left(dpi, STR_HIGHEST_DROP_HEIGHT, &highestDropHeight, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } - if (td6->type != RIDE_TYPE_MINI_GOLF) + if (_loadedTrackDesign->type != RIDE_TYPE_MINI_GOLF) { - uint16_t inversions = td6->inversions & 0x1F; + uint16_t inversions = _loadedTrackDesign->inversions & 0x1F; if (inversions != 0) { // Inversions @@ -689,18 +677,19 @@ static void window_track_list_paint(rct_window* w, rct_drawpixelinfo* dpi) y += 4; } - if (td6->space_required_x != 0xFF) + if (_loadedTrackDesign->space_required_x != 0xFF) { // Space required - set_format_arg(0, uint16_t, td6->space_required_x); - set_format_arg(2, uint16_t, td6->space_required_y); + set_format_arg(0, uint16_t, _loadedTrackDesign->space_required_x); + set_format_arg(2, uint16_t, _loadedTrackDesign->space_required_y); gfx_draw_string_left(dpi, STR_TRACK_LIST_SPACE_REQUIRED, gCommonFormatArgs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } - if (td6->cost != 0) + if (_loadedTrackDesign->cost != 0) { - gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, &td6->cost, COLOUR_BLACK, x, y); + set_format_arg(0, uint32_t, _loadedTrackDesign->cost); + gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, gCommonFormatArgs, COLOUR_BLACK, x, y); } } @@ -718,7 +707,7 @@ static void window_track_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, size_t listIndex = 0; if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { - if (_trackDesigns.size() == 0) + if (_trackDesigns.empty()) { // No track designs gfx_draw_string_left(dpi, STR_NO_TRACK_DESIGNS_OF_THIS_TYPE, nullptr, COLOUR_BLACK, x, y - 1); @@ -776,26 +765,29 @@ static void window_track_list_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, static void track_list_load_designs(ride_list_item item) { auto repo = OpenRCT2::GetContext()->GetTrackDesignRepository(); - if (!RideGroupManager::RideTypeHasRideGroups(item.type)) + if (RideGroupManager::RideTypeHasRideGroups(item.type)) { - char entry[9]; - const char* entryPtr = nullptr; + auto rideEntry = get_ride_entry(item.entry_index); + if (rideEntry != nullptr) + { + auto rideGroup = RideGroupManager::GetRideGroup(item.type, rideEntry); + if (rideGroup != nullptr) + { + _trackDesigns = repo->GetItemsForRideGroup(item.type, rideGroup); + } + } + } + else + { + std::string entryName; if (item.type < 0x80) { if (RideGroupManager::RideTypeIsIndependent(item.type)) { - get_ride_entry_name(entry, item.entry_index); - entryPtr = entry; + entryName = get_ride_entry_name(item.entry_index); } } - - _trackDesigns = repo->GetItemsForObjectEntry(item.type, String::ToStd(entryPtr)); - } - else - { - auto rideEntry = get_ride_entry(item.entry_index); - auto rideGroup = RideGroupManager::GetRideGroup(item.type, rideEntry); - _trackDesigns = repo->GetItemsForRideGroup(item.type, rideGroup); + _trackDesigns = repo->GetItemsForObjectEntry(item.type, entryName); } window_track_list_filter_list(); @@ -803,17 +795,10 @@ static void track_list_load_designs(ride_list_item item) static bool track_list_load_design_for_preview(utf8* path) { - // Dispose currently loaded track - track_design_dispose(_loadedTrackDesign); - _loadedTrackDesign = nullptr; - _loadedTrackDesign = track_design_open(path); if (_loadedTrackDesign != nullptr) { - if (drawing_engine_get_type() != DRAWING_ENGINE_OPENGL) - { - track_design_draw_preview(_loadedTrackDesign, _trackDesignPreviewPixels.data()); - } + track_design_draw_preview(_loadedTrackDesign.get(), _trackDesignPreviewPixels.data()); return true; } return false; diff --git a/src/openrct2-ui/windows/ViewClipping.cpp b/src/openrct2-ui/windows/ViewClipping.cpp index 0a461e1c74..db02e8e94b 100644 --- a/src/openrct2-ui/windows/ViewClipping.cpp +++ b/src/openrct2-ui/windows/ViewClipping.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -147,7 +147,7 @@ rct_window* window_view_clipping_open() } // Window is not open - create it. - window = window_create(32, 32, WW, WH, &window_view_clipping_events, WC_VIEW_CLIPPING, 0); + window = window_create(ScreenCoordsXY(32, 32), WW, WH, &window_view_clipping_events, WC_VIEW_CLIPPING, 0); window->widgets = window_view_clipping_widgets; window->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_CLIP_CHECKBOX_ENABLE) | (1ULL << WIDX_CLIP_HEIGHT_VALUE) | (1ULL << WIDX_CLIP_HEIGHT_INCREASE) | (1ULL << WIDX_CLIP_HEIGHT_DECREASE) | (1ULL << WIDX_CLIP_HEIGHT_SLIDER) @@ -165,7 +165,7 @@ rct_window* window_view_clipping_open() if (mainWindow != nullptr) { mainWindow->viewport->flags |= VIEWPORT_FLAG_CLIP_VIEW; - window_invalidate(mainWindow); + mainWindow->Invalidate(); } _toolActive = false; @@ -181,7 +181,7 @@ static void window_view_clipping_close() if (mainWindow != nullptr) { mainWindow->viewport->flags &= ~VIEWPORT_FLAG_CLIP_VIEW; - window_invalidate(mainWindow); + mainWindow->Invalidate(); } } @@ -216,9 +216,9 @@ static void window_view_clipping_mouseup(rct_window* w, rct_widgetindex widgetIn if (mainWindow != nullptr) { mainWindow->viewport->flags ^= VIEWPORT_FLAG_CLIP_VIEW; - window_invalidate(mainWindow); + mainWindow->Invalidate(); } - window_invalidate(w); + w->Invalidate(); break; case WIDX_CLIP_HEIGHT_VALUE: // Toggle display of the cut height value in RAW vs UNITS @@ -230,7 +230,7 @@ static void window_view_clipping_mouseup(rct_window* w, rct_widgetindex widgetIn { gClipHeightDisplayType = DISPLAY_TYPE::DISPLAY_RAW; } - window_invalidate(w); + w->Invalidate(); break; case WIDX_CLIP_SELECTOR: // Activate the selection tool @@ -269,14 +269,14 @@ static void window_view_clipping_mousedown(rct_window* w, rct_widgetindex widget window_view_clipping_set_clipheight(w, gClipHeight + 1); mainWindow = window_get_main(); if (mainWindow != nullptr) - window_invalidate(mainWindow); + mainWindow->Invalidate(); break; case WIDX_CLIP_HEIGHT_DECREASE: if (gClipHeight > 0) window_view_clipping_set_clipheight(w, gClipHeight - 1); mainWindow = window_get_main(); if (mainWindow != nullptr) - window_invalidate(mainWindow); + mainWindow->Invalidate(); break; } } @@ -295,7 +295,7 @@ static void window_view_clipping_update(rct_window* w) rct_window* mainWindow = window_get_main(); if (mainWindow != nullptr) { - window_invalidate(mainWindow); + mainWindow->Invalidate(); } } diff --git a/src/openrct2-ui/windows/Viewport.cpp b/src/openrct2-ui/windows/Viewport.cpp index dd6d7c96dc..33a4af79dc 100644 --- a/src/openrct2-ui/windows/Viewport.cpp +++ b/src/openrct2-ui/windows/Viewport.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -135,14 +135,14 @@ static void window_viewport_mouseup(rct_window* w, rct_widgetindex widgetIndex) if (w->viewport != nullptr && w->viewport->zoom > 0) { w->viewport->zoom--; - window_invalidate(w); + w->Invalidate(); } break; case WIDX_ZOOM_OUT: if (w->viewport != nullptr && w->viewport->zoom < 3) { w->viewport->zoom++; - window_invalidate(w); + w->Invalidate(); } break; case WIDX_LOCATE: @@ -152,7 +152,7 @@ static void window_viewport_mouseup(rct_window* w, rct_widgetindex widgetIndex) get_map_coordinates_from_pos( w->x + (w->width / 2), w->y + (w->height / 2), VIEWPORT_INTERACTION_MASK_NONE, &x, &y, nullptr, nullptr, nullptr); - window_scroll_to_location(mainWindow, x, y, tile_element_height(x, y)); + window_scroll_to_location(mainWindow, x, y, tile_element_height({ x, y })); } break; } @@ -175,7 +175,7 @@ static void window_viewport_update(rct_window* w) if (w->viewport->flags != mainWindow->viewport->flags) { w->viewport->flags = mainWindow->viewport->flags; - window_invalidate(w); + w->Invalidate(); } // Not sure how to invalidate part of the viewport that has changed, this will have to do for now diff --git a/src/openrct2-ui/windows/Water.cpp b/src/openrct2-ui/windows/Water.cpp index aaaf504ddd..80cb127849 100644 --- a/src/openrct2-ui/windows/Water.cpp +++ b/src/openrct2-ui/windows/Water.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,6 +14,7 @@ #include #include #include +#include // clang-format off enum WINDOW_WATER_WIDGET_IDX { @@ -89,7 +90,7 @@ rct_window* window_water_open() if (window != nullptr) return window; - window = window_create(context_get_width() - 76, 29, 76, 77, &window_water_events, WC_WATER, 0); + window = window_create(ScreenCoordsXY(context_get_width() - 76, 29), 76, 77, &window_water_events, WC_WATER, 0); window->widgets = window_water_widgets; window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_DECREMENT) | (1 << WIDX_INCREMENT) | (1 << WIDX_PREVIEW); window->hold_down_widgets = (1 << WIDX_INCREMENT) | (1 << WIDX_DECREMENT); @@ -140,14 +141,14 @@ static void window_water_mousedown(rct_window* w, rct_widgetindex widgetIndex, r gLandToolSize = std::max(MINIMUM_TOOL_SIZE, gLandToolSize - 1); // Invalidate the window - window_invalidate(w); + w->Invalidate(); break; case WIDX_INCREMENT: // Increment land tool size gLandToolSize = std::min(MAXIMUM_TOOL_SIZE, gLandToolSize + 1); // Invalidate the window - window_invalidate(w); + w->Invalidate(); break; } } @@ -167,7 +168,7 @@ static void window_water_textinput(rct_window* w, rct_widgetindex widgetIndex, c size = std::min(MAXIMUM_TOOL_SIZE, size); gLandToolSize = size; - window_invalidate(w); + w->Invalidate(); } } @@ -220,14 +221,17 @@ static void window_water_paint(rct_window* w, rct_drawpixelinfo* dpi) gfx_draw_string_centred(dpi, STR_LAND_TOOL_SIZE_VALUE, x, y - 2, COLOUR_BLACK, &gLandToolSize); } - // Draw raise cost amount - x = (window_water_widgets[WIDX_PREVIEW].left + window_water_widgets[WIDX_PREVIEW].right) / 2 + w->x; - y = window_water_widgets[WIDX_PREVIEW].bottom + w->y + 5; - if (gWaterToolRaiseCost != MONEY32_UNDEFINED && gWaterToolRaiseCost != 0) - gfx_draw_string_centred(dpi, STR_RAISE_COST_AMOUNT, x, y, COLOUR_BLACK, &gWaterToolRaiseCost); - y += 10; + if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) + { + // Draw raise cost amount + x = (window_water_widgets[WIDX_PREVIEW].left + window_water_widgets[WIDX_PREVIEW].right) / 2 + w->x; + y = window_water_widgets[WIDX_PREVIEW].bottom + w->y + 5; + if (gWaterToolRaiseCost != MONEY32_UNDEFINED && gWaterToolRaiseCost != 0) + gfx_draw_string_centred(dpi, STR_RAISE_COST_AMOUNT, x, y, COLOUR_BLACK, &gWaterToolRaiseCost); + y += 10; - // Draw lower cost amount - if (gWaterToolLowerCost != MONEY32_UNDEFINED && gWaterToolLowerCost != 0) - gfx_draw_string_centred(dpi, STR_LOWER_COST_AMOUNT, x, y, COLOUR_BLACK, &gWaterToolLowerCost); + // Draw lower cost amount + if (gWaterToolLowerCost != MONEY32_UNDEFINED && gWaterToolLowerCost != 0) + gfx_draw_string_centred(dpi, STR_LOWER_COST_AMOUNT, x, y, COLOUR_BLACK, &gWaterToolLowerCost); + } } diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h index 85bfb918a5..bd7e2a815a 100644 --- a/src/openrct2-ui/windows/Window.h +++ b/src/openrct2-ui/windows/Window.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -43,8 +43,10 @@ rct_window* window_news_open(); rct_window* window_news_options_open(); rct_window* window_options_open(); rct_window* window_save_prompt_open(); +#ifndef DISABLE_NETWORK rct_window* window_server_list_open(); rct_window* window_server_start_open(); +#endif rct_window* window_shortcut_change_open(int32_t selected_key); rct_window* window_shortcut_keys_open(); rct_window* window_staff_list_open(); @@ -88,8 +90,8 @@ void window_title_command_editor_open(struct TitleSequence* sequence, int32_t co rct_window* window_scenarioselect_open(scenarioselect_callback callback, bool titleEditor); rct_window* window_error_open(rct_string_id title, rct_string_id message); - -rct_window* window_loadsave_open(int32_t type, const char* defaultName, loadsave_callback callback); +struct TrackDesign; +rct_window* window_loadsave_open(int32_t type, const char* defaultName, loadsave_callback callback, TrackDesign* t6Exporter); rct_window* window_track_place_open(const struct track_design_file_ref* tdFileRef); rct_window* window_track_manage_open(struct track_design_file_ref* tdFileRef); @@ -162,7 +164,7 @@ void window_tile_inspector_clear_clipboard(); rct_window* window_editor_object_selection_open(); -void window_tooltip_reset(int32_t x, int32_t y); -void window_tooltip_show(rct_string_id id, int32_t x, int32_t y); -void window_tooltip_open(rct_window* widgetWindow, rct_widgetindex widgetIndex, int32_t x, int32_t y); +void window_tooltip_reset(ScreenCoordsXY screenCoords); +void window_tooltip_show(rct_string_id id, ScreenCoordsXY screenCoords); +void window_tooltip_open(rct_window* widgetWindow, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); void window_tooltip_close(); diff --git a/src/openrct2-win/openrct2-win.cpp b/src/openrct2-win/openrct2-win.cpp index 73ed66dda9..1d7f2da57d 100644 --- a/src/openrct2-win/openrct2-win.cpp +++ b/src/openrct2-win/openrct2-win.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/CMakeLists.txt b/src/openrct2/CMakeLists.txt index 157120fa15..61be3d13c2 100644 --- a/src/openrct2/CMakeLists.txt +++ b/src/openrct2/CMakeLists.txt @@ -1,17 +1,92 @@ -# CMAKE project for libopenrct2 (core OpenRCT2 component) cmake_minimum_required(VERSION 3.9) +project(libopenrct2 CXX) + if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR) message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt") endif () -# Needed for linking with non-broken OpenSSL on Apple platforms +file(GLOB_RECURSE OPENRCT2_CORE_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.cpp") +file(GLOB_RECURSE OPENRCT2_CORE_HEADERS "${CMAKE_CURRENT_LIST_DIR}/*.h" + "${CMAKE_CURRENT_LIST_DIR}/*.hpp") if (APPLE) - set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/opt/openssl/lib/pkgconfig") + file(GLOB_RECURSE OPENRCT2_CORE_MM_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.mm") + set_source_files_properties(${OPENRCT2_CORE_MM_SOURCES} PROPERTIES COMPILE_FLAGS "-x objective-c++ -fmodules") +endif () + +add_library(${PROJECT_NAME} ${OPENRCT2_CORE_SOURCES} ${OPENRCT2_CORE_MM_SOURCES} ${RCT2_SECTIONS}) +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") +SET_CHECK_CXX_FLAGS(${PROJECT_NAME}) + +if (NOT DISABLE_NETWORK OR NOT DISABLE_HTTP) + if (APPLE) + # Needed for linking with non-broken OpenSSL on Apple platforms + set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/opt/openssl/lib/pkgconfig") + endif () + + find_package(OpenSSL 1.0.0 REQUIRED) + + target_include_directories(${PROJECT_NAME} PUBLIC ${OPENSSL_INCLUDE_DIR}) + + if(STATIC) + target_link_libraries(${PROJECT_NAME} ${SSL_STATIC_LIBRARIES}) + else () + target_link_libraries(${PROJECT_NAME} ${OPENSSL_LIBRARIES}) + endif() +endif () + +if (NOT DISABLE_NETWORK AND WIN32) + target_link_libraries(${PROJECT_NAME} ws2_32 crypt32 wldap32 version winmm imm32 advapi32 shell32 ole32) +endif () + +if (NOT DISABLE_HTTP) + if (MSVC) + find_package(curl REQUIRED) + set(LIBCURL_LIBRARIES ${CURL_LIBRARIES}) + else () + PKG_CHECK_MODULES(LIBCURL REQUIRED libcurl) + endif () + + target_include_directories(${PROJECT_NAME} PRIVATE ${LIBCURL_INCLUDE_DIRS}) + + if (STATIC) + target_link_libraries(${PROJECT_NAME} ${LIBCURL_STATIC_LIBRARIES}) + else () + target_link_libraries(${PROJECT_NAME} ${LIBCURL_LIBRARIES}) + endif () +endif () + +if (NOT DISABLE_TTF) + if (UNIX AND NOT APPLE AND NOT MSVC) + PKG_CHECK_MODULES(FONTCONFIG REQUIRED fontconfig) + endif () + + if (MSVC) + find_package(freetype REQUIRED) + else () + PKG_CHECK_MODULES(FREETYPE REQUIRED freetype2) + endif () + + target_include_directories(${PROJECT_NAME} PRIVATE ${FREETYPE_INCLUDE_DIRS}) + + if (UNIX AND NOT APPLE) + target_include_directories(${PROJECT_NAME} PRIVATE ${FONTCONFIG_INCLUDE_DIRS}) + endif () +endif () + +find_package(benchmark 1.4 QUIET) +if (benchmark_FOUND) + message("Found Google benchmark, enabling support") + set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_DEFINITIONS USE_BENCHMARK) + target_link_libraries(${PROJECT_NAME} benchmark::benchmark) + target_include_directories(${PROJECT_NAME} PRIVATE ${benchmark_INCLUDE_DIRS}) +else () + message("Google benchmark not found, disabling support") endif () # Third party libraries if (MSVC) - find_package(jansson 2.5 REQUIRED) + find_package(jansson CONFIG REQUIRED) + set(JANSSON_LIBRARIES "jansson::jansson") find_package(png 1.6 REQUIRED) find_package(zlib REQUIRED) @@ -34,65 +109,13 @@ else () endif () endif () -# Third party libraries (optional) -if (NOT DISABLE_HTTP_TWITCH OR NOT DISABLE_NETWORK) - if (MSVC) - find_package(curl REQUIRED) - set(LIBCURL_LIBRARIES ${CURL_LIBRARIES}) - else () - PKG_CHECK_MODULES(LIBCURL REQUIRED libcurl) - endif () -endif () -if (NOT DISABLE_NETWORK) - find_package(OpenSSL 1.0.0 REQUIRED) -endif () - -find_package(benchmark 1.4 QUIET) - -if (NOT DISABLE_TTF) - if (UNIX AND NOT APPLE AND NOT MSVC) - PKG_CHECK_MODULES(FONTCONFIG REQUIRED fontconfig) - endif () - if (MSVC) - find_package(freetype REQUIRED) - else () - PKG_CHECK_MODULES(FREETYPE REQUIRED freetype2) - endif () -endif () - -# Sources -file(GLOB_RECURSE OPENRCT2_CORE_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.cpp") -file(GLOB_RECURSE OPENRCT2_CORE_HEADERS "${CMAKE_CURRENT_LIST_DIR}/*.h" - "${CMAKE_CURRENT_LIST_DIR}/*.hpp") -if (APPLE) - file(GLOB_RECURSE OPENRCT2_CORE_MM_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.mm") - set_source_files_properties(${OPENRCT2_CORE_MM_SOURCES} PROPERTIES COMPILE_FLAGS "-x objective-c++ -fmodules") -endif () - -# Outputs -set(PROJECT libopenrct2) -project(${PROJECT} CXX) -add_library(${PROJECT} ${OPENRCT2_CORE_SOURCES} ${OPENRCT2_CORE_MM_SOURCES} ${RCT2_SECTIONS}) -set_target_properties(${PROJECT} PROPERTIES PREFIX "") -SET_CHECK_CXX_FLAGS(${PROJECT}) - -if (benchmark_FOUND) - message("Found Google benchmark, enabling support") - set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS USE_BENCHMARK) - target_link_libraries(${PROJECT} benchmark::benchmark) - target_include_directories(${PROJECT} PRIVATE ${benchmark_INCLUDE_DIRS}) -else () - message("Google benchmark not found, disabling support") -endif () - -# Libraries if (STATIC) - target_link_libraries(${PROJECT} ${JANSSON_STATIC_LIBRARIES} + target_link_libraries(${PROJECT_NAME} ${JANSSON_STATIC_LIBRARIES} ${PNG_STATIC_LIBRARIES} ${ZLIB_STATIC_LIBRARIES} ${LIBZIP_STATIC_LIBRARIES}) else () - target_link_libraries(${PROJECT} ${JANSSON_LIBRARIES} + target_link_libraries(${PROJECT_NAME} ${JANSSON_LIBRARIES} ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${LIBZIP_LIBRARIES}) @@ -100,35 +123,23 @@ endif () if (UNIX AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "BSD") # Include libdl for dlopen - target_link_libraries(${PROJECT} dl) + target_link_libraries(${PROJECT_NAME} dl) endif () -if (NOT DISABLE_NETWORK) - if (WIN32) - target_link_libraries(${PROJECT} ws2_32 crypt32 wldap32 version winmm imm32 advapi32 shell32 ole32) - endif () - - # our HTTP implementation requires use of threads - set(THREADS_PREFER_PTHREAD_FLAG ON) - find_package(Threads REQUIRED) - target_link_libraries(${PROJECT} Threads::Threads) - - if (STATIC) - target_link_libraries(${PROJECT} ${LIBCURL_STATIC_LIBRARIES} - ${SSL_STATIC_LIBRARIES}) - else () - target_link_libraries(${PROJECT} ${LIBCURL_LIBRARIES} - ${OPENSSL_LIBRARIES}) - endif () -endif () +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) +target_link_libraries(${PROJECT_NAME} Threads::Threads) if (NOT MINGW AND NOT MSVC) # For unicode code page conversion. find_package(ICU 59.0 REQUIRED COMPONENTS uc) + + target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE ${ICU_INCLUDE_DIRS}) + if (STATIC) - target_link_libraries(${PROJECT} ${ICU_STATIC_LIBRARIES}) + target_link_libraries(${PROJECT_NAME} ${ICU_STATIC_LIBRARIES}) else () - target_link_libraries(${PROJECT} ${ICU_LIBRARIES}) + target_link_libraries(${PROJECT_NAME} ${ICU_LIBRARIES}) endif () endif () @@ -136,19 +147,21 @@ if (NOT APPLE AND NOT MINGW AND NOT MSVC) # This is ugly hack to work around https://bugs.launchpad.net/ubuntu/+source/gcc-5/+bug/1568899. # Once C++17 is enabled (and thus old compilers are no longer supported, this needs to be gone. # We cannot simply detect the _compiler_ version, as the bug exists with the C++ _library_ - target_link_libraries(${PROJECT} gcc_s gcc) + target_link_libraries(${PROJECT_NAME} gcc_s gcc) endif () if (NOT DISABLE_TTF) if (STATIC) - target_link_libraries(${PROJECT} ${FREETYPE_STATIC_LIBRARIES}) + target_link_libraries(${PROJECT_NAME} ${FREETYPE_STATIC_LIBRARIES}) + if (UNIX AND NOT APPLE) - target_link_libraries(${PROJECT} ${FONTCONFIG_STATIC_LIBRARIES}) + target_link_libraries(${PROJECT_NAME} ${FONTCONFIG_STATIC_LIBRARIES}) endif () else () - target_link_libraries(${PROJECT} ${FREETYPE_LIBRARIES}) + target_link_libraries(${PROJECT_NAME} ${FREETYPE_LIBRARIES}) + if (UNIX AND NOT APPLE) - target_link_libraries(${PROJECT} ${FONTCONFIG_LIBRARIES}) + target_link_libraries(${PROJECT_NAME} ${FONTCONFIG_LIBRARIES}) endif () endif () endif () @@ -158,25 +171,10 @@ if (HAVE_DISCORD_RPC) endif() # Includes -target_include_directories(${PROJECT} PRIVATE ${LIBZIP_INCLUDE_DIRS}) -target_include_directories(${PROJECT} PUBLIC ${JANSSON_INCLUDE_DIRS}) -target_include_directories(${PROJECT} PRIVATE ${PNG_INCLUDE_DIRS} +target_include_directories(${PROJECT_NAME} PRIVATE ${LIBZIP_INCLUDE_DIRS}) +target_include_directories(${PROJECT_NAME} PUBLIC ${JANSSON_INCLUDE_DIRS}) +target_include_directories(${PROJECT_NAME} PRIVATE ${PNG_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}) -if (NOT DISABLE_HTTP_TWITCH OR NOT DISABLE_NETWORK) - target_include_directories(${PROJECT} PRIVATE ${LIBCURL_INCLUDE_DIRS}) -endif () -if (NOT DISABLE_NETWORK) - target_include_directories(${PROJECT} PUBLIC ${OPENSSL_INCLUDE_DIR}) -endif () -if (NOT DISABLE_TTF) - target_include_directories(${PROJECT} PRIVATE ${FREETYPE_INCLUDE_DIRS}) - if (UNIX AND NOT APPLE) - target_include_directories(${PROJECT} PRIVATE ${FONTCONFIG_INCLUDE_DIRS}) - endif () -endif () -if (NOT MINGW AND NOT MSVC) - target_include_directories(${PROJECT} SYSTEM PRIVATE ${ICU_INCLUDE_DIRS}) -endif () # To avoid unnecessary rebuilds set the current branch and # short sha1 only for the two files that use these @@ -207,13 +205,13 @@ endif() # - GCC 8 does not support -Wno-pragma-once-outside-header # - Other compilers status unknown if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - add_library(${PROJECT}-headers-check OBJECT ${OPENRCT2_CORE_HEADERS}) - set_target_properties(${PROJECT}-headers-check PROPERTIES LINKER_LANGUAGE CXX) + add_library(${PROJECT_NAME}-headers-check OBJECT ${OPENRCT2_CORE_HEADERS}) + set_target_properties(${PROJECT_NAME}-headers-check PROPERTIES LINKER_LANGUAGE CXX) set_source_files_properties(${OPENRCT2_CORE_HEADERS} PROPERTIES LANGUAGE CXX) add_definitions("-x c++ -Wno-pragma-once-outside-header -Wno-unused-const-variable") - get_target_property(LIBOPENRCT2_INCLUDE_DIRS ${PROJECT} INCLUDE_DIRECTORIES) - set_target_properties(${PROJECT}-headers-check PROPERTIES INCLUDE_DIRECTORIES "${LIBOPENRCT2_INCLUDE_DIRS}") + get_target_property(LIBOPENRCT2_INCLUDE_DIRS ${PROJECT_NAME} INCLUDE_DIRECTORIES) + set_target_properties(${PROJECT_NAME}-headers-check PROPERTIES INCLUDE_DIRECTORIES "${LIBOPENRCT2_INCLUDE_DIRS}") else () # Dummy target to ease invocation - add_custom_target(${PROJECT}-headers-check) + add_custom_target(${PROJECT_NAME}-headers-check) endif () diff --git a/src/openrct2/Cheats.cpp b/src/openrct2/Cheats.cpp index a46c25fca9..342b90cb1a 100644 --- a/src/openrct2/Cheats.cpp +++ b/src/openrct2/Cheats.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,7 +11,9 @@ #include "GameState.h" #include "actions/ParkSetLoanAction.hpp" +#include "actions/SetCheatAction.hpp" #include "config/Config.h" +#include "core/DataSerialiser.h" #include "localisation/Localisation.h" #include "network/network.h" #include "ride/Ride.h" @@ -27,6 +29,8 @@ using namespace OpenRCT2; +// TODO: Refactor this. Cheat variables should contain the cheat type +// and a serialisation method. bool gCheatsSandboxMode = false; bool gCheatsDisableClearanceChecks = false; bool gCheatsDisableSupportLimits = false; @@ -49,620 +53,7 @@ bool gCheatsDisableRideValueAging = false; bool gCheatsIgnoreResearchStatus = false; bool gCheatsEnableAllDrawableTrackPieces = false; -int32_t park_rating_spinner_value; -int32_t year_spinner_value = 1; -int32_t month_spinner_value = 1; -int32_t day_spinner_value = 1; - -#pragma region Cheat functions - -static void cheat_set_grass_length(int32_t length) -{ - int32_t x, y; - - for (y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) - { - for (x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) - { - auto surfaceElement = map_get_surface_element_at(x, y)->AsSurface(); - if (surfaceElement != nullptr && (surfaceElement->GetOwnership() & OWNERSHIP_OWNED) - && surfaceElement->GetWaterHeight() == 0 && surfaceElement->CanGrassGrow()) - { - surfaceElement->SetGrassLength(length); - } - } - } - - gfx_invalidate_screen(); -} - -static void cheat_water_plants() -{ - tile_element_iterator it; - - tile_element_iterator_begin(&it); - do - { - if (it.element->GetType() == TILE_ELEMENT_TYPE_SMALL_SCENERY) - { - it.element->AsSmallScenery()->SetAge(0); - } - } while (tile_element_iterator_next(&it)); - - gfx_invalidate_screen(); -} - -static void cheat_fix_vandalism() -{ - tile_element_iterator it; - - tile_element_iterator_begin(&it); - do - { - if (it.element->GetType() != TILE_ELEMENT_TYPE_PATH) - continue; - - if (!(it.element)->AsPath()->HasAddition()) - continue; - - it.element->AsPath()->SetIsBroken(false); - } while (tile_element_iterator_next(&it)); - - gfx_invalidate_screen(); -} - -static void cheat_remove_litter() -{ - rct_litter* litter; - uint16_t spriteIndex, nextSpriteIndex; - - for (spriteIndex = gSpriteListHead[SPRITE_LIST_LITTER]; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = nextSpriteIndex) - { - litter = &(get_sprite(spriteIndex)->litter); - nextSpriteIndex = litter->next; - sprite_remove((rct_sprite*)litter); - } - - tile_element_iterator it; - rct_scenery_entry* sceneryEntry; - - tile_element_iterator_begin(&it); - do - { - if (it.element->GetType() != TILE_ELEMENT_TYPE_PATH) - continue; - - if (!(it.element)->AsPath()->HasAddition()) - continue; - - sceneryEntry = it.element->AsPath()->GetAdditionEntry(); - if (sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BIN) - it.element->AsPath()->SetAdditionStatus(0xFF); - - } while (tile_element_iterator_next(&it)); - - gfx_invalidate_screen(); -} - -static void cheat_fix_rides() -{ - ride_id_t rideIndex; - Ride* ride; - - FOR_ALL_RIDES (rideIndex, ride) - { - if ((ride->mechanic_status != RIDE_MECHANIC_STATUS_FIXING) - && (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN))) - { - auto mechanic = ride_get_assigned_mechanic(ride); - if (mechanic != nullptr) - { - mechanic->RemoveFromRide(); - } - - ride_fix_breakdown(ride, 0); - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; - } - } -} - -static void cheat_renew_rides() -{ - int32_t i; - Ride* ride; - - FOR_ALL_RIDES (i, ride) - { - ride_renew(ride); - } - window_invalidate_by_class(WC_RIDE); -} - -static void cheat_make_destructible() -{ - int32_t i; - Ride* ride; - FOR_ALL_RIDES (i, ride) - { - if (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE) - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; - } - window_invalidate_by_class(WC_RIDE); -} - -static void cheat_reset_crash_status() -{ - int32_t i; - Ride* ride; - - FOR_ALL_RIDES (i, ride) - { - // Reset crash status - if (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_CRASHED; - // Reset crash history - ride->last_crash_type = RIDE_CRASH_TYPE_NONE; - } - window_invalidate_by_class(WC_RIDE); -} - -static void cheat_10_minute_inspections() -{ - int32_t i; - Ride* ride; - - FOR_ALL_RIDES (i, ride) - { - // Set inspection interval to 10 minutes - ride->inspection_interval = RIDE_INSPECTION_EVERY_10_MINUTES; - } - window_invalidate_by_class(WC_RIDE); -} - -static void cheat_no_money(bool enabled) -{ - if (enabled) - { - gParkFlags |= PARK_FLAGS_NO_MONEY; - } - else - { - gParkFlags &= ~PARK_FLAGS_NO_MONEY; - } - // Invalidate all windows that have anything to do with finance - window_invalidate_by_class(WC_RIDE); - window_invalidate_by_class(WC_PEEP); - window_invalidate_by_class(WC_PARK_INFORMATION); - window_invalidate_by_class(WC_FINANCES); - window_invalidate_by_class(WC_BOTTOM_TOOLBAR); - window_invalidate_by_class(WC_TOP_TOOLBAR); - window_invalidate_by_class(WC_CHEATS); -} - -static void cheat_set_money(money32 amount) -{ - gCash = amount; - - window_invalidate_by_class(WC_FINANCES); - window_invalidate_by_class(WC_BOTTOM_TOOLBAR); -} - -static void cheat_add_money(money32 amount) -{ - gCash = add_clamp_money32(gCash, amount); - - window_invalidate_by_class(WC_FINANCES); - window_invalidate_by_class(WC_BOTTOM_TOOLBAR); -} - -static void cheat_clear_loan() -{ - // First give money - cheat_add_money(gBankLoan); - - // Then pay the loan - auto gameAction = ParkSetLoanAction(MONEY(0, 00)); - GameActions::Execute(&gameAction); -} - -static void cheat_generate_guests(int32_t count) -{ - auto& park = GetContext()->GetGameState()->GetPark(); - for (int32_t i = 0; i < count; i++) - { - park.GenerateGuest(); - } - window_invalidate_by_class(WC_BOTTOM_TOOLBAR); -} - -static void cheat_set_guest_parameter(int32_t parameter, int32_t value) -{ - int32_t spriteIndex; - Peep* p; - FOR_ALL_GUESTS (spriteIndex, p) - { - auto peep = p->AsGuest(); - assert(peep != nullptr); - switch (parameter) - { - case GUEST_PARAMETER_HAPPINESS: - peep->happiness = value; - peep->happiness_target = value; - // Clear the 'red-faced with anger' status if we're making the guest happy - if (value > 0) - { - peep->peep_flags &= ~PEEP_FLAGS_ANGRY; - peep->angriness = 0; - } - break; - case GUEST_PARAMETER_ENERGY: - peep->energy = value; - peep->energy_target = value; - break; - case GUEST_PARAMETER_HUNGER: - peep->hunger = value; - break; - case GUEST_PARAMETER_THIRST: - peep->thirst = value; - break; - case GUEST_PARAMETER_NAUSEA: - peep->nausea = value; - peep->nausea_target = value; - break; - case GUEST_PARAMETER_NAUSEA_TOLERANCE: - peep->nausea_tolerance = value; - break; - case GUEST_PARAMETER_BATHROOM: - peep->toilet = value; - break; - case GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY: - peep->intensity = (15 << 4) | value; - break; - } - peep->UpdateSpriteType(); - } -} - -static void cheat_give_all_guests(int32_t object) -{ - int32_t spriteIndex; - Peep* p; - FOR_ALL_GUESTS (spriteIndex, p) - { - auto peep = p->AsGuest(); - assert(peep != nullptr); - switch (object) - { - case OBJECT_MONEY: - peep->cash_in_pocket = MONEY(1000, 00); - break; - case OBJECT_PARK_MAP: - peep->item_standard_flags |= PEEP_ITEM_MAP; - break; - case OBJECT_BALLOON: - peep->item_standard_flags |= PEEP_ITEM_BALLOON; - peep->balloon_colour = scenario_rand_max(COLOUR_COUNT - 1); - peep->UpdateSpriteType(); - break; - case OBJECT_UMBRELLA: - peep->item_standard_flags |= PEEP_ITEM_UMBRELLA; - peep->umbrella_colour = scenario_rand_max(COLOUR_COUNT - 1); - peep->UpdateSpriteType(); - break; - } - } - window_invalidate_by_class(WC_PEEP); -} - -static void cheat_remove_all_guests() -{ - Peep* peep; - rct_vehicle* vehicle; - uint16_t spriteIndex, nextSpriteIndex; - ride_id_t rideIndex; - Ride* ride; - - FOR_ALL_RIDES (rideIndex, ride) - { - ride->num_riders = 0; - - for (size_t stationIndex = 0; stationIndex < MAX_STATIONS; stationIndex++) - { - ride->stations[stationIndex].QueueLength = 0; - ride->stations[stationIndex].LastPeepInQueue = SPRITE_INDEX_NULL; - } - - for (auto trainIndex : ride->vehicles) - { - spriteIndex = trainIndex; - while (spriteIndex != SPRITE_INDEX_NULL) - { - vehicle = GET_VEHICLE(spriteIndex); - for (size_t i = 0, offset = 0; i < vehicle->num_peeps; i++) - { - while (vehicle->peep[i + offset] == SPRITE_INDEX_NULL) - { - offset++; - } - peep = GET_PEEP(vehicle->peep[i + offset]); - vehicle->mass -= peep->mass; - } - - for (auto& peepInTrainIndex : vehicle->peep) - { - peepInTrainIndex = SPRITE_INDEX_NULL; - } - - vehicle->num_peeps = 0; - vehicle->next_free_seat = 0; - - spriteIndex = vehicle->next_vehicle_on_train; - } - } - } - - for (spriteIndex = gSpriteListHead[SPRITE_LIST_PEEP]; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = nextSpriteIndex) - { - peep = &(get_sprite(spriteIndex)->peep); - nextSpriteIndex = peep->next; - if (peep->type == PEEP_TYPE_GUEST) - { - peep->Remove(); - } - } - - window_invalidate_by_class(WC_RIDE); - gfx_invalidate_screen(); -} - -static void cheat_explode_guests() -{ - int32_t sprite_index; - Peep* peep; - - FOR_ALL_GUESTS (sprite_index, peep) - { - if (scenario_rand_max(6) == 0) - { - peep->peep_flags |= PEEP_FLAGS_EXPLODE; - } - } -} - -static void cheat_set_staff_speed(uint8_t value) -{ - uint16_t spriteIndex; - Peep* peep; - - FOR_ALL_STAFF (spriteIndex, peep) - { - peep->energy = value; - peep->energy_target = value; - } -} - -static void cheat_own_all_land() -{ - const int32_t min = 32; - const int32_t max = gMapSizeUnits - 32; - - for (CoordsXY coords = { min, min }; coords.y <= max; coords.y += 32) - { - for (coords.x = min; coords.x <= max; coords.x += 32) - { - TileElement* surfaceElement = map_get_surface_element_at(coords); - if (surfaceElement == nullptr) - continue; - - // Ignore already owned tiles. - if (surfaceElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED) - continue; - - int32_t base_z = surfaceElement->base_height; - int32_t destOwnership = check_max_allowable_land_rights_for_tile(coords.x >> 5, coords.y >> 5, base_z); - - // only own tiles that were not set to 0 - if (destOwnership != OWNERSHIP_UNOWNED) - { - surfaceElement->AsSurface()->SetOwnership(destOwnership); - update_park_fences_around_tile(coords); - uint16_t baseHeight = surfaceElement->base_height * 8; - map_invalidate_tile(coords.x, coords.y, baseHeight, baseHeight + 16); - } - } - } - - // Completely unown peep spawn points - for (const auto& spawn : gPeepSpawns) - { - int32_t x = spawn.x; - int32_t y = spawn.y; - if (x != PEEP_SPAWN_UNDEFINED) - { - TileElement* surfaceElement = map_get_surface_element_at({ x, y }); - surfaceElement->AsSurface()->SetOwnership(OWNERSHIP_UNOWNED); - update_park_fences_around_tile({ x, y }); - uint16_t baseHeight = surfaceElement->base_height * 8; - map_invalidate_tile(x, y, baseHeight, baseHeight + 16); - } - } - - map_count_remaining_land_rights(); -} - -#pragma endregion - -void game_command_cheat( - [[maybe_unused]] int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, - [[maybe_unused]] int32_t* ebp) -{ - int32_t cheat = *ecx; - if (*ebx & GAME_COMMAND_FLAG_APPLY) - { - switch (cheat) - { - case CHEAT_SANDBOXMODE: - gCheatsSandboxMode = *edx != 0; - window_invalidate_by_class(WC_MAP); - window_invalidate_by_class(WC_FOOTPATH); - break; - case CHEAT_DISABLECLEARANCECHECKS: - gCheatsDisableClearanceChecks = *edx != 0; - break; - case CHEAT_DISABLESUPPORTLIMITS: - gCheatsDisableSupportLimits = *edx != 0; - break; - case CHEAT_SHOWALLOPERATINGMODES: - gCheatsShowAllOperatingModes = *edx != 0; - break; - case CHEAT_SHOWVEHICLESFROMOTHERTRACKTYPES: - gCheatsShowVehiclesFromOtherTrackTypes = *edx != 0; - break; - case CHEAT_FASTLIFTHILL: - gCheatsFastLiftHill = *edx != 0; - break; - case CHEAT_DISABLEBRAKESFAILURE: - gCheatsDisableBrakesFailure = *edx != 0; - break; - case CHEAT_DISABLEALLBREAKDOWNS: - gCheatsDisableAllBreakdowns = *edx != 0; - break; - case CHEAT_DISABLETRAINLENGTHLIMIT: - gCheatsDisableTrainLengthLimit = *edx != 0; - break; - case CHEAT_ENABLECHAINLIFTONALLTRACK: - gCheatsEnableChainLiftOnAllTrack = *edx != 0; - break; - case CHEAT_BUILDINPAUSEMODE: - gCheatsBuildInPauseMode = *edx != 0; - break; - case CHEAT_IGNORERIDEINTENSITY: - gCheatsIgnoreRideIntensity = *edx != 0; - break; - case CHEAT_DISABLEVANDALISM: - gCheatsDisableVandalism = *edx != 0; - break; - case CHEAT_DISABLELITTERING: - gCheatsDisableLittering = *edx != 0; - break; - case CHEAT_NOMONEY: - cheat_no_money(*edx != 0); - break; - case CHEAT_ADDMONEY: - cheat_add_money(*edx); - break; - case CHEAT_SETMONEY: - cheat_set_money(*edx); - break; - case CHEAT_CLEARLOAN: - cheat_clear_loan(); - break; - case CHEAT_SETGUESTPARAMETER: - cheat_set_guest_parameter(*edx, *edi); - break; - case CHEAT_GENERATEGUESTS: - cheat_generate_guests(*edx); - break; - case CHEAT_REMOVEALLGUESTS: - cheat_remove_all_guests(); - break; - case CHEAT_EXPLODEGUESTS: - cheat_explode_guests(); - break; - case CHEAT_GIVEALLGUESTS: - cheat_give_all_guests(*edx); - break; - case CHEAT_SETGRASSLENGTH: - cheat_set_grass_length(*edx); - break; - case CHEAT_WATERPLANTS: - cheat_water_plants(); - break; - case CHEAT_FIXVANDALISM: - cheat_fix_vandalism(); - break; - case CHEAT_REMOVELITTER: - cheat_remove_litter(); - break; - case CHEAT_DISABLEPLANTAGING: - gCheatsDisablePlantAging = *edx != 0; - break; - case CHEAT_SETSTAFFSPEED: - cheat_set_staff_speed(*edx); - break; - case CHEAT_RENEWRIDES: - cheat_renew_rides(); - break; - case CHEAT_MAKEDESTRUCTIBLE: - cheat_make_destructible(); - break; - case CHEAT_FIXRIDES: - cheat_fix_rides(); - break; - case CHEAT_RESETCRASHSTATUS: - cheat_reset_crash_status(); - break; - case CHEAT_10MINUTEINSPECTIONS: - cheat_10_minute_inspections(); - break; - case CHEAT_WINSCENARIO: - scenario_success(); - break; - case CHEAT_FORCEWEATHER: - climate_force_weather(*edx); - break; - case CHEAT_FREEZEWEATHER: - gCheatsFreezeWeather = *edx != 0; - break; - case CHEAT_NEVERENDINGMARKETING: - gCheatsNeverendingMarketing = *edx != 0; - break; - case CHEAT_OPENCLOSEPARK: - park_set_open(park_is_open() ? 0 : 1); - break; - case CHEAT_HAVEFUN: - gScenarioObjectiveType = OBJECTIVE_HAVE_FUN; - break; - case CHEAT_SETFORCEDPARKRATING: - if (*edx > -1) - { - park_rating_spinner_value = *edx; - } - set_forced_park_rating(*edx); - break; - case CHEAT_RESETDATE: - date_reset(); - window_invalidate_by_class(WC_BOTTOM_TOOLBAR); - break; - case CHEAT_ALLOW_ARBITRARY_RIDE_TYPE_CHANGES: - gCheatsAllowArbitraryRideTypeChanges = *edx != 0; - window_invalidate_by_class(WC_RIDE); - break; - case CHEAT_OWNALLLAND: - cheat_own_all_land(); - break; - case CHEAT_DISABLERIDEVALUEAGING: - gCheatsDisableRideValueAging = *edx != 0; - break; - case CHEAT_IGNORERESEARCHSTATUS: - gCheatsIgnoreResearchStatus = *edx != 0; - break; - case CHEAT_ENABLEALLDRAWABLETRACKPIECES: - gCheatsEnableAllDrawableTrackPieces = *edx != 0; - break; - } - if (network_get_mode() == NETWORK_MODE_NONE) - { - config_save_default(); - } - window_invalidate_by_class(WC_CHEATS); - } - *ebx = 0; -} - -void cheats_reset() +void CheatsReset() { gCheatsSandboxMode = false; gCheatsDisableClearanceChecks = false; @@ -686,310 +77,239 @@ void cheats_reset() gCheatsIgnoreResearchStatus = false; } -// Generates the string to print for the server log when a cheat is used. -const char* cheats_get_cheat_string(int cheat, int edx, int edi) +void CheatsSet(CheatType cheatType, int32_t param1 /* = 0*/, int32_t param2 /* = 0*/) { - switch (cheat) - { - case CHEAT_SANDBOXMODE: - if (gCheatsSandboxMode) - { - return language_get_string(STR_CHEAT_SANDBOX_MODE_DISABLE); - } - else - { - return language_get_string(STR_CHEAT_SANDBOX_MODE); - } - case CHEAT_DISABLECLEARANCECHECKS: - return language_get_string(STR_DISABLE_CLEARANCE_CHECKS); - case CHEAT_DISABLESUPPORTLIMITS: - return language_get_string(STR_DISABLE_SUPPORT_LIMITS); - case CHEAT_SHOWALLOPERATINGMODES: - return language_get_string(STR_CHEAT_SHOW_ALL_OPERATING_MODES); - case CHEAT_SHOWVEHICLESFROMOTHERTRACKTYPES: - return language_get_string(STR_CHEAT_SHOW_VEHICLES_FROM_OTHER_TRACK_TYPES); - case CHEAT_FASTLIFTHILL: - return language_get_string(STR_CHEAT_UNLOCK_OPERATING_LIMITS); - case CHEAT_DISABLEBRAKESFAILURE: - return language_get_string(STR_CHEAT_DISABLE_BRAKES_FAILURE); - case CHEAT_DISABLEALLBREAKDOWNS: - return language_get_string(STR_CHEAT_DISABLE_BREAKDOWNS); - case CHEAT_DISABLETRAINLENGTHLIMIT: - return language_get_string(STR_CHEAT_DISABLE_TRAIN_LENGTH_LIMIT); - case CHEAT_ENABLECHAINLIFTONALLTRACK: - return language_get_string(STR_CHEAT_ENABLE_CHAIN_LIFT_ON_ALL_TRACK); - case CHEAT_BUILDINPAUSEMODE: - return language_get_string(STR_CHEAT_BUILD_IN_PAUSE_MODE); - case CHEAT_IGNORERIDEINTENSITY: - return language_get_string(STR_CHEAT_IGNORE_INTENSITY); - case CHEAT_DISABLEVANDALISM: - return language_get_string(STR_CHEAT_DISABLE_VANDALISM); - case CHEAT_DISABLELITTERING: - return language_get_string(STR_CHEAT_DISABLE_LITTERING); - case CHEAT_NOMONEY: - return language_get_string(STR_MAKE_PARK_NO_MONEY); - case CHEAT_ADDMONEY: - return language_get_string(STR_LOG_CHEAT_ADD_MONEY); - case CHEAT_CLEARLOAN: - return language_get_string(STR_CHEAT_CLEAR_LOAN); - case CHEAT_SETGUESTPARAMETER: - { - static char cheat_string[128]; - safe_strcpy(cheat_string, language_get_string(STR_CHEAT_SET_GUESTS_PARAMETERS), 128); - safe_strcat(cheat_string, " ", 128); - switch (edx) - { - case GUEST_PARAMETER_HAPPINESS: - safe_strcat(cheat_string, language_get_string(STR_CHEAT_GUEST_HAPPINESS), 128); - safe_strcat(cheat_string, " ", 128); - if (edi == 255) - { - safe_strcat(cheat_string, language_get_string(STR_MAX), 128); - } - else if (edi == 0) - { - safe_strcat(cheat_string, language_get_string(STR_MIN), 128); - } - break; - case GUEST_PARAMETER_ENERGY: - safe_strcat(cheat_string, language_get_string(STR_CHEAT_GUEST_ENERGY), 128); - safe_strcat(cheat_string, " ", 128); - if (edi == 127) - { - safe_strcat(cheat_string, language_get_string(STR_MAX), 128); - } - else if (edi == 0) - { - safe_strcat(cheat_string, language_get_string(STR_MIN), 128); - } - break; - case GUEST_PARAMETER_HUNGER: - safe_strcat(cheat_string, language_get_string(STR_CHEAT_GUEST_HUNGER), 128); - safe_strcat(cheat_string, " ", 128); - if (edi == 255) - { - safe_strcat(cheat_string, language_get_string(STR_MIN), 128); - } - else if (edi == 0) - { - safe_strcat(cheat_string, language_get_string(STR_MAX), 128); - } - break; - case GUEST_PARAMETER_THIRST: - safe_strcat(cheat_string, language_get_string(STR_CHEAT_GUEST_THIRST), 128); - safe_strcat(cheat_string, " ", 128); - if (edi == 255) - { - safe_strcat(cheat_string, language_get_string(STR_MIN), 128); - } - else if (edi == 0) - { - safe_strcat(cheat_string, language_get_string(STR_MAX), 128); - } - break; - case GUEST_PARAMETER_NAUSEA: - safe_strcat(cheat_string, language_get_string(STR_CHEAT_GUEST_NAUSEA), 128); - safe_strcat(cheat_string, " ", 128); - if (edi == 255) - { - safe_strcat(cheat_string, language_get_string(STR_MAX), 128); - } - else if (edi == 0) - { - safe_strcat(cheat_string, language_get_string(STR_MIN), 128); - } - break; - case GUEST_PARAMETER_NAUSEA_TOLERANCE: - safe_strcat(cheat_string, language_get_string(STR_CHEAT_GUEST_NAUSEA_TOLERANCE), 128); - safe_strcat(cheat_string, " ", 128); - if (edi == PEEP_NAUSEA_TOLERANCE_HIGH) - { - safe_strcat(cheat_string, language_get_string(STR_MAX), 128); - } - else if (edi == PEEP_NAUSEA_TOLERANCE_NONE) - { - safe_strcat(cheat_string, language_get_string(STR_MIN), 128); - } - break; - case GUEST_PARAMETER_BATHROOM: - safe_strcat(cheat_string, language_get_string(STR_CHEAT_GUEST_BATHROOM), 128); - safe_strcat(cheat_string, " ", 128); - if (edi == 255) - { - safe_strcat(cheat_string, language_get_string(STR_MAX), 128); - } - else if (edi == 0) - { - safe_strcat(cheat_string, language_get_string(STR_MIN), 128); - } - break; - case GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY: - safe_strcat(cheat_string, language_get_string(STR_CHEAT_GUEST_PREFERRED_INTENSITY), 128); - safe_strcat(cheat_string, " ", 128); - if (edi == 1) - { - safe_strcat(cheat_string, language_get_string(STR_CHEAT_MORE_THAN_1), 128); - } - else if (edi == 0) - { - safe_strcat(cheat_string, language_get_string(STR_CHEAT_LESS_THAN_15), 128); - } - break; - } - return cheat_string; - } - case CHEAT_GENERATEGUESTS: - return language_get_string(STR_CHEAT_LARGE_TRAM_GUESTS); - case CHEAT_REMOVEALLGUESTS: - return language_get_string(STR_CHEAT_REMOVE_ALL_GUESTS); - case CHEAT_EXPLODEGUESTS: - return language_get_string(STR_CHEAT_EXPLODE); - case CHEAT_GIVEALLGUESTS: - { - static char cheat_string[64]; - safe_strcpy(cheat_string, language_get_string(STR_CHEAT_GIVE_ALL_GUESTS), 64); - safe_strcat(cheat_string, " ", 64); - switch (edx) - { - case OBJECT_MONEY: - { - char money[16]; - set_format_arg(0, money32, CHEATS_GIVE_GUESTS_MONEY); - format_string(money, 16, STR_CHEAT_CURRENCY_FORMAT, gCommonFormatArgs); - safe_strcat(cheat_string, money, 64); - break; - } - case OBJECT_PARK_MAP: - safe_strcat(cheat_string, language_get_string(STR_SHOP_ITEM_PLURAL_PARK_MAP), 64); - break; - case OBJECT_BALLOON: - safe_strcat(cheat_string, language_get_string(STR_SHOP_ITEM_PLURAL_BALLOON), 64); - break; - case OBJECT_UMBRELLA: - safe_strcat(cheat_string, language_get_string(STR_SHOP_ITEM_PLURAL_UMBRELLA), 64); - break; - } - return cheat_string; - } - case CHEAT_SETGRASSLENGTH: - if (edx == 0) - { - return language_get_string(STR_CHEAT_MOWED_GRASS); - } - return language_get_string(STR_CHEAT_CLEAR_GRASS); - case CHEAT_WATERPLANTS: - return language_get_string(STR_CHEAT_WATER_PLANTS); - case CHEAT_FIXVANDALISM: - return language_get_string(STR_CHEAT_FIX_VANDALISM); - case CHEAT_REMOVELITTER: - return language_get_string(STR_CHEAT_REMOVE_LITTER); - case CHEAT_DISABLEPLANTAGING: - return language_get_string(STR_CHEAT_DISABLE_PLANT_AGING); - case CHEAT_SETSTAFFSPEED: - { - static char cheat_string[64]; - safe_strcpy(cheat_string, language_get_string(STR_CHEAT_STAFF_SPEED), 64); - safe_strcat(cheat_string, " ", 64); - if (edx == CHEATS_STAFF_FAST_SPEED) - { - safe_strcat(cheat_string, language_get_string(STR_FAST), 64); - } - else if (edx == CHEATS_STAFF_NORMAL_SPEED) - { - safe_strcat(cheat_string, language_get_string(STR_NORMAL), 64); - } - return cheat_string; - } - case CHEAT_RENEWRIDES: - return language_get_string(STR_CHEAT_RENEW_RIDES); - case CHEAT_MAKEDESTRUCTIBLE: - return language_get_string(STR_CHEAT_MAKE_DESTRUCTABLE); - case CHEAT_FIXRIDES: - return language_get_string(STR_CHEAT_FIX_ALL_RIDES); - case CHEAT_RESETCRASHSTATUS: - return language_get_string(STR_CHEAT_RESET_CRASH_STATUS); - case CHEAT_10MINUTEINSPECTIONS: - return language_get_string(STR_CHEAT_10_MINUTE_INSPECTIONS); - case CHEAT_WINSCENARIO: - return language_get_string(STR_CHEAT_WIN_SCENARIO); - case CHEAT_FORCEWEATHER: - { - static char cheat_string[64]; - safe_strcpy(cheat_string, language_get_string(STR_FORCE_WEATHER), 64); - safe_strcat(cheat_string, " ", 64); - switch (edx) - { - case 0: - safe_strcat(cheat_string, language_get_string(STR_SUNNY), 64); - break; - case 1: - safe_strcat(cheat_string, language_get_string(STR_PARTIALLY_CLOUDY), 64); - break; - case 2: - safe_strcat(cheat_string, language_get_string(STR_CLOUDY), 64); - break; - case 3: - safe_strcat(cheat_string, language_get_string(STR_RAIN), 64); - break; - case 4: - safe_strcat(cheat_string, language_get_string(STR_HEAVY_RAIN), 64); - break; - case 5: - safe_strcat(cheat_string, language_get_string(STR_THUNDERSTORM), 64); - break; - } - return cheat_string; - } - case CHEAT_FREEZEWEATHER: - if (gCheatsFreezeWeather) - { - return language_get_string(STR_CHEAT_UNFREEZE_WEATHER); - } - else - { - return language_get_string(STR_CHEAT_FREEZE_WEATHER); - } - case CHEAT_NEVERENDINGMARKETING: - return language_get_string(STR_CHEAT_NEVERENDING_MARKETING); - case CHEAT_OPENCLOSEPARK: - if (park_is_open()) - { - return language_get_string(STR_CHEAT_CLOSE_PARK); - } - else - { - return language_get_string(STR_CHEAT_OPEN_PARK); - } - case CHEAT_HAVEFUN: - return language_get_string(STR_CHEAT_HAVE_FUN); - case CHEAT_SETFORCEDPARKRATING: - { - static char cheat_string[64]; - safe_strcpy(cheat_string, language_get_string(STR_FORCE_PARK_RATING), 64); - safe_strcat(cheat_string, " ", 64); - - char buffer[8]; - snprintf(buffer, sizeof(buffer), "%d", park_rating_spinner_value); - char* park_rating = buffer; - safe_strcat(cheat_string, park_rating, 64); - - return cheat_string; - } - case CHEAT_RESETDATE: - return language_get_string(STR_CHEAT_RESET_DATE); - case CHEAT_ALLOW_ARBITRARY_RIDE_TYPE_CHANGES: - return language_get_string(STR_CHEAT_ALLOW_ARBITRARY_RIDE_TYPE_CHANGES); - case CHEAT_SETMONEY: - return language_get_string(STR_SET_MONEY); - case CHEAT_OWNALLLAND: - return language_get_string(STR_CHEAT_OWN_ALL_LAND); - case CHEAT_DISABLERIDEVALUEAGING: - return language_get_string(STR_CHEAT_DISABLE_RIDE_VALUE_AGING); - case CHEAT_IGNORERESEARCHSTATUS: - return language_get_string(STR_CHEAT_IGNORE_RESEARCH_STATUS); - case CHEAT_ENABLEALLDRAWABLETRACKPIECES: - return language_get_string(STR_CHEAT_ENABLE_ALL_DRAWABLE_TRACK_PIECES); - } - - return ""; + auto setCheatAction = SetCheatAction(cheatType, param1, param2); + GameActions::Execute(&setCheatAction); +} + +template static void CheatEntrySerialise(DataSerialiser& ds, CheatType type, const T& value, uint16_t& count) +{ + ds << static_cast(type) << value; + count++; +} + +void CheatsSerialise(DataSerialiser& ds) +{ + uint16_t count = 0; + + if (ds.IsSaving()) + { + IStream& stream = ds.GetStream(); + + // Temporarily write 0, will be updated after every cheat is written. + uint64_t countOffset = stream.GetPosition(); + ds << count; + + CheatEntrySerialise(ds, CheatType::SandboxMode, gCheatsSandboxMode, count); + CheatEntrySerialise(ds, CheatType::DisableClearanceChecks, gCheatsDisableClearanceChecks, count); + CheatEntrySerialise(ds, CheatType::DisableSupportLimits, gCheatsDisableSupportLimits, count); + CheatEntrySerialise(ds, CheatType::ShowAllOperatingModes, gCheatsShowAllOperatingModes, count); + CheatEntrySerialise(ds, CheatType::ShowVehiclesFromOtherTrackTypes, gCheatsShowVehiclesFromOtherTrackTypes, count); + CheatEntrySerialise(ds, CheatType::FastLiftHill, gCheatsFastLiftHill, count); + CheatEntrySerialise(ds, CheatType::DisableBrakesFailure, gCheatsDisableBrakesFailure, count); + CheatEntrySerialise(ds, CheatType::DisableAllBreakdowns, gCheatsDisableAllBreakdowns, count); + CheatEntrySerialise(ds, CheatType::BuildInPauseMode, gCheatsBuildInPauseMode, count); + CheatEntrySerialise(ds, CheatType::IgnoreRideIntensity, gCheatsIgnoreRideIntensity, count); + CheatEntrySerialise(ds, CheatType::DisableVandalism, gCheatsDisableVandalism, count); + CheatEntrySerialise(ds, CheatType::DisableLittering, gCheatsDisableLittering, count); + CheatEntrySerialise(ds, CheatType::NeverEndingMarketing, gCheatsNeverendingMarketing, count); + CheatEntrySerialise(ds, CheatType::FreezeWeather, gCheatsFreezeWeather, count); + CheatEntrySerialise(ds, CheatType::DisableTrainLengthLimit, gCheatsDisableTrainLengthLimit, count); + CheatEntrySerialise(ds, CheatType::DisablePlantAging, gCheatsDisablePlantAging, count); + CheatEntrySerialise(ds, CheatType::EnableChainLiftOnAllTrack, gCheatsEnableChainLiftOnAllTrack, count); + CheatEntrySerialise(ds, CheatType::AllowArbitraryRideTypeChanges, gCheatsAllowArbitraryRideTypeChanges, count); + CheatEntrySerialise(ds, CheatType::DisableRideValueAging, gCheatsDisableRideValueAging, count); + CheatEntrySerialise(ds, CheatType::IgnoreResearchStatus, gCheatsIgnoreResearchStatus, count); + CheatEntrySerialise(ds, CheatType::EnableAllDrawableTrackPieces, gCheatsEnableAllDrawableTrackPieces, count); + + // Remember current position and update count. + uint64_t endOffset = stream.GetPosition(); + + stream.SetPosition(countOffset); + ds << count; // Write correct count. + + // Set position back. + stream.SetPosition(endOffset); + } + else + { + ds << count; + + for (uint16_t i = 0; i < count; i++) + { + int32_t type = 0; + ds << type; + + switch (static_cast(type)) + { + case CheatType::SandboxMode: + ds << gCheatsSandboxMode; + break; + case CheatType::DisableClearanceChecks: + ds << gCheatsDisableClearanceChecks; + break; + case CheatType::DisableSupportLimits: + ds << gCheatsDisableSupportLimits; + break; + case CheatType::ShowAllOperatingModes: + ds << gCheatsShowAllOperatingModes; + break; + case CheatType::ShowVehiclesFromOtherTrackTypes: + ds << gCheatsShowVehiclesFromOtherTrackTypes; + break; + case CheatType::FastLiftHill: + ds << gCheatsFastLiftHill; + break; + case CheatType::DisableBrakesFailure: + ds << gCheatsDisableBrakesFailure; + break; + case CheatType::DisableAllBreakdowns: + ds << gCheatsDisableAllBreakdowns; + break; + case CheatType::BuildInPauseMode: + ds << gCheatsBuildInPauseMode; + break; + case CheatType::IgnoreRideIntensity: + ds << gCheatsIgnoreRideIntensity; + break; + case CheatType::DisableVandalism: + ds << gCheatsDisableVandalism; + break; + case CheatType::DisableLittering: + ds << gCheatsDisableLittering; + break; + case CheatType::NeverEndingMarketing: + ds << gCheatsNeverendingMarketing; + break; + case CheatType::FreezeWeather: + ds << gCheatsFreezeWeather; + break; + case CheatType::DisableTrainLengthLimit: + ds << gCheatsDisableTrainLengthLimit; + break; + case CheatType::DisablePlantAging: + ds << gCheatsDisablePlantAging; + break; + case CheatType::EnableChainLiftOnAllTrack: + ds << gCheatsEnableChainLiftOnAllTrack; + break; + case CheatType::AllowArbitraryRideTypeChanges: + ds << gCheatsAllowArbitraryRideTypeChanges; + break; + case CheatType::DisableRideValueAging: + ds << gCheatsDisableRideValueAging; + break; + case CheatType::IgnoreResearchStatus: + ds << gCheatsIgnoreResearchStatus; + break; + case CheatType::EnableAllDrawableTrackPieces: + ds << gCheatsEnableAllDrawableTrackPieces; + break; + default: + break; + } + } + } +} + +const char* CheatsGetName(CheatType cheatType) +{ + switch (cheatType) + { + case CheatType::SandboxMode: + return language_get_string(STR_CHEAT_SANDBOX_MODE); + case CheatType::DisableClearanceChecks: + return language_get_string(STR_DISABLE_CLEARANCE_CHECKS); + case CheatType::DisableSupportLimits: + return language_get_string(STR_DISABLE_SUPPORT_LIMITS); + case CheatType::ShowAllOperatingModes: + return language_get_string(STR_CHEAT_SHOW_ALL_OPERATING_MODES); + case CheatType::ShowVehiclesFromOtherTrackTypes: + return language_get_string(STR_CHEAT_SHOW_VEHICLES_FROM_OTHER_TRACK_TYPES); + case CheatType::FastLiftHill: + return language_get_string(STR_CHEAT_UNLOCK_OPERATING_LIMITS); + case CheatType::DisableBrakesFailure: + return language_get_string(STR_CHEAT_DISABLE_BRAKES_FAILURE); + case CheatType::DisableAllBreakdowns: + return language_get_string(STR_CHEAT_DISABLE_BREAKDOWNS); + case CheatType::DisableTrainLengthLimit: + return language_get_string(STR_CHEAT_DISABLE_TRAIN_LENGTH_LIMIT); + case CheatType::EnableChainLiftOnAllTrack: + return language_get_string(STR_CHEAT_ENABLE_CHAIN_LIFT_ON_ALL_TRACK); + case CheatType::BuildInPauseMode: + return language_get_string(STR_CHEAT_BUILD_IN_PAUSE_MODE); + case CheatType::IgnoreRideIntensity: + return language_get_string(STR_CHEAT_IGNORE_INTENSITY); + case CheatType::DisableVandalism: + return language_get_string(STR_CHEAT_DISABLE_VANDALISM); + case CheatType::DisableLittering: + return language_get_string(STR_CHEAT_DISABLE_LITTERING); + case CheatType::NoMoney: + return language_get_string(STR_MAKE_PARK_NO_MONEY); + case CheatType::AddMoney: + return language_get_string(STR_LOG_CHEAT_ADD_MONEY); + case CheatType::ClearLoan: + return language_get_string(STR_CHEAT_CLEAR_LOAN); + case CheatType::SetGuestParameter: + return language_get_string(STR_CHEAT_SET_GUESTS_PARAMETERS); + case CheatType::GenerateGuests: + return language_get_string(STR_CHEAT_LARGE_TRAM_GUESTS); + case CheatType::RemoveAllGuests: + return language_get_string(STR_CHEAT_REMOVE_ALL_GUESTS); + case CheatType::ExplodeGuests: + return language_get_string(STR_CHEAT_EXPLODE); + case CheatType::GiveAllGuests: + return language_get_string(STR_CHEAT_GIVE_ALL_GUESTS); + case CheatType::SetGrassLength: + return language_get_string(STR_CHEAT_CLEAR_GRASS); + case CheatType::WaterPlants: + return language_get_string(STR_CHEAT_WATER_PLANTS); + case CheatType::FixVandalism: + return language_get_string(STR_CHEAT_FIX_VANDALISM); + case CheatType::RemoveLitter: + return language_get_string(STR_CHEAT_REMOVE_LITTER); + case CheatType::DisablePlantAging: + return language_get_string(STR_CHEAT_DISABLE_PLANT_AGING); + case CheatType::SetStaffSpeed: + return language_get_string(STR_CHEAT_STAFF_SPEED); + case CheatType::RenewRides: + return language_get_string(STR_CHEAT_RENEW_RIDES); + case CheatType::MakeDestructible: + return language_get_string(STR_CHEAT_MAKE_DESTRUCTABLE); + case CheatType::FixRides: + return language_get_string(STR_CHEAT_FIX_ALL_RIDES); + case CheatType::ResetCrashStatus: + return language_get_string(STR_CHEAT_RESET_CRASH_STATUS); + case CheatType::TenMinuteInspections: + return language_get_string(STR_CHEAT_10_MINUTE_INSPECTIONS); + case CheatType::WinScenario: + return language_get_string(STR_CHEAT_WIN_SCENARIO); + case CheatType::ForceWeather: + return language_get_string(STR_FORCE_WEATHER); + case CheatType::FreezeWeather: + return language_get_string(STR_CHEAT_FREEZE_WEATHER); + case CheatType::NeverEndingMarketing: + return language_get_string(STR_CHEAT_NEVERENDING_MARKETING); + case CheatType::OpenClosePark: + return language_get_string(STR_CHEAT_OPEN_PARK); + case CheatType::HaveFun: + return language_get_string(STR_CHEAT_HAVE_FUN); + case CheatType::SetForcedParkRating: + return language_get_string(STR_FORCE_PARK_RATING); + case CheatType::AllowArbitraryRideTypeChanges: + return language_get_string(STR_CHEAT_ALLOW_ARBITRARY_RIDE_TYPE_CHANGES); + case CheatType::SetMoney: + return language_get_string(STR_SET_MONEY); + case CheatType::OwnAllLand: + return language_get_string(STR_CHEAT_OWN_ALL_LAND); + case CheatType::DisableRideValueAging: + return language_get_string(STR_CHEAT_DISABLE_RIDE_VALUE_AGING); + case CheatType::IgnoreResearchStatus: + return language_get_string(STR_CHEAT_IGNORE_RESEARCH_STATUS); + case CheatType::EnableAllDrawableTrackPieces: + return language_get_string(STR_CHEAT_ENABLE_ALL_DRAWABLE_TRACK_PIECES); + default: + return "Unknown Cheat"; + } } diff --git a/src/openrct2/Cheats.h b/src/openrct2/Cheats.h index bcc8a18880..8c70820a9b 100644 --- a/src/openrct2/Cheats.h +++ b/src/openrct2/Cheats.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -34,57 +34,58 @@ extern bool gCheatsAllowArbitraryRideTypeChanges; extern bool gCheatsIgnoreResearchStatus; extern bool gCheatsEnableAllDrawableTrackPieces; -enum +enum class CheatType : int32_t { - CHEAT_SANDBOXMODE, - CHEAT_DISABLECLEARANCECHECKS, - CHEAT_DISABLESUPPORTLIMITS, - CHEAT_SHOWALLOPERATINGMODES, - CHEAT_SHOWVEHICLESFROMOTHERTRACKTYPES, - CHEAT_DISABLETRAINLENGTHLIMIT, - CHEAT_ENABLECHAINLIFTONALLTRACK, - CHEAT_FASTLIFTHILL, - CHEAT_DISABLEBRAKESFAILURE, - CHEAT_DISABLEALLBREAKDOWNS, - CHEAT_UNLOCKALLPRICES, - CHEAT_BUILDINPAUSEMODE, - CHEAT_IGNORERIDEINTENSITY, - CHEAT_DISABLEVANDALISM, - CHEAT_DISABLELITTERING, - CHEAT_NOMONEY, - CHEAT_ADDMONEY, - CHEAT_SETMONEY, - CHEAT_CLEARLOAN, - CHEAT_SETGUESTPARAMETER, - CHEAT_GENERATEGUESTS, - CHEAT_REMOVEALLGUESTS, - CHEAT_EXPLODEGUESTS, - CHEAT_GIVEALLGUESTS, - CHEAT_SETGRASSLENGTH, - CHEAT_WATERPLANTS, - CHEAT_DISABLEPLANTAGING, - CHEAT_FIXVANDALISM, - CHEAT_REMOVELITTER, - CHEAT_SETSTAFFSPEED, - CHEAT_RENEWRIDES, - CHEAT_MAKEDESTRUCTIBLE, - CHEAT_FIXRIDES, - CHEAT_RESETCRASHSTATUS, - CHEAT_10MINUTEINSPECTIONS, - CHEAT_WINSCENARIO, - CHEAT_FORCEWEATHER, - CHEAT_FREEZEWEATHER, - CHEAT_OPENCLOSEPARK, - CHEAT_HAVEFUN, - CHEAT_SETFORCEDPARKRATING, - CHEAT_NEVERENDINGMARKETING, - CHEAT_RESETDATE, - CHEAT_ALLOW_ARBITRARY_RIDE_TYPE_CHANGES, - CHEAT_OWNALLLAND, - CHEAT_DISABLERIDEVALUEAGING, - CHEAT_IGNORERESEARCHSTATUS, - CHEAT_ENABLEALLDRAWABLETRACKPIECES, - CHEAT_DATE_SET, + SandboxMode, + DisableClearanceChecks, + DisableSupportLimits, + ShowAllOperatingModes, + ShowVehiclesFromOtherTrackTypes, + DisableTrainLengthLimit, + EnableChainLiftOnAllTrack, + FastLiftHill, + DisableBrakesFailure, + DisableAllBreakdowns, + UnlockAllPrices, + BuildInPauseMode, + IgnoreRideIntensity, + DisableVandalism, + DisableLittering, + NoMoney, + AddMoney, + SetMoney, + ClearLoan, + SetGuestParameter, + GenerateGuests, + RemoveAllGuests, + ExplodeGuests, + GiveAllGuests, + SetGrassLength, + WaterPlants, + DisablePlantAging, + FixVandalism, + RemoveLitter, + SetStaffSpeed, + RenewRides, + MakeDestructible, + FixRides, + ResetCrashStatus, + TenMinuteInspections, + WinScenario, + ForceWeather, + FreezeWeather, + OpenClosePark, + HaveFun, + SetForcedParkRating, + NeverEndingMarketing, + AllowArbitraryRideTypeChanges, + OwnAllLand, + DisableRideValueAging, + IgnoreResearchStatus, + EnableAllDrawableTrackPieces, + CreateDucks, + RemoveDucks, + Count, }; enum @@ -109,19 +110,14 @@ enum #define CHEATS_GIVE_GUESTS_MONEY MONEY(1000, 00) #define CHEATS_TRAM_INCREMENT 250 +#define CHEATS_DUCK_INCREMENT 20 #define CHEATS_STAFF_FAST_SPEED 0xFF #define CHEATS_STAFF_NORMAL_SPEED 0x60 #define CHEATS_STAFF_FREEZE_SPEED 0 -extern int32_t park_rating_spinner_value; -extern int32_t year_spinner_value; -extern int32_t month_spinner_value; -extern int32_t day_spinner_value; - -void game_command_cheat(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); - -void cheats_reset(); - -const char* cheats_get_cheat_string(int cheat, int edx, int edi); +void CheatsReset(); +const char* CheatsGetName(CheatType cheatType); +void CheatsSet(CheatType cheatType, int32_t param1 = 0, int32_t param2 = 0); +void CheatsSerialise(class DataSerialiser& ds); #endif diff --git a/src/openrct2/CmdlineSprite.cpp b/src/openrct2/CmdlineSprite.cpp index 89671fadd0..e99f1b13df 100644 --- a/src/openrct2/CmdlineSprite.cpp +++ b/src/openrct2/CmdlineSprite.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,7 @@ #include "OpenRCT2.h" #include "core/Imaging.h" +#include "core/String.hpp" #include "drawing/Drawing.h" #include "drawing/ImageImporter.h" #include "localisation/Language.h" @@ -68,11 +69,9 @@ static uint8_t* spriteFileData; static FILE* fopen_utf8(const char* path, const char* mode) { - wchar_t* pathW = utf8_to_widechar(path); - wchar_t* modeW = utf8_to_widechar(mode); - FILE* file = _wfopen(pathW, modeW); - free(pathW); - free(modeW); + auto pathW = String::ToWideChar(path); + auto modeW = String::ToWideChar(mode); + auto file = _wfopen(pathW.c_str(), modeW.c_str()); return file; } @@ -239,14 +238,14 @@ static bool sprite_file_export(int32_t spriteIndex, const char* outPath) if (spriteHeader->flags & G1_FLAG_RLE_COMPRESSION) { gfx_rle_sprite_to_buffer( - spriteHeader->offset, pixels, (uint8_t*)spriteFilePalette, &dpi, IMAGE_TYPE_DEFAULT, 0, spriteHeader->height, 0, + spriteHeader->offset, pixels, (uint8_t*)spriteFilePalette, &dpi, ImageId(), 0, spriteHeader->height, 0, spriteHeader->width); } else { gfx_bmp_sprite_to_buffer( (uint8_t*)spriteFilePalette, spriteHeader->offset, pixels, spriteHeader, &dpi, spriteHeader->height, - spriteHeader->width, IMAGE_TYPE_DEFAULT); + spriteHeader->width, ImageId()); } auto const pixels8 = dpi.bits; @@ -640,20 +639,19 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) } // Resolve absolute sprite path - char* imagePath = platform_get_absolute_path(json_string_value(path), directoryPath); + auto imagePath = platform_get_absolute_path(json_string_value(path), directoryPath); rct_g1_element spriteElement; uint8_t* buffer; int bufferLength; if (!sprite_file_import( - imagePath, x_offset == nullptr ? 0 : json_integer_value(x_offset), + imagePath.c_str(), x_offset == nullptr ? 0 : json_integer_value(x_offset), y_offset == nullptr ? 0 : json_integer_value(y_offset), keep_palette, forceBmp, &spriteElement, &buffer, &bufferLength, gSpriteMode)) { - fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath); + fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath.c_str()); json_decref(sprite_list); - free(imagePath); return -1; } @@ -661,7 +659,6 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) { fprintf(stderr, "Unable to open sprite file: %s\nCanceling\n", spriteFilePath); json_decref(sprite_list); - free(imagePath); return -1; } @@ -683,16 +680,14 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) if (!sprite_file_save(spriteFilePath)) { - fprintf(stderr, "Could not save sprite file: %s\nCanceling\n", imagePath); + fprintf(stderr, "Could not save sprite file: %s\nCanceling\n", imagePath.c_str()); json_decref(sprite_list); - free(imagePath); return -1; } if (!silent) - fprintf(stdout, "Added: %s\n", imagePath); + fprintf(stdout, "Added: %s\n", imagePath.c_str()); - free(imagePath); sprite_file_close(); } diff --git a/src/openrct2/CmdlineSprite.h b/src/openrct2/CmdlineSprite.h index 3d3a4a22b8..a5b2f0e9a3 100644 --- a/src/openrct2/CmdlineSprite.h +++ b/src/openrct2/CmdlineSprite.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index c27269f374..05c57f880e 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -16,6 +16,7 @@ #include "FileClassifier.h" #include "Game.h" #include "GameState.h" +#include "GameStateSnapshots.h" #include "Input.h" #include "Intro.h" #include "OpenRCT2.h" @@ -23,6 +24,7 @@ #include "PlatformEnvironment.h" #include "ReplayManager.h" #include "Version.h" +#include "actions/GameAction.h" #include "audio/AudioContext.h" #include "audio/audio.h" #include "config/Config.h" @@ -44,8 +46,8 @@ #include "localisation/LocalisationService.h" #include "network/DiscordService.h" #include "network/Http.h" +#include "network/Twitch.h" #include "network/network.h" -#include "network/twitch.h" #include "object/ObjectManager.h" #include "object/ObjectRepository.h" #include "paint/Painter.h" @@ -92,12 +94,13 @@ namespace OpenRCT2 std::unique_ptr _trackDesignRepository; std::unique_ptr _scenarioRepository; std::unique_ptr _replayManager; + std::unique_ptr _gameStateSnapshots; #ifdef __ENABLE_DISCORD__ std::unique_ptr _discordService; #endif StdInOutConsole _stdInOutConsole; #ifndef DISABLE_HTTP - Network::Http::Http _http; + Networking::Http::Http _http; #endif // Game states @@ -133,6 +136,7 @@ namespace OpenRCT2 , _audioContext(audioContext) , _uiContext(uiContext) , _localisationService(std::make_unique(env)) + , _painter(std::make_unique(uiContext)) { // Can't have more than one context currently. Guard::Assert(Instance == nullptr); @@ -145,13 +149,17 @@ namespace OpenRCT2 // NOTE: We must shutdown all systems here before Instance is set back to null. // If objects use GetContext() in their destructor things won't go well. - if (_objectManager) + GameActions::ClearQueue(); + network_close(); + window_close_all(); + + // Unload objects after closing all windows, this is to overcome windows like + // the object selection window which loads objects when closed. + if (_objectManager != nullptr) { _objectManager->UnloadAll(); } - network_close(); - window_close_all(); gfx_object_check_all_images_freed(); gfx_unload_g2(); gfx_unload_g1(); @@ -210,6 +218,11 @@ namespace OpenRCT2 return _replayManager.get(); } + IGameStateSnapshots* GetGameStateSnapshots() override + { + return _gameStateSnapshots.get(); + } + int32_t GetDrawingEngineType() override { return _drawingEngineType; @@ -220,6 +233,11 @@ namespace OpenRCT2 return _drawingEngine.get(); } + virtual Paint::Painter* GetPainter() override + { + return _painter.get(); + } + int32_t RunOpenRCT2(int argc, const char** argv) override { if (Initialise()) @@ -334,8 +352,12 @@ namespace OpenRCT2 _trackDesignRepository = CreateTrackDesignRepository(_env); _scenarioRepository = CreateScenarioRepository(_env); _replayManager = CreateReplayManager(); + _gameStateSnapshots = CreateGameStateSnapshots(); #ifdef __ENABLE_DISCORD__ - _discordService = std::make_unique(); + if (!gOpenRCT2Headless) + { + _discordService = std::make_unique(); + } #endif try @@ -410,8 +432,8 @@ namespace OpenRCT2 lightfx_init(); #endif } + gScenarioTicks = 0; - util_srand((uint32_t)time(nullptr)); input_reset_place_obj_modifier(); viewport_init_all(); @@ -425,7 +447,6 @@ namespace OpenRCT2 void InitialiseDrawingEngine() final override { assert(_drawingEngine == nullptr); - assert(_painter == nullptr); _drawingEngineType = gConfigGeneral.drawing_engine; @@ -452,7 +473,6 @@ namespace OpenRCT2 } else { - _painter = std::make_unique(_uiContext); try { drawingEngine->Initialise(); @@ -461,7 +481,6 @@ namespace OpenRCT2 } catch (const std::exception& ex) { - _painter = nullptr; if (_drawingEngineType == DRAWING_ENGINE_SOFTWARE) { _drawingEngineType = DRAWING_ENGINE_NONE; @@ -486,11 +505,11 @@ namespace OpenRCT2 void DisposeDrawingEngine() final override { _drawingEngine = nullptr; - _painter = nullptr; } bool LoadParkFromFile(const std::string& path, bool loadTitleScreenOnFail) final override { + log_verbose("Context::LoadParkFromFile(%s)", path.c_str()); try { auto fs = FileStream(path, FILE_MODE_OPEN); @@ -532,9 +551,12 @@ namespace OpenRCT2 gCurrentLoadedPath = path; gFirstTimeSaving = true; game_fix_save_vars(); + AutoCreateMapAnimations(); sprite_position_tween_reset(); gScreenAge = 0; gLastAutoSaveUpdate = AUTOSAVE_PAUSE; + + bool sendMap = false; if (info.Type == FILE_TYPE::SAVED_GAME) { if (network_get_mode() == NETWORK_MODE_CLIENT) @@ -544,7 +566,7 @@ namespace OpenRCT2 game_load_init(); if (network_get_mode() == NETWORK_MODE_SERVER) { - network_send_map(); + sendMap = true; } } else @@ -552,7 +574,7 @@ namespace OpenRCT2 scenario_begin(); if (network_get_mode() == NETWORK_MODE_SERVER) { - network_send_map(); + sendMap = true; } if (network_get_mode() == NETWORK_MODE_CLIENT) { @@ -562,6 +584,10 @@ namespace OpenRCT2 // This ensures that the newly loaded save reflects the user's // 'show real names of guests' option, now that it's a global setting peep_update_names(gConfigGeneral.show_real_names_of_guests); + if (sendMap) + { + network_send_map(); + } return true; } catch (const ObjectLoadException& e) @@ -695,7 +721,7 @@ namespace OpenRCT2 #ifndef DISABLE_HTTP // Download park and open it using its temporary filename void* data; - size_t dataSize = Network::Http::DownloadPark(gOpenRCT2StartupActionPath, &data); + size_t dataSize = Networking::Http::DownloadPark(gOpenRCT2StartupActionPath, &data); if (dataSize == 0) { title_load(); @@ -968,7 +994,7 @@ namespace OpenRCT2 } #endif - twitch_update(); + Twitch::Update(); chat_update(); _stdInOutConsole.ProcessEvalQueue(); _uiContext->Update(); @@ -991,6 +1017,7 @@ namespace OpenRCT2 DIRID::THEME, DIRID::SEQUENCE, DIRID::REPLAY, + DIRID::LOG_DESYNCS, }); } diff --git a/src/openrct2/Context.h b/src/openrct2/Context.h index ba940adc04..5e147c33ad 100644 --- a/src/openrct2/Context.h +++ b/src/openrct2/Context.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -19,6 +19,7 @@ interface IObjectRepository; interface IScenarioRepository; interface IStream; interface ITrackDesignRepository; +interface IGameStateSnapshots; class Intent; struct rct_window; @@ -88,6 +89,11 @@ namespace OpenRCT2 interface IUiContext; } + namespace Paint + { + interface Painter; + } + /** * Represents an instance of OpenRCT2 and can be used to get various services. */ @@ -105,8 +111,10 @@ namespace OpenRCT2 virtual ITrackDesignRepository* GetTrackDesignRepository() abstract; virtual IScenarioRepository* GetScenarioRepository() abstract; virtual IReplayManager* GetReplayManager() abstract; + virtual IGameStateSnapshots* GetGameStateSnapshots() abstract; virtual int32_t GetDrawingEngineType() abstract; virtual Drawing::IDrawingEngine* GetDrawingEngine() abstract; + virtual Paint::Painter* GetPainter() abstract; virtual int32_t RunOpenRCT2(int argc, const char** argv) abstract; diff --git a/src/openrct2/Date.cpp b/src/openrct2/Date.cpp index 698d99d17e..ff1e1eff60 100644 --- a/src/openrct2/Date.cpp +++ b/src/openrct2/Date.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/Date.h b/src/openrct2/Date.h index db6bb90493..6811c5d56b 100644 --- a/src/openrct2/Date.h +++ b/src/openrct2/Date.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/Diagnostic.cpp b/src/openrct2/Diagnostic.cpp index 47744e5176..7e595c4f1b 100644 --- a/src/openrct2/Diagnostic.cpp +++ b/src/openrct2/Diagnostic.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/Diagnostic.h b/src/openrct2/Diagnostic.h index ef1bbc013e..908cae8621 100644 --- a/src/openrct2/Diagnostic.h +++ b/src/openrct2/Diagnostic.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/Editor.cpp b/src/openrct2/Editor.cpp index f28f53713c..bf8c11c0ad 100644 --- a/src/openrct2/Editor.cpp +++ b/src/openrct2/Editor.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -16,8 +16,11 @@ #include "GameState.h" #include "OpenRCT2.h" #include "ParkImporter.h" +#include "actions/LandBuyRightsAction.hpp" +#include "actions/LandSetRightsAction.hpp" #include "audio/audio.h" #include "interface/Viewport.h" +#include "interface/Window_internal.h" #include "localisation/Localisation.h" #include "localisation/LocalisationService.h" #include "management/NewsItem.h" @@ -84,7 +87,7 @@ namespace Editor gS6Info.category = SCENARIO_CATEGORY_OTHER; viewport_init_all(); rct_window* mainWindow = context_open_window_view(WV_EDITOR_MAIN); - window_set_location(mainWindow, 2400, 2400, 112); + mainWindow->SetLocation(2400, 2400, 112); load_palette(); gScreenAge = 0; gScenarioName = language_get_string(STR_MY_NEW_SCENARIO); @@ -160,7 +163,7 @@ namespace Editor gS6Info.editor_step = EDITOR_STEP_OBJECT_SELECTION; viewport_init_all(); rct_window* mainWindow = context_open_window_view(WV_EDITOR_MAIN); - window_set_location(mainWindow, 2400, 2400, 112); + mainWindow->SetLocation(2400, 2400, 112); load_palette(); } @@ -181,7 +184,7 @@ namespace Editor gS6Info.editor_step = EDITOR_STEP_OBJECT_SELECTION; viewport_init_all(); rct_window* mainWindow = context_open_window_view(WV_EDITOR_MAIN); - window_set_location(mainWindow, 2400, 2400, 112); + mainWindow->SetLocation(2400, 2400, 112); load_palette(); } @@ -193,7 +196,14 @@ namespace Editor { int32_t mapSize = gMapSize; - game_do_command(64, 1, 64, 2, GAME_COMMAND_SET_LAND_OWNERSHIP, (mapSize - 3) * 32, (mapSize - 3) * 32); + MapRange range = { 64, 64, (mapSize - 3) * 32, (mapSize - 3) * 32 }; + auto landSetRightsAction = LandSetRightsAction(range, LandSetRightSetting::SetForSale); + landSetRightsAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND); + GameActions::Execute(&landSetRightsAction); + + auto landBuyRightsAction = LandBuyRightsAction(range, LandBuyRightSetting::BuyLand); + landBuyRightsAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND); + GameActions::Execute(&landBuyRightsAction); } /** @@ -285,21 +295,12 @@ namespace Editor map_remove_all_rides(); // - for (auto& banner : gBanners) + for (BannerIndex i = 0; i < MAX_BANNERS; i++) { - if (banner.type == 255) + auto banner = GetBanner(i); + if (banner->type == BANNER_NULL) { - banner.flags &= ~BANNER_FLAG_LINKED_TO_RIDE; - } - } - - // - { - int32_t i; - Ride* ride; - FOR_ALL_RIDES (i, ride) - { - user_string_free(ride->name); + banner->flags &= ~BANNER_FLAG_LINKED_TO_RIDE; } } @@ -311,7 +312,7 @@ namespace Editor auto peep = get_sprite(i)->AsPeep(); if (peep != nullptr) { - user_string_free(peep->name_string_idx); + peep->SetName({}); } } @@ -491,7 +492,7 @@ namespace Editor return false; } - if (gParkEntrances.size() == 0) + if (gParkEntrances.empty()) { gGameCommandErrorText = STR_NO_PARK_ENTRANCES; return false; @@ -520,7 +521,7 @@ namespace Editor } } - if (gPeepSpawns.size() == 0) + if (gPeepSpawns.empty()) { gGameCommandErrorText = STR_PEEP_SPAWNS_NOT_SET; return false; @@ -529,219 +530,6 @@ namespace Editor return true; } - void GameCommandEditScenarioOptions( - [[maybe_unused]] int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, - [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) - { - if (!(*ebx & GAME_COMMAND_FLAG_APPLY)) - { - *ebx = 0; - return; - } - - switch (*ecx) - { - case EDIT_SCENARIOOPTIONS_SETNOMONEY: - if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) - { - if (*edx != 0) - { - gParkFlags |= PARK_FLAGS_NO_MONEY_SCENARIO; - } - else - { - gParkFlags &= ~PARK_FLAGS_NO_MONEY_SCENARIO; - } - } - else - { - if (*edx != 0) - { - gParkFlags |= PARK_FLAGS_NO_MONEY; - } - else - { - gParkFlags &= ~PARK_FLAGS_NO_MONEY; - } - // Invalidate all windows that have anything to do with finance - window_invalidate_by_class(WC_RIDE); - window_invalidate_by_class(WC_PEEP); - window_invalidate_by_class(WC_PARK_INFORMATION); - window_invalidate_by_class(WC_FINANCES); - window_invalidate_by_class(WC_BOTTOM_TOOLBAR); - window_invalidate_by_class(WC_TOP_TOOLBAR); - } - break; - case EDIT_SCENARIOOPTIONS_SETINITIALCASH: - gInitialCash = std::clamp(*edx, MONEY(0, 00), MONEY(1000000, 00)); - gCash = gInitialCash; - window_invalidate_by_class(WC_FINANCES); - window_invalidate_by_class(WC_BOTTOM_TOOLBAR); - break; - case EDIT_SCENARIOOPTIONS_SETINITIALLOAN: - gBankLoan = std::clamp(*edx, MONEY(0, 00), MONEY(5000000, 00)); - gMaxBankLoan = std::max(gBankLoan, gMaxBankLoan); - window_invalidate_by_class(WC_FINANCES); - break; - case EDIT_SCENARIOOPTIONS_SETMAXIMUMLOANSIZE: - gMaxBankLoan = std::clamp(*edx, MONEY(0, 00), MONEY(5000000, 00)); - gBankLoan = std::min(gBankLoan, gMaxBankLoan); - window_invalidate_by_class(WC_FINANCES); - break; - case EDIT_SCENARIOOPTIONS_SETANNUALINTERESTRATE: - gBankLoanInterestRate = std::clamp(*edx, 0, 80); - window_invalidate_by_class(WC_FINANCES); - break; - case EDIT_SCENARIOOPTIONS_SETFORBIDMARKETINGCAMPAIGNS: - if (*edx != 0) - { - gParkFlags |= PARK_FLAGS_FORBID_MARKETING_CAMPAIGN; - } - else - { - gParkFlags &= ~PARK_FLAGS_FORBID_MARKETING_CAMPAIGN; - } - break; - case EDIT_SCENARIOOPTIONS_SETAVERAGECASHPERGUEST: - gGuestInitialCash = std::clamp(*edx, MONEY(0, 00), MONEY(1000, 00)); - break; - case EDIT_SCENARIOOPTIONS_SETGUESTINITIALHAPPINESS: - gGuestInitialHappiness = std::clamp(*edx, 40, 250); - break; - case EDIT_SCENARIOOPTIONS_SETGUESTINITIALHUNGER: - gGuestInitialHunger = std::clamp(*edx, 40, 250); - break; - case EDIT_SCENARIOOPTIONS_SETGUESTINITIALTHIRST: - gGuestInitialThirst = std::clamp(*edx, 40, 250); - break; - case EDIT_SCENARIOOPTIONS_SETGUESTSPREFERLESSINTENSERIDES: - if (*edx != 0) - { - gParkFlags |= PARK_FLAGS_PREF_LESS_INTENSE_RIDES; - } - else - { - gParkFlags &= ~PARK_FLAGS_PREF_LESS_INTENSE_RIDES; - } - break; - case EDIT_SCENARIOOPTIONS_SETGUESTSPREFERMOREINTENSERIDES: - if (*edx != 0) - { - gParkFlags |= PARK_FLAGS_PREF_MORE_INTENSE_RIDES; - } - else - { - gParkFlags &= ~PARK_FLAGS_PREF_MORE_INTENSE_RIDES; - } - break; - case EDIT_SCENARIOOPTIONS_SETCOSTTOBUYLAND: - gLandPrice = std::clamp(*edx, MONEY(5, 00), MONEY(200, 00)); - break; - case EDIT_SCENARIOOPTIONS_SETCOSTTOBUYCONSTRUCTIONRIGHTS: - gConstructionRightsPrice = std::clamp(*edx, MONEY(5, 00), MONEY(200, 00)); - break; - case EDIT_SCENARIOOPTIONS_SETPARKCHARGEMETHOD: - if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) - { - if (*edx == 0) - { - gParkFlags |= PARK_FLAGS_PARK_FREE_ENTRY; - gParkFlags &= ~PARK_FLAGS_UNLOCK_ALL_PRICES; - gParkEntranceFee = MONEY(0, 00); - } - else if (*edx == 1) - { - gParkFlags &= ~PARK_FLAGS_PARK_FREE_ENTRY; - gParkFlags &= ~PARK_FLAGS_UNLOCK_ALL_PRICES; - gParkEntranceFee = MONEY(10, 00); - } - else - { - gParkFlags |= PARK_FLAGS_PARK_FREE_ENTRY; - gParkFlags |= PARK_FLAGS_UNLOCK_ALL_PRICES; - gParkEntranceFee = MONEY(10, 00); - } - } - else - { - if (*edx == 0) - { - gParkFlags |= PARK_FLAGS_PARK_FREE_ENTRY; - gParkFlags &= ~PARK_FLAGS_UNLOCK_ALL_PRICES; - } - else if (*edx == 1) - { - gParkFlags &= ~PARK_FLAGS_PARK_FREE_ENTRY; - gParkFlags &= ~PARK_FLAGS_UNLOCK_ALL_PRICES; - } - else - { - gParkFlags |= PARK_FLAGS_PARK_FREE_ENTRY; - gParkFlags |= PARK_FLAGS_UNLOCK_ALL_PRICES; - } - window_invalidate_by_class(WC_PARK_INFORMATION); - window_invalidate_by_class(WC_RIDE); - } - break; - case EDIT_SCENARIOOPTIONS_SETPARKCHARGEENTRYFEE: - gParkEntranceFee = std::clamp(*edx, MONEY(0, 00), MAX_ENTRANCE_FEE); - window_invalidate_by_class(WC_PARK_INFORMATION); - break; - case EDIT_SCENARIOOPTIONS_SETFORBIDTREEREMOVAL: - if (*edx != 0) - { - gParkFlags |= PARK_FLAGS_FORBID_TREE_REMOVAL; - } - else - { - gParkFlags &= ~PARK_FLAGS_FORBID_TREE_REMOVAL; - } - break; - case EDIT_SCENARIOOPTIONS_SETFORBIDLANDSCAPECHANGES: - if (*edx != 0) - { - gParkFlags |= PARK_FLAGS_FORBID_LANDSCAPE_CHANGES; - } - else - { - gParkFlags &= ~PARK_FLAGS_FORBID_LANDSCAPE_CHANGES; - } - break; - case EDIT_SCENARIOOPTIONS_SETFORBIDHIGHCONSTRUCTION: - if (*edx != 0) - { - gParkFlags |= PARK_FLAGS_FORBID_HIGH_CONSTRUCTION; - } - else - { - gParkFlags &= ~PARK_FLAGS_FORBID_HIGH_CONSTRUCTION; - } - break; - case EDIT_SCENARIOOPTIONS_SETPARKRATINGHIGHERDIFFICULTLEVEL: - if (*edx != 0) - { - gParkFlags |= PARK_FLAGS_DIFFICULT_PARK_RATING; - } - else - { - gParkFlags &= ~PARK_FLAGS_DIFFICULT_PARK_RATING; - } - break; - case EDIT_SCENARIOOPTIONS_SETGUESTGENERATIONHIGHERDIFFICULTLEVEL: - if (*edx != 0) - { - gParkFlags |= PARK_FLAGS_DIFFICULT_GUEST_GENERATION; - } - else - { - gParkFlags &= ~PARK_FLAGS_DIFFICULT_GUEST_GENERATION; - } - break; - } - window_invalidate_by_class(WC_EDITOR_SCENARIO_OPTIONS); - *ebx = 0; - } - uint8_t GetSelectedObjectFlags(int32_t objectType, size_t index) { uint8_t result = 0; @@ -778,9 +566,3 @@ void editor_open_windows_for_current_step() { Editor::OpenWindowsForCurrentStep(); } - -void game_command_edit_scenario_options( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp) -{ - Editor::GameCommandEditScenarioOptions(eax, ebx, ecx, edx, esi, edi, ebp); -} diff --git a/src/openrct2/Editor.h b/src/openrct2/Editor.h index d3acde5d4f..50bfb8d3fd 100644 --- a/src/openrct2/Editor.h +++ b/src/openrct2/Editor.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -24,7 +24,6 @@ namespace Editor int32_t CheckObjectSelection(); void OpenWindowsForCurrentStep(); - void GameCommandEditScenarioOptions(int32_t*, int32_t*, int32_t*, int32_t*, int32_t*, int32_t*, int32_t*); uint8_t GetSelectedObjectFlags(int32_t objectType, size_t index); void ClearSelectedObject(int32_t objectType, size_t index, uint32_t flags); @@ -70,7 +69,4 @@ enum void editor_open_windows_for_current_step(); -void game_command_edit_scenario_options( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); - #endif diff --git a/src/openrct2/EditorObjectSelectionSession.cpp b/src/openrct2/EditorObjectSelectionSession.cpp index 3968f03bda..14a4bf90ba 100644 --- a/src/openrct2/EditorObjectSelectionSession.cpp +++ b/src/openrct2/EditorObjectSelectionSession.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -125,7 +125,6 @@ void setup_in_use_selection_flags() do { uint16_t type; - rct_banner* banner; switch (iter.element->GetType()) { @@ -173,18 +172,23 @@ void setup_in_use_selection_flags() Editor::SetSelectedObject(OBJECT_TYPE_LARGE_SCENERY, type, OBJECT_SELECTION_FLAG_SELECTED); break; case TILE_ELEMENT_TYPE_BANNER: - banner = &gBanners[iter.element->AsBanner()->GetIndex()]; - type = banner->type; - assert(type < object_entry_group_counts[OBJECT_TYPE_BANNERS]); - Editor::SetSelectedObject(OBJECT_TYPE_BANNERS, type, OBJECT_SELECTION_FLAG_SELECTED); + { + auto banner = iter.element->AsBanner()->GetBanner(); + if (banner != nullptr) + { + type = banner->type; + assert(type < object_entry_group_counts[OBJECT_TYPE_BANNERS]); + Editor::SetSelectedObject(OBJECT_TYPE_BANNERS, type, OBJECT_SELECTION_FLAG_SELECTED); + } break; + } } } while (tile_element_iterator_next(&iter)); for (uint8_t ride_index = 0; ride_index < 0xFF; ride_index++) { - Ride* ride = get_ride(ride_index); - if (ride->type != RIDE_TYPE_NULL) + auto ride = get_ride(ride_index); + if (ride != nullptr) { uint8_t type = ride->subtype; Editor::SetSelectedObject(OBJECT_TYPE_RIDE, type, OBJECT_SELECTION_FLAG_SELECTED); @@ -288,7 +292,7 @@ static void remove_selected_objects_from_research(const rct_object_entry* instal for (auto rideType : rideEntry->ride_type) { - rct_research_item tmp = {}; + ResearchItem tmp = {}; tmp.type = RESEARCH_ENTRY_TYPE_RIDE; tmp.entryIndex = entry_index; tmp.baseRideType = rideType; @@ -297,7 +301,7 @@ static void remove_selected_objects_from_research(const rct_object_entry* instal } else if (entry_type == OBJECT_TYPE_SCENERY_GROUP) { - rct_research_item tmp = {}; + ResearchItem tmp = {}; tmp.type = RESEARCH_ENTRY_TYPE_SCENERY; tmp.entryIndex = entry_index; research_remove(&tmp); @@ -526,20 +530,23 @@ int32_t editor_remove_unused_objects() int32_t numUnselectedObjects = 0; for (int32_t i = 0; i < numObjects; i++) { - if (!(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_IN_USE) - && !(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED)) + if (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED) { - const ObjectRepositoryItem* item = &items[i]; - uint8_t objectType = object_entry_get_type(&item->ObjectEntry); - - if (objectType >= OBJECT_TYPE_SCENERY_GROUP) + if (!(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_IN_USE) + && !(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED)) { - continue; - } + const ObjectRepositoryItem* item = &items[i]; + uint8_t objectType = object_entry_get_type(&item->ObjectEntry); - _numSelectedObjectsForType[objectType]--; - _objectSelectionFlags[i] &= ~OBJECT_SELECTION_FLAG_SELECTED; - numUnselectedObjects++; + if (objectType >= OBJECT_TYPE_SCENERY_GROUP) + { + continue; + } + + _numSelectedObjectsForType[objectType]--; + _objectSelectionFlags[i] &= ~OBJECT_SELECTION_FLAG_SELECTED; + numUnselectedObjects++; + } } } unload_unselected_objects(); diff --git a/src/openrct2/EditorObjectSelectionSession.h b/src/openrct2/EditorObjectSelectionSession.h index 454d2e0ab4..0b6f94fb11 100644 --- a/src/openrct2/EditorObjectSelectionSession.h +++ b/src/openrct2/EditorObjectSelectionSession.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/FileClassifier.cpp b/src/openrct2/FileClassifier.cpp index c2ea8b7fca..42b956eb66 100644 --- a/src/openrct2/FileClassifier.cpp +++ b/src/openrct2/FileClassifier.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/FileClassifier.h b/src/openrct2/FileClassifier.h index 207e57d7a9..0fbc118180 100644 --- a/src/openrct2/FileClassifier.h +++ b/src/openrct2/FileClassifier.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/Game.cpp b/src/openrct2/Game.cpp index 7a46e8b733..2032e8e63c 100644 --- a/src/openrct2/Game.cpp +++ b/src/openrct2/Game.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,7 @@ #include "Context.h" #include "Editor.h" #include "FileClassifier.h" +#include "GameStateSnapshots.h" #include "Input.h" #include "OpenRCT2.h" #include "ParkImporter.h" @@ -73,62 +74,18 @@ float gDayNightCycle = 0; bool gInUpdateCode = false; bool gInMapInitCode = false; int32_t gGameCommandNestLevel; -bool gGameCommandIsNetworked; std::string gCurrentLoadedPath; bool gLoadKeepWindowsOpen = false; -uint8_t gUnk13CA740; -uint8_t gUnk141F568; - uint32_t gCurrentTicks; uint32_t gCurrentRealTimeTicks; -// clang-format off -GAME_COMMAND_CALLBACK_POINTER * game_command_callback = nullptr; -static GAME_COMMAND_CALLBACK_POINTER * const game_command_callback_table[] = { - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - game_command_callback_place_banner, - nullptr, - game_command_callback_hire_new_staff_member, - game_command_callback_pickup_guest, - game_command_callback_pickup_staff -}; -// clang-format on -int32_t game_command_playerid = -1; - rct_string_id gGameCommandErrorTitle; rct_string_id gGameCommandErrorText; -uint8_t gErrorType; -rct_string_id gErrorStringId; using namespace OpenRCT2; -int32_t game_command_callback_get_index(GAME_COMMAND_CALLBACK_POINTER* callback) -{ - for (uint32_t i = 0; i < std::size(game_command_callback_table); i++) - { - if (game_command_callback_table[i] == callback) - { - return i; - } - } - return 0; -} - -GAME_COMMAND_CALLBACK_POINTER* game_command_callback_get_callback(uint32_t index) -{ - if (index < std::size(game_command_callback_table)) - { - return game_command_callback_table[index]; - } - return nullptr; -} - void game_increase_game_speed() { gGameSpeed = std::min(gConfigGeneral.debugging_tools ? 5 : 4, gGameSpeed + 1); @@ -335,10 +292,6 @@ void update_palette_effects() */ static int32_t game_check_affordability(int32_t cost, uint32_t flags) { - // Only checked for game commands. - if (gUnk141F568 & 0xF0) - return cost; - if (finance_check_affordability(cost, flags)) return cost; @@ -401,36 +354,11 @@ int32_t game_do_command_p( if (gGameCommandNestLevel == 0) { gGameCommandErrorText = STR_NONE; - gGameCommandIsNetworked = (flags & GAME_COMMAND_FLAG_NETWORKED) != 0; } // Increment nest count gGameCommandNestLevel++; - // Remove ghost scenery so it doesn't interfere with incoming network command - if ((flags & GAME_COMMAND_FLAG_NETWORKED) && !(flags & GAME_COMMAND_FLAG_GHOST) - && (command == GAME_COMMAND_PLACE_WALL || command == GAME_COMMAND_PLACE_SCENERY - || command == GAME_COMMAND_PLACE_LARGE_SCENERY || command == GAME_COMMAND_PLACE_BANNER - || command == GAME_COMMAND_PLACE_PATH)) - { - scenery_remove_ghost_tool_placement(); - } - - if (game_command_playerid == -1) - { - game_command_playerid = network_get_current_player_id(); - } - - // Log certain commands if we are in multiplayer and logging is enabled - bool serverLog = (network_get_mode() == NETWORK_MODE_SERVER) && gGameCommandNestLevel == 1 - && gConfigNetwork.log_server_actions; - bool clientLog = (network_get_mode() == NETWORK_MODE_CLIENT) && (flags & GAME_COMMAND_FLAG_NETWORKED) - && gGameCommandNestLevel == 1 && gConfigNetwork.log_server_actions; - if (serverLog || clientLog) - { - game_log_multiplayer_command(command, eax, ebx, ecx, edx, edi, ebp); - } - *ebx &= ~GAME_COMMAND_FLAG_APPLY; // Make sure the camera position won't change if the command skips setting them. @@ -446,7 +374,7 @@ int32_t game_do_command_p( { // Check funds int32_t insufficientFunds = 0; - if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_2) && !(flags & GAME_COMMAND_FLAG_5) && cost != 0) + if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_NO_SPEND) && cost != 0) insufficientFunds = game_check_affordability(cost, flags); if (insufficientFunds != MONEY32_UNDEFINED) @@ -464,59 +392,9 @@ int32_t game_do_command_p( return cost; } - if (network_get_mode() != NETWORK_MODE_NONE && !(flags & GAME_COMMAND_FLAG_NETWORKED) - && !(flags & GAME_COMMAND_FLAG_GHOST) && !(flags & GAME_COMMAND_FLAG_5) - && gGameCommandNestLevel == 1) /* Send only top-level commands */ - { - network_send_gamecmd( - *eax, *ebx, *ecx, *edx, *esi, *edi, *ebp, game_command_callback_get_index(game_command_callback)); - if (network_get_mode() == NETWORK_MODE_CLIENT) - { - // Client sent the command to the server, do not run it locally, just return. It will run when server - // sends it. - game_command_callback = nullptr; - // Decrement nest count - gGameCommandNestLevel--; - return cost; - } - } - // Second call to actually perform the operation new_game_command_table[command](eax, ebx, ecx, edx, esi, edi, ebp); - if (replayManager != nullptr) - { - bool recordCommand = false; - bool commandExecutes = (flags & GAME_COMMAND_FLAG_APPLY) && (flags & GAME_COMMAND_FLAG_GHOST) == 0 - && (flags & GAME_COMMAND_FLAG_5) == 0; - - if (replayManager->IsRecording() && commandExecutes) - recordCommand = true; - else if (replayManager->IsNormalising() && commandExecutes && (flags & GAME_COMMAND_FLAG_REPLAY) != 0) - recordCommand = true; - - if (recordCommand && gGameCommandNestLevel == 1) - { - int32_t callback = game_command_callback_get_index(game_command_callback); - - replayManager->AddGameCommand( - gCurrentTicks, *eax, original_ebx, *ecx, original_edx, original_esi, original_edi, original_ebp, - callback); - } - } - - // Do the callback (required for multiplayer to work correctly), but only for top level commands - if (gGameCommandNestLevel == 1) - { - if (game_command_callback && !(flags & GAME_COMMAND_FLAG_GHOST)) - { - game_command_callback(*eax, *ebx, *ecx, *edx, *esi, *edi, *ebp); - game_command_callback = nullptr; - } - } - - game_command_playerid = -1; - *edx = *ebx; if (*edx != MONEY32_UNDEFINED && *edx < cost) @@ -532,19 +410,10 @@ int32_t game_do_command_p( { // Update money balance finance_payment(cost, gCommandExpenditureType); - if (gUnk141F568 == gUnk13CA740) - { - // Create a +/- money text effect - if (cost != 0 && game_is_not_paused()) - money_effect_create(cost); - } - } - if (network_get_mode() == NETWORK_MODE_SERVER && !(flags & GAME_COMMAND_FLAG_NETWORKED) - && !(flags & GAME_COMMAND_FLAG_GHOST)) - { - network_set_player_last_action(network_get_player_index(network_get_current_player_id()), command); - network_add_player_money_spent(network_get_current_player_id(), cost); + // Create a +/- money text effect + if (cost != 0 && game_is_not_paused()) + rct_money_effect::Create(cost); } // Start autosave timer after game command @@ -560,13 +429,9 @@ int32_t game_do_command_p( // Decrement nest count gGameCommandNestLevel--; - // Clear the game command callback to prevent the next command triggering it - game_command_callback = nullptr; - // Show error window - if (gGameCommandNestLevel == 0 && (flags & GAME_COMMAND_FLAG_APPLY) && gUnk141F568 == gUnk13CA740 - && !(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) && !(flags & GAME_COMMAND_FLAG_NETWORKED) - && !(flags & GAME_COMMAND_FLAG_GHOST)) + if (gGameCommandNestLevel == 0 && (flags & GAME_COMMAND_FLAG_APPLY) && !(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) + && !(flags & GAME_COMMAND_FLAG_NETWORKED) && !(flags & GAME_COMMAND_FLAG_GHOST)) { context_show_error(gGameCommandErrorTitle, gGameCommandErrorText); } @@ -574,134 +439,6 @@ int32_t game_do_command_p( return MONEY32_UNDEFINED; } -void game_log_multiplayer_command(int command, const int* eax, const int* ebx, const int* ecx, int* edx, int* edi, int* ebp) -{ - // Get player name - const char* player_name = "localhost"; - - int player_index = network_get_player_index(game_command_playerid); - if (player_index != -1) - { - player_name = network_get_player_name(player_index); - } - - char log_msg[256]; - if (command == GAME_COMMAND_CHEAT) - { - // Get cheat name - const char* cheat = cheats_get_cheat_string(*ecx, *edx, *edi); - char* args[2] = { - (char*)player_name, - (char*)cheat, - }; - format_string(log_msg, 256, STR_LOG_CHEAT_USED, args); - network_append_server_log(log_msg); - } - else if (command == GAME_COMMAND_DEMOLISH_RIDE && (*ebp == 1 || *ebp == 0)) - { // ebp is 1 if command comes from ride window prompt, so we don't log "demolishing" ride previews - // Get ride name - Ride* ride = get_ride(*edx); - char ride_name[128]; - format_string(ride_name, 128, ride->name, &ride->name_arguments); - - char* args[2] = { - (char*)player_name, - ride_name, - }; - format_string(log_msg, 256, STR_LOG_DEMOLISH_RIDE, args); - network_append_server_log(log_msg); - } - else if (command == GAME_COMMAND_SET_PARK_OPEN) - { - // Log change in park open/close - char* args[1] = { - (char*)player_name, - }; - - if (*edx >> 8 == 0) - { - format_string(log_msg, 256, STR_LOG_PARK_OPEN, args); - } - else if (*edx >> 8 == 1) - { - format_string(log_msg, 256, STR_LOG_PARK_CLOSED, args); - } - - network_append_server_log(log_msg); - } - else if ( - command == GAME_COMMAND_PLACE_WALL || command == GAME_COMMAND_PLACE_LARGE_SCENERY - || command == GAME_COMMAND_PLACE_BANNER) - { - uint8_t flags = *ebx & 0xFF; - if (flags & GAME_COMMAND_FLAG_GHOST) - { - // Don't log ghost previews being removed - return; - } - - // Log placing scenery - char* args[1] = { - (char*)player_name, - }; - - format_string(log_msg, 256, STR_LOG_PLACE_SCENERY, args); - network_append_server_log(log_msg); - } - else if (command == GAME_COMMAND_REMOVE_BANNER) - { - uint8_t flags = *ebx & 0xFF; - if (flags & GAME_COMMAND_FLAG_GHOST) - { - // Don't log ghost previews being removed - return; - } - - // Log removing scenery - char* args[1] = { - (char*)player_name, - }; - format_string(log_msg, 256, STR_LOG_REMOVE_SCENERY, args); - network_append_server_log(log_msg); - } - else if ( - command == GAME_COMMAND_SET_SCENERY_COLOUR || command == GAME_COMMAND_SET_WALL_COLOUR - || command == GAME_COMMAND_SET_LARGE_SCENERY_COLOUR || command == GAME_COMMAND_SET_BANNER_COLOUR - || command == GAME_COMMAND_SET_BANNER_STYLE) - { - // Log editing scenery - char* args[1] = { - (char*)player_name, - }; - format_string(log_msg, 256, STR_LOG_EDIT_SCENERY, args); - network_append_server_log(log_msg); - if (command == GAME_COMMAND_SET_BANNER_NAME || command == GAME_COMMAND_SET_SIGN_NAME) - { - static char banner_name[128]; - - std::fill_n(banner_name, sizeof(banner_name), ' '); - int nameChunkIndex = *eax & 0xFFFF; - - int nameChunkOffset = nameChunkIndex - 1; - if (nameChunkOffset < 0) - nameChunkOffset = 2; - nameChunkOffset *= 12; - nameChunkOffset = std::min(nameChunkOffset, (int32_t)(std::size(banner_name) - 12)); - std::memcpy(banner_name + nameChunkOffset + 0, edx, 4); - std::memcpy(banner_name + nameChunkOffset + 4, ebp, 4); - std::memcpy(banner_name + nameChunkOffset + 8, edi, 4); - banner_name[sizeof(banner_name) - 1] = '\0'; - - char* args_sign[2] = { - (char*)player_name, - (char*)banner_name, - }; - format_string(log_msg, 256, STR_LOG_SET_SIGN_NAME, args_sign); - network_append_server_log(log_msg); - } - } -} - void pause_toggle() { gGamePaused ^= GAME_PAUSED_NORMAL; @@ -788,16 +525,6 @@ void game_convert_strings_to_utf8() gScenarioName = rct2_to_utf8(gScenarioName, RCT2_LANGUAGE_ID_ENGLISH_UK); gScenarioDetails = rct2_to_utf8(gScenarioDetails, RCT2_LANGUAGE_ID_ENGLISH_UK); - // User strings - for (auto* string : gUserStrings) - { - if (!str_is_null_or_empty(string)) - { - rct2_to_utf8_self(string, RCT12_USER_STRING_MAX_LENGTH); - utf8_remove_formatting(string, true); - } - } - // News items game_convert_news_items_to_utf8(); } @@ -877,10 +604,9 @@ void game_fix_save_vars() continue; } set_format_arg(0, uint32_t, peep->id); - utf8* curName = gCommonStringFormatBuffer; - rct_string_id curId = peep->name_string_idx; - format_string(curName, 256, curId, gCommonFormatArgs); - log_warning("Peep %u (%s) has invalid ride station = %u for ride %u.", spriteIndex, curName, srcStation, rideIdx); + auto curName = peep->GetName(); + log_warning( + "Peep %u (%s) has invalid ride station = %u for ride %u.", spriteIndex, curName.c_str(), srcStation, rideIdx); int8_t station = ride_get_first_valid_station_exit(get_ride(rideIdx)); if (station == -1) { @@ -895,7 +621,7 @@ void game_fix_save_vars() } } - if (peepsToRemove.size() > 0) + if (!peepsToRemove.empty()) { // Some broken saves have broken spatial indexes reset_sprite_spatial_index(); @@ -912,27 +638,28 @@ void game_fix_save_vars() { for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) { - TileElement* tileElement = map_get_surface_element_at(x, y); + auto* surfaceElement = map_get_surface_element_at(x, y); - if (tileElement == nullptr) + if (surfaceElement == nullptr) { log_error("Null map element at x = %d and y = %d. Fixing...", x, y); - tileElement = tile_element_insert(x, y, 14, 0); + auto tileElement = tile_element_insert({ x, y, 14 }, 0b0000); if (tileElement == nullptr) { log_error("Unable to fix: Map element limit reached."); return; } + surfaceElement = tileElement->AsSurface(); } // Fix the invisible border tiles. - // At this point, we can be sure that tileElement is not NULL. + // At this point, we can be sure that surfaceElement is not NULL. if (x == 0 || x == gMapSize - 1 || y == 0 || y == gMapSize - 1) { - tileElement->base_height = 2; - tileElement->clearance_height = 2; - tileElement->AsSurface()->SetSlope(0); - tileElement->AsSurface()->SetWaterHeight(0); + surfaceElement->base_height = 2; + surfaceElement->clearance_height = 2; + surfaceElement->SetSlope(0); + surfaceElement->SetWaterHeight(0); } } } @@ -956,6 +683,9 @@ void game_load_init() { rct_window* mainWindow; + IGameStateSnapshots* snapshots = GetContext()->GetGameStateSnapshots(); + snapshots->Reset(); + gScreenFlags = SCREEN_FLAGS_PLAYING; audio_stop_all_music_and_sounds(); if (!gLoadKeepWindowsOpen) @@ -975,6 +705,7 @@ void game_load_init() if (network_get_mode() != NETWORK_MODE_CLIENT) { + GameActions::ClearQueue(); reset_sprite_spatial_index(); } reset_all_sprite_quadrant_placements(); @@ -1266,11 +997,6 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = { nullptr, nullptr, nullptr, - game_command_create_ride, - game_command_demolish_ride, - game_command_set_ride_status, - nullptr, - game_command_set_ride_name, nullptr, nullptr, nullptr, @@ -1278,59 +1004,64 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = { nullptr, nullptr, nullptr, - game_command_place_footpath_from_track, - nullptr, - game_command_change_surface_style, - nullptr, - game_command_set_guest_name, - game_command_set_staff_name, - game_command_raise_land, - game_command_lower_land, - game_command_smooth_land, - game_command_raise_water, - game_command_lower_water, - game_command_set_brakes_speed, - game_command_hire_new_staff_member, - game_command_set_staff_patrol, - game_command_fire_staff_member, nullptr, nullptr, - game_command_set_park_open, - game_command_buy_land_rights, - game_command_place_park_entrance, - game_command_remove_park_entrance, - game_command_set_maze_track, - game_command_set_park_entrance_fee, nullptr, - game_command_place_wall, nullptr, - game_command_place_large_scenery, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, nullptr, nullptr, nullptr, game_command_place_track_design, nullptr, game_command_place_maze_design, - game_command_place_banner, - game_command_remove_banner, - game_command_set_scenery_colour, - game_command_set_wall_colour, - game_command_set_large_scenery_colour, - game_command_set_banner_colour, - game_command_set_land_ownership, nullptr, nullptr, nullptr, - game_command_set_banner_style, nullptr, - game_command_set_player_group, - game_command_modify_groups, - game_command_kick_player, - game_command_cheat, - game_command_pickup_guest, - game_command_pickup_staff, - game_command_balloon_press, - game_command_modify_tile, - game_command_edit_scenario_options, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, NULL, }; diff --git a/src/openrct2/Game.h b/src/openrct2/Game.h index 99961ed78e..37b150210b 100644 --- a/src/openrct2/Game.h +++ b/src/openrct2/Game.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -36,66 +36,68 @@ enum GAME_COMMAND GAME_COMMAND_PLACE_SCENERY, // GA GAME_COMMAND_SET_WATER_HEIGHT, // GA GAME_COMMAND_PLACE_PATH, // GA - GAME_COMMAND_PLACE_PATH_FROM_TRACK, - GAME_COMMAND_REMOVE_PATH, // GA - GAME_COMMAND_CHANGE_SURFACE_STYLE, - GAME_COMMAND_SET_RIDE_PRICE, // GA - GAME_COMMAND_SET_GUEST_NAME, // GA - GAME_COMMAND_SET_STAFF_NAME, // GA - GAME_COMMAND_RAISE_LAND, - GAME_COMMAND_LOWER_LAND, - GAME_COMMAND_EDIT_LAND_SMOOTH, - GAME_COMMAND_RAISE_WATER, - GAME_COMMAND_LOWER_WATER, - GAME_COMMAND_SET_BRAKES_SPEED, - GAME_COMMAND_HIRE_NEW_STAFF_MEMBER, - GAME_COMMAND_SET_STAFF_PATROL, - GAME_COMMAND_FIRE_STAFF_MEMBER, - GAME_COMMAND_SET_STAFF_ORDERS, // GA - GAME_COMMAND_SET_PARK_NAME, // GA - GAME_COMMAND_SET_PARK_OPEN, - GAME_COMMAND_BUY_LAND_RIGHTS, - GAME_COMMAND_PLACE_PARK_ENTRANCE, // GA - GAME_COMMAND_REMOVE_PARK_ENTRANCE, - GAME_COMMAND_SET_MAZE_TRACK, - GAME_COMMAND_SET_PARK_ENTRANCE_FEE, // GA - GAME_COMMAND_SET_STAFF_COLOUR, // GA - GAME_COMMAND_PLACE_WALL, - GAME_COMMAND_REMOVE_WALL, // GA - GAME_COMMAND_PLACE_LARGE_SCENERY, - GAME_COMMAND_REMOVE_LARGE_SCENERY, // GA - GAME_COMMAND_SET_CURRENT_LOAN, // GA - GAME_COMMAND_SET_RESEARCH_FUNDING, // GA + GAME_COMMAND_PLACE_PATH_FROM_TRACK, // GA + GAME_COMMAND_REMOVE_PATH, // GA + GAME_COMMAND_CHANGE_SURFACE_STYLE, // GA + GAME_COMMAND_SET_RIDE_PRICE, // GA + GAME_COMMAND_SET_GUEST_NAME, // GA + GAME_COMMAND_SET_STAFF_NAME, // GA + GAME_COMMAND_RAISE_LAND, // GA + GAME_COMMAND_LOWER_LAND, // GA + GAME_COMMAND_EDIT_LAND_SMOOTH, // GA + GAME_COMMAND_RAISE_WATER, // GA + GAME_COMMAND_LOWER_WATER, // GA + GAME_COMMAND_SET_BRAKES_SPEED, // GA + GAME_COMMAND_HIRE_NEW_STAFF_MEMBER, // GA + GAME_COMMAND_SET_STAFF_PATROL, // GA + GAME_COMMAND_FIRE_STAFF_MEMBER, // GA + GAME_COMMAND_SET_STAFF_ORDERS, // GA + GAME_COMMAND_SET_PARK_NAME, // GA + GAME_COMMAND_SET_PARK_OPEN, // GA + GAME_COMMAND_BUY_LAND_RIGHTS, // GA + GAME_COMMAND_PLACE_PARK_ENTRANCE, // GA + GAME_COMMAND_REMOVE_PARK_ENTRANCE, // GA + GAME_COMMAND_SET_MAZE_TRACK, // GA + GAME_COMMAND_SET_PARK_ENTRANCE_FEE, // GA + GAME_COMMAND_SET_STAFF_COLOUR, // GA + GAME_COMMAND_PLACE_WALL, // GA + GAME_COMMAND_REMOVE_WALL, // GA + GAME_COMMAND_PLACE_LARGE_SCENERY, // GA + GAME_COMMAND_REMOVE_LARGE_SCENERY, // GA + GAME_COMMAND_SET_CURRENT_LOAN, // GA + GAME_COMMAND_SET_RESEARCH_FUNDING, // GA GAME_COMMAND_PLACE_TRACK_DESIGN, GAME_COMMAND_START_MARKETING_CAMPAIGN, // GA GAME_COMMAND_PLACE_MAZE_DESIGN, - GAME_COMMAND_PLACE_BANNER, - GAME_COMMAND_REMOVE_BANNER, - GAME_COMMAND_SET_SCENERY_COLOUR, - GAME_COMMAND_SET_WALL_COLOUR, - GAME_COMMAND_SET_LARGE_SCENERY_COLOUR, - GAME_COMMAND_SET_BANNER_COLOUR, - GAME_COMMAND_SET_LAND_OWNERSHIP, - GAME_COMMAND_CLEAR_SCENERY, // GA - GAME_COMMAND_SET_BANNER_NAME, // GA - GAME_COMMAND_SET_SIGN_NAME, // GA - GAME_COMMAND_SET_BANNER_STYLE, - GAME_COMMAND_SET_SIGN_STYLE, // GA - GAME_COMMAND_SET_PLAYER_GROUP, - GAME_COMMAND_MODIFY_GROUPS, - GAME_COMMAND_KICK_PLAYER, - GAME_COMMAND_CHEAT, - GAME_COMMAND_PICKUP_GUEST, - GAME_COMMAND_PICKUP_STAFF, - GAME_COMMAND_BALLOON_PRESS, - GAME_COMMAND_MODIFY_TILE, - GAME_COMMAND_EDIT_SCENARIO_OPTIONS, - GAME_COMMAND_PLACE_PEEP_SPAWN, // GA, TODO: refactor to separate array for just game actions - GAME_COMMAND_SET_CLIMATE, // GA - GAME_COMMAND_SET_COLOUR_SCHEME, // GA - GAME_COMMAND_SET_STAFF_COSTUME, // GA - GAME_COMMAND_PLACE_FOOTPATH_SCENERY, // GA - GAME_COMMAND_REMOVE_FOOTPATH_SCENERY, // GA + GAME_COMMAND_PLACE_BANNER, // GA + GAME_COMMAND_REMOVE_BANNER, // GA + GAME_COMMAND_SET_SCENERY_COLOUR, // GA + GAME_COMMAND_SET_WALL_COLOUR, // GA + GAME_COMMAND_SET_LARGE_SCENERY_COLOUR, // GA + GAME_COMMAND_SET_BANNER_COLOUR, // GA + GAME_COMMAND_SET_LAND_OWNERSHIP, // GA + GAME_COMMAND_CLEAR_SCENERY, // GA + GAME_COMMAND_SET_BANNER_NAME, // GA + GAME_COMMAND_SET_SIGN_NAME, // GA + GAME_COMMAND_SET_BANNER_STYLE, // GA + GAME_COMMAND_SET_SIGN_STYLE, // GA + GAME_COMMAND_SET_PLAYER_GROUP, // GA + GAME_COMMAND_MODIFY_GROUPS, // GA + GAME_COMMAND_KICK_PLAYER, // GA + GAME_COMMAND_CHEAT, // GA + GAME_COMMAND_PICKUP_GUEST, // GA + GAME_COMMAND_PICKUP_STAFF, // GA + GAME_COMMAND_BALLOON_PRESS, // GA + GAME_COMMAND_MODIFY_TILE, // GA + GAME_COMMAND_EDIT_SCENARIO_OPTIONS, // GA + GAME_COMMAND_PLACE_PEEP_SPAWN, // GA, TODO: refactor to separate array for just game actions + GAME_COMMAND_SET_CLIMATE, // GA + GAME_COMMAND_SET_COLOUR_SCHEME, // GA + GAME_COMMAND_SET_STAFF_COSTUME, // GA + GAME_COMMAND_PLACE_FOOTPATH_SCENERY, // GA + GAME_COMMAND_REMOVE_FOOTPATH_SCENERY, // GA + GAME_COMMAND_GUEST_SET_FLAGS, // GA + GAME_COMMAND_SET_DATE, // GA GAME_COMMAND_COUNT, }; @@ -103,11 +105,11 @@ enum : uint32_t { GAME_COMMAND_FLAG_APPLY = (1 << 0), // If this flag is set, the command is applied, otherwise only the cost is retrieved GAME_COMMAND_FLAG_REPLAY = (1 << 1), // Command was issued from replay manager. - GAME_COMMAND_FLAG_2 = (1 << 2), + GAME_COMMAND_FLAG_2 = (1 << 2), // Unused GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED = (1 << 3), // Allow while paused - GAME_COMMAND_FLAG_4 = (1 << 4), - GAME_COMMAND_FLAG_5 = (1 << 5), - GAME_COMMAND_FLAG_GHOST = (1 << 6), + GAME_COMMAND_FLAG_4 = (1 << 4), // Unused + GAME_COMMAND_FLAG_NO_SPEND = (1 << 5), // Game command is not networked + GAME_COMMAND_FLAG_GHOST = (1 << 6), // Game command is not networked GAME_COMMAND_FLAG_PATH_SCENERY = (1 << 7), GAME_COMMAND_FLAG_NETWORKED = (1u << 31) // Game command is coming from network }; @@ -128,18 +130,9 @@ enum using GAME_COMMAND_POINTER = void( int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -using GAME_COMMAND_CALLBACK_POINTER = void( - int32_t eax, int32_t ebx, int32_t ecx, int32_t edx, int32_t esi, int32_t edi, int32_t ebp); - -extern GAME_COMMAND_CALLBACK_POINTER* game_command_callback; -int32_t game_command_callback_get_index(GAME_COMMAND_CALLBACK_POINTER* callback); -GAME_COMMAND_CALLBACK_POINTER* game_command_callback_get_callback(uint32_t index); -extern int32_t game_command_playerid; extern rct_string_id gGameCommandErrorTitle; extern rct_string_id gGameCommandErrorText; -extern uint8_t gErrorType; -extern rct_string_id gErrorStringId; extern GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT]; @@ -154,14 +147,10 @@ extern float gDayNightCycle; extern bool gInUpdateCode; extern bool gInMapInitCode; extern int32_t gGameCommandNestLevel; -extern bool gGameCommandIsNetworked; extern std::string gCurrentLoadedPath; extern bool gLoadKeepWindowsOpen; -extern uint8_t gUnk13CA740; -extern uint8_t gUnk141F568; - void game_increase_game_speed(); void game_reduce_game_speed(); @@ -173,8 +162,6 @@ int32_t game_do_command(int32_t eax, int32_t ebx, int32_t ecx, int32_t edx, int3 int32_t game_do_command_p( uint32_t command, int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_log_multiplayer_command(int command, const int* eax, const int* ebx, const int* ecx, int* edx, int* edi, int* ebp); - void game_load_or_quit_no_save_prompt(); void load_from_sv6(const char* path); void game_load_init(); diff --git a/src/openrct2/GameState.cpp b/src/openrct2/GameState.cpp index cb6de02a1e..38f0c82caa 100644 --- a/src/openrct2/GameState.cpp +++ b/src/openrct2/GameState.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,9 +12,12 @@ #include "Context.h" #include "Editor.h" #include "Game.h" +#include "GameStateSnapshots.h" #include "Input.h" #include "OpenRCT2.h" #include "ReplayManager.h" +#include "actions/GameAction.h" +#include "config/Config.h" #include "interface/Screenshot.h" #include "localisation/Date.h" #include "localisation/Localisation.h" @@ -50,7 +53,6 @@ void GameState::InitAll(int32_t mapSize) map_init(mapSize); _park->Initialise(); finance_init(); - reset_park_entry(); banner_init(); ride_init_all(); reset_sprite_list(); @@ -58,7 +60,6 @@ void GameState::InitAll(int32_t mapSize) date_reset(); climate_reset(CLIMATE_COOL_AND_WET); news_item_init_queue(); - user_string_clear_all(); gInMapInitCode = false; @@ -105,25 +106,35 @@ void GameState::Update() // We use this variable to always advance ticks in normal speed. gCurrentRealTimeTicks += realtimeTicksElapsed; - // Determine how many times we need to update the game - if (gGameSpeed > 1) - { - // Update more often if game speed is above normal. - numUpdates = 1 << (gGameSpeed - 1); - } + network_update(); if (network_get_mode() == NETWORK_MODE_CLIENT && network_get_status() == NETWORK_STATUS_CONNECTED && network_get_authstatus() == NETWORK_AUTH_OK) { - if (network_get_server_tick() - gCurrentTicks >= 10) + numUpdates = std::clamp(network_get_server_tick() - gCurrentTicks, 0, 10); + } + else + { + // Determine how many times we need to update the game + if (gGameSpeed > 1) { - // Make sure client doesn't fall behind the server too much - numUpdates += 10; + // Update more often if game speed is above normal. + numUpdates = 1 << (gGameSpeed - 1); + } + } + + bool isPaused = game_is_paused(); + if (network_get_mode() == NETWORK_MODE_SERVER && gConfigNetwork.pause_server_if_no_clients) + { + // If we are headless we always have 1 player (host), pause if no one else is around. + if (gOpenRCT2Headless && network_get_num_players() == 1) + { + isPaused |= true; } } bool didRunSingleFrame = false; - if (game_is_paused()) + if (isPaused) { if (gDoSingleUpdate && network_get_mode() == NETWORK_MODE_NONE) { @@ -139,9 +150,9 @@ void GameState::Update() map_animation_invalidate_all(); // Special case because we set numUpdates to 0, otherwise in game_logic_update. - network_update(); - network_process_pending(); + + GameActions::ProcessQueue(); } } @@ -190,9 +201,6 @@ void GameState::Update() context_update_map_tooltip(); - // Input - gUnk141F568 = gUnk13CA740; - context_handle_input(); } @@ -221,30 +229,36 @@ void GameState::UpdateLogic() if (gScreenAge == 0) gScreenAge--; - network_update(); - GetContext()->GetReplayManager()->Update(); - if (network_get_mode() == NETWORK_MODE_CLIENT && network_get_status() == NETWORK_STATUS_CONNECTED - && network_get_authstatus() == NETWORK_AUTH_OK) - { - // Can't be in sync with server, round trips won't work if we are at same level. - if (gCurrentTicks >= network_get_server_tick()) - { - // Don't run past the server - return; - } - } + network_update(); if (network_get_mode() == NETWORK_MODE_SERVER) { + if (network_gamestate_snapshots_enabled()) + { + CreateStateSnapshot(); + } + // Send current tick out. network_send_tick(); } else if (network_get_mode() == NETWORK_MODE_CLIENT) { // Check desync. - network_check_desynchronization(); + bool desynced = network_check_desynchronisation(); + if (desynced) + { + // If desync debugging is enabled and we are still connected request the specific game state from server. + if (network_gamestate_snapshots_enabled() && network_get_status() == NETWORK_STATUS_CONNECTED) + { + // Create snapshot from this tick so we can compare it later + // as we won't pause the game on this event. + CreateStateSnapshot(); + + network_request_gamestate_snapshot(); + } + } } date_update(); @@ -260,9 +274,9 @@ void GameState::UpdateLogic() map_restore_provisional_elements(); vehicle_update_all(); sprite_misc_update_all(); - ride_update_all(); + Ride::UpdateAll(); - if (!(gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER))) + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR)) { _park->Update(_date); } @@ -281,33 +295,27 @@ void GameState::UpdateLogic() // Update windows // window_dispatch_update_all(); - if (gErrorType != ERROR_TYPE_NONE) - { - rct_string_id title_text = STR_UNABLE_TO_LOAD_FILE; - rct_string_id body_text = gErrorStringId; - if (gErrorType == ERROR_TYPE_GENERIC) - { - title_text = gErrorStringId; - body_text = 0xFFFF; - } - gErrorType = ERROR_TYPE_NONE; - - context_show_error(title_text, body_text); - } - // Start autosave timer after update if (gLastAutoSaveUpdate == AUTOSAVE_PAUSE) { gLastAutoSaveUpdate = Platform::GetTicks(); } - // Separated out processing commands in network_update which could call scenario_rand where gInUpdateCode is false. - // All commands that are received are first queued and then executed where gInUpdateCode is set to true. - network_process_pending(); + GameActions::ProcessQueue(); + network_process_pending(); network_flush(); gCurrentTicks++; gScenarioTicks++; gSavedAge++; } + +void GameState::CreateStateSnapshot() +{ + IGameStateSnapshots* snapshots = GetContext()->GetGameStateSnapshots(); + + auto& snapshot = snapshots->CreateSnapshot(); + snapshots->Capture(snapshot); + snapshots->LinkSnapshot(snapshot, gCurrentTicks, scenario_rand_state().s0); +} diff --git a/src/openrct2/GameState.h b/src/openrct2/GameState.h index 479ef0d6b5..83bad8a89e 100644 --- a/src/openrct2/GameState.h +++ b/src/openrct2/GameState.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -42,5 +42,8 @@ namespace OpenRCT2 void InitAll(int32_t mapSize); void Update(); void UpdateLogic(); + + private: + void CreateStateSnapshot(); }; } // namespace OpenRCT2 diff --git a/src/openrct2/GameStateSnapshots.cpp b/src/openrct2/GameStateSnapshots.cpp new file mode 100644 index 0000000000..cb66ebc6cf --- /dev/null +++ b/src/openrct2/GameStateSnapshots.cpp @@ -0,0 +1,577 @@ +#include "GameStateSnapshots.h" + +#include "core/CircularBuffer.h" +#include "peep/Peep.h" +#include "world/Sprite.h" + +static constexpr size_t MaximumGameStateSnapshots = 32; +static constexpr uint32_t InvalidTick = 0xFFFFFFFF; + +struct GameStateSnapshot_t +{ + GameStateSnapshot_t& operator=(GameStateSnapshot_t&& mv) + { + tick = mv.tick; + storedSprites = std::move(mv.storedSprites); + return *this; + } + + uint32_t tick = InvalidTick; + uint32_t srand0 = 0; + + MemoryStream storedSprites; + MemoryStream parkParameters; + + void SerialiseSprites(rct_sprite* sprites, const size_t numSprites, bool saving) + { + const bool loading = !saving; + + storedSprites.SetPosition(0); + DataSerialiser ds(saving, storedSprites); + + std::vector indexTable; + indexTable.reserve(numSprites); + + uint32_t numSavedSprites = 0; + + if (saving) + { + for (size_t i = 0; i < numSprites; i++) + { + if (sprites[i].generic.sprite_identifier == SPRITE_IDENTIFIER_NULL) + continue; + indexTable.push_back((uint32_t)i); + } + numSavedSprites = (uint32_t)indexTable.size(); + } + + ds << numSavedSprites; + + if (loading) + { + indexTable.resize(numSavedSprites); + } + + for (uint32_t i = 0; i < numSavedSprites; i++) + { + ds << indexTable[i]; + + const uint32_t spriteIdx = indexTable[i]; + rct_sprite& sprite = sprites[spriteIdx]; + + ds << sprite.generic.sprite_identifier; + + switch (sprite.generic.sprite_identifier) + { + case SPRITE_IDENTIFIER_VEHICLE: + ds << reinterpret_cast(sprite.vehicle); + break; + case SPRITE_IDENTIFIER_PEEP: + ds << reinterpret_cast(sprite.peep); + break; + case SPRITE_IDENTIFIER_LITTER: + ds << reinterpret_cast(sprite.litter); + break; + case SPRITE_IDENTIFIER_MISC: + { + ds << sprite.generic.type; + switch (sprite.generic.type) + { + case SPRITE_MISC_MONEY_EFFECT: + ds << reinterpret_cast(sprite.money_effect); + break; + case SPRITE_MISC_BALLOON: + ds << reinterpret_cast(sprite.balloon); + break; + case SPRITE_MISC_DUCK: + ds << reinterpret_cast(sprite.duck); + break; + case SPRITE_MISC_JUMPING_FOUNTAIN_WATER: + ds << reinterpret_cast(sprite.jumping_fountain); + break; + case SPRITE_MISC_STEAM_PARTICLE: + ds << reinterpret_cast(sprite.steam_particle); + break; + } + } + break; + } + } + } +}; + +struct GameStateSnapshots : public IGameStateSnapshots +{ + virtual void Reset() override final + { + _snapshots.clear(); + } + + virtual GameStateSnapshot_t& CreateSnapshot() override final + { + auto snapshot = std::make_unique(); + _snapshots.push_back(std::move(snapshot)); + + return *_snapshots.back(); + } + + virtual void LinkSnapshot(GameStateSnapshot_t& snapshot, uint32_t tick, uint32_t srand0) override final + { + snapshot.tick = tick; + snapshot.srand0 = srand0; + } + + virtual void Capture(GameStateSnapshot_t& snapshot) override final + { + snapshot.SerialiseSprites(get_sprite(0), MAX_SPRITES, true); + + // log_info("Snapshot size: %u bytes", (uint32_t)snapshot.storedSprites.GetLength()); + } + + virtual const GameStateSnapshot_t* GetLinkedSnapshot(uint32_t tick) const override final + { + for (size_t i = 0; i < _snapshots.size(); i++) + { + if (_snapshots[i]->tick == tick) + return _snapshots[i].get(); + } + return nullptr; + } + + virtual void SerialiseSnapshot(GameStateSnapshot_t& snapshot, DataSerialiser& ds) const override final + { + ds << snapshot.tick; + ds << snapshot.srand0; + ds << snapshot.storedSprites; + ds << snapshot.parkParameters; + } + + std::vector BuildSpriteList(GameStateSnapshot_t& snapshot) const + { + std::vector spriteList; + spriteList.resize(MAX_SPRITES); + + for (auto& sprite : spriteList) + { + // By default they don't exist. + sprite.generic.sprite_identifier = SPRITE_IDENTIFIER_NULL; + } + + snapshot.SerialiseSprites(spriteList.data(), MAX_SPRITES, false); + + return spriteList; + } + +#define COMPARE_FIELD(struc, field) \ + if (std::memcmp(&spriteBase.field, &spriteCmp.field, sizeof(struc::field)) != 0) \ + { \ + uint64_t valA = 0; \ + uint64_t valB = 0; \ + std::memcpy(&valA, &spriteBase.field, sizeof(struc::field)); \ + std::memcpy(&valB, &spriteCmp.field, sizeof(struc::field)); \ + uintptr_t offset = reinterpret_cast(&spriteBase.field) - reinterpret_cast(&spriteBase); \ + changeData.diffs.push_back( \ + GameStateSpriteChange_t::Diff_t{ (size_t)offset, sizeof(struc::field), #struc, #field, valA, valB }); \ + } + + void CompareSpriteDataCommon( + const rct_sprite_common& spriteBase, const rct_sprite_common& spriteCmp, GameStateSpriteChange_t& changeData) const + { + COMPARE_FIELD(rct_sprite_common, sprite_identifier); + COMPARE_FIELD(rct_sprite_common, type); + COMPARE_FIELD(rct_sprite_common, next_in_quadrant); + COMPARE_FIELD(rct_sprite_common, next); + COMPARE_FIELD(rct_sprite_common, previous); + COMPARE_FIELD(rct_sprite_common, linked_list_index); + COMPARE_FIELD(rct_sprite_common, sprite_index); + COMPARE_FIELD(rct_sprite_common, flags); + COMPARE_FIELD(rct_sprite_common, x); + COMPARE_FIELD(rct_sprite_common, y); + COMPARE_FIELD(rct_sprite_common, z); + /* Only relevant for rendering, does not affect game state. + COMPARE_FIELD(rct_sprite_common, sprite_width); + COMPARE_FIELD(rct_sprite_common, sprite_height_negative); + COMPARE_FIELD(rct_sprite_common, sprite_height_positive); + COMPARE_FIELD(rct_sprite_common, sprite_left); + COMPARE_FIELD(rct_sprite_common, sprite_top); + COMPARE_FIELD(rct_sprite_common, sprite_right); + COMPARE_FIELD(rct_sprite_common, sprite_bottom); + */ + COMPARE_FIELD(rct_sprite_common, sprite_direction); + } + + void CompareSpriteDataPeep(const Peep& spriteBase, const Peep& spriteCmp, GameStateSpriteChange_t& changeData) const + { + COMPARE_FIELD(Peep, next_x); + COMPARE_FIELD(Peep, next_y); + COMPARE_FIELD(Peep, next_z); + COMPARE_FIELD(Peep, next_flags); + COMPARE_FIELD(Peep, outside_of_park); + COMPARE_FIELD(Peep, state); + COMPARE_FIELD(Peep, sub_state); + COMPARE_FIELD(Peep, sprite_type); + COMPARE_FIELD(Peep, type); + COMPARE_FIELD(Peep, no_of_rides); + COMPARE_FIELD(Peep, tshirt_colour); + COMPARE_FIELD(Peep, trousers_colour); + COMPARE_FIELD(Peep, destination_x); + COMPARE_FIELD(Peep, destination_y); + COMPARE_FIELD(Peep, destination_tolerance); + COMPARE_FIELD(Peep, var_37); + COMPARE_FIELD(Peep, energy); + COMPARE_FIELD(Peep, energy_target); + COMPARE_FIELD(Peep, happiness); + COMPARE_FIELD(Peep, happiness_target); + COMPARE_FIELD(Peep, nausea); + COMPARE_FIELD(Peep, nausea_target); + COMPARE_FIELD(Peep, hunger); + COMPARE_FIELD(Peep, thirst); + COMPARE_FIELD(Peep, toilet); + COMPARE_FIELD(Peep, mass); + COMPARE_FIELD(Peep, time_to_consume); + COMPARE_FIELD(Peep, intensity); + COMPARE_FIELD(Peep, nausea_tolerance); + COMPARE_FIELD(Peep, window_invalidate_flags); + COMPARE_FIELD(Peep, paid_on_drink); + for (int i = 0; i < PEEP_MAX_THOUGHTS; i++) + { + COMPARE_FIELD(Peep, ride_types_been_on[i]); + } + COMPARE_FIELD(Peep, item_extra_flags); + COMPARE_FIELD(Peep, photo2_ride_ref); + COMPARE_FIELD(Peep, photo3_ride_ref); + COMPARE_FIELD(Peep, photo4_ride_ref); + COMPARE_FIELD(Peep, current_ride); + COMPARE_FIELD(Peep, current_ride_station); + COMPARE_FIELD(Peep, current_train); + COMPARE_FIELD(Peep, time_to_sitdown); + COMPARE_FIELD(Peep, special_sprite); + COMPARE_FIELD(Peep, action_sprite_type); + COMPARE_FIELD(Peep, next_action_sprite_type); + COMPARE_FIELD(Peep, action_sprite_image_offset); + COMPARE_FIELD(Peep, action); + COMPARE_FIELD(Peep, action_frame); + COMPARE_FIELD(Peep, step_progress); + COMPARE_FIELD(Peep, next_in_queue); + COMPARE_FIELD(Peep, maze_last_edge); + COMPARE_FIELD(Peep, interaction_ride_index); + COMPARE_FIELD(Peep, time_in_queue); + for (int i = 0; i < 32; i++) + { + COMPARE_FIELD(Peep, rides_been_on[i]); + } + COMPARE_FIELD(Peep, id); + COMPARE_FIELD(Peep, cash_in_pocket); + COMPARE_FIELD(Peep, cash_spent); + COMPARE_FIELD(Peep, time_in_park); + COMPARE_FIELD(Peep, rejoin_queue_timeout); + COMPARE_FIELD(Peep, previous_ride); + COMPARE_FIELD(Peep, previous_ride_time_out); + for (int i = 0; i < PEEP_MAX_THOUGHTS; i++) + { + COMPARE_FIELD(Peep, thoughts[i]); + } + COMPARE_FIELD(Peep, path_check_optimisation); + COMPARE_FIELD(Peep, guest_heading_to_ride_id); + COMPARE_FIELD(Peep, staff_orders); + COMPARE_FIELD(Peep, photo1_ride_ref); + COMPARE_FIELD(Peep, peep_flags); + COMPARE_FIELD(Peep, pathfind_goal); + for (int i = 0; i < 4; i++) + { + COMPARE_FIELD(Peep, pathfind_history[i]); + } + COMPARE_FIELD(Peep, no_action_frame_num); + COMPARE_FIELD(Peep, litter_count); + COMPARE_FIELD(Peep, time_on_ride); + COMPARE_FIELD(Peep, disgusting_count); + COMPARE_FIELD(Peep, paid_to_enter); + COMPARE_FIELD(Peep, paid_on_rides); + COMPARE_FIELD(Peep, paid_on_food); + COMPARE_FIELD(Peep, paid_on_souvenirs); + COMPARE_FIELD(Peep, no_of_food); + COMPARE_FIELD(Peep, no_of_drinks); + COMPARE_FIELD(Peep, no_of_souvenirs); + COMPARE_FIELD(Peep, vandalism_seen); + COMPARE_FIELD(Peep, voucher_type); + COMPARE_FIELD(Peep, voucher_arguments); + COMPARE_FIELD(Peep, surroundings_thought_timeout); + COMPARE_FIELD(Peep, angriness); + COMPARE_FIELD(Peep, time_lost); + COMPARE_FIELD(Peep, days_in_queue); + COMPARE_FIELD(Peep, balloon_colour); + COMPARE_FIELD(Peep, umbrella_colour); + COMPARE_FIELD(Peep, hat_colour); + COMPARE_FIELD(Peep, favourite_ride); + COMPARE_FIELD(Peep, favourite_ride_rating); + COMPARE_FIELD(Peep, item_standard_flags); + } + + void CompareSpriteDataVehicle( + const rct_vehicle& spriteBase, const rct_vehicle& spriteCmp, GameStateSpriteChange_t& changeData) const + { + COMPARE_FIELD(rct_vehicle, vehicle_sprite_type); + COMPARE_FIELD(rct_vehicle, bank_rotation); + COMPARE_FIELD(rct_vehicle, remaining_distance); + COMPARE_FIELD(rct_vehicle, velocity); + COMPARE_FIELD(rct_vehicle, acceleration); + COMPARE_FIELD(rct_vehicle, ride); + COMPARE_FIELD(rct_vehicle, vehicle_type); + COMPARE_FIELD(rct_vehicle, colours); + COMPARE_FIELD(rct_vehicle, track_progress); + COMPARE_FIELD(rct_vehicle, track_direction); + COMPARE_FIELD(rct_vehicle, track_x); + COMPARE_FIELD(rct_vehicle, track_y); + COMPARE_FIELD(rct_vehicle, track_z); + COMPARE_FIELD(rct_vehicle, next_vehicle_on_train); + COMPARE_FIELD(rct_vehicle, prev_vehicle_on_ride); + COMPARE_FIELD(rct_vehicle, next_vehicle_on_ride); + COMPARE_FIELD(rct_vehicle, var_44); + COMPARE_FIELD(rct_vehicle, mass); + COMPARE_FIELD(rct_vehicle, update_flags); + COMPARE_FIELD(rct_vehicle, swing_sprite); + COMPARE_FIELD(rct_vehicle, current_station); + COMPARE_FIELD(rct_vehicle, swinging_car_var_0); + COMPARE_FIELD(rct_vehicle, var_4E); + COMPARE_FIELD(rct_vehicle, status); + COMPARE_FIELD(rct_vehicle, sub_state); + for (int i = 0; i < 32; i++) + { + COMPARE_FIELD(rct_vehicle, peep[i]); + } + for (int i = 0; i < 32; i++) + { + COMPARE_FIELD(rct_vehicle, peep_tshirt_colours[i]); + } + COMPARE_FIELD(rct_vehicle, num_seats); + COMPARE_FIELD(rct_vehicle, num_peeps); + COMPARE_FIELD(rct_vehicle, next_free_seat); + COMPARE_FIELD(rct_vehicle, restraints_position); + COMPARE_FIELD(rct_vehicle, spin_speed); + COMPARE_FIELD(rct_vehicle, sound2_flags); + COMPARE_FIELD(rct_vehicle, spin_sprite); + COMPARE_FIELD(rct_vehicle, sound1_id); + COMPARE_FIELD(rct_vehicle, sound1_volume); + COMPARE_FIELD(rct_vehicle, sound2_id); + COMPARE_FIELD(rct_vehicle, sound2_volume); + COMPARE_FIELD(rct_vehicle, sound_vector_factor); + COMPARE_FIELD(rct_vehicle, cable_lift_target); + COMPARE_FIELD(rct_vehicle, speed); + COMPARE_FIELD(rct_vehicle, powered_acceleration); + COMPARE_FIELD(rct_vehicle, var_C4); + COMPARE_FIELD(rct_vehicle, animation_frame); + for (int i = 0; i < 2; i++) + { + COMPARE_FIELD(rct_vehicle, pad_C6[i]); + } + COMPARE_FIELD(rct_vehicle, var_C8); + COMPARE_FIELD(rct_vehicle, var_CA); + COMPARE_FIELD(rct_vehicle, scream_sound_id); + COMPARE_FIELD(rct_vehicle, var_CD); + COMPARE_FIELD(rct_vehicle, num_laps); + COMPARE_FIELD(rct_vehicle, brake_speed); + COMPARE_FIELD(rct_vehicle, lost_time_out); + COMPARE_FIELD(rct_vehicle, vertical_drop_countdown); + COMPARE_FIELD(rct_vehicle, var_D3); + COMPARE_FIELD(rct_vehicle, mini_golf_current_animation); + COMPARE_FIELD(rct_vehicle, mini_golf_flags); + COMPARE_FIELD(rct_vehicle, ride_subtype); + COMPARE_FIELD(rct_vehicle, colours_extended); + COMPARE_FIELD(rct_vehicle, seat_rotation); + COMPARE_FIELD(rct_vehicle, target_seat_rotation); + } + + void CompareSpriteDataLitter( + const rct_litter& spriteBase, const rct_litter& spriteCmp, GameStateSpriteChange_t& changeData) const + { + COMPARE_FIELD(rct_litter, creationTick); + } + + void CompareSpriteData(const rct_sprite& spriteBase, const rct_sprite& spriteCmp, GameStateSpriteChange_t& changeData) const + { + CompareSpriteDataCommon(spriteBase.generic, spriteCmp.generic, changeData); + if (spriteBase.generic.sprite_identifier == spriteCmp.generic.sprite_identifier) + { + switch (spriteBase.generic.sprite_identifier) + { + case SPRITE_IDENTIFIER_PEEP: + CompareSpriteDataPeep(spriteBase.peep, spriteCmp.peep, changeData); + break; + case SPRITE_IDENTIFIER_VEHICLE: + CompareSpriteDataVehicle(spriteBase.vehicle, spriteCmp.vehicle, changeData); + break; + case SPRITE_IDENTIFIER_LITTER: + CompareSpriteDataLitter(spriteBase.litter, spriteCmp.litter, changeData); + break; + } + } + } + + virtual GameStateCompareData_t Compare(const GameStateSnapshot_t& base, const GameStateSnapshot_t& cmp) const override final + { + GameStateCompareData_t res; + res.tick = base.tick; + res.srand0Left = base.srand0; + res.srand0Right = cmp.srand0; + + std::vector spritesBase = BuildSpriteList(const_cast(base)); + std::vector spritesCmp = BuildSpriteList(const_cast(cmp)); + + for (uint32_t i = 0; i < (uint32_t)spritesBase.size(); i++) + { + GameStateSpriteChange_t changeData; + changeData.spriteIndex = i; + + const rct_sprite& spriteBase = spritesBase[i]; + const rct_sprite& spriteCmp = spritesCmp[i]; + + changeData.spriteIdentifier = spriteBase.generic.sprite_identifier; + changeData.miscIdentifier = spriteBase.generic.type; + + if (spriteBase.generic.sprite_identifier == SPRITE_IDENTIFIER_NULL + && spriteCmp.generic.sprite_identifier != SPRITE_IDENTIFIER_NULL) + { + // Sprite was added. + changeData.changeType = GameStateSpriteChange_t::ADDED; + changeData.spriteIdentifier = spriteCmp.generic.sprite_identifier; + } + else if ( + spriteBase.generic.sprite_identifier != SPRITE_IDENTIFIER_NULL + && spriteCmp.generic.sprite_identifier == SPRITE_IDENTIFIER_NULL) + { + // Sprite was removed. + changeData.changeType = GameStateSpriteChange_t::REMOVED; + changeData.spriteIdentifier = spriteBase.generic.sprite_identifier; + } + else if ( + spriteBase.generic.sprite_identifier == SPRITE_IDENTIFIER_NULL + && spriteCmp.generic.sprite_identifier == SPRITE_IDENTIFIER_NULL) + { + // Do nothing. + changeData.changeType = GameStateSpriteChange_t::EQUAL; + } + else + { + CompareSpriteData(spriteBase, spriteCmp, changeData); + if (changeData.diffs.size() == 0) + { + changeData.changeType = GameStateSpriteChange_t::EQUAL; + } + else + { + changeData.changeType = GameStateSpriteChange_t::MODIFIED; + } + } + + res.spriteChanges.push_back(changeData); + } + + return res; + } + + static const char* GetSpriteIdentifierName(uint32_t spriteIdentifier, uint8_t miscIdentifier) + { + switch (spriteIdentifier) + { + case SPRITE_IDENTIFIER_NULL: + return "Null"; + case SPRITE_IDENTIFIER_PEEP: + return "Peep"; + case SPRITE_IDENTIFIER_VEHICLE: + return "Vehicle"; + case SPRITE_IDENTIFIER_LITTER: + return "Litter"; + case SPRITE_IDENTIFIER_MISC: + switch (miscIdentifier) + { + case SPRITE_MISC_STEAM_PARTICLE: + return "Misc: Steam Particle"; + case SPRITE_MISC_MONEY_EFFECT: + return "Misc: Money effect"; + case SPRITE_MISC_CRASHED_VEHICLE_PARTICLE: + return "Misc: Crash Vehicle Particle"; + case SPRITE_MISC_EXPLOSION_CLOUD: + return "Misc: Explosion Cloud"; + case SPRITE_MISC_CRASH_SPLASH: + return "Misc: Crash Splash"; + case SPRITE_MISC_EXPLOSION_FLARE: + return "Misc: Explosion Flare"; + case SPRITE_MISC_JUMPING_FOUNTAIN_WATER: + return "Misc: Jumping fountain water"; + case SPRITE_MISC_BALLOON: + return "Misc: Balloon"; + case SPRITE_MISC_DUCK: + return "Misc: Duck"; + case SPRITE_MISC_JUMPING_FOUNTAIN_SNOW: + return "Misc: Jumping fountain snow"; + } + return "Misc"; + } + return "Unknown"; + } + + virtual bool LogCompareDataToFile(const std::string& fileName, const GameStateCompareData_t& cmpData) const override + { + std::string outputBuffer; + char tempBuffer[1024] = {}; + + snprintf(tempBuffer, sizeof(tempBuffer), "tick: %08X\n", cmpData.tick); + outputBuffer += tempBuffer; + + snprintf( + tempBuffer, sizeof(tempBuffer), "srand0 left = %08X, srand0 right = %08X\n", cmpData.srand0Left, + cmpData.srand0Right); + outputBuffer += tempBuffer; + + for (auto& change : cmpData.spriteChanges) + { + if (change.changeType == GameStateSpriteChange_t::EQUAL) + continue; + + const char* typeName = GetSpriteIdentifierName(change.spriteIdentifier, change.miscIdentifier); + + if (change.changeType == GameStateSpriteChange_t::ADDED) + { + snprintf(tempBuffer, sizeof(tempBuffer), "Sprite added (%s), index: %u\n", typeName, change.spriteIndex); + outputBuffer += tempBuffer; + } + else if (change.changeType == GameStateSpriteChange_t::REMOVED) + { + snprintf(tempBuffer, sizeof(tempBuffer), "Sprite removed (%s), index: %u\n", typeName, change.spriteIndex); + outputBuffer += tempBuffer; + } + else if (change.changeType == GameStateSpriteChange_t::MODIFIED) + { + snprintf( + tempBuffer, sizeof(tempBuffer), "Sprite modifications (%s), index: %u\n", typeName, change.spriteIndex); + outputBuffer += tempBuffer; + for (auto& diff : change.diffs) + { + snprintf( + tempBuffer, sizeof(tempBuffer), + " %s::%s, len = %u, offset = %u, left = 0x%.16llX, right = 0x%.16llX\n", diff.structname, + diff.fieldname, (uint32_t)diff.length, (uint32_t)diff.offset, (unsigned long long)diff.valueA, + (unsigned long long)diff.valueB); + outputBuffer += tempBuffer; + } + } + } + + FILE* fp = fopen(fileName.c_str(), "wt"); + if (!fp) + return false; + + fputs(outputBuffer.c_str(), fp); + fclose(fp); + + return true; + } + +private: + CircularBuffer, MaximumGameStateSnapshots> _snapshots; +}; + +std::unique_ptr CreateGameStateSnapshots() +{ + return std::make_unique(); +} diff --git a/src/openrct2/GameStateSnapshots.h b/src/openrct2/GameStateSnapshots.h new file mode 100644 index 0000000000..235a5e697d --- /dev/null +++ b/src/openrct2/GameStateSnapshots.h @@ -0,0 +1,108 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "common.h" +#include "core/DataSerialiser.h" + +#include +#include +#include + +struct GameStateSnapshot_t; + +struct GameStateSpriteChange_t +{ + enum + { + REMOVED, + ADDED, + MODIFIED, + EQUAL + }; + + struct Diff_t + { + size_t offset; + size_t length; + const char* structname; + const char* fieldname; + uint64_t valueA; + uint64_t valueB; + }; + + uint8_t changeType; + uint8_t spriteIdentifier; + uint8_t miscIdentifier; + uint32_t spriteIndex; + + std::vector diffs; +}; + +struct GameStateCompareData_t +{ + uint32_t tick; + uint32_t srand0Left; + uint32_t srand0Right; + std::vector spriteChanges; +}; + +/* + * Interface to create and capture game states. It only allows to have 32 active snapshots + * the oldest snapshot will be removed from the buffer. Never store the snapshot pointer + * as it may become invalid at any time when a snapshot is created, rather Link the snapshot + * to a specific tick which can be obtained by that later again assuming its still valid. + */ +interface IGameStateSnapshots +{ + virtual ~IGameStateSnapshots() = default; + + /* + * Removes all existing entries and starts over. + */ + virtual void Reset() = 0; + + /* + * Creates a new empty snapshot, oldest snapshot will be removed. + */ + virtual GameStateSnapshot_t& CreateSnapshot() = 0; + + /* + * Links the snapshot to a specific game tick. + */ + virtual void LinkSnapshot(GameStateSnapshot_t & snapshot, uint32_t tick, uint32_t srand0) = 0; + + /* + * This will fill the snapshot with the current game state in a compact form. + */ + virtual void Capture(GameStateSnapshot_t & snapshot) = 0; + + /* + * Returns the snapshot for a given tick in the history, nullptr if not found. + */ + virtual const GameStateSnapshot_t* GetLinkedSnapshot(uint32_t tick) const = 0; + + /* + * Serialisation of GameStateSnapshot_t + */ + virtual void SerialiseSnapshot(GameStateSnapshot_t & snapshot, DataSerialiser & serialiser) const = 0; + + /* + * Compares two states resulting GameStateCompareData_t with all mismatches stored. + */ + virtual GameStateCompareData_t Compare(const GameStateSnapshot_t& base, const GameStateSnapshot_t& cmp) const = 0; + + /* + * Writes the GameStateCompareData_t into the specified file as readable text. + */ + virtual bool LogCompareDataToFile(const std::string& fileName, const GameStateCompareData_t& cmpData) const = 0; +}; + +std::unique_ptr CreateGameStateSnapshots(); diff --git a/src/openrct2/Input.cpp b/src/openrct2/Input.cpp index ebcdd6216b..16102939c4 100644 --- a/src/openrct2/Input.cpp +++ b/src/openrct2/Input.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/Input.h b/src/openrct2/Input.h index 15d49d3708..c20e86b753 100644 --- a/src/openrct2/Input.h +++ b/src/openrct2/Input.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -95,7 +95,7 @@ extern INPUT_STATE _inputState; extern uint8_t _inputFlags; extern uint16_t _tooltipNotShownTicks; -void input_window_position_begin(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y); +void input_window_position_begin(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); void title_handle_keyboard_input(); void game_handle_input(); @@ -103,7 +103,7 @@ void game_handle_keyboard_input(); void game_handle_edge_scroll(); int32_t get_next_key(); -void store_mouse_input(int32_t state, int32_t x, int32_t y); +void store_mouse_input(int32_t state, ScreenCoordsXY screenCoords); void input_set_flag(INPUT_FLAGS flag, bool on); bool input_test_flag(INPUT_FLAGS flag); @@ -118,6 +118,6 @@ void reset_tooltip_not_shown(); void input_reset_place_obj_modifier(); -void input_scroll_viewport(int32_t scrollX, int32_t scrollY); +void input_scroll_viewport(ScreenCoordsXY screenCoords); #endif diff --git a/src/openrct2/Intro.cpp b/src/openrct2/Intro.cpp index 3feaf93ccb..24a0a3be71 100644 --- a/src/openrct2/Intro.cpp +++ b/src/openrct2/Intro.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -55,7 +55,7 @@ void intro_update() _introStateCounter = -580; // Play the chain lift sound - _soundChannel = Mixer_Play_Effect(SOUND_LIFT_7, MIXER_LOOP_INFINITE, MIXER_VOLUME_MAX, 0.5f, 1, true); + _soundChannel = Mixer_Play_Effect(SoundId::LiftBM, MIXER_LOOP_INFINITE, MIXER_VOLUME_MAX, 0.5f, 1, true); _chainLiftFinished = false; gIntroState++; break; @@ -94,7 +94,7 @@ void intro_update() // Play the track friction sound _soundChannel = Mixer_Play_Effect( - SOUND_TRACK_FRICTION_3, MIXER_LOOP_INFINITE, MIXER_VOLUME_MAX, 0.25f, 0.75, true); + SoundId::TrackFrictionBM, MIXER_LOOP_INFINITE, MIXER_VOLUME_MAX, 0.25f, 0.75, true); } // Check if logo is off the screen...ish @@ -108,7 +108,7 @@ void intro_update() } // Play long peep scream sound - _soundChannel = Mixer_Play_Effect(SOUND_SCREAM_1, MIXER_LOOP_NONE, MIXER_VOLUME_MAX, 0.5f, 1, false); + _soundChannel = Mixer_Play_Effect(SoundId::Scream1, MIXER_LOOP_NONE, MIXER_VOLUME_MAX, 0.5f, 1, false); gIntroState++; _introStateCounter = 0; diff --git a/src/openrct2/Intro.h b/src/openrct2/Intro.h index dba1ba9bde..95f00d662a 100644 --- a/src/openrct2/Intro.h +++ b/src/openrct2/Intro.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/OpenRCT2.cpp b/src/openrct2/OpenRCT2.cpp index 175cb0e4d2..30dd4abcae 100644 --- a/src/openrct2/OpenRCT2.cpp +++ b/src/openrct2/OpenRCT2.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/OpenRCT2.h b/src/openrct2/OpenRCT2.h index 1185307bf6..ac57ac4e17 100644 --- a/src/openrct2/OpenRCT2.h +++ b/src/openrct2/OpenRCT2.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ParkImporter.cpp b/src/openrct2/ParkImporter.cpp index fdc2d14e8a..f891d3d80f 100644 --- a/src/openrct2/ParkImporter.cpp +++ b/src/openrct2/ParkImporter.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ParkImporter.h b/src/openrct2/ParkImporter.h index e2ae34fdee..c65c977e69 100644 --- a/src/openrct2/ParkImporter.h +++ b/src/openrct2/ParkImporter.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/PlatformEnvironment.cpp b/src/openrct2/PlatformEnvironment.cpp index f595fd4a18..9e1fd90522 100644 --- a/src/openrct2/PlatformEnvironment.cpp +++ b/src/openrct2/PlatformEnvironment.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -221,6 +221,7 @@ const char * PlatformEnvironment::DirectoryNamesOpenRCT2[] = "track", // TRACK "heightmap", // HEIGHTMAP "replay", // REPLAY + "desyncs", // DESYNCS }; const char * PlatformEnvironment::FileNames[] = diff --git a/src/openrct2/PlatformEnvironment.h b/src/openrct2/PlatformEnvironment.h index 30747c9e32..2df41e60ee 100644 --- a/src/openrct2/PlatformEnvironment.h +++ b/src/openrct2/PlatformEnvironment.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -47,6 +47,7 @@ namespace OpenRCT2 TRACK, // Contains track designs. HEIGHTMAP, // Contains heightmap data. REPLAY, // Contains recorded replays. + LOG_DESYNCS, // Contains desync reports. }; enum class PATHID diff --git a/src/openrct2/ReplayManager.cpp b/src/openrct2/ReplayManager.cpp index 0b9c2312ed..3360b748ac 100644 --- a/src/openrct2/ReplayManager.cpp +++ b/src/openrct2/ReplayManager.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,6 +18,8 @@ #include "actions/GameAction.h" #include "actions/RideEntranceExitPlaceAction.hpp" #include "actions/RideSetSetting.hpp" +#include "actions/SetCheatAction.hpp" +#include "actions/TileModifyAction.hpp" #include "actions/TrackPlaceAction.hpp" #include "config/Config.h" #include "core/DataSerialiser.h" @@ -35,40 +37,21 @@ namespace OpenRCT2 { - // NOTE: This is currently very close to what the network version uses. - // Should be refactored once the old game commands are gone. struct ReplayCommand { + uint32_t tick = 0; + GameAction::Ptr action; + uint32_t commandIndex = 0; + ReplayCommand() = default; - ReplayCommand(uint32_t t, uint32_t* args, uint8_t cb, uint32_t id) - { - tick = t; - eax = args[0]; - ebx = args[1]; - ecx = args[2]; - edx = args[3]; - esi = args[4]; - edi = args[5]; - ebp = args[6]; - callback = cb; - action = nullptr; - commandIndex = id; - } - ReplayCommand(uint32_t t, std::unique_ptr&& ga, uint32_t id) - { - tick = t; - action = std::move(ga); - commandIndex = id; - } + : tick(t) + , action(std::move(ga)) + , commandIndex(id) - uint32_t tick = 0; - uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0, esi = 0, edi = 0, ebp = 0; - GameAction::Ptr action; - uint8_t playerid = 0; - uint8_t callback = 0; - uint32_t commandIndex = 0; + { + } bool operator<(const ReplayCommand& comp) const { @@ -99,6 +82,7 @@ namespace OpenRCT2 MemoryStream parkData; MemoryStream spriteSpatialData; MemoryStream parkParams; + MemoryStream cheatData; std::string name; // Name of play std::string filePath; // File path of replay. uint64_t timeRecorded; // Posix Time. @@ -111,7 +95,7 @@ namespace OpenRCT2 class ReplayManager final : public IReplayManager { - static constexpr uint16_t ReplayVersion = 2; + static constexpr uint16_t ReplayVersion = 3; static constexpr uint32_t ReplayMagic = 0x5243524F; // ORCR. static constexpr int ReplayCompressionLevel = 9; @@ -143,25 +127,6 @@ namespace OpenRCT2 return _mode == ReplayMode::NORMALISATION; } - virtual void AddGameCommand( - uint32_t tick, uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi, uint32_t ebp, - uint8_t callback) override - { - if (_currentRecording == nullptr) - return; - - uint32_t args[7]; - args[0] = eax; - args[1] = ebx; - args[2] = ecx; - args[3] = edx; - args[4] = esi; - args[5] = edi; - args[6] = ebp; - - _currentRecording->commands.emplace(gCurrentTicks, args, callback, _commandId++); - } - virtual void AddGameAction(uint32_t tick, const GameAction* action) override { if (_currentRecording == nullptr) @@ -264,8 +229,11 @@ namespace OpenRCT2 replayData->spriteSpatialData.Write(gSpriteSpatialIndex, sizeof(gSpriteSpatialIndex)); replayData->timeRecorded = std::chrono::seconds(std::time(nullptr)).count(); - DataSerialiser parkParams(true, replayData->parkParams); - SerialiseParkParameters(parkParams); + DataSerialiser parkParamsDs(true, replayData->parkParams); + SerialiseParkParameters(parkParamsDs); + + DataSerialiser cheatDataDs(true, replayData->cheatData); + SerialiseCheats(cheatDataDs); if (_mode != ReplayMode::NORMALISATION) _mode = ReplayMode::RECORDING; @@ -379,12 +347,6 @@ namespace OpenRCT2 return false; } - if (!TranslateDeprecatedGameCommands(*replayData)) - { - log_error("Unable to translate deprecated game commands."); - return false; - } - if (!LoadReplayDataMap(*replayData)) { log_error("Unable to load map."); @@ -442,12 +404,12 @@ namespace OpenRCT2 { _mode = ReplayMode::NORMALISATION; - if (StartPlayback(file) == false) + if (!StartPlayback(file)) { return false; } - if (StartRecording(outFile, k_MaxReplayTicks) == false) + if (!StartRecording(outFile, k_MaxReplayTicks)) { StopPlayback(); return false; @@ -459,112 +421,6 @@ namespace OpenRCT2 } private: - bool ConvertDeprecatedGameCommand(const ReplayCommand& command, ReplayCommand& result) - { - // NOTE: If game actions are being ported it is required to implement temporarily - // a mapping from game command to game action. This will allow the normalisation - // stage to save a new replay file with the game action being used instead of the - // old game command. Once normalised the code will be no longer required. - - /* Example case - case GAME_COMMAND_RAISE_WATER: - { - uint32_t param1 = command.ebp; - uint32_t param2 = command.edi; - result.action = std::make_unique(param1, param2, ...); - } - */ - - switch (command.esi) - { - case GAME_COMMAND_COUNT: // prevent default without case warning. - break; - case GAME_COMMAND_PLACE_TRACK: - { - ride_id_t rideId = command.edx & 0xFF; - int32_t trackType = (command.edx >> 8) & 0xFF; - CoordsXYZD origin = { (int32_t)(command.eax & 0xFFFF), (int32_t)(command.ecx & 0xFFFF), - (int32_t)(command.edi & 0xFFFF), (uint8_t)((command.ebx >> 8) & 0xFF) }; - int32_t brakeSpeed = (command.edi >> 16) & 0xFF; - int32_t colour = (command.edi >> 24) & 0x0F; - int32_t seatRotation = (command.edi >> 28) & 0x0F; - int32_t liftHillAndAlternativeState = (command.edx >> 16); - - result.action = std::make_unique( - rideId, trackType, origin, brakeSpeed, colour, seatRotation, liftHillAndAlternativeState); - result.action->SetFlags(command.ebx & 0xFF); - break; - } - case GAME_COMMAND_SET_RIDE_SETTING: - { - ride_id_t rideId = command.edx & 0xFF; - RideSetSetting setting = static_cast((command.edx >> 8) & 0xFF); - uint8_t value = (command.ebx >> 8) & 0xFF; - - result.action = std::make_unique(rideId, setting, value); - result.action->SetFlags(command.ebx & 0xFF); - break; - } - case GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT: - { - CoordsXY loc = { (int32_t)(command.eax & 0xFFFF), (int32_t)(command.ecx & 0xFFFF) }; - Direction direction = (command.ebx >> 8) & 0xFF; - ride_id_t rideId = command.edx & 0xFF; - uint8_t stationNum = command.edi & 0xFF; - bool isExit = ((command.edx >> 8) & 0xFF) != 0; - result.action = std::make_unique(loc, direction, rideId, stationNum, isExit); - result.action->SetFlags(command.ebx & 0xFF); - break; - } - case GAME_COMMAND_PLACE_PATH: - { - CoordsXYZ loc = { (int32_t)(command.eax & 0xFFFF), (int32_t)(command.ecx & 0xFFFF), - (int32_t)(command.edx & 0xFF) * 8 }; - uint8_t slope = (command.ebx >> 8) & 0xFF; - uint8_t type = (command.edx >> 8) & 0xFF; - result.action = std::make_unique(loc, slope, type); - result.action->SetFlags(command.ebx & 0xFF); - break; - } - default: - throw std::runtime_error("Deprecated game command requires replay translation."); - } - - return true; - } - - bool TranslateDeprecatedGameCommands(ReplayRecordData& data) - { - for (auto it = data.commands.begin(); it != data.commands.end(); it++) - { - const ReplayCommand& replayCommand = *it; - - if (replayCommand.action == nullptr) - { - // Check if we can create a game action with the command id. - uint32_t commandId = replayCommand.esi; - if (GameActions::IsValidId(commandId)) - { - // Convert - ReplayCommand converted; - converted.commandIndex = replayCommand.commandIndex; - - if (!ConvertDeprecatedGameCommand(replayCommand, converted)) - { - return false; - } - - // Remove deprecated command. - data.commands.erase(it); - - // Insert new game action, iterator points to the replaced element. - it = data.commands.emplace(std::move(converted)); - } - } - } - return true; - } - bool LoadReplayDataMap(ReplayRecordData& data) { try @@ -589,8 +445,14 @@ namespace OpenRCT2 std::memcpy(gSpriteSpatialIndex, data.spriteSpatialData.GetData(), data.spriteSpatialData.GetLength()); // Load all map global variables. - DataSerialiser parkParams(false, data.parkParams); - SerialiseParkParameters(parkParams); + DataSerialiser parkParamsDs(false, data.parkParams); + SerialiseParkParameters(parkParamsDs); + + // New cheats might not be serialised, make sure they are using their defaults. + CheatsReset(); + + DataSerialiser cheatDataDs(false, data.cheatData); + SerialiseCheats(cheatDataDs); game_load_init(); fix_invalid_vehicle_sprite_sizes(); @@ -610,7 +472,7 @@ namespace OpenRCT2 return false; char buffer[128]; - while (feof(fp) == false) + while (feof(fp) == 0) { size_t numBytesRead = fread(buffer, 1, 128, fp); if (numBytesRead == 0) @@ -695,36 +557,39 @@ namespace OpenRCT2 // Reset position of all streams. data.parkData.SetPosition(0); data.parkParams.SetPosition(0); + data.cheatData.SetPosition(0); data.spriteSpatialData.SetPosition(0); return true; } + bool SerialiseCheats(DataSerialiser& serialiser) + { + CheatsSerialise(serialiser); + + return true; + } + bool SerialiseParkParameters(DataSerialiser& serialiser) { serialiser << _guestGenerationProbability; serialiser << _suggestedGuestMaximum; - serialiser << gCheatsSandboxMode; - serialiser << gCheatsDisableClearanceChecks; - serialiser << gCheatsDisableSupportLimits; - serialiser << gCheatsDisableTrainLengthLimit; - serialiser << gCheatsEnableChainLiftOnAllTrack; - serialiser << gCheatsShowAllOperatingModes; - serialiser << gCheatsShowVehiclesFromOtherTrackTypes; - serialiser << gCheatsFastLiftHill; - serialiser << gCheatsDisableBrakesFailure; - serialiser << gCheatsDisableAllBreakdowns; - serialiser << gCheatsBuildInPauseMode; - serialiser << gCheatsIgnoreRideIntensity; - serialiser << gCheatsDisableVandalism; - serialiser << gCheatsDisableLittering; - serialiser << gCheatsNeverendingMarketing; - serialiser << gCheatsFreezeWeather; - serialiser << gCheatsDisablePlantAging; - serialiser << gCheatsAllowArbitraryRideTypeChanges; - serialiser << gCheatsDisableRideValueAging; serialiser << gConfigGeneral.show_real_names_of_guests; - serialiser << gCheatsIgnoreResearchStatus; + + // To make this a little bit less volatile against updates + // we reserve some space for future additions. + uint64_t tempStorage = 0; + + // If another park parameter has to be added simply swap tempStorage. + // and ensure the length read/write will stay uint64_t + serialiser << tempStorage; + serialiser << tempStorage; + serialiser << tempStorage; + serialiser << tempStorage; + serialiser << tempStorage; + serialiser << tempStorage; + serialiser << tempStorage; + serialiser << tempStorage; return true; } @@ -734,50 +599,27 @@ namespace OpenRCT2 serialiser << command.tick; serialiser << command.commandIndex; - bool isGameAction = false; + uint32_t actionType = 0; if (serialiser.IsSaving()) { - isGameAction = command.action != nullptr; + actionType = command.action->GetType(); } - serialiser << isGameAction; + serialiser << actionType; - if (isGameAction) + if (serialiser.IsLoading()) { - uint32_t actionType = 0; - if (serialiser.IsSaving()) - { - actionType = command.action->GetType(); - } - serialiser << actionType; - - if (serialiser.IsLoading()) - { - command.action = GameActions::Create(actionType); - Guard::Assert(command.action != nullptr); - } - - command.action->Serialise(serialiser); - } - else - { - serialiser << command.eax; - serialiser << command.ebx; - serialiser << command.ecx; - serialiser << command.edx; - serialiser << command.esi; - serialiser << command.edi; - serialiser << command.ebp; - serialiser << command.callback; + command.action = GameActions::Create(actionType); + Guard::Assert(command.action != nullptr); } + + command.action->Serialise(serialiser); + return true; } bool Compatible(ReplayRecordData& data) { - if (data.version == 1 && ReplayVersion == 2) - return true; - - return false; + return data.version == ReplayVersion; } bool Serialise(DataSerialiser& serialiser, ReplayRecordData& data) @@ -810,6 +652,7 @@ namespace OpenRCT2 serialiser << data.timeRecorded; serialiser << data.parkData; serialiser << data.parkParams; + serialiser << data.cheatData; serialiser << data.spriteSpatialData; serialiser << data.tickStart; serialiser << data.tickEnd; @@ -915,26 +758,13 @@ namespace OpenRCT2 bool isPositionValid = false; - if (command.action != nullptr) - { - GameAction* action = command.action.get(); - action->SetFlags(action->GetFlags() | GAME_COMMAND_FLAG_REPLAY); + GameAction* action = command.action.get(); + action->SetFlags(action->GetFlags() | GAME_COMMAND_FLAG_REPLAY); - GameActionResult::Ptr result = GameActions::Execute(action); - if (result->Error == GA_ERROR::OK) - { - isPositionValid = true; - } - } - else + GameActionResult::Ptr result = GameActions::Execute(action); + if (result->Error == GA_ERROR::OK) { - uint32_t flags = command.ebx | GAME_COMMAND_FLAG_REPLAY; - int32_t res = game_do_command( - command.eax, flags, command.ecx, command.edx, command.esi, command.edi, command.ebp); - if (res != MONEY32_UNDEFINED) - { - isPositionValid = true; - } + isPositionValid = true; } // Focus camera on event. diff --git a/src/openrct2/ReplayManager.h b/src/openrct2/ReplayManager.h index 976ae4dcb9..9e18692fa1 100644 --- a/src/openrct2/ReplayManager.h +++ b/src/openrct2/ReplayManager.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -43,11 +43,6 @@ namespace OpenRCT2 virtual bool IsRecording() const = 0; virtual bool IsNormalising() const = 0; - // NOTE: Will become obsolete eventually once all game actions are done. - virtual void AddGameCommand( - uint32_t tick, uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi, uint32_t ebp, - uint8_t callback) - = 0; virtual void AddGameAction(uint32_t tick, const GameAction* action) = 0; virtual bool StartRecording(const std::string& name, uint32_t maxTicks = k_MaxReplayTicks) = 0; diff --git a/src/openrct2/TrackImporter.cpp b/src/openrct2/TrackImporter.cpp new file mode 100644 index 0000000000..6b3ce5aab9 --- /dev/null +++ b/src/openrct2/TrackImporter.cpp @@ -0,0 +1,38 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include "TrackImporter.h" + +#include "core/Path.hpp" +#include "core/String.hpp" + +#include + +namespace TrackImporter +{ + std::unique_ptr Create(const std::string& hintPath) + { + std::unique_ptr trackImporter; + std::string extension = Path::GetExtension(hintPath); + if (ExtensionIsRCT1(extension)) + { + trackImporter = CreateTD4(); + } + else + { + trackImporter = CreateTD6(); + } + return trackImporter; + } + + bool ExtensionIsRCT1(const std::string& extension) + { + return String::Equals(extension, ".td4", true); + } +} // namespace TrackImporter diff --git a/src/openrct2/TrackImporter.h b/src/openrct2/TrackImporter.h new file mode 100644 index 0000000000..dfe68b18ad --- /dev/null +++ b/src/openrct2/TrackImporter.h @@ -0,0 +1,41 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "common.h" +#include "core/IStream.hpp" +#include "ride/TrackDesign.h" + +#include +#include +#include + +/** + * Interface to import scenarios and saved games. + */ +interface ITrackImporter +{ +public: + virtual ~ITrackImporter() = default; + + virtual bool Load(const utf8* path) abstract; + virtual bool LoadFromStream(IStream * stream) abstract; + + virtual std::unique_ptr Import() abstract; +}; + +namespace TrackImporter +{ + std::unique_ptr Create(const std::string& hintPath); + std::unique_ptr CreateTD4(); + std::unique_ptr CreateTD6(); + + bool ExtensionIsRCT1(const std::string& extension); +} // namespace TrackImporter diff --git a/src/openrct2/Version.cpp b/src/openrct2/Version.cpp index 1c2ccd9ded..27321357db 100644 --- a/src/openrct2/Version.cpp +++ b/src/openrct2/Version.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/Version.h b/src/openrct2/Version.h index 29da424899..a2efb7985f 100644 --- a/src/openrct2/Version.h +++ b/src/openrct2/Version.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,13 +12,13 @@ #include "common.h" #define OPENRCT2_NAME "OpenRCT2" -#define OPENRCT2_VERSION "0.2.2" +#define OPENRCT2_VERSION "0.2.3" #if defined(__amd64__) || defined(_M_AMD64) # define OPENRCT2_ARCHITECTURE "x86-64" #elif defined(__i386__) || defined(_M_IX86) # define OPENRCT2_ARCHITECTURE "x86" -#elif defined(__aarch64__) +#elif defined(__aarch64__) || defined(_M_ARM64) # define OPENRCT2_ARCHITECTURE "AArch64" #elif defined(__arm__) || defined(_M_ARM) # if defined(__ARM_ARCH_7A__) diff --git a/src/openrct2/actions/BalloonPressAction.hpp b/src/openrct2/actions/BalloonPressAction.hpp new file mode 100644 index 0000000000..087f0f6501 --- /dev/null +++ b/src/openrct2/actions/BalloonPressAction.hpp @@ -0,0 +1,61 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../world/Sprite.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(BalloonPressAction, GAME_COMMAND_BALLOON_PRESS, GameActionResult) +{ + uint16_t _spriteIndex = SPRITE_INDEX_NULL; + +public: + BalloonPressAction() = default; + BalloonPressAction(uint16_t spriteIndex) + : _spriteIndex(spriteIndex) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + stream << DS_TAG(_spriteIndex); + } + + GameActionResult::Ptr Query() const override + { + rct_sprite* sprite = try_get_sprite(_spriteIndex); + if (sprite == nullptr || !sprite->IsBalloon()) + { + log_error("Tried getting invalid sprite for balloon: %u", _spriteIndex); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + return MakeResult(); + } + + GameActionResult::Ptr Execute() const override + { + rct_sprite* sprite = try_get_sprite(_spriteIndex); + if (sprite == nullptr || !sprite->IsBalloon()) + { + log_error("Tried getting invalid sprite for balloon: %u", _spriteIndex); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + sprite->AsBalloon()->Press(); + + return MakeResult(); + } +}; diff --git a/src/openrct2/actions/BannerPlaceAction.hpp b/src/openrct2/actions/BannerPlaceAction.hpp new file mode 100644 index 0000000000..6821f54902 --- /dev/null +++ b/src/openrct2/actions/BannerPlaceAction.hpp @@ -0,0 +1,200 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../management/Finance.h" +#include "../world/Banner.h" +#include "../world/MapAnimation.h" +#include "../world/Scenery.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(BannerPlaceAction, GAME_COMMAND_PLACE_BANNER, GameActionResult) +{ +private: + CoordsXYZD _loc; + uint8_t _bannerType{ std::numeric_limits::max() }; + BannerIndex _bannerIndex{ BANNER_INDEX_NULL }; + uint8_t _primaryColour; + +public: + BannerPlaceAction() = default; + BannerPlaceAction(CoordsXYZD loc, uint8_t bannerType, BannerIndex bannerIndex, uint8_t primaryColour) + : _loc(loc) + , _bannerType(bannerType) + , _bannerIndex(bannerIndex) + , _primaryColour(primaryColour) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_loc) << DS_TAG(_bannerType) << DS_TAG(_bannerIndex) << DS_TAG(_primaryColour); + } + + GameActionResult::Ptr Query() const override + { + auto res = MakeResult(); + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = _loc.z; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->ErrorTitle = STR_CANT_POSITION_THIS_HERE; + + if (!map_check_free_elements_and_reorganise(1)) + { + log_error("No free map elements."); + return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE); + } + + if (!map_is_location_valid(_loc)) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + } + + auto pathElement = GetValidPathElement(); + + if (pathElement == nullptr) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BE_BUILT_ACROSS_PATHS); + } + + if (!map_can_build_at(_loc)) + { + return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK); + } + + uint8_t baseHeight = _loc.z / 8 + 2; + BannerElement* existingBannerElement = map_get_banner_element_at(_loc.x / 32, _loc.y / 32, baseHeight, _loc.direction); + if (existingBannerElement != nullptr) + { + return MakeResult(GA_ERROR::ITEM_ALREADY_PLACED, STR_CANT_POSITION_THIS_HERE, STR_BANNER_SIGN_IN_THE_WAY); + } + + if (_bannerIndex == BANNER_INDEX_NULL || _bannerIndex >= MAX_BANNERS) + { + log_error("Invalid banner index, bannerIndex = %u", _bannerIndex); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + } + + auto banner = GetBanner(_bannerIndex); + if (banner->type != BANNER_NULL) + { + log_error("Banner index in use, bannerIndex = %u", _bannerIndex); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + } + + rct_scenery_entry* bannerEntry = get_banner_entry(_bannerType); + if (bannerEntry == nullptr) + { + log_error("Invalid banner object type. bannerType = ", _bannerType); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + } + res->Cost = bannerEntry->banner.price; + return res; + } + + GameActionResult::Ptr Execute() const override + { + auto res = MakeResult(); + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = _loc.z; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->ErrorTitle = STR_CANT_POSITION_THIS_HERE; + + if (!map_check_free_elements_and_reorganise(1)) + { + log_error("No free map elements."); + return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE); + } + + uint8_t baseHeight = _loc.z / 8 + 2; + + if (_bannerIndex == BANNER_INDEX_NULL || _bannerIndex >= MAX_BANNERS) + { + log_error("Invalid banner index, bannerIndex = %u", _bannerIndex); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + } + + auto banner = GetBanner(_bannerIndex); + if (banner->type != BANNER_NULL) + { + log_error("Banner index in use, bannerIndex = %u", _bannerIndex); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + } + + TileElement* newTileElement = tile_element_insert({ _loc.x / 32, _loc.y / 32, baseHeight }, 0b0000); + assert(newTileElement != nullptr); + + banner->flags = 0; + banner->text = {}; + banner->text_colour = 2; + banner->type = _bannerType; + banner->colour = _primaryColour; + banner->position.x = _loc.x / 32; + banner->position.y = _loc.y / 32; + newTileElement->SetType(TILE_ELEMENT_TYPE_BANNER); + BannerElement* bannerElement = newTileElement->AsBanner(); + bannerElement->clearance_height = newTileElement->base_height + 2; + bannerElement->SetPosition(_loc.direction); + bannerElement->ResetAllowedEdges(); + bannerElement->SetIndex(_bannerIndex); + if (GetFlags() & GAME_COMMAND_FLAG_GHOST) + { + bannerElement->SetGhost(true); + } + map_invalidate_tile_full(_loc.x, _loc.y); + map_animation_create(MAP_ANIMATION_TYPE_BANNER, _loc.x, _loc.y, bannerElement->base_height); + + rct_scenery_entry* bannerEntry = get_banner_entry(_bannerType); + if (bannerEntry == nullptr) + { + log_error("Invalid banner object type. bannerType = ", _bannerType); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + } + res->Cost = bannerEntry->banner.price; + return res; + } + +private: + PathElement* GetValidPathElement() const + { + TileElement* tileElement = map_get_first_element_at(_loc.x / 32, _loc.y / 32); + do + { + if (tileElement == nullptr) + break; + + if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) + continue; + + auto pathElement = tileElement->AsPath(); + + if (pathElement->base_height != _loc.z / 8 && pathElement->base_height != _loc.z / 8 - 2) + continue; + + if (!(pathElement->GetEdges() & (1 << _loc.direction))) + continue; + + if (pathElement->IsGhost() && !(GetFlags() & GAME_COMMAND_FLAG_GHOST)) + continue; + + return pathElement; + } while (!(tileElement++)->IsLastForTile()); + return nullptr; + } +}; diff --git a/src/openrct2/actions/BannerRemoveAction.hpp b/src/openrct2/actions/BannerRemoveAction.hpp new file mode 100644 index 0000000000..e093ebbfe5 --- /dev/null +++ b/src/openrct2/actions/BannerRemoveAction.hpp @@ -0,0 +1,153 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../management/Finance.h" +#include "../world/Banner.h" +#include "../world/MapAnimation.h" +#include "../world/Scenery.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(BannerRemoveAction, GAME_COMMAND_REMOVE_BANNER, GameActionResult) +{ +private: + CoordsXYZD _loc; + +public: + BannerRemoveAction() = default; + BannerRemoveAction(CoordsXYZD loc) + : _loc(loc) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_loc); + } + + GameActionResult::Ptr Query() const override + { + auto res = MakeResult(); + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = _loc.z; + res->ErrorTitle = STR_CANT_REMOVE_THIS; + + if (!map_can_build_at({ _loc.x, _loc.y, _loc.z - 16 })) + { + return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK); + } + + BannerElement* bannerElement = GetBannerElementAt(); + if (bannerElement == nullptr) + { + log_error( + "Invalid banner location, x = %d, y = %d, z = %d, direction = %d", _loc.x, _loc.y, _loc.z, _loc.direction); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS); + } + + if (bannerElement->GetIndex() >= MAX_BANNERS || bannerElement->GetIndex() == BANNER_INDEX_NULL) + { + log_error("Invalid banner index. index = ", bannerElement->GetIndex()); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS); + } + + auto banner = bannerElement->GetBanner(); + if (banner == nullptr) + { + log_error("Invalid banner index. index = ", bannerElement->GetIndex()); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS); + } + + rct_scenery_entry* bannerEntry = get_banner_entry(banner->type); + if (bannerEntry != nullptr) + { + res->Cost = -((bannerEntry->banner.price * 3) / 4); + } + + return res; + } + + GameActionResult::Ptr Execute() const override + { + auto res = MakeResult(); + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = _loc.z; + res->ErrorTitle = STR_CANT_REMOVE_THIS; + + BannerElement* bannerElement = GetBannerElementAt(); + if (bannerElement == nullptr) + { + log_error( + "Invalid banner location, x = %d, y = %d, z = %d, direction = %d", _loc.x, _loc.y, _loc.z, _loc.direction); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS); + } + + if (bannerElement->GetIndex() >= MAX_BANNERS || bannerElement->GetIndex() == BANNER_INDEX_NULL) + { + log_error("Invalid banner index. index = ", bannerElement->GetIndex()); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS); + } + + auto banner = bannerElement->GetBanner(); + if (banner == nullptr) + { + log_error("Invalid banner index. index = ", bannerElement->GetIndex()); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS); + } + + rct_scenery_entry* bannerEntry = get_banner_entry(banner->type); + if (bannerEntry != nullptr) + { + res->Cost = -((bannerEntry->banner.price * 3) / 4); + } + + tile_element_remove_banner_entry(reinterpret_cast(bannerElement)); + map_invalidate_tile_zoom1(_loc.x, _loc.y, _loc.z / 8, _loc.z / 8 + 32); + bannerElement->Remove(); + + return res; + } + +private: + BannerElement* GetBannerElementAt() const + { + TileElement* tileElement = map_get_first_element_at(_loc.x / 32, _loc.y / 32); + + // Find the banner element at known z and position + do + { + if (tileElement == nullptr) + break; + if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER) + continue; + if (tileElement->base_height != _loc.z / 8) + continue; + if (tileElement->IsGhost() && !(GetFlags() & GAME_COMMAND_FLAG_GHOST)) + continue; + if (tileElement->AsBanner()->GetPosition() != _loc.direction) + continue; + + return tileElement->AsBanner(); + } while (!(tileElement++)->IsLastForTile()); + + return nullptr; + } +}; diff --git a/src/openrct2/actions/BannerSetColourAction.hpp b/src/openrct2/actions/BannerSetColourAction.hpp new file mode 100644 index 0000000000..ad27e8f038 --- /dev/null +++ b/src/openrct2/actions/BannerSetColourAction.hpp @@ -0,0 +1,111 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Context.h" +#include "../management/Finance.h" +#include "../windows/Intent.h" +#include "../world/Banner.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(BannerSetColourAction, GAME_COMMAND_SET_BANNER_COLOUR, GameActionResult) +{ +private: + CoordsXYZD _loc; + uint8_t _primaryColour; + +public: + BannerSetColourAction() = default; + + BannerSetColourAction(CoordsXYZD loc, uint8_t primaryColour) + : _loc(loc) + , _primaryColour(primaryColour) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_loc) << DS_TAG(_primaryColour); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = _loc.z; + res->ErrorTitle = STR_CANT_REPAINT_THIS; + + if (_loc.x < 0 || _loc.y < 0 || _loc.x > gMapSizeMaxXY || _loc.y > gMapSizeMaxXY) + { + log_error("Invalid x / y coordinates: x = %d, y = %d", _loc.x, _loc.y); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + if (_primaryColour > 31) + { + log_error("Invalid primary colour: colour = %u", _primaryColour); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + if (!map_can_build_at({ _loc.x, _loc.y, _loc.z - 16 })) + { + return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_REPAINT_THIS, STR_LAND_NOT_OWNED_BY_PARK); + } + + auto bannerElement = map_get_banner_element_at(_loc.x / 32, _loc.y / 32, _loc.z / 8, _loc.direction); + + if (bannerElement == nullptr) + { + log_error( + "Could not find banner at: x = %d, y = %d, z = %d, direction = %u", _loc.x, _loc.y, _loc.z, _loc.direction); + return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_REPAINT_THIS); + } + + auto index = bannerElement->GetIndex(); + if (index >= MAX_BANNERS || index == BANNER_INDEX_NULL) + { + log_error("Invalid banner index: index = %u", index); + return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_REPAINT_THIS); + } + + if (isExecuting) + { + auto intent = Intent(INTENT_ACTION_UPDATE_BANNER); + intent.putExtra(INTENT_EXTRA_BANNER_INDEX, index); + context_broadcast_intent(&intent); + + auto banner = GetBanner(index); + banner->colour = _primaryColour; + map_invalidate_tile_zoom1(_loc.x, _loc.y, _loc.z, _loc.z + 32); + } + + return res; + } +}; diff --git a/src/openrct2/actions/BannerSetNameAction.hpp b/src/openrct2/actions/BannerSetNameAction.hpp index f8638adbda..0232305400 100644 --- a/src/openrct2/actions/BannerSetNameAction.hpp +++ b/src/openrct2/actions/BannerSetNameAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -54,39 +54,13 @@ public: log_warning("Invalid game command for setting banner name, banner id = %d", _bannerIndex); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } - - // Ensure user string space. - rct_string_id string_id = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, _name.c_str()); - if (string_id != 0) - { - user_string_free(string_id); - } - else - { - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_ERR_CANT_SET_BANNER_TEXT); - } - return MakeResult(); } GameActionResult::Ptr Execute() const override { - rct_banner* banner = &gBanners[_bannerIndex]; - - utf8* buffer = gCommonStringFormatBuffer; - utf8* dst = buffer; - dst = utf8_write_codepoint(dst, FORMAT_COLOUR_CODE_START + banner->text_colour); - String::Set(dst, sizeof(gCommonStringFormatBuffer) - (dst - buffer), _name.c_str(), _name.size()); - - rct_string_id string_id = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, buffer); - if (string_id == 0) - { - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_ERR_CANT_SET_BANNER_TEXT); - } - - rct_string_id prev_string_id = banner->string_idx; - banner->string_idx = string_id; - user_string_free(prev_string_id); + auto banner = GetBanner(_bannerIndex); + banner->text = _name; auto intent = Intent(INTENT_ACTION_UPDATE_BANNER); intent.putExtra(INTENT_EXTRA_BANNER_INDEX, _bannerIndex); diff --git a/src/openrct2/actions/BannerSetStyleAction.hpp b/src/openrct2/actions/BannerSetStyleAction.hpp new file mode 100644 index 0000000000..20de246531 --- /dev/null +++ b/src/openrct2/actions/BannerSetStyleAction.hpp @@ -0,0 +1,169 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Context.h" +#include "../management/Finance.h" +#include "../windows/Intent.h" +#include "../world/Banner.h" +#include "GameAction.h" + +// There is also the BannerSetColourAction that sets primary colour but this action takes banner index rather than x, y, z, +// direction +enum class BannerSetStyleType : uint8_t +{ + PrimaryColour, + TextColour, + NoEntry, + Count +}; + +DEFINE_GAME_ACTION(BannerSetStyleAction, GAME_COMMAND_SET_BANNER_STYLE, GameActionResult) +{ +private: + uint8_t _type = static_cast(BannerSetStyleType::Count); + uint8_t _bannerIndex = BANNER_INDEX_NULL; + uint8_t _parameter; + +public: + BannerSetStyleAction() = default; + + BannerSetStyleAction(BannerSetStyleType type, uint8_t bannerIndex, uint8_t parameter) + : _type(static_cast(type)) + , _bannerIndex(bannerIndex) + , _parameter(parameter) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_type) << DS_TAG(_bannerIndex) << DS_TAG(_parameter); + } + + GameActionResult::Ptr Query() const override + { + auto res = MakeResult(); + + if (_bannerIndex >= MAX_BANNERS || _bannerIndex == BANNER_INDEX_NULL) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_INVALID_SELECTION_OF_OBJECTS); + } + + auto banner = GetBanner(_bannerIndex); + + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = banner->position.x * 32 + 16; + res->Position.y = banner->position.y * 32 + 16; + res->Position.z = tile_element_height(res->Position); + + TileElement* tileElement = banner_get_tile_element(_bannerIndex); + + if (tileElement == nullptr) + { + log_error("Could not find banner index = %u", _bannerIndex); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + switch (static_cast(_type)) + { + case BannerSetStyleType::PrimaryColour: + if (_parameter > 31) + { + log_error("Invalid primary colour: colour = %u", _parameter); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + break; + + case BannerSetStyleType::TextColour: + if (_parameter > 13) + { + log_error("Invalid text colour: colour = %u", _parameter); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + break; + case BannerSetStyleType::NoEntry: + if (tileElement->AsBanner() == nullptr) + { + log_error("Tile element was not a banner."); + return MakeResult(GA_ERROR::UNKNOWN, STR_NONE); + } + break; + default: + log_error("Invalid type: %u", _type); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + return res; + } + + GameActionResult::Ptr Execute() const override + { + auto res = MakeResult(); + + auto banner = GetBanner(_bannerIndex); + + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = banner->position.x * 32 + 16; + res->Position.y = banner->position.y * 32 + 16; + res->Position.z = tile_element_height(res->Position); + + TileElement* tileElement = banner_get_tile_element(_bannerIndex); + + if (tileElement == nullptr) + { + log_error("Could not find banner index = %u", _bannerIndex); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + switch (static_cast(_type)) + { + case BannerSetStyleType::PrimaryColour: + banner->colour = _parameter; + break; + case BannerSetStyleType::TextColour: + banner->text_colour = _parameter; + break; + case BannerSetStyleType::NoEntry: + { + BannerElement* bannerElement = tileElement->AsBanner(); + if (bannerElement == nullptr) + { + log_error("Tile element was not a banner."); + return MakeResult(GA_ERROR::UNKNOWN, STR_NONE); + } + + banner->flags &= ~BANNER_FLAG_NO_ENTRY; + banner->flags |= (_parameter != 0) ? BANNER_FLAG_NO_ENTRY : 0; + uint8_t allowedEdges = 0xF; + if (banner->flags & BANNER_FLAG_NO_ENTRY) + { + allowedEdges &= ~(1 << bannerElement->GetPosition()); + } + bannerElement->SetAllowedEdges(allowedEdges); + break; + } + default: + log_error("Invalid type: %u", _type); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + auto intent = Intent(INTENT_ACTION_UPDATE_BANNER); + intent.putExtra(INTENT_EXTRA_BANNER_INDEX, _bannerIndex); + context_broadcast_intent(&intent); + + return res; + } +}; diff --git a/src/openrct2/actions/ClearAction.hpp b/src/openrct2/actions/ClearAction.hpp index 1615734a6d..8ff04526ab 100644 --- a/src/openrct2/actions/ClearAction.hpp +++ b/src/openrct2/actions/ClearAction.hpp @@ -78,7 +78,7 @@ private: auto x = (_range.GetLeft() + _range.GetRight()) / 2 + 16; auto y = (_range.GetTop() + _range.GetBottom()) / 2 + 16; - auto z = tile_element_height(x, y); + auto z = tile_element_height({ x, y }); result->Position = CoordsXYZ(x, y, z); return result; @@ -144,6 +144,8 @@ private: { tileEdited = false; tileElement = map_get_first_element_at(x, y); + if (tileElement == nullptr) + return totalCost; do { auto type = tileElement->GetType(); @@ -152,7 +154,7 @@ private: case TILE_ELEMENT_TYPE_PATH: if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_FOOTPATH) { - auto footpathRemoveAction = FootpathRemoveAction(x * 32, y * 32, tileElement->base_height); + auto footpathRemoveAction = FootpathRemoveAction({ x * 32, y * 32, tileElement->base_height * 8 }); footpathRemoveAction.SetFlags(GetFlags()); auto res = executing ? GameActions::ExecuteNested(&footpathRemoveAction) @@ -169,7 +171,8 @@ private: if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_SMALL) { auto removeSceneryAction = SmallSceneryRemoveAction( - x * 32, y * 32, tileElement->base_height, tileElement->AsSmallScenery()->GetSceneryQuadrant(), + { x * 32, y * 32, tileElement->base_height * 8 }, + tileElement->AsSmallScenery()->GetSceneryQuadrant(), tileElement->AsSmallScenery()->GetEntryIndex()); removeSceneryAction.SetFlags(GetFlags()); @@ -186,7 +189,8 @@ private: case TILE_ELEMENT_TYPE_WALL: if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_SMALL) { - TileCoordsXYZD wallLocation = { x, y, tileElement->base_height, tileElement->GetDirection() }; + CoordsXYZD wallLocation = { x * 32, y * 32, tileElement->base_height * 8, + tileElement->GetDirection() }; auto wallRemoveAction = WallRemoveAction(wallLocation); wallRemoveAction.SetFlags(GetFlags()); @@ -204,7 +208,7 @@ private: if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_LARGE) { auto removeSceneryAction = LargeSceneryRemoveAction( - x * 32, y * 32, tileElement->base_height, tileElement->GetDirection(), + { x * 32, y * 32, tileElement->base_height * 8, tileElement->GetDirection() }, tileElement->AsLargeScenery()->GetSequenceIndex()); removeSceneryAction.SetFlags(GetFlags() | GAME_COMMAND_FLAG_PATH_SCENERY); @@ -239,6 +243,8 @@ private: auto tileElement = map_get_first_element_at(x, y); do { + if (tileElement == nullptr) + break; if (tileElement->GetType() == TILE_ELEMENT_TYPE_LARGE_SCENERY) { tileElement->AsLargeScenery()->SetIsAccounted(false); @@ -250,6 +256,7 @@ private: static bool MapCanClearAt(int32_t x, int32_t y) { - return (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode || map_is_location_owned_or_has_rights(x, y); + return (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode + || map_is_location_owned_or_has_rights({ x, y }); } }; diff --git a/src/openrct2/actions/ClimateSetAction.hpp b/src/openrct2/actions/ClimateSetAction.hpp index 1613c95bd0..870f20fab4 100644 --- a/src/openrct2/actions/ClimateSetAction.hpp +++ b/src/openrct2/actions/ClimateSetAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/actions/FootpathPlaceAction.hpp b/src/openrct2/actions/FootpathPlaceAction.hpp index e3c3a4b122..8e2067f876 100644 --- a/src/openrct2/actions/FootpathPlaceAction.hpp +++ b/src/openrct2/actions/FootpathPlaceAction.hpp @@ -28,11 +28,11 @@ private: CoordsXYZ _loc; uint8_t _slope; uint8_t _type; - uint8_t _direction = 0xFF; + Direction _direction = INVALID_DIRECTION; public: FootpathPlaceAction() = default; - FootpathPlaceAction(CoordsXYZ loc, uint8_t slope, uint8_t type, uint8_t direction = 0xFF) + FootpathPlaceAction(CoordsXYZ loc, uint8_t slope, uint8_t type, Direction direction = INVALID_DIRECTION) : _loc(loc) , _slope(slope) , _type(type) @@ -63,13 +63,12 @@ public: gFootpathGroundFlags = 0; - if (map_is_edge({ _loc.x, _loc.y })) + if (map_is_edge(_loc)) { return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_FOOTPATH_HERE, STR_OFF_EDGE_OF_MAP); } - if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) - && !map_is_location_owned(_loc.x, _loc.y, _loc.z)) + if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc)) { return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_FOOTPATH_HERE, STR_LAND_NOT_OWNED_BY_PARK); } @@ -79,17 +78,17 @@ public: return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_FOOTPATH_HERE, STR_LAND_SLOPE_UNSUITABLE); } - if (_loc.z / 8 < 2) + if (_loc.z / 8 < FootpathMinHeight) { return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_FOOTPATH_HERE, STR_TOO_LOW); } - if (_loc.z / 8 > 248) + if (_loc.z / 8 > FootpathMaxHeight) { return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_FOOTPATH_HERE, STR_TOO_HIGH); } - if (_direction != 0xFF && _direction > 15) + if (_direction != INVALID_DIRECTION && !direction_valid(_direction)) { log_error("Direction invalid. direction = %u", _direction); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_FOOTPATH_HERE); @@ -128,7 +127,7 @@ public: if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST)) { - if (_direction != 0xFF && !gCheatsDisableClearanceChecks) + if (_direction != INVALID_DIRECTION && !gCheatsDisableClearanceChecks) { // It is possible, let's remove walls between the old and new piece of path auto zLow = _loc.z / 8; @@ -252,12 +251,11 @@ private: return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_FOOTPATH_HERE, STR_CANT_BUILD_THIS_UNDERWATER); } - auto tileElement = map_get_surface_element_at({ _loc.x, _loc.y }); - if (tileElement == nullptr) + auto surfaceElement = map_get_surface_element_at(_loc); + if (surfaceElement == nullptr) { return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_FOOTPATH_HERE); } - auto surfaceElement = tileElement->AsSurface(); int32_t supportHeight = zLow - surfaceElement->base_height; res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / 2) * MONEY(5, 00); @@ -314,12 +312,11 @@ private: gFootpathGroundFlags = gMapGroundFlags; - auto tileElement = map_get_surface_element_at({ _loc.x, _loc.y }); - if (tileElement == nullptr) + auto surfaceElement = map_get_surface_element_at(_loc); + if (surfaceElement == nullptr) { return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_FOOTPATH_HERE); } - auto surfaceElement = tileElement->AsSurface(); int32_t supportHeight = zLow - surfaceElement->base_height; res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / 2) * MONEY(5, 00); @@ -334,7 +331,7 @@ private: } else { - tileElement = tile_element_insert(_loc.x / 32, _loc.y / 32, zLow, 0b1111); + auto tileElement = tile_element_insert({ _loc.x / 32, _loc.y / 32, zLow }, 0b1111); assert(tileElement != nullptr); tileElement->SetType(TILE_ELEMENT_TYPE_PATH); PathElement* pathElement = tileElement->AsPath(); @@ -399,7 +396,7 @@ private: } } - if (gPeepSpawns.size() == 0) + if (gPeepSpawns.empty()) { gPeepSpawns.emplace_back(); } @@ -443,6 +440,8 @@ private: tileElement = map_get_first_element_at(x, y); do { + if (tileElement == nullptr) + break; if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH && tileElement->base_height == z && (tileElement->AsPath()->IsSloped() == isSloped) && (tileElement->AsPath()->GetSlopeDirection() == (slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK))) diff --git a/src/openrct2/actions/FootpathPlaceFromTrackAction.hpp b/src/openrct2/actions/FootpathPlaceFromTrackAction.hpp new file mode 100644 index 0000000000..3dfa853b1e --- /dev/null +++ b/src/openrct2/actions/FootpathPlaceFromTrackAction.hpp @@ -0,0 +1,282 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Cheats.h" +#include "../OpenRCT2.h" +#include "../core/MemoryStream.h" +#include "../interface/Window.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../world/Footpath.h" +#include "../world/Location.hpp" +#include "../world/Park.h" +#include "../world/Surface.h" +#include "../world/Wall.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(FootpathPlaceFromTrackAction, GAME_COMMAND_PLACE_PATH_FROM_TRACK, GameActionResult) +{ +private: + CoordsXYZ _loc; + uint8_t _slope; + uint8_t _type; + uint8_t _edges; + +public: + FootpathPlaceFromTrackAction() = default; + FootpathPlaceFromTrackAction(CoordsXYZ loc, uint8_t slope, uint8_t type, uint8_t edges) + : _loc(loc) + , _slope(slope) + , _type(type) + , _edges(edges) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_edges); + } + + GameActionResult::Ptr Query() const override + { + GameActionResult::Ptr res = std::make_unique(); + res->Cost = 0; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position = _loc; + res->Position.x += 16; + res->Position.y += 16; + + gFootpathGroundFlags = 0; + + if (map_is_edge(_loc)) + { + return MakeResult( + GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_OFF_EDGE_OF_MAP); + } + + if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc)) + { + return MakeResult(GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK); + } + + if (_loc.z / 8 < FootpathMinHeight) + { + return MakeResult(GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TOO_LOW); + } + + if (_loc.z / 8 > FootpathMaxHeight) + { + return MakeResult(GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TOO_HIGH); + } + + return ElementInsertQuery(std::move(res)); + } + + GameActionResult::Ptr Execute() const override + { + GameActionResult::Ptr res = std::make_unique(); + res->Cost = 0; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position = _loc; + res->Position.x += 16; + res->Position.y += 16; + + if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST)) + { + footpath_interrupt_peeps(_loc.x, _loc.y, _loc.z); + } + + gFootpathGroundFlags = 0; + + // Force ride construction to recheck area + _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; + + return ElementInsertExecute(std::move(res)); + } + +private: + GameActionResult::Ptr ElementInsertQuery(GameActionResult::Ptr res) const + { + bool entrancePath = false, entranceIsSamePath = false; + + if (!map_check_free_elements_and_reorganise(1)) + { + return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE); + } + + res->Cost = MONEY(12, 00); + + QuarterTile quarterTile{ 0b1111, 0 }; + auto zLow = _loc.z / 8; + auto zHigh = zLow + 4; + if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED) + { + quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK); + zHigh += 2; + } + + auto entranceElement = map_get_park_entrance_element_at(_loc.x, _loc.y, zLow, false); + // Make sure the entrance part is the middle + if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0) + { + entrancePath = true; + // Make the price the same as replacing a path + if (entranceElement->GetPathType() == (_type & 0xF)) + entranceIsSamePath = true; + else + res->Cost -= MONEY(6, 00); + } + + // Do not attempt to build a crossing with a queue or a sloped. + uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT) + ? CREATE_CROSSING_MODE_NONE + : CREATE_CROSSING_MODE_PATH_OVER_TRACK; + if (!entrancePath + && !map_can_construct_with_clear_at( + _loc.x, _loc.y, zLow, zHigh, &map_place_non_scenery_clear_func, quarterTile, GetFlags(), &res->Cost, + crossingMode)) + { + return MakeResult( + GA_ERROR::NO_CLEARANCE, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, gGameCommandErrorText, + gCommonFormatArgs); + } + + gFootpathGroundFlags = gMapGroundFlags; + if (!gCheatsDisableClearanceChecks && (gMapGroundFlags & ELEMENT_IS_UNDERWATER)) + { + return MakeResult( + GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_CANT_BUILD_THIS_UNDERWATER); + } + + auto surfaceElement = map_get_surface_element_at(_loc); + if (surfaceElement == nullptr) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE); + } + int32_t supportHeight = zLow - surfaceElement->base_height; + res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / 2) * MONEY(5, 00); + + // Prevent the place sound from being spammed + if (entranceIsSamePath) + res->Cost = 0; + + return res; + } + + GameActionResult::Ptr ElementInsertExecute(GameActionResult::Ptr res) const + { + bool entrancePath = false, entranceIsSamePath = false; + + if (!(GetFlags() & (GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST))) + { + footpath_remove_litter(_loc.x, _loc.y, _loc.z); + } + + res->Cost = MONEY(12, 00); + + QuarterTile quarterTile{ 0b1111, 0 }; + auto zLow = _loc.z / 8; + auto zHigh = zLow + 4; + if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED) + { + quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK); + zHigh += 2; + } + + auto entranceElement = map_get_park_entrance_element_at(_loc.x, _loc.y, zLow, false); + // Make sure the entrance part is the middle + if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0) + { + entrancePath = true; + // Make the price the same as replacing a path + if (entranceElement->GetPathType() == (_type & 0xF)) + entranceIsSamePath = true; + else + res->Cost -= MONEY(6, 00); + } + + // Do not attempt to build a crossing with a queue or a sloped. + uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT) + ? CREATE_CROSSING_MODE_NONE + : CREATE_CROSSING_MODE_PATH_OVER_TRACK; + if (!entrancePath + && !map_can_construct_with_clear_at( + _loc.x, _loc.y, zLow, zHigh, &map_place_non_scenery_clear_func, quarterTile, + GAME_COMMAND_FLAG_APPLY | GetFlags(), &res->Cost, crossingMode)) + { + return MakeResult( + GA_ERROR::NO_CLEARANCE, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, gGameCommandErrorText, + gCommonFormatArgs); + } + + gFootpathGroundFlags = gMapGroundFlags; + + auto surfaceElement = map_get_surface_element_at(_loc); + if (surfaceElement == nullptr) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE); + } + int32_t supportHeight = zLow - surfaceElement->base_height; + res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / 2) * MONEY(5, 00); + + if (entrancePath) + { + if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !entranceIsSamePath) + { + // Set the path type but make sure it's not a queue as that will not show up + entranceElement->SetPathType(_type & 0x7F); + map_invalidate_tile_full(_loc.x, _loc.y); + } + } + else + { + auto tileElement = tile_element_insert({ _loc.x / 32, _loc.y / 32, zLow }, 0b1111); + assert(tileElement != nullptr); + tileElement->SetType(TILE_ELEMENT_TYPE_PATH); + PathElement* pathElement = tileElement->AsPath(); + pathElement->clearance_height = zHigh; + pathElement->SetPathEntryIndex(_type); + pathElement->SetSlopeDirection(_slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK); + if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED) + { + pathElement->SetSloped(true); + } + if (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) + { + pathElement->SetIsQueue(true); + } + pathElement->SetAddition(0); + pathElement->SetRideIndex(RIDE_ID_NULL); + pathElement->SetAdditionStatus(255); + pathElement->SetIsBroken(false); + pathElement->SetEdges(_edges); + pathElement->SetCorners(0); + if (GetFlags() & GAME_COMMAND_FLAG_GHOST) + { + pathElement->SetGhost(true); + } + map_invalidate_tile_full(_loc.x, _loc.y); + } + + // Prevent the place sound from being spammed + if (entranceIsSamePath) + res->Cost = 0; + + return res; + } +}; diff --git a/src/openrct2/actions/FootpathRemoveAction.hpp b/src/openrct2/actions/FootpathRemoveAction.hpp index 94dfef978c..b3ba4c3803 100644 --- a/src/openrct2/actions/FootpathRemoveAction.hpp +++ b/src/openrct2/actions/FootpathRemoveAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -19,21 +19,18 @@ #include "../world/Location.hpp" #include "../world/Park.h" #include "../world/Wall.h" +#include "BannerRemoveAction.hpp" #include "GameAction.h" DEFINE_GAME_ACTION(FootpathRemoveAction, GAME_COMMAND_REMOVE_PATH, GameActionResult) { private: - int32_t _x; - int32_t _y; - int32_t _z; + CoordsXYZ _loc; public: FootpathRemoveAction() = default; - FootpathRemoveAction(int32_t x, int32_t y, int32_t z) - : _x(x) - , _y(y) - , _z(z) + FootpathRemoveAction(CoordsXYZ location) + : _loc(location) { } @@ -46,7 +43,7 @@ public: { GameAction::Serialise(stream); - stream << DS_TAG(_x) << DS_TAG(_y) << DS_TAG(_z); + stream << DS_TAG(_loc); } GameActionResult::Ptr Query() const override @@ -54,9 +51,9 @@ public: GameActionResult::Ptr res = std::make_unique(); res->Cost = 0; res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - res->Position = { _x + 16, _y + 16, _z * 8 }; + res->Position = { _loc.x + 16, _loc.y + 16, _loc.z }; - if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_x, _y, _z * 8)) + if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc)) { return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_LAND_NOT_OWNED_BY_PARK); } @@ -77,21 +74,25 @@ public: GameActionResult::Ptr res = std::make_unique(); res->Cost = 0; res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - res->Position = { _x + 16, _y + 16, _z * 8 }; + res->Position = { _loc.x + 16, _loc.y + 16, _loc.z }; if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST)) { - footpath_interrupt_peeps(_x, _y, _z * 8); - footpath_remove_litter(_x, _y, _z * 8); + footpath_interrupt_peeps(_loc.x, _loc.y, _loc.z); + footpath_remove_litter(_loc.x, _loc.y, _loc.z); } TileElement* footpathElement = GetFootpathElement(); if (footpathElement != nullptr) { footpath_queue_chain_reset(); - remove_banners_at_element(_x, _y, footpathElement); - footpath_remove_edges_at(_x, _y, footpathElement); - map_invalidate_tile_full(_x, _y); + auto bannerRes = RemoveBannersAtElement(_loc.x, _loc.y, footpathElement); + if (bannerRes->Error == GA_ERROR::OK) + { + res->Cost += bannerRes->Cost; + } + footpath_remove_edges_at(_loc.x, _loc.y, footpathElement); + map_invalidate_tile_full(_loc.x, _loc.y); tile_element_remove(footpathElement); footpath_update_queue_chains(); } @@ -100,7 +101,7 @@ public: return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_FOOTPATH_FROM_HERE); } - res->Cost = GetRefundPrice(footpathElement); + res->Cost += GetRefundPrice(footpathElement); return res; } @@ -110,7 +111,7 @@ private: { bool getGhostPath = GetFlags() & GAME_COMMAND_FLAG_GHOST; - TileElement* tileElement = map_get_footpath_element(_x / 32, _y / 32, _z); + TileElement* tileElement = map_get_footpath_element(_loc.x / 32, _loc.y / 32, _loc.z / 8); TileElement* footpathElement = nullptr; if (tileElement != nullptr) { @@ -140,4 +141,34 @@ private: money32 cost = -MONEY(10, 00); return cost; } + + /** + * + * rct2: 0x006BA23E + */ + GameActionResult::Ptr RemoveBannersAtElement(int32_t x, int32_t y, TileElement * tileElement) const + { + auto result = MakeResult(); + while (!(tileElement++)->IsLastForTile()) + { + if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH) + return result; + else if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER) + continue; + + auto bannerRemoveAction = BannerRemoveAction( + { x, y, tileElement->base_height * 8, tileElement->AsBanner()->GetPosition() }); + bool isGhost = tileElement->IsGhost(); + auto bannerFlags = GetFlags() | (isGhost ? static_cast(GAME_COMMAND_FLAG_GHOST) : 0); + bannerRemoveAction.SetFlags(bannerFlags); + auto res = GameActions::ExecuteNested(&bannerRemoveAction); + // Ghost removal is free + if (res->Error == GA_ERROR::OK && !isGhost) + { + result->Cost += res->Cost; + } + tileElement--; + } + return result; + } }; diff --git a/src/openrct2/actions/FootpathSceneryPlaceAction.hpp b/src/openrct2/actions/FootpathSceneryPlaceAction.hpp index 7e7a3f7377..3ded7310ae 100644 --- a/src/openrct2/actions/FootpathSceneryPlaceAction.hpp +++ b/src/openrct2/actions/FootpathSceneryPlaceAction.hpp @@ -53,13 +53,12 @@ public: auto res = MakeResult(); res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; res->Position = _loc; - if (!map_is_location_valid({ _loc.x, _loc.y })) + if (!map_is_location_valid(_loc)) { return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE, STR_OFF_EDGE_OF_MAP); } - if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) - && !map_is_location_owned(_loc.x, _loc.y, _loc.z / 8)) + if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc)) { return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK); } @@ -75,21 +74,18 @@ public: } auto tileElement = map_get_footpath_element(_loc.x / 32, _loc.y / 32, _loc.z / 8); - auto pathElement = tileElement->AsPath(); - - if (pathElement == nullptr) + if (tileElement == nullptr) { log_error("Could not find path element."); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); } + auto pathElement = tileElement->AsPath(); + // No change if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && pathElement->GetAddition() == _pathItemType && !(pathElement->IsBroken())) { - if (GetFlags() & GAME_COMMAND_FLAG_4) - return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_POSITION_THIS_HERE); - return res; } @@ -129,9 +125,6 @@ public: res->Cost = sceneryEntry->path_bit.price; } - if (GetFlags() & GAME_COMMAND_FLAG_4) - return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_POSITION_THIS_HERE); - // Should place a ghost? if (GetFlags() & GAME_COMMAND_FLAG_GHOST) { diff --git a/src/openrct2/actions/FootpathSceneryRemoveAction.hpp b/src/openrct2/actions/FootpathSceneryRemoveAction.hpp index 9d96e1dc66..3d19cfbc75 100644 --- a/src/openrct2/actions/FootpathSceneryRemoveAction.hpp +++ b/src/openrct2/actions/FootpathSceneryRemoveAction.hpp @@ -47,13 +47,12 @@ public: GameActionResult::Ptr Query() const override { - if (!map_is_location_valid({ _loc.x, _loc.y })) + if (!map_is_location_valid(_loc)) { return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS, STR_OFF_EDGE_OF_MAP); } - if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) - && !map_is_location_owned(_loc.x, _loc.y, _loc.z / 8)) + if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc)) { return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK); } @@ -69,17 +68,22 @@ public: } auto tileElement = map_get_footpath_element(_loc.x / 32, _loc.y / 32, _loc.z / 8); - auto pathElement = tileElement->AsPath(); + if (tileElement == nullptr) + { + log_warning("Could not find path element."); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS); + } + auto pathElement = tileElement->AsPath(); if (pathElement == nullptr) { - log_error("Could not find path element."); + log_warning("Could not find path element."); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS); } if (!pathElement->AdditionIsGhost() && (GetFlags() & GAME_COMMAND_FLAG_GHOST)) { - log_error("Tried to remove non ghost during ghost removal."); + log_warning("Tried to remove non ghost during ghost removal."); return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_REMOVE_THIS); } auto res = MakeResult(); diff --git a/src/openrct2/actions/GameAction.cpp b/src/openrct2/actions/GameAction.cpp index 015e174338..bce93c20d7 100644 --- a/src/openrct2/actions/GameAction.cpp +++ b/src/openrct2/actions/GameAction.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -19,6 +19,7 @@ #include "../platform/platform.h" #include "../scenario/Scenario.h" #include "../world/Park.h" +#include "../world/Scenery.h" #include #include @@ -46,7 +47,36 @@ GameActionResult::GameActionResult(GA_ERROR error, rct_string_id title, rct_stri namespace GameActions { + struct QueuedGameAction + { + uint32_t tick; + uint32_t uniqueId; + GameAction::Ptr action; + + explicit QueuedGameAction(uint32_t t, std::unique_ptr&& ga, uint32_t id) + : tick(t) + , uniqueId(id) + , action(std::move(ga)) + { + } + + bool operator<(const QueuedGameAction& comp) const + { + // First sort by tick + if (tick < comp.tick) + return true; + if (tick > comp.tick) + return false; + + // If the ticks are equal sort by commandIndex + return uniqueId < comp.uniqueId; + } + }; + static GameActionFactory _actions[GAME_COMMAND_COUNT]; + static std::multiset _actionQueue; + static uint32_t _nextUniqueId = 0; + static bool _suspended = false; GameActionFactory Register(uint32_t id, GameActionFactory factory) { @@ -66,6 +96,97 @@ namespace GameActions return false; } + void SuspendQueue() + { + _suspended = true; + } + + void ResumeQueue() + { + _suspended = false; + } + + void Enqueue(const GameAction* ga, uint32_t tick) + { + auto action = Clone(ga); + Enqueue(std::move(action), tick); + } + + void Enqueue(GameAction::Ptr&& ga, uint32_t tick) + { + if (ga->GetPlayer() == -1 && network_get_mode() != NETWORK_MODE_NONE) + { + // Server can directly invoke actions and will have no player id assigned + // as that normally happens when receiving them over network. + ga->SetPlayer(network_get_current_player_id()); + } + _actionQueue.emplace(tick, std::move(ga), _nextUniqueId++); + } + + void ProcessQueue() + { + if (_suspended) + { + // Do nothing if suspended, this is usually the case between connect and map loads. + return; + } + + const uint32_t currentTick = gCurrentTicks; + + while (_actionQueue.begin() != _actionQueue.end()) + { + // run all the game commands at the current tick + const QueuedGameAction& queued = *_actionQueue.begin(); + + if (network_get_mode() == NETWORK_MODE_CLIENT) + { + if (queued.tick < currentTick) + { + // This should never happen. + Guard::Assert( + false, + "Discarding game action from tick behind current tick, ID: %08X, Action Tick: %08X, Current Tick: " + "%08X\n", + queued.uniqueId, queued.tick, currentTick); + } + else if (queued.tick > currentTick) + { + return; + } + } + + // Remove ghost scenery so it doesn't interfere with incoming network command + switch (queued.action->GetType()) + { + case GAME_COMMAND_PLACE_WALL: + case GAME_COMMAND_PLACE_LARGE_SCENERY: + case GAME_COMMAND_PLACE_BANNER: + case GAME_COMMAND_PLACE_SCENERY: + scenery_remove_ghost_tool_placement(); + break; + } + + GameAction* action = queued.action.get(); + action->SetFlags(action->GetFlags() | GAME_COMMAND_FLAG_NETWORKED); + + Guard::Assert(action != nullptr); + + GameActionResult::Ptr result = Execute(action); + if (result->Error == GA_ERROR::OK && network_get_mode() == NETWORK_MODE_SERVER) + { + // Relay this action to all other clients. + network_send_game_action(action); + } + + _actionQueue.erase(_actionQueue.begin()); + } + } + + void ClearQueue() + { + _actionQueue.clear(); + } + void Initialize() { static bool initialized = false; @@ -106,7 +227,7 @@ namespace GameActions action->Serialise(dsOut); // Serialise into new action. - MemoryStream& stream = dsOut.GetStream(); + IStream& stream = dsOut.GetStream(); stream.SetPosition(0); DataSerialiser dsIn(false, stream); @@ -131,7 +252,7 @@ namespace GameActions Guard::ArgumentNotNull(action); uint16_t actionFlags = action->GetActionFlags(); - if (topLevel == true && !CheckActionInPausedMode(actionFlags)) + if (topLevel && !CheckActionInPausedMode(actionFlags)) { GameActionResult::Ptr result = std::make_unique(); @@ -154,7 +275,7 @@ namespace GameActions if (result->Error == GA_ERROR::OK) { - if (finance_check_affordability(result->Cost, action->GetFlags()) == false) + if (!finance_check_affordability(result->Cost, action->GetFlags())) { result->Error = GA_ERROR::INSUFFICIENT_FUNDS; result->ErrorMessage = STR_NOT_ENOUGH_CASH_REQUIRES; @@ -193,7 +314,9 @@ namespace GameActions MemoryStream& output = ctx.output; char temp[128] = {}; - snprintf(temp, sizeof(temp), "[%s] GA: %s (%08X) (", GetRealm(), action->GetName(), action->GetType()); + snprintf( + temp, sizeof(temp), "[%s] Tick: %u, GA: %s (%08X) (", GetRealm(), gCurrentTicks, action->GetName(), + action->GetType()); output.Write(temp, strlen(temp)); @@ -274,7 +397,7 @@ namespace GameActions if (!(actionFlags & GA_FLAGS::CLIENT_ONLY) && !(flags & GAME_COMMAND_FLAG_NETWORKED)) { log_verbose("[%s] GameAction::Execute %s (Queue)", GetRealm(), action->GetName()); - network_enqueue_game_action(action); + Enqueue(action, gCurrentTicks); return result; } @@ -290,7 +413,7 @@ namespace GameActions LogActionFinish(logContext, action, result); // If not top level just give away the result. - if (topLevel == false) + if (!topLevel) return result; gCommandPosition.x = result->Position.x; @@ -301,7 +424,7 @@ namespace GameActions if (result->Error == GA_ERROR::OK && finance_check_money_required(flags) && result->Cost != 0) { finance_payment(result->Cost, result->ExpenditureType); - money_effect_create(result->Cost); + rct_money_effect::Create(result->Cost); } if (!(actionFlags & GA_FLAGS::CLIENT_ONLY) && result->Error == GA_ERROR::OK) @@ -326,7 +449,7 @@ namespace GameActions } else if (network_get_mode() == NETWORK_MODE_NONE) { - bool commandExecutes = (flags & GAME_COMMAND_FLAG_GHOST) == 0 && (flags & GAME_COMMAND_FLAG_5) == 0; + bool commandExecutes = (flags & GAME_COMMAND_FLAG_GHOST) == 0 && (flags & GAME_COMMAND_FLAG_NO_SPEND) == 0; bool recordAction = false; if (replayManager) @@ -357,7 +480,22 @@ namespace GameActions cb(action, result.get()); } - if (result->Error != GA_ERROR::OK && !(flags & GAME_COMMAND_FLAG_GHOST) && !(flags & GAME_COMMAND_FLAG_5)) + // Only show errors when its not a ghost and not a preview and also top level action. + bool shouldShowError = !(flags & GAME_COMMAND_FLAG_GHOST) && !(flags & GAME_COMMAND_FLAG_NO_SPEND) && topLevel; + + // In network mode the error should be only shown to the issuer of the action. + if (network_get_mode() != NETWORK_MODE_NONE) + { + // If the action was never networked and query fails locally the player id is not assigned. + // So compare only if the action went into the queue otherwise show errors by default. + const bool isActionFromNetwork = (action->GetFlags() & GAME_COMMAND_FLAG_NETWORKED) != 0; + if (isActionFromNetwork && action->GetPlayer() != network_get_current_player_id()) + { + shouldShowError = false; + } + } + + if (result->Error != GA_ERROR::OK && shouldShowError) { // Show the error box std::copy(result->ErrorMessageArgs.begin(), result->ErrorMessageArgs.end(), gCommonFormatArgs); diff --git a/src/openrct2/actions/GameAction.h b/src/openrct2/actions/GameAction.h index f98f78b98f..c5f24d2534 100644 --- a/src/openrct2/actions/GameAction.h +++ b/src/openrct2/actions/GameAction.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -71,7 +71,7 @@ public: GA_ERROR Error = GA_ERROR::OK; rct_string_id ErrorTitle = STR_NONE; rct_string_id ErrorMessage = STR_NONE; - std::array ErrorMessageArgs; + std::array ErrorMessageArgs; CoordsXYZ Position = { LOCATION_NULL, LOCATION_NULL, LOCATION_NULL }; money32 Cost = 0; uint16_t ExpenditureType = 0; @@ -93,8 +93,8 @@ public: private: uint32_t const _type; - NetworkPlayerId_t _playerId = { 0 }; // Callee - uint32_t _flags = 0; // GAME_COMMAND_FLAGS + NetworkPlayerId_t _playerId = { -1 }; // Callee + uint32_t _flags = 0; // GAME_COMMAND_FLAGS uint32_t _networkId = 0; Callback_t _callback; @@ -126,7 +126,7 @@ public: // Make sure we execute some things only on the client. uint16_t flags = 0; - if ((GetFlags() & GAME_COMMAND_FLAG_GHOST) != 0 || (GetFlags() & GAME_COMMAND_FLAG_5) != 0) + if ((GetFlags() & GAME_COMMAND_FLAG_GHOST) != 0 || (GetFlags() & GAME_COMMAND_FLAG_NO_SPEND) != 0) { flags |= GA_FLAGS::CLIENT_ONLY; } @@ -188,6 +188,15 @@ public: return const_cast(*this).Serialise(stream); } + /** + * Override this to specify the wait time in milliseconds the player is required to wait before + * being able to execute it again. + */ + virtual uint32_t GetCooldownTime() const + { + return 0; + } + /** * Query the result of the game action without changing the game state. */ @@ -245,6 +254,20 @@ namespace GameActions void Initialize(); void Register(); bool IsValidId(uint32_t id); + + // Halts the queue processing until ResumeQueue is called, any calls to ProcessQueue + // will have no effect during suspension. It has no effect of actions that will not + // cross the network. + void SuspendQueue(); + + // Resumes queue processing. + void ResumeQueue(); + + void Enqueue(const GameAction* ga, uint32_t tick); + void Enqueue(GameAction::Ptr&& ga, uint32_t tick); + void ProcessQueue(); + void ClearQueue(); + GameAction::Ptr Create(uint32_t id); GameAction::Ptr Clone(const GameAction* action); diff --git a/src/openrct2/actions/GameActionCompat.cpp b/src/openrct2/actions/GameActionCompat.cpp index fd235d54f7..5815160294 100644 --- a/src/openrct2/actions/GameActionCompat.cpp +++ b/src/openrct2/actions/GameActionCompat.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -23,7 +23,7 @@ #pragma region PlaceParkEntranceAction money32 place_park_entrance(int16_t x, int16_t y, int16_t z, uint8_t direction) { - auto gameAction = PlaceParkEntranceAction(x, y, z, direction); + auto gameAction = PlaceParkEntranceAction({ x, y, z * 16, direction }); auto result = GameActions::Execute(&gameAction); if (result->Error == GA_ERROR::OK) { @@ -35,17 +35,6 @@ money32 place_park_entrance(int16_t x, int16_t y, int16_t z, uint8_t direction) } } -/** - * - * rct2: 0x006666E7 - */ -void game_command_place_park_entrance( - [[maybe_unused]] int32_t* eax, [[maybe_unused]] int32_t* ebx, [[maybe_unused]] int32_t* ecx, [[maybe_unused]] int32_t* edx, - [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - Guard::Assert(false, "GAME_COMMAND_PLACE_PARK_ENTRANCE DEPRECATED"); -} - /** * * rct2: 0x00666F4E @@ -54,7 +43,7 @@ money32 park_entrance_place_ghost(int32_t x, int32_t y, int32_t z, int32_t direc { park_entrance_remove_ghost(); - auto gameAction = PlaceParkEntranceAction(x, y, z, direction); + auto gameAction = PlaceParkEntranceAction({ x, y, z * 16, (Direction)direction }); gameAction.SetFlags(GAME_COMMAND_FLAG_GHOST); auto result = GameActions::Execute(&gameAction); @@ -76,15 +65,6 @@ void park_set_entrance_fee(money32 fee) auto gameAction = SetParkEntranceFeeAction((money16)fee); GameActions::Execute(&gameAction); } - -void game_command_set_park_entrance_fee( - [[maybe_unused]] int* eax, [[maybe_unused]] int* ebx, [[maybe_unused]] int* ecx, [[maybe_unused]] int* edx, - [[maybe_unused]] int* esi, int* edi, [[maybe_unused]] int* ebp) -{ - money16 fee = (money16)(*edi & 0xFFFF); - auto gameAction = SetParkEntranceFeeAction(fee); - GameActions::Execute(&gameAction); -} #pragma endregion #pragma region RideCreateAction @@ -135,17 +115,6 @@ money32 ride_create_command(int32_t type, int32_t subType, int32_t flags, ride_i return res->Cost; } -/** - * - * rct2: 0x006B3F0F - */ -void game_command_create_ride( - [[maybe_unused]] int32_t* eax, [[maybe_unused]] int32_t* ebx, [[maybe_unused]] int32_t* ecx, [[maybe_unused]] int32_t* edx, - [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - Guard::Assert(false, "GAME_COMMAND_CREATE_RIDE DEPRECATED"); -} - #pragma endregion #pragma region RideSetStatusAction @@ -156,17 +125,6 @@ void ride_set_status(Ride* ride, int32_t status) GameActions::Execute(&gameAction); } -/** - * - * rct2: 0x006B4EA6 - */ -void game_command_set_ride_status( - [[maybe_unused]] int32_t* eax, [[maybe_unused]] int32_t* ebx, [[maybe_unused]] int32_t* ecx, [[maybe_unused]] int32_t* edx, - [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - Guard::Assert(false, "GAME_COMMAND_SET_RIDE_STATUS DEPRECATED"); -} - #pragma endregion #pragma region RideSetNameAction @@ -176,17 +134,6 @@ void ride_set_name(Ride* ride, const char* name, uint32_t flags) gameAction.SetFlags(flags); GameActions::Execute(&gameAction); } - -/** - * - * rct2: 0x006B578B - */ -void game_command_set_ride_name( - [[maybe_unused]] int32_t* eax, [[maybe_unused]] int32_t* ebx, [[maybe_unused]] int32_t* ecx, [[maybe_unused]] int32_t* edx, - [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - Guard::Assert(false, "GAME_COMMAND_SET_RIDE_NAME DEPRECATED"); -} #pragma endregion #pragma region RideModifyAction @@ -197,17 +144,6 @@ void ride_action_modify(Ride* ride, int32_t modifyType, int32_t flags) GameActions::Execute(&gameAction); } - -/** - * - * rct2: 0x006B49D9 - */ -void game_command_demolish_ride( - [[maybe_unused]] int32_t* eax, [[maybe_unused]] int32_t* ebx, [[maybe_unused]] int32_t* ecx, [[maybe_unused]] int32_t* edx, - [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - Guard::Assert(false, "GAME_COMMAND_DEMOLISH_RIDE DEPRECATED"); -} #pragma endregion #pragma region GuestSetName @@ -217,18 +153,6 @@ void guest_set_name(uint16_t spriteIndex, const char* name) auto gameAction = GuestSetNameAction(spriteIndex, name); GameActions::Execute(&gameAction); } - -/** - * - * rct2: 0x00698D6C - */ -void game_command_set_guest_name( - [[maybe_unused]] int32_t* eax, [[maybe_unused]] int32_t* ebx, [[maybe_unused]] int32_t* ecx, [[maybe_unused]] int32_t* edx, - [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - Guard::Assert(false, "GAME_COMMAND_SET_GUEST_NAME DEPRECATED"); -} - #pragma endregion #pragma region StaffSetName @@ -238,13 +162,6 @@ void staff_set_name(uint16_t spriteIndex, const char* name) auto gameAction = StaffSetNameAction(spriteIndex, name); GameActions::Execute(&gameAction); } - -void game_command_set_staff_name( - [[maybe_unused]] int32_t* eax, [[maybe_unused]] int32_t* ebx, [[maybe_unused]] int32_t* ecx, [[maybe_unused]] int32_t* edx, - [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - Guard::Assert(false, "GAME_COMMAND_SET_STAFF_NAME DEPRECATED"); -} #pragma endregion #pragma region PlacePeepSpawn @@ -252,14 +169,7 @@ bool place_peep_spawn(CoordsXYZD location) { auto gameAction = PlacePeepSpawnAction(location); auto result = GameActions::Execute(&gameAction); - if (result->Error == GA_ERROR::OK) - { - return true; - } - else - { - return false; - } + return result->Error == GA_ERROR::OK; } #pragma endregion @@ -268,7 +178,7 @@ money32 maze_set_track( uint16_t x, uint16_t y, uint16_t z, uint8_t flags, bool initialPlacement, uint8_t direction, ride_id_t rideIndex, uint8_t mode) { - auto gameAction = MazeSetTrackAction(x, y, z, initialPlacement, direction, rideIndex, mode); + auto gameAction = MazeSetTrackAction({ x, y, z, direction }, initialPlacement, rideIndex, mode); gameAction.SetFlags(flags); GameActionResult::Ptr res; @@ -290,15 +200,4 @@ money32 maze_set_track( return res->Cost; } - -/** - * - * rct2: 0x006CD8CE - */ -void game_command_set_maze_track( - [[maybe_unused]] int32_t* eax, [[maybe_unused]] int32_t* ebx, [[maybe_unused]] int32_t* ecx, [[maybe_unused]] int32_t* edx, - [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - Guard::Assert(false, "GAME_COMMAND_SET_MAZE_TRACK DEPRECATED"); -} #pragma endregion diff --git a/src/openrct2/actions/GameActionRegistration.cpp b/src/openrct2/actions/GameActionRegistration.cpp index c24a8e0a7f..2caa6cdff5 100644 --- a/src/openrct2/actions/GameActionRegistration.cpp +++ b/src/openrct2/actions/GameActionRegistration.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -7,26 +7,47 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ +#include "BalloonPressAction.hpp" +#include "BannerPlaceAction.hpp" +#include "BannerRemoveAction.hpp" +#include "BannerSetColourAction.hpp" #include "BannerSetNameAction.hpp" +#include "BannerSetStyleAction.hpp" #include "ClearAction.hpp" #include "ClimateSetAction.hpp" #include "FootpathPlaceAction.hpp" +#include "FootpathPlaceFromTrackAction.hpp" #include "FootpathRemoveAction.hpp" #include "FootpathSceneryPlaceAction.hpp" #include "FootpathSceneryRemoveAction.hpp" #include "GameAction.h" +#include "GuestSetFlagsAction.hpp" #include "GuestSetNameAction.hpp" +#include "LandBuyRightsAction.hpp" +#include "LandLowerAction.hpp" +#include "LandRaiseAction.hpp" #include "LandSetHeightAction.hpp" +#include "LandSetRightsAction.hpp" +#include "LandSmoothAction.hpp" +#include "LargeSceneryPlaceAction.hpp" #include "LargeSceneryRemoveAction.hpp" +#include "LargeScenerySetColourAction.hpp" #include "LoadOrQuitAction.hpp" #include "MazeSetTrackAction.hpp" +#include "NetworkModifyGroupAction.hpp" +#include "ParkEntranceRemoveAction.hpp" #include "ParkMarketingAction.hpp" +#include "ParkSetDateAction.hpp" #include "ParkSetLoanAction.hpp" #include "ParkSetNameAction.hpp" +#include "ParkSetParameterAction.hpp" #include "ParkSetResearchFundingAction.hpp" #include "PauseToggleAction.hpp" +#include "PeepPickupAction.hpp" #include "PlaceParkEntranceAction.hpp" #include "PlacePeepSpawnAction.hpp" +#include "PlayerKickAction.hpp" +#include "PlayerSetGroupAction.hpp" #include "RideCreateAction.hpp" #include "RideDemolishAction.hpp" #include "RideEntranceExitPlaceAction.hpp" @@ -38,38 +59,63 @@ #include "RideSetSetting.hpp" #include "RideSetStatus.hpp" #include "RideSetVehiclesAction.hpp" +#include "ScenarioSetSettingAction.hpp" +#include "SetCheatAction.hpp" #include "SetParkEntranceFeeAction.hpp" #include "SignSetNameAction.hpp" #include "SignSetStyleAction.hpp" #include "SmallSceneryPlaceAction.hpp" #include "SmallSceneryRemoveAction.hpp" +#include "SmallScenerySetColourAction.hpp" +#include "StaffFireAction.hpp" +#include "StaffHireNewAction.hpp" #include "StaffSetColourAction.hpp" #include "StaffSetCostumeAction.hpp" #include "StaffSetNameAction.hpp" #include "StaffSetOrdersAction.hpp" +#include "StaffSetPatrolAreaAction.hpp" +#include "SurfaceSetStyleAction.hpp" +#include "TileModifyAction.hpp" #include "TrackPlaceAction.hpp" #include "TrackRemoveAction.hpp" +#include "TrackSetBrakeSpeedAction.hpp" +#include "WallPlaceAction.hpp" #include "WallRemoveAction.hpp" +#include "WallSetColourAction.hpp" +#include "WaterLowerAction.hpp" +#include "WaterRaiseAction.hpp" #include "WaterSetHeightAction.hpp" namespace GameActions { void Register() { + Register(); + Register(); + Register(); + Register(); Register(); + Register(); Register(); Register(); + Register(); Register(); Register(); Register(); Register(); Register(); + Register(); Register(); + Register(); Register(); Register(); + Register(); Register(); + Register(); Register(); Register(); + Register(); + Register(); Register(); Register(); Register(); @@ -81,23 +127,45 @@ namespace GameActions Register(); Register(); Register(); + Register(); Register(); Register(); Register(); + Register(); + Register(); Register(); Register(); Register(); Register(); + Register(); + Register(); + Register(); Register(); + Register(); Register(); Register(); + Register(); + Register(); Register(); + Register(); + Register(); + Register(); + Register(); Register(); + Register(); + Register(); + Register(); Register(); Register(); + Register(); Register(); Register(); Register(); Register(); + Register(); + Register(); + Register(); + Register(); + Register(); } } // namespace GameActions diff --git a/src/openrct2/actions/GuestSetFlagsAction.hpp b/src/openrct2/actions/GuestSetFlagsAction.hpp new file mode 100644 index 0000000000..56a1b86fb9 --- /dev/null +++ b/src/openrct2/actions/GuestSetFlagsAction.hpp @@ -0,0 +1,70 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Context.h" +#include "../OpenRCT2.h" +#include "../world/Sprite.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(GuestSetFlagsAction, GAME_COMMAND_GUEST_SET_FLAGS, GameActionResult) +{ +private: + uint16_t _peepId = SPRITE_INDEX_NULL; + uint32_t _newFlags = 0; + +public: + GuestSetFlagsAction() + { + } + + GuestSetFlagsAction(uint16_t peepId, uint32_t flags) + : _peepId(peepId) + , _newFlags(flags) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_peepId) << DS_TAG(_newFlags); + } + + GameActionResult::Ptr Query() const override + { + Peep* peep = GET_PEEP(_peepId); + if (peep == nullptr) + { + log_error("Used invalid sprite index for peep: %u", (uint32_t)_peepId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_CHANGE_THIS); + } + return std::make_unique(); + } + + GameActionResult::Ptr Execute() const override + { + Peep* peep = GET_PEEP(_peepId); + if (peep == nullptr) + { + log_error("Used invalid sprite index for peep: %u", (uint32_t)_peepId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_CHANGE_THIS); + } + + peep->peep_flags = _newFlags; + + return std::make_unique(); + } +}; diff --git a/src/openrct2/actions/GuestSetNameAction.hpp b/src/openrct2/actions/GuestSetNameAction.hpp index 09dcbbbdec..acf8d9c7b4 100644 --- a/src/openrct2/actions/GuestSetNameAction.hpp +++ b/src/openrct2/actions/GuestSetNameAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -56,64 +56,45 @@ public: return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_NAME_GUEST, STR_NONE); } - if (_name.empty()) - { - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_CANT_NAME_GUEST, STR_ERR_INVALID_NAME_FOR_GUEST); - } - - Peep* peep = GET_PEEP(_spriteIndex); + auto peep = GET_PEEP(_spriteIndex); if (peep->type != PEEP_TYPE_GUEST) { log_warning("Invalid game command for sprite %u", _spriteIndex); return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_NAME_GUEST, STR_NONE); } - rct_string_id newUserStringId = user_string_allocate( - USER_STRING_HIGH_ID_NUMBER | USER_STRING_DUPLICATION_PERMITTED, _name.c_str()); - if (newUserStringId == 0) - { - // TODO: Probably exhausted, introduce new error. - return std::make_unique(GA_ERROR::UNKNOWN, STR_CANT_NAME_GUEST, gGameCommandErrorText); - } - user_string_free(newUserStringId); - return std::make_unique(); } GameActionResult::Ptr Execute() const override { - rct_string_id newUserStringId = user_string_allocate( - USER_STRING_HIGH_ID_NUMBER | USER_STRING_DUPLICATION_PERMITTED, _name.c_str()); - if (newUserStringId == 0) - { - // TODO: Probably exhausted, introduce new error. - return std::make_unique(GA_ERROR::UNKNOWN, STR_CANT_NAME_GUEST, gGameCommandErrorText); - } - - Peep* peep = GET_PEEP(_spriteIndex); + auto peep = GET_PEEP(_spriteIndex); if (peep->type != PEEP_TYPE_GUEST) { log_warning("Invalid game command for sprite %u", _spriteIndex); return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_NAME_GUEST, STR_NONE); } - set_format_arg(0, uint32_t, peep->id); - utf8* curName = gCommonStringFormatBuffer; - rct_string_id curId = peep->name_string_idx; - format_string(curName, 256, curId, gCommonFormatArgs); - - if (strcmp(curName, _name.c_str()) == 0) + auto curName = peep->GetName(); + if (curName == _name) { return std::make_unique(GA_ERROR::OK, STR_NONE); } - user_string_free(peep->name_string_idx); - peep->name_string_idx = newUserStringId; + if (!peep->SetName(_name)) + { + return std::make_unique(GA_ERROR::UNKNOWN, STR_CANT_NAME_GUEST, STR_NONE); + } peep_update_name_sort(peep); - peep_handle_easteregg_name(peep); + // Easter egg functions are for guests only + Guest* guest = peep->AsGuest(); + + if (guest != nullptr) + { + guest->HandleEasterEggName(); + } gfx_invalidate_screen(); diff --git a/src/openrct2/actions/LandBuyRightsAction.hpp b/src/openrct2/actions/LandBuyRightsAction.hpp new file mode 100644 index 0000000000..42c88d6c3a --- /dev/null +++ b/src/openrct2/actions/LandBuyRightsAction.hpp @@ -0,0 +1,183 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Context.h" +#include "../OpenRCT2.h" +#include "../actions/LandSetHeightAction.hpp" +#include "../audio/audio.h" +#include "../interface/Window.h" +#include "../localisation/Localisation.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../ride/RideData.h" +#include "../windows/Intent.h" +#include "../world/Park.h" +#include "../world/Scenery.h" +#include "../world/Sprite.h" +#include "../world/Surface.h" +#include "GameAction.h" + +enum class LandBuyRightSetting : uint8_t +{ + BuyLand, + BuyConstructionRights, + Count +}; + +DEFINE_GAME_ACTION(LandBuyRightsAction, GAME_COMMAND_BUY_LAND_RIGHTS, GameActionResult) +{ +private: + MapRange _range; + uint8_t _setting = static_cast(LandBuyRightSetting::Count); + + constexpr static rct_string_id _ErrorTitles[] = { STR_CANT_BUY_LAND, STR_CANT_BUY_CONSTRUCTION_RIGHTS_HERE }; + +public: + LandBuyRightsAction() = default; + + LandBuyRightsAction(MapRange range, LandBuyRightSetting setting) + : _range(range) + , _setting(static_cast(setting)) + { + } + + LandBuyRightsAction(CoordsXY coord, LandBuyRightSetting setting) + : _range(coord.x, coord.y, coord.x, coord.y) + , _setting(static_cast(setting)) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_range) << DS_TAG(_setting); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + + MapRange normRange = _range.Normalise(); + // Keep big coordinates within map boundaries + auto aX = std::max(32, normRange.GetLeft()); + auto bX = std::min(gMapSizeMaxXY, normRange.GetRight()); + auto aY = std::max(32, normRange.GetTop()); + auto bY = std::min(gMapSizeMaxXY, normRange.GetBottom()); + + MapRange validRange = MapRange{ aX, aY, bX, bY }; + + CoordsXYZ centre{ (validRange.GetLeft() + validRange.GetRight()) / 2 + 16, + (validRange.GetTop() + validRange.GetBottom()) / 2 + 16, 0 }; + centre.z = tile_element_height(centre); + + res->Position = centre; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; + + // Game command modified to accept selection size + for (auto y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + for (auto x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + auto result = map_buy_land_rights_for_tile({ x, y }, isExecuting); + if (result->Error == GA_ERROR::OK) + { + res->Cost += result->Cost; + } + } + } + if (isExecuting) + { + map_count_remaining_land_rights(); + } + return res; + } + + GameActionResult::Ptr map_buy_land_rights_for_tile(const CoordsXY loc, bool isExecuting) const + { + if (_setting >= static_cast(LandBuyRightSetting::Count)) + { + log_warning("Tried calling buy land rights with an incorrect setting. setting = %u", _setting); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, _ErrorTitles[0], STR_NONE); + } + + SurfaceElement* surfaceElement = map_get_surface_element_at(loc); + if (surfaceElement == nullptr) + { + log_error("Could not find surface. x = %d, y = %d", loc.x, loc.y); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, _ErrorTitles[_setting], STR_NONE); + } + + auto res = MakeResult(); + switch (static_cast(_setting)) + { + case LandBuyRightSetting::BuyLand: // 0 + if ((surfaceElement->GetOwnership() & OWNERSHIP_OWNED) != 0) + { // If the land is already owned + return res; + } + + if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0 + || (surfaceElement->GetOwnership() & OWNERSHIP_AVAILABLE) == 0) + { + return MakeResult(GA_ERROR::NOT_OWNED, _ErrorTitles[_setting], STR_LAND_NOT_FOR_SALE); + } + if (isExecuting) + { + surfaceElement->SetOwnership(OWNERSHIP_OWNED); + update_park_fences_around_tile(loc); + } + res->Cost = gLandPrice; + return res; + + case LandBuyRightSetting::BuyConstructionRights: // 2 + if ((surfaceElement->GetOwnership() & (OWNERSHIP_OWNED | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED)) != 0) + { // If the land or construction rights are already owned + return res; + } + + if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0 + || (surfaceElement->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) == 0) + { + return MakeResult(GA_ERROR::NOT_OWNED, _ErrorTitles[_setting], STR_CONSTRUCTION_RIGHTS_NOT_FOR_SALE); + } + + if (isExecuting) + { + surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED); + uint16_t baseHeight = surfaceElement->base_height * 8; + map_invalidate_tile(loc.x, loc.y, baseHeight, baseHeight + 16); + } + res->Cost = gConstructionRightsPrice; + return res; + + default: + log_warning("Tried calling buy land rights with an incorrect setting. setting = %u", _setting); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, _ErrorTitles[0], STR_NONE); + } + } +}; diff --git a/src/openrct2/actions/LandLowerAction.hpp b/src/openrct2/actions/LandLowerAction.hpp new file mode 100644 index 0000000000..c0bcd975e6 --- /dev/null +++ b/src/openrct2/actions/LandLowerAction.hpp @@ -0,0 +1,142 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Context.h" +#include "../OpenRCT2.h" +#include "../actions/LandSetHeightAction.hpp" +#include "../audio/audio.h" +#include "../interface/Window.h" +#include "../localisation/Localisation.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../ride/RideData.h" +#include "../windows/Intent.h" +#include "../world/Park.h" +#include "../world/Scenery.h" +#include "../world/Sprite.h" +#include "../world/Surface.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(LandLowerAction, GAME_COMMAND_LOWER_LAND, GameActionResult) +{ +private: + CoordsXY _coords; + MapRange _range; + uint8_t _selectionType; + +public: + LandLowerAction() + { + } + LandLowerAction(CoordsXY coords, MapRange range, uint8_t selectionType) + : _coords(coords) + , _range(range) + , _selectionType(selectionType) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_coords) << DS_TAG(_range) << DS_TAG(_selectionType); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + size_t tableRow = _selectionType; + + // The selections between MAP_SELECT_TYPE_FULL and MAP_SELECT_TYPE_EDGE_0 are not included in the tables + if (_selectionType >= MAP_SELECT_TYPE_EDGE_0 && _selectionType <= MAP_SELECT_TYPE_EDGE_3) + tableRow -= MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1; + + // Keep big coordinates within map boundaries + auto aX = std::max(32, _range.GetLeft()); + auto bX = std::min(gMapSizeMaxXY, _range.GetRight()); + auto aY = std::max(32, _range.GetTop()); + auto bY = std::min(gMapSizeMaxXY, _range.GetBottom()); + + MapRange validRange = MapRange{ aX, aY, bX, bY }; + + res->Position = { _coords.x, _coords.y, tile_element_height(_coords) }; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + if (isExecuting) + { + audio_play_sound_at_location(SoundId::PlaceItem, { _coords.x, _coords.y, tile_element_height(_coords) }); + } + + uint8_t maxHeight = map_get_highest_land_height(validRange); + + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + auto* surfaceElement = map_get_surface_element_at(x / 32, y / 32); + if (surfaceElement == nullptr) + continue; + + uint8_t height = surfaceElement->base_height; + if (surfaceElement->GetSlope() & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK) + height += 2; + if (surfaceElement->GetSlope() & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG) + height += 2; + + if (height < maxHeight) + continue; + + height = surfaceElement->base_height; + uint8_t currentSlope = surfaceElement->GetSlope(); + uint8_t newSlope = tile_element_lower_styles[tableRow][currentSlope]; + if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + height -= 2; + + newSlope &= TILE_ELEMENT_SURFACE_SLOPE_MASK; + + auto landSetHeightAction = LandSetHeightAction({ x, y }, height, newSlope); + landSetHeightAction.SetFlags(GetFlags()); + auto result = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction) + : GameActions::QueryNested(&landSetHeightAction); + if (result->Error == GA_ERROR::OK) + { + res->Cost += result->Cost; + } + else + { + result->ErrorTitle = STR_CANT_LOWER_LAND_HERE; + return result; + } + } + } + + // Force ride construction to recheck area + _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; + + return res; + } +}; diff --git a/src/openrct2/actions/LandRaiseAction.hpp b/src/openrct2/actions/LandRaiseAction.hpp new file mode 100644 index 0000000000..c5688ebdb9 --- /dev/null +++ b/src/openrct2/actions/LandRaiseAction.hpp @@ -0,0 +1,137 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Context.h" +#include "../OpenRCT2.h" +#include "../actions/LandSetHeightAction.hpp" +#include "../audio/audio.h" +#include "../interface/Window.h" +#include "../localisation/Localisation.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../ride/RideData.h" +#include "../windows/Intent.h" +#include "../world/Park.h" +#include "../world/Scenery.h" +#include "../world/Sprite.h" +#include "../world/Surface.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(LandRaiseAction, GAME_COMMAND_RAISE_LAND, GameActionResult) +{ +private: + CoordsXY _coords; + MapRange _range; + uint8_t _selectionType; + +public: + LandRaiseAction() + { + } + LandRaiseAction(CoordsXY coords, MapRange range, uint8_t selectionType) + : _coords(coords) + , _range(range) + , _selectionType(selectionType) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_coords) << DS_TAG(_range) << DS_TAG(_selectionType); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + size_t tableRow = _selectionType; + + // The selections between MAP_SELECT_TYPE_FULL and MAP_SELECT_TYPE_EDGE_0 are not included in the tables + if (_selectionType >= MAP_SELECT_TYPE_EDGE_0 && _selectionType <= MAP_SELECT_TYPE_EDGE_3) + tableRow -= MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1; + + // Keep big coordinates within map boundaries + auto aX = std::max(32, _range.GetLeft()); + auto bX = std::min(gMapSizeMaxXY, _range.GetRight()); + auto aY = std::max(32, _range.GetTop()); + auto bY = std::min(gMapSizeMaxXY, _range.GetBottom()); + + MapRange validRange = MapRange{ aX, aY, bX, bY }; + + res->Position = { _coords.x, _coords.y, tile_element_height(_coords) }; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + if (isExecuting) + { + audio_play_sound_at_location(SoundId::PlaceItem, { _coords.x, _coords.y, tile_element_height(_coords) }); + } + + uint8_t minHeight = map_get_lowest_land_height(validRange); + + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + auto* surfaceElement = map_get_surface_element_at(x / 32, y / 32); + if (surfaceElement == nullptr) + continue; + uint8_t height = surfaceElement->base_height; + + if (height > minHeight) + continue; + + uint8_t currentSlope = surfaceElement->GetSlope(); + uint8_t newSlope = tile_element_raise_styles[tableRow][currentSlope]; + if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + height += 2; + + newSlope &= TILE_ELEMENT_SURFACE_SLOPE_MASK; + + auto landSetHeightAction = LandSetHeightAction({ x, y }, height, newSlope); + landSetHeightAction.SetFlags(GetFlags()); + auto result = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction) + : GameActions::QueryNested(&landSetHeightAction); + if (result->Error == GA_ERROR::OK) + { + res->Cost += result->Cost; + } + else + { + result->ErrorTitle = STR_CANT_RAISE_LAND_HERE; + return result; + } + } + } + + // Force ride construction to recheck area + _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; + + return res; + } +}; diff --git a/src/openrct2/actions/LandSetHeightAction.hpp b/src/openrct2/actions/LandSetHeightAction.hpp index 0e505602e8..856190016d 100644 --- a/src/openrct2/actions/LandSetHeightAction.hpp +++ b/src/openrct2/actions/LandSetHeightAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -19,6 +19,7 @@ #include "../windows/Intent.h" #include "../world/Park.h" #include "../world/Scenery.h" +#include "../world/SmallScenery.h" #include "../world/Sprite.h" #include "../world/Surface.h" #include "GameAction.h" @@ -80,7 +81,7 @@ public: if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) { // Check for obstructing large trees - TileElement* tileElement = CheckTallTreeObstructions(); + TileElement* tileElement = CheckTreeObstructions(); if (tileElement != nullptr) { map_obstruction_set_error_text(tileElement); @@ -100,8 +101,8 @@ public: } } - TileElement* surfaceElement = map_get_surface_element_at(_coords); - TileElement* tileElement = CheckFloatingStructures(surfaceElement, _height); + auto* surfaceElement = map_get_surface_element_at(_coords); + TileElement* tileElement = CheckFloatingStructures(reinterpret_cast(surfaceElement), _height); if (tileElement != nullptr) { map_obstruction_set_error_text(tileElement); @@ -127,7 +128,7 @@ public: GA_ERROR::DISALLOWED, STR_NONE, gGameCommandErrorText, gCommonFormatArgs); } - tileElement = CheckUnremovableObstructions(surfaceElement, zCorner); + tileElement = CheckUnremovableObstructions(reinterpret_cast(surfaceElement), zCorner); if (tileElement != nullptr) { map_obstruction_set_error_text(tileElement); @@ -143,7 +144,7 @@ public: GameActionResult::Ptr Execute() const override { money32 cost = MONEY(0, 0); - auto surfaceHeight = tile_element_height(_coords.x, _coords.y) & 0xFFFF; + auto surfaceHeight = tile_element_height(_coords); footpath_remove_litter(_coords.x, _coords.y, surfaceHeight); if (!gCheatsDisableClearanceChecks) @@ -153,9 +154,9 @@ public: SmallSceneryRemoval(); } - TileElement* surfaceElement = map_get_surface_element_at(_coords); + auto* surfaceElement = map_get_surface_element_at(_coords); cost += GetSurfaceHeightChangeCost(surfaceElement); - SetSurfaceHeight(surfaceElement); + SetSurfaceHeight(reinterpret_cast(surfaceElement)); auto res = std::make_unique(); res->Position = { _coords.x + 16, _coords.y + 16, surfaceHeight }; @@ -195,11 +196,13 @@ private: return STR_NONE; } - TileElement* CheckTallTreeObstructions() const + TileElement* CheckTreeObstructions() const { TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32); do { + if (tileElement == nullptr) + break; if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) continue; if (_height > tileElement->clearance_height) @@ -207,7 +210,7 @@ private: if (_height + 4 < tileElement->base_height) continue; rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); - if (sceneryEntry->small_scenery.height > 64) + if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_IS_TREE)) { return tileElement; } @@ -221,6 +224,8 @@ private: TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32); do { + if (tileElement == nullptr) + break; if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) continue; if (_height > tileElement->clearance_height) @@ -238,6 +243,8 @@ private: TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32); do { + if (tileElement == nullptr) + break; if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) continue; if (_height > tileElement->clearance_height) @@ -253,19 +260,21 @@ private: TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32); do { + if (tileElement == nullptr) + break; if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK) { ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); if (ride != nullptr) { - rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride); + rct_ride_entry* rideEntry = ride->GetRideEntry(); if (rideEntry != nullptr) { int32_t maxHeight = rideEntry->max_height; if (maxHeight == 0) { - maxHeight = RideData5[get_ride(rideIndex)->type].max_height; + maxHeight = RideData5[ride->type].max_height; } int32_t zDelta = tileElement->clearance_height - _height; if (zDelta >= 0 && zDelta / 2 > maxHeight) @@ -308,6 +317,8 @@ private: TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32); do { + if (tileElement == nullptr) + break; int32_t elementType = tileElement->GetType(); // Wall's and Small Scenery are removed and therefore do not need checked @@ -335,10 +346,10 @@ private: return nullptr; } - money32 GetSurfaceHeightChangeCost(TileElement * surfaceElement) const + money32 GetSurfaceHeightChangeCost(SurfaceElement * surfaceElement) const { money32 cost{ 0 }; - for (int32_t i = 0; i < 4; i += 1) + for (Direction i : ALL_DIRECTIONS) { int32_t cornerHeight = tile_element_get_corner_height(surfaceElement, i); cornerHeight -= map_get_corner_height(_height, _style & TILE_ELEMENT_SURFACE_SLOPE_MASK, i); diff --git a/src/openrct2/actions/LandSetRightsAction.hpp b/src/openrct2/actions/LandSetRightsAction.hpp new file mode 100644 index 0000000000..8ff958e642 --- /dev/null +++ b/src/openrct2/actions/LandSetRightsAction.hpp @@ -0,0 +1,233 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Context.h" +#include "../OpenRCT2.h" +#include "../actions/LandSetHeightAction.hpp" +#include "../audio/audio.h" +#include "../interface/Window.h" +#include "../localisation/Localisation.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../ride/RideData.h" +#include "../windows/Intent.h" +#include "../world/Park.h" +#include "../world/Scenery.h" +#include "../world/Sprite.h" +#include "../world/Surface.h" +#include "GameAction.h" + +enum class LandSetRightSetting : uint8_t +{ + UnownLand, + UnownConstructionRights, + SetForSale, + SetConstructionRightsForSale, + SetOwnershipWithChecks, + Count +}; + +DEFINE_GAME_ACTION(LandSetRightsAction, GAME_COMMAND_SET_LAND_OWNERSHIP, GameActionResult) +{ +private: + MapRange _range; + uint8_t _setting = static_cast(LandSetRightSetting::Count); + uint8_t _ownership = 0; + +public: + LandSetRightsAction() = default; + + LandSetRightsAction(MapRange range, LandSetRightSetting setting, uint8_t ownership = 0) + : _range(range) + , _setting(static_cast(setting)) + , _ownership(ownership) + { + } + + LandSetRightsAction(CoordsXY coord, LandSetRightSetting setting, uint8_t ownership = 0) + : _range(coord.x, coord.y, coord.x, coord.y) + , _setting(static_cast(setting)) + , _ownership(ownership) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::EDITOR_ONLY; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_range) << DS_TAG(_setting) << DS_TAG(_ownership); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + + MapRange normRange = _range.Normalise(); + // Keep big coordinates within map boundaries + auto aX = std::max(32, normRange.GetLeft()); + auto bX = std::min(gMapSizeMaxXY, normRange.GetRight()); + auto aY = std::max(32, normRange.GetTop()); + auto bY = std::min(gMapSizeMaxXY, normRange.GetBottom()); + + MapRange validRange = MapRange{ aX, aY, bX, bY }; + + CoordsXYZ centre{ (validRange.GetLeft() + validRange.GetRight()) / 2 + 16, + (validRange.GetTop() + validRange.GetBottom()) / 2 + 16, 0 }; + centre.z = tile_element_height(centre); + + res->Position = centre; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; + + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode) + { + return MakeResult(GA_ERROR::NOT_IN_EDITOR_MODE, STR_NONE, STR_LAND_NOT_FOR_SALE); + } + + // Game command modified to accept selection size + for (auto y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + for (auto x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + auto result = map_buy_land_rights_for_tile({ x, y }, isExecuting); + if (result->Error == GA_ERROR::OK) + { + res->Cost += result->Cost; + } + } + } + + if (isExecuting) + { + map_count_remaining_land_rights(); + audio_play_sound_at_location(SoundId::PlaceItem, centre); + } + return res; + } + + GameActionResult::Ptr map_buy_land_rights_for_tile(const CoordsXY loc, bool isExecuting) const + { + SurfaceElement* surfaceElement = map_get_surface_element_at(loc); + if (surfaceElement == nullptr) + { + log_error("Could not find surface. x = %d, y = %d", loc.x, loc.y); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE, STR_NONE); + } + + auto res = MakeResult(); + switch (static_cast(_setting)) + { + case LandSetRightSetting::UnownLand: + if (isExecuting) + { + surfaceElement->SetOwnership( + surfaceElement->GetOwnership() & ~(OWNERSHIP_OWNED | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED)); + update_park_fences_around_tile(loc); + } + return res; + case LandSetRightSetting::UnownConstructionRights: + if (isExecuting) + { + surfaceElement->SetOwnership(surfaceElement->GetOwnership() & ~OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED); + uint16_t baseHeight = surfaceElement->base_height * 8; + map_invalidate_tile(loc.x, loc.y, baseHeight, baseHeight + 16); + } + return res; + case LandSetRightSetting::SetForSale: + if (isExecuting) + { + surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_AVAILABLE); + uint16_t baseHeight = surfaceElement->base_height * 8; + map_invalidate_tile(loc.x, loc.y, baseHeight, baseHeight + 16); + } + return res; + case LandSetRightSetting::SetConstructionRightsForSale: + if (isExecuting) + { + surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE); + uint16_t baseHeight = surfaceElement->base_height * 8; + map_invalidate_tile(loc.x, loc.y, baseHeight, baseHeight + 16); + } + return res; + case LandSetRightSetting::SetOwnershipWithChecks: + { + if (_ownership == surfaceElement->GetOwnership()) + { + return res; + } + + TileElement* tileElement = map_get_first_element_at(loc.x / 32, loc.y / 32); + do + { + if (tileElement == nullptr) + break; + + if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) + continue; + + if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE) + continue; + + // Do not allow ownership of park entrance. + if (_ownership == OWNERSHIP_OWNED || _ownership == OWNERSHIP_AVAILABLE) + return res; + + // Allow construction rights available / for sale on park entrances on surface. + // There is no need to check the height if _ownership is 0 (unowned and no rights available). + if (_ownership == OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED + || _ownership == OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) + { + if (tileElement->base_height - 3 > surfaceElement->base_height + || tileElement->base_height < surfaceElement->base_height) + return res; + } + } while (!(tileElement++)->IsLastForTile()); + + res->Cost = gLandPrice; + if (isExecuting) + { + if (_ownership != OWNERSHIP_UNOWNED) + { + gPeepSpawns.erase( + std::remove_if( + gPeepSpawns.begin(), gPeepSpawns.end(), + [x = loc.x, y = loc.y](const auto& spawn) { + return floor2(spawn.x, 32) == x && floor2(spawn.y, 32) == y; + }), + gPeepSpawns.end()); + } + surfaceElement->SetOwnership(_ownership); + update_park_fences_around_tile(loc); + gMapLandRightsUpdateSuccess = true; + } + return res; + } + default: + log_warning("Tried calling set land rights with an incorrect setting. setting = %u", _setting); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE, STR_NONE); + } + } +}; diff --git a/src/openrct2/actions/LandSmoothAction.hpp b/src/openrct2/actions/LandSmoothAction.hpp new file mode 100644 index 0000000000..7236e17ff8 --- /dev/null +++ b/src/openrct2/actions/LandSmoothAction.hpp @@ -0,0 +1,634 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Context.h" +#include "../OpenRCT2.h" +#include "../actions/LandLowerAction.hpp" +#include "../actions/LandRaiseAction.hpp" +#include "../actions/LandSetHeightAction.hpp" +#include "../audio/audio.h" +#include "../interface/Window.h" +#include "../localisation/Localisation.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../ride/RideData.h" +#include "../windows/Intent.h" +#include "../world/Park.h" +#include "../world/Scenery.h" +#include "../world/Sprite.h" +#include "../world/Surface.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(LandSmoothAction, GAME_COMMAND_EDIT_LAND_SMOOTH, GameActionResult) +{ +private: + CoordsXY _coords; + MapRange _range; + uint8_t _selectionType{ 0 }; + bool _isLowering{ false }; + + constexpr static rct_string_id _ErrorTitles[] = { STR_CANT_LOWER_LAND_HERE, STR_CANT_RAISE_LAND_HERE }; + +public: + LandSmoothAction() + { + } + LandSmoothAction(CoordsXY coords, MapRange range, uint8_t selectionType, bool isLowering) + : _coords(coords) + , _range(range) + , _selectionType(selectionType) + , _isLowering(isLowering) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_coords) << DS_TAG(_range) << DS_TAG(_selectionType) << DS_TAG(_isLowering); + } + + GameActionResult::Ptr Query() const override + { + return SmoothLand(false); + } + + GameActionResult::Ptr Execute() const override + { + return SmoothLand(true); + } + +private: + GameActionResult::Ptr SmoothLandTile( + int32_t direction, bool isExecuting, int32_t x, int32_t y, SurfaceElement* surfaceElement) const + { + int32_t targetBaseZ = surfaceElement->base_height; + int32_t slope = surfaceElement->GetSlope(); + if (_isLowering) + { + slope = tile_element_lower_styles[direction][slope]; + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + { + targetBaseZ -= 2; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + } + } + else + { + slope = tile_element_raise_styles[direction][slope]; + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + { + targetBaseZ += 2; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + } + } + + auto landSetHeightAction = LandSetHeightAction({ x, y }, targetBaseZ, slope); + landSetHeightAction.SetFlags(GetFlags()); + auto res = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction) + : GameActions::QueryNested(&landSetHeightAction); + + return res; + } + + money32 SmoothLandRowByEdge( + bool isExecuting, CoordsXY loc, int32_t expectedLandHeight1, int32_t expectedLandHeight2, int32_t stepX, int32_t stepY, + int32_t direction1, int32_t direction2, int32_t checkDirection1, int32_t checkDirection2) const + { + uint8_t shouldContinue = 0xF; + int32_t landChangePerTile = _isLowering ? 2 : -2; + money32 totalCost = 0; + + // check if we need to start at all + if (!map_is_location_valid(loc) || !map_is_location_valid({ loc.x + stepX, loc.y + stepY })) + { + return 0; + } + auto surfaceElement = map_get_surface_element_at(loc); + auto nextSurfaceElement = map_get_surface_element_at({ loc.x + stepX, loc.y + stepY }); + if (surfaceElement == nullptr || nextSurfaceElement == nullptr) + { + return 0; + } + if (tile_element_get_corner_height(surfaceElement, checkDirection1) != expectedLandHeight1 + landChangePerTile) + { + shouldContinue &= ~0x1; + } + if (tile_element_get_corner_height(surfaceElement, checkDirection2) != expectedLandHeight2 + landChangePerTile) + { + shouldContinue &= ~0x2; + } + if (tile_element_get_corner_height(surfaceElement, checkDirection1) + != tile_element_get_corner_height(nextSurfaceElement, direction1)) + { + shouldContinue &= ~0x1; + } + if (tile_element_get_corner_height(surfaceElement, checkDirection2) + != tile_element_get_corner_height(nextSurfaceElement, direction2)) + { + shouldContinue &= ~0x2; + } + auto nextLoc = loc; + while ((shouldContinue & 0x3) != 0) + { + shouldContinue = ((shouldContinue << 2) | 0x3) & shouldContinue; + nextLoc.x += stepX; + nextLoc.y += stepY; + // check if we need to continue after raising the current tile + // this needs to be checked before the tile is changed + if (!map_is_location_valid({ nextLoc.x + stepX, nextLoc.y + stepY })) + { + shouldContinue &= ~0x3; + } + else + { + surfaceElement = nextSurfaceElement; + nextSurfaceElement = map_get_surface_element_at({ nextLoc.x + stepX, nextLoc.y + stepY }); + if (nextSurfaceElement == nullptr) + { + shouldContinue &= ~0x3; + } + if (tile_element_get_corner_height(surfaceElement, direction1) + landChangePerTile + != tile_element_get_corner_height(surfaceElement, checkDirection1)) + { + shouldContinue &= ~0x1; + } + if (tile_element_get_corner_height(surfaceElement, direction2) + landChangePerTile + != tile_element_get_corner_height(surfaceElement, checkDirection2)) + { + shouldContinue &= ~0x2; + } + if ((shouldContinue & 0x1) + && tile_element_get_corner_height(surfaceElement, checkDirection1) + != tile_element_get_corner_height(nextSurfaceElement, direction1)) + { + shouldContinue &= ~0x1; + } + if ((shouldContinue & 0x2) + && tile_element_get_corner_height(surfaceElement, checkDirection2) + != tile_element_get_corner_height(nextSurfaceElement, direction2)) + { + shouldContinue &= ~0x2; + } + } + expectedLandHeight1 += landChangePerTile; + + // change land of current tile + int32_t targetBaseZ = surfaceElement->base_height; + int32_t slope = surfaceElement->GetSlope(); + int32_t oldSlope = slope; + if (_isLowering) + { + if (shouldContinue & 0x4) + { + slope = tile_element_lower_styles[direction1][slope]; + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + { + targetBaseZ -= 2; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + } + } + if ((shouldContinue & 0x8) + && map_get_corner_height(surfaceElement->base_height, oldSlope, direction2) + == map_get_corner_height(targetBaseZ, slope, direction2)) + { + slope = tile_element_lower_styles[direction2][slope]; + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + { + targetBaseZ -= 2; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + } + } + } + else + { + if (shouldContinue & 0x4) + { + slope = tile_element_raise_styles[direction1][slope]; + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + { + targetBaseZ += 2; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + } + } + if ((shouldContinue & 0x8) + && map_get_corner_height(surfaceElement->base_height, oldSlope, direction2) + == map_get_corner_height(targetBaseZ, slope, direction2)) + { + slope = tile_element_raise_styles[direction2][slope]; + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + { + targetBaseZ += 2; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + } + } + } + auto landSetHeightAction = LandSetHeightAction(nextLoc, targetBaseZ, slope); + landSetHeightAction.SetFlags(GetFlags()); + auto res = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction) + : GameActions::QueryNested(&landSetHeightAction); + if (res->Error == GA_ERROR::OK) + { + totalCost += res->Cost; + } + } + return totalCost; + } + + money32 SmoothLandRowByCorner( + bool isExecuting, CoordsXY loc, int32_t expectedLandHeight, int32_t stepX, int32_t stepY, int32_t direction, + int32_t checkDirection) const + { + bool shouldContinue = true; + money32 totalCost = 0; + int32_t landChangePerTile; + if (stepX == 0 || stepY == 0) + { + landChangePerTile = _isLowering ? 2 : -2; + } + else + { + landChangePerTile = _isLowering ? 4 : -4; + } + + // check if we need to start at all + if (!map_is_location_valid(loc) || !map_is_location_valid({ loc.x + stepX, loc.y + stepY })) + { + return 0; + } + auto surfaceElement = map_get_surface_element_at(loc); + auto nextSurfaceElement = map_get_surface_element_at({ loc.x + stepX, loc.y + stepY }); + if (surfaceElement == nullptr || nextSurfaceElement == nullptr) + { + return 0; + } + if (tile_element_get_corner_height(surfaceElement, checkDirection) != expectedLandHeight + (_isLowering ? 2 : -2)) + { + return 0; + } + if (tile_element_get_corner_height(surfaceElement, checkDirection) + != tile_element_get_corner_height(nextSurfaceElement, direction)) + { + return 0; + } + + auto nextLoc = loc; + while (shouldContinue) + { + nextLoc.x += stepX; + nextLoc.y += stepY; + // check if we need to continue after raising the current tile + // this needs to be checked before the tile is changed + if (!map_is_location_valid({ nextLoc.x + stepX, nextLoc.y + stepY })) + { + shouldContinue = false; + } + else + { + surfaceElement = nextSurfaceElement; + nextSurfaceElement = map_get_surface_element_at({ nextLoc.x + stepX, nextLoc.y + stepY }); + if (nextSurfaceElement == nullptr) + { + shouldContinue = false; + } + if (tile_element_get_corner_height(surfaceElement, direction) + landChangePerTile + != tile_element_get_corner_height(surfaceElement, checkDirection)) + { + shouldContinue = false; + } + if (shouldContinue + && tile_element_get_corner_height(surfaceElement, checkDirection) + != tile_element_get_corner_height(nextSurfaceElement, direction)) + { + shouldContinue = false; + } + } + if (stepX * stepY != 0) + { + totalCost += SmoothLandRowByCorner( + isExecuting, nextLoc, expectedLandHeight + (landChangePerTile / 2), 0, stepY, direction, + checkDirection ^ 3); + totalCost += SmoothLandRowByCorner( + isExecuting, nextLoc, expectedLandHeight + (landChangePerTile / 2), stepX, 0, direction, + checkDirection ^ 1); + } + expectedLandHeight += landChangePerTile; + // change land of current tile + auto result = SmoothLandTile(direction, isExecuting, nextLoc.x, nextLoc.y, surfaceElement); + if (result->Error == GA_ERROR::OK) + { + totalCost += result->Cost; + } + } + return totalCost; + } + + GameActionResult::Ptr SmoothLand(bool isExecuting) const + { + const bool raiseLand = !_isLowering; + const int32_t selectionType = _selectionType; + const int32_t heightOffset = raiseLand ? 2 : -2; + + auto normRange = _range.Normalise(); + // Cap bounds to map + auto l = std::max(normRange.GetLeft(), 32); + auto t = std::max(normRange.GetTop(), 32); + auto r = std::clamp(normRange.GetRight(), 0, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); + auto b = std::clamp(normRange.GetBottom(), 0, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); + auto validRange = MapRange{ l, t, r, b }; + + int32_t centreZ = tile_element_height(_coords); + + auto res = MakeResult(); + res->ErrorTitle = _ErrorTitles[_isLowering ? 0 : 1]; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position = { _coords.x, _coords.y, centreZ }; + + // Do the smoothing + switch (selectionType) + { + case MAP_SELECT_TYPE_FULL: + { + uint8_t minHeight = heightOffset + map_get_lowest_land_height(validRange); + uint8_t maxHeight = heightOffset + map_get_highest_land_height(validRange); + + // Smooth the 4 corners + { // top-left + auto surfaceElement = map_get_surface_element_at({ validRange.GetLeft(), validRange.GetTop() }); + int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 2), minHeight, maxHeight); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2); + } + { // bottom-left + auto surfaceElement = map_get_surface_element_at({ validRange.GetLeft(), validRange.GetBottom() }); + int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 3), minHeight, maxHeight); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetBottom() }, z, -32, 32, 1, 3); + } + { // bottom-right + auto surfaceElement = map_get_surface_element_at({ validRange.GetRight(), validRange.GetBottom() }); + int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 0), minHeight, maxHeight); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetRight(), validRange.GetBottom() }, z, 32, 32, 2, 0); + } + { // top-right + auto surfaceElement = map_get_surface_element_at({ validRange.GetRight(), validRange.GetTop() }); + int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 1), minHeight, maxHeight); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetRight(), validRange.GetTop() }, z, 32, -32, 3, 1); + } + + // Smooth the edges + int32_t z1, z2; + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + auto surfaceElement = map_get_surface_element_at({ validRange.GetLeft(), y }); + z1 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 3), minHeight, maxHeight); + z2 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 2), minHeight, maxHeight); + res->Cost += SmoothLandRowByEdge(isExecuting, { validRange.GetLeft(), y }, z1, z2, -32, 0, 0, 1, 3, 2); + + surfaceElement = map_get_surface_element_at({ validRange.GetRight(), y }); + z1 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 1), minHeight, maxHeight); + z2 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 0), minHeight, maxHeight); + res->Cost += SmoothLandRowByEdge(isExecuting, { validRange.GetRight(), y }, z1, z2, 32, 0, 2, 3, 1, 0); + } + + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + auto surfaceElement = map_get_surface_element_at({ x, validRange.GetTop() }); + z1 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 1), minHeight, maxHeight); + z2 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 2), minHeight, maxHeight); + res->Cost += SmoothLandRowByEdge(isExecuting, { x, validRange.GetTop() }, z1, z2, 0, -32, 0, 3, 1, 2); + + surfaceElement = map_get_surface_element_at({ x, validRange.GetBottom() }); + z1 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 0), minHeight, maxHeight); + z2 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 3), minHeight, maxHeight); + res->Cost += SmoothLandRowByEdge(isExecuting, { x, validRange.GetBottom() }, z1, z2, 0, 32, 1, 2, 0, 3); + } + break; + } + case MAP_SELECT_TYPE_CORNER_0: + case MAP_SELECT_TYPE_CORNER_1: + case MAP_SELECT_TYPE_CORNER_2: + case MAP_SELECT_TYPE_CORNER_3: + { + auto surfaceElement = map_get_surface_element_at({ validRange.GetLeft(), validRange.GetTop() }); + uint8_t newBaseZ = surfaceElement->base_height; + uint8_t newSlope = surfaceElement->GetSlope(); + + if (raiseLand) + { + newSlope = tile_element_raise_styles[selectionType][newSlope]; + } + else + { + newSlope = tile_element_lower_styles[selectionType][newSlope]; + } + + if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + { + newBaseZ += heightOffset; + newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + } + + // Smooth the corners + int32_t z = map_get_corner_height(newBaseZ, newSlope, 2); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2); + z = map_get_corner_height(newBaseZ, newSlope, 0); + res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 32, 2, 0); + z = map_get_corner_height(newBaseZ, newSlope, 3); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 32, 1, 3); + z = map_get_corner_height(newBaseZ, newSlope, 1); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, -32, 3, 1); + + // Smooth the edges + switch (selectionType) + { + case MAP_SELECT_TYPE_CORNER_0: + z = map_get_corner_height(newBaseZ, newSlope, 0); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 3, 0); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 1, 0); + z = map_get_corner_height(newBaseZ, newSlope, 3); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 0, 3); + z = map_get_corner_height(newBaseZ, newSlope, 1); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 0, 1); + break; + case MAP_SELECT_TYPE_CORNER_1: + z = map_get_corner_height(newBaseZ, newSlope, 1); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 2, 1); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 0, 1); + z = map_get_corner_height(newBaseZ, newSlope, 2); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 1, 2); + z = map_get_corner_height(newBaseZ, newSlope, 0); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 1, 0); + break; + case MAP_SELECT_TYPE_CORNER_2: + z = map_get_corner_height(newBaseZ, newSlope, 2); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 1, 2); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 3, 2); + z = map_get_corner_height(newBaseZ, newSlope, 1); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 2, 1); + z = map_get_corner_height(newBaseZ, newSlope, 3); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 2, 3); + break; + case MAP_SELECT_TYPE_CORNER_3: + z = map_get_corner_height(newBaseZ, newSlope, 3); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 0, 3); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 2, 3); + z = map_get_corner_height(newBaseZ, newSlope, 0); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 3, 0); + z = map_get_corner_height(newBaseZ, newSlope, 2); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 3, 2); + break; + } + break; + } + case MAP_SELECT_TYPE_EDGE_0: + case MAP_SELECT_TYPE_EDGE_1: + case MAP_SELECT_TYPE_EDGE_2: + case MAP_SELECT_TYPE_EDGE_3: + { + // TODO: Handle smoothing by edge + // Get the two corners to raise + auto surfaceElement = map_get_surface_element_at({ validRange.GetLeft(), validRange.GetTop() }); + uint8_t newBaseZ = surfaceElement->base_height; + uint8_t oldSlope = surfaceElement->GetSlope(); + uint8_t newSlope = oldSlope; + int32_t rowIndex = selectionType - (MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1); + + if (raiseLand) + { + newSlope = tile_element_raise_styles[rowIndex][oldSlope]; + } + else + { + newSlope = tile_element_lower_styles[rowIndex][oldSlope]; + } + + const bool changeBaseHeight = newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + if (changeBaseHeight) + { + newBaseZ += heightOffset; + newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + } + + const uint8_t edge = selectionType - MAP_SELECT_TYPE_EDGE_0; + + // Table with corners for each edge selection. The first two are the selected corners, the latter + // two are the opposites + static constexpr uint8_t cornerIndices[][4] = { + { 2, 3, 1, 0 }, // MAP_SELECT_TYPE_EDGE_0 + { 3, 0, 2, 1 }, // MAP_SELECT_TYPE_EDGE_1 + { 0, 1, 3, 2 }, // MAP_SELECT_TYPE_EDGE_2 + { 1, 2, 0, 3 }, // MAP_SELECT_TYPE_EDGE_3 + }; + // Big coordinate offsets for the neigbouring tile for the given edge selection + static constexpr sLocationXY8 stepOffsets[] = { + { -32, 0 }, + { 0, 32 }, + { 32, 0 }, + { 0, -32 }, + }; + + // Smooth higher and lower edges + uint8_t c1 = cornerIndices[edge][0]; + uint8_t c2 = cornerIndices[edge][1]; + uint8_t c3 = cornerIndices[edge][2]; + uint8_t c4 = cornerIndices[edge][3]; + uint8_t z1 = map_get_corner_height(newBaseZ, newSlope, c1); + uint8_t z2 = map_get_corner_height(newBaseZ, newSlope, c2); + uint8_t z3 = map_get_corner_height(newBaseZ, newSlope, c3); + uint8_t z4 = map_get_corner_height(newBaseZ, newSlope, c4); + // Smooth the edge at the top of the new slope + res->Cost += SmoothLandRowByEdge( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z1, z2, stepOffsets[edge].x, + stepOffsets[edge].y, c3, c4, c1, c2); + // Smooth the edge at the bottom of the new slope + res->Cost += SmoothLandRowByEdge( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z3, z4, -stepOffsets[edge].x, + -stepOffsets[edge].y, c1, c2, c3, c4); + + // Smooth corners + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z1, -stepOffsets[edge].y, stepOffsets[edge].x, + c2, c1); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z2, stepOffsets[edge].y, -stepOffsets[edge].x, + c1, c2); + int32_t z = map_get_corner_height(newBaseZ, newSlope, 2); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2); + z = map_get_corner_height(newBaseZ, newSlope, 0); + res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 32, 2, 0); + z = map_get_corner_height(newBaseZ, newSlope, 3); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 32, 1, 3); + z = map_get_corner_height(newBaseZ, newSlope, 1); + res->Cost += SmoothLandRowByCorner( + isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, -32, 3, 1); + break; + } + default: + log_error("Invalid map selection %u", _selectionType); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, res->ErrorTitle); + } // switch selectionType + + // Raise / lower the land tool selection area + GameActionResult::Ptr result; + if (raiseLand) + { + auto raiseLandAction = LandRaiseAction({ _coords.x, _coords.y }, validRange, selectionType); + raiseLandAction.SetFlags(GetFlags()); + result = isExecuting ? GameActions::ExecuteNested(&raiseLandAction) : GameActions::QueryNested(&raiseLandAction); + } + else + { + auto lowerLandAction = LandLowerAction({ _coords.x, _coords.y }, validRange, selectionType); + lowerLandAction.SetFlags(GetFlags()); + result = isExecuting ? GameActions::ExecuteNested(&lowerLandAction) : GameActions::QueryNested(&lowerLandAction); + } + if (result->Error != GA_ERROR::OK) + { + return result; + } + + if (isExecuting) + { + audio_play_sound_at_location(SoundId::PlaceItem, { _coords.x, _coords.y, centreZ }); + } + res->Cost += result->Cost; + return res; + } +}; diff --git a/src/openrct2/actions/LargeSceneryPlaceAction.hpp b/src/openrct2/actions/LargeSceneryPlaceAction.hpp new file mode 100644 index 0000000000..a768191d92 --- /dev/null +++ b/src/openrct2/actions/LargeSceneryPlaceAction.hpp @@ -0,0 +1,414 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../OpenRCT2.h" +#include "../management/Finance.h" +#include "../object/ObjectLimits.h" +#include "../ride/Ride.h" +#include "../world/Banner.h" +#include "../world/LargeScenery.h" +#include "../world/MapAnimation.h" +#include "../world/Scenery.h" +#include "GameAction.h" + +class LargeSceneryPlaceActionResult final : public GameActionResult +{ +public: + LargeSceneryPlaceActionResult() + : GameActionResult(GA_ERROR::OK, STR_CANT_POSITION_THIS_HERE) + { + } + LargeSceneryPlaceActionResult(GA_ERROR error) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE) + { + } + LargeSceneryPlaceActionResult(GA_ERROR error, rct_string_id message) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE, message) + { + } + LargeSceneryPlaceActionResult(GA_ERROR error, rct_string_id message, uint8_t* args) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE, message, args) + { + } + + uint8_t GroundFlags{ 0 }; + TileElement* tileElement = nullptr; +}; + +DEFINE_GAME_ACTION(LargeSceneryPlaceAction, GAME_COMMAND_PLACE_LARGE_SCENERY, LargeSceneryPlaceActionResult) +{ +private: + CoordsXYZD _loc; + uint8_t _sceneryType{ std::numeric_limits::max() }; + uint8_t _primaryColour; + uint8_t _secondaryColour; + BannerIndex _bannerId{ BANNER_INDEX_NULL }; + +public: + LargeSceneryPlaceAction() = default; + + LargeSceneryPlaceAction(CoordsXYZD loc, uint8_t sceneryType, uint8_t primaryColour, uint8_t secondaryColour) + : _loc(loc) + , _sceneryType(sceneryType) + , _primaryColour(primaryColour) + , _secondaryColour(secondaryColour) + { + rct_scenery_entry* sceneryEntry = get_large_scenery_entry(_sceneryType); + if (sceneryEntry != nullptr) + { + if (sceneryEntry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE) + { + _bannerId = create_new_banner(0); + } + } + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_loc) << DS_TAG(_sceneryType) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour) + << DS_TAG(_bannerId); + } + + GameActionResult::Ptr Query() const override + { + auto res = std::make_unique(); + res->ErrorTitle = STR_CANT_POSITION_THIS_HERE; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + int16_t surfaceHeight = tile_element_height(_loc); + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = surfaceHeight; + res->GroundFlags = 0; + + money32 supportsCost = 0; + + if (_primaryColour > TILE_ELEMENT_COLOUR_MASK || _secondaryColour > TILE_ELEMENT_COLOUR_MASK) + { + log_error( + "Invalid game command for scenery placement, primaryColour = %u, secondaryColour = %u", _primaryColour, + _secondaryColour); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + + if (_sceneryType >= MAX_LARGE_SCENERY_OBJECTS) + { + log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + + rct_scenery_entry* sceneryEntry = get_large_scenery_entry(_sceneryType); + if (sceneryEntry == nullptr) + { + log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + + uint32_t totalNumTiles = GetTotalNumTiles(sceneryEntry->large_scenery.tiles); + int16_t maxHeight = GetMaxSurfaceHeight(sceneryEntry->large_scenery.tiles); + + if (_loc.z != 0) + { + maxHeight = _loc.z; + } + + res->Position.z = maxHeight; + + if (sceneryEntry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE) + { + if (_bannerId == BANNER_INDEX_NULL) + { + log_error("Banner Index not specified."); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_TOO_MANY_BANNERS_IN_GAME); + } + + auto banner = GetBanner(_bannerId); + if (banner->type != BANNER_NULL) + { + log_error("No free banners available"); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); + } + } + + if (!map_check_free_elements_and_reorganise(totalNumTiles)) + { + log_error("No free map elements available"); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); + } + + uint8_t tileNum = 0; + for (rct_large_scenery_tile* tile = sceneryEntry->large_scenery.tiles; tile->x_offset != -1; tile++, tileNum++) + { + auto tempX = tile->x_offset; + auto tempY = tile->y_offset; + rotate_map_coordinates(&tempX, &tempY, _loc.direction); + CoordsXY curTile = { tempX, tempY }; + + curTile.x += _loc.x; + curTile.y += _loc.y; + + int32_t zLow = (tile->z_offset + maxHeight) / 8; + int32_t zHigh = (tile->z_clearance / 8) + zLow; + + QuarterTile quarterTile = QuarterTile{ static_cast(tile->flags >> 12), 0 }.Rotate(_loc.direction); + if (!map_can_construct_with_clear_at( + curTile.x, curTile.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &supportsCost, + CREATE_CROSSING_MODE_NONE)) + { + return std::make_unique( + GA_ERROR::NO_CLEARANCE, gGameCommandErrorText, gCommonFormatArgs); + } + + int32_t tempSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); + if (!gCheatsDisableClearanceChecks) + { + if ((gMapGroundFlags & ELEMENT_IS_UNDERWATER) || (gMapGroundFlags & ELEMENT_IS_UNDERGROUND)) + { + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CANT_BUILD_THIS_UNDERWATER); + } + if (res->GroundFlags && !(res->GroundFlags & tempSceneryGroundFlags)) + { + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND); + } + } + + res->GroundFlags = tempSceneryGroundFlags; + + if (curTile.x >= gMapSizeUnits || curTile.y >= gMapSizeUnits) + { + return std::make_unique(GA_ERROR::DISALLOWED, STR_OFF_EDGE_OF_MAP); + } + + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_owned({ curTile, zLow * 8 }) + && !gCheatsSandboxMode) + { + return std::make_unique(GA_ERROR::DISALLOWED, STR_LAND_NOT_OWNED_BY_PARK); + } + } + + // Force ride construction to recheck area + _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; + + res->Cost = (sceneryEntry->large_scenery.price * 10) + supportsCost; + return res; + } + + GameActionResult::Ptr Execute() const override + { + auto res = std::make_unique(); + res->ErrorTitle = STR_CANT_POSITION_THIS_HERE; + + int16_t surfaceHeight = tile_element_height(_loc); + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = surfaceHeight; + res->GroundFlags = 0; + + money32 supportsCost = 0; + + rct_scenery_entry* sceneryEntry = get_large_scenery_entry(_sceneryType); + if (sceneryEntry == nullptr) + { + log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + + if (sceneryEntry->large_scenery.tiles == nullptr) + { + log_error("Invalid large scenery object, sceneryType = %u", _sceneryType); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + + uint32_t totalNumTiles = GetTotalNumTiles(sceneryEntry->large_scenery.tiles); + int16_t maxHeight = GetMaxSurfaceHeight(sceneryEntry->large_scenery.tiles); + + if (_loc.z != 0) + { + maxHeight = _loc.z; + } + + res->Position.z = maxHeight; + + if (sceneryEntry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE) + { + if (_bannerId == BANNER_INDEX_NULL) + { + log_error("No free banners available"); + return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_BANNERS_IN_GAME); + } + + auto banner = GetBanner(_bannerId); + if (banner->type != BANNER_NULL) + { + log_error("No free banners available"); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); + } + + banner->text = {}; + banner->colour = 2; + banner->text_colour = 2; + banner->flags = BANNER_FLAG_IS_LARGE_SCENERY; + banner->type = 0; + banner->position.x = _loc.x / 32; + banner->position.y = _loc.y / 32; + + ride_id_t rideIndex = banner_get_closest_ride_index(_loc.x, _loc.y, maxHeight); + if (rideIndex != RIDE_ID_NULL) + { + banner->ride_index = rideIndex; + banner->flags |= BANNER_FLAG_LINKED_TO_RIDE; + } + } + + if (!map_check_free_elements_and_reorganise(totalNumTiles)) + { + log_error("No free map elements available"); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); + } + + uint8_t tileNum = 0; + for (rct_large_scenery_tile* tile = sceneryEntry->large_scenery.tiles; tile->x_offset != -1; tile++, tileNum++) + { + auto tempX = tile->x_offset; + auto tempY = tile->y_offset; + rotate_map_coordinates(&tempX, &tempY, _loc.direction); + CoordsXY curTile = { tempX, tempY }; + + curTile.x += _loc.x; + curTile.y += _loc.y; + + int32_t zLow = (tile->z_offset + maxHeight) / 8; + int32_t zHigh = (tile->z_clearance / 8) + zLow; + + QuarterTile quarterTile = QuarterTile{ static_cast(tile->flags >> 12), 0 }.Rotate(_loc.direction); + if (!map_can_construct_with_clear_at( + curTile.x, curTile.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &supportsCost, + CREATE_CROSSING_MODE_NONE)) + { + return std::make_unique( + GA_ERROR::NO_CLEARANCE, gGameCommandErrorText, gCommonFormatArgs); + } + + res->GroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); + + if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST)) + { + footpath_remove_litter(curTile.x, curTile.y, zLow * 8); + if (!gCheatsDisableClearanceChecks) + { + wall_remove_at(curTile.x, curTile.y, zLow * 8, zHigh * 8); + } + } + + TileElement* newTileElement = tile_element_insert( + { curTile.x / 32, curTile.y / 32, zLow }, quarterTile.GetBaseQuarterOccupied()); + Guard::Assert(newTileElement != nullptr); + map_animation_create(MAP_ANIMATION_TYPE_LARGE_SCENERY, curTile.x, curTile.y, zLow); + newTileElement->SetType(TILE_ELEMENT_TYPE_LARGE_SCENERY); + newTileElement->clearance_height = zHigh; + auto newSceneryElement = newTileElement->AsLargeScenery(); + + SetNewLargeSceneryElement(*newSceneryElement, tileNum); + + if (tileNum == 0) + { + res->tileElement = newTileElement; + } + map_invalidate_tile_full(curTile.x, curTile.y); + } + + // Force ride construction to recheck area + _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; + + res->Cost = (sceneryEntry->large_scenery.price * 10) + supportsCost; + return res; + } + +private: + int16_t GetTotalNumTiles(rct_large_scenery_tile * tiles) const + { + uint32_t totalNumTiles = 0; + for (rct_large_scenery_tile* tile = tiles; tile->x_offset != -1; tile++) + { + totalNumTiles++; + } + return totalNumTiles; + } + + int16_t GetMaxSurfaceHeight(rct_large_scenery_tile * tiles) const + { + int16_t maxHeight = -1; + for (rct_large_scenery_tile* tile = tiles; tile->x_offset != -1; tile++) + { + auto tempX = tile->x_offset; + auto tempY = tile->y_offset; + rotate_map_coordinates(&tempX, &tempY, _loc.direction); + CoordsXY curTile = { tempX, tempY }; + + curTile.x += _loc.x; + curTile.y += _loc.y; + + if (curTile.x >= 0x1FFF || curTile.y >= 0x1FFF || curTile.x < 0 || curTile.y < 0) + { + continue; + } + + auto* surfaceElement = map_get_surface_element_at(curTile); + if (surfaceElement == nullptr) + continue; + + int32_t height = surfaceElement->base_height * 8; + int32_t slope = surfaceElement->GetSlope(); + + if (slope & 0xF) + { + height += 16; + if (slope & 0x10) + { + height += 16; + } + } + + if (height > maxHeight) + { + maxHeight = height; + } + } + return maxHeight; + } + + void SetNewLargeSceneryElement(LargeSceneryElement & sceneryElement, uint8_t tileNum) const + { + sceneryElement.SetDirection(_loc.direction); + sceneryElement.SetEntryIndex(_sceneryType); + sceneryElement.SetSequenceIndex(tileNum); + sceneryElement.SetPrimaryColour(_primaryColour); + sceneryElement.SetSecondaryColour(_secondaryColour); + + if (_bannerId != BANNER_INDEX_NULL) + { + sceneryElement.SetBannerIndex(_bannerId); + } + + if (GetFlags() & GAME_COMMAND_FLAG_GHOST) + { + sceneryElement.SetGhost(true); + } + } +}; diff --git a/src/openrct2/actions/LargeSceneryRemoveAction.hpp b/src/openrct2/actions/LargeSceneryRemoveAction.hpp index 4de461e788..18f7c4b4f3 100644 --- a/src/openrct2/actions/LargeSceneryRemoveAction.hpp +++ b/src/openrct2/actions/LargeSceneryRemoveAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -26,20 +26,14 @@ DEFINE_GAME_ACTION(LargeSceneryRemoveAction, GAME_COMMAND_REMOVE_LARGE_SCENERY, GameActionResult) { private: - int16_t _x; - int16_t _y; - uint8_t _baseHeight; - uint8_t _direction; + CoordsXYZD _loc; uint16_t _tileIndex; public: LargeSceneryRemoveAction() = default; - LargeSceneryRemoveAction(int16_t x, int16_t y, uint8_t baseHeight, uint8_t direction, uint16_t tileIndex) - : _x(x) - , _y(y) - , _baseHeight(baseHeight) - , _direction(direction) + LargeSceneryRemoveAction(CoordsXYZD location, uint16_t tileIndex) + : _loc(location) , _tileIndex(tileIndex) { } @@ -53,7 +47,7 @@ public: { GameAction::Serialise(stream); - stream << DS_TAG(_x) << DS_TAG(_y) << DS_TAG(_baseHeight) << DS_TAG(_direction) << DS_TAG(_tileIndex); + stream << DS_TAG(_loc) << DS_TAG(_tileIndex); } GameActionResult::Ptr Query() const override @@ -62,9 +56,9 @@ public: const uint32_t flags = GetFlags(); - int32_t z = tile_element_height(_x, _y); - res->Position.x = _x + 16; - res->Position.y = _y + 16; + int32_t z = tile_element_height(_loc); + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; res->Position.z = z; res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; res->Cost = 0; @@ -72,21 +66,20 @@ public: TileElement* tileElement = FindLargeSceneryElement(); if (tileElement == nullptr) { - log_warning("Invalid game command for scenery removal, x = %d, y = %d", _x, _y); + log_warning("Invalid game command for scenery removal, x = %d, y = %d", _loc.x, _loc.y); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_INVALID_SELECTION_OF_OBJECTS); } rct_scenery_entry* scenery_entry = tileElement->AsLargeScenery()->GetEntry(); - LocationXYZ16 firstTile = { - scenery_entry->large_scenery.tiles[_tileIndex].x_offset, scenery_entry->large_scenery.tiles[_tileIndex].y_offset, - static_cast((_baseHeight * 8) - scenery_entry->large_scenery.tiles[_tileIndex].z_offset) - }; + LocationXYZ16 firstTile = { scenery_entry->large_scenery.tiles[_tileIndex].x_offset, + scenery_entry->large_scenery.tiles[_tileIndex].y_offset, + static_cast((_loc.z) - scenery_entry->large_scenery.tiles[_tileIndex].z_offset) }; - rotate_map_coordinates(&firstTile.x, &firstTile.y, _direction); + rotate_map_coordinates(&firstTile.x, &firstTile.y, _loc.direction); - firstTile.x = _x - firstTile.x; - firstTile.y = _y - firstTile.y; + firstTile.x = _loc.x - firstTile.x; + firstTile.y = _loc.y - firstTile.y; bool calculate_cost = true; for (int32_t i = 0; scenery_entry->large_scenery.tiles[i].x_offset != -1; i++) @@ -95,7 +88,7 @@ public: scenery_entry->large_scenery.tiles[i].y_offset, scenery_entry->large_scenery.tiles[i].z_offset }; - rotate_map_coordinates(¤tTile.x, ¤tTile.y, _direction); + rotate_map_coordinates(¤tTile.x, ¤tTile.y, _loc.direction); currentTile.x += firstTile.x; currentTile.y += firstTile.y; @@ -103,7 +96,7 @@ public: if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) { - if (!map_is_location_owned(currentTile.x, currentTile.y, currentTile.z)) + if (!map_is_location_owned({ currentTile.x, currentTile.y, currentTile.z })) { return MakeResult(GA_ERROR::NO_CLEARANCE, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK); } @@ -121,7 +114,7 @@ public: } } - if (calculate_cost == true) + if (calculate_cost) res->Cost = scenery_entry->large_scenery.removal_price * 10; return res; @@ -133,9 +126,9 @@ public: const uint32_t flags = GetFlags(); - int32_t z = tile_element_height(_x, _y); - res->Position.x = _x + 16; - res->Position.y = _y + 16; + int32_t z = tile_element_height(_loc); + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; res->Position.z = z; res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; res->Cost = 0; @@ -143,7 +136,7 @@ public: TileElement* tileElement = FindLargeSceneryElement(); if (tileElement == nullptr) { - log_warning("Invalid game command for scenery removal, x = %d, y = %d", _x, _y); + log_warning("Invalid game command for scenery removal, x = %d, y = %d", _loc.x, _loc.y); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_INVALID_SELECTION_OF_OBJECTS); } @@ -151,15 +144,14 @@ public: rct_scenery_entry* scenery_entry = tileElement->AsLargeScenery()->GetEntry(); - LocationXYZ16 firstTile = { - scenery_entry->large_scenery.tiles[_tileIndex].x_offset, scenery_entry->large_scenery.tiles[_tileIndex].y_offset, - static_cast((_baseHeight * 8) - scenery_entry->large_scenery.tiles[_tileIndex].z_offset) - }; + LocationXYZ16 firstTile = { scenery_entry->large_scenery.tiles[_tileIndex].x_offset, + scenery_entry->large_scenery.tiles[_tileIndex].y_offset, + static_cast((_loc.z) - scenery_entry->large_scenery.tiles[_tileIndex].z_offset) }; - rotate_map_coordinates(&firstTile.x, &firstTile.y, _direction); + rotate_map_coordinates(&firstTile.x, &firstTile.y, _loc.direction); - firstTile.x = _x - firstTile.x; - firstTile.y = _y - firstTile.y; + firstTile.x = _loc.x - firstTile.x; + firstTile.y = _loc.y - firstTile.y; for (int32_t i = 0; scenery_entry->large_scenery.tiles[i].x_offset != -1; i++) { @@ -167,7 +159,7 @@ public: scenery_entry->large_scenery.tiles[i].y_offset, scenery_entry->large_scenery.tiles[i].z_offset }; - rotate_map_coordinates(¤tTile.x, ¤tTile.y, _direction); + rotate_map_coordinates(¤tTile.x, ¤tTile.y, _loc.direction); currentTile.x += firstTile.x; currentTile.y += firstTile.y; @@ -175,7 +167,7 @@ public: if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) { - if (!map_is_location_owned(currentTile.x, currentTile.y, currentTile.z)) + if (!map_is_location_owned({ currentTile.x, currentTile.y, currentTile.z })) { return MakeResult(GA_ERROR::NO_CLEARANCE, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK); } @@ -183,30 +175,33 @@ public: TileElement* sceneryElement = map_get_first_element_at(currentTile.x / 32, currentTile.y / 32); bool element_found = false; - do + if (sceneryElement != nullptr) { - if (sceneryElement->GetType() != TILE_ELEMENT_TYPE_LARGE_SCENERY) - continue; + do + { + if (sceneryElement->GetType() != TILE_ELEMENT_TYPE_LARGE_SCENERY) + continue; - if (sceneryElement->GetDirection() != _direction) - continue; + if (sceneryElement->GetDirection() != _loc.direction) + continue; - if (sceneryElement->AsLargeScenery()->GetSequenceIndex() != i) - continue; + if (sceneryElement->AsLargeScenery()->GetSequenceIndex() != i) + continue; - if (sceneryElement->base_height != currentTile.z / 8) - continue; + if (sceneryElement->base_height != currentTile.z / 8) + continue; - // If we are removing ghost elements - if ((flags & GAME_COMMAND_FLAG_GHOST) && sceneryElement->IsGhost() == false) - continue; + // If we are removing ghost elements + if ((flags & GAME_COMMAND_FLAG_GHOST) && sceneryElement->IsGhost() == false) + continue; - map_invalidate_tile_full(currentTile.x, currentTile.y); - tile_element_remove(sceneryElement); + map_invalidate_tile_full(currentTile.x, currentTile.y); + tile_element_remove(sceneryElement); - element_found = true; - break; - } while (!(sceneryElement++)->IsLastForTile()); + element_found = true; + break; + } while (!(sceneryElement++)->IsLastForTile()); + } if (element_found == false) { @@ -222,7 +217,7 @@ public: private: TileElement* FindLargeSceneryElement() const { - TileElement* tileElement = map_get_first_element_at(_x / 32, _y / 32); + TileElement* tileElement = map_get_first_element_at(_loc.x / 32, _loc.y / 32); if (tileElement == nullptr) return nullptr; @@ -231,13 +226,13 @@ private: if (tileElement->GetType() != TILE_ELEMENT_TYPE_LARGE_SCENERY) continue; - if (tileElement->base_height != _baseHeight) + if (tileElement->base_height != _loc.z / 8) continue; if (tileElement->AsLargeScenery()->GetSequenceIndex() != _tileIndex) continue; - if (tileElement->GetDirection() != _direction) + if (tileElement->GetDirection() != _loc.direction) continue; // If we are removing ghost elements diff --git a/src/openrct2/actions/LargeScenerySetColourAction.hpp b/src/openrct2/actions/LargeScenerySetColourAction.hpp new file mode 100644 index 0000000000..03ad61bd44 --- /dev/null +++ b/src/openrct2/actions/LargeScenerySetColourAction.hpp @@ -0,0 +1,152 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../OpenRCT2.h" +#include "../management/Finance.h" +#include "../world/Scenery.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(LargeScenerySetColourAction, GAME_COMMAND_SET_LARGE_SCENERY_COLOUR, GameActionResult) +{ +private: + CoordsXYZD _loc; + uint8_t _tileIndex; + uint8_t _primaryColour; + uint8_t _secondaryColour; + +public: + LargeScenerySetColourAction() = default; + + LargeScenerySetColourAction(CoordsXYZD loc, uint8_t tileIndex, uint8_t primaryColour, uint8_t secondaryColour) + : _loc(loc) + , _tileIndex(tileIndex) + , _primaryColour(primaryColour) + , _secondaryColour(secondaryColour) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_loc) << DS_TAG(_tileIndex) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = tile_element_height(_loc); + res->ErrorTitle = STR_CANT_REPAINT_THIS; + + if (_loc.x < 0 || _loc.y < 0 || _loc.x > gMapSizeMaxXY || _loc.y > gMapSizeMaxXY) + { + log_error("Invalid x / y coordinates: x = %d, y = %d", _loc.x, _loc.y); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + if (_primaryColour > 31) + { + log_error("Invalid primary colour: colour = %u", _primaryColour); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + if (_secondaryColour > 31) + { + log_error("Invalid primary colour: colour = %u", _secondaryColour); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + auto largeElement = map_get_large_scenery_segment(_loc.x, _loc.y, _loc.z / 8, _loc.direction, _tileIndex); + + if (largeElement == nullptr) + { + log_error( + "Could not find large scenery at: x = %d, y = %d, z = %d, direction = %d, tileIndex = %u", _loc.x, _loc.y, + _loc.z, _loc.direction, _tileIndex); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + if ((GetFlags() & GAME_COMMAND_FLAG_GHOST) && !(largeElement->IsGhost())) + { + return res; + } + + rct_scenery_entry* sceneryEntry = largeElement->GetEntry(); + + if (sceneryEntry == nullptr) + { + log_error("Could not find scenery object. type = %u", largeElement->GetEntryIndex()); + return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_REPAINT_THIS); + } + // Work out the base tile coordinates (Tile with index 0) + auto baseX = sceneryEntry->large_scenery.tiles[_tileIndex].x_offset; + auto baseY = sceneryEntry->large_scenery.tiles[_tileIndex].y_offset; + rotate_map_coordinates(&baseX, &baseY, _loc.direction); + + CoordsXYZ baseTile = { _loc.x - baseX, _loc.y - baseY, + _loc.z - sceneryEntry->large_scenery.tiles[_tileIndex].z_offset }; + + auto i = 0; + for (auto tile = sceneryEntry->large_scenery.tiles; tile->x_offset != -1; ++tile, ++i) + { + // Work out the current tile coordinates + auto tileX = tile->x_offset; + auto tileY = tile->y_offset; + rotate_map_coordinates(&tileX, &tileY, _loc.direction); + CoordsXYZ currentTile = { tileX + baseTile.x, tileY + baseTile.y, tile->z_offset + baseTile.z }; + + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) + { + if (!map_is_location_owned(currentTile)) + { + return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_REPAINT_THIS, STR_LAND_NOT_OWNED_BY_PARK); + } + } + + auto tileElement = map_get_large_scenery_segment(currentTile.x, currentTile.y, _loc.z / 8, _loc.direction, i); + + if (tileElement == nullptr) + { + log_error( + "Large scenery element not found at: x = %d, y = %d, z = %d, direction = %d", _loc.x, _loc.y, _loc.z, + _loc.direction); + return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_REPAINT_THIS); + } + if (isExecuting) + { + tileElement->SetPrimaryColour(_primaryColour); + tileElement->SetSecondaryColour(_secondaryColour); + + map_invalidate_tile_full(currentTile.x, currentTile.y); + } + } + return res; + } +}; diff --git a/src/openrct2/actions/MazeSetTrackAction.hpp b/src/openrct2/actions/MazeSetTrackAction.hpp index 4df00ae4f1..cd156af7cc 100644 --- a/src/openrct2/actions/MazeSetTrackAction.hpp +++ b/src/openrct2/actions/MazeSetTrackAction.hpp @@ -1,12 +1,11 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ - #pragma once #include "../Cheats.h" @@ -20,7 +19,6 @@ #include "../world/Footpath.h" #include "../world/Park.h" #include "GameAction.h" - // clang-format off /** rct2: 0x00993CE9 */ static constexpr const uint8_t byte_993CE9[] = { @@ -51,11 +49,8 @@ static constexpr const uint8_t byte_993D0C[] = { DEFINE_GAME_ACTION(MazeSetTrackAction, GAME_COMMAND_SET_MAZE_TRACK, GameActionResult) { private: - uint16_t _x; - uint16_t _y; - uint16_t _z; + CoordsXYZD _loc; bool _initialPlacement; - uint8_t _direction; NetworkRideId_t _rideIndex; uint8_t _mode; @@ -63,13 +58,9 @@ public: MazeSetTrackAction() { } - MazeSetTrackAction( - uint16_t x, uint16_t y, uint16_t z, bool initialPlacement, uint8_t direction, NetworkRideId_t rideIndex, uint8_t mode) - : _x(x) - , _y(y) - , _z(z) + MazeSetTrackAction(CoordsXYZD location, bool initialPlacement, NetworkRideId_t rideIndex, uint8_t mode) + : _loc(location) , _initialPlacement(initialPlacement) - , _direction(direction) , _rideIndex(rideIndex) , _mode(mode) { @@ -78,54 +69,50 @@ public: void Serialise(DataSerialiser & stream) override { GameAction::Serialise(stream); - - stream << DS_TAG(_x) << DS_TAG(_y) << DS_TAG(_z) << DS_TAG(_initialPlacement) << DS_TAG(_direction) - << DS_TAG(_rideIndex) << DS_TAG(_mode); + stream << DS_TAG(_loc) << DS_TAG(_loc.direction) << DS_TAG(_initialPlacement) << DS_TAG(_rideIndex) << DS_TAG(_mode); } GameActionResult::Ptr Query() const override { auto res = std::make_unique(); - res->Position.x = _x + 8; - res->Position.y = _y + 8; - res->Position.z = _z; + res->Position.x = _loc.x + 8; + res->Position.y = _loc.y + 8; + res->Position.z = _loc.z; res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; res->ErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE; - if (!map_check_free_elements_and_reorganise(1)) { res->Error = GA_ERROR::NO_FREE_ELEMENTS; res->ErrorMessage = STR_TILE_ELEMENT_LIMIT_REACHED; return res; } - - if ((_z & 0xF) != 0) + if ((_loc.z & 0xF) != 0) { res->Error = GA_ERROR::UNKNOWN; res->ErrorMessage = STR_CONSTRUCTION_ERR_UNKNOWN; return res; } - if (!map_is_location_owned(floor2(_x, 32), floor2(_y, 32), _z) && !gCheatsSandboxMode) + if (!map_is_location_owned(_loc) && !gCheatsSandboxMode) { res->Error = GA_ERROR::NOT_OWNED; res->ErrorMessage = STR_LAND_NOT_OWNED_BY_PARK; return res; } - TileElement* tileElement = map_get_surface_element_at(_x / 32, _y / 32); - if (tileElement == nullptr) + auto surfaceElement = map_get_surface_element_at(_loc); + if (surfaceElement == nullptr) { res->Error = GA_ERROR::UNKNOWN; res->ErrorMessage = STR_INVALID_SELECTION_OF_OBJECTS; return res; } - uint8_t baseHeight = _z >> 3; - uint8_t clearanceHeight = (_z + 32) >> 3; + uint8_t baseHeight = _loc.z / 8; + uint8_t clearanceHeight = (_loc.z + 32) / 8; - int8_t heightDifference = baseHeight - tileElement->base_height; + int8_t heightDifference = baseHeight - surfaceElement->base_height; if (heightDifference >= 0 && !gCheatsDisableSupportLimits) { heightDifference = heightDifference >> 1; @@ -138,7 +125,8 @@ public: } } - tileElement = map_get_track_element_at_of_type_from_ride(_x, _y, baseHeight, TRACK_ELEM_MAZE, _rideIndex); + TileElement* tileElement = map_get_track_element_at_of_type_from_ride( + _loc.x, _loc.y, baseHeight, TRACK_ELEM_MAZE, _rideIndex); if (tileElement == nullptr) { if (_mode != GC_SET_MAZE_TRACK_BUILD) @@ -148,7 +136,7 @@ public: return res; } - if (!map_can_construct_at(floor2(_x, 32), floor2(_y, 32), baseHeight, clearanceHeight, { 0b1111, 0 })) + if (!map_can_construct_at(floor2(_loc.x, 32), floor2(_loc.y, 32), baseHeight, clearanceHeight, { 0b1111, 0 })) { return MakeResult(GA_ERROR::NO_CLEARANCE, res->ErrorTitle, gGameCommandErrorText, gCommonFormatArgs); } @@ -167,7 +155,7 @@ public: return res; } - Ride* ride = get_ride(_rideIndex); + auto ride = get_ride(_rideIndex); if (ride == nullptr || ride->type == RIDE_CRASH_TYPE_NONE) { res->Error = GA_ERROR::NO_CLEARANCE; @@ -188,12 +176,20 @@ public: { auto res = std::make_unique(); - res->Position.x = _x + 8; - res->Position.y = _y + 8; - res->Position.z = _z; + res->Position.x = _loc.x + 8; + res->Position.y = _loc.y + 8; + res->Position.z = _loc.z; res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; res->ErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE; + auto ride = get_ride(_rideIndex); + if (ride == nullptr) + { + res->Error = GA_ERROR::INVALID_PARAMETERS; + res->ErrorMessage = STR_NONE; + return res; + } + if (!map_check_free_elements_and_reorganise(1)) { res->Error = GA_ERROR::NO_FREE_ELEMENTS; @@ -202,28 +198,25 @@ public: } uint32_t flags = GetFlags(); - if (!(flags & GAME_COMMAND_FLAG_GHOST) && !(flags & GAME_COMMAND_FLAG_2)) + if (!(flags & GAME_COMMAND_FLAG_GHOST)) { - footpath_remove_litter(_x, _y, _z); - wall_remove_at(floor2(_x, 32), floor2(_y, 32), _z, _z + 32); + footpath_remove_litter(_loc.x, _loc.y, _loc.z); + wall_remove_at(floor2(_loc.x, 32), floor2(_loc.y, 32), _loc.z, _loc.z + 32); } - uint8_t baseHeight = _z >> 3; - uint8_t clearanceHeight = (_z + 32) >> 3; + uint8_t baseHeight = _loc.z / 8; + uint8_t clearanceHeight = (_loc.z + 32) / 8; - TileElement* tileElement = map_get_track_element_at_of_type_from_ride(_x, _y, baseHeight, TRACK_ELEM_MAZE, _rideIndex); + auto tileElement = map_get_track_element_at_of_type_from_ride(_loc.x, _loc.y, baseHeight, TRACK_ELEM_MAZE, _rideIndex); if (tileElement == nullptr) { - Ride* ride = get_ride(_rideIndex); - openrct2_assert(ride != nullptr, "Invalid ride index: %d\n", uint32_t(_rideIndex)); - money32 price = (((RideTrackCosts[ride->type].track_price * TrackPricing[TRACK_ELEM_MAZE]) >> 16)); res->Cost = price / 2 * 10; - uint16_t flooredX = floor2(_x, 32); - uint16_t flooredY = floor2(_y, 32); + uint16_t flooredX = floor2(_loc.x, 32); + uint16_t flooredY = floor2(_loc.y, 32); - tileElement = tile_element_insert(_x / 32, _y / 32, baseHeight, 0xF); + tileElement = tile_element_insert({ _loc.x / 32, _loc.y / 32, baseHeight }, 0b1111); assert(tileElement != nullptr); tileElement->clearance_height = clearanceHeight; @@ -255,20 +248,20 @@ public: { case GC_SET_MAZE_TRACK_BUILD: { - uint8_t segmentOffset = MazeGetSegmentBit(_x, _y); + uint8_t segmentOffset = MazeGetSegmentBit(_loc.x, _loc.y); tileElement->AsTrack()->MazeEntrySubtract(1 << segmentOffset); if (!_initialPlacement) { - segmentOffset = byte_993CE9[(_direction + segmentOffset)]; + segmentOffset = byte_993CE9[(_loc.direction + segmentOffset)]; tileElement->AsTrack()->MazeEntrySubtract(1 << segmentOffset); uint8_t temp_edx = byte_993CFC[segmentOffset]; if (temp_edx != 0xFF) { - uint16_t previousElementX = floor2(_x, 32) - CoordsDirectionDelta[_direction].x; - uint16_t previousElementY = floor2(_y, 32) - CoordsDirectionDelta[_direction].y; + uint16_t previousElementX = floor2(_loc.x, 32) - CoordsDirectionDelta[_loc.direction].x; + uint16_t previousElementY = floor2(_loc.y, 32) - CoordsDirectionDelta[_loc.direction].y; TileElement* previousTileElement = map_get_track_element_at_of_type_from_ride( previousElementX, previousElementY, baseHeight, TRACK_ELEM_MAZE, _rideIndex); @@ -293,8 +286,8 @@ public: case GC_SET_MAZE_TRACK_FILL: if (!_initialPlacement) { - uint16_t previousSegmentX = _x - CoordsDirectionDelta[_direction].x / 2; - uint16_t previousSegmentY = _y - CoordsDirectionDelta[_direction].y / 2; + uint16_t previousSegmentX = _loc.x - CoordsDirectionDelta[_loc.direction].x / 2; + uint16_t previousSegmentY = _loc.y - CoordsDirectionDelta[_loc.direction].y / 2; tileElement = map_get_track_element_at_of_type_from_ride( previousSegmentX, previousSegmentY, baseHeight, TRACK_ELEM_MAZE, _rideIndex); @@ -302,7 +295,7 @@ public: map_invalidate_tile_full(floor2(previousSegmentX, 32), floor2(previousSegmentY, 32)); if (tileElement == nullptr) { - log_error("No surface found\n"); + log_error("No surface found"); res->Error = GA_ERROR::UNKNOWN; res->ErrorMessage = STR_NONE; return res; @@ -340,11 +333,11 @@ public: break; } - map_invalidate_tile(floor2(_x, 32), floor2(_y, 32), tileElement->base_height * 8, tileElement->clearance_height * 8); + map_invalidate_tile( + floor2(_loc.x, 32), floor2(_loc.y, 32), tileElement->base_height * 8, tileElement->clearance_height * 8); if ((tileElement->AsTrack()->GetMazeEntry() & 0x8888) == 0x8888) { - Ride* ride = get_ride(_rideIndex); tile_element_remove(tileElement); sub_6CB945(ride); ride->maze_tiles--; diff --git a/src/openrct2/actions/NetworkModifyGroupAction.hpp b/src/openrct2/actions/NetworkModifyGroupAction.hpp new file mode 100644 index 0000000000..f8185d605d --- /dev/null +++ b/src/openrct2/actions/NetworkModifyGroupAction.hpp @@ -0,0 +1,81 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../network/network.h" +#include "GameAction.h" + +enum class ModifyGroupType : uint8_t +{ + AddGroup, + RemoveGroup, + SetPermissions, + SetName, + SetDefault, + Count +}; + +enum class PermissionState : uint8_t +{ + Toggle, + SetAll, + ClearAll, + Count +}; + +DEFINE_GAME_ACTION(NetworkModifyGroupAction, GAME_COMMAND_MODIFY_GROUPS, GameActionResult) +{ +private: + uint8_t _type{ static_cast(ModifyGroupType::Count) }; + uint8_t _groupId{ std::numeric_limits::max() }; + std::string _name; + uint32_t _permissionIndex{ std::numeric_limits::max() }; + uint8_t _permissionState{ static_cast(PermissionState::Count) }; + +public: + NetworkModifyGroupAction() = default; + + NetworkModifyGroupAction( + ModifyGroupType type, uint8_t groupId = std::numeric_limits::max(), const std::string name = "", + uint32_t permissionIndex = 0, PermissionState permissionState = PermissionState::Count) + : _type(static_cast(type)) + , _groupId(groupId) + , _name(name) + , _permissionIndex(permissionIndex) + , _permissionState(static_cast(permissionState)) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_type) << DS_TAG(_groupId) << DS_TAG(_name) << DS_TAG(_permissionIndex) << DS_TAG(_permissionState); + } + + GameActionResult::Ptr Query() const override + { + return network_modify_groups( + GetPlayer(), static_cast(_type), _groupId, _name, _permissionIndex, + static_cast(_permissionState), false); + } + + GameActionResult::Ptr Execute() const override + { + return network_modify_groups( + GetPlayer(), static_cast(_type), _groupId, _name, _permissionIndex, + static_cast(_permissionState), true); + } +}; diff --git a/src/openrct2/actions/ParkEntranceRemoveAction.hpp b/src/openrct2/actions/ParkEntranceRemoveAction.hpp new file mode 100644 index 0000000000..f0c5d1ae65 --- /dev/null +++ b/src/openrct2/actions/ParkEntranceRemoveAction.hpp @@ -0,0 +1,108 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../OpenRCT2.h" +#include "../management/Finance.h" +#include "../world/Entrance.h" +#include "../world/Park.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(ParkEntranceRemoveAction, GAME_COMMAND_REMOVE_PARK_ENTRANCE, GameActionResult) +{ +private: + CoordsXYZ _loc; + +public: + ParkEntranceRemoveAction() = default; + + ParkEntranceRemoveAction(CoordsXYZ loc) + : _loc(loc) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::EDITOR_ONLY; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_loc); + } + + GameActionResult::Ptr Query() const override + { + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode) + { + return MakeResult(GA_ERROR::NOT_IN_EDITOR_MODE, STR_CANT_REMOVE_THIS); + } + + auto res = MakeResult(); + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; + res->Position = _loc; + res->ErrorTitle = STR_CANT_REMOVE_THIS; + + auto entranceIndex = park_entrance_get_index(_loc.x, _loc.y, _loc.z); + if (entranceIndex == -1) + { + log_error("Could not find entrance at x = %d, y = %d, z = %d", _loc.x, _loc.y, _loc.z); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS); + } + return res; + } + + GameActionResult::Ptr Execute() const override + { + auto res = MakeResult(); + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; + res->Position = _loc; + res->ErrorTitle = STR_CANT_REMOVE_THIS; + + auto entranceIndex = park_entrance_get_index(_loc.x, _loc.y, _loc.z); + if (entranceIndex == -1) + { + log_error("Could not find entrance at x = %d, y = %d, z = %d", _loc.x, _loc.y, _loc.z); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS); + } + + auto direction = (gParkEntrances[entranceIndex].direction - 1) & 3; + + // Centre (sign) + ParkEntranceRemoveSegment(_loc); + + // Left post + ParkEntranceRemoveSegment( + { _loc.x + CoordsDirectionDelta[direction].x, _loc.y + CoordsDirectionDelta[direction].y, _loc.z }); + + // Right post + ParkEntranceRemoveSegment( + { _loc.x - CoordsDirectionDelta[direction].x, _loc.y - CoordsDirectionDelta[direction].y, _loc.z }); + + gParkEntrances.erase(gParkEntrances.begin() + entranceIndex); + return res; + } + +private: + void ParkEntranceRemoveSegment(CoordsXYZ loc) const + { + auto entranceElement = map_get_park_entrance_element_at(loc.x, loc.y, loc.z / 8, true); + if (entranceElement == nullptr) + { + return; + } + + map_invalidate_tile(loc.x, loc.y, entranceElement->base_height * 8, entranceElement->clearance_height * 8); + entranceElement->Remove(); + update_park_fences({ loc.x, loc.y }); + } +}; diff --git a/src/openrct2/actions/ParkMarketingAction.hpp b/src/openrct2/actions/ParkMarketingAction.hpp index 8b8179671c..607d0c1249 100644 --- a/src/openrct2/actions/ParkMarketingAction.hpp +++ b/src/openrct2/actions/ParkMarketingAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -71,6 +71,7 @@ public: MarketingCampaign campaign{}; campaign.Type = _type; campaign.WeeksLeft = _numWeeks; + campaign.Flags = MarketingCampaignFlags::FIRST_WEEK; if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE) { campaign.RideId = _item; diff --git a/src/openrct2/actions/ParkSetDateAction.hpp b/src/openrct2/actions/ParkSetDateAction.hpp new file mode 100644 index 0000000000..72d776c7e5 --- /dev/null +++ b/src/openrct2/actions/ParkSetDateAction.hpp @@ -0,0 +1,65 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Context.h" +#include "../core/MemoryStream.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../ui/UiContext.h" +#include "../ui/WindowManager.h" +#include "../windows/Intent.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(ParkSetDateAction, GAME_COMMAND_SET_DATE, GameActionResult) +{ +private: + int32_t _year = 0; + int32_t _month = 0; + int32_t _day = 0; + +public: + ParkSetDateAction() + { + } + ParkSetDateAction(int32_t year, int32_t month, int32_t day) + : _year(year) + , _month(month) + , _day(day) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + stream << DS_TAG(_year) << DS_TAG(_month) << DS_TAG(_day); + } + + GameActionResult::Ptr Query() const override + { + if (_year <= 0 || _year > MAX_YEAR || _month <= 0 || _month > MONTH_COUNT || _day <= 0 || _day > 31) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + return MakeResult(); + } + + GameActionResult::Ptr Execute() const override + { + date_set(_year, _month, _day); + return MakeResult(); + } +}; diff --git a/src/openrct2/actions/ParkSetLoanAction.hpp b/src/openrct2/actions/ParkSetLoanAction.hpp index d92587db82..c5dca530e6 100644 --- a/src/openrct2/actions/ParkSetLoanAction.hpp +++ b/src/openrct2/actions/ParkSetLoanAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/actions/ParkSetNameAction.hpp b/src/openrct2/actions/ParkSetNameAction.hpp index acf2d50728..fc158f27fd 100644 --- a/src/openrct2/actions/ParkSetNameAction.hpp +++ b/src/openrct2/actions/ParkSetNameAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,6 +10,7 @@ #pragma once #include "../Context.h" +#include "../GameState.h" #include "../config/Config.h" #include "../core/MemoryStream.h" #include "../drawing/Drawing.h" @@ -53,49 +54,19 @@ public: { return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_RENAME_PARK, STR_INVALID_NAME_FOR_PARK); } - - // TODO create a version of user_string_allocate that only tests so we do not have to free it straight afterwards - auto stringId = user_string_allocate(USER_STRING_HIGH_ID_NUMBER, _name.c_str()); - if (stringId == 0) - { - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_RENAME_PARK, STR_INVALID_NAME_FOR_PARK); - } - user_string_free(stringId); - return MakeResult(); } GameActionResult::Ptr Execute() const override { // Do a no-op if new name is the same as the current name is the same - std::string oldName = GetCurrentParkName(); - if (_name == oldName) + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + if (_name != park.Name) { - return MakeResult(); + park.Name = _name; + scrolling_text_invalidate(); + gfx_invalidate_screen(); } - - // Allocate new string for park name - auto newNameId = user_string_allocate(USER_STRING_HIGH_ID_NUMBER, _name.c_str()); - if (newNameId == 0) - { - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_RENAME_PARK, STR_INVALID_NAME_FOR_PARK); - } - - // Replace park name with new string id - user_string_free(gParkName); - gParkName = newNameId; - - scrolling_text_invalidate(); - gfx_invalidate_screen(); - return MakeResult(); } - -private: - std::string GetCurrentParkName() const - { - char buffer[128]; - format_string(buffer, sizeof(buffer), gParkName, &gParkNameArgs); - return buffer; - } }; diff --git a/src/openrct2/actions/ParkSetParameterAction.hpp b/src/openrct2/actions/ParkSetParameterAction.hpp new file mode 100644 index 0000000000..65b86bf1c3 --- /dev/null +++ b/src/openrct2/actions/ParkSetParameterAction.hpp @@ -0,0 +1,97 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../interface/Window.h" +#include "../ride/ShopItem.h" +#include "../world/Park.h" +#include "GameAction.h" + +enum class ParkParameter : uint8_t +{ + Close, + Open, + SamePriceInPark, + Count +}; + +DEFINE_GAME_ACTION(ParkSetParameterAction, GAME_COMMAND_SET_PARK_OPEN, GameActionResult) +{ +private: + uint8_t _parameter{ static_cast(ParkParameter::Count) }; + uint64_t _value; + + constexpr static rct_string_id _ErrorTitles[] = { STR_CANT_CLOSE_PARK, STR_CANT_OPEN_PARK, STR_NONE, STR_NONE }; + +public: + ParkSetParameterAction() + { + } + ParkSetParameterAction(ParkParameter parameter, uint64_t value = 0) + : _parameter(static_cast(parameter)) + , _value(value) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + stream << DS_TAG(_parameter) << DS_TAG(_value); + } + + GameActionResult::Ptr Query() const override + { + if (_parameter >= static_cast(ParkParameter::Count)) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + auto res = MakeResult(); + res->ErrorTitle = _ErrorTitles[_parameter]; + return res; + } + + GameActionResult::Ptr Execute() const override + { + switch (static_cast(_parameter)) + { + case ParkParameter::Close: + if (gParkFlags & PARK_FLAGS_PARK_OPEN) + { + gParkFlags &= ~PARK_FLAGS_PARK_OPEN; + window_invalidate_by_class(WC_PARK_INFORMATION); + } + break; + case ParkParameter::Open: + if (!(gParkFlags & PARK_FLAGS_PARK_OPEN)) + { + gParkFlags |= PARK_FLAGS_PARK_OPEN; + window_invalidate_by_class(WC_PARK_INFORMATION); + } + break; + case ParkParameter::SamePriceInPark: + gSamePriceThroughoutPark = _value; + window_invalidate_by_class(WC_RIDE); + break; + default: + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + break; + } + + auto res = MakeResult(); + res->ErrorTitle = _ErrorTitles[_parameter]; + return res; + } +}; diff --git a/src/openrct2/actions/ParkSetResearchFundingAction.hpp b/src/openrct2/actions/ParkSetResearchFundingAction.hpp index 641f564967..cde01cbce4 100644 --- a/src/openrct2/actions/ParkSetResearchFundingAction.hpp +++ b/src/openrct2/actions/ParkSetResearchFundingAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/actions/PauseToggleAction.hpp b/src/openrct2/actions/PauseToggleAction.hpp index a6a460f6e0..52d6bcd689 100644 --- a/src/openrct2/actions/PauseToggleAction.hpp +++ b/src/openrct2/actions/PauseToggleAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/actions/PeepPickupAction.hpp b/src/openrct2/actions/PeepPickupAction.hpp new file mode 100644 index 0000000000..dcabc0784b --- /dev/null +++ b/src/openrct2/actions/PeepPickupAction.hpp @@ -0,0 +1,212 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Input.h" +#include "../network/network.h" +#include "../world/Sprite.h" +#include "GameAction.h" + +enum class PeepPickupType : uint8_t +{ + Pickup, + Cancel, + Place, + Count +}; + +DEFINE_GAME_ACTION(PeepPickupAction, GAME_COMMAND_PICKUP_GUEST, GameActionResult) +{ +private: + uint8_t _type = static_cast(PeepPickupType::Count); + uint32_t _spriteId = SPRITE_INDEX_NULL; + CoordsXYZ _loc; + NetworkPlayerId_t _owner = { -1 }; + +public: + PeepPickupAction() = default; + PeepPickupAction(PeepPickupType type, uint32_t spriteId, CoordsXYZ loc, NetworkPlayerId_t owner) + : _type(static_cast(type)) + , _spriteId(spriteId) + , _loc(loc) + , _owner(owner) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_type) << DS_TAG(_spriteId) << DS_TAG(_loc) << DS_TAG(_owner); + } + + GameActionResult::Ptr Query() const override + { + if (_spriteId >= MAX_SPRITES || _spriteId == SPRITE_INDEX_NULL) + { + log_error("Failed to pick up peep for sprite %d", _spriteId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PERSON_HERE); + } + + Peep* const peep = GET_PEEP(_spriteId); + if (!peep || peep->sprite_identifier != SPRITE_IDENTIFIER_PEEP) + { + log_error("Failed to pick up peep for sprite %d", _spriteId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PERSON_HERE); + } + + auto res = MakeResult(); + + switch (static_cast(_type)) + { + case PeepPickupType::Pickup: + { + res->Position = { peep->x, peep->y, peep->z }; + if (!peep_can_be_picked_up(peep)) + { + return MakeResult(GA_ERROR::DISALLOWED, STR_ERR_CANT_PLACE_PERSON_HERE); + } + Peep* existing = network_get_pickup_peep(_owner); + if (existing) + { + // already picking up a peep + PeepPickupAction existingPickupAction{ + PeepPickupType::Cancel, existing->sprite_index, { network_get_pickup_peep_old_x(_owner), 0, 0 }, _owner + }; + auto result = GameActions::QueryNested(&existingPickupAction); + + if (existing == peep) + { + return result; + } + } + } + break; + case PeepPickupType::Cancel: + res->Position = { peep->x, peep->y, peep->z }; + break; + case PeepPickupType::Place: + res->Position = _loc; + if (network_get_pickup_peep(_owner) != peep) + { + return MakeResult(GA_ERROR::UNKNOWN, STR_ERR_CANT_PLACE_PERSON_HERE); + } + + if (!peep->Place({ _loc.x / 32, _loc.y / 32, _loc.z }, false)) + { + return MakeResult(GA_ERROR::UNKNOWN, STR_ERR_CANT_PLACE_PERSON_HERE, gGameCommandErrorText); + } + break; + default: + log_error("Invalid pickup type: %u", _type); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PERSON_HERE); + break; + } + return res; + } + + GameActionResult::Ptr Execute() const override + { + Peep* const peep = GET_PEEP(_spriteId); + if (!peep || peep->sprite_identifier != SPRITE_IDENTIFIER_PEEP) + { + log_error("Failed to pick up peep for sprite %d", _spriteId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PERSON_HERE); + } + + auto res = MakeResult(); + + switch (static_cast(_type)) + { + case PeepPickupType::Pickup: + { + res->Position = { peep->x, peep->y, peep->z }; + + Peep* existing = network_get_pickup_peep(_owner); + if (existing) + { + // already picking up a peep + PeepPickupAction existingPickupAction{ + PeepPickupType::Cancel, existing->sprite_index, { network_get_pickup_peep_old_x(_owner), 0, 0 }, _owner + }; + auto result = GameActions::ExecuteNested(&existingPickupAction); + + if (existing == peep) + { + return result; + } + if (_owner == network_get_current_player_id()) + { + // prevent tool_cancel() + input_set_flag(INPUT_FLAG_TOOL_ACTIVE, false); + } + } + + network_set_pickup_peep(_owner, peep); + network_set_pickup_peep_old_x(_owner, peep->x); + peep->Pickup(); + } + break; + case PeepPickupType::Cancel: + { + res->Position = { peep->x, peep->y, peep->z }; + + Peep* const pickedUpPeep = network_get_pickup_peep(_owner); + if (pickedUpPeep) + { + pickedUpPeep->PickupAbort(_loc.x); + } + + network_set_pickup_peep(_owner, nullptr); + } + break; + case PeepPickupType::Place: + res->Position = _loc; + if (!peep->Place({ _loc.x / 32, _loc.y / 32, _loc.z }, true)) + { + return MakeResult(GA_ERROR::UNKNOWN, STR_ERR_CANT_PLACE_PERSON_HERE, gGameCommandErrorText); + } + CancelConcurrentPickups(peep); + break; + default: + log_error("Invalid pickup type: %u", _type); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PERSON_HERE); + break; + } + return res; + } + +private: + void CancelConcurrentPickups(Peep * pickedPeep) const + { + // This part is only relevant in multiplayer games. + if (network_get_mode() == NETWORK_MODE_NONE) + return; + + // Not relevant for owner, owner gets to place it normally. + NetworkPlayerId_t currentPlayerId = network_get_current_player_id(); + if (currentPlayerId == _owner) + return; + + Peep* peep = network_get_pickup_peep(network_get_current_player_id()); + if (peep != pickedPeep) + return; + + // By assigning the peep to null before calling tool_cancel we can avoid + // resetting the peep to the initial position. + network_set_pickup_peep(currentPlayerId, nullptr); + tool_cancel(); + } +}; diff --git a/src/openrct2/actions/PlaceParkEntranceAction.hpp b/src/openrct2/actions/PlaceParkEntranceAction.hpp index ade56a24c0..9d45e2edda 100644 --- a/src/openrct2/actions/PlaceParkEntranceAction.hpp +++ b/src/openrct2/actions/PlaceParkEntranceAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -25,20 +25,14 @@ DEFINE_GAME_ACTION(PlaceParkEntranceAction, GAME_COMMAND_PLACE_PARK_ENTRANCE, GameActionResult) { private: - int16_t _x; - int16_t _y; - int16_t _z; - uint8_t _direction; + CoordsXYZD _loc; public: PlaceParkEntranceAction() { } - PlaceParkEntranceAction(int16_t x, int16_t y, int16_t z, int16_t direction) - : _x(x) - , _y(y) - , _z(z) - , _direction(direction) + PlaceParkEntranceAction(CoordsXYZD location) + : _loc(location) { } @@ -51,7 +45,7 @@ public: { GameAction::Serialise(stream); - stream << DS_TAG(_x) << DS_TAG(_y) << DS_TAG(_z) << DS_TAG(_direction); + stream << DS_TAG(_loc); } GameActionResult::Ptr Query() const override @@ -64,14 +58,14 @@ public: auto res = std::make_unique(); res->ExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; - res->Position = CoordsXYZ{ _x, _y, _z * 16 }; + res->Position = { _loc.x, _loc.y, _loc.z }; if (!map_check_free_elements_and_reorganise(3)) { return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_BUILD_PARK_ENTRANCE_HERE, STR_NONE); } - if (_x <= 32 || _y <= 32 || _x >= (gMapSizeUnits - 32) || _y >= (gMapSizeUnits - 32)) + if (_loc.x <= 32 || _loc.y <= 32 || _loc.x >= (gMapSizeUnits - 32) || _loc.y >= (gMapSizeUnits - 32)) { return std::make_unique( GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_PARK_ENTRANCE_HERE, STR_TOO_CLOSE_TO_EDGE_OF_MAP); @@ -83,20 +77,20 @@ public: GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_PARK_ENTRANCE_HERE, STR_ERR_TOO_MANY_PARK_ENTRANCES); } - int8_t zLow = _z * 2; + int8_t zLow = _loc.z / 8; int8_t zHigh = zLow + 12; - LocationXY16 entranceLoc = { _x, _y }; + LocationXY16 entranceLoc = { (int16_t)_loc.x, (int16_t)_loc.y }; for (uint8_t index = 0; index < 3; index++) { if (index == 1) { - entranceLoc.x += CoordsDirectionDelta[(_direction - 1) & 0x3].x; - entranceLoc.y += CoordsDirectionDelta[(_direction - 1) & 0x3].y; + entranceLoc.x += CoordsDirectionDelta[(_loc.direction - 1) & 0x3].x; + entranceLoc.y += CoordsDirectionDelta[(_loc.direction - 1) & 0x3].y; } else if (index == 2) { - entranceLoc.x += CoordsDirectionDelta[(_direction + 1) & 0x3].x * 2; - entranceLoc.y += CoordsDirectionDelta[(_direction + 1) & 0x3].y * 2; + entranceLoc.x += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].x * 2; + entranceLoc.y += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].y * 2; } if (!map_can_construct_at(entranceLoc.x, entranceLoc.y, zLow, zHigh, { 0b1111, 0 })) @@ -121,40 +115,38 @@ public: { auto res = std::make_unique(); res->ExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; - res->Position = CoordsXYZ{ _x, _y, _z * 16 }; + res->Position = CoordsXYZ{ _loc.x, _loc.y, _loc.z }; uint32_t flags = GetFlags(); CoordsXYZD parkEntrance; - parkEntrance.x = _x; - parkEntrance.y = _y; - parkEntrance.z = _z * 16; - parkEntrance.direction = _direction; + parkEntrance = _loc; + gParkEntrances.push_back(parkEntrance); - int8_t zLow = _z * 2; + int8_t zLow = _loc.z / 8; int8_t zHigh = zLow + 12; - CoordsXY entranceLoc = { _x, _y }; + CoordsXY entranceLoc = { _loc.x, _loc.y }; for (uint8_t index = 0; index < 3; index++) { if (index == 1) { - entranceLoc.x += CoordsDirectionDelta[(_direction - 1) & 0x3].x; - entranceLoc.y += CoordsDirectionDelta[(_direction - 1) & 0x3].y; + entranceLoc.x += CoordsDirectionDelta[(_loc.direction - 1) & 0x3].x; + entranceLoc.y += CoordsDirectionDelta[(_loc.direction - 1) & 0x3].y; } else if (index == 2) { - entranceLoc.x += CoordsDirectionDelta[(_direction + 1) & 0x3].x * 2; - entranceLoc.y += CoordsDirectionDelta[(_direction + 1) & 0x3].y * 2; + entranceLoc.x += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].x * 2; + entranceLoc.y += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].y * 2; } if (!(flags & GAME_COMMAND_FLAG_GHOST)) { - SurfaceElement* surfaceElement = map_get_surface_element_at(entranceLoc)->AsSurface(); + SurfaceElement* surfaceElement = map_get_surface_element_at(entranceLoc); surfaceElement->SetOwnership(OWNERSHIP_UNOWNED); } - TileElement* newElement = tile_element_insert(entranceLoc.x / 32, entranceLoc.y / 32, zLow, 0xF); + TileElement* newElement = tile_element_insert({ entranceLoc.x / 32, entranceLoc.y / 32, zLow }, 0b1111); Guard::Assert(newElement != nullptr); newElement->SetType(TILE_ELEMENT_TYPE_ENTRANCE); auto entranceElement = newElement->AsEntrance(); @@ -170,7 +162,7 @@ public: newElement->SetGhost(true); } - entranceElement->SetDirection(_direction); + entranceElement->SetDirection(_loc.direction); entranceElement->SetSequenceIndex(index); entranceElement->SetEntranceType(ENTRANCE_TYPE_PARK_ENTRANCE); entranceElement->SetPathType(gFootpathSelectedId); diff --git a/src/openrct2/actions/PlacePeepSpawnAction.hpp b/src/openrct2/actions/PlacePeepSpawnAction.hpp index 60c5269bde..58ac3c93ca 100644 --- a/src/openrct2/actions/PlacePeepSpawnAction.hpp +++ b/src/openrct2/actions/PlacePeepSpawnAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -19,8 +19,6 @@ #include "../world/Surface.h" #include "GameAction.h" -static int32_t _nextPeepSpawnIndex = 0; - DEFINE_GAME_ACTION(PlacePeepSpawnAction, GAME_COMMAND_PLACE_PEEP_SPAWN, GameActionResult) { private: @@ -71,22 +69,21 @@ public: GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE, STR_OFF_EDGE_OF_MAP); } - TileElement *mapElement, *surfaceMapElement; // Verify footpath exists at location, and retrieve coordinates - mapElement = map_get_path_element_at(_location.x >> 5, _location.y >> 5, _location.z / 8); - if (mapElement == nullptr) + auto pathElement = map_get_path_element_at({ _location.x >> 5, _location.y >> 5, _location.z / 8 }); + if (pathElement == nullptr) { return std::make_unique( GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE, STR_CAN_ONLY_BE_BUILT_ACROSS_PATHS); } // Verify location is unowned - surfaceMapElement = map_get_surface_element_at(_location.x >> 5, _location.y >> 5); + auto surfaceMapElement = map_get_surface_element_at(_location); if (surfaceMapElement == nullptr) { return std::make_unique(GA_ERROR::UNKNOWN, STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE, STR_NONE); } - if (surfaceMapElement->AsSurface()->GetOwnership() != OWNERSHIP_UNOWNED) + if (surfaceMapElement->GetOwnership() != OWNERSHIP_UNOWNED) { return std::make_unique( GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE, STR_ERR_MUST_BE_OUTSIDE_PARK_BOUNDARIES); @@ -101,17 +98,14 @@ public: res->ExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; res->Position = CoordsXYZ{ _location.x, _location.y, _location.z / 8 }; - // If we have reached our max peep spawns, use peep spawn next to last one set. - if (gPeepSpawns.size() >= MAX_PEEP_SPAWNS) + // If we have reached our max peep spawns, remove the oldest spawns + while (gPeepSpawns.size() >= MAX_PEEP_SPAWNS) { - auto peepSpawnIndex = _nextPeepSpawnIndex; - _nextPeepSpawnIndex = (peepSpawnIndex + 1) % (int32_t)gPeepSpawns.size(); - - // Before the new location is set, clear the old location - int32_t prevX = gPeepSpawns[peepSpawnIndex].x; - gPeepSpawns[peepSpawnIndex].x = PEEP_SPAWN_UNDEFINED; - - map_invalidate_tile_full(prevX, gPeepSpawns[peepSpawnIndex].y); + auto oldestSpawn = gPeepSpawns.begin(); + auto oldX = oldestSpawn->x; + auto oldY = oldestSpawn->y; + gPeepSpawns.erase(oldestSpawn); + map_invalidate_tile_full(oldX, oldY); } // Shift the spawn point to the middle of the tile diff --git a/src/openrct2/actions/PlayerKickAction.hpp b/src/openrct2/actions/PlayerKickAction.hpp new file mode 100644 index 0000000000..1478e9be9d --- /dev/null +++ b/src/openrct2/actions/PlayerKickAction.hpp @@ -0,0 +1,48 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../network/network.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(PlayerKickAction, GAME_COMMAND_KICK_PLAYER, GameActionResult) +{ +private: + NetworkPlayerId_t _playerId{ -1 }; + +public: + PlayerKickAction() = default; + + PlayerKickAction(NetworkPlayerId_t playerId) + : _playerId(playerId) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_playerId); + } + GameActionResult::Ptr Query() const override + { + return network_kick_player(_playerId, false); + } + + GameActionResult::Ptr Execute() const override + { + return network_kick_player(_playerId, true); + } +}; diff --git a/src/openrct2/actions/PlayerSetGroupAction.hpp b/src/openrct2/actions/PlayerSetGroupAction.hpp new file mode 100644 index 0000000000..688d26ab03 --- /dev/null +++ b/src/openrct2/actions/PlayerSetGroupAction.hpp @@ -0,0 +1,52 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../network/network.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(PlayerSetGroupAction, GAME_COMMAND_SET_PLAYER_GROUP, GameActionResult) +{ +private: + NetworkPlayerId_t _playerId{ -1 }; + uint8_t _groupId{ std::numeric_limits::max() }; + +public: + PlayerSetGroupAction() + { + } + + PlayerSetGroupAction(NetworkPlayerId_t playerId, uint8_t groupId) + : _playerId(playerId) + , _groupId(groupId) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_playerId) << DS_TAG(_groupId); + } + GameActionResult::Ptr Query() const override + { + return network_set_player_group(GetPlayer(), _playerId, _groupId, false); + } + + GameActionResult::Ptr Execute() const override + { + return network_set_player_group(GetPlayer(), _playerId, _groupId, true); + } +}; diff --git a/src/openrct2/actions/RideCreateAction.hpp b/src/openrct2/actions/RideCreateAction.hpp index ccab3c90af..e631af55d4 100644 --- a/src/openrct2/actions/RideCreateAction.hpp +++ b/src/openrct2/actions/RideCreateAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -30,7 +30,7 @@ class RideCreateGameActionResult final : public GameActionResult { public: RideCreateGameActionResult() - : GameActionResult(GA_ERROR::OK, 0) + : GameActionResult(GA_ERROR::OK, STR_NONE) { } RideCreateGameActionResult(GA_ERROR error, rct_string_id message) @@ -44,15 +44,14 @@ public: DEFINE_GAME_ACTION(RideCreateAction, GAME_COMMAND_CREATE_RIDE, RideCreateGameActionResult) { private: - int32_t _rideType; - int32_t _subType; - uint8_t _colour1; - uint8_t _colour2; + int32_t _rideType{ RIDE_ID_NULL }; + int32_t _subType{ RIDE_ENTRY_INDEX_NULL }; + uint8_t _colour1{ 0xFF }; + uint8_t _colour2{ 0xFF }; public: - RideCreateAction() - { - } + RideCreateAction() = default; + RideCreateAction(int32_t rideType, int32_t subType, int32_t colour1, int32_t colour2) : _rideType(rideType) , _subType(subType) @@ -75,53 +74,56 @@ public: GameActionResult::Ptr Query() const override { - ride_id_t rideIndex = ride_get_empty_slot(); + auto rideIndex = GetNextFreeRideId(); if (rideIndex == RIDE_ID_NULL) { // No more free slots available. - return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_RIDES); + return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_RIDES); } if (_rideType >= RIDE_TYPE_COUNT) { - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_INVALID_RIDE_TYPE); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_INVALID_RIDE_TYPE); } int32_t rideEntryIndex = ride_get_entry_index(_rideType, _subType); if (rideEntryIndex >= 128) { - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_INVALID_RIDE_TYPE); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_INVALID_RIDE_TYPE); } const track_colour_preset_list* colourPresets = &RideColourPresets[_rideType]; if (_colour1 >= colourPresets->count) { - // FIXME: Add new error string. - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_INVALID_RIDE_TYPE); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } rct_ride_entry* rideEntry = get_ride_entry(rideEntryIndex); + if (rideEntry == nullptr) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + vehicle_colour_preset_list* presetList = rideEntry->vehicle_preset_list; if ((presetList->count > 0 && presetList->count != 255) && _colour2 >= presetList->count) { - // FIXME: Add new error string. - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_INVALID_RIDE_TYPE); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } - return std::make_unique(); + return MakeResult(); } GameActionResult::Ptr Execute() const override { rct_ride_entry* rideEntry; - auto res = std::make_unique(); + auto res = MakeResult(); int32_t rideEntryIndex = ride_get_entry_index(_rideType, _subType); - ride_id_t rideIndex = ride_get_empty_slot(); + auto rideIndex = GetNextFreeRideId(); res->rideIndex = rideIndex; - auto ride = get_ride(rideIndex); + auto ride = GetOrAllocateRide(rideIndex); rideEntry = get_ride_entry(rideEntryIndex); if (rideEntry == nullptr) { @@ -134,18 +136,9 @@ public: ride->id = rideIndex; ride->type = _rideType; ride->subtype = rideEntryIndex; - ride_set_colour_preset(ride, _colour1); + ride->SetColourPreset(_colour1); ride->overall_view.xy = RCT_XY8_UNDEFINED; - - // Ride name - if (rideEntryIndex == RIDE_ENTRY_INDEX_NULL) - { - ride_set_name_to_track_default(ride, rideEntry); - } - else - { - ride_set_name_to_default(ride, rideEntry); - } + ride->SetNameToDefault(); for (int32_t i = 0; i < MAX_STATIONS; i++) { @@ -184,7 +177,7 @@ public: ride->lift_hill_speed = RideLiftData[ride->type].minimum_speed; - ride->measurement_index = 255; + ride->measurement = {}; ride->excitement = (ride_rating)-1; ride->cur_num_customers = 0; ride->num_customers_timeout = 0; @@ -206,11 +199,11 @@ public: } else { - ride->price = DefaultShopItemPrice[rideEntry->shop_item]; + ride->price = ShopItems[rideEntry->shop_item].DefaultPrice; } if (rideEntry->shop_item_secondary != SHOP_ITEM_NONE) { - ride->price_secondary = DefaultShopItemPrice[rideEntry->shop_item_secondary]; + ride->price_secondary = ShopItems[rideEntry->shop_item_secondary].DefaultPrice; } if (gScenarioObjectiveType == OBJECTIVE_BUILD_THE_BEST) @@ -301,7 +294,7 @@ public: ride->guests_favourite = 0; ride->num_circuits = 1; - ride->mode = ride_get_default_mode(ride); + ride->mode = ride->GetDefaultMode(); ride->min_max_cars_per_train = (rideEntry->min_cars_in_train << 4) | rideEntry->max_cars_in_train; ride_set_vehicle_colours_to_random_preset(ride, _colour2); window_invalidate_by_class(WC_RIDE_LIST); diff --git a/src/openrct2/actions/RideDemolishAction.hpp b/src/openrct2/actions/RideDemolishAction.hpp index 3d1a461b18..90a2c457b2 100644 --- a/src/openrct2/actions/RideDemolishAction.hpp +++ b/src/openrct2/actions/RideDemolishAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -45,6 +45,11 @@ public: { } + uint32_t GetCooldownTime() const override + { + return 1000; + } + void Serialise(DataSerialiser & stream) override { GameAction::Serialise(stream); @@ -54,14 +59,15 @@ public: GameActionResult::Ptr Query() const override { - Ride* ride = get_ride(_rideIndex); - if (ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command for ride %u", uint32_t(_rideIndex)); return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_DEMOLISH_RIDE, STR_NONE); } - if (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE && _modifyType == RIDE_MODIFY_DEMOLISH) + if (ride->lifecycle_flags & (RIDE_LIFECYCLE_INDESTRUCTIBLE | RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) + && _modifyType == RIDE_MODIFY_DEMOLISH) { return std::make_unique( GA_ERROR::NO_CLEARANCE, STR_CANT_DEMOLISH_RIDE, @@ -72,7 +78,7 @@ public: if (_modifyType == RIDE_MODIFY_RENEW) { - if (ride->status != RIDE_STATUS_CLOSED) + if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING) { return std::make_unique( GA_ERROR::DISALLOWED, STR_CANT_REFURBISH_RIDE, STR_MUST_BE_CLOSED_FIRST); @@ -99,8 +105,8 @@ public: GameActionResult::Ptr Execute() const override { - Ride* ride = get_ride(_rideIndex); - if (ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command for ride %u", uint32_t(_rideIndex)); return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_DEMOLISH_RIDE, STR_NONE); @@ -124,18 +130,19 @@ private: ride_clear_for_construction(ride); ride_remove_peeps(ride); - ride_stop_peeps_queuing(ride); + ride->StopGuestsQueuing(); sub_6CB945(ride); ride_clear_leftover_entrances(ride); news_item_disable_news(NEWS_ITEM_RIDE, _rideIndex); - for (auto& banner : gBanners) + for (BannerIndex i = 0; i < MAX_BANNERS; i++) { - if (banner.type != BANNER_NULL && banner.flags & BANNER_FLAG_LINKED_TO_RIDE && banner.ride_index == _rideIndex) + auto banner = GetBanner(i); + if (banner->type != BANNER_NULL && banner->flags & BANNER_FLAG_LINKED_TO_RIDE && banner->ride_index == _rideIndex) { - banner.flags &= 0xFB; - banner.string_idx = STR_DEFAULT_SIGN; + banner->flags &= ~BANNER_FLAG_LINKED_TO_RIDE; + banner->text = {}; } } @@ -223,10 +230,6 @@ private: } } - user_string_free(ride->name); - ride->type = RIDE_TYPE_NULL; - gParkValue = GetContext()->GetGameState()->GetPark().CalculateParkValue(); - auto res = std::make_unique(); res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; res->Cost = refundPrice; @@ -235,11 +238,14 @@ private: { int32_t x = (ride->overall_view.x * 32) + 16; int32_t y = (ride->overall_view.y * 32) + 16; - int32_t z = tile_element_height(x, y); + int32_t z = tile_element_height({ x, y }); res->Position = { x, y, z }; } + ride->Delete(); + gParkValue = GetContext()->GetGameState()->GetPark().CalculateParkValue(); + // Close windows related to the demolished ride if (!(GetFlags() & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED)) { @@ -262,7 +268,7 @@ private: money32 MazeRemoveTrack(uint16_t x, uint16_t y, uint16_t z, uint8_t direction) const { - auto setMazeTrack = MazeSetTrackAction(x, y, z, false, direction, _rideIndex, GC_SET_MAZE_TRACK_FILL); + auto setMazeTrack = MazeSetTrackAction(CoordsXYZD{ x, y, z, direction }, false, _rideIndex, GC_SET_MAZE_TRACK_FILL); setMazeTrack.SetFlags(GetFlags()); auto execRes = GameActions::ExecuteNested(&setMazeTrack); @@ -289,7 +295,7 @@ private: if (it.element->GetType() != TILE_ELEMENT_TYPE_TRACK) continue; - if (it.element->AsTrack()->GetRideIndex() != _rideIndex) + if (it.element->AsTrack()->GetRideIndex() != (ride_idnew_t)_rideIndex) continue; int32_t x = it.x * 32, y = it.y * 32; @@ -298,11 +304,11 @@ private: uint8_t rotation = it.element->GetDirection(); uint8_t type = it.element->AsTrack()->GetTrackType(); - if (type != TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP) + if (type != TRACK_ELEM_MAZE) { auto trackRemoveAction = TrackRemoveAction( type, it.element->AsTrack()->GetSequenceIndex(), { x, y, z, rotation }); - trackRemoveAction.SetFlags(GAME_COMMAND_FLAG_5); + trackRemoveAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND); auto removRes = GameActions::ExecuteNested(&trackRemoveAction); @@ -326,7 +332,7 @@ private: { 16, 0 }, }; - for (uint8_t dir = 0; dir < 4; dir++) + for (Direction dir : ALL_DIRECTIONS) { const LocationXY16& off = DirOffsets[dir]; money32 removePrice = MazeRemoveTrack(x + off.x, y + off.y, z, dir); @@ -349,7 +355,7 @@ private: res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; res->Cost = GetRefurbishPrice(ride); - ride_renew(ride); + ride->Renew(); ride->lifecycle_flags &= ~RIDE_LIFECYCLE_EVER_BEEN_OPENED; ride->last_crash_type = RIDE_CRASH_TYPE_NONE; @@ -360,7 +366,7 @@ private: { int32_t x = (ride->overall_view.x * 32) + 16; int32_t y = (ride->overall_view.y * 32) + 16; - int32_t z = tile_element_height(x, y); + int32_t z = tile_element_height({ x, y }); res->Position = { x, y, z }; } diff --git a/src/openrct2/actions/RideEntranceExitPlaceAction.hpp b/src/openrct2/actions/RideEntranceExitPlaceAction.hpp index 7c584689d2..94a470e304 100644 --- a/src/openrct2/actions/RideEntranceExitPlaceAction.hpp +++ b/src/openrct2/actions/RideEntranceExitPlaceAction.hpp @@ -60,14 +60,8 @@ public: return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, errorTitle); } - if (_rideIndex >= MAX_RIDES || _rideIndex == RIDE_ID_NULL) - { - log_warning("Invalid game command for ride %d", (int32_t)_rideIndex); - return MakeResult(GA_ERROR::INVALID_PARAMETERS, errorTitle); - } - - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command for ride %d", (int32_t)_rideIndex); return MakeResult(GA_ERROR::INVALID_PARAMETERS, errorTitle); @@ -79,7 +73,7 @@ public: return MakeResult(GA_ERROR::INVALID_PARAMETERS, errorTitle); } - if (ride->status != RIDE_STATUS_CLOSED) + if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING) { return MakeResult(GA_ERROR::NOT_CLOSED, errorTitle, STR_MUST_BE_CLOSED_FIRST); } @@ -89,9 +83,6 @@ public: return MakeResult(GA_ERROR::DISALLOWED, errorTitle, STR_NOT_ALLOWED_TO_MODIFY_STATION); } - ride_clear_for_construction(ride); - ride_remove_peeps(ride); - const auto location = _isExit ? ride_get_exit_location(ride, _stationNum) : ride_get_entrance_location(ride, _stationNum); @@ -111,7 +102,7 @@ public: auto z = ride->stations[_stationNum].Height * 8; gCommandPosition.z = z; - if (!gCheatsSandboxMode && !map_is_location_owned(_loc.x, _loc.y, z)) + if (!gCheatsSandboxMode && !map_is_location_owned({ _loc, z })) { return MakeResult(GA_ERROR::NOT_OWNED, errorTitle); } @@ -138,7 +129,7 @@ public: auto res = MakeResult(); res->Position.x = _loc.x + 16; res->Position.y = _loc.y + 16; - res->Position.z = tile_element_height(_loc.x, _loc.y); + res->Position.z = tile_element_height(_loc); res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; return res; } @@ -149,15 +140,18 @@ public: // When in known station num mode rideIndex is known and z is unknown auto errorTitle = _isExit ? STR_CANT_BUILD_MOVE_EXIT_FOR_THIS_RIDE_ATTRACTION : STR_CANT_BUILD_MOVE_ENTRANCE_FOR_THIS_RIDE_ATTRACTION; - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command for ride %d", (int32_t)_rideIndex); return MakeResult(GA_ERROR::INVALID_PARAMETERS, errorTitle); } - ride_clear_for_construction(ride); - ride_remove_peeps(ride); + if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST)) + { + ride_clear_for_construction(ride); + ride_remove_peeps(ride); + } const auto location = _isExit ? ride_get_exit_location(ride, _stationNum) : ride_get_entrance_location(ride, _stationNum); @@ -195,10 +189,10 @@ public: auto res = MakeResult(); res->Position.x = _loc.x + 16; res->Position.y = _loc.y + 16; - res->Position.z = tile_element_height(_loc.x, _loc.y); + res->Position.z = tile_element_height(_loc); res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; - TileElement* tileElement = tile_element_insert(_loc.x / 32, _loc.y / 32, z / 8, 0b1111); + TileElement* tileElement = tile_element_insert({ _loc.x / 32, _loc.y / 32, z / 8 }, 0b1111); assert(tileElement != nullptr); tileElement->SetType(TILE_ELEMENT_TYPE_ENTRANCE); tileElement->SetDirection(_direction); @@ -251,7 +245,7 @@ public: return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, errorTitle); } - if (!gCheatsSandboxMode && !map_is_location_owned(loc.x, loc.y, loc.z)) + if (!gCheatsSandboxMode && !map_is_location_owned(loc)) { return MakeResult(GA_ERROR::NOT_OWNED, errorTitle); } @@ -278,7 +272,7 @@ public: auto res = MakeResult(); res->Position.x = loc.x + 16; res->Position.y = loc.y + 16; - res->Position.z = tile_element_height(loc.x, loc.y); + res->Position.z = tile_element_height(loc); res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; return res; } diff --git a/src/openrct2/actions/RideEntranceExitRemoveAction.hpp b/src/openrct2/actions/RideEntranceExitRemoveAction.hpp index c13f1deaa3..22c9b07dac 100644 --- a/src/openrct2/actions/RideEntranceExitRemoveAction.hpp +++ b/src/openrct2/actions/RideEntranceExitRemoveAction.hpp @@ -47,20 +47,14 @@ public: GameActionResult::Ptr Query() const override { - if (_rideIndex >= MAX_RIDES || _rideIndex == RIDE_ID_NULL) - { - log_warning("Invalid game command for ride %d", int32_t(_rideIndex)); - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_NONE); - } - - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid ride id %d for entrance/exit removal", (int32_t)_rideIndex); return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } - if (ride->status != RIDE_STATUS_CLOSED) + if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING) { return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_MUST_BE_CLOSED_FIRST); } @@ -116,16 +110,19 @@ public: GameActionResult::Ptr Execute() const override { - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid ride id %d for entrance/exit removal", (int32_t)_rideIndex); return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } - ride_clear_for_construction(ride); - ride_remove_peeps(ride); - invalidate_test_results(ride); + if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST)) + { + ride_clear_for_construction(ride); + ride_remove_peeps(ride); + invalidate_test_results(ride); + } bool found = false; TileElement* tileElement = map_get_first_element_at(_loc.x / 32, _loc.y / 32); @@ -171,7 +168,7 @@ public: auto res = MakeResult(); res->Position.x = _loc.x + 16; res->Position.y = _loc.y + 16; - res->Position.z = tile_element_height(res->Position.x, res->Position.y); + res->Position.z = tile_element_height(res->Position); footpath_queue_chain_reset(); maze_entrance_hedge_replacement(_loc.x, _loc.y, tileElement); diff --git a/src/openrct2/actions/RideSetAppearanceAction.hpp b/src/openrct2/actions/RideSetAppearanceAction.hpp index 4f58296046..c54a994cc8 100644 --- a/src/openrct2/actions/RideSetAppearanceAction.hpp +++ b/src/openrct2/actions/RideSetAppearanceAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -68,14 +68,8 @@ public: GameActionResult::Ptr Query() const override { - if (_rideIndex >= MAX_RIDES || _rideIndex == RIDE_ID_NULL) - { - log_warning("Invalid game command for ride %u", uint32_t(_rideIndex)); - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_NONE); - } - - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command, ride_id = %u", uint32_t(_rideIndex)); return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_NONE); @@ -114,7 +108,7 @@ public: GameActionResult::Ptr Execute() const override { - Ride* ride = get_ride(_rideIndex); + auto ride = get_ride(_rideIndex); if (ride == nullptr) { log_warning("Invalid game command, ride_id = %u", uint32_t(_rideIndex)); @@ -169,7 +163,7 @@ public: { res->Position.x = ride->overall_view.x * 32 + 16; res->Position.y = ride->overall_view.y * 32 + 16; - res->Position.z = tile_element_height(res->Position.x, res->Position.y); + res->Position.z = tile_element_height(res->Position); } return res; diff --git a/src/openrct2/actions/RideSetColourScheme.hpp b/src/openrct2/actions/RideSetColourScheme.hpp index a91a0255d1..7f4bf91b80 100644 --- a/src/openrct2/actions/RideSetColourScheme.hpp +++ b/src/openrct2/actions/RideSetColourScheme.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -24,16 +24,14 @@ DEFINE_GAME_ACTION(RideSetColourSchemeAction, GAME_COMMAND_SET_COLOUR_SCHEME, GameActionResult) { private: - int32_t _x = 0, _y = 0, _z = 0, _direction = 0, _trackType = 0; + CoordsXYZD _loc{ 0, 0, 0, 0 }; + int32_t _trackType = 0; uint16_t _newColourScheme = 0; public: RideSetColourSchemeAction() = default; - RideSetColourSchemeAction(int32_t x, int32_t y, int32_t z, int32_t direction, int32_t trackType, uint16_t newColourScheme) - : _x(x) - , _y(y) - , _z(z) - , _direction(direction) + RideSetColourSchemeAction(CoordsXYZD location, int32_t trackType, uint16_t newColourScheme) + : _loc(location) , _trackType(trackType) , _newColourScheme(newColourScheme) { @@ -48,8 +46,7 @@ public: { GameAction::Serialise(stream); - stream << DS_TAG(_x) << DS_TAG(_y) << DS_TAG(_z) << DS_TAG(_direction) << DS_TAG(_trackType) - << DS_TAG(_newColourScheme); + stream << DS_TAG(_loc) << DS_TAG(_trackType) << DS_TAG(_newColourScheme); } GameActionResult::Ptr Query() const override @@ -63,8 +60,8 @@ public: res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; res->ErrorTitle = STR_CANT_SET_COLOUR_SCHEME; - int32_t x = _x, y = _y, z = _z; - sub_6C683D(&x, &y, &z, _direction, _trackType, _newColourScheme, nullptr, 4); + int32_t x = _loc.x, y = _loc.y, z = _loc.z; + sub_6C683D(&x, &y, &z, _loc.direction, _trackType, _newColourScheme, nullptr, 4); return res; } diff --git a/src/openrct2/actions/RideSetName.hpp b/src/openrct2/actions/RideSetName.hpp index d94f533d0f..a4cb2e6bcb 100644 --- a/src/openrct2/actions/RideSetName.hpp +++ b/src/openrct2/actions/RideSetName.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -52,8 +52,26 @@ public: GameActionResult::Ptr Query() const override { - Ride* ride = get_ride(_rideIndex); - if (ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) + { + log_warning("Invalid game command for ride %u", uint32_t(_rideIndex)); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_RENAME_RIDE_ATTRACTION, STR_NONE); + } + + if (!_name.empty() && Ride::NameExists(_name, ride->id)) + { + return std::make_unique( + GA_ERROR::INVALID_PARAMETERS, STR_CANT_RENAME_RIDE_ATTRACTION, STR_ERROR_EXISTING_NAME); + } + + return std::make_unique(); + } + + GameActionResult::Ptr Execute() const override + { + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command for ride %u", uint32_t(_rideIndex)); return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_RENAME_RIDE_ATTRACTION, STR_NONE); @@ -61,36 +79,12 @@ public: if (_name.empty()) { - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_CANT_RENAME_RIDE_ATTRACTION, STR_INVALID_RIDE_ATTRACTION_NAME); + ride->SetNameToDefault(); } - - rct_string_id newUserStringId = user_string_allocate( - USER_STRING_HIGH_ID_NUMBER | USER_STRING_DUPLICATION_PERMITTED, _name.c_str()); - if (newUserStringId == 0) + else { - // TODO: Probably exhausted, introduce new error. - return std::make_unique(GA_ERROR::UNKNOWN, STR_CANT_RENAME_RIDE_ATTRACTION, STR_NONE); + ride->custom_name = _name; } - user_string_free(newUserStringId); - - return std::make_unique(); - } - - GameActionResult::Ptr Execute() const override - { - rct_string_id newUserStringId = user_string_allocate( - USER_STRING_HIGH_ID_NUMBER | USER_STRING_DUPLICATION_PERMITTED, _name.c_str()); - - Ride* ride = get_ride(_rideIndex); - if (ride->type == RIDE_TYPE_NULL) - { - log_warning("Invalid game command for ride %u", uint32_t(_rideIndex)); - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_RENAME_RIDE_ATTRACTION, STR_NONE); - } - - user_string_free(ride->name); - ride->name = newUserStringId; scrolling_text_invalidate(); gfx_invalidate_screen(); @@ -103,7 +97,7 @@ public: auto res = std::make_unique(); res->Position.x = ride->overall_view.x * 32 + 16; res->Position.y = ride->overall_view.y * 32 + 16; - res->Position.z = tile_element_height(res->Position.x, res->Position.y); + res->Position.z = tile_element_height(res->Position); return res; } diff --git a/src/openrct2/actions/RideSetPriceAction.hpp b/src/openrct2/actions/RideSetPriceAction.hpp index bee8a205e8..cc21feef32 100644 --- a/src/openrct2/actions/RideSetPriceAction.hpp +++ b/src/openrct2/actions/RideSetPriceAction.hpp @@ -57,14 +57,8 @@ public: { GameActionResult::Ptr res = std::make_unique(); - if (_rideIndex >= MAX_RIDES || _rideIndex == RIDE_ID_NULL) - { - log_warning("Invalid game command for ride %u", uint32_t(_rideIndex)); - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); - } - - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command, ride_id = %u", uint32_t(_rideIndex)); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); @@ -85,8 +79,8 @@ public: GameActionResult::Ptr res = std::make_unique(); res->ExpenditureType = RCT_EXPENDITURE_TYPE_PARK_RIDE_TICKETS; - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command, ride_id = %u", uint32_t(_rideIndex)); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); @@ -103,7 +97,7 @@ public: { res->Position.x = ride->overall_view.x * 32 + 16; res->Position.y = ride->overall_view.y * 32 + 16; - res->Position.z = tile_element_height(res->Position.x, res->Position.y); + res->Position.z = tile_element_height(res->Position); } uint32_t shopItem; @@ -159,35 +153,42 @@ public: private: void RideSetCommonPrice(int32_t shopItem) const { - Ride* ride = get_ride(0); - for (uint8_t rideId = 0; rideId < MAX_RIDES; rideId++, ride++) + for (auto& ride : GetRideManager()) { - // Unplaced rides have a type of NULL - if (ride->type == RIDE_TYPE_NULL) - continue; - - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - - if (ride->type != RIDE_TYPE_TOILETS || shopItem != SHOP_ITEM_ADMISSION) + auto invalidate = false; + auto rideEntry = get_ride_entry(ride.subtype); + if (ride.type == RIDE_TYPE_TOILETS && shopItem == SHOP_ITEM_ADMISSION) { - if (rideEntry->shop_item == shopItem) + if (ride.price != _price) { - ride->price = _price; - window_invalidate_by_number(WC_RIDE, rideId); + ride.price = _price; + invalidate = true; } } - else + else if (rideEntry != nullptr && rideEntry->shop_item == shopItem) { - ride->price = _price; - window_invalidate_by_number(WC_RIDE, rideId); + if (ride.price != _price) + { + ride.price = _price; + invalidate = true; + } } - - // If the shop item is the same or an on-ride photo - if (rideEntry->shop_item_secondary == shopItem - || (rideEntry->shop_item_secondary == SHOP_ITEM_NONE && shop_item_is_photo(shopItem))) + if (rideEntry != nullptr) { - ride->price_secondary = _price; - window_invalidate_by_number(WC_RIDE, rideId); + // If the shop item is the same or an on-ride photo + if (rideEntry->shop_item_secondary == shopItem + || (rideEntry->shop_item_secondary == SHOP_ITEM_NONE && shop_item_is_photo(shopItem))) + { + if (ride.price_secondary != _price) + { + ride.price_secondary = _price; + invalidate = true; + } + } + } + if (invalidate) + { + window_invalidate_by_number(WC_RIDE, ride.id); } } } diff --git a/src/openrct2/actions/RideSetSetting.hpp b/src/openrct2/actions/RideSetSetting.hpp index 6478bde798..d7ca3da971 100644 --- a/src/openrct2/actions/RideSetSetting.hpp +++ b/src/openrct2/actions/RideSetSetting.hpp @@ -60,14 +60,8 @@ public: GameActionResult::Ptr Query() const override { - if (_rideIndex >= MAX_RIDES || _rideIndex < 0) - { - log_warning("Invalid game command for ride %d", int32_t(_rideIndex)); - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_CHANGE_OPERATING_MODE); - } - - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid ride: #%d.", (int32_t)_rideIndex); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_CHANGE_OPERATING_MODE); @@ -82,7 +76,7 @@ public: GA_ERROR::DISALLOWED, STR_CANT_CHANGE_OPERATING_MODE, STR_HAS_BROKEN_DOWN_AND_REQUIRES_FIXING); } - if (ride->status != RIDE_STATUS_CLOSED) + if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING) { return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_CHANGE_OPERATING_MODE, STR_MUST_BE_CLOSED_FIRST); } @@ -171,8 +165,8 @@ public: GameActionResult::Ptr Execute() const override { - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid ride: #%d.", (int32_t)_rideIndex); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_CHANGE_OPERATING_MODE); @@ -186,7 +180,8 @@ public: ride_remove_peeps(ride); ride->mode = _value; - ride_update_max_vehicles(ride); + ride->UpdateMaxVehicles(); + ride->UpdateNumberOfCircuits(); break; case RideSetSetting::Departure: ride->depart_flags = _value; @@ -243,6 +238,7 @@ public: break; case RideSetSetting::RideType: ride->type = _value; + gfx_invalidate_screen(); break; } @@ -251,7 +247,7 @@ public: { res->Position.x = ride->overall_view.x * 32 + 16; res->Position.y = ride->overall_view.y * 32 + 16; - res->Position.z = tile_element_height(res->Position.x, res->Position.y); + res->Position.z = tile_element_height(res->Position); } window_invalidate_by_number(WC_RIDE, _rideIndex); return res; diff --git a/src/openrct2/actions/RideSetStatus.hpp b/src/openrct2/actions/RideSetStatus.hpp index 09cd1246f1..8743a19184 100644 --- a/src/openrct2/actions/RideSetStatus.hpp +++ b/src/openrct2/actions/RideSetStatus.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -25,6 +25,7 @@ static rct_string_id _StatusErrorTitles[] = { STR_CANT_CLOSE, STR_CANT_OPEN, STR_CANT_TEST, + STR_CANT_SIMULATE, }; DEFINE_GAME_ACTION(RideSetStatusAction, GAME_COMMAND_SET_RIDE_STATUS, GameActionResult) @@ -58,24 +59,31 @@ public: GameActionResult::Ptr Query() const override { GameActionResult::Ptr res = std::make_unique(); - Ride* ride = get_ride(_rideIndex); - res->ErrorTitle = _StatusErrorTitles[_status]; - set_format_arg_on(res->ErrorMessageArgs.data(), 6, rct_string_id, ride->name); - set_format_arg_on(res->ErrorMessageArgs.data(), 8, uint32_t, ride->name_arguments); - if (_rideIndex >= MAX_RIDES || _rideIndex < 0) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command for ride %u", uint32_t(_rideIndex)); res->Error = GA_ERROR::INVALID_PARAMETERS; - res->ErrorMessage = STR_INVALID_SELECTION_OF_OBJECTS; + res->ErrorTitle = STR_RIDE_DESCRIPTION_UNKNOWN; + res->ErrorMessage = STR_NONE; return res; } + res->ErrorTitle = _StatusErrorTitles[_status]; + ride->FormatNameTo(res->ErrorMessageArgs.data() + 6); if (_status != ride->status) { - if (_status == RIDE_STATUS_TESTING) + if (_status == RIDE_STATUS_SIMULATING && (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)) { - if (!ride_is_valid_for_test(ride, _status == RIDE_STATUS_OPEN, 0)) + // Simulating will force clear the track, so make sure player can't cheat around a break down + res->Error = GA_ERROR::DISALLOWED; + res->ErrorMessage = STR_HAS_BROKEN_DOWN_AND_REQUIRES_FIXING; + return res; + } + else if (_status == RIDE_STATUS_TESTING || _status == RIDE_STATUS_SIMULATING) + { + if (!ride_is_valid_for_test(ride, _status, false)) { res->Error = GA_ERROR::UNKNOWN; res->ErrorMessage = gGameCommandErrorText; @@ -84,7 +92,7 @@ public: } else if (_status == RIDE_STATUS_OPEN) { - if (!ride_is_valid_for_open(ride, _status == RIDE_STATUS_OPEN, 0)) + if (!ride_is_valid_for_open(ride, _status == RIDE_STATUS_OPEN, false)) { res->Error = GA_ERROR::UNKNOWN; res->ErrorMessage = gGameCommandErrorText; @@ -100,30 +108,29 @@ public: GameActionResult::Ptr res = std::make_unique(); res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_RUNNING_COSTS; - Ride* ride = get_ride(_rideIndex); - res->ErrorTitle = _StatusErrorTitles[_status]; - set_format_arg_on(res->ErrorMessageArgs.data(), 6, rct_string_id, ride->name); - set_format_arg_on(res->ErrorMessageArgs.data(), 8, uint32_t, ride->name_arguments); - - if (ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command for ride %u", uint32_t(_rideIndex)); res->Error = GA_ERROR::INVALID_PARAMETERS; - res->ErrorMessage = STR_INVALID_SELECTION_OF_OBJECTS; + res->ErrorTitle = STR_RIDE_DESCRIPTION_UNKNOWN; + res->ErrorMessage = STR_NONE; return res; } + res->ErrorTitle = _StatusErrorTitles[_status]; + ride->FormatNameTo(res->ErrorMessageArgs.data() + 6); if (ride->overall_view.xy != RCT_XY8_UNDEFINED) { res->Position.x = ride->overall_view.x * 32 + 16; res->Position.y = ride->overall_view.y * 32 + 16; - res->Position.z = tile_element_height(res->Position.x, res->Position.y); + res->Position.z = tile_element_height(res->Position); } switch (_status) { case RIDE_STATUS_CLOSED: - if (ride->status == _status) + if (ride->status == _status || ride->status == RIDE_STATUS_SIMULATING) { if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)) { @@ -139,6 +146,29 @@ public: ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; window_invalidate_by_number(WC_RIDE, _rideIndex); break; + case RIDE_STATUS_SIMULATING: + { + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_CRASHED; + ride_clear_for_construction(ride); + ride_remove_peeps(ride); + + if (!ride_is_valid_for_test(ride, _status, true)) + { + res->Error = GA_ERROR::UNKNOWN; + res->ErrorMessage = gGameCommandErrorText; + return res; + } + + ride->status = _status; + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING; + ride->race_winner = SPRITE_INDEX_NULL; + ride->current_issues = 0; + ride->last_issue_time = 0; + ride_get_measurement(ride); + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; + window_invalidate_by_number(WC_RIDE, _rideIndex); + break; + } case RIDE_STATUS_TESTING: case RIDE_STATUS_OPEN: { @@ -147,6 +177,12 @@ public: return res; } + if (ride->status == RIDE_STATUS_SIMULATING) + { + ride_clear_for_construction(ride); + ride_remove_peeps(ride); + } + // Fix #3183: Make sure we close the construction window so the ride finishes any editing code before opening // otherwise vehicles get added to the ride incorrectly (such as to a ghost station) rct_window* constructionWindow = window_find_by_number(WC_RIDE_CONSTRUCTION, _rideIndex); @@ -157,14 +193,14 @@ public: if (_status == RIDE_STATUS_TESTING) { - if (!ride_is_valid_for_test(ride, _status == RIDE_STATUS_OPEN, 1)) + if (!ride_is_valid_for_test(ride, _status, true)) { res->Error = GA_ERROR::UNKNOWN; res->ErrorMessage = gGameCommandErrorText; return res; } } - else if (!ride_is_valid_for_open(ride, _status == RIDE_STATUS_OPEN, 1)) + else if (!ride_is_valid_for_open(ride, _status == RIDE_STATUS_OPEN, true)) { res->Error = GA_ERROR::UNKNOWN; res->ErrorMessage = gGameCommandErrorText; @@ -175,7 +211,7 @@ public: ride->status = _status; ride->current_issues = 0; ride->last_issue_time = 0; - ride_get_measurement(ride, nullptr); + ride_get_measurement(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; window_invalidate_by_number(WC_RIDE, _rideIndex); break; diff --git a/src/openrct2/actions/RideSetVehiclesAction.hpp b/src/openrct2/actions/RideSetVehiclesAction.hpp index 5af7cb2309..a05b0ced49 100644 --- a/src/openrct2/actions/RideSetVehiclesAction.hpp +++ b/src/openrct2/actions/RideSetVehiclesAction.hpp @@ -74,14 +74,8 @@ public: } auto errTitle = SetVehicleTypeErrorTitle[_type]; - if (_rideIndex >= MAX_RIDES || _rideIndex == RIDE_ID_NULL) - { - log_warning("Invalid game command for ride %u", uint32_t(_rideIndex)); - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, errTitle); - } - - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command, ride_id = %u", uint32_t(_rideIndex)); return std::make_unique(GA_ERROR::INVALID_PARAMETERS, errTitle); @@ -92,7 +86,7 @@ public: return std::make_unique(GA_ERROR::BROKEN, errTitle, STR_HAS_BROKEN_DOWN_AND_REQUIRES_FIXING); } - if (ride->status != RIDE_STATUS_CLOSED) + if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING) { return std::make_unique(GA_ERROR::NOT_CLOSED, errTitle, STR_MUST_BE_CLOSED_FIRST); } @@ -137,8 +131,8 @@ public: GameActionResult::Ptr Execute() const override { auto errTitle = SetVehicleTypeErrorTitle[_type]; - Ride* ride = get_ride(_rideIndex); - if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(_rideIndex); + if (ride == nullptr) { log_warning("Invalid game command, ride_id = %u", uint32_t(_rideIndex)); return std::make_unique(GA_ERROR::INVALID_PARAMETERS, errTitle); @@ -204,14 +198,14 @@ public: } ride->num_circuits = 1; - ride_update_max_vehicles(ride); + ride->UpdateMaxVehicles(); auto res = std::make_unique(); if (ride->overall_view.xy != RCT_XY8_UNDEFINED) { res->Position.x = ride->overall_view.x * 32 + 16; res->Position.y = ride->overall_view.y * 32 + 16; - res->Position.z = tile_element_height(res->Position.x, res->Position.y); + res->Position.z = tile_element_height(res->Position); } auto intent = Intent(INTENT_ACTION_RIDE_PAINT_RESET_VEHICLE); diff --git a/src/openrct2/actions/ScenarioSetSettingAction.hpp b/src/openrct2/actions/ScenarioSetSettingAction.hpp new file mode 100644 index 0000000000..230854a5d2 --- /dev/null +++ b/src/openrct2/actions/ScenarioSetSettingAction.hpp @@ -0,0 +1,294 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../OpenRCT2.h" +#include "../interface/Window.h" +#include "../management/Finance.h" +#include "../peep/Peep.h" +#include "../world/Park.h" +#include "GameAction.h" + +#include + +enum class ScenarioSetSetting : uint8_t +{ + NoMoney, + InitialCash, + InitialLoan, + MaximumLoanSize, + AnnualInterestRate, + ForbidMarketingCampaigns, + AverageCashPerGuest, + GuestInitialHappiness, + GuestInitialHunger, + GuestInitialThirst, + GuestsPreferLessIntenseRides, + GuestsPreferMoreIntenseRides, + CostToBuyLand, + CostToBuyConstructionRights, + ParkChargeMethod, + ParkChargeEntryFee, + ForbidTreeRemoval, + ForbidLandscapeChanges, + ForbidHighConstruction, + ParkRatingHigherDifficultyLevel, + GuestGenerationHigherDifficultyLevel, + Count +}; + +DEFINE_GAME_ACTION(ScenarioSetSettingAction, GAME_COMMAND_EDIT_SCENARIO_OPTIONS, GameActionResult) +{ +private: + uint8_t _setting{ static_cast(ScenarioSetSetting::Count) }; + uint32_t _value{ 0 }; + +public: + ScenarioSetSettingAction() + { + } + ScenarioSetSettingAction(ScenarioSetSetting setting, uint32_t value) + : _setting(static_cast(setting)) + , _value(value) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_setting) << DS_TAG(_value); + } + + GameActionResult::Ptr Query() const override + { + if (_setting >= static_cast(ScenarioSetSetting::Count)) + { + log_error("Invalid setting: %u", _setting); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + return MakeResult(); + } + + GameActionResult::Ptr Execute() const override + { + switch (static_cast(_setting)) + { + case ScenarioSetSetting::NoMoney: + if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + { + if (_value != 0) + { + gParkFlags |= PARK_FLAGS_NO_MONEY_SCENARIO; + } + else + { + gParkFlags &= ~PARK_FLAGS_NO_MONEY_SCENARIO; + } + } + else + { + if (_value != 0) + { + gParkFlags |= PARK_FLAGS_NO_MONEY; + } + else + { + gParkFlags &= ~PARK_FLAGS_NO_MONEY; + } + // Invalidate all windows that have anything to do with finance + window_invalidate_by_class(WC_RIDE); + window_invalidate_by_class(WC_PEEP); + window_invalidate_by_class(WC_PARK_INFORMATION); + window_invalidate_by_class(WC_FINANCES); + window_invalidate_by_class(WC_BOTTOM_TOOLBAR); + window_invalidate_by_class(WC_TOP_TOOLBAR); + } + break; + case ScenarioSetSetting::InitialCash: + gInitialCash = std::clamp(_value, MONEY(0, 00), MONEY(1000000, 00)); + gCash = gInitialCash; + window_invalidate_by_class(WC_FINANCES); + window_invalidate_by_class(WC_BOTTOM_TOOLBAR); + break; + case ScenarioSetSetting::InitialLoan: + gBankLoan = std::clamp(_value, MONEY(0, 00), MONEY(5000000, 00)); + gMaxBankLoan = std::max(gBankLoan, gMaxBankLoan); + window_invalidate_by_class(WC_FINANCES); + break; + case ScenarioSetSetting::MaximumLoanSize: + gMaxBankLoan = std::clamp(_value, MONEY(0, 00), MONEY(5000000, 00)); + gBankLoan = std::min(gBankLoan, gMaxBankLoan); + window_invalidate_by_class(WC_FINANCES); + break; + case ScenarioSetSetting::AnnualInterestRate: + gBankLoanInterestRate = std::clamp(_value, 0, 80); + window_invalidate_by_class(WC_FINANCES); + break; + case ScenarioSetSetting::ForbidMarketingCampaigns: + if (_value != 0) + { + gParkFlags |= PARK_FLAGS_FORBID_MARKETING_CAMPAIGN; + } + else + { + gParkFlags &= ~PARK_FLAGS_FORBID_MARKETING_CAMPAIGN; + } + break; + case ScenarioSetSetting::AverageCashPerGuest: + gGuestInitialCash = std::clamp(_value, MONEY(0, 00), MONEY(1000, 00)); + break; + case ScenarioSetSetting::GuestInitialHappiness: + gGuestInitialHappiness = std::clamp(_value, 40, 250); + break; + case ScenarioSetSetting::GuestInitialHunger: + gGuestInitialHunger = std::clamp(_value, 40, 250); + break; + case ScenarioSetSetting::GuestInitialThirst: + gGuestInitialThirst = std::clamp(_value, 40, 250); + break; + case ScenarioSetSetting::GuestsPreferLessIntenseRides: + if (_value != 0) + { + gParkFlags |= PARK_FLAGS_PREF_LESS_INTENSE_RIDES; + } + else + { + gParkFlags &= ~PARK_FLAGS_PREF_LESS_INTENSE_RIDES; + } + break; + case ScenarioSetSetting::GuestsPreferMoreIntenseRides: + if (_value != 0) + { + gParkFlags |= PARK_FLAGS_PREF_MORE_INTENSE_RIDES; + } + else + { + gParkFlags &= ~PARK_FLAGS_PREF_MORE_INTENSE_RIDES; + } + break; + case ScenarioSetSetting::CostToBuyLand: + gLandPrice = std::clamp(_value, MONEY(5, 00), MONEY(200, 00)); + break; + case ScenarioSetSetting::CostToBuyConstructionRights: + gConstructionRightsPrice = std::clamp(_value, MONEY(5, 00), MONEY(200, 00)); + break; + case ScenarioSetSetting::ParkChargeMethod: + if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + { + if (_value == 0) + { + gParkFlags |= PARK_FLAGS_PARK_FREE_ENTRY; + gParkFlags &= ~PARK_FLAGS_UNLOCK_ALL_PRICES; + gParkEntranceFee = MONEY(0, 00); + } + else if (_value == 1) + { + gParkFlags &= ~PARK_FLAGS_PARK_FREE_ENTRY; + gParkFlags &= ~PARK_FLAGS_UNLOCK_ALL_PRICES; + gParkEntranceFee = MONEY(10, 00); + } + else + { + gParkFlags |= PARK_FLAGS_PARK_FREE_ENTRY; + gParkFlags |= PARK_FLAGS_UNLOCK_ALL_PRICES; + gParkEntranceFee = MONEY(10, 00); + } + } + else + { + if (_value == 0) + { + gParkFlags |= PARK_FLAGS_PARK_FREE_ENTRY; + gParkFlags &= ~PARK_FLAGS_UNLOCK_ALL_PRICES; + } + else if (_value == 1) + { + gParkFlags &= ~PARK_FLAGS_PARK_FREE_ENTRY; + gParkFlags &= ~PARK_FLAGS_UNLOCK_ALL_PRICES; + } + else + { + gParkFlags |= PARK_FLAGS_PARK_FREE_ENTRY; + gParkFlags |= PARK_FLAGS_UNLOCK_ALL_PRICES; + } + window_invalidate_by_class(WC_PARK_INFORMATION); + window_invalidate_by_class(WC_RIDE); + } + break; + case ScenarioSetSetting::ParkChargeEntryFee: + gParkEntranceFee = std::clamp(_value, MONEY(0, 00), MAX_ENTRANCE_FEE); + window_invalidate_by_class(WC_PARK_INFORMATION); + break; + case ScenarioSetSetting::ForbidTreeRemoval: + if (_value != 0) + { + gParkFlags |= PARK_FLAGS_FORBID_TREE_REMOVAL; + } + else + { + gParkFlags &= ~PARK_FLAGS_FORBID_TREE_REMOVAL; + } + break; + case ScenarioSetSetting::ForbidLandscapeChanges: + if (_value != 0) + { + gParkFlags |= PARK_FLAGS_FORBID_LANDSCAPE_CHANGES; + } + else + { + gParkFlags &= ~PARK_FLAGS_FORBID_LANDSCAPE_CHANGES; + } + break; + case ScenarioSetSetting::ForbidHighConstruction: + if (_value != 0) + { + gParkFlags |= PARK_FLAGS_FORBID_HIGH_CONSTRUCTION; + } + else + { + gParkFlags &= ~PARK_FLAGS_FORBID_HIGH_CONSTRUCTION; + } + break; + case ScenarioSetSetting::ParkRatingHigherDifficultyLevel: + if (_value != 0) + { + gParkFlags |= PARK_FLAGS_DIFFICULT_PARK_RATING; + } + else + { + gParkFlags &= ~PARK_FLAGS_DIFFICULT_PARK_RATING; + } + break; + case ScenarioSetSetting::GuestGenerationHigherDifficultyLevel: + if (_value != 0) + { + gParkFlags |= PARK_FLAGS_DIFFICULT_GUEST_GENERATION; + } + else + { + gParkFlags &= ~PARK_FLAGS_DIFFICULT_GUEST_GENERATION; + } + break; + default: + log_error("Invalid setting: %u", _setting); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + break; + } + window_invalidate_by_class(WC_EDITOR_SCENARIO_OPTIONS); + return MakeResult(); + } +}; diff --git a/src/openrct2/actions/SetCheatAction.hpp b/src/openrct2/actions/SetCheatAction.hpp new file mode 100644 index 0000000000..53863244c8 --- /dev/null +++ b/src/openrct2/actions/SetCheatAction.hpp @@ -0,0 +1,796 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Cheats.h" +#include "../Context.h" +#include "../GameState.h" +#include "../config/Config.h" +#include "../core/String.hpp" +#include "../drawing/Drawing.h" +#include "../localisation/Localisation.h" +#include "../localisation/StringIds.h" +#include "../network/network.h" +#include "../ride/Ride.h" +#include "../scenario/Scenario.h" +#include "../ui/UiContext.h" +#include "../util/Util.h" +#include "../windows/Intent.h" +#include "../world/Banner.h" +#include "../world/Climate.h" +#include "../world/Footpath.h" +#include "../world/Map.h" +#include "../world/Park.h" +#include "../world/Scenery.h" +#include "../world/Sprite.h" +#include "../world/Surface.h" +#include "GameAction.h" +#include "ParkSetLoanAction.hpp" +#include "ParkSetParameterAction.hpp" + +DEFINE_GAME_ACTION(SetCheatAction, GAME_COMMAND_CHEAT, GameActionResult) +{ + using ParametersRange = std::pair, std::pair>; + +private: + NetworkCheatType_t _cheatType; + int32_t _param1 = 0; + int32_t _param2 = 0; + +public: + SetCheatAction() = default; + SetCheatAction(CheatType cheatType, int32_t param1 = 0, int32_t param2 = 0) + : _cheatType(static_cast(cheatType)) + , _param1(param1) + , _param2(param2) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + stream << DS_TAG(_cheatType) << DS_TAG(_param1) << DS_TAG(_param2); + } + + GameActionResult::Ptr Query() const override + { + if (static_cast(_cheatType) >= static_cast(CheatType::Count)) + { + MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + ParametersRange validRange = GetParameterRange(static_cast(_cheatType.id)); + + if (_param1 < validRange.first.first || _param1 > validRange.first.second) + { + MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + if (_param2 < validRange.second.first || _param2 > validRange.second.second) + { + MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + return MakeResult(); + } + + GameActionResult::Ptr Execute() const override + { + switch (static_cast(_cheatType.id)) + { + case CheatType::SandboxMode: + gCheatsSandboxMode = _param1 != 0; + window_invalidate_by_class(WC_MAP); + window_invalidate_by_class(WC_FOOTPATH); + break; + case CheatType::DisableClearanceChecks: + gCheatsDisableClearanceChecks = _param1 != 0; + break; + case CheatType::DisableSupportLimits: + gCheatsDisableSupportLimits = _param1 != 0; + break; + case CheatType::ShowAllOperatingModes: + gCheatsShowAllOperatingModes = _param1 != 0; + break; + case CheatType::ShowVehiclesFromOtherTrackTypes: + gCheatsShowVehiclesFromOtherTrackTypes = _param1 != 0; + break; + case CheatType::FastLiftHill: + gCheatsFastLiftHill = _param1 != 0; + break; + case CheatType::DisableBrakesFailure: + gCheatsDisableBrakesFailure = _param1 != 0; + break; + case CheatType::DisableAllBreakdowns: + gCheatsDisableAllBreakdowns = _param1 != 0; + break; + case CheatType::DisableTrainLengthLimit: + gCheatsDisableTrainLengthLimit = _param1 != 0; + break; + case CheatType::EnableChainLiftOnAllTrack: + gCheatsEnableChainLiftOnAllTrack = _param1 != 0; + break; + case CheatType::BuildInPauseMode: + gCheatsBuildInPauseMode = _param1 != 0; + break; + case CheatType::IgnoreRideIntensity: + gCheatsIgnoreRideIntensity = _param1 != 0; + break; + case CheatType::DisableVandalism: + gCheatsDisableVandalism = _param1 != 0; + break; + case CheatType::DisableLittering: + gCheatsDisableLittering = _param1 != 0; + break; + case CheatType::NoMoney: + SetScenarioNoMoney(_param1 != 0); + break; + case CheatType::AddMoney: + AddMoney(_param1); + break; + case CheatType::SetMoney: + SetMoney(_param1); + break; + case CheatType::ClearLoan: + ClearLoan(); + break; + case CheatType::SetGuestParameter: + SetGuestParameter(_param1, _param2); + break; + case CheatType::GenerateGuests: + GenerateGuests(_param1); + break; + case CheatType::RemoveAllGuests: + RemoveAllGuests(); + break; + case CheatType::ExplodeGuests: + ExplodeGuests(); + break; + case CheatType::GiveAllGuests: + GiveObjectToGuests(_param1); + break; + case CheatType::SetGrassLength: + SetGrassLength(_param1); + break; + case CheatType::WaterPlants: + WaterPlants(); + break; + case CheatType::FixVandalism: + FixVandalism(); + break; + case CheatType::RemoveLitter: + RemoveLitter(); + break; + case CheatType::DisablePlantAging: + gCheatsDisablePlantAging = _param1 != 0; + break; + case CheatType::SetStaffSpeed: + SetStaffSpeed(_param1); + break; + case CheatType::RenewRides: + RenewRides(); + break; + case CheatType::MakeDestructible: + MakeDestructible(); + break; + case CheatType::FixRides: + FixBrokenRides(); + break; + case CheatType::ResetCrashStatus: + ResetRideCrashStatus(); + break; + case CheatType::TenMinuteInspections: + Set10MinuteInspection(); + break; + case CheatType::WinScenario: + scenario_success(); + break; + case CheatType::ForceWeather: + climate_force_weather(_param1); + break; + case CheatType::FreezeWeather: + gCheatsFreezeWeather = _param1 != 0; + break; + case CheatType::NeverEndingMarketing: + gCheatsNeverendingMarketing = _param1 != 0; + break; + case CheatType::OpenClosePark: + ParkSetOpen(!park_is_open()); + break; + case CheatType::HaveFun: + gScenarioObjectiveType = OBJECTIVE_HAVE_FUN; + break; + case CheatType::SetForcedParkRating: + set_forced_park_rating(_param1); + break; + case CheatType::AllowArbitraryRideTypeChanges: + gCheatsAllowArbitraryRideTypeChanges = _param1 != 0; + window_invalidate_by_class(WC_RIDE); + break; + case CheatType::OwnAllLand: + OwnAllLand(); + break; + case CheatType::DisableRideValueAging: + gCheatsDisableRideValueAging = _param1 != 0; + break; + case CheatType::IgnoreResearchStatus: + gCheatsIgnoreResearchStatus = _param1 != 0; + break; + case CheatType::EnableAllDrawableTrackPieces: + gCheatsEnableAllDrawableTrackPieces = _param1 != 0; + break; + case CheatType::CreateDucks: + CreateDucks(_param1); + break; + case CheatType::RemoveDucks: + duck_remove_all(); + break; + default: + { + log_error("Unabled cheat: %d", _cheatType.id); + MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + break; + } + + if (network_get_mode() == NETWORK_MODE_NONE) + { + config_save_default(); + } + + window_invalidate_by_class(WC_CHEATS); + return MakeResult(); + } + +private: + ParametersRange GetParameterRange(CheatType cheatType) const + { + switch (static_cast(_cheatType.id)) + { + case CheatType::SandboxMode: + [[fallthrough]]; + case CheatType::DisableClearanceChecks: + [[fallthrough]]; + case CheatType::DisableSupportLimits: + [[fallthrough]]; + case CheatType::ShowAllOperatingModes: + [[fallthrough]]; + case CheatType::ShowVehiclesFromOtherTrackTypes: + [[fallthrough]]; + case CheatType::FastLiftHill: + [[fallthrough]]; + case CheatType::DisableBrakesFailure: + [[fallthrough]]; + case CheatType::DisableAllBreakdowns: + [[fallthrough]]; + case CheatType::DisableTrainLengthLimit: + [[fallthrough]]; + case CheatType::EnableChainLiftOnAllTrack: + [[fallthrough]]; + case CheatType::BuildInPauseMode: + [[fallthrough]]; + case CheatType::IgnoreRideIntensity: + [[fallthrough]]; + case CheatType::DisableVandalism: + [[fallthrough]]; + case CheatType::DisableLittering: + [[fallthrough]]; + case CheatType::NoMoney: + [[fallthrough]]; + case CheatType::DisablePlantAging: + [[fallthrough]]; + case CheatType::FreezeWeather: + [[fallthrough]]; + case CheatType::NeverEndingMarketing: + [[fallthrough]]; + case CheatType::AllowArbitraryRideTypeChanges: + [[fallthrough]]; + case CheatType::DisableRideValueAging: + [[fallthrough]]; + case CheatType::IgnoreResearchStatus: + [[fallthrough]]; + case CheatType::EnableAllDrawableTrackPieces: + [[fallthrough]]; + case CheatType::OpenClosePark: + return { { 0, 1 }, { 0, 0 } }; + case CheatType::AddMoney: + [[fallthrough]]; + case CheatType::SetMoney: + return { { std::numeric_limits::min(), std::numeric_limits::max() }, { 0, 0 } }; + case CheatType::SetGuestParameter: + switch (_param1) + { + case GUEST_PARAMETER_HAPPINESS: + return { { GUEST_PARAMETER_HAPPINESS, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY }, + { 0, PEEP_MAX_HAPPINESS } }; + case GUEST_PARAMETER_ENERGY: + return { { GUEST_PARAMETER_HAPPINESS, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY }, + { PEEP_MIN_ENERGY, PEEP_MAX_ENERGY } }; + case GUEST_PARAMETER_HUNGER: + return { { GUEST_PARAMETER_HAPPINESS, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY }, + { 0, PEEP_MAX_HUNGER } }; + case GUEST_PARAMETER_THIRST: + return { { GUEST_PARAMETER_HAPPINESS, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY }, + { 0, PEEP_MAX_THIRST } }; + case GUEST_PARAMETER_NAUSEA: + return { { GUEST_PARAMETER_HAPPINESS, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY }, + { 0, PEEP_MAX_NAUSEA } }; + case GUEST_PARAMETER_NAUSEA_TOLERANCE: + return { { GUEST_PARAMETER_HAPPINESS, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY }, + { PEEP_NAUSEA_TOLERANCE_NONE, PEEP_NAUSEA_TOLERANCE_HIGH } }; + case GUEST_PARAMETER_BATHROOM: + return { { GUEST_PARAMETER_HAPPINESS, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY }, + { 0, PEEP_MAX_BATHROOM } }; + case GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY: + return { { GUEST_PARAMETER_HAPPINESS, GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY }, { 0, 255 } }; + default: + return { { 0, 0 }, { 0, 0 } }; + } + case CheatType::GenerateGuests: + return { { 1, 10000 }, { 0, 0 } }; + case CheatType::GiveAllGuests: + return { { OBJECT_MONEY, OBJECT_UMBRELLA }, { 0, 0 } }; + case CheatType::SetGrassLength: + return { { 0, 7 }, { 0, 0 } }; + case CheatType::SetStaffSpeed: + return { { 0, 255 }, { 0, 0 } }; + case CheatType::ForceWeather: + return { { 0, 5 }, { 0, 0 } }; + case CheatType::SetForcedParkRating: + return { { 0, 999 }, { 0, 0 } }; + case CheatType::CreateDucks: + return { { 0, 100 }, { 0, 0 } }; + default: + return { { 0, 0 }, { 0, 0 } }; + } + } + + void SetGrassLength(int32_t length) const + { + int32_t x, y; + + for (y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) + { + for (x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) + { + auto surfaceElement = map_get_surface_element_at(x, y); + if (surfaceElement == nullptr) + continue; + + if (surfaceElement != nullptr && (surfaceElement->GetOwnership() & OWNERSHIP_OWNED) + && surfaceElement->GetWaterHeight() == 0 && surfaceElement->CanGrassGrow()) + { + surfaceElement->SetGrassLength(length); + } + } + } + + gfx_invalidate_screen(); + } + + void WaterPlants() const + { + tile_element_iterator it; + + tile_element_iterator_begin(&it); + do + { + if (it.element->GetType() == TILE_ELEMENT_TYPE_SMALL_SCENERY) + { + it.element->AsSmallScenery()->SetAge(0); + } + } while (tile_element_iterator_next(&it)); + + gfx_invalidate_screen(); + } + + void FixVandalism() const + { + tile_element_iterator it; + + tile_element_iterator_begin(&it); + do + { + if (it.element->GetType() != TILE_ELEMENT_TYPE_PATH) + continue; + + if (!(it.element)->AsPath()->HasAddition()) + continue; + + it.element->AsPath()->SetIsBroken(false); + } while (tile_element_iterator_next(&it)); + + gfx_invalidate_screen(); + } + + void RemoveLitter() const + { + rct_litter* litter; + uint16_t spriteIndex, nextSpriteIndex; + + for (spriteIndex = gSpriteListHead[SPRITE_LIST_LITTER]; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = nextSpriteIndex) + { + litter = &(get_sprite(spriteIndex)->litter); + nextSpriteIndex = litter->next; + sprite_remove((rct_sprite*)litter); + } + + tile_element_iterator it; + rct_scenery_entry* sceneryEntry; + + tile_element_iterator_begin(&it); + do + { + if (it.element->GetType() != TILE_ELEMENT_TYPE_PATH) + continue; + + if (!(it.element)->AsPath()->HasAddition()) + continue; + + sceneryEntry = it.element->AsPath()->GetAdditionEntry(); + if (sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BIN) + it.element->AsPath()->SetAdditionStatus(0xFF); + + } while (tile_element_iterator_next(&it)); + + gfx_invalidate_screen(); + } + + void FixBrokenRides() const + { + for (auto& ride : GetRideManager()) + { + if ((ride.mechanic_status != RIDE_MECHANIC_STATUS_FIXING) + && (ride.lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN))) + { + auto mechanic = ride_get_assigned_mechanic(&ride); + if (mechanic != nullptr) + { + mechanic->RemoveFromRide(); + } + + ride_fix_breakdown(&ride, 0); + ride.window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; + } + } + } + + void RenewRides() const + { + for (auto& ride : GetRideManager()) + { + ride.Renew(); + } + window_invalidate_by_class(WC_RIDE); + } + + void MakeDestructible() const + { + for (auto& ride : GetRideManager()) + { + ride.lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE; + ride.lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; + } + window_invalidate_by_class(WC_RIDE); + } + + void ResetRideCrashStatus() const + { + for (auto& ride : GetRideManager()) + { + // Reset crash status and history + ride.lifecycle_flags &= ~RIDE_LIFECYCLE_CRASHED; + ride.last_crash_type = RIDE_CRASH_TYPE_NONE; + } + window_invalidate_by_class(WC_RIDE); + } + + void Set10MinuteInspection() const + { + for (auto& ride : GetRideManager()) + { + // Set inspection interval to 10 minutes + ride.inspection_interval = RIDE_INSPECTION_EVERY_10_MINUTES; + } + window_invalidate_by_class(WC_RIDE); + } + + void SetScenarioNoMoney(bool enabled) const + { + if (enabled) + { + gParkFlags |= PARK_FLAGS_NO_MONEY; + } + else + { + gParkFlags &= ~PARK_FLAGS_NO_MONEY; + } + // Invalidate all windows that have anything to do with finance + window_invalidate_by_class(WC_RIDE); + window_invalidate_by_class(WC_PEEP); + window_invalidate_by_class(WC_PARK_INFORMATION); + window_invalidate_by_class(WC_FINANCES); + window_invalidate_by_class(WC_BOTTOM_TOOLBAR); + window_invalidate_by_class(WC_TOP_TOOLBAR); + window_invalidate_by_class(WC_CHEATS); + } + + void SetMoney(money32 amount) const + { + gCash = amount; + + window_invalidate_by_class(WC_FINANCES); + window_invalidate_by_class(WC_BOTTOM_TOOLBAR); + } + + void AddMoney(money32 amount) const + { + gCash = add_clamp_money32(gCash, amount); + + window_invalidate_by_class(WC_FINANCES); + window_invalidate_by_class(WC_BOTTOM_TOOLBAR); + } + + void ClearLoan() const + { + // First give money + AddMoney(gBankLoan); + + // Then pay the loan + auto gameAction = ParkSetLoanAction(MONEY(0, 00)); + GameActions::ExecuteNested(&gameAction); + } + + void GenerateGuests(int32_t count) const + { + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + for (int32_t i = 0; i < count; i++) + { + park.GenerateGuest(); + } + window_invalidate_by_class(WC_BOTTOM_TOOLBAR); + } + + void SetGuestParameter(int32_t parameter, int32_t value) const + { + int32_t spriteIndex; + Peep* p; + FOR_ALL_GUESTS (spriteIndex, p) + { + auto peep = p->AsGuest(); + assert(peep != nullptr); + switch (parameter) + { + case GUEST_PARAMETER_HAPPINESS: + peep->happiness = value; + peep->happiness_target = value; + // Clear the 'red-faced with anger' status if we're making the guest happy + if (value > 0) + { + peep->peep_flags &= ~PEEP_FLAGS_ANGRY; + peep->angriness = 0; + } + break; + case GUEST_PARAMETER_ENERGY: + peep->energy = value; + peep->energy_target = value; + break; + case GUEST_PARAMETER_HUNGER: + peep->hunger = value; + break; + case GUEST_PARAMETER_THIRST: + peep->thirst = value; + break; + case GUEST_PARAMETER_NAUSEA: + peep->nausea = value; + peep->nausea_target = value; + break; + case GUEST_PARAMETER_NAUSEA_TOLERANCE: + peep->nausea_tolerance = value; + break; + case GUEST_PARAMETER_BATHROOM: + peep->toilet = value; + break; + case GUEST_PARAMETER_PREFERRED_RIDE_INTENSITY: + peep->intensity = (15 << 4) | value; + break; + } + peep->UpdateSpriteType(); + } + } + + void GiveObjectToGuests(int32_t object) const + { + int32_t spriteIndex; + Peep* p; + FOR_ALL_GUESTS (spriteIndex, p) + { + auto peep = p->AsGuest(); + assert(peep != nullptr); + switch (object) + { + case OBJECT_MONEY: + peep->cash_in_pocket = MONEY(1000, 00); + break; + case OBJECT_PARK_MAP: + peep->item_standard_flags |= PEEP_ITEM_MAP; + break; + case OBJECT_BALLOON: + peep->item_standard_flags |= PEEP_ITEM_BALLOON; + peep->balloon_colour = scenario_rand_max(COLOUR_COUNT - 1); + peep->UpdateSpriteType(); + break; + case OBJECT_UMBRELLA: + peep->item_standard_flags |= PEEP_ITEM_UMBRELLA; + peep->umbrella_colour = scenario_rand_max(COLOUR_COUNT - 1); + peep->UpdateSpriteType(); + break; + } + } + window_invalidate_by_class(WC_PEEP); + } + + void RemoveAllGuests() const + { + uint16_t spriteIndex, nextSpriteIndex; + for (auto& ride : GetRideManager()) + { + ride.num_riders = 0; + + for (size_t stationIndex = 0; stationIndex < MAX_STATIONS; stationIndex++) + { + ride.stations[stationIndex].QueueLength = 0; + ride.stations[stationIndex].LastPeepInQueue = SPRITE_INDEX_NULL; + } + + for (auto trainIndex : ride.vehicles) + { + spriteIndex = trainIndex; + while (spriteIndex != SPRITE_INDEX_NULL) + { + auto vehicle = GET_VEHICLE(spriteIndex); + for (size_t i = 0, offset = 0; i < vehicle->num_peeps; i++) + { + while (vehicle->peep[i + offset] == SPRITE_INDEX_NULL) + { + offset++; + } + auto peep = GET_PEEP(vehicle->peep[i + offset]); + if (peep != nullptr) + { + vehicle->mass -= peep->mass; + } + } + + for (auto& peepInTrainIndex : vehicle->peep) + { + peepInTrainIndex = SPRITE_INDEX_NULL; + } + + vehicle->num_peeps = 0; + vehicle->next_free_seat = 0; + + spriteIndex = vehicle->next_vehicle_on_train; + } + } + } + + for (spriteIndex = gSpriteListHead[SPRITE_LIST_PEEP]; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = nextSpriteIndex) + { + auto peep = &(get_sprite(spriteIndex)->peep); + nextSpriteIndex = peep->next; + if (peep->type == PEEP_TYPE_GUEST) + { + peep->Remove(); + } + } + + window_invalidate_by_class(WC_RIDE); + gfx_invalidate_screen(); + } + + void ExplodeGuests() const + { + int32_t sprite_index; + Peep* peep; + + FOR_ALL_GUESTS (sprite_index, peep) + { + if (scenario_rand_max(6) == 0) + { + peep->peep_flags |= PEEP_FLAGS_EXPLODE; + } + } + } + + void SetStaffSpeed(uint8_t value) const + { + uint16_t spriteIndex; + Peep* peep; + + FOR_ALL_STAFF (spriteIndex, peep) + { + peep->energy = value; + peep->energy_target = value; + } + } + + void OwnAllLand() const + { + const int32_t min = 32; + const int32_t max = gMapSizeUnits - 32; + + for (CoordsXY coords = { min, min }; coords.y <= max; coords.y += 32) + { + for (coords.x = min; coords.x <= max; coords.x += 32) + { + auto* surfaceElement = map_get_surface_element_at(coords); + if (surfaceElement == nullptr) + continue; + + // Ignore already owned tiles. + if (surfaceElement->GetOwnership() & OWNERSHIP_OWNED) + continue; + + int32_t base_z = surfaceElement->base_height; + int32_t destOwnership = check_max_allowable_land_rights_for_tile(coords.x >> 5, coords.y >> 5, base_z); + + // only own tiles that were not set to 0 + if (destOwnership != OWNERSHIP_UNOWNED) + { + surfaceElement->SetOwnership(destOwnership); + update_park_fences_around_tile(coords); + uint16_t baseHeight = surfaceElement->base_height * 8; + map_invalidate_tile(coords.x, coords.y, baseHeight, baseHeight + 16); + } + } + } + + // Completely unown peep spawn points + for (const auto& spawn : gPeepSpawns) + { + int32_t x = spawn.x; + int32_t y = spawn.y; + if (x != PEEP_SPAWN_UNDEFINED) + { + auto* surfaceElement = map_get_surface_element_at({ x, y }); + surfaceElement->SetOwnership(OWNERSHIP_UNOWNED); + update_park_fences_around_tile({ x, y }); + uint16_t baseHeight = surfaceElement->base_height * 8; + map_invalidate_tile(x, y, baseHeight, baseHeight + 16); + } + } + + map_count_remaining_land_rights(); + } + + void ParkSetOpen(bool isOpen) const + { + auto parkSetParameter = ParkSetParameterAction(isOpen ? ParkParameter::Open : ParkParameter::Close); + GameActions::ExecuteNested(&parkSetParameter); + } + + void CreateDucks(int count) const + { + for (int i = 0; i < count; i++) + { + // 100 attempts at finding some water to create a few ducks at + for (int32_t attempts = 0; attempts < 100; attempts++) + { + if (scenario_create_ducks()) + break; + } + } + } +}; diff --git a/src/openrct2/actions/SetParkEntranceFeeAction.hpp b/src/openrct2/actions/SetParkEntranceFeeAction.hpp index 58e1d8cc9f..734bfa099a 100644 --- a/src/openrct2/actions/SetParkEntranceFeeAction.hpp +++ b/src/openrct2/actions/SetParkEntranceFeeAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/actions/SignSetNameAction.hpp b/src/openrct2/actions/SignSetNameAction.hpp index 15a3e3c820..74199cd44e 100644 --- a/src/openrct2/actions/SignSetNameAction.hpp +++ b/src/openrct2/actions/SignSetNameAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -53,46 +53,21 @@ public: log_warning("Invalid game command for setting sign name, banner id = %d", _bannerIndex); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } - - // Ensure user string space. - rct_string_id string_id = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, _name.c_str()); - if (string_id != 0) - { - user_string_free(string_id); - } - else - { - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_ERR_CANT_SET_BANNER_TEXT); - } - return MakeResult(); } GameActionResult::Ptr Execute() const override { - rct_banner* banner = &gBanners[_bannerIndex]; + auto banner = GetBanner(_bannerIndex); - int32_t x = banner->x << 5; - int32_t y = banner->y << 5; + int32_t x = banner->position.x << 5; + int32_t y = banner->position.y << 5; - if (_name.empty() == false) + if (!_name.empty()) { - rct_string_id string_id = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, _name.c_str()); - if (string_id != 0) - { - rct_string_id prev_string_id = banner->string_idx; - banner->string_idx = string_id; - user_string_free(prev_string_id); - - banner->flags &= ~(BANNER_FLAG_LINKED_TO_RIDE); - - scrolling_text_invalidate(); - gfx_invalidate_screen(); - } - else - { - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_ERR_CANT_SET_BANNER_TEXT); - } + banner->flags &= ~BANNER_FLAG_LINKED_TO_RIDE; + banner->ride_index = RIDE_ID_NULL; + banner->text = _name; } else { @@ -100,20 +75,20 @@ public: ride_id_t rideIndex = banner_get_closest_ride_index(x, y, 16); if (rideIndex == RIDE_ID_NULL) { - return MakeResult(); + banner->flags &= ~BANNER_FLAG_LINKED_TO_RIDE; + banner->ride_index = RIDE_ID_NULL; + banner->text = {}; + } + else + { + banner->flags |= BANNER_FLAG_LINKED_TO_RIDE; + banner->ride_index = rideIndex; + banner->text = {}; } - - banner->ride_index = rideIndex; - banner->flags |= BANNER_FLAG_LINKED_TO_RIDE; - - rct_string_id prev_string_id = banner->string_idx; - banner->string_idx = STR_DEFAULT_SIGN; - user_string_free(prev_string_id); - - scrolling_text_invalidate(); - gfx_invalidate_screen(); } + scrolling_text_invalidate(); + gfx_invalidate_screen(); return MakeResult(); } }; diff --git a/src/openrct2/actions/SignSetStyleAction.hpp b/src/openrct2/actions/SignSetStyleAction.hpp index dae643cf23..9f7c1fe2b1 100644 --- a/src/openrct2/actions/SignSetStyleAction.hpp +++ b/src/openrct2/actions/SignSetStyleAction.hpp @@ -57,9 +57,9 @@ public: return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } - rct_banner* banner = &gBanners[_bannerIndex]; + auto banner = GetBanner(_bannerIndex); - CoordsXY coords{ banner->x * 32, banner->y * 32 }; + CoordsXY coords{ banner->position.x * 32, banner->position.y * 32 }; if (_isLarge) { @@ -77,23 +77,9 @@ public: } else { - TileElement* tileElement = map_get_first_element_at(coords.x / 32, coords.y / 32); - bool wallFound = false; - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_WALL) - continue; + WallElement* wallElement = banner_get_scrolling_wall_tile_element(static_cast(_bannerIndex)); - rct_scenery_entry* scenery_entry = tileElement->AsWall()->GetEntry(); - if (scenery_entry->wall.scrolling_mode == SCROLLING_MODE_NONE) - continue; - if (tileElement->AsWall()->GetBannerIndex() != (BannerIndex)_bannerIndex) - continue; - wallFound = true; - break; - } while (!(tileElement++)->IsLastForTile()); - - if (!wallFound == false) + if (!wallElement) { log_warning("Invalid game command for setting sign style, banner id '%d' not found", _bannerIndex); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); @@ -105,9 +91,9 @@ public: GameActionResult::Ptr Execute() const override { - rct_banner* banner = &gBanners[_bannerIndex]; + auto banner = GetBanner(_bannerIndex); - CoordsXY coords{ banner->x * 32, banner->y * 32 }; + CoordsXY coords{ banner->position.x * 32, banner->position.y * 32 }; if (_isLarge) { @@ -121,10 +107,11 @@ public: } else { - TileElement* tileElement = map_get_first_element_at(coords.x / 32, coords.y / 32); - tileElement->AsWall()->SetPrimaryColour(_mainColour); - tileElement->AsWall()->SetSecondaryColour(_textColour); - map_invalidate_tile(coords.x, coords.y, tileElement->base_height * 8, tileElement->clearance_height * 8); + WallElement* wallElement = banner_get_scrolling_wall_tile_element(static_cast(_bannerIndex)); + + wallElement->SetPrimaryColour(_mainColour); + wallElement->SetSecondaryColour(_textColour); + map_invalidate_tile(coords.x, coords.y, wallElement->base_height * 8, wallElement->clearance_height * 8); } auto intent = Intent(INTENT_ACTION_UPDATE_BANNER); diff --git a/src/openrct2/actions/SmallSceneryPlaceAction.hpp b/src/openrct2/actions/SmallSceneryPlaceAction.hpp index 7cc9052de1..97ed65375d 100644 --- a/src/openrct2/actions/SmallSceneryPlaceAction.hpp +++ b/src/openrct2/actions/SmallSceneryPlaceAction.hpp @@ -27,7 +27,31 @@ #include "../world/TileElement.h" #include "GameAction.h" -DEFINE_GAME_ACTION(SmallSceneryPlaceAction, GAME_COMMAND_PLACE_SCENERY, GameActionResult) +class SmallSceneryPlaceActionResult final : public GameActionResult +{ +public: + SmallSceneryPlaceActionResult() + : GameActionResult(GA_ERROR::OK, STR_CANT_POSITION_THIS_HERE) + { + } + SmallSceneryPlaceActionResult(GA_ERROR error) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE) + { + } + SmallSceneryPlaceActionResult(GA_ERROR error, rct_string_id message) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE, message) + { + } + SmallSceneryPlaceActionResult(GA_ERROR error, rct_string_id message, uint8_t* args) + : GameActionResult(error, STR_CANT_POSITION_THIS_HERE, message, args) + { + } + + uint8_t GroundFlags{ 0 }; + TileElement* tileElement = nullptr; +}; + +DEFINE_GAME_ACTION(SmallSceneryPlaceAction, GAME_COMMAND_PLACE_SCENERY, SmallSceneryPlaceActionResult) { private: CoordsXYZD _loc; @@ -49,6 +73,11 @@ public: { } + uint32_t GetCooldownTime() const override + { + return 20; + } + uint16_t GetActionFlags() const override { return GameAction::GetActionFlags(); @@ -70,36 +99,39 @@ public: { supportsRequired = true; } - int32_t baseHeight = tile_element_height(_loc.x, _loc.y); + int32_t landHeight = tile_element_height(_loc); + int16_t waterHeight = tile_element_water_height(_loc); + + int32_t surfaceHeight = landHeight; // If on water - if (baseHeight & 0xFFFF0000) + if (waterHeight > 0) { - baseHeight >>= 16; + surfaceHeight = waterHeight; } - auto res = MakeResult(); + auto res = std::make_unique(); res->Position.x = _loc.x + 16; res->Position.y = _loc.y + 16; - res->Position.z = baseHeight; + res->Position.z = surfaceHeight; if (_loc.z != 0) { - baseHeight = _loc.z; - res->Position.z = baseHeight; + surfaceHeight = _loc.z; + res->Position.z = surfaceHeight; } if (!map_check_free_elements_and_reorganise(1)) { - return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); } if (!byte_9D8150 && (_loc.x > gMapSizeMaxXY || _loc.y > gMapSizeMaxXY)) { - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } rct_scenery_entry* sceneryEntry = get_small_scenery_entry(_sceneryType); if (sceneryEntry == nullptr) { - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } auto quadrant = _quadrant; @@ -127,12 +159,15 @@ public: x2 += ScenerySubTileOffsets[quadrant & 3].x - 1; y2 += ScenerySubTileOffsets[quadrant & 3].y - 1; } - baseHeight = tile_element_height(x2, y2); + landHeight = tile_element_height({ x2, y2 }); + waterHeight = tile_element_water_height({ x2, y2 }); + + surfaceHeight = landHeight; // If on water - if (baseHeight & 0xFFFF0000) + if (waterHeight > 0) { // base_height2 is now the water height - baseHeight >>= 16; + surfaceHeight = waterHeight; if (_loc.z == 0) { isOnWater = true; @@ -141,23 +176,23 @@ public: auto targetHeight = _loc.z; if (_loc.z == 0) { - targetHeight = baseHeight; + targetHeight = surfaceHeight; } if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode - && !map_is_location_owned(_loc.x, _loc.y, targetHeight)) + && !map_is_location_owned({ _loc.x, _loc.y, targetHeight })) { - return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK); + return std::make_unique(GA_ERROR::NOT_OWNED, STR_LAND_NOT_OWNED_BY_PARK); } - TileElement* surfaceElement = map_get_surface_element_at({ _loc.x, _loc.y }); + auto* surfaceElement = map_get_surface_element_at(_loc); - if (surfaceElement != nullptr && !gCheatsDisableClearanceChecks && surfaceElement->AsSurface()->GetWaterHeight() > 0) + if (surfaceElement != nullptr && !gCheatsDisableClearanceChecks && surfaceElement->GetWaterHeight() > 0) { - int32_t water_height = (surfaceElement->AsSurface()->GetWaterHeight() * 16) - 1; + int32_t water_height = (surfaceElement->GetWaterHeight() * 16) - 1; if (water_height > targetHeight) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CANT_BUILD_THIS_UNDERWATER); + return std::make_unique(GA_ERROR::DISALLOWED, STR_CANT_BUILD_THIS_UNDERWATER); } } @@ -165,23 +200,24 @@ public: { if (isOnWater) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_LAND); + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_LAND); } - if (surfaceElement != nullptr && surfaceElement->AsSurface()->GetWaterHeight() > 0) + if (surfaceElement != nullptr && surfaceElement->GetWaterHeight() > 0) { - if (static_cast((surfaceElement->AsSurface()->GetWaterHeight() * 16)) > targetHeight) + if (static_cast((surfaceElement->GetWaterHeight() * 16)) > targetHeight) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_LAND); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_LAND); } } } if (!gCheatsDisableClearanceChecks && (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_REQUIRE_FLAT_SURFACE)) && !supportsRequired - && !isOnWater && surfaceElement != nullptr && (surfaceElement->AsSurface()->GetSlope() != TILE_ELEMENT_SLOPE_FLAT)) + && !isOnWater && surfaceElement != nullptr && (surfaceElement->GetSlope() != TILE_ELEMENT_SLOPE_FLAT)) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_LEVEL_LAND_REQUIRED); + return std::make_unique(GA_ERROR::DISALLOWED, STR_LEVEL_LAND_REQUIRED); } if (!gCheatsDisableSupportLimits && !(scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_STACKABLE)) @@ -191,15 +227,15 @@ public: { if (surfaceElement != nullptr) { - if (surfaceElement->AsSurface()->GetWaterHeight() || (surfaceElement->base_height * 8) != targetHeight) + if (surfaceElement->GetWaterHeight() || (surfaceElement->base_height * 8) != targetHeight) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_LEVEL_LAND_REQUIRED); + return std::make_unique(GA_ERROR::DISALLOWED, STR_LEVEL_LAND_REQUIRED); } } } else { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_LAND); + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_LAND); } } @@ -247,10 +283,11 @@ public: _loc.x, _loc.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &clearCost, CREATE_CROSSING_MODE_NONE)) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, gGameCommandErrorText, gCommonFormatArgs); + return std::make_unique( + GA_ERROR::DISALLOWED, gGameCommandErrorText, gCommonFormatArgs); } - gSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); + res->GroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; res->Cost = (sceneryEntry->small_scenery.price * 10) + clearCost; @@ -265,26 +302,29 @@ public: { supportsRequired = true; } - int32_t baseHeight = tile_element_height(_loc.x, _loc.y); + int32_t landHeight = tile_element_height(_loc); + int16_t waterHeight = tile_element_water_height(_loc); + + int32_t surfaceHeight = landHeight; // If on water - if (baseHeight & 0xFFFF0000) + if (waterHeight > 0) { - baseHeight >>= 16; + surfaceHeight = waterHeight; } - auto res = MakeResult(); + auto res = std::make_unique(); res->Position.x = _loc.x + 16; res->Position.y = _loc.y + 16; - res->Position.z = baseHeight; + res->Position.z = surfaceHeight; if (_loc.z != 0) { - baseHeight = _loc.z; - res->Position.z = baseHeight; + surfaceHeight = _loc.z; + res->Position.z = surfaceHeight; } rct_scenery_entry* sceneryEntry = get_small_scenery_entry(_sceneryType); if (sceneryEntry == nullptr) { - return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } auto quadrant = _quadrant; @@ -312,17 +352,20 @@ public: x2 += ScenerySubTileOffsets[quadrant & 3].x - 1; y2 += ScenerySubTileOffsets[quadrant & 3].y - 1; } - baseHeight = tile_element_height(x2, y2); + landHeight = tile_element_height({ x2, y2 }); + waterHeight = tile_element_water_height({ x2, y2 }); + + surfaceHeight = landHeight; // If on water - if (baseHeight & 0xFFFF0000) + if (waterHeight > 0) { // base_height2 is now the water height - baseHeight >>= 16; + surfaceHeight = waterHeight; } auto targetHeight = _loc.z; if (_loc.z == 0) { - targetHeight = baseHeight; + targetHeight = surfaceHeight; } if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST)) @@ -378,17 +421,18 @@ public: _loc.x, _loc.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags() | GAME_COMMAND_FLAG_APPLY, &clearCost, CREATE_CROSSING_MODE_NONE)) { - return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, gGameCommandErrorText, gCommonFormatArgs); + return std::make_unique( + GA_ERROR::DISALLOWED, gGameCommandErrorText, gCommonFormatArgs); } - gSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); + res->GroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; res->Cost = (sceneryEntry->small_scenery.price * 10) + clearCost; - TileElement* newElement = tile_element_insert(_loc.x / 32, _loc.y / 32, zLow, quarterTile.GetBaseQuarterOccupied()); + TileElement* newElement = tile_element_insert({ _loc.x / 32, _loc.y / 32, zLow }, quarterTile.GetBaseQuarterOccupied()); assert(newElement != nullptr); - gSceneryTileElement = newElement; + res->tileElement = newElement; newElement->SetType(TILE_ELEMENT_TYPE_SMALL_SCENERY); newElement->SetDirection(_loc.direction); SmallSceneryElement* sceneryElement = newElement->AsSmallScenery(); @@ -412,7 +456,7 @@ public: map_invalidate_tile_full(_loc.x, _loc.y); if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_ANIMATED)) { - map_animation_create(2, _loc.x, _loc.y, sceneryElement->base_height); + map_animation_create(MAP_ANIMATION_TYPE_SMALL_SCENERY, _loc.x, _loc.y, sceneryElement->base_height); } return res; diff --git a/src/openrct2/actions/SmallSceneryRemoveAction.hpp b/src/openrct2/actions/SmallSceneryRemoveAction.hpp index 499099afb5..0b2c47b86a 100644 --- a/src/openrct2/actions/SmallSceneryRemoveAction.hpp +++ b/src/openrct2/actions/SmallSceneryRemoveAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -26,19 +26,15 @@ DEFINE_GAME_ACTION(SmallSceneryRemoveAction, GAME_COMMAND_REMOVE_SCENERY, GameActionResult) { private: - int16_t _x; - int16_t _y; - uint8_t _baseHeight; + CoordsXYZ _loc; uint8_t _quadrant; uint8_t _sceneryType; public: SmallSceneryRemoveAction() = default; - SmallSceneryRemoveAction(int16_t x, int16_t y, uint8_t baseHeight, uint8_t quadrant, uint8_t sceneryType) - : _x(x) - , _y(y) - , _baseHeight(baseHeight) + SmallSceneryRemoveAction(CoordsXYZ location, uint8_t quadrant, uint8_t sceneryType) + : _loc(location) , _quadrant(quadrant) , _sceneryType(sceneryType) { @@ -53,14 +49,14 @@ public: { GameAction::Serialise(stream); - stream << DS_TAG(_x) << DS_TAG(_y) << DS_TAG(_baseHeight) << DS_TAG(_quadrant) << DS_TAG(_sceneryType); + stream << DS_TAG(_loc) << DS_TAG(_quadrant) << DS_TAG(_sceneryType); } GameActionResult::Ptr Query() const override { GameActionResult::Ptr res = std::make_unique(); - if (!map_is_location_valid({ _x, _y })) + if (!map_is_location_valid(_loc)) { return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK); } @@ -73,16 +69,14 @@ public: res->Cost = entry->small_scenery.removal_price * 10; res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - res->Position.x = _x; - res->Position.y = _y; - res->Position.z = _baseHeight * 8; + res->Position = _loc; if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !gCheatsSandboxMode) { // Check if allowed to remove item if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) { - if (entry->small_scenery.height > 64) + if (scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_IS_TREE)) { res->Error = GA_ERROR::NO_CLEARANCE; res->ErrorTitle = STR_CANT_REMOVE_THIS; @@ -92,7 +86,7 @@ public: } // Check if the land is owned - if (!map_is_location_owned(_x, _y, _baseHeight * 8)) + if (!map_is_location_owned(_loc)) { res->Error = GA_ERROR::NO_CLEARANCE; res->ErrorTitle = STR_CANT_REMOVE_THIS; @@ -122,9 +116,7 @@ public: res->Cost = entry->small_scenery.removal_price * 10; res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - res->Position.x = _x; - res->Position.y = _y; - res->Position.z = _baseHeight * 8; + res->Position = _loc; TileElement* tileElement = FindSceneryElement(); if (tileElement == nullptr) @@ -132,9 +124,9 @@ public: return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS, STR_INVALID_SELECTION_OF_OBJECTS); } - res->Position.z = tile_element_height(res->Position.x, res->Position.y); + res->Position.z = tile_element_height(res->Position); - map_invalidate_tile_full(_x, _y); + map_invalidate_tile_full(_loc.x, _loc.y); tile_element_remove(tileElement); return res; @@ -143,7 +135,7 @@ public: private: TileElement* FindSceneryElement() const { - TileElement* tileElement = map_get_first_element_at(_x / 32, _y / 32); + TileElement* tileElement = map_get_first_element_at(_loc.x / 32, _loc.y / 32); if (!tileElement) return nullptr; @@ -153,7 +145,7 @@ private: continue; if ((tileElement->AsSmallScenery()->GetSceneryQuadrant()) != _quadrant) continue; - if (tileElement->base_height != _baseHeight) + if (tileElement->base_height != _loc.z / 8) continue; if (tileElement->AsSmallScenery()->GetEntryIndex() != _sceneryType) continue; diff --git a/src/openrct2/actions/SmallScenerySetColourAction.hpp b/src/openrct2/actions/SmallScenerySetColourAction.hpp new file mode 100644 index 0000000000..07e2bc4e1e --- /dev/null +++ b/src/openrct2/actions/SmallScenerySetColourAction.hpp @@ -0,0 +1,116 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Cheats.h" +#include "../OpenRCT2.h" +#include "../common.h" +#include "../core/MemoryStream.h" +#include "../interface/Window.h" +#include "../localisation/Localisation.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../ride/Ride.h" +#include "../ride/TrackDesign.h" +#include "../world/MapAnimation.h" +#include "../world/Park.h" +#include "../world/SmallScenery.h" +#include "../world/Sprite.h" +#include "../world/Surface.h" +#include "../world/TileElement.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(SmallScenerySetColourAction, GAME_COMMAND_SET_SCENERY_COLOUR, GameActionResult) +{ +private: + CoordsXYZ _loc; + uint8_t _quadrant; + uint8_t _sceneryType; + uint8_t _primaryColour; + uint8_t _secondaryColour; + +public: + SmallScenerySetColourAction() = default; + + SmallScenerySetColourAction( + CoordsXYZ loc, uint8_t quadrant, uint8_t sceneryType, uint8_t primaryColour, uint8_t secondaryColour) + : _loc(loc) + , _quadrant(quadrant) + , _sceneryType(sceneryType) + , _primaryColour(primaryColour) + , _secondaryColour(secondaryColour) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_loc) << DS_TAG(_quadrant) << DS_TAG(_sceneryType) << DS_TAG(_primaryColour) + << DS_TAG(_secondaryColour); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = _loc.z; + res->ErrorTitle = STR_CANT_REPAINT_THIS; + + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) + { + if (!map_is_location_owned(_loc)) + { + return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_REPAINT_THIS, STR_LAND_NOT_OWNED_BY_PARK); + } + } + + auto sceneryElement = map_get_small_scenery_element_at(_loc.x, _loc.y, _loc.z / 8, _sceneryType, _quadrant); + + if (sceneryElement == nullptr) + { + log_error("Small scenery not found at: x = %d, y = %d, z = %d", _loc.x, _loc.y, _loc.z); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + if ((GetFlags() & GAME_COMMAND_FLAG_GHOST) && !(sceneryElement->IsGhost())) + { + return res; + } + + if (isExecuting) + { + sceneryElement->SetPrimaryColour(_primaryColour); + sceneryElement->SetSecondaryColour(_secondaryColour); + + map_invalidate_tile_full(_loc.x, _loc.y); + } + + return res; + } +}; diff --git a/src/openrct2/actions/StaffFireAction.hpp b/src/openrct2/actions/StaffFireAction.hpp new file mode 100644 index 0000000000..49e1f70f05 --- /dev/null +++ b/src/openrct2/actions/StaffFireAction.hpp @@ -0,0 +1,72 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../interface/Window.h" +#include "../peep/Peep.h" +#include "../world/Sprite.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(StaffFireAction, GAME_COMMAND_FIRE_STAFF_MEMBER, GameActionResult) +{ +private: + uint16_t _spriteId{ SPRITE_INDEX_NULL }; + +public: + StaffFireAction() + { + } + StaffFireAction(uint16_t spriteId) + : _spriteId(spriteId) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + stream << DS_TAG(_spriteId); + } + + GameActionResult::Ptr Query() const override + { + if (_spriteId >= MAX_SPRITES) + { + log_error("Invalid spriteId. spriteId = %u", _spriteId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + auto peep = GET_PEEP(_spriteId); + if (peep == nullptr || peep->sprite_identifier != SPRITE_IDENTIFIER_PEEP || peep->type != PEEP_TYPE_STAFF) + { + log_error("Invalid spriteId. spriteId = %u", _spriteId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + return MakeResult(); + } + + GameActionResult::Ptr Execute() const override + { + auto peep = GET_PEEP(_spriteId); + if (peep == nullptr || peep->sprite_identifier != SPRITE_IDENTIFIER_PEEP || peep->type != PEEP_TYPE_STAFF) + { + log_error("Invalid spriteId. spriteId = %u", _spriteId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + window_close_by_class(WC_FIRE_PROMPT); + peep_sprite_remove(peep); + return MakeResult(); + } +}; diff --git a/src/openrct2/actions/StaffHireNewAction.hpp b/src/openrct2/actions/StaffHireNewAction.hpp new file mode 100644 index 0000000000..c1c290be35 --- /dev/null +++ b/src/openrct2/actions/StaffHireNewAction.hpp @@ -0,0 +1,340 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Cheats.h" +#include "../Context.h" +#include "../core/MemoryStream.h" +#include "../drawing/Drawing.h" +#include "../interface/Window.h" +#include "../localisation/Localisation.h" +#include "../localisation/StringIds.h" +#include "../management/Finance.h" +#include "../ride/Ride.h" +#include "../scenario/Scenario.h" +#include "../ui/UiContext.h" +#include "../ui/WindowManager.h" +#include "../world/Entrance.h" +#include "../world/Park.h" +#include "../world/Sprite.h" +#include "GameAction.h" + +/* rct2: 0x009929FC */ +static constexpr const PeepSpriteType spriteTypes[] = { + PEEP_SPRITE_TYPE_HANDYMAN, + PEEP_SPRITE_TYPE_MECHANIC, + PEEP_SPRITE_TYPE_SECURITY, + PEEP_SPRITE_TYPE_ENTERTAINER_PANDA, +}; + +class StaffHireNewActionResult final : public GameActionResult +{ +public: + StaffHireNewActionResult() + : GameActionResult(GA_ERROR::OK, STR_CANT_HIRE_NEW_STAFF) + { + } + StaffHireNewActionResult(GA_ERROR error, rct_string_id message) + : GameActionResult(error, STR_CANT_HIRE_NEW_STAFF, message) + { + } + + uint32_t peepSriteIndex = SPRITE_INDEX_NULL; +}; + +DEFINE_GAME_ACTION(StaffHireNewAction, GAME_COMMAND_HIRE_NEW_STAFF_MEMBER, StaffHireNewActionResult) +{ +private: + bool _autoPosition = false; + uint8_t _staffType = STAFF_TYPE::STAFF_TYPE_COUNT; + uint8_t _entertainerType = ENTERTAINER_COSTUME::ENTERTAINER_COSTUME_COUNT; + uint32_t _staffOrders = 0; + +public: + StaffHireNewAction() = default; + StaffHireNewAction(bool autoPosition, STAFF_TYPE staffType, ENTERTAINER_COSTUME entertainerType, uint32_t staffOrders) + : _autoPosition(autoPosition) + , _staffType(staffType) + , _entertainerType(entertainerType) + , _staffOrders(staffOrders) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_autoPosition) << DS_TAG(_staffType) << DS_TAG(_entertainerType) << DS_TAG(_staffOrders); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool execute) const + { + auto res = std::make_unique(); + + res->ExpenditureType = RCT_EXPENDITURE_TYPE_WAGES; + + if (_staffType >= STAFF_TYPE_COUNT) + { + // Invalid staff type. + log_error("Tried to use invalid staff type: %u", (uint32_t)_staffType); + + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + if (gSpriteListCount[SPRITE_LIST_FREE] < 400) + { + return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_PEOPLE_IN_GAME); + } + + if (_staffType == STAFF_TYPE_ENTERTAINER) + { + if (_entertainerType >= ENTERTAINER_COSTUME_COUNT) + { + // Invalid entertainer costume + log_error("Tried to use invalid entertainer type: %u", (uint32_t)_entertainerType); + + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + uint32_t availableCostumes = staff_get_available_entertainer_costumes(); + if (!(availableCostumes & (1 << _entertainerType))) + { + // Entertainer costume unavailable + log_error("Tried to use unavailable entertainer type: %u", (uint32_t)_entertainerType); + + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + } + + // Look for a free slot in the staff modes. + int32_t staffIndex; + for (staffIndex = 0; staffIndex < STAFF_MAX_COUNT; ++staffIndex) + { + if (!(gStaffModes[staffIndex] & 1)) + break; + } + + if (staffIndex == STAFF_MAX_COUNT) + { + // Too many staff members exist already. + return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_STAFF_IN_GAME); + } + + Peep* newPeep = &(create_sprite(SPRITE_IDENTIFIER_PEEP)->peep); + if (newPeep == nullptr) + { + // Too many peeps exist already. + return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_TOO_MANY_PEOPLE_IN_GAME); + } + + if (execute == false) + { + // In query we just want to see if we can obtain a sprite slot. + sprite_remove((rct_sprite*)newPeep); + } + else + { + move_sprite_to_list((rct_sprite*)newPeep, SPRITE_LIST_PEEP); + + newPeep->sprite_identifier = 1; + newPeep->window_invalidate_flags = 0; + newPeep->action = PEEP_ACTION_NONE_2; + newPeep->special_sprite = 0; + newPeep->action_sprite_image_offset = 0; + newPeep->no_action_frame_num = 0; + newPeep->action_sprite_type = PEEP_ACTION_SPRITE_TYPE_NONE; + newPeep->path_check_optimisation = 0; + newPeep->type = PEEP_TYPE_STAFF; + newPeep->outside_of_park = 0; + newPeep->peep_flags = 0; + newPeep->paid_to_enter = 0; + newPeep->paid_on_rides = 0; + newPeep->paid_on_food = 0; + newPeep->paid_on_souvenirs = 0; + newPeep->staff_orders = _staffOrders; + + uint16_t idSearchSpriteIndex; + Peep* idSearchPeep; + + // We search for the first available id for a given staff type + uint32_t newStaffId = 0; + for (;;) + { + bool found = false; + ++newStaffId; + + FOR_ALL_STAFF (idSearchSpriteIndex, idSearchPeep) + { + if (idSearchPeep->staff_type != _staffType) + continue; + + if (idSearchPeep->id == newStaffId) + { + found = true; + break; + } + } + + if (!found) + break; + } + + newPeep->id = newStaffId; + newPeep->staff_type = _staffType; + + PeepSpriteType spriteType = spriteTypes[_staffType]; + if (_staffType == STAFF_TYPE_ENTERTAINER) + { + spriteType = static_cast(PEEP_SPRITE_TYPE_ENTERTAINER_PANDA + _entertainerType); + } + newPeep->name = nullptr; + newPeep->sprite_type = spriteType; + + const rct_sprite_bounds* spriteBounds = g_peep_animation_entries[spriteType].sprite_bounds; + newPeep->sprite_width = spriteBounds->sprite_width; + newPeep->sprite_height_negative = spriteBounds->sprite_height_negative; + newPeep->sprite_height_positive = spriteBounds->sprite_height_positive; + + if (_autoPosition) + { + AutoPositionNewStaff(newPeep); + } + else + { + // NOTE: This state is required for the window to act. + newPeep->state = PEEP_STATE_PICKED; + + sprite_move(newPeep->x, newPeep->y, newPeep->z, (rct_sprite*)newPeep); + invalidate_sprite_2((rct_sprite*)newPeep); + } + + // Staff uses this + newPeep->time_in_park = gDateMonthsElapsed; + newPeep->pathfind_goal.x = 0xFF; + newPeep->pathfind_goal.y = 0xFF; + newPeep->pathfind_goal.z = 0xFF; + newPeep->pathfind_goal.direction = 0xFF; + + uint8_t colour = staff_get_colour(_staffType); + newPeep->tshirt_colour = colour; + newPeep->trousers_colour = colour; + + // Staff energy determines their walking speed + newPeep->energy = 0x60; + newPeep->energy_target = 0x60; + newPeep->staff_mowing_timeout = 0; + + peep_update_name_sort(newPeep); + + newPeep->staff_id = staffIndex; + + gStaffModes[staffIndex] = STAFF_MODE_WALK; + + for (int32_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++) + { + gStaffPatrolAreas[staffIndex * STAFF_PATROL_AREA_SIZE + i] = 0; + } + + res->peepSriteIndex = newPeep->sprite_index; + } + + return res; + } + + void AutoPositionNewStaff(Peep * newPeep) const + { + // Find a location to place new staff member + + newPeep->state = PEEP_STATE_FALLING; + + int16_t x, y, z; + uint32_t count = 0; + uint16_t sprite_index; + Peep* guest = nullptr; + PathElement* guest_tile = nullptr; + + // Count number of walking guests + FOR_ALL_GUESTS (sprite_index, guest) + { + if (guest->state == PEEP_STATE_WALKING) + { + // Check the walking guest's tile. Only count them if they're on a path tile. + guest_tile = map_get_path_element_at({ guest->next_x / 32, guest->next_y / 32, guest->next_z }); + if (guest_tile != nullptr) + ++count; + } + } + + if (count > 0) + { + // Place staff at a random guest + uint32_t rand = scenario_rand_max(count); + FOR_ALL_GUESTS (sprite_index, guest) + { + if (guest->state == PEEP_STATE_WALKING) + { + guest_tile = map_get_path_element_at({ guest->next_x / 32, guest->next_y / 32, guest->next_z }); + if (guest_tile != nullptr) + { + if (rand == 0) + break; + --rand; + } + } + } + + x = guest->x; + y = guest->y; + z = guest->z; + } + else + { + // No walking guests; pick random park entrance + if (!gParkEntrances.empty()) + { + auto rand = scenario_rand_max((uint32_t)gParkEntrances.size()); + const auto& entrance = gParkEntrances[rand]; + auto dir = entrance.direction; + x = entrance.x; + y = entrance.y; + z = entrance.z; + x += 16 + ((dir & 1) == 0 ? ((dir & 2) ? 32 : -32) : 0); + y += 16 + ((dir & 1) == 1 ? ((dir & 2) ? -32 : 32) : 0); + } + else + { + // User must pick a location + newPeep->state = PEEP_STATE_PICKED; + x = newPeep->x; + y = newPeep->y; + z = newPeep->z; + } + } + + sprite_move(x, y, z + 16, (rct_sprite*)newPeep); + invalidate_sprite_2((rct_sprite*)newPeep); + } +}; diff --git a/src/openrct2/actions/StaffSetColourAction.hpp b/src/openrct2/actions/StaffSetColourAction.hpp index 1e30cb18fb..dd5f6aacb6 100644 --- a/src/openrct2/actions/StaffSetColourAction.hpp +++ b/src/openrct2/actions/StaffSetColourAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/actions/StaffSetCostumeAction.hpp b/src/openrct2/actions/StaffSetCostumeAction.hpp index 17355bb762..f6ebb14110 100644 --- a/src/openrct2/actions/StaffSetCostumeAction.hpp +++ b/src/openrct2/actions/StaffSetCostumeAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/actions/StaffSetNameAction.hpp b/src/openrct2/actions/StaffSetNameAction.hpp index d06538e992..4884b9bcb6 100644 --- a/src/openrct2/actions/StaffSetNameAction.hpp +++ b/src/openrct2/actions/StaffSetNameAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -58,12 +58,7 @@ public: GA_ERROR::INVALID_PARAMETERS, STR_STAFF_ERROR_CANT_NAME_STAFF_MEMBER, STR_NONE); } - if (_name.empty()) - { - return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_STAFF_ERROR_CANT_NAME_STAFF_MEMBER); - } - - Peep* peep = GET_PEEP(_spriteIndex); + auto peep = GET_PEEP(_spriteIndex); if (peep->type != PEEP_TYPE_STAFF) { log_warning("Invalid game command for sprite %u", _spriteIndex); @@ -71,31 +66,12 @@ public: GA_ERROR::INVALID_PARAMETERS, STR_STAFF_ERROR_CANT_NAME_STAFF_MEMBER, STR_NONE); } - rct_string_id newUserStringId = user_string_allocate( - USER_STRING_HIGH_ID_NUMBER | USER_STRING_DUPLICATION_PERMITTED, _name.c_str()); - if (newUserStringId == 0) - { - // TODO: Probably exhausted, introduce new error. - return std::make_unique( - GA_ERROR::UNKNOWN, STR_STAFF_ERROR_CANT_NAME_STAFF_MEMBER, gGameCommandErrorText); - } - user_string_free(newUserStringId); - return std::make_unique(); } GameActionResult::Ptr Execute() const override { - rct_string_id newUserStringId = user_string_allocate( - USER_STRING_HIGH_ID_NUMBER | USER_STRING_DUPLICATION_PERMITTED, _name.c_str()); - if (newUserStringId == 0) - { - // TODO: Probably exhausted, introduce new error. - return std::make_unique( - GA_ERROR::UNKNOWN, STR_STAFF_ERROR_CANT_NAME_STAFF_MEMBER, gGameCommandErrorText); - } - - Peep* peep = GET_PEEP(_spriteIndex); + auto peep = GET_PEEP(_spriteIndex); if (peep->type != PEEP_TYPE_STAFF) { log_warning("Invalid game command for sprite %u", _spriteIndex); @@ -103,23 +79,19 @@ public: GA_ERROR::INVALID_PARAMETERS, STR_STAFF_ERROR_CANT_NAME_STAFF_MEMBER, STR_NONE); } - set_format_arg(0, uint32_t, peep->id); - utf8* curName = gCommonStringFormatBuffer; - rct_string_id curId = peep->name_string_idx; - format_string(curName, 256, curId, gCommonFormatArgs); - - if (strcmp(curName, _name.c_str()) == 0) + auto curName = peep->GetName(); + if (curName == _name) { return std::make_unique(GA_ERROR::OK, STR_NONE); } - user_string_free(peep->name_string_idx); - peep->name_string_idx = newUserStringId; + if (!peep->SetName(_name)) + { + return std::make_unique(GA_ERROR::UNKNOWN, STR_CANT_NAME_GUEST, STR_NONE); + } peep_update_name_sort(peep); - peep_handle_easteregg_name(peep); - gfx_invalidate_screen(); auto intent = Intent(INTENT_ACTION_REFRESH_STAFF_LIST); diff --git a/src/openrct2/actions/StaffSetOrdersAction.hpp b/src/openrct2/actions/StaffSetOrdersAction.hpp index 06eab21379..e9ce50fbfd 100644 --- a/src/openrct2/actions/StaffSetOrdersAction.hpp +++ b/src/openrct2/actions/StaffSetOrdersAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/actions/StaffSetPatrolAreaAction.hpp b/src/openrct2/actions/StaffSetPatrolAreaAction.hpp new file mode 100644 index 0000000000..355494da4f --- /dev/null +++ b/src/openrct2/actions/StaffSetPatrolAreaAction.hpp @@ -0,0 +1,103 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../interface/Window.h" +#include "../peep/Peep.h" +#include "../peep/Staff.h" +#include "../world/Sprite.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(StaffSetPatrolAreaAction, GAME_COMMAND_SET_STAFF_PATROL, GameActionResult) +{ +private: + uint16_t _spriteId{ SPRITE_INDEX_NULL }; + CoordsXY _loc; + +public: + StaffSetPatrolAreaAction() + { + } + StaffSetPatrolAreaAction(uint16_t spriteId, CoordsXY loc) + : _spriteId(spriteId) + , _loc(loc) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + stream << DS_TAG(_spriteId) << DS_TAG(_loc); + } + + GameActionResult::Ptr Query() const override + { + if (_spriteId >= MAX_SPRITES) + { + log_error("Invalid spriteId. spriteId = %u", _spriteId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + auto peep = GET_PEEP(_spriteId); + if (peep == nullptr || peep->sprite_identifier != SPRITE_IDENTIFIER_PEEP || peep->type != PEEP_TYPE_STAFF) + { + log_error("Invalid spriteId. spriteId = %u", _spriteId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + return MakeResult(); + } + + GameActionResult::Ptr Execute() const override + { + auto peep = GET_PEEP(_spriteId); + if (peep == nullptr || peep->sprite_identifier != SPRITE_IDENTIFIER_PEEP || peep->type != PEEP_TYPE_STAFF) + { + log_error("Invalid spriteId. spriteId = %u", _spriteId); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + int32_t patrolOffset = peep->staff_id * STAFF_PATROL_AREA_SIZE; + + staff_toggle_patrol_area(peep->staff_id, _loc.x, _loc.y); + + bool isPatrolling = false; + for (int32_t i = 0; i < 128; i++) + { + if (gStaffPatrolAreas[patrolOffset + i]) + { + isPatrolling = true; + break; + } + } + + gStaffModes[peep->staff_id] &= ~(1 << 1); + if (isPatrolling) + { + gStaffModes[peep->staff_id] |= (1 << 1); + } + + for (int32_t y = 0; y < 4 * 32; y += 32) + { + for (int32_t x = 0; x < 4 * 32; x += 32) + { + map_invalidate_tile_full((_loc.x & 0x1F80) + x, (_loc.y & 0x1F80) + y); + } + } + staff_update_greyed_patrol_areas(); + + return MakeResult(); + } +}; diff --git a/src/openrct2/actions/SurfaceSetStyleAction.hpp b/src/openrct2/actions/SurfaceSetStyleAction.hpp new file mode 100644 index 0000000000..4fa28bcc26 --- /dev/null +++ b/src/openrct2/actions/SurfaceSetStyleAction.hpp @@ -0,0 +1,249 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../Context.h" +#include "../OpenRCT2.h" +#include "../management/Finance.h" +#include "../object/ObjectManager.h" +#include "../object/TerrainEdgeObject.h" +#include "../object/TerrainSurfaceObject.h" +#include "../world/Park.h" +#include "../world/Surface.h" +#include "../world/TileElement.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(SurfaceSetStyleAction, GAME_COMMAND_CHANGE_SURFACE_STYLE, GameActionResult) +{ +private: + MapRange _range; + uint8_t _surfaceStyle; + uint8_t _edgeStyle; + +public: + SurfaceSetStyleAction() + { + } + + SurfaceSetStyleAction(MapRange range, uint8_t surfaceStyle, uint8_t edgeStyle) + : _range(range) + , _surfaceStyle(surfaceStyle) + , _edgeStyle(edgeStyle) + { + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_range) << DS_TAG(_surfaceStyle) << DS_TAG(_edgeStyle); + } + + GameActionResult::Ptr Query() const override + { + auto res = MakeResult(); + res->ErrorTitle = STR_CANT_CHANGE_LAND_TYPE; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + auto normRange = _range.Normalise(); + auto x0 = std::max(normRange.GetLeft(), 32); + auto y0 = std::max(normRange.GetTop(), 32); + auto x1 = std::min(normRange.GetRight(), (int32_t)gMapSizeMaxXY); + auto y1 = std::min(normRange.GetBottom(), (int32_t)gMapSizeMaxXY); + + MapRange validRange{ x0, y0, x1, y1 }; + + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + if (_surfaceStyle != 0xFF) + { + if (_surfaceStyle > 0x1F) + { + log_error("Invalid surface style."); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_CHANGE_LAND_TYPE); + } + + const auto surfaceObj = static_cast( + objManager.GetLoadedObject(OBJECT_TYPE_TERRAIN_SURFACE, _surfaceStyle)); + + if (surfaceObj == nullptr) + { + log_error("Invalid surface style."); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_CHANGE_LAND_TYPE); + } + } + + if (_edgeStyle != 0xFF) + { + if (_edgeStyle > 0xF) + { + log_error("Invalid edge style."); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_CHANGE_LAND_TYPE); + } + + const auto edgeObj = static_cast( + objManager.GetLoadedObject(OBJECT_TYPE_TERRAIN_EDGE, _edgeStyle)); + + if (edgeObj == nullptr) + { + log_error("Invalid edge style."); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_CHANGE_LAND_TYPE); + } + } + + auto xMid = (validRange.GetLeft() + validRange.GetRight()) / 2 + 16; + auto yMid = (validRange.GetTop() + validRange.GetBottom()) / 2 + 16; + auto heightMid = tile_element_height({ xMid, yMid }); + + res->Position.x = xMid; + res->Position.y = yMid; + res->Position.z = heightMid; + + // Do nothing if not in editor, sandbox mode or landscaping is forbidden + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode + && (gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES)) + { + return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_CHANGE_LAND_TYPE, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY); + } + + money32 surfaceCost = 0; + money32 edgeCost = 0; + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) + { + if (!map_is_location_in_park({ x, y })) + continue; + } + + auto surfaceElement = map_get_surface_element_at({ x, y }); + if (surfaceElement == nullptr) + { + continue; + } + + if (_surfaceStyle != 0xFF) + { + uint8_t curSurfaceStyle = surfaceElement->GetSurfaceStyle(); + + if (_surfaceStyle != curSurfaceStyle) + { + const auto surfaceObject = static_cast( + objManager.GetLoadedObject(OBJECT_TYPE_TERRAIN_SURFACE, _surfaceStyle)); + if (surfaceObject != nullptr) + { + surfaceCost += surfaceObject->Price; + } + } + } + + if (_edgeStyle != 0xFF) + { + uint8_t curEdgeStyle = surfaceElement->GetEdgeStyle(); + + if (_edgeStyle != curEdgeStyle) + { + edgeCost += 100; + } + } + } + } + res->Cost = surfaceCost + edgeCost; + + return res; + } + + GameActionResult::Ptr Execute() const override + { + auto res = MakeResult(); + res->ErrorTitle = STR_CANT_CHANGE_LAND_TYPE; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + auto normRange = _range.Normalise(); + auto x0 = std::max(normRange.GetLeft(), 32); + auto y0 = std::max(normRange.GetTop(), 32); + auto x1 = std::min(normRange.GetRight(), (int32_t)gMapSizeMaxXY); + auto y1 = std::min(normRange.GetBottom(), (int32_t)gMapSizeMaxXY); + + MapRange validRange{ x0, y0, x1, y1 }; + + auto xMid = (validRange.GetLeft() + validRange.GetRight()) / 2 + 16; + auto yMid = (validRange.GetTop() + validRange.GetBottom()) / 2 + 16; + auto heightMid = tile_element_height({ xMid, yMid }); + + res->Position.x = xMid; + res->Position.y = yMid; + res->Position.z = heightMid; + + money32 surfaceCost = 0; + money32 edgeCost = 0; + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) + { + if (!map_is_location_in_park({ x, y })) + continue; + } + + auto surfaceElement = map_get_surface_element_at({ x, y }); + if (surfaceElement == nullptr) + { + continue; + } + + if (_surfaceStyle != 0xFF) + { + uint8_t curSurfaceStyle = surfaceElement->GetSurfaceStyle(); + + if (_surfaceStyle != curSurfaceStyle) + { + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + const auto surfaceObject = static_cast( + objManager.GetLoadedObject(OBJECT_TYPE_TERRAIN_SURFACE, _surfaceStyle)); + if (surfaceObject != nullptr) + { + surfaceCost += surfaceObject->Price; + + surfaceElement->SetSurfaceStyle(_surfaceStyle); + + map_invalidate_tile_full(x, y); + footpath_remove_litter(x, y, tile_element_height({ x, y })); + } + } + } + + if (_edgeStyle != 0xFF) + { + uint8_t curEdgeStyle = surfaceElement->GetEdgeStyle(); + + if (_edgeStyle != curEdgeStyle) + { + edgeCost += 100; + + surfaceElement->SetEdgeStyle(_edgeStyle); + map_invalidate_tile_full(x, y); + } + } + + if (surfaceElement->CanGrassGrow() && (surfaceElement->GetGrassLength() & 7) != GRASS_LENGTH_CLEAR_0) + { + surfaceElement->SetGrassLength(GRASS_LENGTH_CLEAR_0); + map_invalidate_tile_full(x, y); + } + } + } + res->Cost = surfaceCost + edgeCost; + + return res; + } +}; diff --git a/src/openrct2/actions/TileModifyAction.hpp b/src/openrct2/actions/TileModifyAction.hpp new file mode 100644 index 0000000000..80b1914366 --- /dev/null +++ b/src/openrct2/actions/TileModifyAction.hpp @@ -0,0 +1,262 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../world/TileInspector.h" +#include "GameAction.h" + +enum class TileModifyType : uint8_t +{ + AnyRemove, + AnySwap, + AnyInsertCorrupt, + AnyRotate, + AnyPaste, + AnySort, + AnyBaseHeightOffset, + SurfaceShowParkFences, + SurfaceToggleCorner, + SurfaceToggleDiagonal, + PathSetSlope, + PathSetBroken, + PathToggleEdge, + EntranceMakeUsable, + WallSetSlope, + TrackBaseHeightOffset, + TrackSetChain, + TrackSetChainBlock, + TrackSetBlockBrake, + TrackSetIndestructible, + ScenerySetQuarterLocation, + ScenerySetQuarterCollision, + BannerToggleBlockingEdge, + CorruptClamp, + Count, +}; + +DEFINE_GAME_ACTION(TileModifyAction, GAME_COMMAND_MODIFY_TILE, GameActionResult) +{ +private: + CoordsXY _loc; + uint8_t _setting{ 0 }; + uint32_t _value1{ 0 }; + uint32_t _value2{ 0 }; + TileElement _pasteElement{}; + +public: + TileModifyAction() + { + } + TileModifyAction( + CoordsXY loc, TileModifyType setting, uint32_t value1 = 0, uint32_t value2 = 0, TileElement pasteElement = {}) + : _loc(loc) + , _setting(static_cast(setting)) + , _value1(value1) + , _value2(value2) + , _pasteElement(pasteElement) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_loc) << DS_TAG(_setting) << DS_TAG(_value1) << DS_TAG(_value2) << DS_TAG(_pasteElement); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + switch (static_cast(_setting)) + { + case TileModifyType::AnyRemove: + { + const auto elementIndex = _value1; + res = tile_inspector_remove_element_at(_loc, elementIndex, isExecuting); + break; + } + case TileModifyType::AnySwap: + { + const auto firstIndex = _value1; + const auto secondIndex = _value2; + res = tile_inspector_swap_elements_at(_loc, firstIndex, secondIndex, isExecuting); + break; + } + case TileModifyType::AnyInsertCorrupt: + { + const auto elementIndex = _value1; + res = tile_inspector_insert_corrupt_at(_loc, elementIndex, isExecuting); + break; + } + case TileModifyType::AnyRotate: + { + const auto elementIndex = _value1; + res = tile_inspector_rotate_element_at(_loc, elementIndex, isExecuting); + break; + } + case TileModifyType::AnyPaste: + { + res = tile_inspector_paste_element_at(_loc, _pasteElement, isExecuting); + break; + } + case TileModifyType::AnySort: + { + res = tile_inspector_sort_elements_at(_loc, isExecuting); + break; + } + case TileModifyType::AnyBaseHeightOffset: + { + const auto elementIndex = _value1; + const auto heightOffset = _value2; + res = tile_inspector_any_base_height_offset(_loc, elementIndex, heightOffset, isExecuting); + break; + } + case TileModifyType::SurfaceShowParkFences: + { + const bool showFences = _value1; + res = tile_inspector_surface_show_park_fences(_loc, showFences, isExecuting); + break; + } + case TileModifyType::SurfaceToggleCorner: + { + const auto cornerIndex = _value1; + res = tile_inspector_surface_toggle_corner(_loc, cornerIndex, isExecuting); + break; + } + case TileModifyType::SurfaceToggleDiagonal: + { + res = tile_inspector_surface_toggle_diagonal(_loc, isExecuting); + break; + } + case TileModifyType::PathSetSlope: + { + const auto elementIndex = _value1; + const bool sloped = _value2; + res = tile_inspector_path_set_sloped(_loc, elementIndex, sloped, isExecuting); + break; + } + case TileModifyType::PathSetBroken: + { + const auto elementIndex = _value1; + const bool broken = _value2; + res = tile_inspector_path_set_broken(_loc, elementIndex, broken, isExecuting); + break; + } + case TileModifyType::PathToggleEdge: + { + const auto elementIndex = _value1; + const auto edgeIndex = _value2; + res = tile_inspector_path_toggle_edge(_loc, elementIndex, edgeIndex, isExecuting); + break; + } + case TileModifyType::EntranceMakeUsable: + { + const auto elementIndex = _value1; + res = tile_inspector_entrance_make_usable(_loc, elementIndex, isExecuting); + break; + } + case TileModifyType::WallSetSlope: + { + const auto elementIndex = _value1; + const auto slopeValue = _value2; + res = tile_inspector_wall_set_slope(_loc, elementIndex, slopeValue, isExecuting); + break; + } + case TileModifyType::TrackBaseHeightOffset: + { + const auto elementIndex = _value1; + const auto heightOffset = _value2; + res = tile_inspector_track_base_height_offset(_loc, elementIndex, heightOffset, isExecuting); + break; + } + case TileModifyType::TrackSetChainBlock: + { + const auto elementIndex = _value1; + const bool setChain = _value2; + res = tile_inspector_track_set_chain(_loc, elementIndex, true, setChain, isExecuting); + break; + } + case TileModifyType::TrackSetChain: + { + const auto elementIndex = _value1; + const bool setChain = _value2; + res = tile_inspector_track_set_chain(_loc, elementIndex, false, setChain, isExecuting); + break; + } + case TileModifyType::TrackSetBlockBrake: + { + const auto elementIndex = _value1; + const bool blockBrake = _value2; + res = tile_inspector_track_set_block_brake(_loc, elementIndex, blockBrake, isExecuting); + break; + } + case TileModifyType::TrackSetIndestructible: + { + const auto elementIndex = _value1; + const bool isIndestructible = _value2; + res = tile_inspector_track_set_indestructible(_loc, elementIndex, isIndestructible, isExecuting); + break; + } + case TileModifyType::ScenerySetQuarterLocation: + { + const auto elementIndex = _value1; + const auto quarterIndex = _value2; + res = tile_inspector_scenery_set_quarter_location(_loc, elementIndex, quarterIndex, isExecuting); + break; + } + case TileModifyType::ScenerySetQuarterCollision: + { + const auto elementIndex = _value1; + const auto quarterIndex = _value2; + res = tile_inspector_scenery_set_quarter_collision(_loc, elementIndex, quarterIndex, isExecuting); + break; + } + case TileModifyType::BannerToggleBlockingEdge: + { + const auto elementIndex = _value1; + const auto edgeIndex = _value2; + res = tile_inspector_banner_toggle_blocking_edge(_loc, elementIndex, edgeIndex, isExecuting); + break; + } + case TileModifyType::CorruptClamp: + { + const auto elementIndex = _value1; + res = tile_inspector_corrupt_clamp(_loc, elementIndex, isExecuting); + break; + } + default: + log_error("invalid instruction"); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + break; + } + + res->Position.x = _loc.x; + res->Position.y = _loc.y; + res->Position.z = tile_element_height(_loc); + + return res; + } +}; diff --git a/src/openrct2/actions/TrackPlaceAction.hpp b/src/openrct2/actions/TrackPlaceAction.hpp index 74dab23c72..2682277fa8 100644 --- a/src/openrct2/actions/TrackPlaceAction.hpp +++ b/src/openrct2/actions/TrackPlaceAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -19,7 +19,30 @@ #include "../world/Surface.h" #include "GameAction.h" -DEFINE_GAME_ACTION(TrackPlaceAction, GAME_COMMAND_PLACE_TRACK, GameActionResult) +class TrackPlaceActionResult final : public GameActionResult +{ +public: + TrackPlaceActionResult() + : GameActionResult(GA_ERROR::OK, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE) + { + } + TrackPlaceActionResult(GA_ERROR error) + : GameActionResult(error, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE) + { + } + TrackPlaceActionResult(GA_ERROR error, rct_string_id message) + : GameActionResult(error, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, message) + { + } + TrackPlaceActionResult(GA_ERROR error, rct_string_id message, uint8_t* args) + : GameActionResult(error, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, message, args) + { + } + + uint8_t GroundFlags{ 0 }; +}; + +DEFINE_GAME_ACTION(TrackPlaceAction, GAME_COMMAND_PLACE_TRACK, TrackPlaceActionResult) { private: NetworkRideId_t _rideIndex; @@ -64,57 +87,46 @@ public: GameActionResult::Ptr Query() const override { - Ride* ride = get_ride(_rideIndex); + auto ride = get_ride(_rideIndex); if (ride == nullptr) { log_warning("Invalid ride for track placement, rideIndex = %d", (int32_t)_rideIndex); - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_NONE); - } - if (ride->type == RIDE_TYPE_NULL) - { - log_warning("Invalid ride type, rideIndex = %d", (int32_t)_rideIndex); - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_NONE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); if (rideEntry == nullptr) { log_warning("Invalid ride subtype for track placement, rideIndex = %d", (int32_t)_rideIndex); - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_NONE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } if (!direction_valid(_origin.direction)) { log_warning("Invalid direction for track placement, direction = %d", _origin.direction); - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_NONE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } - auto res = std::make_unique(); + auto res = std::make_unique(); res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; res->Position.x = _origin.x + 16; res->Position.y = _origin.y + 16; res->Position.z = _origin.z; - gTrackGroundFlags = 0; + res->GroundFlags = 0; uint32_t rideTypeFlags = RideProperties[ride->type].flags; if ((ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) && _trackType == TRACK_ELEM_END_STATION) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_NOT_ALLOWED_TO_MODIFY_STATION); + return std::make_unique(GA_ERROR::DISALLOWED, STR_NOT_ALLOWED_TO_MODIFY_STATION); } if (!(GetActionFlags() & GA_FLAGS::ALLOW_WHILE_PAUSED)) { if (game_is_paused() && !gCheatsBuildInPauseMode) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, - STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED); } } @@ -124,18 +136,15 @@ public: { if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, - STR_ONLY_ONE_ON_RIDE_PHOTO_PER_RIDE); + return std::make_unique(GA_ERROR::DISALLOWED, STR_ONLY_ONE_ON_RIDE_PHOTO_PER_RIDE); } } else if (_trackType == TRACK_ELEM_CABLE_LIFT_HILL) { if (ride->lifecycle_flags & RIDE_LIFECYCLE_CABLE_LIFT_HILL_COMPONENT_USED) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, - STR_ONLY_ONE_CABLE_LIFT_HILL_PER_RIDE); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_ONLY_ONE_CABLE_LIFT_HILL_PER_RIDE); } } // Backwards steep lift hills are allowed, even on roller coasters that do not support forwards steep lift hills. @@ -145,14 +154,12 @@ public: { if (TrackFlags[_trackType] & TRACK_ELEM_FLAG_IS_STEEP_UP) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TOO_STEEP_FOR_LIFT_HILL); + return std::make_unique(GA_ERROR::DISALLOWED, STR_TOO_STEEP_FOR_LIFT_HILL); } } } money32 cost = 0; - TileElement* tileElement; const rct_preview_track* trackBlock = get_track_def_from_ride(ride, _trackType); uint32_t numElements = 0; // First check if any of the track pieces are outside the park @@ -164,10 +171,9 @@ public: tileCoords.x += track.x; tileCoords.y += track.y; - if (!map_is_location_owned(tileCoords.x, tileCoords.y, tileCoords.z) && !gCheatsSandboxMode) + if (!map_is_location_owned(tileCoords) && !gCheatsSandboxMode) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK); + return std::make_unique(GA_ERROR::DISALLOWED, STR_LAND_NOT_OWNED_BY_PARK); } numElements++; } @@ -175,24 +181,21 @@ public: if (!map_check_free_elements_and_reorganise(numElements)) { log_warning("Not enough free map elments to place track."); - return std::make_unique( - GA_ERROR::NO_FREE_ELEMENTS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TILE_ELEMENT_LIMIT_REACHED); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS, STR_TILE_ELEMENT_LIMIT_REACHED); } const uint16_t* trackFlags = (rideTypeFlags & RIDE_TYPE_FLAG_FLAT_RIDE) ? FlatTrackFlags : TrackFlags; if (trackFlags[_trackType] & TRACK_ELEM_FLAG_STARTS_AT_HALF_HEIGHT) { if ((_origin.z & 0x0F) != 8) { - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_CONSTRUCTION_ERR_UNKNOWN); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CONSTRUCTION_ERR_UNKNOWN); } } else { if ((_origin.z & 0x0F) != 0) { - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_CONSTRUCTION_ERR_UNKNOWN); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CONSTRUCTION_ERR_UNKNOWN); } } @@ -211,8 +214,7 @@ public: if (mapLoc.z < 16) { - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TOO_LOW); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_TOO_LOW); } int32_t baseZ = mapLoc.z / 8; @@ -231,8 +233,7 @@ public: if (clearanceZ >= 255) { - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TOO_HIGH); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_TOO_HIGH); } uint8_t crossingMode = (ride->type == RIDE_TYPE_MINIATURE_RAILWAY && _trackType == TRACK_ELEM_FLAT) @@ -242,29 +243,26 @@ public: mapLoc.x, mapLoc.y, baseZ, clearanceZ, &map_place_non_scenery_clear_func, quarterTile, GetFlags(), &cost, crossingMode)) { - return std::make_unique( - GA_ERROR::NO_CLEARANCE, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, gGameCommandErrorText, - gCommonFormatArgs); + return std::make_unique( + GA_ERROR::NO_CLEARANCE, gGameCommandErrorText, gCommonFormatArgs); } uint8_t mapGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); - if (gTrackGroundFlags != 0 && (gTrackGroundFlags & mapGroundFlags) == 0) + if (res->GroundFlags != 0 && (res->GroundFlags & mapGroundFlags) == 0) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, - STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND); } - gTrackGroundFlags = mapGroundFlags; + res->GroundFlags = mapGroundFlags; if (rideTypeFlags & RIDE_TYPE_FLAG_FLAT_RIDE) { if (FlatTrackFlags[_trackType] & TRACK_ELEM_FLAG_ONLY_ABOVE_GROUND) { - if (gTrackGroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) + if (res->GroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, - STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); } } } @@ -272,11 +270,10 @@ public: { if (TrackFlags[_trackType] & TRACK_ELEM_FLAG_ONLY_ABOVE_GROUND) { - if (gTrackGroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) + if (res->GroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, - STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); } } } @@ -287,9 +284,8 @@ public: { if (!(gMapGroundFlags & ELEMENT_IS_UNDERWATER)) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, - STR_CAN_ONLY_BUILD_THIS_UNDERWATER); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_UNDERWATER); } } } @@ -299,45 +295,39 @@ public: { // No element has this flag if (gMapGroundFlags & ELEMENT_IS_UNDERWATER) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, - STR_CAN_ONLY_BUILD_THIS_UNDERWATER); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_UNDERWATER); } } } if (gMapGroundFlags & ELEMENT_IS_UNDERWATER && !gCheatsDisableClearanceChecks) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_RIDE_CANT_BUILD_THIS_UNDERWATER); + return std::make_unique(GA_ERROR::DISALLOWED, STR_RIDE_CANT_BUILD_THIS_UNDERWATER); } if ((rideTypeFlags & RIDE_TYPE_FLAG_TRACK_MUST_BE_ON_WATER) && !byte_9D8150) { - tileElement = map_get_surface_element_at({ mapLoc.x, mapLoc.y }); + auto surfaceElement = map_get_surface_element_at(mapLoc); - uint8_t waterHeight = tileElement->AsSurface()->GetWaterHeight() * 2; + uint8_t waterHeight = surfaceElement->GetWaterHeight() * 2; if (waterHeight == 0) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_WATER); + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_WATER); } if (waterHeight != baseZ) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_WATER); + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_WATER); } waterHeight -= 2; - if (waterHeight == tileElement->base_height) + if (waterHeight == surfaceElement->base_height) { - uint8_t slope = tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP; + uint8_t slope = surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP; if (slope == TILE_ELEMENT_SLOPE_W_CORNER_DN || slope == TILE_ELEMENT_SLOPE_S_CORNER_DN || slope == TILE_ELEMENT_SLOPE_E_CORNER_DN || slope == TILE_ELEMENT_SLOPE_N_CORNER_DN) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, - STR_CAN_ONLY_BUILD_THIS_ON_WATER); + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_WATER); } } } @@ -355,16 +345,15 @@ public: { if (!track_add_station_element(mapLoc.x, mapLoc.y, baseZ, _origin.direction, _rideIndex, 0)) { - return std::make_unique( - GA_ERROR::UNKNOWN, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, gGameCommandErrorText); + return std::make_unique(GA_ERROR::UNKNOWN, gGameCommandErrorText); } } // 6c5648 12 push - tileElement = map_get_surface_element_at({ mapLoc.x, mapLoc.y }); + auto surfaceElement = map_get_surface_element_at(mapLoc); if (!gCheatsDisableSupportLimits) { - int32_t ride_height = clearanceZ - tileElement->base_height; + int32_t ride_height = clearanceZ - surfaceElement->base_height; if (ride_height >= 0) { uint16_t maxHeight; @@ -386,13 +375,12 @@ public: ride_height /= 2; if (ride_height > maxHeight && !byte_9D8150) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TOO_HIGH_FOR_SUPPORTS); + return std::make_unique(GA_ERROR::DISALLOWED, STR_TOO_HIGH_FOR_SUPPORTS); } } } - int32_t supportHeight = baseZ - tileElement->base_height; + int32_t supportHeight = baseZ - surfaceElement->base_height; if (supportHeight < 0) { supportHeight = 10; @@ -411,29 +399,27 @@ public: GameActionResult::Ptr Execute() const override { - Ride* ride = get_ride(_rideIndex); + auto ride = get_ride(_rideIndex); if (ride == nullptr) { log_warning("Invalid ride for track placement, rideIndex = %d", (int32_t)_rideIndex); - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); if (rideEntry == nullptr) { log_warning("Invalid ride subtype for track placement, rideIndex = %d", (int32_t)_rideIndex); - return std::make_unique( - GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); } - auto res = std::make_unique(); + auto res = std::make_unique(); res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; res->Position.x = _origin.x + 16; res->Position.y = _origin.y + 16; res->Position.z = _origin.z; - gTrackGroundFlags = 0; + res->GroundFlags = 0; uint32_t rideTypeFlags = RideProperties[ride->type].flags; @@ -448,7 +434,6 @@ public: } money32 cost = 0; - TileElement* tileElement; const rct_preview_track* trackBlock = get_track_def_from_ride(ride, _trackType); trackBlock = get_track_def_from_ride(ride, _trackType); @@ -483,9 +468,8 @@ public: mapLoc.x, mapLoc.y, baseZ, clearanceZ, &map_place_non_scenery_clear_func, quarterTile, GetFlags() | GAME_COMMAND_FLAG_APPLY, &cost, crossingMode)) { - return std::make_unique( - GA_ERROR::NO_CLEARANCE, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, gGameCommandErrorText, - gCommonFormatArgs); + return std::make_unique( + GA_ERROR::NO_CLEARANCE, gGameCommandErrorText, gCommonFormatArgs); } if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !gCheatsDisableClearanceChecks) @@ -500,6 +484,7 @@ public: // Remove walls in the directions this track intersects uint8_t intersectingDirections = (*wallEdges)[blockIndex]; intersectingDirections ^= 0x0F; + intersectingDirections = rol4(intersectingDirections, _origin.direction); for (int32_t i = 0; i < 4; i++) { if (intersectingDirections & (1 << i)) @@ -511,19 +496,18 @@ public: } uint8_t mapGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); - if (gTrackGroundFlags != 0 && (gTrackGroundFlags & mapGroundFlags) == 0) + if (res->GroundFlags != 0 && (res->GroundFlags & mapGroundFlags) == 0) { - return std::make_unique( - GA_ERROR::DISALLOWED, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, - STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND); + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND); } - gTrackGroundFlags = mapGroundFlags; + res->GroundFlags = mapGroundFlags; // 6c5648 12 push - tileElement = map_get_surface_element_at({ mapLoc.x, mapLoc.y }); + auto surfaceElement = map_get_surface_element_at(mapLoc); - int32_t supportHeight = baseZ - tileElement->base_height; + int32_t supportHeight = baseZ - surfaceElement->base_height; if (supportHeight < 0) { supportHeight = 10; @@ -576,7 +560,7 @@ public: int32_t entranceDirections = 0; if (ride->overall_view.xy != RCT_XY8_UNDEFINED) { - if (!(GetFlags() & GAME_COMMAND_FLAG_5)) + if (!(GetFlags() & GAME_COMMAND_FLAG_NO_SPEND)) { if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) { @@ -595,7 +579,8 @@ public: ride->overall_view.y = mapLoc.y / 32; } - tileElement = tile_element_insert(mapLoc.x / 32, mapLoc.y / 32, baseZ, quarterTile.GetBaseQuarterOccupied()); + auto tileElement = tile_element_insert( + { mapLoc.x / 32, mapLoc.y / 32, baseZ }, quarterTile.GetBaseQuarterOccupied()); assert(tileElement != nullptr); tileElement->clearance_height = clearanceZ; tileElement->SetType(TILE_ELEMENT_TYPE_TRACK); @@ -682,14 +667,14 @@ public: mapLoc.x, mapLoc.y, baseZ, _origin.direction, _rideIndex, GAME_COMMAND_FLAG_APPLY); } sub_6CB945(ride); - ride_update_max_vehicles(ride); + ride->UpdateMaxVehicles(); } if (rideTypeFlags & RIDE_TYPE_FLAG_TRACK_MUST_BE_ON_WATER) { - TileElement* surfaceElement = map_get_surface_element_at({ mapLoc.x, mapLoc.y }); - surfaceElement->AsSurface()->SetHasTrackThatNeedsWater(true); - tileElement = surfaceElement; + auto* waterSurfaceElement = map_get_surface_element_at(mapLoc); + waterSurfaceElement->SetHasTrackThatNeedsWater(true); + tileElement = reinterpret_cast(waterSurfaceElement); } if (!gCheatsDisableClearanceChecks || !(GetFlags() & GAME_COMMAND_FLAG_GHOST)) diff --git a/src/openrct2/actions/TrackRemoveAction.hpp b/src/openrct2/actions/TrackRemoveAction.hpp index fb73e5c763..5cd51c3a84 100644 --- a/src/openrct2/actions/TrackRemoveAction.hpp +++ b/src/openrct2/actions/TrackRemoveAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -126,7 +126,7 @@ public: ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); auto trackType = tileElement->AsTrack()->GetTrackType(); - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); if (ride == nullptr) { log_warning("Ride not found. ride index = %d.", rideIndex); @@ -216,7 +216,7 @@ public: } } - TileElement* surfaceElement = map_get_surface_element_at({ mapLoc.x, mapLoc.y }); + auto* surfaceElement = map_get_surface_element_at(mapLoc); if (surfaceElement == nullptr) { log_warning("Surface Element not found. x = %d, y = %d", mapLoc.x, mapLoc.y); @@ -322,7 +322,7 @@ public: auto trackType = tileElement->AsTrack()->GetTrackType(); bool isLiftHill = tileElement->AsTrack()->HasChain(); - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); if (ride == nullptr) { log_warning("Ride not found. ride index = %d.", rideIndex); @@ -411,7 +411,7 @@ public: } } - TileElement* surfaceElement = map_get_surface_element_at({ mapLoc.x, mapLoc.y }); + auto* surfaceElement = map_get_surface_element_at(mapLoc); if (surfaceElement == nullptr) { log_warning("Surface Element not found. x = %d, y = %d", mapLoc.x, mapLoc.y); @@ -437,7 +437,7 @@ public: if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_TRACK_MUST_BE_ON_WATER)) { - surfaceElement->AsSurface()->SetHasTrackThatNeedsWater(false); + surfaceElement->SetHasTrackThatNeedsWater(false); } invalidate_test_results(ride); @@ -450,7 +450,7 @@ public: sub_6CB945(ride); if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST)) { - ride_update_max_vehicles(ride); + ride->UpdateMaxVehicles(); } } diff --git a/src/openrct2/actions/TrackSetBrakeSpeedAction.hpp b/src/openrct2/actions/TrackSetBrakeSpeedAction.hpp new file mode 100644 index 0000000000..e1d671099c --- /dev/null +++ b/src/openrct2/actions/TrackSetBrakeSpeedAction.hpp @@ -0,0 +1,75 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../management/Finance.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(TrackSetBrakeSpeedAction, GAME_COMMAND_SET_BRAKES_SPEED, GameActionResult) +{ +private: + CoordsXYZ _loc; + uint8_t _trackType; + uint8_t _brakeSpeed; + +public: + TrackSetBrakeSpeedAction() = default; + TrackSetBrakeSpeedAction(CoordsXYZ loc, uint8_t trackType, uint8_t brakeSpeed) + : _loc(loc) + , _trackType(trackType) + , _brakeSpeed(brakeSpeed) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + stream << DS_TAG(_loc) << DS_TAG(_trackType) << DS_TAG(_brakeSpeed); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + + res->Position = _loc; + res->Position.x += 16; + res->Position.y += 16; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; + + TileElement* tileElement = map_get_track_element_at_of_type(_loc.x, _loc.y, _loc.z / 8, _trackType); + if (tileElement == nullptr) + { + log_warning("Invalid game command for setting brakes speed. x = %d, y = %d", _loc.x, _loc.y); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + } + + if (isExecuting) + { + tileElement->AsTrack()->SetBrakeBoosterSpeed(_brakeSpeed); + } + return res; + } +}; diff --git a/src/openrct2/actions/WallPlaceAction.hpp b/src/openrct2/actions/WallPlaceAction.hpp new file mode 100644 index 0000000000..6598dfeacd --- /dev/null +++ b/src/openrct2/actions/WallPlaceAction.hpp @@ -0,0 +1,720 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../OpenRCT2.h" +#include "../management/Finance.h" +#include "../ride/RideGroupManager.h" +#include "../ride/Track.h" +#include "../ride/TrackData.h" +#include "../ride/TrackDesign.h" +#include "../world/Banner.h" +#include "../world/LargeScenery.h" +#include "../world/MapAnimation.h" +#include "../world/Scenery.h" +#include "../world/SmallScenery.h" +#include "../world/Surface.h" +#include "GameAction.h" + +class WallPlaceActionResult final : public GameActionResult +{ +public: + WallPlaceActionResult() + : GameActionResult(GA_ERROR::OK, STR_CANT_BUILD_PARK_ENTRANCE_HERE) + { + } + + WallPlaceActionResult(GA_ERROR err) + : GameActionResult(err, STR_CANT_BUILD_PARK_ENTRANCE_HERE) + { + } + + WallPlaceActionResult(GA_ERROR err, rct_string_id msg) + : GameActionResult(err, STR_CANT_BUILD_PARK_ENTRANCE_HERE, msg) + { + } + + WallPlaceActionResult(GA_ERROR error, rct_string_id msg, uint8_t* args) + : GameActionResult(error, STR_CANT_BUILD_PARK_ENTRANCE_HERE, msg, args) + { + } + + TileElement* tileElement = nullptr; +}; + +DEFINE_GAME_ACTION(WallPlaceAction, GAME_COMMAND_PLACE_WALL, WallPlaceActionResult) +{ +private: + int32_t _wallType{ -1 }; + CoordsXYZ _loc; + Direction _edge{ INVALID_DIRECTION }; + int32_t _primaryColour; + int32_t _secondaryColour; + int32_t _tertiaryColour; + BannerIndex _bannerId{ BANNER_INDEX_NULL }; + +public: + WallPlaceAction() + { + } + + WallPlaceAction( + int32_t wallType, CoordsXYZ loc, uint8_t edge, int32_t primaryColour, int32_t secondaryColour, int32_t tertiaryColour) + : _wallType(wallType) + , _loc(loc) + , _edge(edge) + , _primaryColour(primaryColour) + , _secondaryColour(secondaryColour) + , _tertiaryColour(tertiaryColour) + { + rct_scenery_entry* sceneryEntry = get_wall_entry(_wallType); + if (sceneryEntry != nullptr) + { + if (sceneryEntry->wall.scrolling_mode != SCROLLING_MODE_NONE) + { + _bannerId = create_new_banner(0); + } + } + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_wallType) << DS_TAG(_loc) << DS_TAG(_edge) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour) + << DS_TAG(_tertiaryColour) << DS_TAG(_bannerId); + } + + GameActionResult::Ptr Query() const override + { + auto res = std::make_unique(); + res->ErrorTitle = STR_CANT_BUILD_PARK_ENTRANCE_HERE; + res->Position = _loc; + + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x += 16; + res->Position.y += 16; + + if (_loc.z == 0) + { + res->Position.z = tile_element_height(res->Position); + } + + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY) + && !gCheatsSandboxMode) + { + if (_loc.z == 0) + { + if (!map_is_location_in_park(_loc)) + { + return std::make_unique(GA_ERROR::NOT_OWNED); + } + } + else if (!map_is_location_owned(_loc)) + { + return std::make_unique(GA_ERROR::NOT_OWNED); + } + } + else if (!byte_9D8150 && (_loc.x > gMapSizeMaxXY || _loc.y > gMapSizeMaxXY)) + { + log_error("Invalid x/y coordinates. x = %d y = %d", _loc.x, _loc.y); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + + if (_edge > 3) + { + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + + uint8_t edgeSlope = 0; + auto targetHeight = _loc.z; + if (targetHeight == 0) + { + auto* surfaceElement = map_get_surface_element_at(_loc); + if (surfaceElement == nullptr) + { + log_error("Surface element not found at %d, %d.", _loc.x, _loc.y); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + targetHeight = surfaceElement->base_height * 8; + + uint8_t slope = surfaceElement->GetSlope(); + edgeSlope = EdgeSlopes[slope][_edge & 3]; + if (edgeSlope & EDGE_SLOPE_ELEVATED) + { + targetHeight += 16; + edgeSlope &= ~EDGE_SLOPE_ELEVATED; + } + } + + auto* surfaceElement = map_get_surface_element_at(_loc); + if (surfaceElement == nullptr) + { + log_error("Surface element not found at %d, %d.", _loc.x, _loc.y); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + + if (surfaceElement->GetWaterHeight() > 0) + { + uint16_t waterHeight = surfaceElement->GetWaterHeight() * 16; + + if (targetHeight < waterHeight && !gCheatsDisableClearanceChecks) + { + return std::make_unique(GA_ERROR::DISALLOWED, STR_CANT_BUILD_THIS_UNDERWATER); + } + } + + if (targetHeight / 8 < surfaceElement->base_height && !gCheatsDisableClearanceChecks) + { + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); + } + + if (!(edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS))) + { + uint8_t newEdge = (_edge + 2) & 3; + uint8_t newBaseHeight = surfaceElement->base_height; + newBaseHeight += 2; + if (surfaceElement->GetSlope() & (1 << newEdge)) + { + if (targetHeight / 8 < newBaseHeight) + { + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); + } + + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + { + newEdge = (newEdge - 1) & 3; + + if (surfaceElement->GetSlope() & (1 << newEdge)) + { + newEdge = (newEdge + 2) & 3; + if (surfaceElement->GetSlope() & (1 << newEdge)) + { + newBaseHeight += 2; + if (targetHeight / 8 < newBaseHeight) + { + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); + } + newBaseHeight -= 2; + } + } + } + } + + newEdge = (_edge + 3) & 3; + if (surfaceElement->GetSlope() & (1 << newEdge)) + { + if (targetHeight / 8 < newBaseHeight) + { + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); + } + + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + { + newEdge = (newEdge - 1) & 3; + + if (surfaceElement->GetSlope() & (1 << newEdge)) + { + newEdge = (newEdge + 2) & 3; + if (surfaceElement->GetSlope() & (1 << newEdge)) + { + newBaseHeight += 2; + if (targetHeight / 8 < newBaseHeight) + { + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); + } + } + } + } + } + } + + rct_scenery_entry* wallEntry = get_wall_entry(_wallType); + + if (wallEntry == nullptr) + { + log_error("Wall Type not found %d", _wallType); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + + if (wallEntry->wall.scrolling_mode != SCROLLING_MODE_NONE) + { + if (_bannerId == BANNER_INDEX_NULL) + { + log_error("Banner Index not specified."); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_TOO_MANY_BANNERS_IN_GAME); + } + + auto banner = GetBanner(_bannerId); + if (banner->type != BANNER_NULL) + { + log_error("No free banners available"); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); + } + } + + uint8_t clearanceHeight = targetHeight / 8; + if (edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS)) + { + if (wallEntry->wall.flags & WALL_SCENERY_CANT_BUILD_ON_SLOPE) + { + return std::make_unique(GA_ERROR::DISALLOWED, STR_ERR_UNABLE_TO_BUILD_THIS_ON_SLOPE); + } + clearanceHeight += 2; + } + clearanceHeight += wallEntry->wall.height; + + bool wallAcrossTrack = false; + if (!(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY) && !gCheatsDisableClearanceChecks) + { + if (!WallCheckObstruction(wallEntry, targetHeight / 8, clearanceHeight, &wallAcrossTrack)) + { + return std::make_unique( + GA_ERROR::NO_CLEARANCE, gGameCommandErrorText, gCommonFormatArgs); + } + } + + if (!map_check_free_elements_and_reorganise(1)) + { + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS, gGameCommandErrorText); + } + + res->Cost = wallEntry->wall.price; + return res; + } + + GameActionResult::Ptr Execute() const override + { + auto res = std::make_unique(); + res->ErrorTitle = STR_CANT_BUILD_PARK_ENTRANCE_HERE; + res->Position = _loc; + + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x += 16; + res->Position.y += 16; + + if (res->Position.z == 0) + { + res->Position.z = tile_element_height(res->Position); + } + + uint8_t edgeSlope = 0; + auto targetHeight = _loc.z; + if (targetHeight == 0) + { + auto* surfaceElement = map_get_surface_element_at(_loc); + if (surfaceElement == nullptr) + { + log_error("Surface element not found at %d, %d.", _loc.x, _loc.y); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + targetHeight = surfaceElement->base_height * 8; + + uint8_t slope = surfaceElement->GetSlope(); + edgeSlope = EdgeSlopes[slope][_edge & 3]; + if (edgeSlope & EDGE_SLOPE_ELEVATED) + { + targetHeight += 16; + edgeSlope &= ~EDGE_SLOPE_ELEVATED; + } + } + + rct_scenery_entry* wallEntry = get_wall_entry(_wallType); + + if (wallEntry == nullptr) + { + log_error("Wall Type not found %d", _wallType); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS); + } + + if (wallEntry->wall.scrolling_mode != SCROLLING_MODE_NONE) + { + if (_bannerId == BANNER_INDEX_NULL) + { + log_error("Banner Index not specified."); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_TOO_MANY_BANNERS_IN_GAME); + } + + auto banner = GetBanner(_bannerId); + if (banner->type != BANNER_NULL) + { + log_error("No free banners available"); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); + } + + banner->text = {}; + banner->colour = 2; + banner->text_colour = 2; + banner->flags = BANNER_FLAG_IS_WALL; + banner->type = 0; + banner->position.x = _loc.x / 32; + banner->position.y = _loc.y / 32; + + ride_id_t rideIndex = banner_get_closest_ride_index(_loc.x, _loc.y, targetHeight); + if (rideIndex != RIDE_ID_NULL) + { + banner->ride_index = rideIndex; + banner->flags |= BANNER_FLAG_LINKED_TO_RIDE; + } + } + + uint8_t clearanceHeight = targetHeight / 8; + if (edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS)) + { + clearanceHeight += 2; + } + clearanceHeight += wallEntry->wall.height; + + bool wallAcrossTrack = false; + if (!(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY) && !gCheatsDisableClearanceChecks) + { + if (!WallCheckObstruction(wallEntry, targetHeight / 8, clearanceHeight, &wallAcrossTrack)) + { + return std::make_unique( + GA_ERROR::NO_CLEARANCE, gGameCommandErrorText, gCommonFormatArgs); + } + } + + if (!map_check_free_elements_and_reorganise(1)) + { + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS, gGameCommandErrorText); + } + + TileElement* tileElement = tile_element_insert({ _loc.x / 32, _loc.y / 32, targetHeight / 8 }, 0b0000); + assert(tileElement != nullptr); + + map_animation_create(MAP_ANIMATION_TYPE_WALL, _loc.x, _loc.y, targetHeight / 8); + + tileElement->SetType(TILE_ELEMENT_TYPE_WALL); + WallElement* wallElement = tileElement->AsWall(); + wallElement->clearance_height = clearanceHeight; + wallElement->SetDirection(_edge); + // TODO: Normalise the edge slope code. + wallElement->SetSlope(edgeSlope >> 6); + + wallElement->SetPrimaryColour(_primaryColour); + wallElement->SetSecondaryColour(_secondaryColour); + + if (wallAcrossTrack) + { + wallElement->SetAcrossTrack(true); + } + + wallElement->SetEntryIndex(_wallType); + if (_bannerId != BANNER_INDEX_NULL) + { + wallElement->SetBannerIndex(_bannerId); + } + + if (wallEntry->wall.flags & WALL_SCENERY_HAS_TERNARY_COLOUR) + { + wallElement->SetTertiaryColour(_tertiaryColour); + } + + if (GetFlags() & GAME_COMMAND_FLAG_GHOST) + { + wallElement->SetGhost(true); + } + + res->tileElement = tileElement; + map_invalidate_tile_zoom1(_loc.x, _loc.y, wallElement->base_height * 8, wallElement->base_height * 8 + 72); + + res->Cost = wallEntry->wall.price; + return res; + } + +private: +#pragma region Edge Slopes Table + + // clang-format off + enum EDGE_SLOPE + { + EDGE_SLOPE_ELEVATED = (1 << 0), // 0x01 + EDGE_SLOPE_UPWARDS = (1 << 6), // 0x40 + EDGE_SLOPE_DOWNWARDS = (1 << 7), // 0x80 + + EDGE_SLOPE_UPWARDS_ELEVATED = EDGE_SLOPE_UPWARDS | EDGE_SLOPE_ELEVATED, + EDGE_SLOPE_DOWNWARDS_ELEVATED = EDGE_SLOPE_DOWNWARDS | EDGE_SLOPE_ELEVATED, + }; + + /** rct2: 0x009A3FEC */ + static constexpr const uint8_t EdgeSlopes[][4] = { + // Top right Bottom right Bottom left Top left + { 0, 0, 0, 0 }, + { 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, 0 }, + { 0, 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS }, + { 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS }, + { EDGE_SLOPE_DOWNWARDS, 0, 0, EDGE_SLOPE_UPWARDS }, + { EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS }, + { EDGE_SLOPE_DOWNWARDS, 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED }, + { EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED }, + { EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, 0, 0 }, + { EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, 0 }, + { EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS }, + { EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS }, + { EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, 0, EDGE_SLOPE_UPWARDS }, + { EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS }, + { EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED }, + { EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS }, + { 0, 0, 0, 0 }, + { EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS }, + { EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED }, + { 0, 0, 0, 0 }, + }; + // clang-format on + +#pragma endregion + + /** + * + * rct2: 0x006E5CBA + */ + bool WallCheckObstructionWithTrack(rct_scenery_entry * wall, int32_t z0, TrackElement * trackElement, bool* wallAcrossTrack) + const + { + int32_t trackType = trackElement->GetTrackType(); + int32_t sequence = trackElement->GetSequenceIndex(); + int32_t direction = (_edge - trackElement->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK; + auto ride = get_ride(trackElement->GetRideIndex()); + if (ride == nullptr) + { + return false; + } + + if (TrackIsAllowedWallEdges(ride->type, trackType, sequence, direction)) + { + return true; + } + + if (!(wall->wall.flags & WALL_SCENERY_IS_DOOR)) + { + return false; + } + + if (RideGroupManager::RideTypeHasRideGroups(ride->type)) + { + auto rideEntry = get_ride_entry(ride->subtype); + if (rideEntry == nullptr) + { + return false; + } + auto rideGroup = RideGroupManager::GetRideGroup(ride->type, rideEntry); + if (rideGroup == nullptr) + { + return false; + } + if (!(rideGroup->Flags & RIDE_GROUP_FLAG_ALLOW_DOORS_ON_TRACK)) + { + return false; + } + } + else if (!(RideData4[ride->type].flags & RIDE_TYPE_FLAG4_ALLOW_DOORS_ON_TRACK)) + { + return false; + } + + *wallAcrossTrack = true; + if (z0 & 1) + { + return false; + } + + int32_t z; + if (sequence == 0) + { + if (TrackSequenceProperties[trackType][0] & TRACK_SEQUENCE_FLAG_DISALLOW_DOORS) + { + return false; + } + + if (TrackDefinitions[trackType].bank_start == 0) + { + if (!(TrackCoordinates[trackType].rotation_begin & 4)) + { + direction = direction_reverse(trackElement->GetDirection()); + if (direction == _edge) + { + const rct_preview_track* trackBlock = &TrackBlocks[trackType][sequence]; + z = TrackCoordinates[trackType].z_begin; + z = trackElement->base_height + ((z - trackBlock->z) * 8); + if (z == z0) + { + return true; + } + } + } + } + } + + const rct_preview_track* trackBlock = &TrackBlocks[trackType][sequence + 1]; + if (trackBlock->index != 0xFF) + { + return false; + } + + if (TrackDefinitions[trackType].bank_end != 0) + { + return false; + } + + direction = TrackCoordinates[trackType].rotation_end; + if (direction & 4) + { + return false; + } + + direction = trackElement->GetDirection(); + if (direction != _edge) + { + return false; + } + + trackBlock = &TrackBlocks[trackType][sequence]; + z = TrackCoordinates[trackType].z_end; + z = trackElement->base_height + ((z - trackBlock->z) * 8); + return z == z0; + } + + /** + * + * rct2: 0x006E5C1A + */ + bool WallCheckObstruction(rct_scenery_entry * wall, int32_t z0, int32_t z1, bool* wallAcrossTrack) const + { + int32_t entryType, sequence; + rct_scenery_entry* entry; + rct_large_scenery_tile* tile; + + *wallAcrossTrack = false; + gMapGroundFlags = ELEMENT_IS_ABOVE_GROUND; + if (map_is_location_at_edge(_loc)) + { + gGameCommandErrorText = STR_OFF_EDGE_OF_MAP; + return false; + } + + TileElement* tileElement = map_get_first_element_at(_loc.x / 32, _loc.y / 32); + do + { + if (tileElement == nullptr) + break; + int32_t elementType = tileElement->GetType(); + if (elementType == TILE_ELEMENT_TYPE_SURFACE) + continue; + if (tileElement->IsGhost()) + continue; + if (z0 >= tileElement->clearance_height) + continue; + if (z1 <= tileElement->base_height) + continue; + if (elementType == TILE_ELEMENT_TYPE_WALL) + { + int32_t direction = tileElement->GetDirection(); + if (_edge == direction) + { + map_obstruction_set_error_text(tileElement); + return false; + } + continue; + } + if (tileElement->GetOccupiedQuadrants() == 0) + continue; + + switch (elementType) + { + case TILE_ELEMENT_TYPE_ENTRANCE: + map_obstruction_set_error_text(tileElement); + return false; + case TILE_ELEMENT_TYPE_PATH: + if (tileElement->AsPath()->GetEdges() & (1 << _edge)) + { + map_obstruction_set_error_text(tileElement); + return false; + } + break; + case TILE_ELEMENT_TYPE_LARGE_SCENERY: + entryType = tileElement->AsLargeScenery()->GetEntryIndex(); + sequence = tileElement->AsLargeScenery()->GetSequenceIndex(); + entry = get_large_scenery_entry(entryType); + tile = &entry->large_scenery.tiles[sequence]; + { + int32_t direction = ((_edge - tileElement->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK) + 8; + if (!(tile->flags & (1 << direction))) + { + map_obstruction_set_error_text(tileElement); + return false; + } + } + break; + case TILE_ELEMENT_TYPE_SMALL_SCENERY: + entry = tileElement->AsSmallScenery()->GetEntry(); + if (scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_NO_WALLS)) + { + map_obstruction_set_error_text(tileElement); + return false; + } + break; + case TILE_ELEMENT_TYPE_TRACK: + if (!WallCheckObstructionWithTrack(wall, z0, tileElement->AsTrack(), wallAcrossTrack)) + { + return false; + } + break; + } + } while (!(tileElement++)->IsLastForTile()); + + return true; + } + + /** + * Gets whether the given track type can have a wall placed on the edge of the given direction. + * Some thin tracks for example are allowed to have walls either side of the track, but wider tracks can not. + */ + static bool TrackIsAllowedWallEdges(uint8_t rideType, uint8_t trackType, uint8_t trackSequence, uint8_t direction) + { + if (!ride_type_has_flag(rideType, RIDE_TYPE_FLAG_TRACK_NO_WALLS)) + { + if (ride_type_has_flag(rideType, RIDE_TYPE_FLAG_FLAT_RIDE)) + { + if (FlatRideTrackSequenceElementAllowedWallEdges[trackType][trackSequence] & (1 << direction)) + { + return true; + } + } + else + { + if (TrackSequenceElementAllowedWallEdges[trackType][trackSequence] & (1 << direction)) + { + return true; + } + } + } + return false; + } +}; diff --git a/src/openrct2/actions/WallRemoveAction.hpp b/src/openrct2/actions/WallRemoveAction.hpp index ea385c75dd..993e88bd60 100644 --- a/src/openrct2/actions/WallRemoveAction.hpp +++ b/src/openrct2/actions/WallRemoveAction.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -22,12 +22,12 @@ DEFINE_GAME_ACTION(WallRemoveAction, GAME_COMMAND_REMOVE_WALL, GameActionResult) { private: - TileCoordsXYZD _location; + CoordsXYZD _loc; public: WallRemoveAction() = default; - WallRemoveAction(const TileCoordsXYZD& location) - : _location(location) + WallRemoveAction(const CoordsXYZD& loc) + : _loc(loc) { } @@ -35,7 +35,7 @@ public: { GameAction::Serialise(stream); - stream << DS_TAG(_location.x) << DS_TAG(_location.y) << DS_TAG(_location.z) << DS_TAG(_location.direction); + stream << DS_TAG(_loc); } GameActionResult::Ptr Query() const override @@ -44,20 +44,19 @@ public: res->Cost = 0; res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - if (!map_is_location_valid({ _location.x << 5, _location.y << 5 })) + if (!map_is_location_valid(_loc)) { return std::make_unique( GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS, STR_INVALID_SELECTION_OF_OBJECTS); } const bool isGhost = GetFlags() & GAME_COMMAND_FLAG_GHOST; - if (!isGhost && !(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode - && !map_is_location_owned(_location.x << 5, _location.y << 5, _location.z << 3)) + if (!isGhost && !(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode && !map_is_location_owned(_loc)) { return std::make_unique(GA_ERROR::NOT_OWNED, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK); } - TileElement* wallElement = GetFirstWallElementAt(_location, isGhost); + TileElement* wallElement = GetFirstWallElementAt(_loc, isGhost); if (wallElement == nullptr) { return std::make_unique( @@ -76,29 +75,28 @@ public: const bool isGhost = GetFlags() & GAME_COMMAND_FLAG_GHOST; - TileElement* wallElement = GetFirstWallElementAt(_location, isGhost); + TileElement* wallElement = GetFirstWallElementAt(_loc, isGhost); if (wallElement == nullptr) { return std::make_unique( GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS, STR_INVALID_SELECTION_OF_OBJECTS); } - res->Position.x = (_location.x << 5) + 16; - res->Position.y = (_location.y << 5) + 16; - res->Position.z = tile_element_height(res->Position.x, res->Position.y); + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = tile_element_height(res->Position); tile_element_remove_banner_entry(wallElement); - map_invalidate_tile_zoom1( - _location.x << 5, _location.y << 5, wallElement->base_height * 8, (wallElement->base_height * 8) + 72); + map_invalidate_tile_zoom1(_loc.x, _loc.y, wallElement->base_height * 8, (wallElement->base_height * 8) + 72); tile_element_remove(wallElement); return res; } private: - TileElement* GetFirstWallElementAt(const TileCoordsXYZD& location, bool isGhost) const + TileElement* GetFirstWallElementAt(const CoordsXYZD& location, bool isGhost) const { - TileElement* tileElement = map_get_first_element_at(location.x, location.y); + TileElement* tileElement = map_get_first_element_at(location.x / 32, location.y / 32); if (!tileElement) return nullptr; @@ -106,7 +104,7 @@ private: { if (tileElement->GetType() != TILE_ELEMENT_TYPE_WALL) continue; - if (tileElement->base_height != location.z) + if (tileElement->base_height != location.z / 8) continue; if (tileElement->GetDirection() != location.direction) continue; diff --git a/src/openrct2/actions/WallSetColourAction.hpp b/src/openrct2/actions/WallSetColourAction.hpp new file mode 100644 index 0000000000..4091d72287 --- /dev/null +++ b/src/openrct2/actions/WallSetColourAction.hpp @@ -0,0 +1,160 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../OpenRCT2.h" +#include "../management/Finance.h" +#include "../ride/RideGroupManager.h" +#include "../ride/Track.h" +#include "../ride/TrackData.h" +#include "../world/Banner.h" +#include "../world/LargeScenery.h" +#include "../world/MapAnimation.h" +#include "../world/Scenery.h" +#include "../world/SmallScenery.h" +#include "../world/Surface.h" +#include "GameAction.h" + +DEFINE_GAME_ACTION(WallSetColourAction, GAME_COMMAND_SET_WALL_COLOUR, GameActionResult) +{ +private: + CoordsXYZD _loc; + int32_t _primaryColour; + int32_t _secondaryColour; + int32_t _tertiaryColour; + +public: + WallSetColourAction() + { + } + + WallSetColourAction(CoordsXYZD loc, int32_t primaryColour, int32_t secondaryColour, int32_t tertiaryColour) + : _loc(loc) + , _primaryColour(primaryColour) + , _secondaryColour(secondaryColour) + , _tertiaryColour(tertiaryColour) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_loc) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour) << DS_TAG(_tertiaryColour); + } + + GameActionResult::Ptr Query() const override + { + auto res = MakeResult(); + res->ErrorTitle = STR_CANT_REPAINT_THIS; + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = _loc.z; + + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_in_park(_loc) && !gCheatsSandboxMode) + { + return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_REPAINT_THIS, STR_LAND_NOT_OWNED_BY_PARK); + } + + auto wallElement = map_get_wall_element_at(_loc.x, _loc.y, _loc.z / 8, _loc.direction); + if (wallElement == nullptr) + { + log_error( + "Could not find wall element at: x = %d, y = %d, z = %d, direction = %u", _loc.x, _loc.y, _loc.z, + _loc.direction); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + if ((GetFlags() & GAME_COMMAND_FLAG_GHOST) && !(wallElement->IsGhost())) + { + return res; + } + + rct_scenery_entry* sceneryEntry = wallElement->GetEntry(); + if (sceneryEntry == nullptr) + { + log_error("Could not find wall object"); + return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_REPAINT_THIS); + } + + if (_primaryColour > 31) + { + log_error("Primary colour invalid: colour = %d", _primaryColour); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + if (_secondaryColour > 31) + { + log_error("Secondary colour invalid: colour = %d", _secondaryColour); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + if (sceneryEntry->wall.flags & WALL_SCENERY_HAS_TERNARY_COLOUR) + { + if (_tertiaryColour > 31) + { + log_error("Tertiary colour invalid: colour = %d", _tertiaryColour); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + } + return res; + } + + GameActionResult::Ptr Execute() const override + { + auto res = MakeResult(); + res->ErrorTitle = STR_CANT_REPAINT_THIS; + res->Position.x = _loc.x + 16; + res->Position.y = _loc.y + 16; + res->Position.z = _loc.z; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + auto wallElement = map_get_wall_element_at(_loc.x, _loc.y, _loc.z / 8, _loc.direction); + if (wallElement == nullptr) + { + log_error( + "Could not find wall element at: x = %d, y = %d, z = %d, direction = %u", _loc.x, _loc.y, _loc.z, + _loc.direction); + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REPAINT_THIS); + } + + if ((GetFlags() & GAME_COMMAND_FLAG_GHOST) && !(wallElement->IsGhost())) + { + return res; + } + + rct_scenery_entry* sceneryEntry = wallElement->GetEntry(); + if (sceneryEntry == nullptr) + { + log_error("Could not find wall object"); + return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_REPAINT_THIS); + } + + wallElement->SetPrimaryColour(_primaryColour); + wallElement->SetSecondaryColour(_secondaryColour); + + if (sceneryEntry->wall.flags & WALL_SCENERY_HAS_TERNARY_COLOUR) + { + wallElement->SetTertiaryColour(_tertiaryColour); + } + map_invalidate_tile_zoom1(_loc.x, _loc.y, _loc.z, _loc.z + 72); + + return res; + } + +private: +}; diff --git a/src/openrct2/actions/WaterLowerAction.hpp b/src/openrct2/actions/WaterLowerAction.hpp new file mode 100644 index 0000000000..3218bbcbb9 --- /dev/null +++ b/src/openrct2/actions/WaterLowerAction.hpp @@ -0,0 +1,148 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once +#include "../audio/audio.h" +#include "GameAction.h" +#include "WaterSetHeightAction.hpp" + +DEFINE_GAME_ACTION(WaterLowerAction, GAME_COMMAND_LOWER_WATER, GameActionResult) +{ +private: + MapRange _range; + +public: + WaterLowerAction() + { + } + WaterLowerAction(MapRange range) + : _range(range) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_range); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + + // Keep big coordinates within map boundaries + auto aX = std::max(32, _range.GetLeft()); + auto bX = std::min(gMapSizeMaxXY, _range.GetRight()); + auto aY = std::max(32, _range.GetTop()); + auto bY = std::min(gMapSizeMaxXY, _range.GetBottom()); + + MapRange validRange = MapRange{ aX, aY, bX, bY }; + + res->Position.x = ((validRange.GetLeft() + validRange.GetRight()) / 2) + 16; + res->Position.y = ((validRange.GetTop() + validRange.GetBottom()) / 2) + 16; + int16_t z = tile_element_height(res->Position); + int16_t waterHeight = tile_element_water_height(res->Position); + if (waterHeight != 0) + { + z = waterHeight; + } + res->Position.z = z; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + uint8_t minHeight = GetLowestHeight(validRange); + bool hasChanged = false; + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + auto* surfaceElement = map_get_surface_element_at(x / 32, y / 32); + if (surfaceElement == nullptr) + continue; + + uint8_t height = surfaceElement->GetWaterHeight(); + if (height == 0) + continue; + + height *= 2; + if (height < minHeight) + continue; + + height -= 2; + auto waterSetHeightAction = WaterSetHeightAction({ x, y }, height); + waterSetHeightAction.SetFlags(GetFlags()); + auto result = isExecuting ? GameActions::ExecuteNested(&waterSetHeightAction) + : GameActions::QueryNested(&waterSetHeightAction); + if (result->Error == GA_ERROR::OK) + { + res->Cost += result->Cost; + hasChanged = true; + } + else + { + result->ErrorTitle = STR_CANT_LOWER_WATER_LEVEL_HERE; + return result; + } + } + } + + if (isExecuting && hasChanged) + { + audio_play_sound_at_location(SoundId::LayingOutWater, res->Position); + } + // Force ride construction to recheck area + _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; + + return res; + } + +private: + uint8_t GetLowestHeight(MapRange validRange) const + { + // The lowest height to lower the water to is the highest water level in the selection + uint8_t minHeight{ 0 }; + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + auto* surfaceElement = map_get_surface_element_at({ x, y }); + if (surfaceElement == nullptr) + continue; + + uint8_t height = surfaceElement->GetWaterHeight(); + if (height == 0) + continue; + + height *= 2; + if (height > minHeight) + { + minHeight = height; + } + } + } + + return minHeight; + } +}; diff --git a/src/openrct2/actions/WaterRaiseAction.hpp b/src/openrct2/actions/WaterRaiseAction.hpp new file mode 100644 index 0000000000..666214a233 --- /dev/null +++ b/src/openrct2/actions/WaterRaiseAction.hpp @@ -0,0 +1,156 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../audio/audio.h" +#include "GameAction.h" +#include "WaterSetHeightAction.hpp" + +DEFINE_GAME_ACTION(WaterRaiseAction, GAME_COMMAND_RAISE_WATER, GameActionResult) +{ +private: + MapRange _range; + +public: + WaterRaiseAction() + { + } + WaterRaiseAction(MapRange range) + : _range(range) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags(); + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + + stream << DS_TAG(_range); + } + + GameActionResult::Ptr Query() const override + { + return QueryExecute(false); + } + + GameActionResult::Ptr Execute() const override + { + return QueryExecute(true); + } + +private: + GameActionResult::Ptr QueryExecute(bool isExecuting) const + { + auto res = MakeResult(); + + // Keep big coordinates within map boundaries + auto aX = std::max(32, _range.GetLeft()); + auto bX = std::min(gMapSizeMaxXY, _range.GetRight()); + auto aY = std::max(32, _range.GetTop()); + auto bY = std::min(gMapSizeMaxXY, _range.GetBottom()); + + MapRange validRange = MapRange{ aX, aY, bX, bY }; + + res->Position.x = ((validRange.GetLeft() + validRange.GetRight()) / 2) + 16; + res->Position.y = ((validRange.GetTop() + validRange.GetBottom()) / 2) + 16; + int32_t z = tile_element_height(res->Position); + int16_t waterHeight = tile_element_water_height(res->Position); + if (waterHeight != 0) + { + z = waterHeight; + } + res->Position.z = z; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + uint8_t maxHeight = GetHighestHeight(validRange); + bool hasChanged = false; + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + auto surfaceElement = map_get_surface_element_at(x / 32, y / 32); + if (surfaceElement == nullptr) + continue; + uint8_t height = surfaceElement->GetWaterHeight(); + + if (surfaceElement->base_height > maxHeight) + continue; + + if (height != 0) + { + height *= 2; + if (height > maxHeight) + continue; + height += 2; + } + else + { + height = surfaceElement->base_height + 2; + } + auto waterSetHeightAction = WaterSetHeightAction({ x, y }, height); + waterSetHeightAction.SetFlags(GetFlags()); + auto result = isExecuting ? GameActions::ExecuteNested(&waterSetHeightAction) + : GameActions::QueryNested(&waterSetHeightAction); + if (result->Error == GA_ERROR::OK) + { + res->Cost += result->Cost; + hasChanged = true; + } + else + { + result->ErrorTitle = STR_CANT_RAISE_WATER_LEVEL_HERE; + return result; + } + } + } + + if (isExecuting && hasChanged) + { + audio_play_sound_at_location(SoundId::LayingOutWater, res->Position); + } + // Force ride construction to recheck area + _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; + + return res; + } + +private: + uint8_t GetHighestHeight(MapRange validRange) const + { + // The highest height to raise the water to is the lowest water level in the selection + uint8_t maxHeight{ 255 }; + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + auto* surfaceElement = map_get_surface_element_at({ x, y }); + if (surfaceElement == nullptr) + continue; + + uint8_t height = surfaceElement->base_height; + if (surfaceElement->GetWaterHeight() > 0) + { + height = surfaceElement->GetWaterHeight() * 2; + } + + if (maxHeight > height) + { + maxHeight = height; + } + } + } + + return maxHeight; + } +}; diff --git a/src/openrct2/actions/WaterSetHeightAction.hpp b/src/openrct2/actions/WaterSetHeightAction.hpp index e7b76ae133..f797fc02aa 100644 --- a/src/openrct2/actions/WaterSetHeightAction.hpp +++ b/src/openrct2/actions/WaterSetHeightAction.hpp @@ -71,7 +71,7 @@ public: } } - SurfaceElement* surfaceElement = map_get_surface_element_at(_coords)->AsSurface(); + SurfaceElement* surfaceElement = map_get_surface_element_at(_coords); if (surfaceElement == nullptr) { log_error("Could not find surface element at: x %u, y %u", _coords.x, _coords.y); @@ -113,12 +113,12 @@ public: res->Position.y = _coords.y + 16; res->Position.z = _height * 8; - int32_t surfaceHeight = tile_element_height(_coords.x, _coords.y) & 0xFFFF; + int32_t surfaceHeight = tile_element_height(_coords); footpath_remove_litter(_coords.x, _coords.y, surfaceHeight); if (!gCheatsDisableClearanceChecks) wall_remove_at_z(_coords.x, _coords.y, surfaceHeight); - SurfaceElement* surfaceElement = map_get_surface_element_at(_coords)->AsSurface(); + SurfaceElement* surfaceElement = map_get_surface_element_at(_coords); if (surfaceElement == nullptr) { log_error("Could not find surface element at: x %u, y %u", _coords.x, _coords.y); diff --git a/src/openrct2/audio/Audio.cpp b/src/openrct2/audio/Audio.cpp index a52ab8e2b3..2122a3b262 100644 --- a/src/openrct2/audio/Audio.cpp +++ b/src/openrct2/audio/Audio.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -49,7 +49,7 @@ void* gTitleMusicChannel = nullptr; void* gRainSoundChannel = nullptr; rct_ride_music gRideMusicList[AUDIO_MAX_RIDE_MUSIC]; -rct_ride_music_params gRideMusicParamsList[6]; +rct_ride_music_params gRideMusicParamsList[AUDIO_MAX_RIDE_MUSIC]; rct_ride_music_params* gRideMusicParamsListEnd; rct_vehicle_sound gVehicleSoundList[AUDIO_MAX_VEHICLE_SOUNDS]; @@ -57,75 +57,75 @@ rct_vehicle_sound_params gVehicleSoundParamsList[AUDIO_MAX_VEHICLE_SOUNDS]; rct_vehicle_sound_params* gVehicleSoundParamsListEnd; // clang-format off -static int32_t SoundVolumeAdjust[SOUND_MAXID] = +static int32_t SoundVolumeAdjust[RCT2SoundCount] = { - 0, // SOUND_LIFT_1 - 0, // SOUND_TRACK_FRICTION_1 - 0, // SOUND_LIFT_2 - 0, // SOUND_SCREAM_1 - 0, // SOUND_CLICK_1 - 0, // SOUND_CLICK_2 - 0, // SOUND_PLACE_ITEM - 0, // SOUND_SCREAM_2 - 0, // SOUND_SCREAM_3 - 0, // SOUND_SCREAM_4 - 0, // SOUND_SCREAM_5 - 0, // SOUND_SCREAM_6 - 0, // SOUND_LIFT_3 - -400, // SOUND_PURCHASE - 0, // SOUND_CRASH - 0, // SOUND_LAYING_OUT_WATER - 0, // SOUND_WATER_1 - 0, // SOUND_WATER_2 - 0, // SOUND_TRAIN_WHISTLE - 0, // SOUND_TRAIN_CHUGGING - -1000, // SOUND_WATER_SPLASH - 0, // SOUND_HAMMERING - -800, // SOUND_RIDE_LAUNCH_1 - -1700, // SOUND_RIDE_LAUNCH_2 - -700, // SOUND_COUGH_1 - -700, // SOUND_COUGH_2 - -700, // SOUND_COUGH_3 - -700, // SOUND_COUGH_4 - 0, // SOUND_RAIN_1 - 0, // SOUND_THUNDER_1 - 0, // SOUND_THUNDER_2 - 0, // SOUND_RAIN_2 - 0, // SOUND_RAIN_3 - 0, // SOUND_BALLOON_POP - -700, // SOUND_MECHANIC_FIX - 0, // SOUND_SCREAM_7 - -2500, // SOUND_TOILET_FLUSH original value: -1000 - 0, // SOUND_CLICK_3 - 0, // SOUND_QUACK - 0, // SOUND_NEWS_ITEM - 0, // SOUND_WINDOW_OPEN - -900, // SOUND_LAUGH_1 - -900, // SOUND_LAUGH_2 - -900, // SOUND_LAUGH_3 - 0, // SOUND_APPLAUSE - -600, // SOUND_HAUNTED_HOUSE_SCARE - -700, // SOUND_HAUNTED_HOUSE_SCREAM_1 - -700, // SOUND_HAUNTED_HOUSE_SCREAM_2 - -2550, // SOUND_48 - -2900, // SOUND_49 - 0, // SOUND_ERROR - -3400, // SOUND_51 - 0, // SOUND_LIFT_4 - 0, // SOUND_LIFT_5 - 0, // SOUND_TRACK_FRICTION_2 - 0, // SOUND_LIFT_6 - 0, // SOUND_LIFT_7 - 0, // SOUND_TRACK_FRICTION_3 - 0, // SOUND_SCREAM_8 - 0, // SOUND_TRAM - -2000, // SOUND_DOOR_OPEN - -2700, // SOUND_DOOR_CLOSE - -700 // SOUND_62 + 0, // LiftClassic + 0, // TrackFrictionClassicWood + 0, // FrictionClassic + 0, // Scream1 + 0, // Click1 + 0, // Click2 + 0, // PlaceItem + 0, // Scream2 + 0, // Scream3 + 0, // Scream4 + 0, // Scream5 + 0, // Scream6 + 0, // LiftFrictionWheels + -400, // Purchase + 0, // Crash + 0, // LayingOutWater + 0, // Water1 + 0, // Water2 + 0, // TrainWhistle + 0, // TrainDeparting + -1000, // WaterSplash + 0, // GoKartEngine + -800, // RideLaunch1 + -1700, // RideLaunch2 + -700, // Cough1 + -700, // Cough2 + -700, // Cough3 + -700, // Cough4 + 0, // Rain + 0, // Thunder1 + 0, // Thunder2 + 0, // TrackFrictionTrain + 0, // TrackFrictionWater + 0, // BalloonPop + -700, // MechanicFix + 0, // Scream7 + -2500, // ToiletFlush original value: -1000 + 0, // Click3 + 0, // Quack + 0, // NewsItem + 0, // WindowOpen + -900, // Laugh1 + -900, // Laugh2 + -900, // Laugh3 + 0, // Applause + -600, // HauntedHouseScare + -700, // HauntedHouseScream1 + -700, // HauntedHouseScream2 + -2550, // BlockBrakeClose + -2900, // BlockBrakeRelease + 0, // Error + -3400, // BrakeRelease + 0, // LiftArrow + 0, // LiftWood + 0, // TrackFrictionWood + 0, // LiftWildMouse + 0, // LiftBM + 0, // TrackFrictionBM + 0, // Scream8 + 0, // Tram + -2000, // DoorOpen + -2700, // DoorClose + -700 // Portcullis }; // clang-format on -AudioParams audio_get_params_from_location(int32_t soundId, const LocationXYZ16* location); +static AudioParams audio_get_params_from_location(SoundId soundId, const CoordsXYZ& location); void audio_init() { @@ -180,22 +180,16 @@ void audio_populate_devices() } } -int32_t audio_play_sound_at_location(int32_t soundId, int16_t x, int16_t y, int16_t z) +void audio_play_sound_at_location(SoundId soundId, const CoordsXYZ& loc) { if (gGameSoundsOff) - return 0; + return; - LocationXYZ16 location; - location.x = x; - location.y = y; - location.z = z; - - AudioParams params = audio_get_params_from_location(soundId, &location); + AudioParams params = audio_get_params_from_location(soundId, loc); if (params.in_range) { - soundId = audio_play_sound(soundId, params.volume, params.pan); + audio_play_sound(soundId, params.volume, params.pan); } - return soundId; } /** @@ -204,7 +198,7 @@ int32_t audio_play_sound_at_location(int32_t soundId, int16_t x, int16_t y, int1 * @param location The location at which the sound effect is to be played. * @return The audio parameters to be used when playing this sound effect. */ -AudioParams audio_get_params_from_location(int32_t soundId, const LocationXYZ16* location) +static AudioParams audio_get_params_from_location(SoundId soundId, const CoordsXYZ& location) { int32_t volumeDown = 0; AudioParams params; @@ -212,14 +206,14 @@ AudioParams audio_get_params_from_location(int32_t soundId, const LocationXYZ16* params.volume = 0; params.pan = 0; - TileElement* element = map_get_surface_element_at({ location->x, location->y }); - if (element && (element->base_height * 8) - 5 > location->z) + auto element = map_get_surface_element_at(location); + if (element && (element->base_height * 8) - 5 > location.z) { volumeDown = 10; } uint8_t rotation = get_current_rotation(); - LocationXY16 pos2 = coordinate_3d_to_2d(location, rotation); + auto pos2 = translate_3d_to_2d_with_z(rotation, location); rct_viewport* viewport = nullptr; while ((viewport = window_get_previous_viewport(viewport)) != nullptr) @@ -229,7 +223,8 @@ AudioParams audio_get_params_from_location(int32_t soundId, const LocationXYZ16* int16_t vy = pos2.y - viewport->view_y; int16_t vx = pos2.x - viewport->view_x; params.pan = viewport->x + (vx >> viewport->zoom); - params.volume = SoundVolumeAdjust[soundId] + ((-1024 * viewport->zoom - 1) * (1 << volumeDown)) + 1; + params.volume = SoundVolumeAdjust[static_cast(soundId)] + + ((-1024 * viewport->zoom - 1) * (1 << volumeDown)) + 1; if (vy < 0 || vy >= viewport->view_height || vx < 0 || vx >= viewport->view_width || params.volume < -10000) { @@ -242,10 +237,10 @@ AudioParams audio_get_params_from_location(int32_t soundId, const LocationXYZ16* return params; } -int32_t audio_play_sound(int32_t soundId, int32_t volume, int32_t pan) +void audio_play_sound(SoundId soundId, int32_t volume, int32_t pan) { if (gGameSoundsOff) - return 0; + return; int32_t mixerPan = 0; if (pan != AUDIO_PLAY_AT_CENTRE) @@ -256,7 +251,6 @@ int32_t audio_play_sound(int32_t soundId, int32_t volume, int32_t pan) } Mixer_Play_Effect(soundId, MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(mixerPan), 1, 1); - return 0; } void audio_start_title_music() @@ -435,11 +429,11 @@ void audio_stop_vehicle_sounds() if (vehicleSound.id != SOUND_ID_NULL) { vehicleSound.id = SOUND_ID_NULL; - if (vehicleSound.sound1_id != SOUND_ID_NULL) + if (vehicleSound.sound1_id != SoundId::Null) { Mixer_Stop_Channel(vehicleSound.sound1_channel); } - if (vehicleSound.sound2_id != SOUND_ID_NULL) + if (vehicleSound.sound2_id != SoundId::Null) { Mixer_Stop_Channel(vehicleSound.sound2_channel); } diff --git a/src/openrct2/audio/AudioChannel.h b/src/openrct2/audio/AudioChannel.h index c3eca7df73..a0823c7386 100644 --- a/src/openrct2/audio/AudioChannel.h +++ b/src/openrct2/audio/AudioChannel.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/audio/AudioContext.h b/src/openrct2/audio/AudioContext.h index 254d898fef..9093270c53 100644 --- a/src/openrct2/audio/AudioContext.h +++ b/src/openrct2/audio/AudioContext.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/audio/AudioMixer.cpp b/src/openrct2/audio/AudioMixer.cpp index 5a4df89319..f19b11c661 100644 --- a/src/openrct2/audio/AudioMixer.cpp +++ b/src/openrct2/audio/AudioMixer.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -37,12 +37,12 @@ void Mixer_Init(const char* device) audioContext->SetOutputDevice(std::string(device)); } -void* Mixer_Play_Effect(size_t id, int32_t loop, int32_t volume, float pan, double rate, int32_t deleteondone) +void* Mixer_Play_Effect(SoundId id, int32_t loop, int32_t volume, float pan, double rate, int32_t deleteondone) { IAudioChannel* channel = nullptr; if (gConfigSound.sound_enabled) { - if (id >= SOUND_MAXID) + if (static_cast(id) >= RCT2SoundCount) { log_error("Tried to play an invalid sound id. %i", id); } @@ -52,7 +52,7 @@ void* Mixer_Play_Effect(size_t id, int32_t loop, int32_t volume, float pan, doub if (mixer != nullptr) { mixer->Lock(); - IAudioSource* source = mixer->GetSoundSource((int32_t)id); + IAudioSource* source = mixer->GetSoundSource(id); channel = mixer->Play(source, loop, deleteondone != 0, false); if (channel != nullptr) { diff --git a/src/openrct2/audio/AudioMixer.h b/src/openrct2/audio/AudioMixer.h index e5b88bc514..8b883ae4cf 100644 --- a/src/openrct2/audio/AudioMixer.h +++ b/src/openrct2/audio/AudioMixer.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -15,6 +15,8 @@ #define MIXER_LOOP_NONE 0 #define MIXER_LOOP_INFINITE (-1) +enum class SoundId : uint8_t; + enum MIXER_GROUP { MIXER_GROUP_SOUND, @@ -43,7 +45,7 @@ namespace OpenRCT2::Audio virtual bool LoadMusic(size_t pathid) abstract; virtual void SetVolume(float volume) abstract; - virtual IAudioSource* GetSoundSource(int32_t id) abstract; + virtual IAudioSource* GetSoundSource(SoundId id) abstract; virtual IAudioSource* GetMusicSource(int32_t id) abstract; }; } // namespace OpenRCT2::Audio @@ -56,7 +58,7 @@ namespace OpenRCT2::Audio #endif void Mixer_Init(const char* device); -void* Mixer_Play_Effect(size_t id, int32_t loop, int32_t volume, float pan, double rate, int32_t deleteondone); +void* Mixer_Play_Effect(SoundId id, int32_t loop, int32_t volume, float pan, double rate, int32_t deleteondone); void Mixer_Stop_Channel(void* channel); void Mixer_Channel_Volume(void* channel, int32_t volume); void Mixer_Channel_Pan(void* channel, float pan); diff --git a/src/openrct2/audio/AudioSource.h b/src/openrct2/audio/AudioSource.h index 7b5bedc962..60869206fe 100644 --- a/src/openrct2/audio/AudioSource.h +++ b/src/openrct2/audio/AudioSource.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/audio/DummyAudioContext.cpp b/src/openrct2/audio/DummyAudioContext.cpp index 9730a294b4..71fedfa927 100644 --- a/src/openrct2/audio/DummyAudioContext.cpp +++ b/src/openrct2/audio/DummyAudioContext.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/audio/NullAudioSource.cpp b/src/openrct2/audio/NullAudioSource.cpp index 33d15922d2..10166f7952 100644 --- a/src/openrct2/audio/NullAudioSource.cpp +++ b/src/openrct2/audio/NullAudioSource.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -8,6 +8,7 @@ *****************************************************************************/ #include "AudioSource.h" +#include "audio.h" namespace OpenRCT2::Audio { diff --git a/src/openrct2/audio/audio.h b/src/openrct2/audio/audio.h index b855813b55..0645292d24 100644 --- a/src/openrct2/audio/audio.h +++ b/src/openrct2/audio/audio.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,13 +13,16 @@ #include "../ride/RideTypes.h" #define AUDIO_DEVICE_NAME_SIZE 256 -#define AUDIO_MAX_RIDE_MUSIC 2 +#define AUDIO_MAX_RIDE_MUSIC 32 #define AUDIO_MAX_VEHICLE_SOUNDS 14 #define NUM_DEFAULT_MUSIC_TRACKS 46 #define AUDIO_PLAY_AT_CENTRE 0x8000 #define AUDIO_PLAY_AT_LOCATION 0x8001 #define SOUND_ID_NULL 0xFFFF +enum class SoundId : uint8_t; +struct CoordsXYZ; + struct audio_device { char name[AUDIO_DEVICE_NAME_SIZE]; @@ -56,11 +59,11 @@ struct rct_vehicle_sound { uint16_t id; int16_t volume; - uint16_t sound1_id; + SoundId sound1_id; int16_t sound1_volume; int16_t sound1_pan; uint16_t sound1_freq; - uint16_t sound2_id; + SoundId sound2_id; int16_t sound2_volume; int16_t sound2_pan; uint16_t sound2_freq; @@ -78,74 +81,77 @@ struct rct_vehicle_sound_params uint16_t priority; }; -enum RCT2_SOUND +enum class SoundId : uint8_t { - SOUND_LIFT_1, - SOUND_TRACK_FRICTION_1, - SOUND_LIFT_2, - SOUND_SCREAM_1, - SOUND_CLICK_1, - SOUND_CLICK_2, - SOUND_PLACE_ITEM, - SOUND_SCREAM_2, - SOUND_SCREAM_3, - SOUND_SCREAM_4, - SOUND_SCREAM_5, - SOUND_SCREAM_6, - SOUND_LIFT_3, - SOUND_PURCHASE, - SOUND_CRASH, - SOUND_LAYING_OUT_WATER, - SOUND_WATER_1, - SOUND_WATER_2, - SOUND_TRAIN_WHISTLE, - SOUND_TRAIN_CHUGGING, - SOUND_WATER_SPLASH, - SOUND_HAMMERING, - SOUND_RIDE_LAUNCH_1, - SOUND_RIDE_LAUNCH_2, - SOUND_COUGH_1, - SOUND_COUGH_2, - SOUND_COUGH_3, - SOUND_COUGH_4, - SOUND_RAIN_1, - SOUND_THUNDER_1, - SOUND_THUNDER_2, - SOUND_RAIN_2, - SOUND_RAIN_3, - SOUND_BALLOON_POP, - SOUND_MECHANIC_FIX, - SOUND_SCREAM_7, - SOUND_TOILET_FLUSH, - SOUND_CLICK_3, - SOUND_QUACK, - SOUND_NEWS_ITEM, - SOUND_WINDOW_OPEN, - SOUND_LAUGH_1, - SOUND_LAUGH_2, - SOUND_LAUGH_3, - SOUND_APPLAUSE, - SOUND_HAUNTED_HOUSE_SCARE, - SOUND_HAUNTED_HOUSE_SCREAM_1, - SOUND_HAUNTED_HOUSE_SCREAM_2, - SOUND_48, - SOUND_49, - SOUND_ERROR, - SOUND_51, - SOUND_LIFT_4, - SOUND_LIFT_5, - SOUND_TRACK_FRICTION_2, - SOUND_LIFT_6, - SOUND_LIFT_7, - SOUND_TRACK_FRICTION_3, - SOUND_SCREAM_8, - SOUND_TRAM, - SOUND_DOOR_OPEN, - SOUND_DOOR_CLOSE, - SOUND_62, - SOUND_MAXID + LiftClassic, + TrackFrictionClassicWood, + FrictionClassic, + Scream1, + Click1, + Click2, + PlaceItem, + Scream2, + Scream3, + Scream4, + Scream5, + Scream6, + LiftFrictionWheels, + Purchase, + Crash, + LayingOutWater, + Water1, + Water2, + TrainWhistle, + TrainDeparting, + WaterSplash, + GoKartEngine, + RideLaunch1, + RideLaunch2, + Cough1, + Cough2, + Cough3, + Cough4, + Rain, + Thunder1, + Thunder2, + TrackFrictionTrain, + TrackFrictionWater, + BalloonPop, + MechanicFix, + Scream7, + ToiletFlush, + Click3, + Quack, + NewsItem, + WindowOpen, + Laugh1, + Laugh2, + Laugh3, + Applause, + HauntedHouseScare, + HauntedHouseScream1, + HauntedHouseScream2, + BlockBrakeClose, + BlockBrakeRelease, + Error, + BrakeRelease, + LiftArrow, + LiftWood, + TrackFrictionWood, + LiftWildMouse, + LiftBM, + TrackFrictionBM, + Scream8, + Tram, + DoorOpen, + DoorClose, + Portcullis, + NoScream = 254, + Null = 255 }; +constexpr uint8_t RCT2SoundCount = static_cast(SoundId::Portcullis) + 1; + extern audio_device* gAudioDevices; extern int32_t gAudioDeviceCount; extern int32_t gAudioCurrentDevice; @@ -158,7 +164,7 @@ extern void* gRainSoundChannel; extern rct_ride_music gRideMusicList[AUDIO_MAX_RIDE_MUSIC]; extern rct_ride_music_info gRideMusicInfoList[NUM_DEFAULT_MUSIC_TRACKS]; -extern rct_ride_music_params gRideMusicParamsList[6]; +extern rct_ride_music_params gRideMusicParamsList[AUDIO_MAX_RIDE_MUSIC]; extern rct_ride_music_params* gRideMusicParamsListEnd; extern rct_vehicle_sound gVehicleSoundList[AUDIO_MAX_VEHICLE_SOUNDS]; @@ -195,18 +201,16 @@ void audio_pause_sounds(); * @param volume The volume at which the sound effect should be played. * @param pan The pan at which the sound effect should be played. If set to anything other than AUDIO_PLAY_AT_CENTRE, plays the * sound at a position relative to the centre of the viewport. - * @return 0 if the sound was not out of range; otherwise, soundId. */ -int32_t audio_play_sound(int32_t soundId, int32_t volume, int32_t pan); +void audio_play_sound(SoundId soundId, int32_t volume, int32_t pan); /** * Plays the specified sound at a virtual location. * @param soundId The sound effect to play. * @param x The x coordinate of the location. * @param y The y coordinate of the location. * @param z The z coordinate of the location. - * @return 0 if the sound was not out of range; otherwise, soundId. */ -int32_t audio_play_sound_at_location(int32_t soundId, int16_t x, int16_t y, int16_t z); +void audio_play_sound_at_location(SoundId soundId, const CoordsXYZ& loc); /** * Populates the gAudioDevices array with the available audio devices. */ diff --git a/src/openrct2/cmdline/BenchGfxCommmands.cpp b/src/openrct2/cmdline/BenchGfxCommmands.cpp index 806d0eaaf4..343a715c21 100644 --- a/src/openrct2/cmdline/BenchGfxCommmands.cpp +++ b/src/openrct2/cmdline/BenchGfxCommmands.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/cmdline/BenchSpriteSort.cpp b/src/openrct2/cmdline/BenchSpriteSort.cpp index 0c9776ece8..2341e72612 100644 --- a/src/openrct2/cmdline/BenchSpriteSort.cpp +++ b/src/openrct2/cmdline/BenchSpriteSort.cpp @@ -105,7 +105,7 @@ static std::vector extract_paint_session(const std::string parkFi int32_t customY = (gMapSize / 2) * 32 + 16; int32_t x = 0, y = 0; - int32_t z = tile_element_height(customX, customY) & 0xFFFF; + int32_t z = tile_element_height({ customX, customY }); x = customY - customX; y = ((customX + customY) / 2) - z; diff --git a/src/openrct2/cmdline/CommandLine.cpp b/src/openrct2/cmdline/CommandLine.cpp index d15ddc2af3..adc87af25c 100644 --- a/src/openrct2/cmdline/CommandLine.cpp +++ b/src/openrct2/cmdline/CommandLine.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/cmdline/CommandLine.hpp b/src/openrct2/cmdline/CommandLine.hpp index bba00a04cd..67be8c3c59 100644 --- a/src/openrct2/cmdline/CommandLine.hpp +++ b/src/openrct2/cmdline/CommandLine.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/cmdline/ConvertCommand.cpp b/src/openrct2/cmdline/ConvertCommand.cpp index e5cf63812d..6355485713 100644 --- a/src/openrct2/cmdline/ConvertCommand.cpp +++ b/src/openrct2/cmdline/ConvertCommand.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/cmdline/RootCommands.cpp b/src/openrct2/cmdline/RootCommands.cpp index 1c17170222..b39f1fda55 100644 --- a/src/openrct2/cmdline/RootCommands.cpp +++ b/src/openrct2/cmdline/RootCommands.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/cmdline/ScreenshotCommands.cpp b/src/openrct2/cmdline/ScreenshotCommands.cpp index 54a9bf439a..dbe126e027 100644 --- a/src/openrct2/cmdline/ScreenshotCommands.cpp +++ b/src/openrct2/cmdline/ScreenshotCommands.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,20 +10,21 @@ #include "../interface/Screenshot.h" #include "CommandLine.hpp" -static ScreenshotOptions options; +static ScreenshotOptions _options; // clang-format off static constexpr const CommandLineOptionDefinition ScreenshotOptionsDef[] { - { CMDLINE_TYPE_INTEGER, &options.weather, NAC, "weather", "weather to be used (0 = default, 1 = sunny, ..., 6 = thunder)." }, - { CMDLINE_TYPE_SWITCH, &options.hide_guests, NAC, "no-peeps", "hide peeps" }, - { CMDLINE_TYPE_SWITCH, &options.hide_sprites, NAC, "no-sprites", "hide all sprites (e.g. balloons, vehicles, guests)" }, - { CMDLINE_TYPE_SWITCH, &options.clear_grass, NAC, "clear-grass", "set all grass to be clear of weeds" }, - { CMDLINE_TYPE_SWITCH, &options.mowed_grass, NAC, "mowed-grass", "set all grass to be mowed" }, - { CMDLINE_TYPE_SWITCH, &options.water_plants, NAC, "water-plants", "water plants for the screenshot" }, - { CMDLINE_TYPE_SWITCH, &options.fix_vandalism, NAC, "fix-vandalism", "fix vandalism for the screenshot" }, - { CMDLINE_TYPE_SWITCH, &options.remove_litter, NAC, "remove-litter", "remove litter for the screenshot" }, - { CMDLINE_TYPE_SWITCH, &options.tidy_up_park, NAC, "tidy-up-park", "clear grass, water plants, fix vandalism and remove litter" }, + { CMDLINE_TYPE_INTEGER, &_options.weather, NAC, "weather", "weather to be used (0 = default, 1 = sunny, ..., 6 = thunder)." }, + { CMDLINE_TYPE_SWITCH, &_options.hide_guests, NAC, "no-peeps", "hide peeps" }, + { CMDLINE_TYPE_SWITCH, &_options.hide_sprites, NAC, "no-sprites", "hide all sprites (e.g. balloons, vehicles, guests)" }, + { CMDLINE_TYPE_SWITCH, &_options.clear_grass, NAC, "clear-grass", "set all grass to be clear of weeds" }, + { CMDLINE_TYPE_SWITCH, &_options.mowed_grass, NAC, "mowed-grass", "set all grass to be mowed" }, + { CMDLINE_TYPE_SWITCH, &_options.water_plants, NAC, "water-plants", "water plants for the screenshot" }, + { CMDLINE_TYPE_SWITCH, &_options.fix_vandalism, NAC, "fix-vandalism", "fix vandalism for the screenshot" }, + { CMDLINE_TYPE_SWITCH, &_options.remove_litter, NAC, "remove-litter", "remove litter for the screenshot" }, + { CMDLINE_TYPE_SWITCH, &_options.tidy_up_park, NAC, "tidy-up-park", "clear grass, water plants, fix vandalism and remove litter" }, + { CMDLINE_TYPE_SWITCH, &_options.transparent, NAC, "transparent", "make the background transparent" }, OptionTableEnd }; @@ -42,7 +43,7 @@ static exitcode_t HandleScreenshot(CommandLineArgEnumerator* argEnumerator) { const char** argv = (const char**)argEnumerator->GetArguments() + argEnumerator->GetIndex(); int32_t argc = argEnumerator->GetCount() - argEnumerator->GetIndex(); - int32_t result = cmdline_for_screenshot(argv, argc, &options); + int32_t result = cmdline_for_screenshot(argv, argc, &_options); if (result < 0) { return EXITCODE_FAIL; diff --git a/src/openrct2/cmdline/SimulateCommands.cpp b/src/openrct2/cmdline/SimulateCommands.cpp index 55eacedd3f..3848f9ccae 100644 --- a/src/openrct2/cmdline/SimulateCommands.cpp +++ b/src/openrct2/cmdline/SimulateCommands.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -63,7 +63,7 @@ static exitcode_t HandleSimulate(CommandLineArgEnumerator* argEnumerator) { context->GetGameState()->UpdateLogic(); } - Console::WriteLine("Completed: %s", sprite_checksum()); + Console::WriteLine("Completed: %s", sprite_checksum().ToString().c_str()); } else { diff --git a/src/openrct2/cmdline/SpriteCommands.cpp b/src/openrct2/cmdline/SpriteCommands.cpp index 311c0aa619..900894ffdc 100644 --- a/src/openrct2/cmdline/SpriteCommands.cpp +++ b/src/openrct2/cmdline/SpriteCommands.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/cmdline/UriHandler.cpp b/src/openrct2/cmdline/UriHandler.cpp index ae98c358fa..669e313bf3 100644 --- a/src/openrct2/cmdline/UriHandler.cpp +++ b/src/openrct2/cmdline/UriHandler.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/common.h b/src/openrct2/common.h index 54379a710c..1af006ac29 100644 --- a/src/openrct2/common.h +++ b/src/openrct2/common.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -31,10 +31,6 @@ using namespace Numerics; using utf8 = char; using utf8string = utf8*; using const_utf8string = const utf8*; -#ifdef _WIN32 -using utf16 = wchar_t; -using utf16string = utf16*; -#endif // Define MAX_PATH for various headers that don't want to include system headers // just for MAX_PATH @@ -65,7 +61,7 @@ const constexpr auto ror64 = ror; #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) # include -# define STUB() log_warning("Function %s at %s:%d is a stub.\n", __PRETTY_FUNCTION__, __FILE__, __LINE__) +# define STUB() log_warning("Function %s at %s:%d is a stub.", __PRETTY_FUNCTION__, __FILE__, __LINE__) # define _strcmpi _stricmp # define _stricmp(x, y) strcasecmp((x), (y)) # define _strnicmp(x, y, n) strncasecmp((x), (y), (n)) diff --git a/src/openrct2/config/Config.cpp b/src/openrct2/config/Config.cpp index dfc7776cbf..08d59488ee 100644 --- a/src/openrct2/config/Config.cpp +++ b/src/openrct2/config/Config.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -170,11 +170,6 @@ namespace Config model->use_vsync = reader->GetBoolean("use_vsync", true); model->virtual_floor_style = reader->GetEnum( "virtual_floor_style", VIRTUAL_FLOOR_STYLE_GLASSY, Enum_VirtualFloorStyle); - - // Default config setting is false until ghost trains are implemented #4540 - model->test_unfinished_tracks = reader->GetBoolean("test_unfinished_tracks", false); - - model->no_test_crashes = reader->GetBoolean("no_test_crashes", false); model->date_format = reader->GetEnum("date_format", platform_get_locale_date_format(), Enum_DateFormat); model->auto_staff_placement = reader->GetBoolean("auto_staff", true); model->handymen_mow_default = reader->GetBoolean("handymen_mow_default", false); @@ -195,6 +190,7 @@ namespace Config model->window_scale = reader->GetFloat("window_scale", platform_get_default_scale()); model->scale_quality = reader->GetEnum("scale_quality", SCALE_QUALITY_SMOOTH_NN, Enum_ScaleQuality); model->show_fps = reader->GetBoolean("show_fps", false); + model->multithreading = reader->GetBoolean("multi_threading", false); model->trap_cursor = reader->GetBoolean("trap_cursor", false); model->auto_open_shops = reader->GetBoolean("auto_open_shops", false); model->scenario_select_mode = reader->GetInt32("scenario_select_mode", SCENARIO_SELECT_MODE_ORIGIN); @@ -212,6 +208,7 @@ namespace Config model->show_guest_purchases = reader->GetBoolean("show_guest_purchases", false); model->show_real_names_of_guests = reader->GetBoolean("show_real_names_of_guests", true); model->allow_early_completion = reader->GetBoolean("allow_early_completion", false); + model->transparent_screenshot = reader->GetBoolean("transparent_screenshot", true); } } @@ -249,8 +246,6 @@ namespace Config writer->WriteEnum("drawing_engine", model->drawing_engine, Enum_DrawingEngine); writer->WriteBoolean("uncap_fps", model->uncap_fps); writer->WriteBoolean("use_vsync", model->use_vsync); - writer->WriteBoolean("test_unfinished_tracks", model->test_unfinished_tracks); - writer->WriteBoolean("no_test_crashes", model->no_test_crashes); writer->WriteEnum("date_format", model->date_format, Enum_DateFormat); writer->WriteBoolean("auto_staff", model->auto_staff_placement); writer->WriteBoolean("handymen_mow_default", model->handymen_mow_default); @@ -268,6 +263,7 @@ namespace Config writer->WriteFloat("window_scale", model->window_scale); writer->WriteEnum("scale_quality", model->scale_quality, Enum_ScaleQuality); writer->WriteBoolean("show_fps", model->show_fps); + writer->WriteBoolean("multi_threading", model->multithreading); writer->WriteBoolean("trap_cursor", model->trap_cursor); writer->WriteBoolean("auto_open_shops", model->auto_open_shops); writer->WriteInt32("scenario_select_mode", model->scenario_select_mode); @@ -286,6 +282,7 @@ namespace Config writer->WriteBoolean("show_real_names_of_guests", model->show_real_names_of_guests); writer->WriteBoolean("allow_early_completion", model->allow_early_completion); writer->WriteEnum("virtual_floor_style", model->virtual_floor_style, Enum_VirtualFloorStyle); + writer->WriteBoolean("transparent_screenshot", model->transparent_screenshot); } static void ReadInterface(IIniReader* reader) @@ -303,6 +300,7 @@ namespace Config model->current_theme_preset = reader->GetCString("current_theme", "*RCT2"); model->current_title_sequence_preset = reader->GetCString("current_title_sequence", "*OPENRCT2"); model->object_selection_filter_flags = reader->GetInt32("object_selection_filter_flags", 0x3FFF); + model->scenarioselect_last_tab = reader->GetInt32("scenarioselect_last_tab", 0); } } @@ -320,6 +318,7 @@ namespace Config writer->WriteString("current_theme", model->current_theme_preset); writer->WriteString("current_title_sequence", model->current_title_sequence_preset); writer->WriteInt32("object_selection_filter_flags", model->object_selection_filter_flags); + writer->WriteInt32("scenarioselect_last_tab", model->scenarioselect_last_tab); } static void ReadSound(IIniReader* reader) @@ -363,7 +362,7 @@ namespace Config auto playerName = reader->GetString("player_name", ""); if (playerName.empty()) { - playerName = String::ToStd(platform_get_username()); + playerName = platform_get_username(); if (playerName.empty()) { playerName = "Player"; @@ -393,6 +392,7 @@ namespace Config model->log_chat = reader->GetBoolean("log_chat", false); model->log_server_actions = reader->GetBoolean("log_server_actions", false); model->pause_server_if_no_clients = reader->GetBoolean("pause_server_if_no_clients", false); + model->desync_debugging = reader->GetBoolean("desync_debugging", false); } } @@ -418,6 +418,7 @@ namespace Config writer->WriteBoolean("log_chat", model->log_chat); writer->WriteBoolean("log_server_actions", model->log_server_actions); writer->WriteBoolean("pause_server_if_no_clients", model->pause_server_if_no_clients); + writer->WriteBoolean("desync_debugging", model->desync_debugging); } static void ReadNotifications(IIniReader* reader) diff --git a/src/openrct2/config/Config.h b/src/openrct2/config/Config.h index 3aaeb442d7..9d5f6159fd 100644 --- a/src/openrct2/config/Config.h +++ b/src/openrct2/config/Config.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -32,6 +32,7 @@ struct GeneralConfiguration bool uncap_fps; bool use_vsync; bool show_fps; + bool multithreading; bool minimize_fullscreen_focus_loss; // Map rendering @@ -45,6 +46,7 @@ struct GeneralConfiguration bool render_weather_gloom; bool disable_lightning_effect; bool show_guest_purchases; + bool transparent_screenshot; // Localisation int32_t language; @@ -69,8 +71,6 @@ struct GeneralConfiguration int32_t window_snap_proximity; bool allow_loading_with_incorrect_checksum; bool save_plugin_data; - bool test_unfinished_tracks; - bool no_test_crashes; bool debugging_tools; int32_t autosave_frequency; int32_t autosave_amount; @@ -109,6 +109,7 @@ struct InterfaceConfiguration utf8* current_theme_preset; utf8* current_title_sequence_preset; int32_t object_selection_filter_flags; + int32_t scenarioselect_last_tab; }; struct SoundConfiguration @@ -155,6 +156,7 @@ struct NetworkConfiguration bool log_chat; bool log_server_actions; bool pause_server_if_no_clients; + bool desync_debugging; }; struct NotificationConfiguration diff --git a/src/openrct2/config/ConfigEnum.hpp b/src/openrct2/config/ConfigEnum.hpp index 48305a2aaf..a3dfaba108 100644 --- a/src/openrct2/config/ConfigEnum.hpp +++ b/src/openrct2/config/ConfigEnum.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/config/IniReader.cpp b/src/openrct2/config/IniReader.cpp index 2b187e4e9c..3bc24b89c8 100644 --- a/src/openrct2/config/IniReader.cpp +++ b/src/openrct2/config/IniReader.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/config/IniReader.hpp b/src/openrct2/config/IniReader.hpp index 20b3a6ae24..b50ac57676 100644 --- a/src/openrct2/config/IniReader.hpp +++ b/src/openrct2/config/IniReader.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/config/IniWriter.cpp b/src/openrct2/config/IniWriter.cpp index 7f01e6429a..e12eb420dc 100644 --- a/src/openrct2/config/IniWriter.cpp +++ b/src/openrct2/config/IniWriter.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/config/IniWriter.hpp b/src/openrct2/config/IniWriter.hpp index c00a8308fd..a854d4255a 100644 --- a/src/openrct2/config/IniWriter.hpp +++ b/src/openrct2/config/IniWriter.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/CircularBuffer.h b/src/openrct2/core/CircularBuffer.h index a46da8e787..c7c4f1cec9 100644 --- a/src/openrct2/core/CircularBuffer.h +++ b/src/openrct2/core/CircularBuffer.h @@ -104,6 +104,34 @@ public: } } + void push_back(value_type&& val) + { + if (_size == 0) + { + _elements[_head] = std::move(val); + _tail = _head; + _size++; + } + else if (_size != capacity()) + { + _tail++; + if (_tail == capacity()) + _tail = 0; + _size++; + _elements[_tail] = std::move(val); + } + else + { + _head++; + if (_head == capacity()) + _head = 0; + _tail++; + if (_tail == capacity()) + _tail = 0; + _elements[_tail] = std::move(val); + } + } + private: size_t _head = 0; size_t _tail = 0; diff --git a/src/openrct2/core/Collections.hpp b/src/openrct2/core/Collections.hpp index 54ef228c8b..9b3b139d64 100644 --- a/src/openrct2/core/Collections.hpp +++ b/src/openrct2/core/Collections.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Console.cpp b/src/openrct2/core/Console.cpp index b91ffc9937..f85320bfdd 100644 --- a/src/openrct2/core/Console.cpp +++ b/src/openrct2/core/Console.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Console.hpp b/src/openrct2/core/Console.hpp index 097f9ac75b..69956f8cc5 100644 --- a/src/openrct2/core/Console.hpp +++ b/src/openrct2/core/Console.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Crypt.OpenSSL.cpp b/src/openrct2/core/Crypt.OpenSSL.cpp index 9046019cd7..ff950f9c1a 100644 --- a/src/openrct2/core/Crypt.OpenSSL.cpp +++ b/src/openrct2/core/Crypt.OpenSSL.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Crypt.h b/src/openrct2/core/Crypt.h index 2cf3a44389..f3c32cf00a 100644 --- a/src/openrct2/core/Crypt.h +++ b/src/openrct2/core/Crypt.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/DataSerialiser.h b/src/openrct2/core/DataSerialiser.h index 65a0372a7f..7549b2df34 100644 --- a/src/openrct2/core/DataSerialiser.h +++ b/src/openrct2/core/DataSerialiser.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -17,23 +17,23 @@ class DataSerialiser { private: MemoryStream _stream; - IStream* _activeStream = nullptr; + IStream& _activeStream; bool _isSaving = false; bool _isLogging = false; public: DataSerialiser(bool isSaving) - : _isSaving(isSaving) + : _activeStream(_stream) + , _isSaving(isSaving) , _isLogging(false) { - _activeStream = &_stream; } DataSerialiser(bool isSaving, IStream& stream, bool isLogging = false) - : _isSaving(isSaving) + : _activeStream(stream) + , _isSaving(isSaving) , _isLogging(isLogging) { - _activeStream = &stream; } bool IsSaving() const @@ -46,9 +46,9 @@ public: return !_isSaving; } - MemoryStream& GetStream() + IStream& GetStream() { - return _stream; + return _activeStream; } template DataSerialiser& operator<<(const T& data) @@ -56,13 +56,13 @@ public: if (!_isLogging) { if (_isSaving) - DataSerializerTraits::encode(_activeStream, data); + DataSerializerTraits::encode(&_activeStream, data); else - DataSerializerTraits::decode(_activeStream, const_cast(data)); + DataSerializerTraits::decode(&_activeStream, const_cast(data)); } else { - DataSerializerTraits::log(_activeStream, data); + DataSerializerTraits::log(&_activeStream, data); } return *this; @@ -73,13 +73,13 @@ public: if (!_isLogging) { if (_isSaving) - DataSerializerTraits>::encode(_activeStream, data); + DataSerializerTraits>::encode(&_activeStream, data); else - DataSerializerTraits>::decode(_activeStream, data); + DataSerializerTraits>::decode(&_activeStream, data); } else { - DataSerializerTraits>::log(_activeStream, data); + DataSerializerTraits>::log(&_activeStream, data); } return *this; diff --git a/src/openrct2/core/DataSerialiserTag.h b/src/openrct2/core/DataSerialiserTag.h index d430796ef2..69b326f170 100644 --- a/src/openrct2/core/DataSerialiserTag.h +++ b/src/openrct2/core/DataSerialiserTag.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/DataSerialiserTraits.h b/src/openrct2/core/DataSerialiserTraits.h index d2edf0dd46..c6b91dc10e 100644 --- a/src/openrct2/core/DataSerialiserTraits.h +++ b/src/openrct2/core/DataSerialiserTraits.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -9,12 +9,14 @@ #pragma once +#include "../Cheats.h" #include "../core/MemoryStream.h" #include "../localisation/Localisation.h" #include "../network/NetworkTypes.h" #include "../network/network.h" #include "../ride/Ride.h" #include "../world/Location.hpp" +#include "../world/TileElement.h" #include "DataSerialiserTag.h" #include "Endianness.h" #include "MemoryStream.h" @@ -188,14 +190,13 @@ template<> struct DataSerializerTraits stream->Write(rideId, strlen(rideId)); - Ride* ride = get_ride(val.id); - if (ride) + auto ride = get_ride(val.id); + if (ride != nullptr) { - char rideName[256] = {}; - format_string(rideName, 256, ride->name, &ride->name_arguments); + auto rideName = ride->GetName(); stream->Write(" \"", 2); - stream->Write(rideName, strlen(rideName)); + stream->Write(rideName.c_str(), rideName.size()); stream->Write("\"", 1); } } @@ -252,6 +253,64 @@ template<> struct DataSerializerTraits } }; +template struct DataSerializerTraitsPODArray +{ + static void encode(IStream* stream, const _Ty (&val)[_Size]) + { + uint16_t len = (uint16_t)_Size; + uint16_t swapped = ByteSwapBE(len); + stream->Write(&swapped); + + DataSerializerTraits s; + for (auto&& sub : val) + { + s.encode(stream, sub); + } + } + static void decode(IStream* stream, _Ty (&val)[_Size]) + { + uint16_t len; + stream->Read(&len); + len = ByteSwapBE(len); + + if (len != _Size) + throw std::runtime_error("Invalid size, can't decode"); + + DataSerializerTraits<_Ty> s; + for (auto&& sub : val) + { + s.decode(stream, sub); + } + } + static void log(IStream* stream, const _Ty (&val)[_Size]) + { + stream->Write("{", 1); + DataSerializerTraits<_Ty> s; + for (auto&& sub : val) + { + s.log(stream, sub); + stream->Write("; ", 2); + } + stream->Write("}", 1); + } +}; + +template struct DataSerializerTraits : public DataSerializerTraitsPODArray +{ +}; + +template struct DataSerializerTraits : public DataSerializerTraitsPODArray +{ +}; + +template struct DataSerializerTraits : public DataSerializerTraitsPODArray +{ +}; + +template struct DataSerializerTraits : public DataSerializerTraitsPODArray +{ +}; + template struct DataSerializerTraits> { static void encode(IStream* stream, const std::array<_Ty, _Size>& val) @@ -322,6 +381,48 @@ template<> struct DataSerializerTraits } }; +template<> struct DataSerializerTraits +{ + static void encode(IStream* stream, const TileElement& tileElement) + { + stream->WriteValue(tileElement.type); + stream->WriteValue(tileElement.flags); + stream->WriteValue(tileElement.base_height); + stream->WriteValue(tileElement.clearance_height); + for (int i = 0; i < 4; ++i) + { + stream->WriteValue(tileElement.pad_04[i]); + } + for (int i = 0; i < 8; ++i) + { + stream->WriteValue(tileElement.pad_08[i]); + } + } + static void decode(IStream* stream, TileElement& tileElement) + { + tileElement.type = stream->ReadValue(); + tileElement.flags = stream->ReadValue(); + tileElement.base_height = stream->ReadValue(); + tileElement.clearance_height = stream->ReadValue(); + for (int i = 0; i < 4; ++i) + { + tileElement.pad_04[i] = stream->ReadValue(); + } + for (int i = 0; i < 8; ++i) + { + tileElement.pad_08[i] = stream->ReadValue(); + } + } + static void log(IStream* stream, const TileElement& tileElement) + { + char msg[128] = {}; + snprintf( + msg, sizeof(msg), "TileElement(type = %u, flags = %u, base_height = %u)", tileElement.type, tileElement.flags, + tileElement.base_height); + stream->Write(msg, strlen(msg)); + } +}; + template<> struct DataSerializerTraits { static void encode(IStream* stream, const CoordsXY& coords) @@ -395,3 +496,23 @@ template<> struct DataSerializerTraits stream->Write(msg, strlen(msg)); } }; + +template<> struct DataSerializerTraits +{ + static void encode(IStream* stream, const NetworkCheatType_t& val) + { + uint32_t temp = ByteSwapBE(val.id); + stream->Write(&temp); + } + static void decode(IStream* stream, NetworkCheatType_t& val) + { + uint32_t temp; + stream->Read(&temp); + val.id = ByteSwapBE(temp); + } + static void log(IStream* stream, const NetworkCheatType_t& val) + { + const char* cheatName = CheatsGetName(static_cast(val.id)); + stream->Write(cheatName, strlen(cheatName)); + } +}; diff --git a/src/openrct2/core/Diagnostics.cpp b/src/openrct2/core/Diagnostics.cpp index 98e2f48f1e..f841cfa3c4 100644 --- a/src/openrct2/core/Diagnostics.cpp +++ b/src/openrct2/core/Diagnostics.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Diagnostics.hpp b/src/openrct2/core/Diagnostics.hpp index 7a456db216..2981bb602b 100644 --- a/src/openrct2/core/Diagnostics.hpp +++ b/src/openrct2/core/Diagnostics.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Endianness.h b/src/openrct2/core/Endianness.h index b992e8752f..e0d065f512 100644 --- a/src/openrct2/core/Endianness.h +++ b/src/openrct2/core/Endianness.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -17,6 +17,7 @@ template struct ByteSwapT template<> struct ByteSwapT<1> { + typedef uint8_t UIntType; static uint8_t SwapBE(uint8_t value) { return value; @@ -25,6 +26,7 @@ template<> struct ByteSwapT<1> template<> struct ByteSwapT<2> { + typedef uint16_t UIntType; static uint16_t SwapBE(uint16_t value) { return (uint16_t)((value << 8) | (value >> 8)); @@ -33,6 +35,7 @@ template<> struct ByteSwapT<2> template<> struct ByteSwapT<4> { + typedef uint32_t UIntType; static uint32_t SwapBE(uint32_t value) { return (uint32_t)(((value << 24) | ((value << 8) & 0x00FF0000) | ((value >> 8) & 0x0000FF00) | (value >> 24))); @@ -41,6 +44,7 @@ template<> struct ByteSwapT<4> template<> struct ByteSwapT<8> { + typedef uint64_t UIntType; static uint64_t SwapBE(uint64_t value) { value = (value & 0x00000000FFFFFFFF) << 32 | (value & 0xFFFFFFFF00000000) >> 32; @@ -52,5 +56,7 @@ template<> struct ByteSwapT<8> template static T ByteSwapBE(const T& value) { - return ByteSwapT::SwapBE(value); + typedef ByteSwapT ByteSwap; + typename ByteSwap::UIntType result = ByteSwap::SwapBE(reinterpret_cast(value)); + return *reinterpret_cast(&result); } diff --git a/src/openrct2/core/File.cpp b/src/openrct2/core/File.cpp index 97e5f01db4..5df4e4514d 100644 --- a/src/openrct2/core/File.cpp +++ b/src/openrct2/core/File.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -49,7 +49,7 @@ namespace File std::vector result; #if defined(_WIN32) && !defined(__MINGW32__) - auto pathW = String::ToUtf16(std::string(path)); + auto pathW = String::ToWideChar(std::string(path)); std::ifstream fs(pathW, std::ios::in | std::ios::binary); #else std::ifstream fs(std::string(path), std::ios::in | std::ios::binary); @@ -125,8 +125,8 @@ namespace File { uint64_t lastModified = 0; #ifdef _WIN32 - auto pathW = utf8_to_widechar(path.c_str()); - auto hFile = CreateFileW(pathW, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); + auto pathW = String::ToWideChar(path.c_str()); + auto hFile = CreateFileW(pathW.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (hFile != INVALID_HANDLE_VALUE) { FILETIME ftCreate, ftAccess, ftWrite; @@ -136,7 +136,6 @@ namespace File } CloseHandle(hFile); } - free(pathW); #else struct stat statInfo { diff --git a/src/openrct2/core/File.h b/src/openrct2/core/File.h index cbb68b83fe..3b8cd321bc 100644 --- a/src/openrct2/core/File.h +++ b/src/openrct2/core/File.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/FileIndex.hpp b/src/openrct2/core/FileIndex.hpp index 0b9ce01b92..da3e27d4df 100644 --- a/src/openrct2/core/FileIndex.hpp +++ b/src/openrct2/core/FileIndex.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/FileScanner.cpp b/src/openrct2/core/FileScanner.cpp index 68b5abc23b..169d62df4f 100644 --- a/src/openrct2/core/FileScanner.cpp +++ b/src/openrct2/core/FileScanner.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -227,11 +227,11 @@ public: void GetDirectoryChildren(std::vector& children, const std::string& path) override { - std::string pattern = path + "\\*"; - wchar_t* wPattern = utf8_to_widechar(pattern.c_str()); + auto pattern = path + "\\*"; + auto wPattern = String::ToWideChar(pattern.c_str()); WIN32_FIND_DATAW findData; - HANDLE hFile = FindFirstFileW(wPattern, &findData); + HANDLE hFile = FindFirstFileW(wPattern.c_str(), &findData); if (hFile != INVALID_HANDLE_VALUE) { do @@ -244,8 +244,6 @@ public: } while (FindNextFileW(hFile, &findData)); FindClose(hFile); } - - Memory::Free(wPattern); } private: @@ -253,10 +251,7 @@ private: { DirectoryChild result; - utf8* name = widechar_to_utf8(child->cFileName); - result.Name = std::string(name); - Memory::Free(name); - + result.Name = String::ToUtf8(child->cFileName); if (child->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { result.Type = DIRECTORY_CHILD_TYPE::DC_DIRECTORY; diff --git a/src/openrct2/core/FileScanner.h b/src/openrct2/core/FileScanner.h index 1e7b23ddf9..f87aee31a5 100644 --- a/src/openrct2/core/FileScanner.h +++ b/src/openrct2/core/FileScanner.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/FileStream.hpp b/src/openrct2/core/FileStream.hpp index 907c5c851f..ff79408f2f 100644 --- a/src/openrct2/core/FileStream.hpp +++ b/src/openrct2/core/FileStream.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -15,6 +15,9 @@ #include "String.hpp" #include +#ifndef _WIN32 +# include +#endif enum { @@ -67,13 +70,23 @@ public: } #ifdef _WIN32 - wchar_t* pathW = utf8_to_widechar(path); - wchar_t* modeW = utf8_to_widechar(mode); - _file = _wfopen(pathW, modeW); - free(pathW); - free(modeW); + auto pathW = String::ToWideChar(path); + auto modeW = String::ToWideChar(mode); + _file = _wfopen(pathW.c_str(), modeW.c_str()); #else - _file = fopen(path, mode); + if (fileMode == FILE_MODE_OPEN) + { + struct stat fileStat; + // Only allow regular files to be opened as its possible to open directories. + if (stat(path, &fileStat) == 0 && S_ISREG(fileStat.st_mode)) + { + _file = fopen(path, mode); + } + } + else + { + _file = fopen(path, mode); + } #endif if (_file == nullptr) { @@ -201,4 +214,9 @@ public: size_t readBytes = fread(buffer, 1, (size_t)length, _file); return readBytes; } + + const void* GetData() const override + { + return nullptr; + } }; diff --git a/src/openrct2/core/Guard.cpp b/src/openrct2/core/Guard.cpp index b8950c50f1..8419e782f0 100644 --- a/src/openrct2/core/Guard.cpp +++ b/src/openrct2/core/Guard.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Guard.hpp b/src/openrct2/core/Guard.hpp index 83edf426d1..5556232fd5 100644 --- a/src/openrct2/core/Guard.hpp +++ b/src/openrct2/core/Guard.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/IStream.cpp b/src/openrct2/core/IStream.cpp index 60a4af6d94..4a2ad084d4 100644 --- a/src/openrct2/core/IStream.cpp +++ b/src/openrct2/core/IStream.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/IStream.hpp b/src/openrct2/core/IStream.hpp index 3ff0ea1e86..0ded91b216 100644 --- a/src/openrct2/core/IStream.hpp +++ b/src/openrct2/core/IStream.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -49,6 +49,8 @@ interface IStream virtual uint64_t TryRead(void* buffer, uint64_t length) abstract; + virtual const void* GetData() const abstract; + /////////////////////////////////////////////////////////////////////////// // Helper methods /////////////////////////////////////////////////////////////////////////// diff --git a/src/openrct2/core/Imaging.cpp b/src/openrct2/core/Imaging.cpp index e48537977d..18b676fdd0 100644 --- a/src/openrct2/core/Imaging.cpp +++ b/src/openrct2/core/Imaging.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -305,7 +305,7 @@ namespace Imaging default: { #if defined(_WIN32) && !defined(__MINGW32__) - auto pathW = String::ToUtf16(path); + auto pathW = String::ToWideChar(path); std::ifstream fs(pathW, std::ios::binary); #else std::ifstream fs(path.data(), std::ios::binary); @@ -331,7 +331,7 @@ namespace Imaging case IMAGE_FORMAT::PNG: { #if defined(_WIN32) && !defined(__MINGW32__) - auto pathW = String::ToUtf16(path); + auto pathW = String::ToWideChar(path); std::ofstream fs(pathW, std::ios::binary); #else std::ofstream fs(path.data(), std::ios::binary); diff --git a/src/openrct2/core/Imaging.h b/src/openrct2/core/Imaging.h index 3619281de9..74d58e5b31 100644 --- a/src/openrct2/core/Imaging.h +++ b/src/openrct2/core/Imaging.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/JobPool.hpp b/src/openrct2/core/JobPool.hpp index 5b8b39ed25..957a81412a 100644 --- a/src/openrct2/core/JobPool.hpp +++ b/src/openrct2/core/JobPool.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -9,6 +9,7 @@ #pragma once +#include #include #include #include @@ -45,9 +46,10 @@ private: typedef std::unique_lock unique_lock; public: - JobPool() + JobPool(size_t maxThreads = 255) { - for (size_t n = 0; n < std::thread::hardware_concurrency(); n++) + maxThreads = std::min(maxThreads, std::thread::hardware_concurrency()); + for (size_t n = 0; n < maxThreads; n++) { _threads.emplace_back(&JobPool::ProcessQueue, this); } @@ -68,18 +70,13 @@ public: } } - void AddTask(std::function workFn, std::function completionFn) + void AddTask(std::function workFn, std::function completionFn = nullptr) { unique_lock lock(_mutex); _pending.emplace_back(workFn, completionFn); _condPending.notify_one(); } - void AddTask(std::function workFn) - { - return AddTask(workFn, nullptr); - } - void Join(std::function reportFn = nullptr) { unique_lock lock(_mutex); diff --git a/src/openrct2/core/Json.cpp b/src/openrct2/core/Json.cpp index 786f8990d1..07c868630b 100644 --- a/src/openrct2/core/Json.cpp +++ b/src/openrct2/core/Json.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -53,11 +53,11 @@ namespace Json fs.Write(jsonOutput, jsonOutputSize); } - json_t* FromString(const std::string& raw) + json_t* FromString(std::string_view raw) { json_t* root; json_error_t error; - root = json_loads(raw.c_str(), 0, &error); + root = json_loadb(raw.data(), raw.size(), 0, &error); if (root == nullptr) { throw JsonException(&error); diff --git a/src/openrct2/core/Json.hpp b/src/openrct2/core/Json.hpp index d8be8d5fbb..53687b9890 100644 --- a/src/openrct2/core/Json.hpp +++ b/src/openrct2/core/Json.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,6 +14,7 @@ #include #include #include +#include namespace Json { @@ -23,7 +24,7 @@ namespace Json json_t* ReadFromFile(const utf8* path, size_t maxSize = MAX_JSON_SIZE); void WriteToFile(const utf8* path, const json_t* json, size_t flags = 0); - json_t* FromString(const std::string& raw); + json_t* FromString(std::string_view raw); } // namespace Json class JsonException final : public std::runtime_error diff --git a/src/openrct2/core/Memory.hpp b/src/openrct2/core/Memory.hpp index 5c75f2042a..c2096c876b 100644 --- a/src/openrct2/core/Memory.hpp +++ b/src/openrct2/core/Memory.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/MemoryStream.cpp b/src/openrct2/core/MemoryStream.cpp index c309f7b757..fcae646afc 100644 --- a/src/openrct2/core/MemoryStream.cpp +++ b/src/openrct2/core/MemoryStream.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -49,6 +49,11 @@ MemoryStream::MemoryStream(const void* data, size_t dataSize) { } +MemoryStream::MemoryStream(MemoryStream&& mv) +{ + *this = std::move(mv); +} + MemoryStream::~MemoryStream() { if (_access & MEMORY_ACCESS::OWNER) @@ -60,6 +65,21 @@ MemoryStream::~MemoryStream() _data = nullptr; } +MemoryStream& MemoryStream::operator=(MemoryStream&& mv) +{ + _access = mv._access; + _dataCapacity = mv._dataCapacity; + _data = mv._data; + _position = mv._position; + + mv._data = nullptr; + mv._position = nullptr; + mv._dataCapacity = 0; + mv._dataSize = 0; + + return *this; +} + const void* MemoryStream::GetData() const { return _data; diff --git a/src/openrct2/core/MemoryStream.h b/src/openrct2/core/MemoryStream.h index 57dfc0988e..1401d1a87b 100644 --- a/src/openrct2/core/MemoryStream.h +++ b/src/openrct2/core/MemoryStream.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -34,12 +34,15 @@ private: public: MemoryStream() = default; MemoryStream(const MemoryStream& copy); + MemoryStream(MemoryStream&& mv); explicit MemoryStream(size_t capacity); MemoryStream(void* data, size_t dataSize, uint8_t access = MEMORY_ACCESS::READ); MemoryStream(const void* data, size_t dataSize); virtual ~MemoryStream(); - const void* GetData() const; + MemoryStream& operator=(MemoryStream&& mv); + + const void* GetData() const override; void* GetDataCopy() const; void* TakeData(); diff --git a/src/openrct2/core/Nullable.hpp b/src/openrct2/core/Nullable.hpp index 2f94ec1fe8..ecc6b7f651 100644 --- a/src/openrct2/core/Nullable.hpp +++ b/src/openrct2/core/Nullable.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Numerics.hpp b/src/openrct2/core/Numerics.hpp index 4de93adfd8..e699e0c8a0 100644 --- a/src/openrct2/core/Numerics.hpp +++ b/src/openrct2/core/Numerics.hpp @@ -30,6 +30,18 @@ namespace Numerics return (((_UIntType)(x) << shift) | ((_UIntType)(x) >> (limits::digits - shift))); } + /** + * Bitwise left rotate of lowest 4 bits + * @param x unsigned 8-bit integer value + * @param shift positions to shift + * @return rotated value + */ + [[maybe_unused]] static constexpr uint8_t rol4(uint8_t x, size_t shift) + { + x &= 0x0F; + return (x << shift | x >> (4 - shift)) & 0x0F; + } + /** * Bitwise right rotate * @tparam _UIntType unsigned integral type @@ -44,4 +56,16 @@ namespace Numerics return (((_UIntType)(x) >> shift) | ((_UIntType)(x) << (limits::digits - shift))); } + /** + * Bitwise right rotate of lowest 4 bits + * @param x unsigned 8-bit integer value + * @param shift positions to shift + * @return rotated value + */ + [[maybe_unused]] static constexpr uint8_t ror4(uint8_t x, size_t shift) + { + x &= 0x0F; + return (x >> shift | x << (4 - shift)) & 0x0F; + } + } // namespace Numerics diff --git a/src/openrct2/core/Optional.hpp b/src/openrct2/core/Optional.hpp new file mode 100644 index 0000000000..66c67028fb --- /dev/null +++ b/src/openrct2/core/Optional.hpp @@ -0,0 +1,29 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#ifdef __has_include +# if __has_include() +# include +# elif __has_include() +# include +# else +# error Missing +# endif +#else +# error __has_include is not defined +#endif + +// `optional` and friends will be available in NS `::opt` +#if __has_include() +namespace opt = std; +#elif __has_include() +namespace opt = std::experimental; +#endif diff --git a/src/openrct2/core/Path.cpp b/src/openrct2/core/Path.cpp index 18a6990c73..8c1bfeb045 100644 --- a/src/openrct2/core/Path.cpp +++ b/src/openrct2/core/Path.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -171,19 +171,17 @@ namespace Path utf8* GetAbsolute(utf8* buffer, size_t bufferSize, const utf8* relativePath) { #ifdef _WIN32 - wchar_t* relativePathW = utf8_to_widechar(relativePath); + auto relativePathW = String::ToWideChar(relativePath); wchar_t absolutePathW[MAX_PATH]; - DWORD length = GetFullPathNameW(relativePathW, (DWORD)std::size(absolutePathW), absolutePathW, nullptr); - Memory::Free(relativePathW); + DWORD length = GetFullPathNameW(relativePathW.c_str(), (DWORD)std::size(absolutePathW), absolutePathW, nullptr); if (length == 0) { return String::Set(buffer, bufferSize, relativePath); } else { - utf8* absolutePath = widechar_to_utf8(absolutePathW); - String::Set(buffer, bufferSize, absolutePath); - Memory::Free(absolutePath); + auto absolutePath = String::ToUtf8(absolutePathW); + String::Set(buffer, bufferSize, absolutePath.c_str()); return buffer; } #else diff --git a/src/openrct2/core/Path.hpp b/src/openrct2/core/Path.hpp index fcc5addd33..924b2698cb 100644 --- a/src/openrct2/core/Path.hpp +++ b/src/openrct2/core/Path.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Registration.hpp b/src/openrct2/core/Registration.hpp index 4602023bdb..d281aa9c69 100644 --- a/src/openrct2/core/Registration.hpp +++ b/src/openrct2/core/Registration.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/String.cpp b/src/openrct2/core/String.cpp index 735a312c3d..bf5ec321f9 100644 --- a/src/openrct2/core/String.cpp +++ b/src/openrct2/core/String.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -97,7 +97,7 @@ namespace String #endif } - std::wstring ToUtf16(const std::string_view& src) + std::wstring ToWideChar(const std::string_view& src) { #ifdef _WIN32 int srcLen = (int)src.size(); @@ -184,9 +184,9 @@ namespace String { if (ignoreCase) { - while (*str != '\0' && *match != '\0') + while (*match != '\0') { - if (tolower(*str++) != tolower(*match++)) + if (*str == '\0' || tolower(*str++) != tolower(*match++)) { return false; } @@ -195,9 +195,9 @@ namespace String } else { - while (*str != '\0' && *match != '\0') + while (*match != '\0') { - if (*str++ != *match++) + if (*str == '\0' || *str++ != *match++) { return false; } @@ -706,7 +706,7 @@ namespace String std::string ToUpper(const std::string_view& src) { #ifdef _WIN32 - auto srcW = ToUtf16(src); + auto srcW = ToWideChar(src); // Measure how long the destination needs to be auto requiredSize = LCMapStringEx( diff --git a/src/openrct2/core/String.hpp b/src/openrct2/core/String.hpp index 73ce7788f1..3f337aa629 100644 --- a/src/openrct2/core/String.hpp +++ b/src/openrct2/core/String.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -37,7 +37,7 @@ namespace String std::string StdFormat_VA(const utf8* format, va_list args); std::string StdFormat(const utf8* format, ...); std::string ToUtf8(const std::wstring_view& src); - std::wstring ToUtf16(const std::string_view& src); + std::wstring ToWideChar(const std::string_view& src); bool IsNullOrEmpty(const utf8* str); int32_t Compare(const std::string& a, const std::string& b, bool ignoreCase = false); diff --git a/src/openrct2/core/StringBuilder.hpp b/src/openrct2/core/StringBuilder.hpp index 38d6925b61..8b436ba568 100644 --- a/src/openrct2/core/StringBuilder.hpp +++ b/src/openrct2/core/StringBuilder.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/StringReader.hpp b/src/openrct2/core/StringReader.hpp index 1189996632..4c43316fd6 100644 --- a/src/openrct2/core/StringReader.hpp +++ b/src/openrct2/core/StringReader.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Zip.cpp b/src/openrct2/core/Zip.cpp index 5cb0995d8d..65430095e1 100644 --- a/src/openrct2/core/Zip.cpp +++ b/src/openrct2/core/Zip.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/Zip.h b/src/openrct2/core/Zip.h index f2c762a104..62176c97d4 100644 --- a/src/openrct2/core/Zip.h +++ b/src/openrct2/core/Zip.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/core/ZipAndroid.cpp b/src/openrct2/core/ZipAndroid.cpp index 07975a29a8..714a918bf2 100644 --- a/src/openrct2/core/ZipAndroid.cpp +++ b/src/openrct2/core/ZipAndroid.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/AVX2Drawing.cpp b/src/openrct2/drawing/AVX2Drawing.cpp index cbbae06b9d..709af9fa46 100644 --- a/src/openrct2/drawing/AVX2Drawing.cpp +++ b/src/openrct2/drawing/AVX2Drawing.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/Drawing.Sprite.cpp b/src/openrct2/drawing/Drawing.Sprite.cpp index 7d9f06b050..15dc25f17b 100644 --- a/src/openrct2/drawing/Drawing.Sprite.cpp +++ b/src/openrct2/drawing/Drawing.Sprite.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -223,6 +223,7 @@ static rct_gx _csg = {}; static bool _csgLoaded = false; static rct_g1_element _g1Temp = {}; +static std::vector _imageListElements; bool gTinyFontAntiAliased = false; /** @@ -246,8 +247,8 @@ bool gfx_load_g1(const IPlatformEnvironment& env) } // Read element headers - _g1.elements.resize(324206); bool is_rctc = _g1.header.num_entries == SPR_RCTC_G1_END; + _g1.elements.resize(_g1.header.num_entries); read_and_convert_gxdat(&fs, _g1.header.num_entries, is_rctc, _g1.elements.data()); gTinyFontAntiAliased = is_rctc; @@ -401,10 +402,11 @@ bool gfx_load_csg() * Copies a sprite onto the buffer. There is no compression used on the sprite * image. * rct2: 0x0067A690 + * @param imageId Only flags are used. */ void FASTCALL gfx_bmp_sprite_to_buffer( const uint8_t* palette_pointer, uint8_t* source_pointer, uint8_t* dest_pointer, const rct_g1_element* source_image, - rct_drawpixelinfo* dest_dpi, int32_t height, int32_t width, int32_t image_type) + rct_drawpixelinfo* dest_dpi, int32_t height, int32_t width, ImageId imageId) { uint16_t zoom_level = dest_dpi->zoom_level; uint8_t zoom_amount = 1 << zoom_level; @@ -412,7 +414,7 @@ void FASTCALL gfx_bmp_sprite_to_buffer( uint32_t source_line_width = source_image->width * zoom_amount; // Image uses the palette pointer to remap the colours of the image - if (image_type & IMAGE_TYPE_REMAP) + if (imageId.HasPrimary()) { assert(palette_pointer != nullptr); @@ -441,7 +443,7 @@ void FASTCALL gfx_bmp_sprite_to_buffer( // Image is transparent. It only uses source pointer for // telling if it needs to be drawn not for colour. Colour provided // by the palette pointer. - if (image_type & IMAGE_TYPE_TRANSPARENT) + if (imageId.IsBlended()) { // Not tested assert(palette_pointer != nullptr); for (; height > 0; height -= zoom_amount) @@ -506,16 +508,12 @@ void FASTCALL gfx_bmp_sprite_to_buffer( } } -uint8_t* FASTCALL gfx_draw_sprite_get_palette(int32_t image_id, uint32_t tertiary_colour) +uint8_t* FASTCALL gfx_draw_sprite_get_palette(ImageId imageId) { - int32_t image_type = (image_id & 0xE0000000); - if (image_type == 0) - return nullptr; - - if (!(image_type & IMAGE_TYPE_REMAP_2_PLUS)) + if (!imageId.HasSecondary()) { - uint8_t palette_ref = (image_id >> 19) & 0xFF; - if (!(image_type & IMAGE_TYPE_TRANSPARENT)) + uint8_t palette_ref = imageId.GetRemap(); + if (!imageId.IsBlended()) { palette_ref &= 0x7F; } @@ -535,16 +533,16 @@ uint8_t* FASTCALL gfx_draw_sprite_get_palette(int32_t image_id, uint32_t tertiar { uint8_t* palette_pointer = gPeepPalette; - uint32_t primary_offset = palette_to_g1_offset[(image_id >> 19) & 0x1F]; - uint32_t secondary_offset = palette_to_g1_offset[(image_id >> 24) & 0x1F]; + uint32_t primary_offset = palette_to_g1_offset[imageId.GetPrimary()]; + uint32_t secondary_offset = palette_to_g1_offset[imageId.GetSecondary()]; - if (!(image_type & IMAGE_TYPE_REMAP)) + if (imageId.HasTertiary()) { palette_pointer = gOtherPalette; #if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2 assert(tertiary_colour < PALETTE_TO_G1_OFFSET_COUNT); #endif // DEBUG_LEVEL_2 - uint32_t tertiary_offset = palette_to_g1_offset[tertiary_colour]; + uint32_t tertiary_offset = palette_to_g1_offset[imageId.GetTertiary()]; auto tertiary_palette = gfx_get_g1_element(tertiary_offset); if (tertiary_palette != nullptr) { @@ -566,33 +564,12 @@ uint8_t* FASTCALL gfx_draw_sprite_get_palette(int32_t image_id, uint32_t tertiar } } -/** - * - * rct2: 0x0067A28E - * image_id (ebx) - * image_id as below - * 0b_111X_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX image_type - * 0b_XXX1_11XX_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX image_sub_type (unknown pointer) - * 0b_XXX1_1111_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX secondary_colour - * 0b_XXXX_XXXX_1111_1XXX_XXXX_XXXX_XXXX_XXXX primary_colour - * 0b_XXXX_X111_1111_1XXX_XXXX_XXXX_XXXX_XXXX palette_ref - * 0b_XXXX_XXXX_XXXX_X111_1111_1111_1111_1111 image_id (offset to g1) - * x (cx) - * y (dx) - * dpi (esi) - * tertiary_colour (ebp) - */ -void FASTCALL gfx_draw_sprite_software(rct_drawpixelinfo* dpi, int32_t image_id, int32_t x, int32_t y, uint32_t tertiary_colour) +void FASTCALL gfx_draw_sprite_software(rct_drawpixelinfo* dpi, ImageId imageId, int32_t x, int32_t y) { - if (image_id != -1) + if (imageId.HasValue()) { - uint8_t* palette_pointer = gfx_draw_sprite_get_palette(image_id, tertiary_colour); - if (image_id & IMAGE_TYPE_REMAP_2_PLUS) - { - image_id |= IMAGE_TYPE_REMAP; - } - - gfx_draw_sprite_palette_set_software(dpi, image_id, x, y, palette_pointer, nullptr); + auto palette = gfx_draw_sprite_get_palette(imageId); + gfx_draw_sprite_palette_set_software(dpi, imageId, x, y, palette, nullptr); } } @@ -606,12 +583,9 @@ void FASTCALL gfx_draw_sprite_software(rct_drawpixelinfo* dpi, int32_t image_id, * y (dx) */ void FASTCALL gfx_draw_sprite_palette_set_software( - rct_drawpixelinfo* dpi, int32_t image_id, int32_t x, int32_t y, uint8_t* palette_pointer, uint8_t* unknown_pointer) + rct_drawpixelinfo* dpi, ImageId imageId, int32_t x, int32_t y, uint8_t* palette_pointer, uint8_t* unknown_pointer) { - int32_t image_element = image_id & 0x7FFFF; - int32_t image_type = image_id & 0xE0000000; - - const rct_g1_element* g1 = gfx_get_g1_element(image_element); + const auto* g1 = gfx_get_g1_element(imageId); if (g1 == nullptr) { return; @@ -619,7 +593,7 @@ void FASTCALL gfx_draw_sprite_palette_set_software( if (dpi->zoom_level != 0 && (g1->flags & G1_FLAG_HAS_ZOOM_SPRITE)) { - rct_drawpixelinfo zoomed_dpi; + rct_drawpixelinfo zoomed_dpi = *dpi; zoomed_dpi.bits = dpi->bits; zoomed_dpi.x = dpi->x >> 1; zoomed_dpi.y = dpi->y >> 1; @@ -628,7 +602,8 @@ void FASTCALL gfx_draw_sprite_palette_set_software( zoomed_dpi.pitch = dpi->pitch; zoomed_dpi.zoom_level = dpi->zoom_level - 1; gfx_draw_sprite_palette_set_software( - &zoomed_dpi, image_type | (image_element - g1->zoomed_offset), x >> 1, y >> 1, palette_pointer, unknown_pointer); + &zoomed_dpi, imageId.WithIndex(imageId.GetIndex() - g1->zoomed_offset), x >> 1, y >> 1, palette_pointer, + unknown_pointer); return; } @@ -756,16 +731,14 @@ void FASTCALL gfx_draw_sprite_palette_set_software( // We have to use a different method to move the source pointer for // rle encoded sprites so that will be handled within this function gfx_rle_sprite_to_buffer( - g1->offset, dest_pointer, palette_pointer, dpi, image_type, source_start_y, height, source_start_x, width); + g1->offset, dest_pointer, palette_pointer, dpi, imageId, source_start_y, height, source_start_x, width); return; } - uint8_t* source_pointer = g1->offset; - // Move the pointer to the start point of the source - source_pointer += g1->width * source_start_y + source_start_x; - - if (!(g1->flags & G1_FLAG_1)) + else if (!(g1->flags & G1_FLAG_1)) { - gfx_bmp_sprite_to_buffer(palette_pointer, source_pointer, dest_pointer, g1, dpi, height, width, image_type); + // Move the pointer to the start point of the source + auto source_pointer = g1->offset + (((size_t)g1->width * source_start_y) + source_start_x); + gfx_bmp_sprite_to_buffer(palette_pointer, source_pointer, dest_pointer, g1, dpi, height, width, imageId); } } @@ -789,7 +762,7 @@ void FASTCALL // Only BMP format is supported for masking if (!(imgMask->flags & G1_FLAG_BMP) || !(imgColour->flags & G1_FLAG_BMP)) { - gfx_draw_sprite_software(dpi, colourImage, x, y, 0); + gfx_draw_sprite_software(dpi, ImageId::FromUInt32(colourImage), x, y); return; } @@ -830,69 +803,106 @@ void FASTCALL mask_fn(width, height, maskSrc, colourSrc, dst, maskWrap, colourWrap, dstWrap); } +const rct_g1_element* gfx_get_g1_element(ImageId imageId) +{ + return gfx_get_g1_element(imageId.GetIndex()); +} + const rct_g1_element* gfx_get_g1_element(int32_t image_id) { openrct2_assert(!gOpenRCT2NoGraphics, "gfx_get_g1_element called on headless instance"); - if (image_id == (-1 & 0x7FFFF)) + auto offset = (size_t)image_id; + if (offset == 0x7FFFF) { return nullptr; } - - if (image_id == SPR_TEMP) + else if (offset == SPR_TEMP) { return &_g1Temp; } - else if (image_id < SPR_G2_BEGIN) + else if (offset < SPR_RCTC_G1_END) { - if (image_id >= (int32_t)_g1.elements.size()) + if (offset < _g1.elements.size()) { - return nullptr; + return &_g1.elements[offset]; } - return &_g1.elements[image_id]; } - if (image_id < SPR_CSG_BEGIN) + else if (offset < SPR_G2_END) { - const uint32_t idx = image_id - SPR_G2_BEGIN; - if (idx >= _g2.header.num_entries) + size_t idx = offset - SPR_G2_BEGIN; + if (idx < _g2.header.num_entries) + { + return &_g2.elements[idx]; + } + else { log_warning("Invalid entry in g2.dat requested, idx = %u. You may have to update your g2.dat.", idx); - return nullptr; } - return &_g2.elements[idx]; } - - if (is_csg_loaded()) + else if (offset < SPR_CSG_END) { - const uint32_t idx = image_id - SPR_CSG_BEGIN; - if (idx >= _csg.header.num_entries) + if (is_csg_loaded()) { - openrct2_assert(idx < _csg.header.num_entries, "Invalid entry in csg.dat requested, idx = %u.", idx); - return nullptr; + size_t idx = offset - SPR_CSG_BEGIN; + if (idx < _csg.header.num_entries) + { + return &_csg.elements[idx]; + } + else + { + log_warning("Invalid entry in csg.dat requested, idx = %u.", idx); + } + } + } + else if (offset < SPR_IMAGE_LIST_END) + { + size_t idx = offset - SPR_IMAGE_LIST_BEGIN; + if (idx < _imageListElements.size()) + { + return &_imageListElements[idx]; } - return &_csg.elements[idx]; } return nullptr; } void gfx_set_g1_element(int32_t imageId, const rct_g1_element* g1) { - openrct2_assert(!gOpenRCT2NoGraphics, "gfx_set_g1_element called on headless instance"); + bool isTemp = imageId == SPR_TEMP; + bool isValid = (imageId >= SPR_IMAGE_LIST_BEGIN && imageId < SPR_IMAGE_LIST_END) + || (imageId >= SPR_SCROLLING_TEXT_START && imageId < SPR_SCROLLING_TEXT_END); + #ifdef DEBUG - openrct2_assert( - (imageId >= 0 && imageId < SPR_G2_BEGIN) || imageId == SPR_TEMP, "gfx_set_g1_element called with unexpected image id"); + openrct2_assert(!gOpenRCT2NoGraphics, "gfx_set_g1_element called on headless instance"); + openrct2_assert(isValid || isTemp, "gfx_set_g1_element called with unexpected image id"); openrct2_assert(g1 != nullptr, "g1 was nullptr"); #endif - if (imageId == SPR_TEMP) + if (g1 != nullptr) { - _g1Temp = *g1; - } - else if (imageId >= 0 && imageId < SPR_G2_BEGIN) - { - if (imageId < (int32_t)_g1.elements.size()) + if (isTemp) { - _g1.elements[imageId] = *g1; + _g1Temp = *g1; + } + else if (isValid) + { + if (imageId < SPR_RCTC_G1_END) + { + if (imageId < (int32_t)_g1.elements.size()) + { + _g1.elements[imageId] = *g1; + } + } + else + { + size_t idx = (size_t)imageId - SPR_IMAGE_LIST_BEGIN; + // Grow the element buffer if necessary + while (idx >= _imageListElements.size()) + { + _imageListElements.resize(std::max(256, _imageListElements.size() * 2)); + } + _imageListElements[idx] = *g1; + } } } } diff --git a/src/openrct2/drawing/Drawing.String.cpp b/src/openrct2/drawing/Drawing.String.cpp index bd998576a0..f744c0975a 100644 --- a/src/openrct2/drawing/Drawing.String.cpp +++ b/src/openrct2/drawing/Drawing.String.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/Drawing.cpp b/src/openrct2/drawing/Drawing.cpp index 4d529a633f..98402535f6 100644 --- a/src/openrct2/drawing/Drawing.cpp +++ b/src/openrct2/drawing/Drawing.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -15,15 +15,16 @@ #include "../core/Guard.hpp" #include "../object/Object.h" #include "../platform/platform.h" +#include "../sprites.h" #include "../util/Util.h" #include "../world/Water.h" // HACK These were originally passed back through registers -int32_t gLastDrawStringX; -int32_t gLastDrawStringY; +thread_local int32_t gLastDrawStringX; +thread_local int32_t gLastDrawStringY; -int16_t gCurrentFontSpriteBase; -uint16_t gCurrentFontFlags; +thread_local int16_t gCurrentFontSpriteBase; +thread_local uint16_t gCurrentFontFlags; uint8_t gGamePalette[256 * 4]; uint32_t gPaletteEffectFrame; @@ -465,6 +466,32 @@ const translucent_window_palette TranslucentWindowPalettes[COLOUR_COUNT] = { }; // clang-format on +ImageCatalogue ImageId::GetCatalogue() const +{ + auto index = GetIndex(); + if (index == SPR_TEMP) + { + return ImageCatalogue::TEMPORARY; + } + else if (index < SPR_RCTC_G1_END) + { + return ImageCatalogue::G1; + } + else if (index < SPR_G2_END) + { + return ImageCatalogue::G2; + } + else if (index < SPR_CSG_END) + { + return ImageCatalogue::CSG; + } + else if (index < SPR_IMAGE_LIST_END) + { + return ImageCatalogue::OBJECT; + } + return ImageCatalogue::UNKNOWN; +} + void (*mask_fn)( int32_t width, int32_t height, const uint8_t* RESTRICT maskSrc, const uint8_t* RESTRICT colourSrc, uint8_t* RESTRICT dst, int32_t maskWrap, int32_t colourWrap, int32_t dstWrap) @@ -591,12 +618,7 @@ bool clip_drawpixelinfo(rct_drawpixelinfo* dst, rct_drawpixelinfo* src, int32_t int32_t right = x + width; int32_t bottom = y + height; - dst->bits = src->bits; - dst->x = src->x; - dst->y = src->y; - dst->width = src->width; - dst->height = src->height; - dst->pitch = src->pitch; + *dst = *src; dst->zoom_level = 0; if (x > dst->x) diff --git a/src/openrct2/drawing/Drawing.h b/src/openrct2/drawing/Drawing.h index 20b43b188b..ad2c5cc324 100644 --- a/src/openrct2/drawing/Drawing.h +++ b/src/openrct2/drawing/Drawing.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,6 +18,11 @@ namespace OpenRCT2 interface IPlatformEnvironment; } +namespace OpenRCT2::Drawing +{ + interface IDrawingEngine; +} + struct rct_g1_element { uint8_t* offset; // 0x00 @@ -31,13 +36,15 @@ struct rct_g1_element struct rct_drawpixelinfo { - uint8_t* bits; // 0x00 - int16_t x; // 0x04 - int16_t y; // 0x06 - int16_t width; // 0x08 - int16_t height; // 0x0A - int16_t pitch; // 0x0C note: this is actually (pitch - width) - uint16_t zoom_level; // 0x0E + uint8_t* bits{}; + int16_t x{}; + int16_t y{}; + int16_t width{}; + int16_t height{}; + int16_t pitch{}; // note: this is actually (pitch - width) + uint16_t zoom_level{}; + + OpenRCT2::Drawing::IDrawingEngine* DrawingEngine{}; }; struct rct_g1_element_32bit @@ -223,6 +230,181 @@ struct rct_size16 int16_t height; }; +enum class ImageCatalogue +{ + UNKNOWN, + G1, + G2, + CSG, + OBJECT, + TEMPORARY, +}; + +/** + * Represents a specific image from a catalogue such as G1, G2, CSG etc. with remap + * colours and flags. + * + * This is currently all stored as a single 32-bit integer, but will allow easy + * extension to 64-bits or higher so that more images can be used. + */ +struct ImageId +{ +private: + // clang-format off + static constexpr uint32_t MASK_INDEX = 0b00000000000001111111111111111111; + static constexpr uint32_t MASK_REMAP = 0b00000111111110000000000000000000; + static constexpr uint32_t MASK_PRIMARY = 0b00000000111110000000000000000000; + static constexpr uint32_t MASK_SECONDARY = 0b00011111000000000000000000000000; + static constexpr uint32_t FLAG_PRIMARY = 0b00100000000000000000000000000000; + static constexpr uint32_t FLAG_BLEND = 0b01000000000000000000000000000000; + static constexpr uint32_t FLAG_SECONDARY = 0b10000000000000000000000000000000; + static constexpr uint32_t SHIFT_REMAP = 19; + static constexpr uint32_t SHIFT_PRIMARY = 19; + static constexpr uint32_t SHIFT_SECONDARY = 24; + static constexpr uint32_t INDEX_UNDEFINED = 0b00000000000001111111111111111111; + static constexpr uint32_t VALUE_UNDEFINED = INDEX_UNDEFINED; + // clang-format on + + uint32_t _value = VALUE_UNDEFINED; + uint8_t _tertiary = 0; + +public: + static ImageId FromUInt32(uint32_t value) + { + ImageId result; + result._value = value; + return result; + } + + static ImageId FromUInt32(uint32_t value, uint32_t tertiary) + { + ImageId result; + result._value = value; + result._tertiary = tertiary & 0xFF; + return result; + } + + ImageId() = default; + + explicit constexpr ImageId(uint32_t index) + : _value(index & MASK_INDEX) + { + } + + constexpr ImageId(uint32_t index, uint8_t primaryColourOrPalette) + : ImageId(ImageId(index).WithPrimary(primaryColourOrPalette)) + { + } + + constexpr ImageId(uint32_t index, colour_t primaryColour, colour_t secondaryColour) + : ImageId(ImageId(index).WithPrimary(primaryColour).WithSecondary(secondaryColour)) + { + } + + constexpr ImageId(uint32_t index, colour_t primaryColour, colour_t secondaryColour, colour_t tertiaryColour) + : ImageId(ImageId(index).WithPrimary(primaryColour).WithSecondary(secondaryColour).WithTertiary(tertiaryColour)) + { + } + + uint32_t ToUInt32() const + { + return _value; + } + + bool HasValue() const + { + return GetIndex() != INDEX_UNDEFINED; + } + + bool HasPrimary() const + { + return (_value & FLAG_PRIMARY) || (_value & FLAG_SECONDARY); + } + + bool HasSecondary() const + { + return _value & FLAG_SECONDARY; + } + + bool HasTertiary() const + { + return !(_value & FLAG_PRIMARY) && (_value & FLAG_SECONDARY); + } + + bool IsRemap() const + { + return (_value & FLAG_PRIMARY) && !(_value & FLAG_SECONDARY); + } + + bool IsBlended() const + { + return _value & FLAG_BLEND; + } + + uint32_t GetIndex() const + { + return _value & MASK_INDEX; + } + + uint8_t GetRemap() const + { + return (_value & MASK_REMAP) >> SHIFT_REMAP; + } + + colour_t GetPrimary() const + { + return (_value & MASK_PRIMARY) >> SHIFT_PRIMARY; + } + + colour_t GetSecondary() const + { + return (_value & MASK_SECONDARY) >> SHIFT_SECONDARY; + } + + colour_t GetTertiary() const + { + return _tertiary; + } + + ImageCatalogue GetCatalogue() const; + + constexpr ImageId WithIndex(uint32_t index) + { + ImageId result = *this; + result._value = (_value & ~MASK_INDEX) | (index & MASK_INDEX); + return result; + } + + constexpr ImageId WithPrimary(colour_t colour) + { + ImageId result = *this; + result._value = (_value & ~MASK_PRIMARY) | ((colour << SHIFT_PRIMARY) & MASK_PRIMARY) | FLAG_PRIMARY; + return result; + } + + constexpr ImageId WithSecondary(colour_t colour) + { + ImageId result = *this; + result._value = (_value & ~MASK_SECONDARY) | ((colour << SHIFT_SECONDARY) & MASK_SECONDARY) | FLAG_SECONDARY; + return result; + } + + constexpr ImageId WithTertiary(colour_t tertiary) + { + ImageId result = *this; + result._value &= ~FLAG_PRIMARY; + if (!(_value & FLAG_SECONDARY)) + { + // Tertiary implies primary and secondary, so if colour was remap (8-bit primary) then + // we need to zero the secondary colour. + result._value &= ~MASK_SECONDARY; + result._value |= FLAG_SECONDARY; + } + result._tertiary = tertiary; + return result; + } +}; + #define SPRITE_ID_PALETTE_COLOUR_1(colourId) (IMAGE_TYPE_REMAP | ((colourId) << 19)) #define SPRITE_ID_PALETTE_COLOUR_2(primaryId, secondaryId) \ (IMAGE_TYPE_REMAP_2_PLUS | IMAGE_TYPE_REMAP | (((primaryId) << 19) | ((secondaryId) << 24))) @@ -237,8 +419,8 @@ struct rct_size16 #define MAX_SCROLLING_TEXT_MODES 38 -extern int16_t gCurrentFontSpriteBase; -extern uint16_t gCurrentFontFlags; +extern thread_local int16_t gCurrentFontSpriteBase; +extern thread_local uint16_t gCurrentFontFlags; extern rct_palette_entry gPalette[256]; extern uint8_t gGamePalette[256 * 4]; @@ -250,8 +432,8 @@ extern uint8_t gOtherPalette[256]; extern uint8_t text_palette[]; extern const translucent_window_palette TranslucentWindowPalettes[COLOUR_COUNT]; -extern int32_t gLastDrawStringX; -extern int32_t gLastDrawStringY; +extern thread_local int32_t gLastDrawStringX; +extern thread_local int32_t gLastDrawStringY; extern uint32_t gPickupPeepImage; extern int32_t gPickupPeepX; @@ -297,29 +479,31 @@ bool gfx_load_csg(); void gfx_unload_g1(); void gfx_unload_g2(); void gfx_unload_csg(); +const rct_g1_element* gfx_get_g1_element(ImageId imageId); const rct_g1_element* gfx_get_g1_element(int32_t image_id); void gfx_set_g1_element(int32_t imageId, const rct_g1_element* g1); bool is_csg_loaded(); uint32_t gfx_object_allocate_images(const rct_g1_element* images, uint32_t count); void gfx_object_free_images(uint32_t baseImageId, uint32_t count); void gfx_object_check_all_images_freed(); +size_t ImageListGetUsedCount(); +size_t ImageListGetMaximum(); void FASTCALL gfx_bmp_sprite_to_buffer( const uint8_t* palette_pointer, uint8_t* source_pointer, uint8_t* dest_pointer, const rct_g1_element* source_image, - rct_drawpixelinfo* dest_dpi, int32_t height, int32_t width, int32_t image_type); + rct_drawpixelinfo* dest_dpi, int32_t height, int32_t width, ImageId imageId); void FASTCALL gfx_rle_sprite_to_buffer( const uint8_t* RESTRICT source_bits_pointer, uint8_t* RESTRICT dest_bits_pointer, const uint8_t* RESTRICT palette_pointer, - const rct_drawpixelinfo* RESTRICT dpi, int32_t image_type, int32_t source_y_start, int32_t height, int32_t source_x_start, + const rct_drawpixelinfo* RESTRICT dpi, ImageId imageId, int32_t source_y_start, int32_t height, int32_t source_x_start, int32_t width); void FASTCALL gfx_draw_sprite(rct_drawpixelinfo* dpi, int32_t image_id, int32_t x, int32_t y, uint32_t tertiary_colour); void FASTCALL gfx_draw_glpyh(rct_drawpixelinfo* dpi, int32_t image_id, int32_t x, int32_t y, uint8_t* palette); void FASTCALL gfx_draw_sprite_raw_masked(rct_drawpixelinfo* dpi, int32_t x, int32_t y, int32_t maskImage, int32_t colourImage); void FASTCALL gfx_draw_sprite_solid(rct_drawpixelinfo* dpi, int32_t image, int32_t x, int32_t y, uint8_t colour); -void FASTCALL - gfx_draw_sprite_software(rct_drawpixelinfo* dpi, int32_t image_id, int32_t x, int32_t y, uint32_t tertiary_colour); -uint8_t* FASTCALL gfx_draw_sprite_get_palette(int32_t image_id, uint32_t tertiary_colour); +void FASTCALL gfx_draw_sprite_software(rct_drawpixelinfo* dpi, ImageId imageId, int32_t x, int32_t y); +uint8_t* FASTCALL gfx_draw_sprite_get_palette(ImageId imageId); void FASTCALL gfx_draw_sprite_palette_set_software( - rct_drawpixelinfo* dpi, int32_t image_id, int32_t x, int32_t y, uint8_t* palette_pointer, uint8_t* unknown_pointer); + rct_drawpixelinfo* dpi, ImageId imageId, int32_t x, int32_t y, uint8_t* palette_pointer, uint8_t* unknown_pointer); void FASTCALL gfx_draw_sprite_raw_masked_software(rct_drawpixelinfo* dpi, int32_t x, int32_t y, int32_t maskImage, int32_t colourImage); @@ -369,7 +553,8 @@ void ttf_draw_string(rct_drawpixelinfo* dpi, const_utf8string text, int32_t colo // scrolling text void scrolling_text_initialise_bitmaps(); void scrolling_text_invalidate(); -int32_t scrolling_text_setup(struct paint_session* session, rct_string_id stringId, uint16_t scroll, uint16_t scrollingMode); +int32_t scrolling_text_setup( + struct paint_session* session, rct_string_id stringId, uint16_t scroll, uint16_t scrollingMode, colour_t colour); rct_size16 FASTCALL gfx_get_sprite_size(uint32_t image_id); size_t g1_calculate_data_size(const rct_g1_element* g1); diff --git a/src/openrct2/drawing/DrawingFast.cpp b/src/openrct2/drawing/DrawingFast.cpp index 26133cbccd..9db5e4fbe0 100644 --- a/src/openrct2/drawing/DrawingFast.cpp +++ b/src/openrct2/drawing/DrawingFast.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -176,15 +176,16 @@ static void FASTCALL DrawRLESprite1( * Transfers readied images onto buffers * This function copies the sprite data onto the screen * rct2: 0x0067AA18 + * @param imageId Only flags are used. */ void FASTCALL gfx_rle_sprite_to_buffer( const uint8_t* RESTRICT source_bits_pointer, uint8_t* RESTRICT dest_bits_pointer, const uint8_t* RESTRICT palette_pointer, - const rct_drawpixelinfo* RESTRICT dpi, int32_t image_type, int32_t source_y_start, int32_t height, int32_t source_x_start, + const rct_drawpixelinfo* RESTRICT dpi, ImageId imageId, int32_t source_y_start, int32_t height, int32_t source_x_start, int32_t width) { - if (image_type & IMAGE_TYPE_REMAP) + if (imageId.HasPrimary()) { - if (image_type & IMAGE_TYPE_TRANSPARENT) + if (imageId.IsBlended()) { DrawRLESpriteHelper1(IMAGE_TYPE_REMAP | IMAGE_TYPE_TRANSPARENT); } @@ -193,7 +194,7 @@ void FASTCALL gfx_rle_sprite_to_buffer( DrawRLESpriteHelper1(IMAGE_TYPE_REMAP); } } - else if (image_type & IMAGE_TYPE_TRANSPARENT) + else if (imageId.IsBlended()) { DrawRLESpriteHelper1(IMAGE_TYPE_TRANSPARENT); } diff --git a/src/openrct2/drawing/Font.cpp b/src/openrct2/drawing/Font.cpp index c172e3018b..fe0aea55fd 100644 --- a/src/openrct2/drawing/Font.cpp +++ b/src/openrct2/drawing/Font.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -17,7 +17,7 @@ #include "TTF.h" #include -#include +#include static constexpr const int32_t SpriteFontLineHeight[FONT_SIZE_COUNT] = { 6, 10, 10 }; @@ -28,7 +28,7 @@ static uint8_t _additionalSpriteFontCharacterWidth[FONT_SIZE_COUNT][SPR_G2_GLYPH TTFFontSetDescriptor* gCurrentTTFFontSet; #endif // NO_TTF -static const std::map codepointOffsetMap = { +static const std::unordered_map codepointOffsetMap = { { UnicodeChar::ae_uc, SPR_G2_AE_UPPER - SPR_CHAR_START }, { UnicodeChar::o_stroke_uc, SPR_G2_O_STROKE_UPPER - SPR_CHAR_START }, { UnicodeChar::y_acute_uc, SPR_G2_Y_ACUTE_UPPER - SPR_CHAR_START }, @@ -57,18 +57,30 @@ static const std::map codepointOffsetMap = { { UnicodeChar::l_stroke, CSChar::l_stroke - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::n_acute_uc, CSChar::n_acute_uc - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::n_acute, CSChar::n_acute - CS_SPRITE_FONT_OFFSET }, + { UnicodeChar::n_caron_uc, SPR_G2_N_CARON_UPPER - SPR_CHAR_START }, + { UnicodeChar::n_caron, SPR_G2_N_CARON_LOWER - SPR_CHAR_START }, { UnicodeChar::o_double_acute_uc, SPR_G2_O_DOUBLE_ACUTE_UPPER - SPR_CHAR_START }, { UnicodeChar::o_double_acute, SPR_G2_O_DOUBLE_ACUTE_LOWER - SPR_CHAR_START }, + { UnicodeChar::r_caron_uc, SPR_G2_R_CARON_UPPER - SPR_CHAR_START }, + { UnicodeChar::r_caron, SPR_G2_R_CARON_LOWER - SPR_CHAR_START }, { UnicodeChar::s_acute_uc, CSChar::s_acute_uc - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::s_acute, CSChar::s_acute - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::s_cedilla_uc, SPR_G2_S_CEDILLA_UPPER - SPR_CHAR_START }, { UnicodeChar::s_cedilla, SPR_G2_S_CEDILLA_LOWER - SPR_CHAR_START }, + { UnicodeChar::s_caron_uc, SPR_G2_S_CARON_UPPER - SPR_CHAR_START }, + { UnicodeChar::s_caron, SPR_G2_S_CARON_LOWER - SPR_CHAR_START }, + { UnicodeChar::t_caron_uc, SPR_G2_T_CARON_UPPER - SPR_CHAR_START }, + { UnicodeChar::t_caron, SPR_G2_T_CARON_LOWER - SPR_CHAR_START }, + { UnicodeChar::u_ring_uc, SPR_G2_U_RING_UPPER - SPR_CHAR_START }, + { UnicodeChar::u_ring, SPR_G2_U_RING_LOWER - SPR_CHAR_START }, { UnicodeChar::u_double_acute_uc, SPR_G2_U_DOUBLE_ACUTE_UPPER - SPR_CHAR_START }, { UnicodeChar::u_double_acute, SPR_G2_U_DOUBLE_ACUTE_LOWER - SPR_CHAR_START }, { UnicodeChar::z_acute_uc, CSChar::z_acute_uc - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::z_acute, CSChar::z_acute - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::z_dot_uc, CSChar::z_dot_uc - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::z_dot, CSChar::z_dot - CS_SPRITE_FONT_OFFSET }, + { UnicodeChar::z_caron_uc, SPR_G2_Z_CARON_UPPER - SPR_CHAR_START }, + { UnicodeChar::z_caron, SPR_G2_Z_CARON_LOWER - SPR_CHAR_START }, { UnicodeChar::f_with_hook_uc, 'F' - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::s_comma_uc, SPR_G2_S_CEDILLA_UPPER - SPR_CHAR_START }, // No visual difference { UnicodeChar::s_comma, SPR_G2_S_CEDILLA_LOWER - SPR_CHAR_START }, // Ditto @@ -148,6 +160,8 @@ static const std::map codepointOffsetMap = { // Punctuation { UnicodeChar::non_breaking_space, ' ' - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::interpunct, SPR_G2_INTERPUNCT - SPR_CHAR_START }, + { UnicodeChar::multiplication_sign, CSChar::cross - CS_SPRITE_FONT_OFFSET }, + { UnicodeChar::en_dash, '-' - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::single_quote_open, '`' - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::single_quote_end, '\'' - CS_SPRITE_FONT_OFFSET }, { UnicodeChar::single_german_quote_open, ',' - CS_SPRITE_FONT_OFFSET }, @@ -185,12 +199,23 @@ static const std::map codepointOffsetMap = { { UnicodeChar::superscript_minus_one, CSChar::superscript_minus_one - CS_SPRITE_FONT_OFFSET }, }; +static char32_t _smallestCodepointValue = 0; +static char32_t _biggestCodepointValue = 0; + /** * * rct2: 0x006C19AC */ void font_sprite_initialise_characters() { + // Compute min and max that helps avoiding lookups for no reason. + _smallestCodepointValue = std::numeric_limits::max(); + for (const auto& entry : codepointOffsetMap) + { + _smallestCodepointValue = std::min(_smallestCodepointValue, entry.first); + _biggestCodepointValue = std::max(_biggestCodepointValue, entry.first); + } + for (int32_t fontSize = 0; fontSize < FONT_SIZE_COUNT; fontSize++) { int32_t glyphOffset = fontSize * FONT_SPRITE_GLYPH_COUNT; @@ -231,9 +256,14 @@ void font_sprite_initialise_characters() int32_t font_sprite_get_codepoint_offset(int32_t codepoint) { - auto result = codepointOffsetMap.find(codepoint); - if (result != codepointOffsetMap.end()) - return result->second; + // Only search the table when its in range of the map. + if (static_cast(codepoint) >= _smallestCodepointValue + && static_cast(codepoint) <= _biggestCodepointValue) + { + auto result = codepointOffsetMap.find(codepoint); + if (result != codepointOffsetMap.end()) + return result->second; + } if (codepoint < 32 || codepoint >= 256) codepoint = '?'; diff --git a/src/openrct2/drawing/Font.h b/src/openrct2/drawing/Font.h index 7604ba9068..33bf36d227 100644 --- a/src/openrct2/drawing/Font.h +++ b/src/openrct2/drawing/Font.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/IDrawingContext.h b/src/openrct2/drawing/IDrawingContext.h index eb4521c0a4..07ace81e0a 100644 --- a/src/openrct2/drawing/IDrawingContext.h +++ b/src/openrct2/drawing/IDrawingContext.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/IDrawingEngine.h b/src/openrct2/drawing/IDrawingEngine.h index 3f04551c36..4e4376d97e 100644 --- a/src/openrct2/drawing/IDrawingEngine.h +++ b/src/openrct2/drawing/IDrawingEngine.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/Image.cpp b/src/openrct2/drawing/Image.cpp index 44c0cdda09..a6a5e4ce89 100644 --- a/src/openrct2/drawing/Image.cpp +++ b/src/openrct2/drawing/Image.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,13 +10,14 @@ #include "../OpenRCT2.h" #include "../core/Console.hpp" #include "../core/Guard.hpp" +#include "../sprites.h" #include "Drawing.h" #include #include -constexpr uint32_t BASE_IMAGE_ID = 29294; -constexpr uint32_t MAX_IMAGES = 262144; +constexpr uint32_t BASE_IMAGE_ID = SPR_IMAGE_LIST_BEGIN; +constexpr uint32_t MAX_IMAGES = SPR_IMAGE_LIST_END - BASE_IMAGE_ID; constexpr uint32_t INVALID_IMAGE_ID = UINT32_MAX; struct ImageList @@ -240,3 +241,13 @@ void gfx_object_check_all_images_freed() #endif } } + +size_t ImageListGetUsedCount() +{ + return _allocatedImageCount; +} + +size_t ImageListGetMaximum() +{ + return MAX_IMAGES; +} diff --git a/src/openrct2/drawing/ImageImporter.cpp b/src/openrct2/drawing/ImageImporter.cpp index 9bd0f8a1c1..e58377736b 100644 --- a/src/openrct2/drawing/ImageImporter.cpp +++ b/src/openrct2/drawing/ImageImporter.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/ImageImporter.h b/src/openrct2/drawing/ImageImporter.h index b89afe5cee..8355799a58 100644 --- a/src/openrct2/drawing/ImageImporter.h +++ b/src/openrct2/drawing/ImageImporter.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/LightFX.cpp b/src/openrct2/drawing/LightFX.cpp index 4312f62f4f..3970cb8a1b 100644 --- a/src/openrct2/drawing/LightFX.cpp +++ b/src/openrct2/drawing/LightFX.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -170,8 +170,7 @@ void lightfx_update_buffers(rct_drawpixelinfo* info) { _light_rendered_buffer_front = realloc(_light_rendered_buffer_front, info->width * info->height); _light_rendered_buffer_back = realloc(_light_rendered_buffer_back, info->width * info->height); - - std::memcpy(&_pixelInfo, info, sizeof(rct_drawpixelinfo)); + _pixelInfo = *info; } extern void viewport_paint_setup(); @@ -188,14 +187,14 @@ void lightfx_prepare_light_list() continue; } - LocationXYZ16 coord_3d = { /* .x = */ entry->x, - /* .y = */ entry->y, - /* .z = */ entry->z }; + CoordsXYZ coord_3d = { /* .x = */ entry->x, + /* .y = */ entry->y, + /* .z = */ entry->z }; - LocationXY16 coord_2d = coordinate_3d_to_2d(&coord_3d, _current_view_rotation_front); + auto screenCoords = translate_3d_to_2d_with_z(_current_view_rotation_front, coord_3d); - entry->x = coord_2d.x; // - (_current_view_x_front); - entry->y = coord_2d.y; // - (_current_view_y_front); + entry->x = screenCoords.x; // - (_current_view_x_front); + entry->y = screenCoords.y; // - (_current_view_y_front); int32_t posOnScreenX = entry->x - _current_view_x_front; int32_t posOnScreenY = entry->y - _current_view_y_front; @@ -752,7 +751,7 @@ uint32_t lightfx_get_light_polution() void lightfx_add_lights_magic_vehicles() { - uint16_t spriteIndex = gSpriteListHead[SPRITE_LIST_TRAIN]; + uint16_t spriteIndex = gSpriteListHead[SPRITE_LIST_VEHICLE_HEAD]; while (spriteIndex != SPRITE_INDEX_NULL) { rct_vehicle* vehicle = &(get_sprite(spriteIndex)->vehicle); @@ -786,7 +785,10 @@ void lightfx_add_lights_magic_vehicles() -10, -10, -9, -8, -7, -6, -4, -2, 0, 2, 4, 6, 7, 8, 9, 10, }; - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + continue; + switch (ride->type) { case RIDE_TYPE_OBSERVATION_TOWER: diff --git a/src/openrct2/drawing/LightFX.h b/src/openrct2/drawing/LightFX.h index 4e58e0b1f7..fcdb4a3645 100644 --- a/src/openrct2/drawing/LightFX.h +++ b/src/openrct2/drawing/LightFX.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/Line.cpp b/src/openrct2/drawing/Line.cpp index 43e3d17f2b..31f2ec7ab0 100644 --- a/src/openrct2/drawing/Line.cpp +++ b/src/openrct2/drawing/Line.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/NewDrawing.cpp b/src/openrct2/drawing/NewDrawing.cpp index dca79cba34..caf66f57e9 100644 --- a/src/openrct2/drawing/NewDrawing.cpp +++ b/src/openrct2/drawing/NewDrawing.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -170,7 +170,7 @@ void gfx_draw_all_dirty_blocks() void gfx_clear(rct_drawpixelinfo* dpi, uint8_t paletteIndex) { - auto drawingEngine = GetDrawingEngine(); + auto drawingEngine = dpi->DrawingEngine; if (drawingEngine != nullptr) { IDrawingContext* dc = drawingEngine->GetDrawingContext(dpi); @@ -180,7 +180,7 @@ void gfx_clear(rct_drawpixelinfo* dpi, uint8_t paletteIndex) void gfx_fill_rect(rct_drawpixelinfo* dpi, int32_t left, int32_t top, int32_t right, int32_t bottom, int32_t colour) { - auto drawingEngine = GetDrawingEngine(); + auto drawingEngine = dpi->DrawingEngine; if (drawingEngine != nullptr) { IDrawingContext* dc = drawingEngine->GetDrawingContext(dpi); @@ -191,7 +191,7 @@ void gfx_fill_rect(rct_drawpixelinfo* dpi, int32_t left, int32_t top, int32_t ri void gfx_filter_rect( rct_drawpixelinfo* dpi, int32_t left, int32_t top, int32_t right, int32_t bottom, FILTER_PALETTE_ID palette) { - auto drawingEngine = GetDrawingEngine(); + auto drawingEngine = dpi->DrawingEngine; if (drawingEngine != nullptr) { IDrawingContext* dc = drawingEngine->GetDrawingContext(dpi); @@ -201,7 +201,7 @@ void gfx_filter_rect( void gfx_draw_line(rct_drawpixelinfo* dpi, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t colour) { - auto drawingEngine = GetDrawingEngine(); + auto drawingEngine = dpi->DrawingEngine; if (drawingEngine != nullptr) { IDrawingContext* dc = drawingEngine->GetDrawingContext(dpi); @@ -211,7 +211,7 @@ void gfx_draw_line(rct_drawpixelinfo* dpi, int32_t x1, int32_t y1, int32_t x2, i void FASTCALL gfx_draw_sprite(rct_drawpixelinfo* dpi, int32_t image, int32_t x, int32_t y, uint32_t tertiary_colour) { - auto drawingEngine = GetDrawingEngine(); + auto drawingEngine = dpi->DrawingEngine; if (drawingEngine != nullptr) { IDrawingContext* dc = drawingEngine->GetDrawingContext(dpi); @@ -221,7 +221,7 @@ void FASTCALL gfx_draw_sprite(rct_drawpixelinfo* dpi, int32_t image, int32_t x, void FASTCALL gfx_draw_glpyh(rct_drawpixelinfo* dpi, int32_t image, int32_t x, int32_t y, uint8_t* palette) { - auto drawingEngine = GetDrawingEngine(); + auto drawingEngine = dpi->DrawingEngine; if (drawingEngine != nullptr) { IDrawingContext* dc = drawingEngine->GetDrawingContext(dpi); @@ -231,7 +231,7 @@ void FASTCALL gfx_draw_glpyh(rct_drawpixelinfo* dpi, int32_t image, int32_t x, i void FASTCALL gfx_draw_sprite_raw_masked(rct_drawpixelinfo* dpi, int32_t x, int32_t y, int32_t maskImage, int32_t colourImage) { - auto drawingEngine = GetDrawingEngine(); + auto drawingEngine = dpi->DrawingEngine; if (drawingEngine != nullptr) { IDrawingContext* dc = drawingEngine->GetDrawingContext(dpi); @@ -241,7 +241,7 @@ void FASTCALL gfx_draw_sprite_raw_masked(rct_drawpixelinfo* dpi, int32_t x, int3 void FASTCALL gfx_draw_sprite_solid(rct_drawpixelinfo* dpi, int32_t image, int32_t x, int32_t y, uint8_t colour) { - auto drawingEngine = GetDrawingEngine(); + auto drawingEngine = dpi->DrawingEngine; if (drawingEngine != nullptr) { IDrawingContext* dc = drawingEngine->GetDrawingContext(dpi); diff --git a/src/openrct2/drawing/NewDrawing.h b/src/openrct2/drawing/NewDrawing.h index fd5df91045..dfa1e23491 100644 --- a/src/openrct2/drawing/NewDrawing.h +++ b/src/openrct2/drawing/NewDrawing.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/Rain.cpp b/src/openrct2/drawing/Rain.cpp index 693d490b6e..57af305803 100644 --- a/src/openrct2/drawing/Rain.cpp +++ b/src/openrct2/drawing/Rain.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/Rain.h b/src/openrct2/drawing/Rain.h index 7c49446384..dae0e61661 100644 --- a/src/openrct2/drawing/Rain.h +++ b/src/openrct2/drawing/Rain.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,4 +18,14 @@ namespace OpenRCT2::Drawing interface IRainDrawer; } +// clang-format off +static constexpr const uint8_t RainPattern[] = +{ + 32, 32, 0, 12, 0, 14, 0, 16, 255, 0, 255, 0, 255, 0, 255, 0, 255, + 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, + 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, + 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 0 +}; +// clang-format on + void DrawRain(rct_drawpixelinfo* dpi, OpenRCT2::Drawing::IRainDrawer* rainDrawer); diff --git a/src/openrct2/drawing/Rect.cpp b/src/openrct2/drawing/Rect.cpp index ff0466c5c4..97f52596ab 100644 --- a/src/openrct2/drawing/Rect.cpp +++ b/src/openrct2/drawing/Rect.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/SSE41Drawing.cpp b/src/openrct2/drawing/SSE41Drawing.cpp index e781419b16..45cbe97caf 100644 --- a/src/openrct2/drawing/SSE41Drawing.cpp +++ b/src/openrct2/drawing/SSE41Drawing.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/ScrollingText.cpp b/src/openrct2/drawing/ScrollingText.cpp index d4d0126f6c..441ece67c3 100644 --- a/src/openrct2/drawing/ScrollingText.cpp +++ b/src/openrct2/drawing/ScrollingText.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -17,50 +17,43 @@ #include "TTF.h" #include +#include -#pragma pack(push, 1) -/* size: 0xA12 */ struct rct_draw_scroll_text { - rct_string_id string_id; // 0x00 - uint32_t string_args_0; // 0x02 - uint32_t string_args_1; // 0x06 - uint16_t position; // 0x0A - uint16_t mode; // 0x0C - uint32_t id; // 0x0E - uint8_t bitmap[64 * 40]; // 0x12 + rct_string_id string_id; + uint8_t string_args[32]; + colour_t colour; + uint16_t position; + uint16_t mode; + uint32_t id; + uint8_t bitmap[64 * 40]; }; -assert_struct_size(rct_draw_scroll_text, 0xA12); -#pragma pack(pop) #define MAX_SCROLLING_TEXT_ENTRIES 32 static rct_draw_scroll_text _drawScrollTextList[MAX_SCROLLING_TEXT_ENTRIES]; static uint8_t _characterBitmaps[FONT_SPRITE_GLYPH_COUNT + SPR_G2_GLYPH_COUNT][8]; static uint32_t _drawSCrollNextIndex = 0; +static std::mutex _scrollingTextMutex; static void scrolling_text_set_bitmap_for_sprite( - utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets); + utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour); static void scrolling_text_set_bitmap_for_ttf( - utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets); + utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour); void scrolling_text_initialise_bitmaps() { uint8_t drawingSurface[64]; - rct_drawpixelinfo dpi = { - /* .bits = */ (uint8_t*)&drawingSurface, - /* .x = */ 0, - /* .y = */ 0, - /* .width = */ 8, - /* .height = */ 8, - /* .pitch = */ 0, - /* .zoom_level = */ 0, - }; + rct_drawpixelinfo dpi; + dpi.bits = (uint8_t*)&drawingSurface; + dpi.width = 8; + dpi.height = 8; for (int32_t i = 0; i < FONT_SPRITE_GLYPH_COUNT; i++) { std::fill_n(drawingSurface, sizeof(drawingSurface), 0x00); - gfx_draw_sprite_software(&dpi, SPR_CHAR_START + FONT_SPRITE_BASE_TINY + i, -1, 0, 0); + gfx_draw_sprite_software(&dpi, ImageId::FromUInt32(SPR_CHAR_START + FONT_SPRITE_BASE_TINY + i), -1, 0); for (int32_t x = 0; x < 8; x++) { @@ -81,7 +74,8 @@ void scrolling_text_initialise_bitmaps() for (int32_t i = 0; i < SPR_G2_GLYPH_COUNT; i++) { std::fill_n(drawingSurface, sizeof(drawingSurface), 0x00); - gfx_draw_sprite_software(&dpi, SPR_G2_CHAR_BEGIN + (FONT_SIZE_TINY * SPR_G2_GLYPH_COUNT) + i, -1, 0, 0); + gfx_draw_sprite_software( + &dpi, ImageId::FromUInt32(SPR_G2_CHAR_BEGIN + (FONT_SIZE_TINY * SPR_G2_GLYPH_COUNT) + i), -1, 0); for (int32_t x = 0; x < 8; x++) { @@ -133,7 +127,8 @@ static uint8_t* font_sprite_get_codepoint_bitmap(int32_t codepoint) } } -static int32_t scrolling_text_get_matching_or_oldest(rct_string_id stringId, uint16_t scroll, uint16_t scrollingMode) +static int32_t scrolling_text_get_matching_or_oldest( + rct_string_id stringId, uint16_t scroll, uint16_t scrollingMode, colour_t colour) { uint32_t oldestId = 0xFFFFFFFF; int32_t scrollIndex = -1; @@ -147,11 +142,9 @@ static int32_t scrolling_text_get_matching_or_oldest(rct_string_id stringId, uin } // If exact match return the matching index - uint32_t stringArgs0, stringArgs1; - std::memcpy(&stringArgs0, gCommonFormatArgs + 0, sizeof(uint32_t)); - std::memcpy(&stringArgs1, gCommonFormatArgs + 4, sizeof(uint32_t)); - if (scrollText->string_id == stringId && scrollText->string_args_0 == stringArgs0 - && scrollText->string_args_1 == stringArgs1 && scrollText->position == scroll && scrollText->mode == scrollingMode) + if (scrollText->string_id == stringId + && std::memcmp(scrollText->string_args, gCommonFormatArgs, sizeof(scrollText->string_args)) == 0 + && scrollText->colour == colour && scrollText->position == scroll && scrollText->mode == scrollingMode) { scrollText->id = _drawSCrollNextIndex; return i + SPR_SCROLLING_TEXT_START; @@ -160,28 +153,15 @@ static int32_t scrolling_text_get_matching_or_oldest(rct_string_id stringId, uin return scrollIndex; } -static uint8_t scrolling_text_get_colour(uint32_t character) -{ - int32_t colour = character & 0x7F; - if (character & COLOUR_FLAG_TRANSLUCENT) - { - return ColourMapA[colour].light; - } - else - { - return ColourMapA[colour].mid_dark; - } -} - static void scrolling_text_format(utf8* dst, size_t size, rct_draw_scroll_text* scrollText) { if (gConfigGeneral.upper_case_banners) { - format_string_to_upper(dst, size, scrollText->string_id, &scrollText->string_args_0); + format_string_to_upper(dst, size, scrollText->string_id, scrollText->string_args); } else { - format_string(dst, size, scrollText->string_id, &scrollText->string_args_0); + format_string(dst, size, scrollText->string_id, scrollText->string_args); } } @@ -1457,43 +1437,33 @@ void scrolling_text_invalidate() { rct_draw_scroll_text& scrollText = _drawScrollTextList[i]; scrollText.string_id = 0; - scrollText.string_args_0 = 0; - scrollText.string_args_1 = 0; + std::memset(scrollText.string_args, 0, sizeof(scrollText.string_args)); } } -/** - * - * rct2: 0x006C42D9 - * @param stringId (ax) - * @param scroll (cx) - * @param scrollingMode (bp) - * @returns ebx - */ -int32_t scrolling_text_setup(paint_session* session, rct_string_id stringId, uint16_t scroll, uint16_t scrollingMode) +int32_t scrolling_text_setup( + paint_session* session, rct_string_id stringId, uint16_t scroll, uint16_t scrollingMode, colour_t colour) { + std::scoped_lock lock(_scrollingTextMutex); + assert(scrollingMode < MAX_SCROLLING_TEXT_MODES); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level != 0) return SPR_SCROLLING_TEXT_DEFAULT; _drawSCrollNextIndex++; - int32_t scrollIndex = scrolling_text_get_matching_or_oldest(stringId, scroll, scrollingMode); + int32_t scrollIndex = scrolling_text_get_matching_or_oldest(stringId, scroll, scrollingMode, colour); if (scrollIndex >= SPR_SCROLLING_TEXT_START) return scrollIndex; // Setup scrolling text - uint32_t stringArgs0, stringArgs1; - std::memcpy(&stringArgs0, gCommonFormatArgs + 0, sizeof(uint32_t)); - std::memcpy(&stringArgs1, gCommonFormatArgs + 4, sizeof(uint32_t)); - - rct_draw_scroll_text* scrollText = &_drawScrollTextList[scrollIndex]; + auto scrollText = &_drawScrollTextList[scrollIndex]; scrollText->string_id = stringId; - scrollText->string_args_0 = stringArgs0; - scrollText->string_args_1 = stringArgs1; + std::memcpy(scrollText->string_args, gCommonFormatArgs, sizeof(scrollText->string_args)); + scrollText->colour = colour; scrollText->position = scroll; scrollText->mode = scrollingMode; scrollText->id = _drawSCrollNextIndex; @@ -1507,11 +1477,11 @@ int32_t scrolling_text_setup(paint_session* session, rct_string_id stringId, uin std::fill_n(scrollText->bitmap, 320 * 8, 0x00); if (LocalisationService_UseTrueTypeFont()) { - scrolling_text_set_bitmap_for_ttf(scrollString, scroll, scrollText->bitmap, scrollingModePositions); + scrolling_text_set_bitmap_for_ttf(scrollString, scroll, scrollText->bitmap, scrollingModePositions, colour); } else { - scrolling_text_set_bitmap_for_sprite(scrollString, scroll, scrollText->bitmap, scrollingModePositions); + scrolling_text_set_bitmap_for_sprite(scrollString, scroll, scrollText->bitmap, scrollingModePositions, colour); } uint32_t imageId = SPR_SCROLLING_TEXT_START + scrollIndex; @@ -1520,9 +1490,9 @@ int32_t scrolling_text_setup(paint_session* session, rct_string_id stringId, uin } static void scrolling_text_set_bitmap_for_sprite( - utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets) + utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour) { - uint8_t characterColour = scrolling_text_get_colour(gCommonFormatArgs[7]); + auto characterColour = colour; utf8* ch = text; while (true) @@ -1583,19 +1553,17 @@ static void scrolling_text_set_bitmap_for_sprite( } } -static void scrolling_text_set_bitmap_for_ttf(utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets) +static void scrolling_text_set_bitmap_for_ttf( + utf8* text, int32_t scroll, uint8_t* bitmap, const int16_t* scrollPositionOffsets, colour_t colour) { #ifndef NO_TTF TTFFontDescriptor* fontDesc = ttf_get_font_from_sprite_base(FONT_SPRITE_BASE_TINY); if (fontDesc->font == nullptr) { - scrolling_text_set_bitmap_for_sprite(text, scroll, bitmap, scrollPositionOffsets); + scrolling_text_set_bitmap_for_sprite(text, scroll, bitmap, scrollPositionOffsets, colour); return; } - // Currently only supports one colour - uint8_t colour = 0; - utf8* dstCh = text; utf8* ch = text; int32_t codepoint; @@ -1605,7 +1573,12 @@ static void scrolling_text_set_bitmap_for_ttf(utf8* text, int32_t scroll, uint8_ { if (codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END) { - colour = (uint8_t)codepoint; + codepoint -= FORMAT_COLOUR_CODE_START; + auto g1 = gfx_get_g1_element(SPR_TEXT_PALETTE); + if (g1 != nullptr) + { + colour = g1->offset[codepoint * 4]; + } } } else @@ -1615,19 +1588,6 @@ static void scrolling_text_set_bitmap_for_ttf(utf8* text, int32_t scroll, uint8_ } *dstCh = 0; - if (colour == 0) - { - colour = scrolling_text_get_colour(gCommonFormatArgs[7]); - } - else - { - const rct_g1_element* g1 = gfx_get_g1_element(SPR_TEXT_PALETTE); - if (g1 != nullptr) - { - colour = g1->offset[(colour - FORMAT_COLOUR_CODE_START) * 4]; - } - } - TTFSurface* surface = ttf_surface_cache_get_or_add(fontDesc->font, text); if (surface == nullptr) { diff --git a/src/openrct2/drawing/TTF.cpp b/src/openrct2/drawing/TTF.cpp index 104e61f55c..b7d2a079f0 100644 --- a/src/openrct2/drawing/TTF.cpp +++ b/src/openrct2/drawing/TTF.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -9,6 +9,8 @@ #ifndef NO_TTF +# include +# include # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdocumentation" # include @@ -53,6 +55,8 @@ static int32_t _ttfGetWidthCacheCount = 0; static int32_t _ttfGetWidthCacheHitCount = 0; static int32_t _ttfGetWidthCacheMissCount = 0; +static std::mutex _mutex; + static TTF_Font* ttf_open_font(const utf8* fontPath, int32_t ptSize); static void ttf_close_font(TTF_Font* font); static uint32_t ttf_surface_cache_hash(TTF_Font* font, const utf8* text); @@ -60,63 +64,111 @@ static void ttf_surface_cache_dispose(ttf_cache_entry* entry); static void ttf_surface_cache_dispose_all(); static void ttf_getwidth_cache_dispose_all(); static bool ttf_get_size(TTF_Font* font, const utf8* text, int32_t* width, int32_t* height); +static void ttf_toggle_hinting(bool); static TTFSurface* ttf_render(TTF_Font* font, const utf8* text); +template class FontLockHelper +{ + T& _mutex; + const bool _enabled; + +public: + FontLockHelper(T& mutex) + : _mutex(mutex) + , _enabled(gConfigGeneral.multithreading) + { + if (_enabled) + _mutex.lock(); + } + ~FontLockHelper() + { + if (_enabled) + _mutex.unlock(); + } +}; + +static void ttf_toggle_hinting(bool) +{ + if (!LocalisationService_UseTrueTypeFont()) + { + return; + } + + for (int32_t i = 0; i < FONT_SIZE_COUNT; i++) + { + TTFFontDescriptor* fontDesc = &(gCurrentTTFFontSet->size[i]); + bool use_hinting = gConfigFonts.enable_hinting && fontDesc->hinting_threshold; + TTF_SetFontHinting(fontDesc->font, use_hinting ? 1 : 0); + } + + if (_ttfSurfaceCacheCount) + { + ttf_surface_cache_dispose_all(); + } +} + bool ttf_initialise() { - if (!_ttfInitialised) + FontLockHelper lock(_mutex); + + if (_ttfInitialised) + return true; + + if (TTF_Init() != 0) { - if (TTF_Init() != 0) + log_error("Couldn't initialise FreeType engine"); + return false; + } + + for (int32_t i = 0; i < FONT_SIZE_COUNT; i++) + { + TTFFontDescriptor* fontDesc = &(gCurrentTTFFontSet->size[i]); + + utf8 fontPath[MAX_PATH]; + if (!platform_get_font_path(fontDesc, fontPath, sizeof(fontPath))) { - log_error("Couldn't initialise FreeType engine"); + log_verbose("Unable to load font '%s'", fontDesc->font_name); return false; } - for (int32_t i = 0; i < FONT_SIZE_COUNT; i++) + fontDesc->font = ttf_open_font(fontPath, fontDesc->ptSize); + if (fontDesc->font == nullptr) { - TTFFontDescriptor* fontDesc = &(gCurrentTTFFontSet->size[i]); - - utf8 fontPath[MAX_PATH]; - if (!platform_get_font_path(fontDesc, fontPath, sizeof(fontPath))) - { - log_verbose("Unable to load font '%s'", fontDesc->font_name); - return false; - } - - fontDesc->font = ttf_open_font(fontPath, fontDesc->ptSize); - if (fontDesc->font == nullptr) - { - log_verbose("Unable to load '%s'", fontPath); - return false; - } + log_verbose("Unable to load '%s'", fontPath); + return false; } - - ttf_toggle_hinting(); - _ttfInitialised = true; } + + ttf_toggle_hinting(true); + + _ttfInitialised = true; + return true; } void ttf_dispose() { - if (_ttfInitialised) + FontLockHelper lock(_mutex); + + if (!_ttfInitialised) + return; + + ttf_surface_cache_dispose_all(); + ttf_getwidth_cache_dispose_all(); + + for (int32_t i = 0; i < FONT_SIZE_COUNT; i++) { - ttf_surface_cache_dispose_all(); - ttf_getwidth_cache_dispose_all(); - - for (int32_t i = 0; i < FONT_SIZE_COUNT; i++) + TTFFontDescriptor* fontDesc = &(gCurrentTTFFontSet->size[i]); + if (fontDesc->font != nullptr) { - TTFFontDescriptor* fontDesc = &(gCurrentTTFFontSet->size[i]); - if (fontDesc->font != nullptr) - { - ttf_close_font(fontDesc->font); - fontDesc->font = nullptr; - } + ttf_close_font(fontDesc->font); + fontDesc->font = nullptr; } - - TTF_Quit(); - _ttfInitialised = false; } + + TTF_Quit(); + + _ttfInitialised = false; } static TTF_Font* ttf_open_font(const utf8* fontPath, int32_t ptSize) @@ -163,22 +215,8 @@ static void ttf_surface_cache_dispose_all() void ttf_toggle_hinting() { - if (!LocalisationService_UseTrueTypeFont()) - { - return; - } - - for (int32_t i = 0; i < FONT_SIZE_COUNT; i++) - { - TTFFontDescriptor* fontDesc = &(gCurrentTTFFontSet->size[i]); - bool use_hinting = gConfigFonts.enable_hinting && fontDesc->hinting_threshold; - TTF_SetFontHinting(fontDesc->font, use_hinting ? 1 : 0); - } - - if (_ttfSurfaceCacheCount) - { - ttf_surface_cache_dispose_all(); - } + FontLockHelper lock(_mutex); + ttf_toggle_hinting(true); } TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, const utf8* text) @@ -187,6 +225,9 @@ TTFSurface* ttf_surface_cache_get_or_add(TTF_Font* font, const utf8* text) uint32_t hash = ttf_surface_cache_hash(font, text); int32_t index = hash % TTF_SURFACE_CACHE_SIZE; + + FontLockHelper lock(_mutex); + for (int32_t i = 0; i < TTF_SURFACE_CACHE_SIZE; i++) { entry = &_ttfSurfaceCache[index]; @@ -260,6 +301,9 @@ uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, const utf8* text) uint32_t hash = ttf_surface_cache_hash(font, text); int32_t index = hash % TTF_GETWIDTH_CACHE_SIZE; + + FontLockHelper lock(_mutex); + for (int32_t i = 0; i < TTF_GETWIDTH_CACHE_SIZE; i++) { entry = &_ttfGetWidthCache[index]; @@ -304,6 +348,7 @@ uint32_t ttf_getwidth_cache_get_or_add(TTF_Font* font, const utf8* text) TTFFontDescriptor* ttf_get_font_from_sprite_base(uint16_t spriteBase) { + FontLockHelper lock(_mutex); return &gCurrentTTFFontSet->size[font_get_size_from_sprite_base(spriteBase)]; } diff --git a/src/openrct2/drawing/TTF.h b/src/openrct2/drawing/TTF.h index d77164687e..e7df27e0de 100644 --- a/src/openrct2/drawing/TTF.h +++ b/src/openrct2/drawing/TTF.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/Text.cpp b/src/openrct2/drawing/Text.cpp index dfc1e8b1fa..07ba4a506b 100644 --- a/src/openrct2/drawing/Text.cpp +++ b/src/openrct2/drawing/Text.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/Text.h b/src/openrct2/drawing/Text.h index f459878480..e12bf63d9a 100644 --- a/src/openrct2/drawing/Text.h +++ b/src/openrct2/drawing/Text.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/drawing/X8DrawingEngine.cpp b/src/openrct2/drawing/X8DrawingEngine.cpp index cf00ded57b..36f321ba3a 100644 --- a/src/openrct2/drawing/X8DrawingEngine.cpp +++ b/src/openrct2/drawing/X8DrawingEngine.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -47,16 +47,6 @@ void X8RainDrawer::SetDPI(rct_drawpixelinfo* dpi) void X8RainDrawer::Draw(int32_t x, int32_t y, int32_t width, int32_t height, int32_t xStart, int32_t yStart) { - // clang-format off - static constexpr const uint8_t RainPattern[] = - { - 32, 32, 0, 12, 0, 14, 0, 16, 255, 0, 255, 0, 255, 0, 255, 0, 255, - 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, - 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, - 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 0 - }; - // clang-format on - const uint8_t* pattern = RainPattern; uint8_t patternXSpace = *pattern++; uint8_t patternYSpace = *pattern++; @@ -131,6 +121,7 @@ void X8RainDrawer::Restore() X8DrawingEngine::X8DrawingEngine([[maybe_unused]] const std::shared_ptr& uiContext) { _drawingContext = new X8DrawingContext(this); + _bitsDPI.DrawingEngine = this; #ifdef __ENABLE_LIGHTFX__ lightfx_set_available(true); _lastLightFXenabled = (gConfigGeneral.enable_light_fx != 0); @@ -739,7 +730,7 @@ void X8DrawingContext::DrawLine(uint32_t colour, int32_t x1, int32_t y1, int32_t void X8DrawingContext::DrawSprite(uint32_t image, int32_t x, int32_t y, uint32_t tertiaryColour) { - gfx_draw_sprite_software(_dpi, image, x, y, tertiaryColour); + gfx_draw_sprite_software(_dpi, ImageId::FromUInt32(image, tertiaryColour), x, y); } void X8DrawingContext::DrawSpriteRawMasked(int32_t x, int32_t y, uint32_t maskImage, uint32_t colourImage) @@ -752,14 +743,13 @@ void X8DrawingContext::DrawSpriteSolid(uint32_t image, int32_t x, int32_t y, uin uint8_t palette[256]; std::fill_n(palette, sizeof(palette), colour); palette[0] = 0; - - image &= 0x7FFFF; - gfx_draw_sprite_palette_set_software(_dpi, image | IMAGE_TYPE_REMAP, x, y, palette, nullptr); + gfx_draw_sprite_palette_set_software( + _dpi, ImageId::FromUInt32((image & 0x7FFFF) | IMAGE_TYPE_REMAP), x, y, palette, nullptr); } void X8DrawingContext::DrawGlyph(uint32_t image, int32_t x, int32_t y, uint8_t* palette) { - gfx_draw_sprite_palette_set_software(_dpi, image, x, y, palette, nullptr); + gfx_draw_sprite_palette_set_software(_dpi, ImageId::FromUInt32(image), x, y, palette, nullptr); } void X8DrawingContext::SetDPI(rct_drawpixelinfo* dpi) diff --git a/src/openrct2/drawing/X8DrawingEngine.h b/src/openrct2/drawing/X8DrawingEngine.h index 8ab558b9d3..41228594b5 100644 --- a/src/openrct2/drawing/X8DrawingEngine.h +++ b/src/openrct2/drawing/X8DrawingEngine.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/interface/Chat.cpp b/src/openrct2/interface/Chat.cpp index 1c4b2a9ce1..fef0df3b23 100644 --- a/src/openrct2/interface/Chat.cpp +++ b/src/openrct2/interface/Chat.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -157,7 +157,7 @@ void chat_draw(rct_drawpixelinfo* dpi, uint8_t chatBackgroundColor) safe_strcpy(lineBuffer, chat_history_get(i), sizeof(lineBuffer)); - stringHeight = chat_history_draw_string(dpi, (void*)&lineCh, x, y, _chatWidth - 10) + 5; + stringHeight = chat_history_draw_string(dpi, (void*)&lineCh, ScreenCoordsXY(x, y), _chatWidth - 10) + 5; gfx_set_dirty_blocks(x, y - stringHeight, x + _chatWidth, y + 20); if ((y - stringHeight) < 50) @@ -235,7 +235,7 @@ void chat_history_add(const char* src) free(buffer); - Mixer_Play_Effect(SOUND_NEWS_ITEM, 0, MIXER_VOLUME_MAX, 0.5f, 1.5f, true); + Mixer_Play_Effect(SoundId::NewsItem, 0, MIXER_VOLUME_MAX, 0.5f, 1.5f, true); } void chat_input(CHAT_INPUT input) @@ -275,7 +275,7 @@ static void chat_clear_input() // This method is the same as gfx_draw_string_left_wrapped. // But this adjusts the initial Y coordinate depending of the number of lines. -int32_t chat_history_draw_string(rct_drawpixelinfo* dpi, void* args, int32_t x, int32_t y, int32_t width) +int32_t chat_history_draw_string(rct_drawpixelinfo* dpi, void* args, ScreenCoordsXY screenCoords, int32_t width) { int32_t fontSpriteBase, lineHeight, lineY, numLines; @@ -291,20 +291,20 @@ int32_t chat_history_draw_string(rct_drawpixelinfo* dpi, void* args, int32_t x, gCurrentFontFlags = 0; - int32_t expectedY = y - (numLines * lineHeight); + int32_t expectedY = screenCoords.y - (numLines * lineHeight); if (expectedY < 50) { return (numLines * lineHeight); // Skip drawing, return total height. } - lineY = y; + lineY = screenCoords.y; for (int32_t line = 0; line <= numLines; ++line) { - gfx_draw_string(dpi, buffer, TEXT_COLOUR_254, x, lineY - (numLines * lineHeight)); + gfx_draw_string(dpi, buffer, TEXT_COLOUR_254, screenCoords.x, lineY - (numLines * lineHeight)); buffer = get_string_end(buffer) + 1; lineY += lineHeight; } - return lineY - y; + return lineY - screenCoords.y; } // Wrap string without drawing, useful to get the height of a wrapped string. diff --git a/src/openrct2/interface/Chat.h b/src/openrct2/interface/Chat.h index 4262f60a60..82379bb81d 100644 --- a/src/openrct2/interface/Chat.h +++ b/src/openrct2/interface/Chat.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,6 +11,7 @@ #define _CHAT_H_ #include "../common.h" +#include "../world/Location.hpp" #define CHAT_HISTORY_SIZE 10 #define CHAT_INPUT_SIZE 1024 @@ -41,6 +42,6 @@ void chat_history_add(const char* src); void chat_input(CHAT_INPUT input); int32_t chat_string_wrapped_get_height(void* args, int32_t width); -int32_t chat_history_draw_string(rct_drawpixelinfo* dpi, void* args, int32_t x, int32_t y, int32_t width); +int32_t chat_history_draw_string(rct_drawpixelinfo* dpi, void* args, ScreenCoordsXY screenCoords, int32_t width); #endif diff --git a/src/openrct2/interface/Colour.cpp b/src/openrct2/interface/Colour.cpp index 28f0a346e6..baa62266a4 100644 --- a/src/openrct2/interface/Colour.cpp +++ b/src/openrct2/interface/Colour.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/interface/Colour.h b/src/openrct2/interface/Colour.h index 14df38630c..c8e587ba85 100644 --- a/src/openrct2/interface/Colour.h +++ b/src/openrct2/interface/Colour.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/interface/Cursors.h b/src/openrct2/interface/Cursors.h index e3b25b465b..fdb527e05c 100644 --- a/src/openrct2/interface/Cursors.h +++ b/src/openrct2/interface/Cursors.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/interface/FontFamilies.cpp b/src/openrct2/interface/FontFamilies.cpp index d7fe3b1d54..65bdc30efc 100644 --- a/src/openrct2/interface/FontFamilies.cpp +++ b/src/openrct2/interface/FontFamilies.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -16,11 +16,13 @@ TTFontFamily const TTFFamilyChineseSimplified = { &TTFFontSimSun, &TTFFontHeiti, + &TTFFontMicroHei, }; TTFontFamily const TTFFamilyChineseTraditional = { &TTFFontMingLiu, &TTFFontLiHeiPro, + &TTFFontMicroHei, }; TTFontFamily const TTFFamilyJapanese = { diff --git a/src/openrct2/interface/FontFamilies.h b/src/openrct2/interface/FontFamilies.h index e7f45d6085..5508aca08d 100644 --- a/src/openrct2/interface/FontFamilies.h +++ b/src/openrct2/interface/FontFamilies.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/interface/Fonts.cpp b/src/openrct2/interface/Fonts.cpp index 49b4d2a20a..7d28446a5e 100644 --- a/src/openrct2/interface/Fonts.cpp +++ b/src/openrct2/interface/Fonts.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -83,6 +83,12 @@ TTFFontSetDescriptor TTFFontArialUnicode = { { { "arialuni.ttf", "Arial Unicode MS", 10, 0, -1, 12, HINTING_THRESHOLD_LOW, nullptr }, { "arialuni.ttf", "Arial Unicode MS", 11, 0, -1, 12, HINTING_THRESHOLD_LOW, nullptr }, } }; + +TTFFontSetDescriptor TTFFontMicroHei = { { + { "wqy-microhei.ttc", "WenQuanYi Micro Hei", 10, -1, -1, 10, HINTING_THRESHOLD_MEDIUM, nullptr }, + { "wqy-microhei.ttc", "WenQuanYi Micro Hei", 11, 1, -1, 14, HINTING_THRESHOLD_MEDIUM, nullptr }, + { "wqy-microhei.ttc", "WenQuanYi Micro Hei", 11, 1, -2, 14, HINTING_THRESHOLD_MEDIUM, nullptr }, +} }; // clang-format on #endif // NO_TTF diff --git a/src/openrct2/interface/Fonts.h b/src/openrct2/interface/Fonts.h index afe7aac0d2..25be04206b 100644 --- a/src/openrct2/interface/Fonts.h +++ b/src/openrct2/interface/Fonts.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -28,6 +28,7 @@ extern TTFFontSetDescriptor TTFFontGulim; extern TTFFontSetDescriptor TTFFontNanum; extern TTFFontSetDescriptor TTFFontArial; extern TTFFontSetDescriptor TTFFontArialUnicode; +extern TTFFontSetDescriptor TTFFontMicroHei; # define FONT(x) x #else # define FONT(x) FONT_OPENRCT2_SPRITE diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index 0c12365fc9..d167a80a99 100644 --- a/src/openrct2/interface/InteractiveConsole.cpp +++ b/src/openrct2/interface/InteractiveConsole.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -16,7 +16,9 @@ #include "../ReplayManager.h" #include "../Version.h" #include "../actions/ClimateSetAction.hpp" +#include "../actions/RideSetPriceAction.hpp" #include "../actions/RideSetSetting.hpp" +#include "../actions/SetCheatAction.hpp" #include "../actions/StaffSetCostumeAction.hpp" #include "../config/Config.h" #include "../core/Guard.hpp" @@ -25,8 +27,8 @@ #include "../drawing/Font.h" #include "../interface/Chat.h" #include "../interface/Colour.h" +#include "../interface/Window_internal.h" #include "../localisation/Localisation.h" -#include "../localisation/User.h" #include "../management/Finance.h" #include "../management/Research.h" #include "../network/network.h" @@ -135,15 +137,12 @@ static int32_t cc_rides(InteractiveConsole& console, const arguments_t& argv) { if (argv[0] == "list") { - Ride* ride; - int32_t i; - FOR_ALL_RIDES (i, ride) + for (const auto& ride : GetRideManager()) { - char name[128]; - format_string(name, 128, ride->name, &ride->name_arguments); + auto name = ride.GetName(); console.WriteFormatLine( - "ride: %03d type: %02u subtype %03u operating mode: %02u name: %s", i, ride->type, ride->subtype, - ride->mode, name); + "ride: %03d type: %02u subtype %03u operating mode: %02u name: %s", ride.id, ride.type, ride.subtype, + ride.mode, name.c_str()); } } else if (argv[0] == "set") @@ -169,6 +168,7 @@ static int32_t cc_rides(InteractiveConsole& console, const arguments_t& argv) console.WriteFormatLine("rides set excitement "); console.WriteFormatLine("rides set intensity "); console.WriteFormatLine("rides set nausea "); + console.WriteFormatLine("rides set price "); } return 0; } @@ -209,12 +209,12 @@ static int32_t cc_rides(InteractiveConsole& console, const arguments_t& argv) } else { - Ride* ride = get_ride(ride_index); + auto ride = get_ride(ride_index); if (mode <= 0 || mode > (RIDE_MODE_COUNT - 1)) { console.WriteFormatLine("Invalid ride mode."); } - else if (ride == nullptr || ride->type == RIDE_TYPE_NULL) + else if (ride == nullptr) { console.WriteFormatLine("No ride found with index %d", ride_index); } @@ -241,12 +241,12 @@ static int32_t cc_rides(InteractiveConsole& console, const arguments_t& argv) } else { - Ride* ride = get_ride(ride_index); + auto ride = get_ride(ride_index); if (mass <= 0) { console.WriteFormatLine("Friction value must be strictly positive"); } - else if (ride->type == RIDE_TYPE_NULL) + else if (ride == nullptr) { console.WriteFormatLine("No ride found with index %d", ride_index); } @@ -281,12 +281,12 @@ static int32_t cc_rides(InteractiveConsole& console, const arguments_t& argv) } else { - Ride* ride = get_ride(ride_index); + auto ride = get_ride(ride_index); if (excitement <= 0) { console.WriteFormatLine("Excitement value must be strictly positive"); } - else if (ride->type == RIDE_TYPE_NULL) + else if (ride == nullptr) { console.WriteFormatLine("No ride found with index %d", ride_index); } @@ -312,12 +312,12 @@ static int32_t cc_rides(InteractiveConsole& console, const arguments_t& argv) } else { - Ride* ride = get_ride(ride_index); + auto ride = get_ride(ride_index); if (intensity <= 0) { console.WriteFormatLine("Intensity value must be strictly positive"); } - else if (ride->type == RIDE_TYPE_NULL) + else if (ride == nullptr) { console.WriteFormatLine("No ride found with index %d", ride_index); } @@ -343,12 +343,12 @@ static int32_t cc_rides(InteractiveConsole& console, const arguments_t& argv) } else { - Ride* ride = get_ride(ride_index); + auto ride = get_ride(ride_index); if (nausea <= 0) { console.WriteFormatLine("Nausea value must be strictly positive"); } - else if (ride->type == RIDE_TYPE_NULL) + else if (ride == nullptr) { console.WriteFormatLine("No ride found with index %d", ride_index); } @@ -358,6 +358,66 @@ static int32_t cc_rides(InteractiveConsole& console, const arguments_t& argv) } } } + else if (argv[1] == "price") + { + bool int_valid[2] = { false }; + if (argv[2] == "all") + { + auto arg1 = console_parse_int(argv[3], &int_valid[0]); + if (argv.size() <= 4) + { + auto price = arg1; + if (int_valid[0]) + { + for (const auto& ride : GetRideManager()) + { + auto rideSetPrice = RideSetPriceAction(ride.id, price, true); + GameActions::Execute(&rideSetPrice); + } + } + else + { + console.WriteFormatLine("This command expects one or two integer arguments"); + } + } + else + { + auto rideType = arg1; + auto price = console_parse_int(argv[4], &int_valid[1]); + + if (int_valid[0] && int_valid[1]) + { + for (const auto& ride : GetRideManager()) + { + if (ride.type == rideType) + { + auto rideSetPrice = RideSetPriceAction(ride.id, price, true); + GameActions::Execute(&rideSetPrice); + } + } + } + else + { + console.WriteFormatLine("This command expects one or two integer arguments"); + } + } + } + else + { + int32_t rideId = console_parse_int(argv[2], &int_valid[0]); + money16 price = console_parse_int(argv[3], &int_valid[1]); + + if (!int_valid[0] || !int_valid[1]) + { + console.WriteFormatLine("This command expects the string all or two integer arguments"); + } + else + { + auto rideSetPrice = RideSetPriceAction(rideId, price, true); + GameActions::Execute(&rideSetPrice); + } + } + } } } else @@ -377,10 +437,9 @@ static int32_t cc_staff(InteractiveConsole& console, const arguments_t& argv) int32_t i; FOR_ALL_STAFF (i, peep) { - char name[128]; - format_string(name, 128, peep->name_string_idx, &peep->id); + auto name = peep->GetName(); console.WriteFormatLine( - "staff id %03d type: %02u energy %03u name %s", i, peep->staff_type, peep->energy, name); + "staff id %03d type: %02u energy %03u name %s", i, peep->staff_type, peep->energy, name.c_str()); } } else if (argv[0] == "set") @@ -583,14 +642,6 @@ static int32_t cc_get(InteractiveConsole& console, const arguments_t& argv) { console.WriteFormatLine("console_small_font %d", gConfigInterface.console_small_font); } - else if (argv[0] == "test_unfinished_tracks") - { - console.WriteFormatLine("test_unfinished_tracks %d", gConfigGeneral.test_unfinished_tracks); - } - else if (argv[0] == "no_test_crashes") - { - console.WriteFormatLine("no_test_crashes %d", gConfigGeneral.no_test_crashes); - } else if (argv[0] == "location") { rct_window* w = window_get_main(); @@ -687,26 +738,18 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv) if (argv[0] == "money" && invalidArguments(&invalidArgs, double_valid[0])) { money32 money = MONEY((int32_t)double_val[0], ((int32_t)(double_val[0] * 100)) % 100); - bool run_get_money = true; if (gCash != money) { - if (game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETMONEY, money, GAME_COMMAND_CHEAT, 0, 0) - != MONEY32_UNDEFINED) - { - // When in networked client mode, console.Execute("get money") - // does not print value accurately. Instead, print the argument. - if (network_get_mode() == NETWORK_MODE_CLIENT) - { - run_get_money = false; - console.WriteFormatLine("money %d.%d0", money / 10, money % 10); - } - } - else - { - console.WriteLineError("Network error: Permission denied!"); - } + auto setCheatAction = SetCheatAction(CheatType::SetMoney, money); + setCheatAction.SetCallback([&console](const GameAction*, const GameActionResult* res) { + if (res->Error != GA_ERROR::OK) + console.WriteLineError("Network error: Permission denied!"); + else + console.Execute("get money"); + }); + GameActions::Execute(&setCheatAction); } - if (run_get_money) + else { console.Execute("get money"); } @@ -857,18 +900,6 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv) config_save_default(); console.Execute("get console_small_font"); } - else if (argv[0] == "test_unfinished_tracks" && invalidArguments(&invalidArgs, int_valid[0])) - { - gConfigGeneral.test_unfinished_tracks = (int_val[0] != 0); - config_save_default(); - console.Execute("get test_unfinished_tracks"); - } - else if (argv[0] == "no_test_crashes" && invalidArguments(&invalidArgs, int_valid[0])) - { - gConfigGeneral.no_test_crashes = (int_val[0] != 0); - config_save_default(); - console.Execute("get no_test_crashes"); - } else if (argv[0] == "location" && invalidArguments(&invalidArgs, int_valid[0] && int_valid[1])) { rct_window* w = window_get_main(); @@ -876,8 +907,8 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv) { int32_t x = (int16_t)(int_val[0] * 32 + 16); int32_t y = (int16_t)(int_val[1] * 32 + 16); - int32_t z = tile_element_height(x, y); - window_set_location(w, x, y, z); + int32_t z = tile_element_height({ x, y }); + w->SetLocation(x, y, z); viewport_update_position(w); console.Execute("get location"); } @@ -913,57 +944,55 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv) { if (gCheatsSandboxMode != (int_val[0] != 0)) { - if (game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_SANDBOXMODE, (int_val[0] != 0), GAME_COMMAND_CHEAT, 0, 0) - != MONEY32_UNDEFINED) - { - // Change it locally so it shows the accurate value in the - // "console.Execute("get cheat_sandbox_mode")" line when in networked client mode - gCheatsSandboxMode = (int_val[0] != 0); - } - else - { - console.WriteLineError("Network error: Permission denied!"); - } + auto setCheatAction = SetCheatAction(CheatType::SandboxMode, int_val[0] != 0); + setCheatAction.SetCallback([&console](const GameAction*, const GameActionResult* res) { + if (res->Error != GA_ERROR::OK) + console.WriteLineError("Network error: Permission denied!"); + else + console.Execute("get cheat_sandbox_mode"); + }); + GameActions::Execute(&setCheatAction); + } + else + { + console.Execute("get cheat_sandbox_mode"); } - console.Execute("get cheat_sandbox_mode"); } else if (argv[0] == "cheat_disable_clearance_checks" && invalidArguments(&invalidArgs, int_valid[0])) { if (gCheatsDisableClearanceChecks != (int_val[0] != 0)) { - if (game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLECLEARANCECHECKS, (int_val[0] != 0), GAME_COMMAND_CHEAT, 0, 0) - != MONEY32_UNDEFINED) - { - // Change it locally so it shows the accurate value in the - // "console.Execute("get cheat_disable_clearance_checks")" line when in networked client mode - gCheatsDisableClearanceChecks = (int_val[0] != 0); - } - else - { - console.WriteLineError("Network error: Permission denied!"); - } + auto setCheatAction = SetCheatAction(CheatType::DisableClearanceChecks, int_val[0] != 0); + setCheatAction.SetCallback([&console](const GameAction*, const GameActionResult* res) { + if (res->Error != GA_ERROR::OK) + console.WriteLineError("Network error: Permission denied!"); + else + console.Execute("get cheat_disable_clearance_checks"); + }); + GameActions::Execute(&setCheatAction); + } + else + { + console.Execute("get cheat_disable_clearance_checks"); } - console.Execute("get cheat_disable_clearance_checks"); } else if (argv[0] == "cheat_disable_support_limits" && invalidArguments(&invalidArgs, int_valid[0])) { if (gCheatsDisableSupportLimits != (int_val[0] != 0)) { - if (game_do_command( - 0, GAME_COMMAND_FLAG_APPLY, CHEAT_DISABLESUPPORTLIMITS, (int_val[0] != 0), GAME_COMMAND_CHEAT, 0, 0) - != MONEY32_UNDEFINED) - { - // Change it locally so it shows the accurate value in the - // "console.Execute("get cheat_disable_support_limits")" line when in networked client mode - gCheatsDisableSupportLimits = (int_val[0] != 0); - } - else - { - console.WriteLineError("Network error: Permission denied!"); - } + auto setCheatAction = SetCheatAction(CheatType::DisableSupportLimits, int_val[0] != 0); + setCheatAction.SetCallback([&console](const GameAction*, const GameActionResult* res) { + if (res->Error != GA_ERROR::OK) + console.WriteLineError("Network error: Permission denied!"); + else + console.Execute("get cheat_disable_support_limits"); + }); + GameActions::Execute(&setCheatAction); + } + else + { + console.Execute("get cheat_disable_support_limits"); } - console.Execute("get cheat_disable_support_limits"); } else if (argv[0] == "current_rotation" && invalidArguments(&invalidArgs, int_valid[0])) { @@ -1118,12 +1147,6 @@ static int32_t cc_object_count(InteractiveConsole& console, [[maybe_unused]] con return 0; } -static int32_t cc_reset_user_strings([[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv) -{ - reset_user_strings(); - return 0; -} - static int32_t cc_open(InteractiveConsole& console, const arguments_t& argv) { if (!argv.empty()) @@ -1188,6 +1211,8 @@ static int32_t cc_remove_park_fences(InteractiveConsole& console, [[maybe_unused } } while (tile_element_iterator_next(&it)); + gfx_invalidate_screen(); + console.WriteFormatLine("Park fences have been removed."); return 0; } @@ -1197,18 +1222,9 @@ static int32_t cc_show_limits(InteractiveConsole& console, [[maybe_unused]] cons map_reorganise_elements(); int32_t tileElementCount = gNextFreeTileElement - gTileElements - 1; - int32_t rideCount = 0; - for (int32_t i = 0; i < MAX_RIDES; ++i) - { - Ride* ride = get_ride(i); - if (ride->type != RIDE_TYPE_NULL) - { - rideCount++; - } - } - + int32_t rideCount = ride_get_count(); int32_t spriteCount = 0; - for (int32_t i = 1; i < NUM_SPRITE_LISTS; ++i) + for (int32_t i = 1; i < SPRITE_LIST_COUNT; ++i) { spriteCount += gSpriteListCount[i]; } @@ -1225,7 +1241,8 @@ static int32_t cc_show_limits(InteractiveConsole& console, [[maybe_unused]] cons int32_t bannerCount = 0; for (BannerIndex i = 0; i < MAX_BANNERS; ++i) { - if (gBanners[i].type != BANNER_NULL) + auto banner = GetBanner(i); + if (banner->type != BANNER_NULL) { bannerCount++; } @@ -1236,6 +1253,7 @@ static int32_t cc_show_limits(InteractiveConsole& console, [[maybe_unused]] cons console.WriteFormatLine("Banners: %d/%zu", bannerCount, MAX_BANNERS); console.WriteFormatLine("Rides: %d/%d", rideCount, MAX_RIDES); console.WriteFormatLine("Staff: %d/%d", staffCount, STAFF_MAX_COUNT); + console.WriteFormatLine("Images: %zu/%zu", ImageListGetUsedCount(), ImageListGetMaximum()); return 0; } @@ -1379,7 +1397,7 @@ static int32_t cc_replay_stoprecord(InteractiveConsole& console, const arguments } auto* replayManager = OpenRCT2::GetContext()->GetReplayManager(); - if (replayManager->IsRecording() == false && replayManager->IsNormalising() == false) + if (!replayManager->IsRecording() && !replayManager->IsNormalising()) { console.WriteFormatLine("Replay currently not recording"); return 0; @@ -1492,6 +1510,66 @@ static int32_t cc_replay_normalise(InteractiveConsole& console, const arguments_ return 0; } +static int32_t cc_mp_desync(InteractiveConsole& console, const arguments_t& argv) +{ + int32_t desyncType = 0; + if (argv.size() >= 1) + { + desyncType = atoi(argv[0].c_str()); + } + + std::vector peeps; + std::vector vehicles; + + for (int i = 0; i < MAX_SPRITES; i++) + { + rct_sprite* sprite = get_sprite(i); + if (sprite->generic.sprite_identifier == SPRITE_IDENTIFIER_NULL) + continue; + + if (sprite->generic.sprite_identifier == SPRITE_IDENTIFIER_PEEP) + peeps.push_back(sprite); + else if (sprite->generic.sprite_identifier == SPRITE_IDENTIFIER_VEHICLE) + vehicles.push_back(sprite); + } + + switch (desyncType) + { + case 0: // Peep t-shirts. + { + if (peeps.empty()) + { + console.WriteFormatLine("No peeps"); + } + else + { + rct_sprite* sprite = peeps[0]; + if (peeps.size() > 1) + sprite = peeps[util_rand() % peeps.size() - 1]; + sprite->peep.tshirt_colour = util_rand() & 0xFF; + invalidate_sprite_0(sprite); + } + break; + } + case 1: // Remove random peep. + { + if (peeps.empty()) + { + console.WriteFormatLine("No peep removed"); + } + else + { + rct_sprite* sprite = peeps[0]; + if (peeps.size() > 1) + sprite = peeps[util_rand() % peeps.size() - 1]; + sprite->AsPeep()->Remove(); + } + break; + } + } + return 0; +} + #pragma warning(push) #pragma warning(disable : 4702) // unreachable code static int32_t cc_abort([[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv) @@ -1556,8 +1634,6 @@ static constexpr const utf8* console_variable_table[] = { "climate", "game_speed", "console_small_font", - "test_unfinished_tracks", - "no_test_crashes", "location", "window_scale", "window_limit", @@ -1597,7 +1673,6 @@ static constexpr const console_command console_command_table[] = { { "quit", cc_close, "Closes the console.", "quit" }, { "remove_park_fences", cc_remove_park_fences, "Removes all park fences from the surface", "remove_park_fences" }, { "remove_unused_objects", cc_remove_unused_objects, "Removes all the unused objects from the object selection.", "remove_unused_objects" }, - { "reset_user_strings", cc_reset_user_strings, "Resets all user-defined strings, to fix incorrectly occurring 'Chosen name in use already' errors.", "reset_user_strings" }, { "rides", cc_rides, "Ride management.", "rides " }, { "save_park", cc_save_park, "Save current state of park. If no name specified default path will be used.", "save_park [name]" }, { "say", cc_say, "Say to other players.", "say " }, @@ -1613,6 +1688,7 @@ static constexpr const console_command console_command_table[] = { { "replay_start", cc_replay_start, "Starts a replay", "replay_start "}, { "replay_stop", cc_replay_stop, "Stops the replay", "replay_stop"}, { "replay_normalise", cc_replay_normalise, "Normalises the replay to remove all gaps", "replay_normalise "}, + { "mp_desync", cc_mp_desync, "Forces a multiplayer desync", "cc_mp_desync [desync_type, 0 = Random t-shirt color on random peep, 1 = Remove random peep ]"}, }; // clang-format on diff --git a/src/openrct2/interface/InteractiveConsole.h b/src/openrct2/interface/InteractiveConsole.h index e689c8b3f3..ec1a3181d4 100644 --- a/src/openrct2/interface/InteractiveConsole.h +++ b/src/openrct2/interface/InteractiveConsole.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/interface/Screenshot.cpp b/src/openrct2/interface/Screenshot.cpp index 2831949b9f..9cbdab5b3e 100644 --- a/src/openrct2/interface/Screenshot.cpp +++ b/src/openrct2/interface/Screenshot.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,10 +13,13 @@ #include "../Game.h" #include "../Intro.h" #include "../OpenRCT2.h" +#include "../actions/SetCheatAction.hpp" #include "../audio/audio.h" #include "../core/Console.hpp" #include "../core/Imaging.h" +#include "../core/Optional.hpp" #include "../drawing/Drawing.h" +#include "../drawing/X8DrawingEngine.h" #include "../localisation/Localisation.h" #include "../platform/platform.h" #include "../util/Util.h" @@ -26,11 +29,15 @@ #include "../world/Surface.h" #include "Viewport.h" +#include #include #include #include +#include +using namespace std::literals::string_literals; using namespace OpenRCT2; +using namespace OpenRCT2::Drawing; uint8_t gScreenshotCountdown = 0; @@ -73,7 +80,7 @@ void screenshot_check() if (!screenshotPath.empty()) { - audio_play_sound(SOUND_WINDOW_OPEN, 100, context_get_width() / 2); + audio_play_sound(SoundId::WindowOpen, 100, context_get_width() / 2); } else { @@ -85,106 +92,96 @@ void screenshot_check() } } -static void screenshot_get_rendered_palette(rct_palette* palette) +static rct_palette screenshot_get_rendered_palette() { + rct_palette palette; for (int32_t i = 0; i < 256; i++) { - palette->entries[i] = gPalette[i]; + palette.entries[i] = gPalette[i]; } + return palette; } -static int32_t screenshot_get_next_path(char* path, size_t size) +static std::string screenshot_get_park_name() +{ + return GetContext()->GetGameState()->GetPark().Name; +} + +static std::string screenshot_get_directory() { char screenshotPath[MAX_PATH]; - platform_get_user_directory(screenshotPath, "screenshot", sizeof(screenshotPath)); - if (!platform_ensure_directory_exists(screenshotPath)) - { - log_error("Unable to save screenshots in OpenRCT2 screenshot directory.\n"); - return -1; - } + return screenshotPath; +} - char park_name[128]; - format_string(park_name, 128, gParkName, &gParkNameArgs); +static std::pair screenshot_get_date_time() +{ + rct2_date date; + platform_get_date_local(&date); - // Retrieve current time - rct2_date currentDate; - platform_get_date_local(¤tDate); - rct2_time currentTime; - platform_get_time_local(¤tTime); + rct2_time time; + platform_get_time_local(&time); -#ifdef _WIN32 - // On NTFS filesystems, a colon (:) in a path - // indicates you want to write a file stream - // (hidden metadata). This will pass the - // file_exists and fopen checks, since it is - // technically valid. We don't want that, so - // replace colons with hyphens in the park name. - char* foundColon = park_name; - while ((foundColon = strchr(foundColon, ':')) != nullptr) - { - *foundColon = '-'; - } -#endif + return { date, time }; +} - // Glue together path and filename - safe_strcpy(path, screenshotPath, size); - path_end_with_separator(path, size); - auto fileNameCh = strchr(path, '\0'); - if (fileNameCh == nullptr) - { - log_error("Unable to generate a screenshot filename."); - return -1; - } - const size_t leftBytes = size - strlen(path); +static std::string screenshot_get_formatted_date_time() +{ + auto [date, time] = screenshot_get_date_time(); + char formatted[64]; snprintf( - fileNameCh, leftBytes, "%s %d-%02d-%02d %02d-%02d-%02d.png", park_name, currentDate.year, currentDate.month, - currentDate.day, currentTime.hour, currentTime.minute, currentTime.second); + formatted, sizeof(formatted), "%4d-%02d-%02d %02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.minute, + time.second); + return formatted; +} - if (!platform_file_exists(path)) +static opt::optional screenshot_get_next_path() +{ + auto screenshotDirectory = screenshot_get_directory(); + if (!platform_ensure_directory_exists(screenshotDirectory.c_str())) { - return 0; // path ok + log_error("Unable to save screenshots in OpenRCT2 screenshot directory."); + return {}; } - // multiple screenshots with same timestamp - // might be possible when switching timezones - // in the unlikely case that this does happen, - // append (%d) to the filename and increment - // this int32_t until it doesn't overwrite any - // other file in the directory. - int32_t i; - for (i = 1; i < 1000; i++) - { - // Glue together path and filename - snprintf( - fileNameCh, leftBytes, "%s %d-%02d-%02d %02d-%02d-%02d (%d).png", park_name, currentDate.year, currentDate.month, - currentDate.day, currentTime.hour, currentTime.minute, currentTime.second, i); + auto parkName = screenshot_get_park_name(); + auto dateTime = screenshot_get_formatted_date_time(); + auto name = parkName + " " + dateTime; - if (!platform_file_exists(path)) + // Generate a path with a `tries` number + auto pathComposer = [&screenshotDirectory, &name](int tries) { + auto composedFilename = platform_sanitise_filename( + name + ((tries > 0) ? " ("s + std::to_string(tries) + ")" : ""s) + ".png"); + return screenshotDirectory + PATH_SEPARATOR + composedFilename; + }; + + for (int tries = 0; tries < 100; tries++) + { + auto path = pathComposer(tries); + if (!platform_file_exists(path.c_str())) { - return i; + return path; } } - log_error("You have too many saved screenshots saved at exactly the same date and time.\n"); - return -1; -} + log_error("You have too many saved screenshots saved at exactly the same date and time."); + return {}; +}; std::string screenshot_dump_png(rct_drawpixelinfo* dpi) { // Get a free screenshot path - char path[MAX_PATH] = ""; - if (screenshot_get_next_path(path, MAX_PATH) == -1) + auto path = screenshot_get_next_path(); + + if (path == opt::nullopt) { return ""; } - rct_palette renderedPalette; - screenshot_get_rendered_palette(&renderedPalette); - - if (WriteDpiToFile(path, dpi, renderedPalette)) + auto renderedPalette = screenshot_get_rendered_palette(); + if (WriteDpiToFile(path->c_str(), dpi, renderedPalette)) { - return std::string(path); + return *path; } else { @@ -194,9 +191,9 @@ std::string screenshot_dump_png(rct_drawpixelinfo* dpi) std::string screenshot_dump_png_32bpp(int32_t width, int32_t height, const void* pixels) { - // Get a free screenshot path - char path[MAX_PATH] = ""; - if (screenshot_get_next_path(path, MAX_PATH) == -1) + auto path = screenshot_get_next_path(); + + if (path == opt::nullopt) { return ""; } @@ -212,8 +209,8 @@ std::string screenshot_dump_png_32bpp(int32_t width, int32_t height, const void* image.Depth = 32; image.Stride = width * 4; image.Pixels = std::vector(pixels8, pixels8 + pixelsLen); - Imaging::WriteToFile(path, image, IMAGE_FORMAT::PNG_32); - return std::string(path); + Imaging::WriteToFile(path->c_str(), image, IMAGE_FORMAT::PNG_32); + return *path; } catch (const std::exception& e) { @@ -222,99 +219,212 @@ std::string screenshot_dump_png_32bpp(int32_t width, int32_t height, const void* } } -void screenshot_giant() +enum class EdgeType { - int32_t originalRotation = get_current_rotation(); - int32_t originalZoom = 0; + LEFT, + TOP, + RIGHT, + BOTTOM +}; - rct_window* mainWindow = window_get_main(); - rct_viewport* vp = window_get_viewport(mainWindow); - if (mainWindow != nullptr && vp != nullptr) - originalZoom = vp->zoom; - - int32_t rotation = originalRotation; - int32_t zoom = originalZoom; - int32_t mapSize = gMapSize; - int32_t resolutionWidth = (mapSize * 32 * 2) >> zoom; - int32_t resolutionHeight = (mapSize * 32 * 1) >> zoom; - - resolutionWidth += 8; - resolutionHeight += 128; - - rct_viewport viewport; - viewport.x = 0; - viewport.y = 0; - viewport.width = resolutionWidth; - viewport.height = resolutionHeight; - viewport.view_width = viewport.width; - viewport.view_height = viewport.height; - viewport.var_11 = 0; - viewport.flags = vp->flags; - - int32_t centreX = (mapSize / 2) * 32 + 16; - int32_t centreY = (mapSize / 2) * 32 + 16; - - int32_t x = 0, y = 0; - int32_t z = tile_element_height(centreX, centreY) & 0xFFFF; - switch (rotation) +static CoordsXY GetEdgeTile(int32_t mapSize, int32_t rotation, EdgeType edgeType, bool visible) +{ + int32_t lower = (visible ? 1 : 0) * 32; + int32_t upper = (visible ? mapSize - 2 : mapSize - 1) * 32; + switch (edgeType) { - case 0: - x = centreY - centreX; - y = ((centreX + centreY) / 2) - z; - break; - case 1: - x = -centreY - centreX; - y = ((-centreX + centreY) / 2) - z; - break; - case 2: - x = -centreY + centreX; - y = ((-centreX - centreY) / 2) - z; - break; - case 3: - x = centreY + centreX; - y = ((centreX - centreY) / 2) - z; - break; + default: + case EdgeType::LEFT: + switch (rotation) + { + default: + case 0: + return { upper, lower }; + case 1: + return { upper, upper }; + case 2: + return { lower, upper }; + case 3: + return { lower, lower }; + } + case EdgeType::TOP: + switch (rotation) + { + default: + case 0: + return { lower, lower }; + case 1: + return { upper, lower }; + case 2: + return { upper, upper }; + case 3: + return { lower, upper }; + } + case EdgeType::RIGHT: + switch (rotation) + { + default: + case 0: + return { lower, upper }; + case 1: + return { lower, lower }; + case 2: + return { upper, lower }; + case 3: + return { upper, upper }; + } + case EdgeType::BOTTOM: + switch (rotation) + { + default: + case 0: + return { upper, upper }; + case 1: + return { lower, upper }; + case 2: + return { lower, lower }; + case 3: + return { upper, lower }; + } } +} - viewport.view_x = x - ((viewport.view_width << zoom) / 2); - viewport.view_y = y - ((viewport.view_height << zoom) / 2); +static int32_t GetHighestBaseClearanceZ(CoordsXY location) +{ + int32_t z = 0; + auto element = map_get_first_element_at(location.x >> 5, location.y >> 5); + if (element != nullptr) + { + do + { + z = std::max(z, element->base_height); + z = std::max(z, element->clearance_height); + } while (!(element++)->IsLastForTile()); + } + return z * 8; +} + +static int32_t GetTallestVisibleTileTop(int32_t mapSize, int32_t rotation) +{ + int32_t minViewY = 0; + for (int32_t y = 1; y < mapSize - 1; y++) + { + for (int32_t x = 1; x < mapSize - 1; x++) + { + auto location = CoordsXY(x * 32, y * 32); + int32_t z = GetHighestBaseClearanceZ(location); + int32_t viewY = translate_3d_to_2d_with_z(rotation, CoordsXYZ(location, z)).y; + minViewY = std::min(minViewY, viewY); + } + } + return minViewY - 256; +} + +static rct_viewport GetGiantViewport(int32_t mapSize, int32_t rotation, int32_t zoom) +{ + // Get the tile coordinates of each corner + auto leftTileCoords = GetEdgeTile(mapSize, rotation, EdgeType::LEFT, false); + auto rightTileCoords = GetEdgeTile(mapSize, rotation, EdgeType::RIGHT, false); + auto bottomTileCoords = GetEdgeTile(mapSize, rotation, EdgeType::BOTTOM, false); + + // Centre the coordinates so we don't have a hard crop at the edge of the visible tile + leftTileCoords += CoordsXY(16, 16); + rightTileCoords += CoordsXY(16, 16); + bottomTileCoords += CoordsXY(16, 16); + + // Calculate the viewport bounds + int32_t left = translate_3d_to_2d_with_z(rotation, CoordsXYZ(leftTileCoords, 0)).x; + int32_t top = GetTallestVisibleTileTop(mapSize, rotation); + int32_t right = translate_3d_to_2d_with_z(rotation, CoordsXYZ(rightTileCoords, 0)).x; + int32_t bottom = translate_3d_to_2d_with_z(rotation, CoordsXYZ(bottomTileCoords, 0)).y; + + rct_viewport viewport{}; + viewport.view_x = left; + viewport.view_y = top; + viewport.view_width = right - left; + viewport.view_height = bottom - top; + viewport.width = viewport.view_width >> zoom; + viewport.height = viewport.view_height >> zoom; viewport.zoom = zoom; - gCurrentRotation = rotation; + return viewport; +} +static rct_drawpixelinfo RenderViewport(IDrawingEngine* drawingEngine, const rct_viewport& viewport) +{ // Ensure sprites appear regardless of rotation reset_all_sprite_quadrant_placements(); rct_drawpixelinfo dpi; - dpi.x = 0; - dpi.y = 0; - dpi.width = resolutionWidth; - dpi.height = resolutionHeight; - dpi.pitch = 0; - dpi.zoom_level = 0; - dpi.bits = (uint8_t*)malloc(dpi.width * dpi.height); - - viewport_render(&dpi, &viewport, 0, 0, viewport.width, viewport.height); - - // Get a free screenshot path - char path[MAX_PATH]; - if (screenshot_get_next_path(path, MAX_PATH) == -1) + dpi.width = viewport.width; + dpi.height = viewport.height; + dpi.bits = (uint8_t*)malloc((size_t)dpi.width * dpi.height); + if (dpi.bits == nullptr) { - log_error("Giant screenshot failed, unable to find a suitable destination path."); - context_show_error(STR_SCREENSHOT_FAILED, STR_NONE); - return; + throw std::runtime_error("Giant screenshot failed, unable to allocate memory for image."); } - rct_palette renderedPalette; - screenshot_get_rendered_palette(&renderedPalette); + if (viewport.flags & VIEWPORT_FLAG_TRANSPARENT_BACKGROUND) + { + std::memset(dpi.bits, PALETTE_INDEX_0, (size_t)dpi.width * dpi.height); + } - WriteDpiToFile(path, &dpi, renderedPalette); + std::unique_ptr tempDrawingEngine; + if (drawingEngine == nullptr) + { + tempDrawingEngine = std::make_unique(GetContext()->GetUiContext()); + drawingEngine = tempDrawingEngine.get(); + } + dpi.DrawingEngine = drawingEngine; + viewport_render(&dpi, &viewport, 0, 0, viewport.width, viewport.height); + return dpi; +} +void screenshot_giant() +{ + rct_drawpixelinfo dpi; + try + { + auto path = screenshot_get_next_path(); + if (path == opt::nullopt) + { + throw std::runtime_error("Giant screenshot failed, unable to find a suitable destination path."); + } + + int32_t rotation = get_current_rotation(); + int32_t zoom = 0; + + auto mainWindow = window_get_main(); + auto vp = window_get_viewport(mainWindow); + if (mainWindow != nullptr && vp != nullptr) + { + zoom = vp->zoom; + } + + auto viewport = GetGiantViewport(gMapSize, rotation, zoom); + if (vp != nullptr) + { + viewport.flags = vp->flags; + } + if (gConfigGeneral.transparent_screenshot) + { + viewport.flags |= VIEWPORT_FLAG_TRANSPARENT_BACKGROUND; + } + + dpi = RenderViewport(nullptr, viewport); + auto renderedPalette = screenshot_get_rendered_palette(); + WriteDpiToFile(path->c_str(), &dpi, renderedPalette); + + // Show user that screenshot saved successfully + set_format_arg(0, rct_string_id, STR_STRING); + set_format_arg(2, char*, path_get_filename(path->c_str())); + context_show_error(STR_SCREENSHOT_SAVED_AS, STR_NONE); + } + catch (const std::exception& e) + { + log_error("%s", e.what()); + context_show_error(STR_SCREENSHOT_FAILED, STR_NONE); + } free(dpi.bits); - - // Show user that screenshot saved successfully - set_format_arg(0, rct_string_id, STR_STRING); - set_format_arg(2, char*, path_get_filename(path)); - context_show_error(STR_SCREENSHOT_SAVED_AS, STR_NONE); } static void benchgfx_render_screenshots(const char* inputPath, std::unique_ptr& context, uint32_t iterationCount) @@ -327,62 +437,32 @@ static void benchgfx_render_screenshots(const char* inputPath, std::unique_ptr duration = endTime - startTime; - char engine_name[128]; - rct_string_id engine_id = DrawingEngineStringIds[drawing_engine_get_type()]; - format_string(engine_name, sizeof(engine_name), engine_id, nullptr); - Console::WriteLine( - "Rendering %d times with drawing engine %s took %.2f seconds.", iterationCount, engine_name, duration.count()); + auto startTime = std::chrono::high_resolution_clock::now(); + for (uint32_t i = 0; i < iterationCount; i++) + { + // Render at various zoom levels + auto viewport = GetGiantViewport(gMapSize, get_current_rotation(), 0); + viewport.zoom = i & 3; + dpi = RenderViewport(nullptr, viewport); + free(dpi.bits); + dpi.bits = nullptr; + } + auto endTime = std::chrono::high_resolution_clock::now(); + std::chrono::duration duration = endTime - startTime; + auto engineStringId = DrawingEngineStringIds[DRAWING_ENGINE_SOFTWARE]; + auto engineName = format_string(engineStringId, nullptr); + std::printf( + "Rendering %u times with drawing engine %s took %.2f seconds.", iterationCount, engineName.c_str(), + duration.count()); + } + catch (const std::exception& e) + { + std::fprintf(stderr, "%s", e.what()); + } free(dpi.bits); } @@ -418,6 +498,60 @@ int32_t cmdline_for_gfxbench(const char** argv, int32_t argc) return 1; } +static void ApplyOptions(const ScreenshotOptions* options, rct_viewport& viewport) +{ + if (options->weather != 0) + { + if (options->weather < 1 || options->weather > 6) + { + throw std::runtime_error("Weather can only be set to an integer value from 1 till 6."); + } + + uint8_t customWeather = options->weather - 1; + climate_force_weather(customWeather); + } + + if (options->hide_guests) + { + viewport.flags |= VIEWPORT_FLAG_INVISIBLE_PEEPS; + } + + if (options->hide_sprites) + { + viewport.flags |= VIEWPORT_FLAG_INVISIBLE_SPRITES; + } + + if (options->mowed_grass) + { + CheatsSet(CheatType::SetGrassLength, GRASS_LENGTH_MOWED); + } + + if (options->clear_grass || options->tidy_up_park) + { + CheatsSet(CheatType::SetGrassLength, GRASS_LENGTH_CLEAR_0); + } + + if (options->water_plants || options->tidy_up_park) + { + CheatsSet(CheatType::WaterPlants); + } + + if (options->fix_vandalism || options->tidy_up_park) + { + CheatsSet(CheatType::FixVandalism); + } + + if (options->remove_litter || options->tidy_up_park) + { + CheatsSet(CheatType::RemoveLitter); + } + + if (options->transparent || gConfigGeneral.transparent_screenshot) + { + viewport.flags |= VIEWPORT_FLAG_TRANSPARENT_BACKGROUND; + } +} + int32_t cmdline_for_screenshot(const char** argv, int32_t argc, ScreenshotOptions* options) { // Don't include options in the count (they have been handled by CommandLine::ParseOptions already) @@ -439,201 +573,121 @@ int32_t cmdline_for_screenshot(const char** argv, int32_t argc, ScreenshotOption return -1; } - core_init(); - bool customLocation = false; - bool centreMapX = false; - bool centreMapY = false; - int32_t resolutionWidth, resolutionHeight, customX = 0, customY = 0, customZoom, customRotation = 0; + int32_t exitCode = 1; + rct_drawpixelinfo dpi; + try + { + core_init(); + bool customLocation = false; + bool centreMapX = false; + bool centreMapY = false; - const char* inputPath = argv[0]; - const char* outputPath = argv[1]; - if (giantScreenshot) - { - resolutionWidth = 0; - resolutionHeight = 0; - customLocation = true; - centreMapX = true; - centreMapY = true; - customZoom = std::atoi(argv[3]); - customRotation = std::atoi(argv[4]) & 3; - } - else - { - resolutionWidth = std::atoi(argv[2]); - resolutionHeight = std::atoi(argv[3]); - if (argc == 8) + const char* inputPath = argv[0]; + const char* outputPath = argv[1]; + + gOpenRCT2Headless = true; + auto context = CreateContext(); + if (!context->Initialise()) { - customLocation = true; - if (argv[4][0] == 'c') - centreMapX = true; - else - customX = std::atoi(argv[4]); - - if (argv[5][0] == 'c') - centreMapY = true; - else - customY = std::atoi(argv[5]); - - customZoom = std::atoi(argv[6]); - customRotation = std::atoi(argv[7]) & 3; + throw std::runtime_error("Failed to initialize context."); } - else - { - customZoom = 0; - } - } - gOpenRCT2Headless = true; - auto context = CreateContext(); - if (context->Initialise()) - { drawing_engine_init(); - try + if (!context->LoadParkFromFile(inputPath)) { - context->LoadParkFromFile(inputPath); - } - catch (const std::exception& e) - { - std::printf("%s\n", e.what()); - drawing_engine_dispose(); - return -1; + throw std::runtime_error("Failed to load park."); } gIntroState = INTRO_STATE_NONE; gScreenFlags = SCREEN_FLAGS_PLAYING; - int32_t mapSize = gMapSize; - if (resolutionWidth == 0 || resolutionHeight == 0) + rct_viewport viewport{}; + if (giantScreenshot) { - resolutionWidth = (mapSize * 32 * 2) >> customZoom; - resolutionHeight = (mapSize * 32 * 1) >> customZoom; - - resolutionWidth += 8; - resolutionHeight += 128; - } - - rct_viewport viewport; - viewport.x = 0; - viewport.y = 0; - viewport.width = resolutionWidth; - viewport.height = resolutionHeight; - viewport.view_width = viewport.width; - viewport.view_height = viewport.height; - viewport.var_11 = 0; - viewport.flags = 0; - - if (customLocation) - { - if (centreMapX) - customX = (mapSize / 2) * 32 + 16; - if (centreMapY) - customY = (mapSize / 2) * 32 + 16; - - int32_t x = 0, y = 0; - int32_t z = tile_element_height(customX, customY) & 0xFFFF; - switch (customRotation) - { - case 0: - x = customY - customX; - y = ((customX + customY) / 2) - z; - break; - case 1: - x = -customY - customX; - y = ((-customX + customY) / 2) - z; - break; - case 2: - x = -customY + customX; - y = ((-customX - customY) / 2) - z; - break; - case 3: - x = customY + customX; - y = ((customX - customY) / 2) - z; - break; - } - - viewport.view_x = x - ((viewport.view_width << customZoom) / 2); - viewport.view_y = y - ((viewport.view_height << customZoom) / 2); - viewport.zoom = customZoom; - gCurrentRotation = customRotation; + auto zoom = std::atoi(argv[3]); + auto rotation = std::atoi(argv[4]) & 3; + viewport = GetGiantViewport(gMapSize, rotation, zoom); + gCurrentRotation = rotation; } else { - viewport.view_x = gSavedViewX - (viewport.view_width / 2); - viewport.view_y = gSavedViewY - (viewport.view_height / 2); - viewport.zoom = gSavedViewZoom; - gCurrentRotation = gSavedViewRotation; - } - - if (options->weather != 0) - { - if (options->weather < 1 || options->weather > 6) + int32_t resolutionWidth = std::atoi(argv[2]); + int32_t resolutionHeight = std::atoi(argv[3]); + int32_t customX = 0; + int32_t customY = 0; + int32_t customZoom = 0; + int32_t customRotation = 0; + if (argc == 8) { - std::printf("Weather can only be set to an integer value from 1 till 6."); - drawing_engine_dispose(); - return -1; + customLocation = true; + if (argv[4][0] == 'c') + centreMapX = true; + else + customX = std::atoi(argv[4]); + + if (argv[5][0] == 'c') + centreMapY = true; + else + customY = std::atoi(argv[5]); + + customZoom = std::atoi(argv[6]); + customRotation = std::atoi(argv[7]) & 3; } - uint8_t customWeather = options->weather - 1; - climate_force_weather(customWeather); + int32_t mapSize = gMapSize; + if (resolutionWidth == 0 || resolutionHeight == 0) + { + resolutionWidth = (mapSize * 32 * 2) >> customZoom; + resolutionHeight = (mapSize * 32 * 1) >> customZoom; + + resolutionWidth += 8; + resolutionHeight += 128; + } + + viewport.width = resolutionWidth; + viewport.height = resolutionHeight; + viewport.view_width = viewport.width; + viewport.view_height = viewport.height; + if (customLocation) + { + if (centreMapX) + customX = (mapSize / 2) * 32 + 16; + if (centreMapY) + customY = (mapSize / 2) * 32 + 16; + + int32_t z = tile_element_height({ customX, customY }); + CoordsXYZ coords3d = { customX, customY, z }; + + auto coords2d = translate_3d_to_2d_with_z(customRotation, coords3d); + + viewport.view_x = coords2d.x - ((viewport.view_width << customZoom) / 2); + viewport.view_y = coords2d.y - ((viewport.view_height << customZoom) / 2); + viewport.zoom = customZoom; + gCurrentRotation = customRotation; + } + else + { + viewport.view_x = gSavedViewX - (viewport.view_width / 2); + viewport.view_y = gSavedViewY - (viewport.view_height / 2); + viewport.zoom = gSavedViewZoom; + gCurrentRotation = gSavedViewRotation; + } } - // Ensure sprites appear regardless of rotation - reset_all_sprite_quadrant_placements(); - - rct_drawpixelinfo dpi; - dpi.x = 0; - dpi.y = 0; - dpi.width = resolutionWidth; - dpi.height = resolutionHeight; - dpi.pitch = 0; - dpi.zoom_level = 0; - dpi.bits = (uint8_t*)malloc(dpi.width * dpi.height); - - if (options->hide_guests) - { - viewport.flags |= VIEWPORT_FLAG_INVISIBLE_PEEPS; - } - - if (options->hide_sprites) - { - viewport.flags |= VIEWPORT_FLAG_INVISIBLE_SPRITES; - } - - if (options->mowed_grass) - { - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGRASSLENGTH, GRASS_LENGTH_MOWED, GAME_COMMAND_CHEAT, 0, 0); - } - - if (options->clear_grass || options->tidy_up_park) - { - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_SETGRASSLENGTH, GRASS_LENGTH_CLEAR_0, GAME_COMMAND_CHEAT, 0, 0); - } - - if (options->water_plants || options->tidy_up_park) - { - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_WATERPLANTS, 0, GAME_COMMAND_CHEAT, 0, 0); - } - - if (options->fix_vandalism || options->tidy_up_park) - { - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_FIXVANDALISM, 0, GAME_COMMAND_CHEAT, 0, 0); - } - - if (options->remove_litter || options->tidy_up_park) - { - game_do_command(0, GAME_COMMAND_FLAG_APPLY, CHEAT_REMOVELITTER, 0, GAME_COMMAND_CHEAT, 0, 0); - } - - viewport_render(&dpi, &viewport, 0, 0, viewport.width, viewport.height); - - rct_palette renderedPalette; - screenshot_get_rendered_palette(&renderedPalette); + ApplyOptions(options, viewport); + dpi = RenderViewport(nullptr, viewport); + auto renderedPalette = screenshot_get_rendered_palette(); WriteDpiToFile(outputPath, &dpi, renderedPalette); - - free(dpi.bits); - drawing_engine_dispose(); } - return 1; + catch (const std::exception& e) + { + std::printf("%s\n", e.what()); + exitCode = -1; + } + free(dpi.bits); + drawing_engine_dispose(); + + return exitCode; } diff --git a/src/openrct2/interface/Screenshot.h b/src/openrct2/interface/Screenshot.h index ee3923c8b2..f0e29bf8ba 100644 --- a/src/openrct2/interface/Screenshot.h +++ b/src/openrct2/interface/Screenshot.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -28,6 +28,7 @@ struct ScreenshotOptions bool fix_vandalism = false; bool remove_litter = false; bool tidy_up_park = false; + bool transparent = false; }; void screenshot_check(); diff --git a/src/openrct2/interface/StdInOutConsole.cpp b/src/openrct2/interface/StdInOutConsole.cpp index b76d0dc3e5..df6c2359f3 100644 --- a/src/openrct2/interface/StdInOutConsole.cpp +++ b/src/openrct2/interface/StdInOutConsole.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/interface/Viewport.cpp b/src/openrct2/interface/Viewport.cpp index 1ad01f0276..f66a4d7897 100644 --- a/src/openrct2/interface/Viewport.cpp +++ b/src/openrct2/interface/Viewport.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,6 +14,8 @@ #include "../Input.h" #include "../OpenRCT2.h" #include "../config/Config.h" +#include "../core/Guard.hpp" +#include "../core/JobPool.hpp" #include "../drawing/Drawing.h" #include "../paint/Paint.h" #include "../peep/Staff.h" @@ -42,6 +44,7 @@ rct_viewport g_viewport_list[MAX_VIEWPORT_COUNT]; rct_viewport* g_music_tracking_viewport; static TileElement* _interaction_element = nullptr; +static std::unique_ptr _paintJobs; int16_t gSavedViewX; int16_t gSavedViewY; @@ -58,9 +61,8 @@ static rct_drawpixelinfo _viewportDpi2; static uint8_t _interactionSpriteType; static int16_t _interactionMapX; static int16_t _interactionMapY; -static uint16_t _unk9AC154; +static uint16_t _interactionFlags; -static void viewport_paint_column(rct_drawpixelinfo* dpi, uint32_t viewFlags, std::vector* sessions); static void viewport_paint_weather_gloom(rct_drawpixelinfo* dpi); /** @@ -94,6 +96,7 @@ void viewport_init_all() textinput_cancel(); } +// TODO: Return ScreenCoords, takein CoordsXYZ /** * Converts between 3d point of a sprite to 2d coordinates for centring on that * sprite @@ -108,9 +111,9 @@ void centre_2d_coordinates(int32_t x, int32_t y, int32_t z, int32_t* out_x, int3 { int32_t start_x = x; - LocationXYZ16 coord_3d = { (int16_t)x, (int16_t)y, (int16_t)z }; + CoordsXYZ coord_3d = { x, y, z }; - LocationXY16 coord_2d = coordinate_3d_to_2d(&coord_3d, get_current_rotation()); + auto coord_2d = translate_3d_to_2d_with_z(get_current_rotation(), coord_3d); // If the start location was invalid // propagate the invalid location to the output. @@ -220,7 +223,7 @@ void viewport_adjust_for_map_height(int16_t* x, int16_t* y, int16_t* z) for (int32_t i = 0; i < 6; i++) { pos = viewport_coord_to_map_coord(start_x, start_y, height); - height = tile_element_height((0xFFFF) & pos.x, (0xFFFF) & pos.y); + height = tile_element_height({ (0xFFFF) & pos.x, (0xFFFF) & pos.y }); // HACK: This is to prevent the x and y values being set to values outside // of the map. This can happen when the height is larger than the map size. @@ -253,9 +256,10 @@ static void viewport_redraw_after_shift( || viewport->x >= window->x + window->width || viewport->y + viewport->height <= window->y || viewport->y >= window->y + window->height) { - auto nextWindowIndex = window_get_index(window) + 1; - auto nextWindow = nextWindowIndex >= g_window_list.size() ? nullptr : g_window_list[nextWindowIndex].get(); - viewport_redraw_after_shift(dpi, nextWindow, viewport, x, y); + auto itWindowPos = window_get_iterator(window); + auto itNextWindow = itWindowPos != g_window_list.end() ? std::next(itWindowPos) : g_window_list.end(); + viewport_redraw_after_shift( + dpi, itNextWindow == g_window_list.end() ? nullptr : itNextWindow->get(), viewport, x, y); return; } @@ -367,9 +371,10 @@ static void viewport_redraw_after_shift( static void viewport_shift_pixels( rct_drawpixelinfo* dpi, rct_window* window, rct_viewport* viewport, int16_t x_diff, int16_t y_diff) { - for (auto i = window_get_index(window); i < g_window_list.size(); i++) + auto it = window_get_iterator(window); + for (; it != g_window_list.end(); it++) { - auto w = g_window_list[i].get(); + auto w = it->get(); if (!(w->flags & WF_TRANSPARENT)) continue; if (w->viewport == viewport) @@ -521,7 +526,7 @@ static void viewport_set_underground_flag(int32_t underground, rct_window* windo if (bit) return; } - window_invalidate(window); + window->Invalidate(); } } @@ -637,7 +642,7 @@ void viewport_update_sprite_follow(rct_window* window) { rct_sprite* sprite = get_sprite(window->viewport_target_sprite); - int32_t height = (tile_element_height(0xFFFF & sprite->generic.x, 0xFFFF & sprite->generic.y) & 0xFFFF) - 16; + int32_t height = (tile_element_height({ sprite->generic.x, sprite->generic.y })) - 16; int32_t underground = sprite->generic.z < height; viewport_set_underground_flag(underground, window, window->viewport); @@ -705,8 +710,8 @@ viewport_focus viewport_update_smart_guest_follow(rct_window* window, Peep* peep if (peep->state == PEEP_STATE_ON_RIDE || peep->state == PEEP_STATE_ENTERING_RIDE || (peep->state == PEEP_STATE_LEAVING_RIDE && peep->x == LOCATION_NULL)) { - Ride* ride = get_ride(peep->current_ride); - if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK) + auto ride = get_ride(peep->current_ride); + if (ride != nullptr && (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) { auto train = GET_VEHICLE(ride->vehicles[peep->current_train]); if (train != nullptr) @@ -730,7 +735,7 @@ viewport_focus viewport_update_smart_guest_follow(rct_window* window, Peep* peep focus.type = VIEWPORT_FOCUS_TYPE_COORDINATE; focus.coordinate.x = x; focus.coordinate.y = y; - focus.coordinate.z = tile_element_height(x, y) + 32; + focus.coordinate.z = tile_element_height({ x, y }) + 32; focus.sprite.type |= VIEWPORT_FOCUS_TYPE_COORDINATE; } } @@ -791,7 +796,7 @@ void viewport_update_smart_vehicle_follow(rct_window* window) * ebp: bottom */ void viewport_render( - rct_drawpixelinfo* dpi, rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom, + rct_drawpixelinfo* dpi, const rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom, std::vector* sessions) { if (right <= viewport->x) @@ -833,6 +838,43 @@ void viewport_render( #endif } +static void viewport_fill_column(paint_session* session) +{ + paint_session_generate(session); + paint_session_arrange(session); +} + +static void viewport_paint_column(paint_session* session) +{ + if (session->ViewFlags + & (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_UNDERGROUND_INSIDE + | VIEWPORT_FLAG_CLIP_VIEW) + && (~session->ViewFlags & VIEWPORT_FLAG_TRANSPARENT_BACKGROUND)) + { + uint8_t colour = COLOUR_AQUAMARINE; + if (session->ViewFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES) + { + colour = COLOUR_BLACK; + } + gfx_clear(&session->DPI, colour); + } + + paint_draw_structs(session); + + if (gConfigGeneral.render_weather_gloom && !gTrackDesignSaveMode && !(session->ViewFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES) + && !(session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES)) + { + viewport_paint_weather_gloom(&session->DPI); + } + + if (session->PSStringHead != nullptr) + { + paint_draw_money_structs(&session->DPI, session->PSStringHead); + } + + paint_session_free(session); +} + /** * * rct2: 0x00685CBF @@ -844,7 +886,7 @@ void viewport_render( * ebp: bottom */ void viewport_paint( - rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, + const rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, std::vector* sessions) { uint32_t viewFlags = viewport->flags; @@ -867,7 +909,7 @@ void viewport_paint( y >>= viewport->zoom; y += viewport->y; - rct_drawpixelinfo dpi1; + rct_drawpixelinfo dpi1 = *dpi; dpi1.bits = dpi->bits + (x - dpi->x) + ((y - dpi->y) * (dpi->width + dpi->pitch)); dpi1.x = left; dpi1.y = top; @@ -880,82 +922,65 @@ void viewport_paint( // this as well as the [x += 32] in the loop causes signed integer overflow -> undefined behaviour. int16_t rightBorder = dpi1.x + dpi1.width; - // Splits the area into 32 pixel columns and renders them - int16_t start_x = floor2(dpi1.x, 32); - if (sessions != nullptr) + std::vector columns; + + bool useMultithreading = gConfigGeneral.multithreading; + if (window_get_main() != nullptr && viewport != window_get_main()->viewport) + useMultithreading = false; + + if (useMultithreading && _paintJobs == nullptr) { - sessions->reserve((rightBorder - start_x) / 32); + _paintJobs = std::make_unique(); } - for (int16_t columnx = start_x; columnx < rightBorder; columnx += 32) + else if (useMultithreading == false && _paintJobs != nullptr) { - rct_drawpixelinfo dpi2 = dpi1; - if (columnx >= dpi2.x) + _paintJobs.reset(); + } + + // Splits the area into 32 pixel columns and renders them + size_t index = 0; + for (x = floor2(dpi1.x, 32); x < rightBorder; x += 32, index++) + { + paint_session* session = paint_session_alloc(&dpi1, viewFlags); + columns.push_back(session); + + rct_drawpixelinfo& dpi2 = session->DPI; + if (x >= dpi2.x) { - int16_t leftPitch = columnx - dpi2.x; + int16_t leftPitch = x - dpi2.x; dpi2.width -= leftPitch; dpi2.bits += leftPitch >> dpi2.zoom_level; dpi2.pitch += leftPitch >> dpi2.zoom_level; - dpi2.x = columnx; + dpi2.x = x; } int16_t paintRight = dpi2.x + dpi2.width; - if (paintRight >= columnx + 32) + if (paintRight >= x + 32) { - int16_t rightPitch = paintRight - columnx - 32; + int16_t rightPitch = paintRight - x - 32; paintRight -= rightPitch; dpi2.pitch += rightPitch >> dpi2.zoom_level; } dpi2.width = paintRight - dpi2.x; - viewport_paint_column(&dpi2, viewFlags, sessions); - } -} - -static void viewport_paint_column(rct_drawpixelinfo* dpi, uint32_t viewFlags, std::vector* sessions) -{ - if (viewFlags - & (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_CLIP_VIEW)) - { - uint8_t colour = 10; - if (viewFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES) + if (useMultithreading) { - colour = 0; + _paintJobs->AddTask([session]() -> void { viewport_fill_column(session); }); } - gfx_clear(dpi, colour); - } - - paint_session* session = paint_session_alloc(dpi, viewFlags); - paint_session_generate(session); - // Perform a deep copy of the paint session, use relative offsets. - // This is done to extract the session for benchmark. - if (sessions != nullptr) - { - sessions->push_back(*session); - paint_session* session_copy = &sessions->at(sessions->size() - 1); - - // Mind the offset needs to be calculated against the original `session`, not `session_copy` - for (auto& ps : session_copy->PaintStructs) + else { - ps.basic.next_quadrant_ps = (paint_struct*)(ps.basic.next_quadrant_ps ? int(ps.basic.next_quadrant_ps - &session->PaintStructs[0].basic) : std::size(session->PaintStructs)); - } - for (auto& quad : session_copy->Quadrants) - { - quad = (paint_struct*)(quad ? int(quad - &session->PaintStructs[0].basic) : std::size(session->Quadrants)); + viewport_fill_column(session); } } - paint_session_arrange(session); - paint_draw_structs(session); - paint_session_free(session); - if (gConfigGeneral.render_weather_gloom && !gTrackDesignSaveMode && !(viewFlags & VIEWPORT_FLAG_INVISIBLE_SPRITES) - && !(viewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES)) + if (useMultithreading) { - viewport_paint_weather_gloom(dpi); + _paintJobs->Join(); } - if (session->PSStringHead != nullptr) + for (auto&& column : columns) { - paint_draw_money_structs(dpi, session->PSStringHead); + viewport_paint_column(column); } } @@ -1066,7 +1091,7 @@ void show_gridlines() if (!(mainWindow->viewport->flags & VIEWPORT_FLAG_GRIDLINES)) { mainWindow->viewport->flags |= VIEWPORT_FLAG_GRIDLINES; - window_invalidate(mainWindow); + mainWindow->Invalidate(); } } } @@ -1088,7 +1113,7 @@ void hide_gridlines() if (!gConfigGeneral.always_show_gridlines) { mainWindow->viewport->flags &= ~VIEWPORT_FLAG_GRIDLINES; - window_invalidate(mainWindow); + mainWindow->Invalidate(); } } } @@ -1108,7 +1133,7 @@ void show_land_rights() if (!(mainWindow->viewport->flags & VIEWPORT_FLAG_LAND_OWNERSHIP)) { mainWindow->viewport->flags |= VIEWPORT_FLAG_LAND_OWNERSHIP; - window_invalidate(mainWindow); + mainWindow->Invalidate(); } } } @@ -1130,7 +1155,7 @@ void hide_land_rights() if (mainWindow->viewport->flags & VIEWPORT_FLAG_LAND_OWNERSHIP) { mainWindow->viewport->flags &= ~VIEWPORT_FLAG_LAND_OWNERSHIP; - window_invalidate(mainWindow); + mainWindow->Invalidate(); } } } @@ -1150,7 +1175,7 @@ void show_construction_rights() if (!(mainWindow->viewport->flags & VIEWPORT_FLAG_CONSTRUCTION_RIGHTS)) { mainWindow->viewport->flags |= VIEWPORT_FLAG_CONSTRUCTION_RIGHTS; - window_invalidate(mainWindow); + mainWindow->Invalidate(); } } } @@ -1172,7 +1197,7 @@ void hide_construction_rights() if (mainWindow->viewport->flags & VIEWPORT_FLAG_CONSTRUCTION_RIGHTS) { mainWindow->viewport->flags &= ~VIEWPORT_FLAG_CONSTRUCTION_RIGHTS; - window_invalidate(mainWindow); + mainWindow->Invalidate(); } } } @@ -1188,7 +1213,7 @@ void viewport_set_visibility(uint8_t mode) if (window != nullptr) { - rct_viewport* edi = window->viewport; + rct_viewport* vp = window->viewport; uint32_t invalidate = 0; switch (mode) @@ -1200,30 +1225,30 @@ void viewport_set_visibility(uint8_t mode) | VIEWPORT_FLAG_LAND_HEIGHTS | VIEWPORT_FLAG_TRACK_HEIGHTS | VIEWPORT_FLAG_PATH_HEIGHTS | VIEWPORT_FLAG_INVISIBLE_PEEPS | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_HIDE_VERTICAL; - invalidate += edi->flags & mask; - edi->flags &= ~mask; + invalidate += vp->flags & mask; + vp->flags &= ~mask; break; } case 1: // 6CB79D case 4: // 6CB7C4 // Set underground on, invalidate if it was off - invalidate += !(edi->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE); - edi->flags |= VIEWPORT_FLAG_UNDERGROUND_INSIDE; + invalidate += !(vp->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE); + vp->flags |= VIEWPORT_FLAG_UNDERGROUND_INSIDE; break; case 2: // 6CB7EB // Set track heights on, invalidate if off - invalidate += !(edi->flags & VIEWPORT_FLAG_TRACK_HEIGHTS); - edi->flags |= VIEWPORT_FLAG_TRACK_HEIGHTS; + invalidate += !(vp->flags & VIEWPORT_FLAG_TRACK_HEIGHTS); + vp->flags |= VIEWPORT_FLAG_TRACK_HEIGHTS; break; case 3: // 6CB7B1 case 5: // 6CB7D8 // Set underground off, invalidate if it was on - invalidate += edi->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE; - edi->flags &= ~((uint16_t)VIEWPORT_FLAG_UNDERGROUND_INSIDE); + invalidate += vp->flags & VIEWPORT_FLAG_UNDERGROUND_INSIDE; + vp->flags &= ~((uint16_t)VIEWPORT_FLAG_UNDERGROUND_INSIDE); break; } if (invalidate != 0) - window_invalidate(window); + window->Invalidate(); } } @@ -1246,7 +1271,7 @@ static void store_interaction_info(paint_struct* ps) else mask = 1 << (ps->sprite_type - 1); - if (!(_unk9AC154 & mask)) + if (!(_interactionFlags & mask)) { _interactionSpriteType = ps->sprite_type; _interactionMapX = ps->map_x; @@ -1258,7 +1283,7 @@ static void store_interaction_info(paint_struct* ps) /** * rct2: 0x00679236, 0x00679662, 0x00679B0D, 0x00679FF1 */ -static bool pixel_is_present_bmp(uint32_t imageType, const rct_g1_element* g1, const uint8_t* index, const uint8_t* palette) +static bool is_pixel_present_bmp(uint32_t imageType, const rct_g1_element* g1, const uint8_t* index, const uint8_t* palette) { // Probably used to check for corruption if (!(g1->flags & G1_FLAG_BMP)) @@ -1374,7 +1399,8 @@ static bool is_pixel_present_rle(const uint8_t* esi, int16_t x_start_point, int1 * @param y (dx) * @return value originally stored in 0x00141F569 */ -static bool sub_679074(rct_drawpixelinfo* dpi, int32_t imageId, int16_t x, int16_t y, const uint8_t* palette) +static bool is_sprite_interacted_with_palette_set( + rct_drawpixelinfo* dpi, int32_t imageId, int16_t x, int16_t y, const uint8_t* palette) { const rct_g1_element* g1 = gfx_get_g1_element(imageId & 0x7FFFF); if (g1 == nullptr) @@ -1402,7 +1428,7 @@ static bool sub_679074(rct_drawpixelinfo* dpi, int32_t imageId, int16_t x, int16 /* .zoom_level = */ (uint16_t)(dpi->zoom_level - 1), }; - return sub_679074(&zoomed_dpi, imageId - g1->zoomed_offset, x / 2, y / 2, palette); + return is_sprite_interacted_with_palette_set(&zoomed_dpi, imageId - g1->zoomed_offset, x / 2, y / 2, palette); } } @@ -1504,60 +1530,18 @@ static bool sub_679074(rct_drawpixelinfo* dpi, int32_t imageId, int16_t x, int16 if (!(g1->flags & G1_FLAG_1)) { - return pixel_is_present_bmp(imageType, g1, offset, palette); + return is_pixel_present_bmp(imageType, g1, offset, palette); } - // Adding assert here, possibly dead code below. Remove after some time. - assert(false); - - // The code below is untested. - int32_t total_no_pixels = g1->width * g1->height; - uint8_t* source_pointer = g1->offset; - uint8_t* new_source_pointer_start = (uint8_t*)malloc(total_no_pixels); - uint8_t* new_source_pointer = (*&new_source_pointer_start); // 0x9E3D28; - intptr_t ebx1; - int32_t ecx; - while (total_no_pixels > 0) - { - int8_t no_pixels = *source_pointer; - if (no_pixels >= 0) - { - source_pointer++; - total_no_pixels -= no_pixels; - std::memcpy((char*)new_source_pointer, (char*)source_pointer, no_pixels); - new_source_pointer += no_pixels; - source_pointer += no_pixels; - continue; - } - ecx = no_pixels; - no_pixels &= 0x7; - ecx >>= 3; // SAR - uintptr_t eax = ((int32_t)no_pixels) << 8; - ecx = -ecx; // Odd - eax = (eax & 0xFF00) + *(source_pointer + 1); - total_no_pixels -= ecx; - source_pointer += 2; - ebx1 = (uintptr_t)new_source_pointer - eax; - eax = (uintptr_t)source_pointer; - source_pointer = (uint8_t*)ebx1; - ebx1 = eax; - eax = 0; - std::memcpy((char*)new_source_pointer, (char*)source_pointer, ecx); - new_source_pointer += ecx; - source_pointer = (uint8_t*)ebx1; - } - - bool output = pixel_is_present_bmp(imageType, g1, new_source_pointer_start + (uintptr_t)offset, palette); - free(new_source_pointer_start); - - return output; + Guard::Assert(false, "Invalid image type encountered."); + return false; } /** * * rct2: 0x00679023 */ -static bool sub_679023(rct_drawpixelinfo* dpi, int32_t imageId, int32_t x, int32_t y) +static bool is_sprite_interacted_with(rct_drawpixelinfo* dpi, int32_t imageId, int32_t x, int32_t y) { const uint8_t* palette = nullptr; imageId &= ~IMAGE_TYPE_TRANSPARENT; @@ -1580,17 +1564,17 @@ static bool sub_679023(rct_drawpixelinfo* dpi, int32_t imageId, int32_t x, int32 { _currentImageType = 0; } - return sub_679074(dpi, imageId, x, y, palette); + return is_sprite_interacted_with_palette_set(dpi, imageId, x, y, palette); } /** * * rct2: 0x0068862C */ -static void sub_68862C(paint_session* session) +static void set_interaction_info_from_paint_session(paint_session* session) { paint_struct* ps = &session->PaintHead; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; while ((ps = ps->next_quadrant_ps) != nullptr) { @@ -1599,7 +1583,7 @@ static void sub_68862C(paint_session* session) while (next_ps != nullptr) { ps = next_ps; - if (sub_679023(dpi, ps->image_id, ps->x, ps->y)) + if (is_sprite_interacted_with(dpi, ps->image_id, ps->x, ps->y)) { store_interaction_info(ps); } @@ -1608,7 +1592,8 @@ static void sub_68862C(paint_session* session) for (attached_paint_struct* attached_ps = ps->attached_ps; attached_ps != nullptr; attached_ps = attached_ps->next) { - if (sub_679023(dpi, attached_ps->image_id, (attached_ps->x + ps->x) & 0xFFFF, (attached_ps->y + ps->y) & 0xFFFF)) + if (is_sprite_interacted_with( + dpi, attached_ps->image_id, (attached_ps->x + ps->x) & 0xFFFF, (attached_ps->y + ps->y) & 0xFFFF)) { store_interaction_info(ps); } @@ -1634,7 +1619,7 @@ void get_map_coordinates_from_pos( int32_t screenX, int32_t screenY, int32_t flags, int16_t* x, int16_t* y, int32_t* interactionType, TileElement** tileElement, rct_viewport** viewport) { - rct_window* window = window_find_from_point(screenX, screenY); + rct_window* window = window_find_from_point(ScreenCoordsXY(screenX, screenY)); get_map_coordinates_from_pos_window(window, screenX, screenY, flags, x, y, interactionType, tileElement, viewport); } @@ -1642,7 +1627,7 @@ void get_map_coordinates_from_pos_window( rct_window* window, int32_t screenX, int32_t screenY, int32_t flags, int16_t* x, int16_t* y, int32_t* interactionType, TileElement** tileElement, rct_viewport** viewport) { - _unk9AC154 = flags & 0xFFFF; + _interactionFlags = flags & 0xFFFF; _interactionSpriteType = 0; if (window != nullptr && window->viewport != nullptr) { @@ -1670,7 +1655,7 @@ void get_map_coordinates_from_pos_window( paint_session* session = paint_session_alloc(dpi, myviewport->flags); paint_session_generate(session); paint_session_arrange(session); - sub_68862C(session); + set_interaction_info_from_paint_session(session); paint_session_free(session); } if (viewport != nullptr) @@ -1739,7 +1724,7 @@ void viewport_invalidate(rct_viewport* viewport, int32_t left, int32_t top, int3 static rct_viewport* viewport_find_from_point(int32_t screenX, int32_t screenY) { - rct_window* w = window_find_from_point(screenX, screenY); + rct_window* w = window_find_from_point(ScreenCoordsXY(screenX, screenY)); if (w == nullptr) return nullptr; @@ -1785,7 +1770,7 @@ void screen_get_map_xy(int32_t screenX, int32_t screenY, int16_t* x, int16_t* y, for (int32_t i = 0; i < 5; i++) { - int32_t z = tile_element_height(map_pos.x, map_pos.y); + int32_t z = tile_element_height({ map_pos.x, map_pos.y }); map_pos = viewport_coord_to_map_coord(start_vp_pos.x, start_vp_pos.y, z); map_pos.x = std::clamp(map_pos.x, my_x, my_x + 31); map_pos.y = std::clamp(map_pos.y, my_y, my_y + 31); diff --git a/src/openrct2/interface/Viewport.h b/src/openrct2/interface/Viewport.h index a348556d8e..be69c929ac 100644 --- a/src/openrct2/interface/Viewport.h +++ b/src/openrct2/interface/Viewport.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -46,6 +46,7 @@ enum VIEWPORT_FLAG_SEETHROUGH_PATHS = (1 << 16), VIEWPORT_FLAG_CLIP_VIEW = (1 << 17), VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES = (1 << 18), + VIEWPORT_FLAG_TRANSPARENT_BACKGROUND = (1 << 19), }; enum @@ -130,10 +131,10 @@ viewport_focus viewport_update_smart_guest_follow(rct_window* window, Peep* peep void viewport_update_smart_staff_follow(rct_window* window, Peep* peep); void viewport_update_smart_vehicle_follow(rct_window* window); void viewport_render( - rct_drawpixelinfo* dpi, rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom, + rct_drawpixelinfo* dpi, const rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom, std::vector* sessions = nullptr); void viewport_paint( - rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, + const rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, std::vector* sessions = nullptr); void viewport_adjust_for_map_height(int16_t* x, int16_t* y, int16_t* z); @@ -164,7 +165,7 @@ int32_t viewport_interaction_get_item_right(int32_t x, int32_t y, viewport_inter int32_t viewport_interaction_right_over(int32_t x, int32_t y); int32_t viewport_interaction_right_click(int32_t x, int32_t y); -void sub_68A15E(int32_t screenX, int32_t screenY, int16_t* x, int16_t* y, int32_t* direction, TileElement** tileElement); +void sub_68A15E(int32_t screenX, int32_t screenY, int16_t* x, int16_t* y); void sub_68B2B7(paint_session* session, int32_t x, int32_t y); void viewport_invalidate(rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom); diff --git a/src/openrct2/interface/Widget.h b/src/openrct2/interface/Widget.h index bd3140e685..e4b04956a7 100644 --- a/src/openrct2/interface/Widget.h +++ b/src/openrct2/interface/Widget.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/interface/Window.cpp b/src/openrct2/interface/Window.cpp index f594809355..6cdfb84dc8 100644 --- a/src/openrct2/interface/Window.cpp +++ b/src/openrct2/interface/Window.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -34,9 +34,11 @@ #include #include +#include #include +#include -std::vector> g_window_list; +std::list> g_window_list; rct_window* gWindowAudioExclusive; uint16_t TextInputDescriptionArgs[4]; @@ -49,7 +51,6 @@ TextInputSession* gTextInput; uint16_t gWindowUpdateTicks; uint16_t gWindowMapFlashingFlags; - colour_t gCurrentWindowColours[4]; // converted from uint16_t values at 0x009A41EC - 0x009A4230 @@ -76,20 +77,31 @@ static constexpr const float window_scroll_locations[][2] = { }; // clang-format on +namespace WindowCloseFlags +{ + static constexpr uint32_t None = 0; + static constexpr uint32_t IterateReverse = (1 << 0); + static constexpr uint32_t CloseSingle = (1 << 1); +} // namespace WindowCloseFlags + static int32_t window_draw_split( rct_drawpixelinfo* dpi, rct_window* w, int32_t left, int32_t top, int32_t right, int32_t bottom); static void window_draw_single(rct_drawpixelinfo* dpi, rct_window* w, int32_t left, int32_t top, int32_t right, int32_t bottom); -size_t window_get_index(const rct_window* w) +std::list>::iterator window_get_iterator(const rct_window* w) { - for (size_t i = 0; i < g_window_list.size(); i++) + return std::find_if(g_window_list.begin(), g_window_list.end(), [w](const std::shared_ptr& w2) -> bool { + return w == w2.get(); + }); +} + +void window_visit_each(std::function func) +{ + auto windowList = g_window_list; + for (auto& w : windowList) { - if (g_window_list[i].get() == w) - { - return i; - } + func(w.get()); } - return std::numeric_limits::max(); } /** @@ -99,27 +111,17 @@ size_t window_get_index(const rct_window* w) void window_dispatch_update_all() { // gTooltipNotShownTicks++; - - // The window list can change during update calls, so use index based iteration - for (auto i = g_window_list.size(); i > 0; i--) - { - if (i - 1 < g_window_list.size()) - { - auto& w = g_window_list[i - 1]; - window_event_update_call(w.get()); - } - } + window_visit_each([&](rct_window* w) { window_event_update_call(w); }); } void window_update_all_viewports() { - for (auto& w : g_window_list) - { - if (w->viewport != nullptr && window_is_visible(w.get())) + window_visit_each([&](rct_window* w) { + if (w->viewport != nullptr && window_is_visible(w)) { - viewport_update_position(w.get()); + viewport_update_position(w); } - } + }); } /** @@ -137,26 +139,21 @@ void window_update_all() if (gWindowUpdateTicks >= 1000) { gWindowUpdateTicks = 0; - for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) - { - auto w = it->get(); - window_event_periodic_update_call(w); - } + + window_visit_each([](rct_window* w) { window_event_periodic_update_call(w); }); } // Border flash invalidation - for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) - { - auto w = it->get(); + window_visit_each([](rct_window* w) { if (w->flags & WF_WHITE_BORDER_MASK) { w->flags -= WF_WHITE_BORDER_ONE; if (!(w->flags & WF_WHITE_BORDER_MASK)) { - window_invalidate(w); + w->Invalidate(); } } - } + }); auto windowManager = OpenRCT2::GetContext()->GetUiContext()->GetWindowManager(); windowManager->UpdateMouseWheel(); @@ -212,21 +209,16 @@ void window_set_window_limit(int32_t value) * * @param window The window to close (esi). */ -void window_close(rct_window* window) +void window_close(rct_window* w) { - if (window == nullptr) + auto itWindow = window_get_iterator(w); + if (itWindow == g_window_list.end()) return; - // Make a copy of the window class and number in case - // the window order is changed by the close event. - rct_windowclass cls = window->classification; - rct_windownumber number = window->number; + // Explicit copy of the shared ptr to keep the memory valid. + std::shared_ptr window = *itWindow; - window_event_close_call(window); - - window = window_find_by_number(cls, number); - if (window == nullptr) - return; + window_event_close_call(window.get()); // Remove viewport if (window->viewport != nullptr) @@ -236,13 +228,63 @@ void window_close(rct_window* window) } // Invalidate the window (area) - window_invalidate(window); + window->Invalidate(); - auto index = window_get_index(window); - if (index != std::numeric_limits::max()) + // The window list may have been modified in the close event + itWindow = window_get_iterator(w); + if (itWindow != g_window_list.end()) + g_window_list.erase(itWindow); +} + +template static void window_close_by_condition(_TPred pred, uint32_t flags = WindowCloseFlags::None) +{ + bool listUpdated; + do { - g_window_list.erase(g_window_list.begin() + index); - } + listUpdated = false; + + auto closeSingle = [&](std::shared_ptr window) -> bool { + if (!pred(window.get())) + { + return false; + } + + // Keep track of current amount, if a new window is created upon closing + // we need to break this current iteration and restart. + size_t previousCount = g_window_list.size(); + + window_close(window.get()); + + if ((flags & WindowCloseFlags::CloseSingle) != 0) + { + // Only close a single one. + return true; + } + + if (previousCount >= g_window_list.size()) + { + // A new window was created during the close event. + return true; + } + + // Keep closing windows. + return false; + }; + + // The closest to something like for_each_if is using find_if in order to avoid duplicate code + // to change the loop direction. + auto windowList = g_window_list; + if ((flags & WindowCloseFlags::IterateReverse) != 0) + listUpdated = std::find_if(windowList.rbegin(), windowList.rend(), closeSingle) != windowList.rend(); + else + listUpdated = std::find_if(windowList.begin(), windowList.end(), closeSingle) != windowList.end(); + + // If requested to close only a single window and a new window was created during close + // we ignore it. + if ((flags & WindowCloseFlags::CloseSingle) != 0) + break; + + } while (listUpdated); } /** @@ -252,15 +294,7 @@ void window_close(rct_window* window) */ void window_close_by_class(rct_windowclass cls) { - for (size_t i = 0; i < g_window_list.size(); i++) - { - auto& w = *g_window_list[i]; - if (w.classification == cls) - { - window_close(&w); - i--; - } - } + window_close_by_condition([&](rct_window* w) -> bool { return w->classification == cls; }); } /** @@ -271,15 +305,7 @@ void window_close_by_class(rct_windowclass cls) */ void window_close_by_number(rct_windowclass cls, rct_windownumber number) { - for (size_t i = 0; i < g_window_list.size(); i++) - { - auto& w = *g_window_list[i]; - if (w.classification == cls && w.number == number) - { - window_close(&w); - i--; - } - } + window_close_by_condition([cls, number](rct_window* w) -> bool { return w->classification == cls && w->number == number; }); } /** @@ -329,18 +355,13 @@ void window_close_top() window_close_by_class(WC_DROPDOWN); if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) + { if (gS6Info.editor_step != EDITOR_STEP_LANDSCAPE_EDITOR) return; - - for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) - { - auto& w = **it; - if (!(w.flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) - { - window_close(&w); - break; - } } + + auto pred = [](rct_window* w) -> bool { return !(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)); }; + window_close_by_condition(pred, WindowCloseFlags::CloseSingle | WindowCloseFlags::IterateReverse); } /** @@ -351,27 +372,16 @@ void window_close_top() void window_close_all() { window_close_by_class(WC_DROPDOWN); - for (size_t i = g_window_list.size(); i > 0; i--) - { - auto& w = *g_window_list[i - 1]; - if (!(w.flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) - { - window_close(&w); - } - } + window_close_by_condition([](rct_window* w) -> bool { return !(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)); }); } void window_close_all_except_class(rct_windowclass cls) { window_close_by_class(WC_DROPDOWN); - for (size_t i = g_window_list.size(); i > 0; i--) - { - auto& w = *g_window_list[i - 1]; - if (w.classification != cls && !(w.flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) - { - window_close(&w); - } - } + + window_close_by_condition([cls](rct_window* w) -> bool { + return w->classification != cls && !(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)); + }); } /** @@ -379,36 +389,30 @@ void window_close_all_except_class(rct_windowclass cls) */ void window_close_all_except_flags(uint16_t flags) { - for (size_t i = g_window_list.size(); i > 0; i--) - { - auto& w = *g_window_list[i - 1]; - if (!(w.flags & flags)) - { - window_close(&w); - } - } + window_close_by_condition([flags](rct_window* w) -> bool { return !(w->flags & flags); }); } /** * * rct2: 0x006EA845 */ -rct_window* window_find_from_point(int32_t x, int32_t y) +rct_window* window_find_from_point(ScreenCoordsXY screenCoords) { for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) { - auto& w = **it; - if (x < w.x || x >= w.x + w.width || y < w.y || y >= w.y + w.height) + auto& w = *it; + if (screenCoords.x < w->x || screenCoords.x >= w->x + w->width || screenCoords.y < w->y + || screenCoords.y >= w->y + w->height) continue; - if (w.flags & WF_NO_BACKGROUND) + if (w->flags & WF_NO_BACKGROUND) { - auto widgetIndex = window_find_widget_from_point(&w, x, y); + auto widgetIndex = window_find_widget_from_point(w.get(), screenCoords); if (widgetIndex == -1) continue; } - return &w; + return w.get(); } return nullptr; @@ -422,7 +426,7 @@ rct_window* window_find_from_point(int32_t x, int32_t y) * returns widget_index (edx) * EDI NEEDS TO BE SET TO w->widgets[widget_index] AFTER */ -rct_widgetindex window_find_widget_from_point(rct_window* w, int32_t x, int32_t y) +rct_widgetindex window_find_widget_from_point(rct_window* w, ScreenCoordsXY screenCoords) { // Invalidate the window window_event_invalidate_call(w); @@ -438,7 +442,8 @@ rct_widgetindex window_find_widget_from_point(rct_window* w, int32_t x, int32_t } else if (widget->type != WWT_EMPTY) { - if (x >= w->x + widget->left && x <= w->x + widget->right && y >= w->y + widget->top && y <= w->y + widget->bottom) + if (screenCoords.x >= w->x + widget->left && screenCoords.x <= w->x + widget->right + && screenCoords.y >= w->y + widget->top && screenCoords.y <= w->y + widget->bottom) { widget_index = i; } @@ -460,10 +465,14 @@ rct_widgetindex window_find_widget_from_point(rct_window* w, int32_t x, int32_t * * @param window The window to invalidate (esi). */ -void window_invalidate(rct_window* window) +template static void window_invalidate_by_condition(_TPred pred) { - if (window != nullptr) - gfx_set_dirty_blocks(window->x, window->y, window->x + window->width, window->y + window->height); + window_visit_each([pred](rct_window* w) { + if (pred(w)) + { + w->Invalidate(); + } + }); } /** @@ -473,13 +482,7 @@ void window_invalidate(rct_window* window) */ void window_invalidate_by_class(rct_windowclass cls) { - for (auto& w : g_window_list) - { - if (w->classification == cls) - { - window_invalidate(w.get()); - } - } + window_invalidate_by_condition([cls](rct_window* w) -> bool { return w->classification == cls; }); } /** @@ -488,13 +491,8 @@ void window_invalidate_by_class(rct_windowclass cls) */ void window_invalidate_by_number(rct_windowclass cls, rct_windownumber number) { - for (auto& w : g_window_list) - { - if (w->classification == cls && w->number == number) - { - window_invalidate(w.get()); - } - } + window_invalidate_by_condition( + [cls, number](rct_window* w) -> bool { return w->classification == cls && w->number == number; }); } /** @@ -502,10 +500,7 @@ void window_invalidate_by_number(rct_windowclass cls, rct_windownumber number) */ void window_invalidate_all() { - for (auto& w : g_window_list) - { - window_invalidate(w.get()); - } + window_visit_each([](rct_window* w) { w->Invalidate(); }); } /** @@ -531,18 +526,27 @@ void widget_invalidate(rct_window* w, rct_widgetindex widgetIndex) gfx_set_dirty_blocks(w->x + widget->left, w->y + widget->top, w->x + widget->right + 1, w->y + widget->bottom + 1); } +template static void widget_invalidate_by_condition(_TPred pred) +{ + window_visit_each([pred](rct_window* w) { + if (pred(w)) + { + w->Invalidate(); + } + }); +} + /** * Invalidates the specified widget of all windows that match the specified window class. */ void widget_invalidate_by_class(rct_windowclass cls, rct_widgetindex widgetIndex) { - for (auto& w : g_window_list) - { + window_visit_each([cls, widgetIndex](rct_window* w) { if (w->classification == cls) { - widget_invalidate(w.get(), widgetIndex); + widget_invalidate(w, widgetIndex); } - } + }); } /** @@ -551,13 +555,12 @@ void widget_invalidate_by_class(rct_windowclass cls, rct_widgetindex widgetIndex */ void widget_invalidate_by_number(rct_windowclass cls, rct_windownumber number, rct_widgetindex widgetIndex) { - for (auto& w : g_window_list) - { + window_visit_each([cls, number, widgetIndex](rct_window* w) { if (w->classification == cls && w->number == number) { - widget_invalidate(w.get(), widgetIndex); + widget_invalidate(w, widgetIndex); } - } + }); } /** @@ -612,7 +615,7 @@ void window_update_scroll_widgets(rct_window* w) if (scrollPositionChanged) { widget_scroll_update_thumbs(w, widgetIndex); - window_invalidate(w); + w->Invalidate(); } scrollIndex++; } @@ -640,26 +643,23 @@ rct_window* window_bring_to_front(rct_window* w) { if (!(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) { - size_t srcIndex = window_get_index(w); - if (srcIndex != std::numeric_limits::max()) + auto itSourcePos = window_get_iterator(w); + if (itSourcePos != g_window_list.end()) { - auto wptr = std::move(g_window_list[srcIndex]); - g_window_list.erase(g_window_list.begin() + srcIndex); - // Insert in front of the first non-stick-to-front window - size_t dstIndex = 0; - for (size_t i = g_window_list.size(); i > 0; i--) + auto itDestPos = g_window_list.begin(); + for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) { - auto& w2 = *g_window_list[i - 1]; - if (!(w2.flags & WF_STICK_TO_FRONT)) + auto& w2 = *it; + if (!(w2->flags & WF_STICK_TO_FRONT)) { - dstIndex = i; + itDestPos = it.base(); break; } } - g_window_list.insert(g_window_list.begin() + dstIndex, std::move(wptr)); - window_invalidate(w); + g_window_list.splice(itDestPos, g_window_list, itSourcePos); + w->Invalidate(); if (w->x + w->width < 20) { @@ -667,7 +667,7 @@ rct_window* window_bring_to_front(rct_window* w) w->x += i; if (w->viewport != nullptr) w->viewport->x += i; - window_invalidate(w); + w->Invalidate(); } } } @@ -682,7 +682,7 @@ rct_window* window_bring_to_front_by_class_with_flags(rct_windowclass cls, uint1 if (w != nullptr) { w->flags |= flags; - window_invalidate(w); + w->Invalidate(); w = window_bring_to_front(w); } @@ -708,7 +708,7 @@ rct_window* window_bring_to_front_by_number(rct_windowclass cls, rct_windownumbe if (w != nullptr) { w->flags |= WF_WHITE_BORDER_MASK; - window_invalidate(w); + w->Invalidate(); w = window_bring_to_front(w); } @@ -721,30 +721,29 @@ rct_window* window_bring_to_front_by_number(rct_windowclass cls, rct_windownumbe */ void window_push_others_right(rct_window* window) { - for (auto& w : g_window_list) - { - if (w.get() == window) - continue; + window_visit_each([window](rct_window* w) { + if (w == window) + return; if (w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)) - continue; + return; if (w->x >= window->x + window->width) - continue; + return; if (w->x + w->width <= window->x) - continue; + return; if (w->y >= window->y + window->height) - continue; + return; if (w->y + w->height <= window->y) - continue; + return; - window_invalidate(w.get()); + w->Invalidate(); if (window->x + window->width + 13 >= context_get_width()) - continue; + return; uint16_t push_amount = window->x + window->width - w->x + 3; w->x += push_amount; - window_invalidate(w.get()); + w->Invalidate(); if (w->viewport != nullptr) w->viewport->x += push_amount; - } + }); } /** @@ -753,41 +752,36 @@ void window_push_others_right(rct_window* window) */ void window_push_others_below(rct_window* w1) { - int32_t push_amount; - // Enumerate through all other windows - for (auto& w2 : g_window_list) - { - if (w1 == w2.get()) - continue; - + window_visit_each([w1](rct_window* w2) { + if (w1 == w2) + return; // ? if (w2->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)) - continue; - + return; // Check if w2 intersects with w1 if (w2->x > (w1->x + w1->width) || w2->x + w2->width < w1->x) - continue; + return; if (w2->y > (w1->y + w1->height) || w2->y + w2->height < w1->y) - continue; + return; // Check if there is room to push it down if (w1->y + w1->height + 80 >= context_get_height()) - continue; + return; // Invalidate the window's current area - window_invalidate(w2.get()); + w2->Invalidate(); - push_amount = w1->y + w1->height - w2->y + 3; + int32_t push_amount = w1->y + w1->height - w2->y + 3; w2->y += push_amount; // Invalidate the window's new area - window_invalidate(w2.get()); + w2->Invalidate(); // Update viewport position if necessary if (w2->viewport != nullptr) w2->viewport->y += push_amount; - } + }); } /** @@ -806,44 +800,6 @@ rct_window* window_get_main() return nullptr; } -/** - * Based on - * rct2: 0x696ee9, 0x66842F, 0x006AF3B3 - */ -void window_scroll_to_viewport(rct_window* w) -{ - int32_t x, y, z; - rct_window* mainWindow; - assert(w != nullptr); - // In original checked to make sure x and y were not -1 as well. - if (w->viewport == nullptr || w->viewport_focus_coordinates.y == -1) - return; - - if (w->viewport_focus_sprite.type & VIEWPORT_FOCUS_TYPE_SPRITE) - { - rct_sprite* sprite = get_sprite(w->viewport_focus_sprite.sprite_id); - x = sprite->generic.x; - y = sprite->generic.y; - z = sprite->generic.z; - } - else - { - x = w->viewport_focus_coordinates.x; - y = w->viewport_focus_coordinates.y & VIEWPORT_FOCUS_Y_MASK; - z = w->viewport_focus_coordinates.z; - } - - mainWindow = window_get_main(); - if (mainWindow != nullptr) - window_scroll_to_location(mainWindow, x, y, z); -} - -void window_set_location(rct_window* w, int32_t x, int32_t y, int32_t z) -{ - window_scroll_to_location(w, x, y, z); - w->flags &= ~WF_SCROLLING_TO_LOCATION; -} - /** * * rct2: 0x006E7C9C @@ -854,7 +810,7 @@ void window_set_location(rct_window* w, int32_t x, int32_t y, int32_t z) */ void window_scroll_to_location(rct_window* w, int32_t x, int32_t y, int32_t z) { - LocationXYZ16 location_3d = { (int16_t)x, (int16_t)y, (int16_t)z }; + CoordsXYZ location_3d = { x, y, z }; assert(w != nullptr); @@ -862,13 +818,13 @@ void window_scroll_to_location(rct_window* w, int32_t x, int32_t y, int32_t z) if (w->viewport) { - int16_t height = tile_element_height(x, y); + int16_t height = tile_element_height({ x, y }); if (z < height - 16) { if (!(w->viewport->flags & 1 << 0)) { w->viewport->flags |= 1 << 0; - window_invalidate(w); + w->Invalidate(); } } else @@ -876,11 +832,11 @@ void window_scroll_to_location(rct_window* w, int32_t x, int32_t y, int32_t z) if (w->viewport->flags & 1 << 0) { w->viewport->flags &= ~(1 << 0); - window_invalidate(w); + w->Invalidate(); } } - LocationXY16 map_coordinate = coordinate_3d_to_2d(&location_3d, get_current_rotation()); + auto screenCoords = translate_3d_to_2d_with_z(get_current_rotation(), location_3d); int32_t i = 0; if (!(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO)) @@ -890,15 +846,11 @@ void window_scroll_to_location(rct_window* w, int32_t x, int32_t y, int32_t z) { int16_t x2 = w->viewport->x + (int16_t)(w->viewport->width * window_scroll_locations[i][0]); int16_t y2 = w->viewport->y + (int16_t)(w->viewport->height * window_scroll_locations[i][1]); - for (auto w2i = window_get_index(w); w2i <= g_window_list.size(); w2i++) - { - if (w2i == g_window_list.size()) - { - found = true; - break; - } - auto& w2 = g_window_list[w2i]; + auto it = window_get_iterator(w); + for (; it != g_window_list.end(); it++) + { + auto w2 = (*it).get(); int16_t x1 = w2->x - 10; int16_t y1 = w2->y - 10; if (x2 >= x1 && x2 <= w2->width + x1 + 20) @@ -912,6 +864,10 @@ void window_scroll_to_location(rct_window* w, int32_t x, int32_t y, int32_t z) } } } + if (it == g_window_list.end()) + { + found = true; + } if (i >= (int32_t)std::size(window_scroll_locations)) { i = 0; @@ -924,8 +880,8 @@ void window_scroll_to_location(rct_window* w, int32_t x, int32_t y, int32_t z) { if (!(w->flags & WF_NO_SCROLLING)) { - w->saved_view_x = map_coordinate.x - (int16_t)(w->viewport->view_width * window_scroll_locations[i][0]); - w->saved_view_y = map_coordinate.y - (int16_t)(w->viewport->view_height * window_scroll_locations[i][1]); + w->saved_view_x = screenCoords.x - (int16_t)(w->viewport->view_width * window_scroll_locations[i][0]); + w->saved_view_y = screenCoords.y - (int16_t)(w->viewport->view_height * window_scroll_locations[i][1]); w->flags |= WF_SCROLLING_TO_LOCATION; } } @@ -938,11 +894,7 @@ void window_scroll_to_location(rct_window* w, int32_t x, int32_t y, int32_t z) */ static void call_event_viewport_rotate_on_all_windows() { - for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) - { - auto w = it->get(); - window_event_viewport_rotate_call(w); - } + window_visit_each([](rct_window* w) { window_event_viewport_rotate_call(w); }); } /** @@ -977,7 +929,7 @@ void window_rotate_camera(rct_window* w, int32_t direction) } else { - z = tile_element_height(x, y); + z = tile_element_height({ x, y }); } gCurrentRotation = (get_current_rotation() + direction) & 3; @@ -990,7 +942,7 @@ void window_rotate_camera(rct_window* w, int32_t direction) viewport->view_x = new_x; viewport->view_y = new_y; - window_invalidate(w); + w->Invalidate(); call_event_viewport_rotate_on_all_windows(); reset_all_sprite_quadrant_placements(); @@ -1007,7 +959,7 @@ void window_viewport_get_map_coords_by_cursor( get_map_coordinates_from_pos(mouse_x, mouse_y, VIEWPORT_INTERACTION_MASK_NONE, map_x, map_y, nullptr, nullptr, nullptr); // Get viewport coordinates centring around the tile. - int32_t base_height = tile_element_height(*map_x, *map_y); + int32_t base_height = tile_element_height({ *map_x, *map_y }); int32_t dest_x, dest_y; centre_2d_coordinates(*map_x, *map_y, base_height, &dest_x, &dest_y, w->viewport); @@ -1024,7 +976,7 @@ void window_viewport_centre_tile_around_cursor(rct_window* w, int16_t map_x, int { // Get viewport coordinates centring around the tile. int32_t dest_x, dest_y; - int32_t base_height = tile_element_height(map_x, map_y); + int32_t base_height = tile_element_height({ map_x, map_y }); centre_2d_coordinates(map_x, map_y, base_height, &dest_x, &dest_y, w->viewport); // Get mouse position to offset against. @@ -1087,7 +1039,7 @@ void window_zoom_set(rct_window* w, int32_t zoomLevel, bool atCursor) // HACK: Prevents the redraw from failing when there is // a window on top of the viewport. window_bring_to_front(w); - window_invalidate(w); + w->Invalidate(); } /** @@ -1151,10 +1103,10 @@ void window_draw(rct_drawpixelinfo* dpi, rct_window* w, int32_t left, int32_t to return; // Draw the window in this region - for (size_t i = window_get_index(w); i < g_window_list.size(); i++) + for (auto it = window_get_iterator(w); it != g_window_list.end(); it++) { // Don't draw overlapping opaque windows, they won't have changed - auto v = g_window_list[i].get(); + auto v = (*it).get(); if ((w == v || (v->flags & WF_TRANSPARENT)) && window_is_visible(v)) { window_draw_single(dpi, v, left, top, right, bottom); @@ -1170,10 +1122,11 @@ static int32_t window_draw_split( rct_drawpixelinfo* dpi, rct_window* w, int32_t left, int32_t top, int32_t right, int32_t bottom) { // Divide the draws up for only the visible regions of the window recursively - for (auto i = window_get_index(w) + 1; i < g_window_list.size(); i++) + auto itPos = window_get_iterator(w); + for (auto it = std::next(itPos); it != g_window_list.end(); it++) { // Check if this window overlaps w - auto topwindow = g_window_list[i].get(); + auto topwindow = it->get(); if (topwindow->x >= right || topwindow->y >= bottom) continue; if (topwindow->x + topwindow->width <= left || topwindow->y + topwindow->height <= top) @@ -1288,30 +1241,30 @@ void window_draw_viewport(rct_drawpixelinfo* dpi, rct_window* w) viewport_render(dpi, w->viewport, dpi->x, dpi->y, dpi->x + dpi->width, dpi->y + dpi->height); } -void window_set_position(rct_window* w, int32_t x, int32_t y) +void window_set_position(rct_window* w, ScreenCoordsXY screenCoords) { - window_move_position(w, x - w->x, y - w->y); + window_move_position(w, ScreenCoordsXY(screenCoords.x - w->x, screenCoords.y - w->y)); } -void window_move_position(rct_window* w, int32_t dx, int32_t dy) +void window_move_position(rct_window* w, ScreenCoordsXY deltaCoords) { - if (dx == 0 && dy == 0) + if (deltaCoords.x == 0 && deltaCoords.y == 0) return; // Invalidate old region - window_invalidate(w); + w->Invalidate(); // Translate window and viewport - w->x += dx; - w->y += dy; + w->x += deltaCoords.x; + w->y += deltaCoords.y; if (w->viewport != nullptr) { - w->viewport->x += dx; - w->viewport->y += dy; + w->viewport->x += deltaCoords.x; + w->viewport->y += deltaCoords.y; } // Invalidate new region - window_invalidate(w); + w->Invalidate(); } void window_resize(rct_window* w, int32_t dw, int32_t dh) @@ -1321,7 +1274,7 @@ void window_resize(rct_window* w, int32_t dw, int32_t dh) return; // Invalidate old region - window_invalidate(w); + w->Invalidate(); // Clamp new size to minimum and maximum w->width = std::clamp(w->width + dw, w->min_width, w->max_width); @@ -1339,7 +1292,7 @@ void window_resize(rct_window* w, int32_t dw, int32_t dh) window_update_scroll_widgets(w); // Invalidate new region - window_invalidate(w); + w->Invalidate(); } void window_set_resize(rct_window* w, int32_t minWidth, int32_t minHeight, int32_t maxWidth, int32_t maxHeight) @@ -1356,10 +1309,10 @@ void window_set_resize(rct_window* w, int32_t minWidth, int32_t minHeight, int32 // Resize window if size has changed if (w->width != width || w->height != height) { - window_invalidate(w); + w->Invalidate(); w->width = width; w->height = height; - window_invalidate(w); + w->Invalidate(); } } @@ -1480,28 +1433,28 @@ void window_event_unknown_08_call(rct_window* w) w->event_handlers->unknown_08(w); } -void window_event_tool_update_call(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) +void window_event_tool_update_call(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords) { if (w->event_handlers->tool_update != nullptr) - w->event_handlers->tool_update(w, widgetIndex, x, y); + w->event_handlers->tool_update(w, widgetIndex, screenCoords.x, screenCoords.y); } -void window_event_tool_down_call(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) +void window_event_tool_down_call(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords) { if (w->event_handlers->tool_down != nullptr) - w->event_handlers->tool_down(w, widgetIndex, x, y); + w->event_handlers->tool_down(w, widgetIndex, screenCoords.x, screenCoords.y); } -void window_event_tool_drag_call(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) +void window_event_tool_drag_call(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords) { if (w->event_handlers->tool_drag != nullptr) - w->event_handlers->tool_drag(w, widgetIndex, x, y); + w->event_handlers->tool_drag(w, widgetIndex, screenCoords.x, screenCoords.y); } -void window_event_tool_up_call(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) +void window_event_tool_up_call(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords) { if (w->event_handlers->tool_up != nullptr) - w->event_handlers->tool_up(w, widgetIndex, x, y); + w->event_handlers->tool_up(w, widgetIndex, screenCoords.x, screenCoords.y); } void window_event_tool_abort_call(rct_window* w, rct_widgetindex widgetIndex) @@ -1524,22 +1477,22 @@ void window_get_scroll_size(rct_window* w, int32_t scrollIndex, int32_t* width, } } -void window_event_scroll_mousedown_call(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) +void window_event_scroll_mousedown_call(rct_window* w, int32_t scrollIndex, ScreenCoordsXY screenCoords) { if (w->event_handlers->scroll_mousedown != nullptr) - w->event_handlers->scroll_mousedown(w, scrollIndex, x, y); + w->event_handlers->scroll_mousedown(w, scrollIndex, screenCoords.x, screenCoords.y); } -void window_event_scroll_mousedrag_call(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) +void window_event_scroll_mousedrag_call(rct_window* w, int32_t scrollIndex, ScreenCoordsXY screenCoords) { if (w->event_handlers->scroll_mousedrag != nullptr) - w->event_handlers->scroll_mousedrag(w, scrollIndex, x, y); + w->event_handlers->scroll_mousedrag(w, scrollIndex, screenCoords.x, screenCoords.y); } -void window_event_scroll_mouseover_call(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) +void window_event_scroll_mouseover_call(rct_window* w, int32_t scrollIndex, ScreenCoordsXY screenCoords) { if (w->event_handlers->scroll_mouseover != nullptr) - w->event_handlers->scroll_mouseover(w, scrollIndex, x, y); + w->event_handlers->scroll_mouseover(w, scrollIndex, screenCoords.x, screenCoords.y); } void window_event_textinput_call(rct_window* w, rct_widgetindex widgetIndex, char* text) @@ -1568,18 +1521,18 @@ rct_string_id window_event_tooltip_call(rct_window* w, rct_widgetindex widgetInd return result; } -int32_t window_event_cursor_call(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y) +int32_t window_event_cursor_call(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords) { int32_t cursorId = CURSOR_ARROW; if (w->event_handlers->cursor != nullptr) - w->event_handlers->cursor(w, widgetIndex, x, y, &cursorId); + w->event_handlers->cursor(w, widgetIndex, screenCoords.x, screenCoords.y, &cursorId); return cursorId; } -void window_event_moved_call(rct_window* w, int32_t x, int32_t y) +void window_event_moved_call(rct_window* w, ScreenCoordsXY screenCoords) { if (w->event_handlers->moved != nullptr) - w->event_handlers->moved(w, x, y); + w->event_handlers->moved(w, screenCoords.x, screenCoords.y); } void window_event_invalidate_call(rct_window* w) @@ -1621,8 +1574,7 @@ void window_bubble_list_item(rct_window* w, int32_t item_position) void window_relocate_windows(int32_t width, int32_t height) { int32_t new_location = 8; - for (auto& w : g_window_list) - { + window_visit_each([width, height, &new_location](rct_window* w) { // Work out if the window requires moving if (w->x + 10 < width) { @@ -1630,12 +1582,12 @@ void window_relocate_windows(int32_t width, int32_t height) { if (w->y - 22 < height) { - continue; + return; } } if (w->y + 10 < height) { - continue; + return; } } @@ -1654,7 +1606,7 @@ void window_relocate_windows(int32_t width, int32_t height) w->viewport->x -= x - w->x; w->viewport->y -= y - w->y; } - } + }); } /** @@ -1662,40 +1614,9 @@ void window_relocate_windows(int32_t width, int32_t height) */ void window_resize_gui(int32_t width, int32_t height) { - if (gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) - { - window_resize_gui_scenario_editor(width, height); + window_resize_gui_scenario_editor(width, height); + if (gScreenFlags & SCREEN_FLAGS_EDITOR) return; - } - rct_window* mainWind = window_get_main(); - if (mainWind != nullptr) - { - rct_viewport* viewport = mainWind->viewport; - mainWind->width = width; - mainWind->height = height; - viewport->width = width; - viewport->height = height; - viewport->view_width = width << viewport->zoom; - viewport->view_height = height << viewport->zoom; - if (mainWind->widgets != nullptr && mainWind->widgets[WC_MAIN_WINDOW__0].type == WWT_VIEWPORT) - { - mainWind->widgets[WC_MAIN_WINDOW__0].right = width; - mainWind->widgets[WC_MAIN_WINDOW__0].bottom = height; - } - } - - rct_window* topWind = window_find_by_class(WC_TOP_TOOLBAR); - if (topWind != nullptr) - { - topWind->width = std::max(640, width); - } - - rct_window* bottomWind = window_find_by_class(WC_BOTTOM_TOOLBAR); - if (bottomWind != nullptr) - { - bottomWind->y = height - 32; - bottomWind->width = std::max(640, width); - } rct_window* titleWind = window_find_by_class(WC_TITLE_MENU); if (titleWind != nullptr) @@ -1827,21 +1748,21 @@ static void window_snap_left(rct_window* w, int32_t proximity) auto wLeftProximity = w->x - (proximity * 2); auto wRightProximity = w->x + (proximity * 2); auto rightMost = INT32_MIN; - for (auto& w2 : g_window_list) - { - if (w2.get() == w || w2.get() == mainWindow) - continue; + + window_visit_each([&](rct_window* w2) { + if (w2 == w || w2 == mainWindow) + return; auto right = w2->x + w2->width; if (wBottom < w2->y || w->y > w2->y + w2->height) - continue; + return; if (right < wLeftProximity || right > wRightProximity) - continue; + return; rightMost = std::max(rightMost, right); - } + }); if (0 >= wLeftProximity && 0 <= wRightProximity) rightMost = std::max(rightMost, 0); @@ -1857,21 +1778,21 @@ static void window_snap_top(rct_window* w, int32_t proximity) auto wTopProximity = w->y - (proximity * 2); auto wBottomProximity = w->y + (proximity * 2); auto bottomMost = INT32_MIN; - for (auto& w2 : g_window_list) - { - if (w2.get() == w || w2.get() == mainWindow) - continue; + + window_visit_each([&](rct_window* w2) { + if (w2 == w || w2 == mainWindow) + return; auto bottom = w2->y + w2->height; if (wRight < w2->x || w->x > w2->x + w2->width) - continue; + return; if (bottom < wTopProximity || bottom > wBottomProximity) - continue; + return; bottomMost = std::max(bottomMost, bottom); - } + }); if (0 >= wTopProximity && 0 <= wBottomProximity) bottomMost = std::max(bottomMost, 0); @@ -1888,19 +1809,19 @@ static void window_snap_right(rct_window* w, int32_t proximity) auto wLeftProximity = wRight - (proximity * 2); auto wRightProximity = wRight + (proximity * 2); auto leftMost = INT32_MAX; - for (auto& w2 : g_window_list) - { - if (w2.get() == w || w2.get() == mainWindow) - continue; + + window_visit_each([&](rct_window* w2) { + if (w2 == w || w2 == mainWindow) + return; if (wBottom < w2->y || w->y > w2->y + w2->height) - continue; + return; if (w2->x < wLeftProximity || w2->x > wRightProximity) - continue; + return; leftMost = std::min(leftMost, w2->x); - } + }); auto screenWidth = context_get_width(); if (screenWidth >= wLeftProximity && screenWidth <= wRightProximity) @@ -1918,19 +1839,19 @@ static void window_snap_bottom(rct_window* w, int32_t proximity) auto wTopProximity = wBottom - (proximity * 2); auto wBottomProximity = wBottom + (proximity * 2); auto topMost = INT32_MAX; - for (auto& w2 : g_window_list) - { - if (w2.get() == w || w2.get() == mainWindow) - continue; + + window_visit_each([&](rct_window* w2) { + if (w2 == w || w2 == mainWindow) + return; if (wRight < w2->x || w->x > w2->x + w2->width) - continue; + return; if (w2->y < wTopProximity || w2->y > wBottomProximity) - continue; + return; topMost = std::min(topMost, w2->y); - } + }); auto screenHeight = context_get_height(); if (screenHeight >= wTopProximity && screenHeight <= wBottomProximity) @@ -1940,18 +1861,18 @@ static void window_snap_bottom(rct_window* w, int32_t proximity) w->y = topMost - w->height; } -void window_move_and_snap(rct_window* w, int32_t newWindowX, int32_t newWindowY, int32_t snapProximity) +void window_move_and_snap(rct_window* w, ScreenCoordsXY newWindowCoords, int32_t snapProximity) { int32_t originalX = w->x; int32_t originalY = w->y; int32_t minY = (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) ? 1 : TOP_TOOLBAR_HEIGHT + 2; - newWindowY = std::clamp(newWindowY, minY, context_get_height() - 34); + newWindowCoords.y = std::clamp(newWindowCoords.y, minY, context_get_height() - 34); if (snapProximity > 0) { - w->x = newWindowX; - w->y = newWindowY; + w->x = newWindowCoords.x; + w->y = newWindowCoords.y; window_snap_right(w, snapProximity); window_snap_bottom(w, snapProximity); @@ -1961,13 +1882,13 @@ void window_move_and_snap(rct_window* w, int32_t newWindowX, int32_t newWindowY, if (w->x == originalX && w->y == originalY) return; - newWindowX = w->x; - newWindowY = w->y; + newWindowCoords.x = w->x; + newWindowCoords.y = w->y; w->x = originalX; w->y = originalY; } - window_set_position(w, newWindowX, newWindowY); + window_set_position(w, newWindowCoords); } int32_t window_can_resize(rct_window* w) @@ -2068,9 +1989,10 @@ bool window_is_visible(rct_window* w) } // start from the window above the current - for (auto i = window_get_index(w) + 1; i < g_window_list.size(); i++) + auto itPos = window_get_iterator(w); + for (auto it = std::next(itPos); it != g_window_list.end(); it++) { - auto& w_other = *g_window_list[i]; + auto& w_other = *(*it); // if covered by a higher window, no rendering needed if (w_other.x <= w->x && w_other.y <= w->y && w_other.x + w_other.width >= w->x + w->width @@ -2098,7 +2020,7 @@ bool window_is_visible(rct_window* w) */ void window_draw_all(rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom) { - rct_drawpixelinfo windowDPI; + rct_drawpixelinfo windowDPI = *dpi; windowDPI.bits = dpi->bits + left + ((dpi->width + dpi->pitch) * top); windowDPI.x = left; windowDPI.y = top; @@ -2107,17 +2029,15 @@ void window_draw_all(rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t windowDPI.pitch = dpi->width + dpi->pitch + left - right; windowDPI.zoom_level = 0; - for (auto& w : g_window_list) - { + window_visit_each([&windowDPI, left, top, right, bottom](rct_window* w) { if (w->flags & WF_TRANSPARENT) - continue; + return; if (right <= w->x || bottom <= w->y) - continue; + return; if (left >= w->x + w->width || top >= w->y + w->height) - continue; - - window_draw(&windowDPI, w.get(), left, top, right, bottom); - } + return; + window_draw(&windowDPI, w, left, top, right, bottom); + }); } rct_viewport* window_get_previous_viewport(rct_viewport* current) @@ -2144,14 +2064,13 @@ rct_viewport* window_get_previous_viewport(rct_viewport* current) void window_reset_visibilities() { // reset window visibility status to unknown - for (auto& w : g_window_list) - { + window_visit_each([](rct_window* w) { w->visibility = VC_UNKNOWN; if (w->viewport != nullptr) { w->viewport->visibility = VC_UNKNOWN; } - } + }); } void window_init_all() diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index 98d0600802..ee4c85f1d9 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,8 +12,12 @@ #include "../common.h" #include "../ride/RideTypes.h" +#include "../world/Location.hpp" +#include #include +#include +#include struct rct_drawpixelinfo; struct rct_window; @@ -487,9 +491,10 @@ enum #define WC_RIDE_CONSTRUCTION__WIDX_ROTATE 32 #define WC_SCENERY__WIDX_SCENERY_TAB_1 4 #define WC_SCENERY__WIDX_SCENERY_ROTATE_OBJECTS_BUTTON 25 +#define WC_SCENERY__WIDX_SCENERY_EYEDROPPER_BUTTON 30 #define WC_PEEP__WIDX_PATROL 11 -#define WC_PEEP__WIDX_ACTION_LBL 12 -#define WC_PEEP__WIDX_PICKUP 13 +#define WC_PEEP__WIDX_ACTION_LBL 13 +#define WC_PEEP__WIDX_PICKUP 14 #define WC_TRACK_DESIGN_LIST__WIDX_ROTATE 8 #define WC_TRACK_DESIGN_PLACE__WIDX_ROTATE 3 #define WC_MAP__WIDX_ROTATE_90 20 @@ -579,7 +584,9 @@ extern colour_t gCurrentWindowColours[4]; extern bool gDisableErrorWindowSound; -size_t window_get_index(const rct_window* w); +std::list>::iterator window_get_iterator(const rct_window* w); +void window_visit_each(std::function func); + void window_dispatch_update_all(); void window_update_all_viewports(); void window_update_all(); @@ -587,7 +594,7 @@ void window_update_all(); void window_set_window_limit(int32_t value); rct_window* window_create( - int32_t x, int32_t y, int32_t width, int32_t height, rct_window_event_list* event_handlers, rct_windowclass cls, + ScreenCoordsXY screenCoords, int32_t width, int32_t height, rct_window_event_list* event_handlers, rct_windowclass cls, uint16_t flags); rct_window* window_create_auto_pos( int32_t width, int32_t height, rct_window_event_list* event_handlers, rct_windowclass cls, uint16_t flags); @@ -602,9 +609,8 @@ void window_close_all_except_class(rct_windowclass cls); void window_close_all_except_flags(uint16_t flags); rct_window* window_find_by_class(rct_windowclass cls); rct_window* window_find_by_number(rct_windowclass cls, rct_windownumber number); -rct_window* window_find_from_point(int32_t x, int32_t y); -rct_widgetindex window_find_widget_from_point(rct_window* w, int32_t x, int32_t y); -void window_invalidate(rct_window* window); +rct_window* window_find_from_point(ScreenCoordsXY screenCoords); +rct_widgetindex window_find_widget_from_point(rct_window* w, ScreenCoordsXY screenCoords); void window_invalidate_by_class(rct_windowclass cls); void window_invalidate_by_number(rct_windowclass cls, rct_windownumber number); void window_invalidate_all(); @@ -625,8 +631,6 @@ void window_push_others_below(rct_window* w1); rct_window* window_get_main(); -void window_set_location(rct_window* w, int32_t x, int32_t y, int32_t z); -void window_scroll_to_viewport(rct_window* w); void window_scroll_to_location(rct_window* w, int32_t x, int32_t y, int32_t z); void window_rotate_camera(rct_window* w, int32_t direction); void window_viewport_get_map_coords_by_cursor( @@ -644,8 +648,8 @@ void window_draw(rct_drawpixelinfo* dpi, rct_window* w, int32_t left, int32_t to void window_draw_widgets(rct_window* w, rct_drawpixelinfo* dpi); void window_draw_viewport(rct_drawpixelinfo* dpi, rct_window* w); -void window_set_position(rct_window* w, int32_t x, int32_t y); -void window_move_position(rct_window* w, int32_t dx, int32_t dy); +void window_set_position(rct_window* w, ScreenCoordsXY screenCoords); +void window_move_position(rct_window* w, ScreenCoordsXY screenCoords); void window_resize(rct_window* w, int32_t dw, int32_t dh); void window_set_resize(rct_window* w, int32_t minWidth, int32_t minHeight, int32_t maxWidth, int32_t maxHeight); @@ -663,9 +667,9 @@ void window_relocate_windows(int32_t width, int32_t height); void window_resize_gui(int32_t width, int32_t height); void window_resize_gui_scenario_editor(int32_t width, int32_t height); void window_ride_construct(rct_window* w); -void ride_construction_toolupdate_entrance_exit(int32_t screenX, int32_t screenY); -void ride_construction_toolupdate_construct(int32_t screenX, int32_t screenY); -void ride_construction_tooldown_construct(int32_t screenX, int32_t screenY); +void ride_construction_toolupdate_entrance_exit(ScreenCoordsXY screenCoords); +void ride_construction_toolupdate_construct(ScreenCoordsXY screenCoords); +void ride_construction_tooldown_construct(ScreenCoordsXY screenCoords); void window_bubble_list_item(rct_window* w, int32_t item_position); @@ -673,11 +677,6 @@ void window_align_tabs(rct_window* w, rct_widgetindex start_tab_id, rct_widgetin void window_staff_list_init_vars(); -void game_command_callback_pickup_guest( - int32_t eax, int32_t ebx, int32_t ecx, int32_t edx, int32_t esi, int32_t edi, int32_t ebp); -void game_command_callback_pickup_staff( - int32_t eax, int32_t ebx, int32_t ecx, int32_t edx, int32_t esi, int32_t edi, int32_t ebp); - void window_event_close_call(rct_window* w); void window_event_mouse_up_call(rct_window* w, rct_widgetindex widgetIndex); void window_event_resize_call(rct_window* w); @@ -687,22 +686,22 @@ void window_event_unknown_05_call(rct_window* w); void window_event_update_call(rct_window* w); void window_event_periodic_update_call(rct_window* w); void window_event_unknown_08_call(rct_window* w); -void window_event_tool_update_call(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y); -void window_event_tool_down_call(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y); -void window_event_tool_drag_call(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y); -void window_event_tool_up_call(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y); +void window_event_tool_update_call(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); +void window_event_tool_down_call(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); +void window_event_tool_drag_call(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); +void window_event_tool_up_call(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); void window_event_tool_abort_call(rct_window* w, rct_widgetindex widgetIndex); void window_event_unknown_0E_call(rct_window* w); void window_get_scroll_size(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height); -void window_event_scroll_mousedown_call(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y); -void window_event_scroll_mousedrag_call(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y); -void window_event_scroll_mouseover_call(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y); +void window_event_scroll_mousedown_call(rct_window* w, int32_t scrollIndex, ScreenCoordsXY screenCoords); +void window_event_scroll_mousedrag_call(rct_window* w, int32_t scrollIndex, ScreenCoordsXY screenCoords); +void window_event_scroll_mouseover_call(rct_window* w, int32_t scrollIndex, ScreenCoordsXY screenCoords); void window_event_textinput_call(rct_window* w, rct_widgetindex widgetIndex, char* text); void window_event_viewport_rotate_call(rct_window* w); void window_event_unknown_15_call(rct_window* w, int32_t scrollIndex, int32_t scrollAreaType); rct_string_id window_event_tooltip_call(rct_window* w, rct_widgetindex widgetIndex); -int32_t window_event_cursor_call(rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y); -void window_event_moved_call(rct_window* w, int32_t x, int32_t y); +int32_t window_event_cursor_call(rct_window* w, rct_widgetindex widgetIndex, ScreenCoordsXY screenCoords); +void window_event_moved_call(rct_window* w, ScreenCoordsXY screenCoords); void window_event_invalidate_call(rct_window* w); void window_event_paint_call(rct_window* w, rct_drawpixelinfo* dpi); void window_event_scroll_paint_call(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex); @@ -710,7 +709,7 @@ void window_event_scroll_paint_call(rct_window* w, rct_drawpixelinfo* dpi, int32 void invalidate_all_windows_after_input(); void textinput_cancel(); -void window_move_and_snap(rct_window* w, int32_t newWindowX, int32_t newWindowY, int32_t snapProximity); +void window_move_and_snap(rct_window* w, ScreenCoordsXY newWindowCoords, int32_t snapProximity); int32_t window_can_resize(rct_window* w); void window_start_textbox( diff --git a/src/openrct2/interface/Window_internal.cpp b/src/openrct2/interface/Window_internal.cpp new file mode 100644 index 0000000000..3a47dd4278 --- /dev/null +++ b/src/openrct2/interface/Window_internal.cpp @@ -0,0 +1,42 @@ +#include "Window_internal.h" + +#include "../world/Sprite.h" + +void rct_window::SetLocation(int32_t newX, int32_t newY, int32_t newZ) +{ + window_scroll_to_location(this, newX, newY, newZ); + flags &= ~WF_SCROLLING_TO_LOCATION; +} + +void rct_window::ScrollToViewport() +{ + int32_t newX, newY, newZ; + rct_window* mainWindow; + + // In original checked to make sure x and y were not -1 as well. + if (viewport == nullptr || viewport_focus_coordinates.y == -1) + return; + + if (viewport_focus_sprite.type & VIEWPORT_FOCUS_TYPE_SPRITE) + { + rct_sprite* sprite = get_sprite(viewport_focus_sprite.sprite_id); + newX = sprite->generic.x; + newY = sprite->generic.y; + newZ = sprite->generic.z; + } + else + { + newX = viewport_focus_coordinates.x; + newY = viewport_focus_coordinates.y & VIEWPORT_FOCUS_Y_MASK; + newZ = viewport_focus_coordinates.z; + } + + mainWindow = window_get_main(); + if (mainWindow != nullptr) + window_scroll_to_location(mainWindow, newX, newY, newZ); +} + +void rct_window::Invalidate() +{ + gfx_set_dirty_blocks(x, y, x + width, y + height); +} diff --git a/src/openrct2/interface/Window_internal.h b/src/openrct2/interface/Window_internal.h index f030c0a9ac..5297737acb 100644 --- a/src/openrct2/interface/Window_internal.h +++ b/src/openrct2/interface/Window_internal.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,10 +11,10 @@ #include "Window.h" +#include #include -#include -struct rct_research_item; +struct ResearchItem; struct rct_object_entry; /** @@ -79,7 +79,7 @@ struct rct_window { // 0x494 uint32_t highlighted_item; uint16_t ride_colour; - rct_research_item* research_item; + ResearchItem* research_item; rct_object_entry* object_entry; const scenario_index_entry* highlighted_scenario; struct @@ -101,7 +101,11 @@ struct rct_window uint8_t colours[6]; // 0x4BA uint8_t visibility; // VISIBILITY_CACHE uint16_t viewport_smart_follow_sprite; // Smart following of sprites. Handles setting viewport target sprite etc + + void SetLocation(int32_t x, int32_t y, int32_t z); + void ScrollToViewport(); + void Invalidate(); }; // rct2: 0x01420078 -extern std::vector> g_window_list; +extern std::list> g_window_list; diff --git a/src/openrct2/localisation/ConversionTables.cpp b/src/openrct2/localisation/ConversionTables.cpp index ccfdde1298..066c737df6 100644 --- a/src/openrct2/localisation/ConversionTables.cpp +++ b/src/openrct2/localisation/ConversionTables.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/localisation/ConversionTables.h b/src/openrct2/localisation/ConversionTables.h index 4707f1c2e0..c99f8764f6 100644 --- a/src/openrct2/localisation/ConversionTables.h +++ b/src/openrct2/localisation/ConversionTables.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/localisation/Convert.cpp b/src/openrct2/localisation/Convert.cpp index c3362ec69a..032b23ee3d 100644 --- a/src/openrct2/localisation/Convert.cpp +++ b/src/openrct2/localisation/Convert.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/localisation/Currency.cpp b/src/openrct2/localisation/Currency.cpp index 99417ce160..9f2332adc7 100644 --- a/src/openrct2/localisation/Currency.cpp +++ b/src/openrct2/localisation/Currency.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -31,6 +31,7 @@ currency_descriptor CurrencyDescriptors[CURRENCY_END] = { { "HKD", 100, CURRENCY_PREFIX, "$", CURRENCY_PREFIX, "HKD", STR_HONG_KONG_DOLLAR}, // Hong Kong Dollar { "TWD", 1000, CURRENCY_PREFIX, "NT$", CURRENCY_PREFIX, "NT$", STR_NEW_TAIWAN_DOLLAR}, // New Taiwan Dollar { "CNY", 100, CURRENCY_PREFIX, "CN\xC2\xA5", CURRENCY_PREFIX, "CNY", STR_CHINESE_YUAN }, // Chinese Yuan + { "HUF", 1000, CURRENCY_SUFFIX, " Ft", CURRENCY_SUFFIX, " Ft", STR_HUNGARIAN_FORINT}, // Hungarian Forint { "CTM", 10, CURRENCY_PREFIX, "Ctm", CURRENCY_PREFIX, "Ctm", STR_CUSTOM_CURRENCY }, // Customizable currency }; // clang-format on diff --git a/src/openrct2/localisation/Currency.h b/src/openrct2/localisation/Currency.h index 63de732462..2b186f1508 100644 --- a/src/openrct2/localisation/Currency.h +++ b/src/openrct2/localisation/Currency.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -31,6 +31,7 @@ enum CURRENCY_TYPE CURRENCY_HKD, // Hong Kong Dollar CURRENCY_TWD, // New Taiwan Dollar CURRENCY_YUAN, // Chinese Yuan + CURRENCY_FORINT, // Hungarian Forint CURRENCY_CUSTOM, // Custom currency diff --git a/src/openrct2/localisation/Date.h b/src/openrct2/localisation/Date.h index 6417794aa2..52eb12a1ed 100644 --- a/src/openrct2/localisation/Date.h +++ b/src/openrct2/localisation/Date.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,6 +12,8 @@ #include "../common.h" +constexpr int32_t MAX_YEAR = 8192; + enum { MONTH_MARCH, diff --git a/src/openrct2/localisation/FormatCodes.cpp b/src/openrct2/localisation/FormatCodes.cpp index dff3f97d8d..17a67a026e 100644 --- a/src/openrct2/localisation/FormatCodes.cpp +++ b/src/openrct2/localisation/FormatCodes.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -96,7 +96,7 @@ const char* format_get_token(uint32_t code) return nullptr; } -bool utf8_should_use_sprite_for_codepoint(int32_t codepoint) +bool utf8_should_use_sprite_for_codepoint(char32_t codepoint) { switch (codepoint) { diff --git a/src/openrct2/localisation/FormatCodes.h b/src/openrct2/localisation/FormatCodes.h index d87da883d3..781d55cced 100644 --- a/src/openrct2/localisation/FormatCodes.h +++ b/src/openrct2/localisation/FormatCodes.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -278,6 +278,8 @@ namespace UnicodeChar constexpr char32_t leftguillemet = 0xAB; constexpr char32_t rightguillemet = 0xBB; constexpr char32_t interpunct = 0xB7; + constexpr char32_t multiplication_sign = 0xD7; + constexpr char32_t en_dash = 0x2013; constexpr char32_t single_quote_open = 0x2018; constexpr char32_t single_quote_end = 0x2019; constexpr char32_t single_german_quote_open = 0x201A; diff --git a/src/openrct2/localisation/Language.cpp b/src/openrct2/localisation/Language.cpp index c895ff88ba..cca9d7428b 100644 --- a/src/openrct2/localisation/Language.cpp +++ b/src/openrct2/localisation/Language.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -28,7 +28,7 @@ const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] = { "ca-ES", "Catalan", u8"Català", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_CATALAN { "zh-CN", "Chinese (Simplified)", "Chinese (Simplified)", FAMILY(&TTFFamilyChineseSimplified), false }, // LANGUAGE_CHINESE_SIMPLIFIED { "zh-TW", "Chinese (Traditional)", "Chinese (Traditional)", FAMILY(&TTFFamilyChineseTraditional), false }, // LANGUAGE_CHINESE_TRADITIONAL - { "cs-CZ", "Czech", "Czech", FAMILY(&TTFFamilySansSerif), false }, // LANGUAGE_CZECH + { "cs-CZ", "Czech", u8"Čeština", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_CZECH { "da-DK", "Danish", "Dansk", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_DANISH { "de-DE", "German", "Deutsch", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_GERMAN { "en-GB", "English (UK)", "English (UK)", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ENGLISH_UK diff --git a/src/openrct2/localisation/Language.h b/src/openrct2/localisation/Language.h index 5791a14229..7bdc8385cd 100644 --- a/src/openrct2/localisation/Language.h +++ b/src/openrct2/localisation/Language.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -99,10 +99,8 @@ utf8* utf8_write_codepoint(utf8* dst, uint32_t codepoint); int32_t utf8_insert_codepoint(utf8* dst, uint32_t codepoint); bool utf8_is_codepoint_start(const utf8* text); void utf8_remove_format_codes(utf8* text, bool allowcolours); -int32_t utf8_get_codepoint_length(int32_t codepoint); +int32_t utf8_get_codepoint_length(char32_t codepoint); int32_t utf8_length(const utf8* text); -wchar_t* utf8_to_widechar(const utf8* src); -utf8* widechar_to_utf8(const wchar_t* src); std::string rct2_to_utf8(const std::string_view& src, RCT2LanguageId languageId); std::string utf8_to_rct2(const std::string_view& src); diff --git a/src/openrct2/localisation/LanguagePack.cpp b/src/openrct2/localisation/LanguagePack.cpp index 79a1da0556..9d0953ba77 100644 --- a/src/openrct2/localisation/LanguagePack.cpp +++ b/src/openrct2/localisation/LanguagePack.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/localisation/LanguagePack.h b/src/openrct2/localisation/LanguagePack.h index 9a9e73439e..b65a933eb9 100644 --- a/src/openrct2/localisation/LanguagePack.h +++ b/src/openrct2/localisation/LanguagePack.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/localisation/Localisation.Date.cpp b/src/openrct2/localisation/Localisation.Date.cpp index 8d4b8fbec5..6507a9afdd 100644 --- a/src/openrct2/localisation/Localisation.Date.cpp +++ b/src/openrct2/localisation/Localisation.Date.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -67,7 +67,7 @@ void date_reset() void date_set(int32_t year, int32_t month, int32_t day) { - year = std::clamp(year, 1, 8192); + year = std::clamp(year, 1, MAX_YEAR); month = std::clamp(month, 1, (int)MONTH_COUNT); day = std::clamp(day, 1, (int)days_in_month[month - 1]); gDateMonthsElapsed = (year - 1) * MONTH_COUNT + month - 1; diff --git a/src/openrct2/localisation/Localisation.cpp b/src/openrct2/localisation/Localisation.cpp index 12b96d9579..ef881fee36 100644 --- a/src/openrct2/localisation/Localisation.cpp +++ b/src/openrct2/localisation/Localisation.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -30,14 +30,15 @@ #include "Localisation.h" #include +#include #include #include #include #include -char gCommonStringFormatBuffer[512]; -uint8_t gCommonFormatArgs[80]; -uint8_t gMapTooltipFormatArgs[40]; +thread_local char gCommonStringFormatBuffer[512]; +thread_local uint8_t gCommonFormatArgs[80]; +thread_local uint8_t gMapTooltipFormatArgs[40]; #ifdef DEBUG // Set to true before a string format call to see details of the formatting. @@ -385,26 +386,6 @@ static void format_append_string(char** dest, size_t* size, const utf8* string) } } -static void format_append_string_n(char** dest, size_t* size, const utf8* string, size_t maxlen) -{ - if ((*size) == 0) - return; - size_t length = std::min(maxlen, strlen(string)); - if (length < (*size)) - { - std::memcpy((*dest), string, length); - (*dest) += length; - (*size) -= length; - } - else - { - std::memcpy((*dest), string, (*size) - 1); - (*dest) += (*size) - 1; - *(*dest)++ = '\0'; - (*size) = 0; - } -} - static void format_integer(char** dest, size_t* size, int64_t value) { int32_t digit; @@ -1256,16 +1237,14 @@ static void format_string_part(utf8** dest, size_t* size, rct_string_id format, } else if (format <= USER_STRING_END) { + // User strings should no longer be used + assert(false); + // Custom string format -= 0x8000; // Bits 10, 11 represent number of bytes to pop off arguments *args += (format & 0xC00) >> 9; - format &= ~0xC00; - - format_append_string_n(dest, size, gUserStrings[format], USER_STRING_MAX_LENGTH); - if ((*size) > 0) - *(*dest) = '\0'; } else if (format <= REAL_NAME_END) { @@ -1279,8 +1258,6 @@ static void format_string_part(utf8** dest, size_t* size, rct_string_id format, format_push_char(real_name_initials[(realNameIndex >> 10) % std::size(real_name_initials)]); format_push_char('.'); *(*dest) = '\0'; - - *args += 4; } else { @@ -1290,6 +1267,32 @@ static void format_string_part(utf8** dest, size_t* size, rct_string_id format, } } +std::string format_string(rct_string_id format, const void* args) +{ + std::string buffer(256, 0); + size_t len{}; + for (;;) + { + format_string(buffer.data(), buffer.size(), format, args); + len = buffer.find('\0'); + if (len == std::string::npos) + { + len = buffer.size(); + } + if (len >= buffer.size() - 1) + { + // Null terminator to close to end of buffer, grow buffer and try again + buffer.resize(buffer.size() * 2); + } + else + { + buffer.resize(len); + break; + } + } + return buffer; +} + /** * Writes a formatted string to a buffer. * rct2: 0x006C2555 @@ -1512,8 +1515,8 @@ money32 string_to_money(const char* string_to_monetise) auto number = std::stod(processedString, nullptr); number /= (currencyDesc->rate / 10.0); - auto whole = static_cast(number); - auto fraction = static_cast((number - whole) * 100); + auto whole = static_cast(number); + auto fraction = static_cast(ceil((number - whole) * 100.0)); money32 result = MONEY(whole, fraction); // Check if MONEY resulted in overflow diff --git a/src/openrct2/localisation/Localisation.h b/src/openrct2/localisation/Localisation.h index 1a92f4095c..f129ea246b 100644 --- a/src/openrct2/localisation/Localisation.h +++ b/src/openrct2/localisation/Localisation.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,13 +18,15 @@ #include "StringIds.h" #include +#include -bool utf8_is_format_code(int32_t codepoint); -bool utf8_is_colour_code(int32_t codepoint); -bool utf8_should_use_sprite_for_codepoint(int32_t codepoint); -int32_t utf8_get_format_code_arg_length(int32_t codepoint); +bool utf8_is_format_code(char32_t codepoint); +bool utf8_is_colour_code(char32_t codepoint); +bool utf8_should_use_sprite_for_codepoint(char32_t codepoint); +int32_t utf8_get_format_code_arg_length(char32_t codepoint); void utf8_remove_formatting(utf8* string, bool allowColours); +std::string format_string(rct_string_id format, const void* args); void format_string(char* dest, size_t size, rct_string_id format, const void* args); void format_string_raw(char* dest, size_t size, const char* src, const void* args); void format_string_to_upper(char* dest, size_t size, rct_string_id format, const void* args); @@ -49,9 +51,6 @@ int32_t get_string_length(const utf8* text); money32 string_to_money(const char* string_to_monetise); void money_to_string(money32 amount, char* buffer_to_put_value_to, size_t buffer_len, bool forceDecimals); -void user_string_clear_all(); -rct_string_id user_string_allocate(int32_t base, const utf8* text); -void user_string_free(rct_string_id id); bool is_user_string_id(rct_string_id stringId); #define MAX_USER_STRINGS 1024 @@ -62,21 +61,13 @@ bool is_user_string_id(rct_string_id stringId); #define REAL_NAME_START 0xA000 #define REAL_NAME_END 0xDFFF -// Constants used by user_string_allocate -enum -{ - USER_STRING_HIGH_ID_NUMBER = 1 << 2, - USER_STRING_DUPLICATION_PERMITTED = 1 << 7 -}; - // Real name data extern const char real_name_initials[16]; extern const char* real_names[1024]; -extern utf8 gUserStrings[MAX_USER_STRINGS][USER_STRING_MAX_LENGTH]; -extern char gCommonStringFormatBuffer[512]; -extern uint8_t gCommonFormatArgs[80]; -extern uint8_t gMapTooltipFormatArgs[40]; +extern thread_local char gCommonStringFormatBuffer[512]; +extern thread_local uint8_t gCommonFormatArgs[80]; +extern thread_local uint8_t gMapTooltipFormatArgs[40]; extern bool gDebugStringFormatting; extern const rct_string_id SpeedNames[5]; diff --git a/src/openrct2/localisation/LocalisationService.cpp b/src/openrct2/localisation/LocalisationService.cpp index b1fbc46f8e..97588214fc 100644 --- a/src/openrct2/localisation/LocalisationService.cpp +++ b/src/openrct2/localisation/LocalisationService.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/localisation/LocalisationService.h b/src/openrct2/localisation/LocalisationService.h index 54c7714f8f..01cc53e5a1 100644 --- a/src/openrct2/localisation/LocalisationService.h +++ b/src/openrct2/localisation/LocalisationService.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/localisation/RealNames.cpp b/src/openrct2/localisation/RealNames.cpp index 50a6693752..d3aeffec97 100644 --- a/src/openrct2/localisation/RealNames.cpp +++ b/src/openrct2/localisation/RealNames.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index c07c48951b..bccaad59f6 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -2502,7 +2502,7 @@ enum STR_OBJECT_SELECTION_SCENERY_GROUPS = 3191, STR_OBJECT_SELECTION_PARK_ENTRANCE = 3192, STR_OBJECT_SELECTION_WATER = 3193, - STR_OBJECT_SELECTION_SCENARIO_DESCRIPTION = 3194, + STR_INVENTION_LIST = 3195, STR_INVENTION_RESEARCH_GROUP = 3196, STR_INVENTION_PREINVENTED_ITEMS = 3197, @@ -2741,8 +2741,6 @@ enum STR_LOCALE_DECIMAL_POINT = 5152, STR_EDIT_THEMES_BUTTON = 5153, STR_HARDWARE_DISPLAY = 5154, // Unused - STR_TEST_UNFINISHED_TRACKS = 5155, - STR_TEST_UNFINISHED_TRACKS_TIP = 5156, STR_CHEAT_UNLOCK_PRICES = 5157, STR_QUIT_TO_MENU = 5158, STR_EXIT_OPENRCT2 = 5159, @@ -3311,7 +3309,7 @@ enum STR_ALL_FILES = 5763, STR_INVALID_RIDE_TYPE = 5764, STR_CANT_EDIT_INVALID_RIDE_TYPE = 5765, - + STR_HUNGARIAN_FORINT = 5766, STR_RIDE_LIST_INCOME = 5767, STR_RIDE_LIST_TOTAL_CUSTOMERS = 5768, STR_RIDE_LIST_TOTAL_PROFIT = 5769, @@ -3933,6 +3931,46 @@ enum STR_COPY_ALL_TIP = 6302, STR_DOWNLOADING_OBJECTS = 6303, + STR_SHORTCUT_OPEN_SCENERY_PICKER = 6304, + + STR_MULTITHREADING = 6305, + STR_MULTITHREADING_TIP = 6306, + + STR_TILE_INSPECTOR_COLOUR_SCHEME = 6307, + STR_TILE_INSPECTOR_TRACK_BLOCK_BRAKE = 6319, + STR_TILE_INSPECTOR_TRACK_IS_INDESTRUCTIBLE = 6320, + STR_TILE_INSPECTOR_PATH_BROKEN = 6321, + + STR_MAP_TOOLTIP_BANNER_STRINGID_STRINGID = 6308, + + STR_MULTIPLAYER_RECONNECT = 6309, + + STR_PEEP_DEBUG_POSITION = 6310, + STR_PEEP_DEBUG_NEXT = 6311, + STR_PEEP_DEBUG_NEXT_SURFACE = 6312, + STR_PEEP_DEBUG_NEXT_SLOPE = 6313, + STR_PEEP_DEBUG_DEST = 6314, + STR_PEEP_DEBUG_PATHFIND_GOAL = 6315, + STR_PEEP_DEBUG_PATHFIND_HISTORY = 6316, + STR_PEEP_DEBUG_PATHFIND_HISTORY_ITEM = 6317, + STR_PEEP_DEBUG_SPRITE_INDEX = 6322, + + STR_DESYNC_REPORT = 6318, + + STR_SIMULATING = 6323, + STR_SIMULATE_RIDE = 6324, + STR_SIMULATE_RIDE_TIP = 6325, + STR_CANT_SIMULATE = 6326, + + STR_TRANSPARENT_SCREENSHOT = 6327, + STR_TRANSPARENT_SCREENSHOT_TIP = 6328, + + STR_STRING_STRINGID = 6329, + STR_DOWNLOADING_OBJECTS_FROM = 6330, + + STR_CREATE_DUCKS = 6331, + STR_REMOVE_DUCKS = 6332, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/openrct2/localisation/UTF8.cpp b/src/openrct2/localisation/UTF8.cpp index b1ea1760e6..a1b3f8b61d 100644 --- a/src/openrct2/localisation/UTF8.cpp +++ b/src/openrct2/localisation/UTF8.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -101,7 +101,7 @@ bool utf8_is_codepoint_start(const utf8* text) return false; } -int32_t utf8_get_codepoint_length(int32_t codepoint) +int32_t utf8_get_codepoint_length(char32_t codepoint) { if (codepoint <= 0x7F) { @@ -137,44 +137,6 @@ int32_t utf8_length(const utf8* text) return count; } -wchar_t* utf8_to_widechar(const utf8* src) -{ - wchar_t* result = (wchar_t*)malloc((utf8_length(src) + 1) * sizeof(wchar_t)); - wchar_t* dst = result; - - const utf8* ch = src; - int32_t codepoint; - while ((codepoint = utf8_get_next(ch, &ch)) != 0) - { - if ((uint32_t)codepoint > 0xFFFF) - { - *dst++ = '?'; - } - else - { - *dst++ = codepoint; - } - } - *dst = 0; - - return result; -} - -utf8* widechar_to_utf8(const wchar_t* src) -{ - utf8* result = (utf8*)malloc((wcslen(src) * 4) + 1); - utf8* dst = result; - - for (; *src != 0; src++) - { - dst = utf8_write_codepoint(dst, *src); - } - *dst++ = 0; - - size_t size = (size_t)(dst - result); - return (utf8*)realloc(result, size); -} - /** * Returns a pointer to the null terminator of the given UTF-8 string. */ @@ -204,7 +166,7 @@ size_t get_string_size(const utf8* text) */ int32_t get_string_length(const utf8* text) { - int32_t codepoint; + char32_t codepoint; const utf8* ch = text; int32_t count = 0; @@ -222,14 +184,14 @@ int32_t get_string_length(const utf8* text) return count; } -int32_t utf8_get_format_code_arg_length(int32_t codepoint) +int32_t utf8_get_format_code_arg_length(char32_t codepoint) { switch (codepoint) { case FORMAT_MOVE_X: case FORMAT_ADJUST_PALETTE: - case 3: - case 4: + case FORMAT_3: + case FORMAT_4: return 1; case FORMAT_NEWLINE_X_Y: return 2; @@ -247,7 +209,7 @@ void utf8_remove_formatting(utf8* string, bool allowColours) while (true) { - uint32_t code = utf8_get_next(readPtr, (const utf8**)&readPtr); + char32_t code = utf8_get_next(readPtr, (const utf8**)&readPtr); if (code == 0) { @@ -261,7 +223,7 @@ void utf8_remove_formatting(utf8* string, bool allowColours) } } -bool utf8_is_format_code(int32_t codepoint) +bool utf8_is_format_code(char32_t codepoint) { if (codepoint < 32) return true; @@ -274,9 +236,7 @@ bool utf8_is_format_code(int32_t codepoint) return false; } -bool utf8_is_colour_code(int32_t codepoint) +bool utf8_is_colour_code(char32_t codepoint) { - if (codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END) - return true; - return false; + return codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END; } diff --git a/src/openrct2/localisation/User.cpp b/src/openrct2/localisation/User.cpp deleted file mode 100644 index 8a23d266ce..0000000000 --- a/src/openrct2/localisation/User.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers - * - * For a complete list of all authors, please refer to contributors.md - * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 - * - * OpenRCT2 is licensed under the GNU General Public License version 3. - *****************************************************************************/ - -#include "User.h" - -#include "../Game.h" -#include "../ride/Ride.h" -#include "../util/Util.h" -#include "Localisation.h" - -utf8 gUserStrings[MAX_USER_STRINGS][USER_STRING_MAX_LENGTH]; - -static bool user_string_exists(const utf8* text); - -/** - * - * rct2: 0x006C4209 - */ -void user_string_clear_all() -{ - std::memset(gUserStrings, 0x00, MAX_USER_STRINGS * USER_STRING_MAX_LENGTH); -} - -/** - * - * rct2: 0x006C421D - */ -rct_string_id user_string_allocate(int32_t base, const utf8* text) -{ - int32_t highBits = (base & 0x7F) << 9; - bool allowDuplicates = base & USER_STRING_DUPLICATION_PERMITTED; - - if (!allowDuplicates && user_string_exists(text)) - { - gGameCommandErrorText = STR_CHOSEN_NAME_IN_USE_ALREADY; - return 0; - } - - for (int32_t i = 0; i < MAX_USER_STRINGS; i++) - { - char* userString = gUserStrings[i]; - - if (userString[0] != 0) - continue; - - safe_strcpy(userString, text, USER_STRING_MAX_LENGTH); - return USER_STRING_START + (i | highBits); - } - gGameCommandErrorText = STR_TOO_MANY_NAMES_DEFINED; - return 0; -} - -/** - * - * rct2: 0x006C42AC - */ -void user_string_free(rct_string_id id) -{ - if (!is_user_string_id(id)) - return; - - id %= MAX_USER_STRINGS; - gUserStrings[id][0] = 0; -} - -static bool user_string_exists(const utf8* text) -{ - char* userString; - for (int32_t i = 0; i < MAX_USER_STRINGS; i++) - { - userString = gUserStrings[i]; - if (userString[0] == 0) - continue; - - if (strcmp(userString, text) == 0) - return true; - } - return false; -} - -bool is_user_string_id(rct_string_id stringId) -{ - return stringId >= 0x8000 && stringId < 0x9000; -} - -void reset_user_strings() -{ - for (int32_t i = 0; i < MAX_USER_STRINGS; i++) - { - gUserStrings[i][0] = 0; - } - - ride_reset_all_names(); -} diff --git a/src/openrct2/localisation/User.h b/src/openrct2/localisation/User.h deleted file mode 100644 index c6c4d9bbd3..0000000000 --- a/src/openrct2/localisation/User.h +++ /dev/null @@ -1,12 +0,0 @@ -/***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers - * - * For a complete list of all authors, please refer to contributors.md - * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 - * - * OpenRCT2 is licensed under the GNU General Public License version 3. - *****************************************************************************/ - -#pragma once - -void reset_user_strings(); diff --git a/src/openrct2/management/Award.cpp b/src/openrct2/management/Award.cpp index 550ca8cea7..0d01d19d3a 100644 --- a/src/openrct2/management/Award.cpp +++ b/src/openrct2/management/Award.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,6 +18,8 @@ #include "../world/Park.h" #include "NewsItem.h" +#include + #define NEGATIVE 0 #define POSITIVE 1 @@ -144,20 +146,16 @@ static bool award_is_deserved_most_tidy(int32_t activeAwardTypes) /** At least 6 open roller coasters. */ static bool award_is_deserved_best_rollercoasters([[maybe_unused]] int32_t activeAwardTypes) { - int32_t i, rollerCoasters; - Ride* ride; - rct_ride_entry* rideEntry; - - rollerCoasters = 0; - FOR_ALL_RIDES (i, ride) + auto rollerCoasters = 0; + for (const auto& ride : GetRideManager()) { - rideEntry = get_ride_entry(ride->subtype); + auto rideEntry = ride.GetRideEntry(); if (rideEntry == nullptr) { continue; } - if (ride->status != RIDE_STATUS_OPEN || (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) + if (ride.status != RIDE_STATUS_OPEN || (ride.lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { continue; } @@ -169,7 +167,6 @@ static bool award_is_deserved_best_rollercoasters([[maybe_unused]] int32_t activ rollerCoasters++; } - return (rollerCoasters >= 6); } @@ -250,12 +247,10 @@ static bool award_is_deserved_worst_value(int32_t activeAwardTypes) /** No more than 2 people who think the vandalism is bad and no crashes. */ static bool award_is_deserved_safest([[maybe_unused]] int32_t activeAwardTypes) { - int32_t i, peepsWhoDislikeVandalism; uint16_t spriteIndex; Peep* peep; - Ride* ride; - peepsWhoDislikeVandalism = 0; + auto peepsWhoDislikeVandalism = 0; FOR_ALL_GUESTS (spriteIndex, peep) { if (peep->outside_of_park != 0) @@ -268,10 +263,12 @@ static bool award_is_deserved_safest([[maybe_unused]] int32_t activeAwardTypes) return false; // Check for rides that have crashed maybe? - FOR_ALL_RIDES (i, ride) + const auto& rideManager = GetRideManager(); + if (std::any_of(rideManager.begin(), rideManager.end(), [](const Ride& ride) { + return ride.last_crash_type != RIDE_CRASH_TYPE_NONE; + })) { - if (ride->last_crash_type != RIDE_CRASH_TYPE_NONE) - return false; + return false; } return true; @@ -310,36 +307,28 @@ static bool award_is_deserved_best_staff(int32_t activeAwardTypes) /** At least 7 shops, 4 unique, one shop per 128 guests and no more than 12 hungry guests. */ static bool award_is_deserved_best_food(int32_t activeAwardTypes) { - int32_t i, hungryPeeps, shops, uniqueShops; - uint64_t shopTypes; - Ride* ride; - rct_ride_entry* rideEntry; - uint16_t spriteIndex; - Peep* peep; - if (activeAwardTypes & (1 << PARK_AWARD_WORST_FOOD)) return false; - shops = 0; - uniqueShops = 0; - shopTypes = 0; - FOR_ALL_RIDES (i, ride) + auto shops = 0; + auto uniqueShops = 0; + auto shopTypes = 0; + for (const auto& ride : GetRideManager()) { - if (ride->status != RIDE_STATUS_OPEN) + if (ride.status != RIDE_STATUS_OPEN) continue; - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_SELLS_FOOD)) + if (!ride_type_has_flag(ride.type, RIDE_TYPE_FLAG_SELLS_FOOD)) continue; shops++; - rideEntry = get_ride_entry(ride->subtype); - if (rideEntry == nullptr) + auto rideEntry = get_ride_entry(ride.subtype); + if (rideEntry != nullptr) { - continue; - } - if (!(shopTypes & (1ULL << rideEntry->shop_item))) - { - shopTypes |= (1ULL << rideEntry->shop_item); - uniqueShops++; + if (!(shopTypes & (1ULL << rideEntry->shop_item))) + { + shopTypes |= (1ULL << rideEntry->shop_item); + uniqueShops++; + } } } @@ -347,7 +336,9 @@ static bool award_is_deserved_best_food(int32_t activeAwardTypes) return false; // Count hungry peeps - hungryPeeps = 0; + auto hungryPeeps = 0; + uint16_t spriteIndex; + Peep* peep; FOR_ALL_GUESTS (spriteIndex, peep) { if (peep->outside_of_park != 0) @@ -356,43 +347,34 @@ static bool award_is_deserved_best_food(int32_t activeAwardTypes) if (peep->thoughts[0].freshness <= 5 && peep->thoughts[0].type == PEEP_THOUGHT_TYPE_HUNGRY) hungryPeeps++; } - return (hungryPeeps <= 12); } /** No more than 2 unique shops, less than one shop per 256 guests and more than 15 hungry guests. */ static bool award_is_deserved_worst_food(int32_t activeAwardTypes) { - int32_t i, hungryPeeps, shops, uniqueShops; - uint64_t shopTypes; - Ride* ride; - rct_ride_entry* rideEntry; - uint16_t spriteIndex; - Peep* peep; - if (activeAwardTypes & (1 << PARK_AWARD_BEST_FOOD)) return false; - shops = 0; - uniqueShops = 0; - shopTypes = 0; - FOR_ALL_RIDES (i, ride) + auto shops = 0; + auto uniqueShops = 0; + auto shopTypes = 0; + for (const auto& ride : GetRideManager()) { - if (ride->status != RIDE_STATUS_OPEN) + if (ride.status != RIDE_STATUS_OPEN) continue; - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_SELLS_FOOD)) + if (!ride_type_has_flag(ride.type, RIDE_TYPE_FLAG_SELLS_FOOD)) continue; shops++; - rideEntry = get_ride_entry(ride->subtype); - if (rideEntry == nullptr) + auto rideEntry = ride.GetRideEntry(); + if (rideEntry != nullptr) { - continue; - } - if (!(shopTypes & (1ULL << rideEntry->shop_item))) - { - shopTypes |= (1ULL << rideEntry->shop_item); - uniqueShops++; + if (!(shopTypes & (1ULL << rideEntry->shop_item))) + { + shopTypes |= (1ULL << rideEntry->shop_item); + uniqueShops++; + } } } @@ -400,7 +382,9 @@ static bool award_is_deserved_worst_food(int32_t activeAwardTypes) return false; // Count hungry peeps - hungryPeeps = 0; + auto hungryPeeps = 0; + uint16_t spriteIndex; + Peep* peep; FOR_ALL_GUESTS (spriteIndex, peep) { if (peep->outside_of_park != 0) @@ -409,25 +393,17 @@ static bool award_is_deserved_worst_food(int32_t activeAwardTypes) if (peep->thoughts[0].freshness <= 5 && peep->thoughts[0].type == PEEP_THOUGHT_TYPE_HUNGRY) hungryPeeps++; } - return (hungryPeeps > 15); } /** At least 4 restrooms, 1 restroom per 128 guests and no more than 16 guests who think they need the restroom. */ static bool award_is_deserved_best_restrooms([[maybe_unused]] int32_t activeAwardTypes) { - uint32_t i, numRestrooms, guestsWhoNeedRestroom; - Ride* ride; - uint16_t spriteIndex; - Peep* peep; - // Count open restrooms - numRestrooms = 0; - FOR_ALL_RIDES (i, ride) - { - if (ride->type == RIDE_TYPE_TOILETS && ride->status == RIDE_STATUS_OPEN) - numRestrooms++; - } + const auto& rideManager = GetRideManager(); + auto numRestrooms = (size_t)std::count_if(rideManager.begin(), rideManager.end(), [](const Ride& ride) { + return ride.type == RIDE_TYPE_TOILETS && ride.status == RIDE_STATUS_OPEN; + }); // At least 4 open restrooms if (numRestrooms < 4) @@ -438,7 +414,9 @@ static bool award_is_deserved_best_restrooms([[maybe_unused]] int32_t activeAwar return false; // Count number of guests who are thinking they need the restroom - guestsWhoNeedRestroom = 0; + auto guestsWhoNeedRestroom = 0; + uint16_t spriteIndex; + Peep* peep; FOR_ALL_GUESTS (spriteIndex, peep) { if (peep->outside_of_park != 0) @@ -447,35 +425,30 @@ static bool award_is_deserved_best_restrooms([[maybe_unused]] int32_t activeAwar if (peep->thoughts[0].freshness <= 5 && peep->thoughts[0].type == PEEP_THOUGHT_TYPE_BATHROOM) guestsWhoNeedRestroom++; } - return (guestsWhoNeedRestroom <= 16); } /** More than half of the rides have satisfaction <= 6 and park rating <= 650. */ static bool award_is_deserved_most_disappointing(int32_t activeAwardTypes) { - uint32_t i, countedRides, disappointingRides; - Ride* ride; - if (activeAwardTypes & (1 << PARK_AWARD_BEST_VALUE)) return false; if (gParkRating > 650) return false; // Count the number of disappointing rides - countedRides = 0; - disappointingRides = 0; - - FOR_ALL_RIDES (i, ride) + auto countedRides = 0; + auto disappointingRides = 0; + for (const auto& ride : GetRideManager()) { - if (ride->excitement == RIDE_RATING_UNDEFINED || ride->popularity == 0xFF) - continue; - - countedRides++; - - // Unpopular - if (ride->popularity <= 6) - disappointingRides++; + if (ride_has_ratings(&ride) && ride.popularity != 0xFF) + { + countedRides++; + if (ride.popularity <= 6) + { + disappointingRides++; + } + } } // Half of the rides are disappointing @@ -485,20 +458,16 @@ static bool award_is_deserved_most_disappointing(int32_t activeAwardTypes) /** At least 6 open water rides. */ static bool award_is_deserved_best_water_rides([[maybe_unused]] int32_t activeAwardTypes) { - int32_t i, waterRides; - Ride* ride; - rct_ride_entry* rideEntry; - - waterRides = 0; - FOR_ALL_RIDES (i, ride) + auto waterRides = 0; + for (const auto& ride : GetRideManager()) { - rideEntry = get_ride_entry(ride->subtype); + auto rideEntry = ride.GetRideEntry(); if (rideEntry == nullptr) { continue; } - if (ride->status != RIDE_STATUS_OPEN || (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) + if (ride.status != RIDE_STATUS_OPEN || (ride.lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { continue; } @@ -517,22 +486,19 @@ static bool award_is_deserved_best_water_rides([[maybe_unused]] int32_t activeAw /** At least 6 custom designed rides. */ static bool award_is_deserved_best_custom_designed_rides(int32_t activeAwardTypes) { - int32_t i, customDesignedRides; - Ride* ride; - if (activeAwardTypes & (1 << PARK_AWARD_MOST_DISAPPOINTING)) return false; - customDesignedRides = 0; - FOR_ALL_RIDES (i, ride) + auto customDesignedRides = 0; + for (const auto& ride : GetRideManager()) { - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_TRACK)) + if (!ride_type_has_flag(ride.type, RIDE_TYPE_FLAG_HAS_TRACK)) continue; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_NOT_CUSTOM_DESIGN) + if (ride.lifecycle_flags & RIDE_LIFECYCLE_NOT_CUSTOM_DESIGN) continue; - if (ride->excitement < RIDE_RATING(5, 50)) + if (ride.excitement < RIDE_RATING(5, 50)) continue; - if (ride->status != RIDE_STATUS_OPEN || (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) + if (ride.status != RIDE_STATUS_OPEN || (ride.lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) continue; customDesignedRides++; @@ -541,29 +507,25 @@ static bool award_is_deserved_best_custom_designed_rides(int32_t activeAwardType return (customDesignedRides >= 6); } -/** At least 5 colourful rides and more than half of the rides are colourful. */ -static constexpr const uint8_t dazzling_ride_colours[] = { COLOUR_BRIGHT_PURPLE, COLOUR_BRIGHT_GREEN, COLOUR_LIGHT_ORANGE, - COLOUR_BRIGHT_PINK }; - static bool award_is_deserved_most_dazzling_ride_colours(int32_t activeAwardTypes) { - int32_t i, countedRides, colourfulRides; - Ride* ride; - uint8_t mainTrackColour; + /** At least 5 colourful rides and more than half of the rides are colourful. */ + static constexpr const colour_t dazzling_ride_colours[] = { COLOUR_BRIGHT_PURPLE, COLOUR_BRIGHT_GREEN, COLOUR_LIGHT_ORANGE, + COLOUR_BRIGHT_PINK }; if (activeAwardTypes & (1 << PARK_AWARD_MOST_DISAPPOINTING)) return false; - countedRides = 0; - colourfulRides = 0; - FOR_ALL_RIDES (i, ride) + auto countedRides = 0; + auto colourfulRides = 0; + for (const auto& ride : GetRideManager()) { - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_TRACK)) + if (!ride_type_has_flag(ride.type, RIDE_TYPE_FLAG_HAS_TRACK)) continue; countedRides++; - mainTrackColour = ride->track_colour[0].main; + auto mainTrackColour = ride.track_colour[0].main; for (auto dazzling_ride_colour : dazzling_ride_colours) { if (mainTrackColour == dazzling_ride_colour) @@ -603,20 +565,16 @@ static bool award_is_deserved_most_confusing_layout([[maybe_unused]] int32_t act /** At least 10 open gentle rides. */ static bool award_is_deserved_best_gentle_rides([[maybe_unused]] int32_t activeAwardTypes) { - int32_t i, gentleRides; - Ride* ride; - rct_ride_entry* rideEntry; - - gentleRides = 0; - FOR_ALL_RIDES (i, ride) + auto gentleRides = 0; + for (const auto& ride : GetRideManager()) { - rideEntry = get_ride_entry(ride->subtype); + auto rideEntry = ride.GetRideEntry(); if (rideEntry == nullptr) { continue; } - if (ride->status != RIDE_STATUS_OPEN || (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) + if (ride.status != RIDE_STATUS_OPEN || (ride.lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { continue; } diff --git a/src/openrct2/management/Award.h b/src/openrct2/management/Award.h index a9ca3725a0..33b901b81c 100644 --- a/src/openrct2/management/Award.h +++ b/src/openrct2/management/Award.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/management/Finance.cpp b/src/openrct2/management/Finance.cpp index b801c63bf7..80b0e46934 100644 --- a/src/openrct2/management/Finance.cpp +++ b/src/openrct2/management/Finance.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -75,7 +75,7 @@ bool finance_check_money_required(uint32_t flags) return false; if (gScreenFlags & SCREEN_FLAGS_EDITOR) return false; - if (flags & GAME_COMMAND_FLAG_5) + if (flags & GAME_COMMAND_FLAG_NO_SPEND) return false; if (flags & GAME_COMMAND_FLAG_GHOST) return false; @@ -89,13 +89,7 @@ bool finance_check_money_required(uint32_t flags) */ bool finance_check_affordability(money32 cost, uint32_t flags) { - if (cost <= 0) - return true; - if (finance_check_money_required(flags) == false) - return true; - if (cost > gCash) - return false; - return true; + return cost <= 0 || !finance_check_money_required(flags) || cost <= gCash; } /** @@ -185,30 +179,27 @@ void finance_pay_interest() */ void finance_pay_ride_upkeep() { - int32_t i; - Ride* ride; - - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED)) + if (!(ride.lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED)) { - ride_renew(ride); + ride.Renew(); } - if (ride->status != RIDE_STATUS_CLOSED && !(gParkFlags & PARK_FLAGS_NO_MONEY)) + if (ride.status != RIDE_STATUS_CLOSED && !(gParkFlags & PARK_FLAGS_NO_MONEY)) { - int16_t upkeep = ride->upkeep_cost; + int16_t upkeep = ride.upkeep_cost; if (upkeep != -1) { - ride->total_profit -= upkeep; - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; + ride.total_profit -= upkeep; + ride.window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; finance_payment(upkeep, RCT_EXPENDITURE_TYPE_RIDE_RUNNING_COSTS); } } - if (ride->last_crash_type != RIDE_CRASH_TYPE_NONE) + if (ride.last_crash_type != RIDE_CRASH_TYPE_NONE) { - ride->last_crash_type--; + ride.last_crash_type--; } } } @@ -289,13 +280,11 @@ void finance_update_daily_profit() current_profit -= current_loan / 600; // Ride costs - Ride* ride; - int32_t i; - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (ride->status != RIDE_STATUS_CLOSED && ride->upkeep_cost != MONEY16_UNDEFINED) + if (ride.status != RIDE_STATUS_CLOSED && ride.upkeep_cost != MONEY16_UNDEFINED) { - current_profit -= 2 * ride->upkeep_cost; + current_profit -= 2 * ride.upkeep_cost; } } } diff --git a/src/openrct2/management/Finance.h b/src/openrct2/management/Finance.h index 2a7191b3d7..7277a591e3 100644 --- a/src/openrct2/management/Finance.h +++ b/src/openrct2/management/Finance.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/management/Marketing.cpp b/src/openrct2/management/Marketing.cpp index 733b924834..8c650af40b 100644 --- a/src/openrct2/management/Marketing.cpp +++ b/src/openrct2/management/Marketing.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -73,13 +73,15 @@ static void marketing_raise_finished_notification(const MarketingCampaign& campa // This sets the string parameters for the marketing types that have an argument. if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE) { - Ride* ride = get_ride(campaign.RideId); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + auto ride = get_ride(campaign.RideId); + if (ride != nullptr) + { + ride->FormatNameTo(gCommonFormatArgs); + } } else if (campaign.Type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE) { - set_format_arg(0, rct_string_id, ShopItemStringIds[campaign.ShopItemType].plural); + set_format_arg(0, rct_string_id, ShopItems[campaign.ShopItemType].Naming.Plural); } news_item_add_to_queue(NEWS_ITEM_MONEY, MarketingCampaignNames[campaign.Type][2], 0); @@ -162,10 +164,6 @@ void marketing_set_guest_campaign(Peep* peep, int32_t campaignType) bool marketing_is_campaign_type_applicable(int32_t campaignType) { - int32_t i; - Ride* ride; - rct_ride_entry* rideEntry; - switch (campaignType) { case ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE: @@ -181,9 +179,9 @@ bool marketing_is_campaign_type_applicable(int32_t campaignType) // fall-through case ADVERTISING_CAMPAIGN_RIDE: // Check if any rides exist - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (gRideClassifications[ride->type] == RIDE_CLASS_RIDE) + if (ride.IsRide()) { return true; } @@ -192,17 +190,16 @@ bool marketing_is_campaign_type_applicable(int32_t campaignType) case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE: // Check if any food or drink stalls exist - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - rideEntry = get_ride_entry(ride->subtype); - if (rideEntry == nullptr) + auto rideEntry = ride.GetRideEntry(); + if (rideEntry != nullptr) { - continue; - } - if (shop_item_is_food_or_drink(rideEntry->shop_item) - || shop_item_is_food_or_drink(rideEntry->shop_item_secondary)) - { - return true; + if (shop_item_is_food_or_drink(rideEntry->shop_item) + || shop_item_is_food_or_drink(rideEntry->shop_item_secondary)) + { + return true; + } } } return false; diff --git a/src/openrct2/management/Marketing.h b/src/openrct2/management/Marketing.h index dd082abfb7..36c931754b 100644 --- a/src/openrct2/management/Marketing.h +++ b/src/openrct2/management/Marketing.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -36,6 +36,7 @@ enum enum { + CAMPAIGN_FIRST_WEEK_FLAG = (1 << 6), CAMPAIGN_ACTIVE_FLAG = (1 << 7) }; diff --git a/src/openrct2/management/NewsItem.cpp b/src/openrct2/management/NewsItem.cpp index 82c4acaed6..c7d9a3361d 100644 --- a/src/openrct2/management/NewsItem.cpp +++ b/src/openrct2/management/NewsItem.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,6 +14,7 @@ #include "../OpenRCT2.h" #include "../audio/audio.h" #include "../interface/Window.h" +#include "../interface/Window_internal.h" #include "../localisation/Date.h" #include "../localisation/Localisation.h" #include "../management/Research.h" @@ -100,7 +101,7 @@ static void news_item_tick_current() if (ticks == 1 && (gScreenFlags == SCREEN_FLAGS_PLAYING)) { // Play sound - audio_play_sound(SOUND_NEWS_ITEM, 0, context_get_width() / 2); + audio_play_sound(SoundId::NewsItem, 0, context_get_width() / 2); } } @@ -216,14 +217,14 @@ void news_item_get_subject_location(int32_t type, int32_t subject, int32_t* x, i { case NEWS_ITEM_RIDE: ride = get_ride(subject); - if (ride->overall_view.xy == RCT_XY8_UNDEFINED) + if (ride == nullptr || ride->overall_view.xy == RCT_XY8_UNDEFINED) { *x = LOCATION_NULL; break; } *x = ride->overall_view.x * 32 + 16; *y = ride->overall_view.y * 32 + 16; - *z = tile_element_height(*x, *y); + *z = tile_element_height({ *x, *y }); break; case NEWS_ITEM_PEEP_ON_RIDE: peep = GET_PEEP(subject); @@ -241,7 +242,7 @@ void news_item_get_subject_location(int32_t type, int32_t subject, int32_t* x, i // Find which ride peep is on ride = get_ride(peep->current_ride); - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) + if (ride == nullptr || !(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) { *x = LOCATION_NULL; break; @@ -267,7 +268,7 @@ void news_item_get_subject_location(int32_t type, int32_t subject, int32_t* x, i case NEWS_ITEM_BLANK: *x = subject; *y = subject >> 16; - *z = tile_element_height(*x, *y); + *z = tile_element_height({ *x, *y }); break; default: *x = LOCATION_NULL; @@ -371,7 +372,7 @@ void news_item_open_subject(int32_t type, int32_t subject) window = window_find_by_class(WC_TOP_TOOLBAR); if (window != nullptr) { - window_invalidate(window); + window->Invalidate(); if (!tool_set(window, WC_TOP_TOOLBAR__WIDX_SCENERY, TOOL_ARROW)) { input_set_flag(INPUT_FLAG_6, true); diff --git a/src/openrct2/management/NewsItem.h b/src/openrct2/management/NewsItem.h index 7b24335af8..22be1657cf 100644 --- a/src/openrct2/management/NewsItem.h +++ b/src/openrct2/management/NewsItem.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/management/Research.cpp b/src/openrct2/management/Research.cpp index bf7c5c878e..467f2a6373 100644 --- a/src/openrct2/management/Research.cpp +++ b/src/openrct2/management/Research.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -46,13 +46,15 @@ uint8_t gResearchFundingLevel; uint8_t gResearchPriorities; uint16_t gResearchProgress; uint8_t gResearchProgressStage; -rct_research_item gResearchLastItem; +ResearchItem gResearchLastItem; uint8_t gResearchExpectedMonth; uint8_t gResearchExpectedDay; -rct_research_item gResearchNextItem; +ResearchItem gResearchNextItem; // 0x01358844[500] -rct_research_item gResearchItems[MAX_RESEARCH_ITEMS]; +ResearchItem gResearchItems[MAX_RESEARCH_ITEMS]; +std::vector gResearchItemsUninvented; +std::vector gResearchItemsInvented; // 0x00EE787C uint8_t gResearchUncompletedCategories; @@ -69,9 +71,8 @@ bool gSilentResearch = false; */ void research_reset_items() { - gResearchItems[0].rawValue = RESEARCHED_ITEMS_SEPARATOR; - gResearchItems[1].rawValue = RESEARCHED_ITEMS_END; - gResearchItems[2].rawValue = RESEARCHED_ITEMS_END_2; + gResearchItemsUninvented.clear(); + gResearchItemsInvented.clear(); } /** @@ -81,13 +82,10 @@ void research_reset_items() void research_update_uncompleted_types() { int32_t uncompletedResearchTypes = 0; - rct_research_item* researchItem = gResearchItems; - while (researchItem++->rawValue != RESEARCHED_ITEMS_SEPARATOR) - ; - for (; researchItem->rawValue != RESEARCHED_ITEMS_END; researchItem++) + for (auto const& researchItem : gResearchItemsUninvented) { - uncompletedResearchTypes |= (1 << researchItem->category); + uncompletedResearchTypes |= (1 << researchItem.category); } gResearchUncompletedCategories = uncompletedResearchTypes; @@ -127,64 +125,62 @@ static void research_invalidate_related_windows() window_invalidate_by_class(WC_RESEARCH); } +static void research_mark_as_fully_completed() +{ + gResearchProgress = 0; + gResearchProgressStage = RESEARCH_STAGE_FINISHED_ALL; + research_invalidate_related_windows(); + // Reset funding to 0 if no more rides. + auto gameAction = ParkSetResearchFundingAction(gResearchPriorities, 0); + GameActions::Execute(&gameAction); +} + /** * * rct2: 0x00684BE5 */ static void research_next_design() { - rct_research_item *firstUnresearchedItem, *researchItem, tmp; - int32_t ignoreActiveResearchTypes; - - // Skip already researched items - firstUnresearchedItem = gResearchItems; - while (firstUnresearchedItem->rawValue != RESEARCHED_ITEMS_SEPARATOR) + if (gResearchItemsUninvented.empty()) { - firstUnresearchedItem++; + research_mark_as_fully_completed(); + return; } - ignoreActiveResearchTypes = 0; - researchItem = firstUnresearchedItem; + ResearchItem researchItem; + + bool ignoreActiveResearchTypes = false; + auto it = gResearchItemsUninvented.begin(); for (;;) { - researchItem++; - if (researchItem->rawValue == RESEARCHED_ITEMS_END) + researchItem = *it; + if (it == gResearchItemsUninvented.end()) { if (!ignoreActiveResearchTypes) { - ignoreActiveResearchTypes = 1; - researchItem = firstUnresearchedItem; + ignoreActiveResearchTypes = true; + it = gResearchItemsUninvented.begin(); continue; } else { - gResearchProgress = 0; - gResearchProgressStage = RESEARCH_STAGE_FINISHED_ALL; - research_invalidate_related_windows(); - // Reset funding to 0 if no more rides. - auto gameAction = ParkSetResearchFundingAction(gResearchPriorities, 0); - GameActions::Execute(&gameAction); + research_mark_as_fully_completed(); return; } } - else if (ignoreActiveResearchTypes || (gResearchPriorities & (1 << researchItem->category))) + else if (ignoreActiveResearchTypes || (gResearchPriorities & (1 << researchItem.category))) { break; } + it++; } - gResearchNextItem = *researchItem; + gResearchNextItem = researchItem; gResearchProgress = 0; gResearchProgressStage = RESEARCH_STAGE_DESIGNING; - // Bubble research item up until it is above the researched items separator - do - { - tmp = *researchItem; - *researchItem = *(researchItem - 1); - *(researchItem - 1) = tmp; - researchItem--; - } while ((researchItem + 1)->rawValue != RESEARCHED_ITEMS_SEPARATOR); + gResearchItemsUninvented.erase(it); + gResearchItemsInvented.push_back(researchItem); research_invalidate_related_windows(); } @@ -193,7 +189,7 @@ static void research_next_design() * * rct2: 0x006848D4 */ -void research_finish_item(rct_research_item* researchItem) +void research_finish_item(ResearchItem* researchItem) { gResearchLastItem = *researchItem; research_invalidate_related_windows(); @@ -229,15 +225,15 @@ void research_finish_item(rct_research_item* researchItem) ride_entry_set_invented(rideEntryIndex); bool seenRideEntry[MAX_RIDE_OBJECTS]{}; - - rct_research_item* researchItem2 = gResearchItems; - for (; researchItem2->rawValue != RESEARCHED_ITEMS_END; researchItem2++) + for (auto const& researchItem3 : gResearchItemsUninvented) { - if (researchItem2->rawValue != RESEARCHED_ITEMS_SEPARATOR && researchItem2->type == RESEARCH_ENTRY_TYPE_RIDE) - { - uint8_t index = researchItem2->entryIndex; - seenRideEntry[index] = true; - } + uint8_t index = researchItem3.entryIndex; + seenRideEntry[index] = true; + } + for (auto const& researchItem3 : gResearchItemsInvented) + { + uint8_t index = researchItem3.entryIndex; + seenRideEntry[index] = true; } // RCT2 made non-separated vehicles available at once, by removing all but one from research. @@ -386,54 +382,12 @@ void research_update() } } -void research_process_random_items() -{ - rct_research_item* research = gResearchItems; - for (; research->rawValue != RESEARCHED_ITEMS_END; research++) - { - } - - research++; - for (; research->rawValue != RESEARCHED_ITEMS_END_2; research += 2) - { - if (scenario_rand() & 1) - { - continue; - } - - rct_research_item* edx = nullptr; - rct_research_item* ebp = nullptr; - rct_research_item* inner_research = gResearchItems; - do - { - if (research->rawValue == inner_research->rawValue) - { - edx = inner_research; - } - if ((research + 1)->rawValue == inner_research->rawValue) - { - ebp = inner_research; - } - } while ((inner_research++)->rawValue != RESEARCHED_ITEMS_END); - assert(edx != nullptr); - edx->rawValue = research->rawValue; - assert(ebp != nullptr); - ebp->rawValue = (research + 1)->rawValue; - - uint8_t cat = edx->category; - edx->category = ebp->category; - ebp->category = cat; - } -} - /** * * rct2: 0x00684AC3 */ void research_reset_current_item() { - research_process_random_items(); - set_every_ride_type_not_invented(); set_every_ride_entry_not_invented(); @@ -441,9 +395,9 @@ void research_reset_current_item() set_all_scenery_items_invented(); set_all_scenery_groups_not_invented(); - for (rct_research_item* research = gResearchItems; research->rawValue != RESEARCHED_ITEMS_SEPARATOR; research++) + for (auto& researchItem : gResearchItemsInvented) { - research_finish_item(research); + research_finish_item(&researchItem); } gResearchLastItem.rawValue = RESEARCHED_ITEMS_SEPARATOR; @@ -455,29 +409,9 @@ void research_reset_current_item() * * rct2: 0x006857FA */ -static void research_insert_unresearched(int32_t rawValue, int32_t category) +static void research_insert_unresearched(int32_t rawValue, uint8_t category) { - rct_research_item *researchItem, *researchItem2; - - researchItem = gResearchItems; - do - { - if (researchItem->rawValue == RESEARCHED_ITEMS_END) - { - // Insert slot - researchItem2 = researchItem; - while (researchItem2->rawValue != RESEARCHED_ITEMS_END_2) - { - researchItem2++; - } - memmove(researchItem + 1, researchItem, (researchItem2 - researchItem + 1) * sizeof(rct_research_item)); - - // Place new item - researchItem->rawValue = rawValue; - researchItem->category = category; - break; - } - } while (rawValue != (researchItem++)->rawValue); + gResearchItemsUninvented.push_back({ rawValue, category }); } /** @@ -486,52 +420,37 @@ static void research_insert_unresearched(int32_t rawValue, int32_t category) */ static void research_insert_researched(int32_t rawValue, uint8_t category) { - rct_research_item *researchItem, *researchItem2; - - researchItem = gResearchItems; // First check to make sure that entry is not already accounted for - for (; researchItem->rawValue != RESEARCHED_ITEMS_END; researchItem++) + ResearchItem item = { rawValue, category }; + if (item.Exists()) { - if ((researchItem->rawValue & 0xFFFFFF) == (rawValue & 0xFFFFFF)) - { - return; - } + return; } - researchItem = gResearchItems; - do - { - if (researchItem->rawValue == RESEARCHED_ITEMS_SEPARATOR) - { - // Insert slot - researchItem2 = researchItem; - while (researchItem2->rawValue != RESEARCHED_ITEMS_END_2) - { - researchItem2++; - } - memmove(researchItem + 1, researchItem, (researchItem2 - researchItem + 1) * sizeof(rct_research_item)); - // Place new item - researchItem->rawValue = rawValue; - researchItem->category = category; - break; - } - } while (rawValue != (researchItem++)->rawValue); + gResearchItemsInvented.push_back(item); } /** * * rct2: 0x006857CF */ -void research_remove(rct_research_item* researchItem) +void research_remove(ResearchItem* researchItem) { - for (rct_research_item* researchItem2 = gResearchItems; researchItem2->rawValue != RESEARCHED_ITEMS_END; researchItem2++) + for (auto it = gResearchItemsUninvented.begin(); it != gResearchItemsUninvented.end(); it++) { - if (researchItem2->rawValue == researchItem->rawValue) + auto& researchItem2 = *it; + if (researchItem2.Equals(researchItem)) { - do - { - *researchItem2 = *(researchItem2 + 1); - } while (researchItem2++->rawValue != RESEARCHED_ITEMS_END_2); + gResearchItemsUninvented.erase(it); + return; + } + } + for (auto it = gResearchItemsInvented.begin(); it != gResearchItemsInvented.end(); it++) + { + auto& researchItem2 = *it; + if (researchItem2.Equals(researchItem)) + { + gResearchItemsInvented.erase(it); return; } } @@ -773,7 +692,7 @@ void set_every_ride_entry_not_invented() * * rct2: 0x0068563D */ -rct_string_id research_item_get_name(const rct_research_item* researchItem) +rct_string_id research_item_get_name(const ResearchItem* researchItem) { if (researchItem->type == RESEARCH_ENTRY_TYPE_RIDE) { @@ -821,54 +740,76 @@ rct_string_id research_get_friendly_base_ride_type_name(uint8_t trackType, rct_r * * rct2: 0x00685A79 * Do not use the research list outside of the inventions list window with the flags + * Clears flags like "always researched". */ void research_remove_flags() { - for (rct_research_item* research = gResearchItems; research->rawValue != RESEARCHED_ITEMS_END_2; research++) + for (auto& researchItem : gResearchItemsUninvented) { - // Clear the always researched flags. - if (research->rawValue > RESEARCHED_ITEMS_SEPARATOR) - { - research->flags = 0; - } + researchItem.flags = 0; + } + for (auto& researchItem : gResearchItemsInvented) + { + researchItem.flags = 0; } } void research_fix() { // Fix invalid research items - for (int32_t i = 0; i < MAX_RESEARCH_ITEMS; i++) + for (auto it = gResearchItemsInvented.begin(); it != gResearchItemsInvented.end();) { - rct_research_item* researchItem = &gResearchItems[i]; - if (researchItem->rawValue == RESEARCHED_ITEMS_SEPARATOR) - continue; - if (researchItem->rawValue == RESEARCHED_ITEMS_END) + auto& researchItem = *it; + if (researchItem.type == RESEARCH_ENTRY_TYPE_RIDE) { - if (i == MAX_RESEARCH_ITEMS - 1) - { - (--researchItem)->rawValue = RESEARCHED_ITEMS_END; - } - (++researchItem)->rawValue = RESEARCHED_ITEMS_END_2; - break; - } - if (researchItem->rawValue == RESEARCHED_ITEMS_END_2) - break; - if (researchItem->type == RESEARCH_ENTRY_TYPE_RIDE) - { - rct_ride_entry* rideEntry = get_ride_entry(researchItem->entryIndex); + rct_ride_entry* rideEntry = get_ride_entry(researchItem.entryIndex); if (rideEntry == nullptr) { - research_remove(researchItem); - i--; + it = gResearchItemsInvented.erase(it); + } + else + { + it++; } } else { - rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem->rawValue); + rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem.rawValue); if (sceneryGroupEntry == nullptr) { - research_remove(researchItem); - i--; + it = gResearchItemsInvented.erase(it); + } + else + { + it++; + } + } + } + for (auto it = gResearchItemsUninvented.begin(); it != gResearchItemsUninvented.end();) + { + auto& researchItem = *it; + if (researchItem.type == RESEARCH_ENTRY_TYPE_RIDE) + { + rct_ride_entry* rideEntry = get_ride_entry(researchItem.entryIndex); + if (rideEntry == nullptr) + { + it = gResearchItemsUninvented.erase(it); + } + else + { + it++; + } + } + else + { + rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem.rawValue); + if (sceneryGroupEntry == nullptr) + { + it = gResearchItemsUninvented.erase(it); + } + else + { + it++; } } } @@ -914,51 +855,18 @@ void research_fix() void research_items_make_all_unresearched() { - rct_research_item *researchItem, *nextResearchItem, researchItemTemp; - - int32_t sorted; - do - { - sorted = 1; - for (researchItem = gResearchItems; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - if (research_item_is_always_researched(researchItem)) - continue; - - nextResearchItem = researchItem + 1; - if (nextResearchItem->rawValue == RESEARCHED_ITEMS_SEPARATOR - || research_item_is_always_researched(nextResearchItem)) - { - // Bubble up always researched item or separator - researchItemTemp = *researchItem; - *researchItem = *nextResearchItem; - *nextResearchItem = researchItemTemp; - sorted = 0; - - if (researchItem->rawValue == RESEARCHED_ITEMS_SEPARATOR) - break; - } - } - } while (!sorted); + gResearchItemsUninvented.insert( + gResearchItemsUninvented.end(), std::make_move_iterator(gResearchItemsInvented.begin()), + std::make_move_iterator(gResearchItemsInvented.end())); + gResearchItemsInvented.clear(); } void research_items_make_all_researched() { - rct_research_item *researchItem, researchItemTemp; - - // Find separator - for (researchItem = gResearchItems; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - } - - // Move separator below all items - for (; (researchItem + 1)->rawValue != RESEARCHED_ITEMS_END; researchItem++) - { - // Swap separator with research item - researchItemTemp = *researchItem; - *researchItem = *(researchItem + 1); - *(researchItem + 1) = researchItemTemp; - } + gResearchItemsInvented.insert( + gResearchItemsInvented.end(), std::make_move_iterator(gResearchItemsUninvented.begin()), + std::make_move_iterator(gResearchItemsUninvented.end())); + gResearchItemsUninvented.clear(); } /** @@ -967,52 +875,41 @@ void research_items_make_all_researched() */ void research_items_shuffle() { - rct_research_item *researchItem, *researchOrderBase, researchItemTemp; - int32_t i, numNonResearchedItems; - - // Skip pre-researched items - for (researchItem = gResearchItems; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - } - researchItem++; - researchOrderBase = researchItem; - - // Count non pre-researched items - numNonResearchedItems = 0; - for (; researchItem->rawValue != RESEARCHED_ITEMS_END; researchItem++) - numNonResearchedItems++; - - // Shuffle list - for (i = 0; i < numNonResearchedItems; i++) - { - int32_t ri = util_rand() % numNonResearchedItems; - if (ri == i) - continue; - - researchItemTemp = researchOrderBase[i]; - researchOrderBase[i] = researchOrderBase[ri]; - researchOrderBase[ri] = researchItemTemp; - } + std::shuffle(std::begin(gResearchItemsUninvented), std::end(gResearchItemsUninvented), std::default_random_engine{}); } -bool research_item_is_always_researched(rct_research_item* researchItem) +bool research_item_is_always_researched(const ResearchItem* researchItem) { return (researchItem->flags & (RESEARCH_ENTRY_FLAG_RIDE_ALWAYS_RESEARCHED | RESEARCH_ENTRY_FLAG_SCENERY_SET_ALWAYS_RESEARCHED)) != 0; } -bool rct_research_item::IsInventedEndMarker() const +bool ResearchItem::IsInventedEndMarker() const { return rawValue == RESEARCHED_ITEMS_SEPARATOR; } -bool rct_research_item::IsUninventedEndMarker() const +bool ResearchItem::Equals(const ResearchItem* otherItem) const { - return rawValue == RESEARCHED_ITEMS_END; + return (entryIndex == otherItem->entryIndex && baseRideType == otherItem->baseRideType && type == otherItem->type); } -bool rct_research_item::IsRandomEndMarker() const +bool ResearchItem::Exists() const { - return rawValue == RESEARCHED_ITEMS_END_2; + for (auto const& researchItem : gResearchItemsUninvented) + { + if (researchItem.Equals(this)) + { + return true; + } + } + for (auto const& researchItem : gResearchItemsInvented) + { + if (researchItem.Equals(this)) + { + return true; + } + } + return false; } diff --git a/src/openrct2/management/Research.h b/src/openrct2/management/Research.h index 815e40d3ae..f222abbdf3 100644 --- a/src/openrct2/management/Research.h +++ b/src/openrct2/management/Research.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -15,8 +15,7 @@ struct rct_ride_entry; -#pragma pack(push, 1) -struct rct_research_item +struct ResearchItem { // Bit 16 (0: scenery entry, 1: ride entry) union @@ -33,11 +32,9 @@ struct rct_research_item uint8_t category; bool IsInventedEndMarker() const; - bool IsRandomEndMarker() const; - bool IsUninventedEndMarker() const; + bool Equals(const ResearchItem* otherItem) const; + bool Exists() const; }; -assert_struct_size(rct_research_item, 5); -#pragma pack(pop) enum { @@ -100,10 +97,11 @@ extern uint16_t gResearchProgress; extern uint8_t gResearchProgressStage; extern uint8_t gResearchExpectedMonth; extern uint8_t gResearchExpectedDay; -extern rct_research_item gResearchLastItem; -extern rct_research_item gResearchNextItem; +extern ResearchItem gResearchLastItem; +extern ResearchItem gResearchNextItem; -extern rct_research_item gResearchItems[MAX_RESEARCH_ITEMS]; +extern std::vector gResearchItemsUninvented; +extern std::vector gResearchItemsInvented; extern uint8_t gResearchUncompletedCategories; extern bool gSilentResearch; @@ -113,11 +111,10 @@ void research_update(); void research_reset_current_item(); void research_populate_list_random(); void research_populate_list_researched(); -void research_process_random_items(); -void research_finish_item(rct_research_item* researchItem); +void research_finish_item(ResearchItem* researchItem); void research_insert(int32_t researched, int32_t rawValue, uint8_t category); -void research_remove(rct_research_item* researchItem); +void research_remove(ResearchItem* researchItem); void research_insert_ride_entry(uint8_t entryIndex, bool researched); void research_insert_scenery_group_entry(uint8_t entryIndex, bool researched); @@ -139,7 +136,7 @@ void set_every_ride_type_invented(); void set_every_ride_type_not_invented(); void set_every_ride_entry_invented(); void set_every_ride_entry_not_invented(); -rct_string_id research_item_get_name(const rct_research_item* researchItem); +rct_string_id research_item_get_name(const ResearchItem* researchItem); rct_string_id research_get_friendly_base_ride_type_name(uint8_t trackType, rct_ride_entry* rideEntry); void research_remove_flags(); void research_fix(); @@ -147,4 +144,4 @@ void research_fix(); void research_items_make_all_unresearched(); void research_items_make_all_researched(); void research_items_shuffle(); -bool research_item_is_always_researched(rct_research_item* researchItem); +bool research_item_is_always_researched(const ResearchItem* researchItem); diff --git a/src/openrct2/network/DiscordService.cpp b/src/openrct2/network/DiscordService.cpp index 1e4fa5b615..1fd3d00b2b 100644 --- a/src/openrct2/network/DiscordService.cpp +++ b/src/openrct2/network/DiscordService.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,6 +12,7 @@ # include "DiscordService.h" # include "../Context.h" +# include "../GameState.h" # include "../OpenRCT2.h" # include "../core/Console.hpp" # include "../core/String.hpp" @@ -56,9 +57,12 @@ DiscordService::~DiscordService() static std::string GetParkName() { - utf8 parkName[128] = {}; - format_string(parkName, sizeof(parkName), gParkName, &gParkNameArgs); - return std::string(parkName); + auto gameState = OpenRCT2::GetContext()->GetGameState(); + if (gameState != nullptr) + { + return gameState->GetPark().Name; + } + return {}; } void DiscordService::Update() diff --git a/src/openrct2/network/DiscordService.h b/src/openrct2/network/DiscordService.h index d98b3cace2..0de94ea68d 100644 --- a/src/openrct2/network/DiscordService.h +++ b/src/openrct2/network/DiscordService.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/Http.cpp b/src/openrct2/network/Http.cpp index 6e4b74057d..926f097423 100644 --- a/src/openrct2/network/Http.cpp +++ b/src/openrct2/network/Http.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -27,7 +27,7 @@ # define OPENRCT2_USER_AGENT "OpenRCT2/" OPENRCT2_VERSION -namespace OpenRCT2::Network::Http +namespace OpenRCT2::Networking::Http { Http::Http() { @@ -221,6 +221,6 @@ namespace OpenRCT2::Network::Http return dataSize; } -} // namespace OpenRCT2::Network::Http +} // namespace OpenRCT2::Networking::Http #endif diff --git a/src/openrct2/network/Http.h b/src/openrct2/network/Http.h index 54204e0fb3..1d6f08eb8a 100644 --- a/src/openrct2/network/Http.h +++ b/src/openrct2/network/Http.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -17,7 +17,7 @@ # include # include -namespace OpenRCT2::Network::Http +namespace OpenRCT2::Networking::Http { enum class Status { @@ -68,6 +68,6 @@ namespace OpenRCT2::Network::Http */ size_t DownloadPark(const char* url, void** outData); -} // namespace OpenRCT2::Network::Http +} // namespace OpenRCT2::Networking::Http #endif // DISABLE_HTTP diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index 95cd5e38b4..d175eb0110 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,9 +11,12 @@ #include "../Context.h" #include "../Game.h" +#include "../GameStateSnapshots.h" #include "../OpenRCT2.h" #include "../PlatformEnvironment.h" #include "../actions/LoadOrQuitAction.hpp" +#include "../actions/NetworkModifyGroupAction.hpp" +#include "../actions/PeepPickupAction.hpp" #include "../core/Guard.hpp" #include "../platform/platform.h" #include "../ui/UiContext.h" @@ -31,19 +34,22 @@ // This string specifies which version of network stream current build uses. // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -#define NETWORK_STREAM_VERSION "0" +#define NETWORK_STREAM_VERSION "22" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION static Peep* _pickup_peep = nullptr; static int32_t _pickup_peep_old_x = LOCATION_NULL; +// General chunk size is 63 KiB, this can not be any larger because the packet size is encoded +// with uint16_t and needs some spare room for other data in the packet. +static constexpr uint32_t CHUNK_SIZE = 1024 * 63; + #ifndef DISABLE_NETWORK # include "../Cheats.h" # include "../ParkImporter.h" # include "../Version.h" # include "../actions/GameAction.h" -# include "../actions/PauseToggleAction.hpp" # include "../config/Config.h" # include "../core/Console.hpp" # include "../core/FileStream.hpp" @@ -70,7 +76,7 @@ static int32_t _pickup_peep_old_x = LOCATION_NULL; # include "NetworkPlayer.h" # include "NetworkServerAdvertiser.h" # include "NetworkUser.h" -# include "TcpSocket.h" +# include "Socket.h" # include # include @@ -85,7 +91,9 @@ static int32_t _pickup_peep_old_x = LOCATION_NULL; # include # include -# pragma comment(lib, "Ws2_32.lib") +# if defined(_WIN32) +# pragma comment(lib, "Ws2_32.lib") +# endif using namespace OpenRCT2; @@ -113,6 +121,7 @@ public: void SetEnvironment(const std::shared_ptr& env); bool Init(); + void Reconnect(); void Close(); bool BeginClient(const std::string& host, uint16_t port); bool BeginServer(uint16_t port, const std::string& address); @@ -125,8 +134,8 @@ public: void Flush(); void ProcessPending(); void ProcessPlayerList(); - void ProcessGameCommands(); - void EnqueueGameAction(const GameAction* action); + void ProcessPlayerInfo(); + void ProcessDisconnectedClients(); std::vector>::iterator GetPlayerIteratorByID(uint8_t id); NetworkPlayer* GetPlayerByID(uint8_t id); std::vector>::iterator GetGroupIteratorByID(uint8_t id); @@ -134,10 +143,13 @@ public: static const char* FormatChat(NetworkPlayer* fromplayer, const char* text); void SendPacketToClients(NetworkPacket& packet, bool front = false, bool gameCmd = false); bool CheckSRAND(uint32_t tick, uint32_t srand0); - void CheckDesynchronizaton(); + bool IsDesynchronised(); + bool CheckDesynchronizaton(); + void RequestStateSnapshot(); + NetworkServerState_t GetServerState() const; void KickPlayer(int32_t playerId); void SetPassword(const char* password); - void ShutdownClient(); + void ServerClientDisconnected(); NetworkGroup* AddGroup(); void RemoveGroup(uint8_t id); uint8_t GetDefaultGroup(); @@ -157,6 +169,8 @@ public: void AppendServerLog(const std::string& s); void CloseServerLog(); + void Client_Send_RequestGameState(uint32_t tick); + void Client_Send_TOKEN(); void Client_Send_AUTH( const std::string& name, const std::string& password, const std::string& pubkey, const std::vector& signature); @@ -165,14 +179,10 @@ public: void Server_Send_MAP(NetworkConnection* connection = nullptr); void Client_Send_CHAT(const char* text); void Server_Send_CHAT(const char* text); - void Client_Send_GAMECMD( - uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi, uint32_t ebp, uint8_t callback); - void Server_Send_GAMECMD( - uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi, uint32_t ebp, uint8_t playerid, - uint8_t callback); void Client_Send_GAME_ACTION(const GameAction* action); void Server_Send_GAME_ACTION(const GameAction* action); void Server_Send_TICK(); + void Server_Send_PLAYERINFO(int32_t playerId); void Server_Send_PLAYERLIST(); void Client_Send_PING(); void Server_Send_PING(); @@ -188,6 +198,7 @@ public: void Server_Send_OBJECTS(NetworkConnection& connection, const std::vector& objects) const; NetworkStats_t GetStats() const; + json_t* GetServerInfoAsJson() const; std::vector> player_list; std::vector> group_list; @@ -195,7 +206,6 @@ public: std::vector _challenge; std::map _gameActionCallbacks; NetworkUserManager _userManager; - std::string ServerName; std::string ServerDescription; std::string ServerGreeting; @@ -204,13 +214,15 @@ public: std::string ServerProviderWebsite; private: + void DecayCooldown(NetworkPlayer* player); void CloseConnection(); bool ProcessConnection(NetworkConnection& connection); void ProcessPacket(NetworkConnection& connection, NetworkPacket& packet); void AddClient(std::unique_ptr&& socket); - void RemoveClient(std::unique_ptr& connection); + void ServerClientDisconnected(std::unique_ptr& connection); + void RemovePlayer(std::unique_ptr& connection); NetworkPlayer* AddPlayer(const std::string& name, const std::string& keyhash); std::string MakePlayerNameUnique(const std::string& name); @@ -221,95 +233,48 @@ private: bool LoadMap(IStream* stream); bool SaveMap(IStream* stream, const std::vector& objects) const; - struct GameCommand - { - GameCommand(uint32_t t, uint32_t* args, uint8_t p, uint8_t cb, uint32_t id) - { - tick = t; - eax = args[0]; - ebx = args[1]; - ecx = args[2]; - edx = args[3]; - esi = args[4]; - edi = args[5]; - ebp = args[6]; - playerid = p; - callback = cb; - action = nullptr; - commandIndex = id; - } - - GameCommand(uint32_t t, std::unique_ptr&& ga, uint32_t id) - { - tick = t; - action = std::move(ga); - commandIndex = id; - } - - ~GameCommand() - { - } - - uint32_t tick = 0; - uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0, esi = 0, edi = 0, ebp = 0; - GameAction::Ptr action; - uint8_t playerid = 0; - uint8_t callback = 0; - uint32_t commandIndex = 0; - bool operator<(const GameCommand& comp) const - { - // First sort by tick - if (tick < comp.tick) - return true; - if (tick > comp.tick) - return false; - - // If the ticks are equal sort by commandIndex - return commandIndex < comp.commandIndex; - } - }; - struct PlayerListUpdate { - uint32_t tick = 0; std::vector players; - - void reset() - { - tick = 0; - players.clear(); - } }; - PlayerListUpdate _pendingPlayerList; + struct ServerTickData_t + { + uint32_t srand0; + uint32_t tick; + std::string spriteHash; + }; + std::map _serverTickData; + std::map _pendingPlayerLists; + std::multimap _pendingPlayerInfo; + bool _playerListInvalidated = false; int32_t mode = NETWORK_MODE_NONE; int32_t status = NETWORK_STATUS_NONE; bool _closeLock = false; bool _requireClose = false; + bool _requireReconnect = false; bool wsa_initialized = false; + bool _clientMapLoaded = false; std::unique_ptr _listenSocket; std::unique_ptr _serverConnection; std::unique_ptr _advertiser; uint16_t listening_port = 0; SOCKET_STATUS _lastConnectStatus = SOCKET_STATUS_CLOSED; - uint32_t last_tick_sent_time = 0; uint32_t last_ping_sent_time = 0; - uint32_t server_tick = 0; - uint32_t server_srand0 = 0; - uint32_t server_srand0_tick = 0; - std::string server_sprite_hash; uint8_t player_id = 0; std::list> client_connection_list; - std::multiset game_command_queue; std::vector chunk_buffer; + std::string _host; + uint16_t _port = 0; std::string _password; - bool _desynchronised = false; + NetworkServerState_t _serverState; + MemoryStream _serverGameState; uint32_t server_connect_time = 0; uint8_t default_group = 0; - uint32_t game_commands_processed_this_tick = 0; - uint32_t _commandId; uint32_t _actionId; + uint32_t _lastUpdateTime = 0; + uint32_t _currentDeltaTime = 0; std::string _chatLogPath; std::string _chatLogFilenameFormat = "%Y%m%d-%H%M%S.txt"; std::string _serverLogPath; @@ -322,17 +287,17 @@ private: private: std::vector client_command_handlers; std::vector server_command_handlers; + void Server_Handle_REQUEST_GAMESTATE(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_AUTH(NetworkConnection& connection, NetworkPacket& packet); void Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& packet); void Server_Client_Joined(const char* name, const std::string& keyhash, NetworkConnection& connection); void Client_Handle_MAP(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_CHAT(NetworkConnection& connection, NetworkPacket& packet); void Server_Handle_CHAT(NetworkConnection& connection, NetworkPacket& packet); - void Client_Handle_GAMECMD(NetworkConnection& connection, NetworkPacket& packet); - void Server_Handle_GAMECMD(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_GAME_ACTION(NetworkConnection& connection, NetworkPacket& packet); void Server_Handle_GAME_ACTION(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_TICK(NetworkConnection& connection, NetworkPacket& packet); + void Client_Handle_PLAYERINFO(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_PLAYERLIST(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_PING(NetworkConnection& connection, NetworkPacket& packet); void Server_Handle_PING(NetworkConnection& connection, NetworkPacket& packet); @@ -346,6 +311,7 @@ private: void Client_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& packet); void Server_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet); + void Client_Handle_GAMESTATE(NetworkConnection& connection, NetworkPacket& packet); void Server_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet); uint8_t* save_for_network(size_t& out_size, const std::vector& objects) const; @@ -361,18 +327,16 @@ Network::Network() wsa_initialized = false; mode = NETWORK_MODE_NONE; status = NETWORK_STATUS_NONE; - last_tick_sent_time = 0; last_ping_sent_time = 0; - _commandId = 0; _actionId = 0; client_command_handlers.resize(NETWORK_COMMAND_MAX, nullptr); client_command_handlers[NETWORK_COMMAND_AUTH] = &Network::Client_Handle_AUTH; client_command_handlers[NETWORK_COMMAND_MAP] = &Network::Client_Handle_MAP; client_command_handlers[NETWORK_COMMAND_CHAT] = &Network::Client_Handle_CHAT; - client_command_handlers[NETWORK_COMMAND_GAMECMD] = &Network::Client_Handle_GAMECMD; client_command_handlers[NETWORK_COMMAND_GAME_ACTION] = &Network::Client_Handle_GAME_ACTION; client_command_handlers[NETWORK_COMMAND_TICK] = &Network::Client_Handle_TICK; client_command_handlers[NETWORK_COMMAND_PLAYERLIST] = &Network::Client_Handle_PLAYERLIST; + client_command_handlers[NETWORK_COMMAND_PLAYERINFO] = &Network::Client_Handle_PLAYERINFO; client_command_handlers[NETWORK_COMMAND_PING] = &Network::Client_Handle_PING; client_command_handlers[NETWORK_COMMAND_PINGLIST] = &Network::Client_Handle_PINGLIST; client_command_handlers[NETWORK_COMMAND_SETDISCONNECTMSG] = &Network::Client_Handle_SETDISCONNECTMSG; @@ -382,15 +346,16 @@ Network::Network() client_command_handlers[NETWORK_COMMAND_GAMEINFO] = &Network::Client_Handle_GAMEINFO; client_command_handlers[NETWORK_COMMAND_TOKEN] = &Network::Client_Handle_TOKEN; client_command_handlers[NETWORK_COMMAND_OBJECTS] = &Network::Client_Handle_OBJECTS; + client_command_handlers[NETWORK_COMMAND_GAMESTATE] = &Network::Client_Handle_GAMESTATE; server_command_handlers.resize(NETWORK_COMMAND_MAX, nullptr); server_command_handlers[NETWORK_COMMAND_AUTH] = &Network::Server_Handle_AUTH; server_command_handlers[NETWORK_COMMAND_CHAT] = &Network::Server_Handle_CHAT; - server_command_handlers[NETWORK_COMMAND_GAMECMD] = &Network::Server_Handle_GAMECMD; server_command_handlers[NETWORK_COMMAND_GAME_ACTION] = &Network::Server_Handle_GAME_ACTION; server_command_handlers[NETWORK_COMMAND_PING] = &Network::Server_Handle_PING; server_command_handlers[NETWORK_COMMAND_GAMEINFO] = &Network::Server_Handle_GAMEINFO; server_command_handlers[NETWORK_COMMAND_TOKEN] = &Network::Server_Handle_TOKEN; server_command_handlers[NETWORK_COMMAND_OBJECTS] = &Network::Server_Handle_OBJECTS; + server_command_handlers[NETWORK_COMMAND_REQUEST_GAMESTATE] = &Network::Server_Handle_REQUEST_GAMESTATE; _chat_log_fs << std::unitbuf; _server_log_fs << std::unitbuf; @@ -419,6 +384,20 @@ bool Network::Init() return true; } +void Network::Reconnect() +{ + if (status != NETWORK_STATUS_NONE) + { + Close(); + } + if (_requireClose) + { + _requireReconnect = true; + return; + } + BeginClient(_host, _port); +} + void Network::Close() { if (status != NETWORK_STATUS_NONE) @@ -437,10 +416,13 @@ void Network::Close() CloseConnection(); client_connection_list.clear(); - game_command_queue.clear(); + GameActions::ClearQueue(); + GameActions::ResumeQueue(); player_list.clear(); group_list.clear(); - _pendingPlayerList.reset(); + _serverTickData.clear(); + _pendingPlayerLists.clear(); + _pendingPlayerInfo.clear(); gfx_invalidate_screen(); @@ -448,6 +430,21 @@ void Network::Close() } } +void Network::DecayCooldown(NetworkPlayer* player) +{ + if (player == nullptr) + return; // No valid connection yet. + + for (auto it = std::begin(player->CooldownTime); it != std::end(player->CooldownTime);) + { + it->second -= _currentDeltaTime; + if (it->second <= 0) + it = player->CooldownTime.erase(it); + else + it++; + } +} + void Network::CloseConnection() { if (mode == NETWORK_MODE_CLIENT) @@ -480,17 +477,28 @@ bool Network::BeginClient(const std::string& host, uint16_t port) mode = NETWORK_MODE_CLIENT; - log_info("Connecting to %s:%u\n", host.c_str(), port); + log_info("Connecting to %s:%u", host.c_str(), port); + _host = host; + _port = port; _serverConnection = std::make_unique(); _serverConnection->Socket = CreateTcpSocket(); _serverConnection->Socket->ConnectAsync(host, port); + _serverState.gamestateSnapshotsEnabled = false; + status = NETWORK_STATUS_CONNECTING; _lastConnectStatus = SOCKET_STATUS_CLOSED; + _clientMapLoaded = false; + _serverTickData.clear(); BeginChatLog(); BeginServerLog(); + // We need to wait for the map load before we execute any actions. + // If the client has the title screen running then theres a potential + // risk of tick collision with the server map and title screen map. + GameActions::SuspendQueue(); + utf8 keyPath[MAX_PATH]; network_get_private_key_path(keyPath, sizeof(keyPath), gConfigNetwork.player_name); if (!platform_file_exists(keyPath)) @@ -590,7 +598,7 @@ bool Network::BeginServer(uint16_t port, const std::string& address) ServerProviderEmail = gConfigNetwork.provider_email; ServerProviderWebsite = gConfigNetwork.provider_website; - cheats_reset(); + CheatsReset(); LoadGroups(); BeginChatLog(); BeginServerLog(); @@ -615,16 +623,8 @@ bool Network::BeginServer(uint16_t port, const std::string& address) status = NETWORK_STATUS_CONNECTED; listening_port = port; - if (gConfigNetwork.advertise) - { - _advertiser = CreateServerAdvertiser(listening_port); - } - - if (gConfigNetwork.pause_server_if_no_clients) - { - auto pauseToggleAction = PauseToggleAction(); - GameActions::Execute(&pauseToggleAction); - } + _serverState.gamestateSnapshotsEnabled = gConfigNetwork.desync_debugging; + _advertiser = CreateServerAdvertiser(listening_port); return true; } @@ -654,7 +654,7 @@ int32_t Network::GetAuthStatus() uint32_t Network::GetServerTick() { - return server_tick; + return _serverState.tick; } uint8_t Network::GetPlayerID() @@ -666,6 +666,11 @@ void Network::Update() { _closeLock = true; + // Update is not necessarily called per game tick, maintain our own delta time + uint32_t ticks = platform_get_ticks(); + _currentDeltaTime = std::max(ticks - _lastUpdateTime, 1); + _lastUpdateTime = ticks; + switch (GetMode()) { case NETWORK_MODE_SERVER: @@ -681,6 +686,10 @@ void Network::Update() if (_requireClose) { Close(); + if (_requireReconnect) + { + Reconnect(); + } } } @@ -701,17 +710,19 @@ void Network::Flush() void Network::UpdateServer() { - auto it = client_connection_list.begin(); - while (it != client_connection_list.end()) + for (auto& connection : client_connection_list) { - if (!ProcessConnection(*(*it))) + // This can be called multiple times before the connection is removed. + if (connection->IsDisconnected) + continue; + + if (!ProcessConnection(*connection)) { - RemoveClient((*it)); - it = client_connection_list.begin(); + connection->IsDisconnected = true; } else { - it++; + DecayCooldown(connection->Player); } } @@ -909,6 +920,12 @@ void Network::SendPacketToClients(NetworkPacket& packet, bool front, bool gameCm { for (auto& client_connection : client_connection_list) { + if (client_connection->IsDisconnected) + { + // Client will be removed at the end of the tick, don't bother. + continue; + } + if (gameCmd) { // If marked as game command we can not send the packet to connections that are not fully connected. @@ -926,34 +943,30 @@ void Network::SendPacketToClients(NetworkPacket& packet, bool front, bool gameCm bool Network::CheckSRAND(uint32_t tick, uint32_t srand0) { - if (server_srand0_tick == 0) + // We have to wait for the map to be loaded first, ticks may match current loaded map. + if (!_clientMapLoaded) return true; - if (tick > server_srand0_tick) - { - server_srand0_tick = 0; + auto itTickData = _serverTickData.find(tick); + if (itTickData == std::end(_serverTickData)) return true; + + const ServerTickData_t storedTick = itTickData->second; + _serverTickData.erase(itTickData); + + if (storedTick.srand0 != srand0) + { + log_info("Srand0 mismatch, client = %08X, server = %08X", srand0, storedTick.srand0); + return false; } - if (game_commands_processed_this_tick != 0) + if (!storedTick.spriteHash.empty()) { - // SRAND/sprite hash is only updated once at beginning of tick so it is invalid otherwise - return true; - } - - if (tick == server_srand0_tick) - { - server_srand0_tick = 0; - // Check that the server and client sprite hashes match rct_sprite_checksum checksum = sprite_checksum(); - std::string client_sprite_hash = checksum.ToString(); - const bool sprites_mismatch = server_sprite_hash[0] != '\0' && client_sprite_hash != server_sprite_hash; - // Check PRNG values and sprite hashes, if exist - if ((srand0 != server_srand0) || sprites_mismatch) + std::string clientSpriteHash = checksum.ToString(); + if (clientSpriteHash != storedTick.spriteHash) { -# ifdef DEBUG_DESYNC - dbg_report_desync(tick, srand0, server_srand0, client_sprite_hash.c_str(), server_sprite_hash.c_str()); -# endif + log_info("Sprite hash mismatch, client = %s, server = %s", clientSpriteHash.c_str(), storedTick.spriteHash.c_str()); return false; } } @@ -961,12 +974,19 @@ bool Network::CheckSRAND(uint32_t tick, uint32_t srand0) return true; } -void Network::CheckDesynchronizaton() +bool Network::IsDesynchronised() +{ + return _serverState.state == NETWORK_SERVER_STATE_DESYNCED; +} + +bool Network::CheckDesynchronizaton() { // Check synchronisation - if (GetMode() == NETWORK_MODE_CLIENT && !_desynchronised && !CheckSRAND(gCurrentTicks, scenario_rand_state().s0)) + if (GetMode() == NETWORK_MODE_CLIENT && _serverState.state != NETWORK_SERVER_STATE_DESYNCED + && !CheckSRAND(gCurrentTicks, scenario_rand_state().s0)) { - _desynchronised = true; + _serverState.state = NETWORK_SERVER_STATE_DESYNCED; + _serverState.desyncTick = gCurrentTicks; char str_desync[256]; format_string(str_desync, 256, STR_MULTIPLAYER_DESYNC, nullptr); @@ -979,7 +999,23 @@ void Network::CheckDesynchronizaton() { Close(); } + + return true; } + + return false; +} + +void Network::RequestStateSnapshot() +{ + log_info("Requesting game state for tick %u", _serverState.desyncTick); + + Client_Send_RequestGameState(_serverState.desyncTick); +} + +NetworkServerState_t Network::GetServerState() const +{ + return _serverState; } void Network::KickPlayer(int32_t playerId) @@ -1004,7 +1040,7 @@ void Network::SetPassword(const char* password) _password = password == nullptr ? "" : password; } -void Network::ShutdownClient() +void Network::ServerClientDisconnected() { if (GetMode() == NETWORK_MODE_CLIENT) { @@ -1253,7 +1289,7 @@ void Network::AppendLog(std::ostream& fs, const std::string& s) } try { - utf8 buffer[256]; + utf8 buffer[1024]; time_t timer; time(&timer); auto tmInfo = localtime(&timer); @@ -1278,8 +1314,8 @@ void Network::BeginChatLog() _chatLogPath = BeginLog(directory, "", _chatLogFilenameFormat); # if defined(_WIN32) && !defined(__MINGW32__) - auto pathW = std::unique_ptr(utf8_to_widechar(_chatLogPath.c_str())); - _chat_log_fs.open(pathW.get(), std::ios::out | std::ios::app); + auto pathW = String::ToWideChar(_chatLogPath.c_str()); + _chat_log_fs.open(pathW.c_str(), std::ios::out | std::ios::app); # else _chat_log_fs.open(_chatLogPath, std::ios::out | std::ios::app); # endif @@ -1304,10 +1340,10 @@ void Network::BeginServerLog() _serverLogPath = BeginLog(directory, ServerName, _serverLogFilenameFormat); # if defined(_WIN32) && !defined(__MINGW32__) - auto pathW = std::unique_ptr(utf8_to_widechar(_serverLogPath.c_str())); - _server_log_fs.open(pathW.get(), std::ios::out | std::ios::app); + auto pathW = String::ToWideChar(_serverLogPath.c_str()); + _server_log_fs.open(pathW.c_str(), std::ios::out | std::ios::app | std::ios::binary); # else - _server_log_fs.open(_serverLogPath, std::ios::out | std::ios::app); + _server_log_fs.open(_serverLogPath, std::ios::out | std::ios::app | std::ios::binary); # endif // Log server start event @@ -1347,6 +1383,20 @@ void Network::CloseServerLog() _server_log_fs.close(); } +void Network::Client_Send_RequestGameState(uint32_t tick) +{ + if (_serverState.gamestateSnapshotsEnabled == false) + { + log_verbose("Server does not store a gamestate history"); + return; + } + + log_verbose("Requesting gamestate from server for tick %u", tick); + std::unique_ptr packet(NetworkPacket::Allocate()); + *packet << (uint32_t)NETWORK_COMMAND_REQUEST_GAMESTATE << tick; + _serverConnection->QueuePacket(std::move(packet)); +} + void Network::Client_Send_TOKEN() { log_verbose("requesting token"); @@ -1475,7 +1525,7 @@ void Network::Server_Send_MAP(NetworkConnection* connection) } return; } - size_t chunksize = 65000; + size_t chunksize = CHUNK_SIZE; for (size_t i = 0; i < out_size; i += chunksize) { size_t datasize = std::min(chunksize, out_size - i); @@ -1563,26 +1613,6 @@ void Network::Server_Send_CHAT(const char* text) SendPacketToClients(*packet); } -void Network::Client_Send_GAMECMD( - uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi, uint32_t ebp, uint8_t callback) -{ - std::unique_ptr packet(NetworkPacket::Allocate()); - *packet << (uint32_t)NETWORK_COMMAND_GAMECMD << gCurrentTicks << eax << (ebx | GAME_COMMAND_FLAG_NETWORKED) << ecx << edx - << esi << edi << ebp << callback; - - _serverConnection->QueuePacket(std::move(packet)); -} - -void Network::Server_Send_GAMECMD( - uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi, uint32_t ebp, uint8_t playerid, - uint8_t callback) -{ - std::unique_ptr packet(NetworkPacket::Allocate()); - *packet << (uint32_t)NETWORK_COMMAND_GAMECMD << gCurrentTicks << eax << (ebx | GAME_COMMAND_FLAG_NETWORKED) << ecx << edx - << esi << edi << ebp << playerid << callback; - SendPacketToClients(*packet, false, true); -} - void Network::Client_Send_GAME_ACTION(const GameAction* action) { std::unique_ptr packet(NetworkPacket::Allocate()); @@ -1618,14 +1648,6 @@ void Network::Server_Send_GAME_ACTION(const GameAction* action) void Network::Server_Send_TICK() { - uint32_t ticks = platform_get_ticks(); - if (ticks < last_tick_sent_time + 25) - { - return; - } - - last_tick_sent_time = ticks; - std::unique_ptr packet(NetworkPacket::Allocate()); *packet << (uint32_t)NETWORK_COMMAND_TICK << gCurrentTicks << scenario_rand_state().s0; uint32_t flags = 0; @@ -1651,6 +1673,19 @@ void Network::Server_Send_TICK() SendPacketToClients(*packet); } +void Network::Server_Send_PLAYERINFO(int32_t playerId) +{ + std::unique_ptr packet(NetworkPacket::Allocate()); + *packet << (uint32_t)NETWORK_COMMAND_PLAYERINFO << gCurrentTicks; + + auto* player = GetPlayerByID(playerId); + if (player == nullptr) + return; + + player->Write(*packet); + SendPacketToClients(*packet); +} + void Network::Server_Send_PLAYERLIST() { std::unique_ptr packet(NetworkPacket::Allocate()); @@ -1700,11 +1735,8 @@ void Network::Server_Send_SETDISCONNECTMSG(NetworkConnection& connection, const connection.QueuePacket(std::move(packet)); } -void Network::Server_Send_GAMEINFO(NetworkConnection& connection) +json_t* Network::GetServerInfoAsJson() const { - std::unique_ptr packet(NetworkPacket::Allocate()); - *packet << (uint32_t)NETWORK_COMMAND_GAMEINFO; -# ifndef DISABLE_HTTP json_t* obj = json_object(); json_object_set_new(obj, "name", json_string(gConfigNetwork.server_name.c_str())); json_object_set_new(obj, "requiresPassword", json_boolean(_password.size() > 0)); @@ -1714,6 +1746,15 @@ void Network::Server_Send_GAMEINFO(NetworkConnection& connection) json_object_set_new(obj, "description", json_string(gConfigNetwork.server_description.c_str())); json_object_set_new(obj, "greeting", json_string(gConfigNetwork.server_greeting.c_str())); json_object_set_new(obj, "dedicated", json_boolean(gOpenRCT2Headless)); + return obj; +} + +void Network::Server_Send_GAMEINFO(NetworkConnection& connection) +{ + std::unique_ptr packet(NetworkPacket::Allocate()); + *packet << (uint32_t)NETWORK_COMMAND_GAMEINFO; +# ifndef DISABLE_HTTP + json_t* obj = GetServerInfoAsJson(); // Provider details json_t* jsonProvider = json_object(); @@ -1723,6 +1764,8 @@ void Network::Server_Send_GAMEINFO(NetworkConnection& connection) json_object_set_new(obj, "provider", jsonProvider); packet->WriteString(json_dumps(obj, 0)); + *packet << _serverState.gamestateSnapshotsEnabled; + json_decref(obj); # endif connection.QueuePacket(std::move(packet)); @@ -1836,169 +1879,131 @@ void Network::ProcessPacket(NetworkConnection& connection, NetworkPacket& packet packet.Clear(); } +// This is called at the end of each game tick, this where things should be processed that affects the game state. void Network::ProcessPending() { - ProcessGameCommands(); + if (GetMode() == NETWORK_MODE_SERVER) + { + ProcessDisconnectedClients(); + } + else if (GetMode() == NETWORK_MODE_CLIENT) + { + ProcessPlayerInfo(); + } ProcessPlayerList(); } void Network::ProcessPlayerList() { - if (_pendingPlayerList.tick == 0 || _pendingPlayerList.tick < gCurrentTicks) + if (GetMode() == NETWORK_MODE_SERVER) { - return; - } - - // List of active players found in the list. - std::vector activePlayerIds; - - for (auto&& pendingPlayer : _pendingPlayerList.players) - { - activePlayerIds.push_back(pendingPlayer.Id); - if (!GetPlayerByID(pendingPlayer.Id)) + // Avoid sending multiple times the player list, we mark the list invalidated on modifications + // and then send at the end of the tick the final player list. + if (_playerListInvalidated) { - NetworkPlayer* player = AddPlayer("", ""); - if (player) - { - *player = pendingPlayer; - if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER) - { - _serverConnection->Player = player; - } - } + _playerListInvalidated = false; + Server_Send_PLAYERLIST(); } } - - // Remove any players that are not in newly received list - auto it = player_list.begin(); - while (it != player_list.end()) + else { - if (std::find(activePlayerIds.begin(), activePlayerIds.end(), (*it)->Id) == activePlayerIds.end()) + // As client we have to keep things in order so the update is tick bound. + // Commands/Actions reference players and so this list needs to be in sync with those. + auto itPending = _pendingPlayerLists.begin(); + while (itPending != _pendingPlayerLists.end()) { - it = player_list.erase(it); + if (itPending->first > gCurrentTicks) + break; + + // List of active players found in the list. + std::vector activePlayerIds; + + for (auto&& pendingPlayer : itPending->second.players) + { + activePlayerIds.push_back(pendingPlayer.Id); + + auto* player = GetPlayerByID(pendingPlayer.Id); + if (player == nullptr) + { + // Add new player. + player = AddPlayer("", ""); + if (player) + { + *player = pendingPlayer; + if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER) + { + _serverConnection->Player = player; + } + } + } + else + { + // Update. + *player = pendingPlayer; + } + } + + // Remove any players that are not in newly received list + auto it = player_list.begin(); + while (it != player_list.end()) + { + if (std::find(activePlayerIds.begin(), activePlayerIds.end(), (*it)->Id) == activePlayerIds.end()) + { + it = player_list.erase(it); + } + else + { + it++; + } + } + + _pendingPlayerLists.erase(itPending); + itPending = _pendingPlayerLists.begin(); + } + } +} + +void Network::ProcessPlayerInfo() +{ + auto range = _pendingPlayerInfo.equal_range(gCurrentTicks); + for (auto it = range.first; it != range.second; it++) + { + auto* player = GetPlayerByID(it->second.Id); + if (player != nullptr) + { + const NetworkPlayer& networkedInfo = it->second; + player->Flags = networkedInfo.Flags; + player->Group = networkedInfo.Group; + player->LastAction = networkedInfo.LastAction; + player->LastActionCoord = networkedInfo.LastActionCoord; + player->MoneySpent = networkedInfo.MoneySpent; + player->CommandsRan = networkedInfo.CommandsRan; + } + } + _pendingPlayerInfo.erase(gCurrentTicks); +} + +void Network::ProcessDisconnectedClients() +{ + for (auto it = client_connection_list.begin(); it != client_connection_list.end();) + { + auto& connection = *it; + if (connection->IsDisconnected) + { + ServerClientDisconnected(connection); + RemovePlayer(connection); + + it = client_connection_list.erase(it); } else { it++; } } - - _pendingPlayerList.reset(); -} - -void Network::ProcessGameCommands() -{ - while (game_command_queue.begin() != game_command_queue.end()) - { - // run all the game commands at the current tick - const GameCommand& gc = (*game_command_queue.begin()); - - if (mode == NETWORK_MODE_CLIENT) - { - if (game_command_queue.begin()->tick < gCurrentTicks) - { - // Having old command from a tick where we have not been active yet or malicious server, - // the command is useless so lets not keep it. - log_warning( - "Discarding game command from tick behind current tick, CMD: %08X, CMD Tick: %08X, Current Tick: %08X\n", - gc.esi, gc.tick, gCurrentTicks); - - game_command_queue.erase(game_command_queue.begin()); - - // At this point we should not return, would add the possibility to skip commands this tick. - continue; - } - - // exit the game command processing loop to still have a chance at finding desync. - if (game_command_queue.begin()->tick != gCurrentTicks) - break; - } - - if (gc.action != nullptr) - { - GameAction* action = gc.action.get(); - action->SetFlags(action->GetFlags() | GAME_COMMAND_FLAG_NETWORKED); - - Guard::Assert(action != nullptr); - - GameActionResult::Ptr result = GameActions::Execute(action); - if (result->Error == GA_ERROR::OK) - { - game_commands_processed_this_tick++; - Server_Send_GAME_ACTION(action); - } - } - else - { - if (GetPlayerID() == gc.playerid) - { - game_command_callback = game_command_callback_get_callback(gc.callback); - } - - game_command_playerid = gc.playerid; - - int32_t command = gc.esi; - int32_t flags = gc.ebx; - if (mode == NETWORK_MODE_SERVER) - flags |= GAME_COMMAND_FLAG_NETWORKED; - - money32 cost = game_do_command(gc.eax, flags, gc.ecx, gc.edx, gc.esi, gc.edi, gc.ebp); - - if (cost != MONEY32_UNDEFINED) - { - game_commands_processed_this_tick++; - NetworkPlayer* player = GetPlayerByID(gc.playerid); - if (!player) - return; - - player->LastAction = NetworkActions::FindCommand(command); - player->LastActionTime = platform_get_ticks(); - player->AddMoneySpent(cost); - - if (mode == NETWORK_MODE_SERVER) - { - // Note these are currently not reached as both commands are ported to GameActions - if (command == GAME_COMMAND_PLACE_SCENERY) - { - player->LastPlaceSceneryTime = player->LastActionTime; - } - else if (command == GAME_COMMAND_DEMOLISH_RIDE) - { - player->LastDemolishRideTime = player->LastActionTime; - } - - Server_Send_GAMECMD(gc.eax, gc.ebx, gc.ecx, gc.edx, gc.esi, gc.edi, gc.ebp, gc.playerid, gc.callback); - } - } - } - game_command_queue.erase(game_command_queue.begin()); - } -} - -void Network::EnqueueGameAction(const GameAction* action) -{ - MemoryStream stream; - DataSerialiser dsOut(true, stream); - action->Serialise(dsOut); - - std::unique_ptr ga = GameActions::Create(action->GetType()); - ga->SetCallback(action->GetCallback()); - - stream.SetPosition(0); - DataSerialiser dsIn(false, stream); - ga->Serialise(dsIn); - - game_command_queue.emplace(gCurrentTicks, std::move(ga), _commandId++); } void Network::AddClient(std::unique_ptr&& socket) { - if (gConfigNetwork.pause_server_if_no_clients && game_is_paused()) - { - auto pauseToggleAction = PauseToggleAction(); - GameActions::Execute(&pauseToggleAction); - } - // Log connection info. char addr[128]; snprintf(addr, sizeof(addr), "Client joined from %s", socket->GetHostName()); @@ -2011,53 +2016,57 @@ void Network::AddClient(std::unique_ptr&& socket) client_connection_list.push_back(std::move(connection)); } -void Network::RemoveClient(std::unique_ptr& connection) +void Network::ServerClientDisconnected(std::unique_ptr& connection) { NetworkPlayer* connection_player = connection->Player; - if (connection_player) + if (connection_player == nullptr) + return; + + char text[256]; + const char* has_disconnected_args[2] = { + connection_player->Name.c_str(), + connection->GetLastDisconnectReason(), + }; + if (has_disconnected_args[1]) { - char text[256]; - const char* has_disconnected_args[2] = { - connection_player->Name.c_str(), - connection->GetLastDisconnectReason(), - }; - if (has_disconnected_args[1]) - { - format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_DISCONNECTED_WITH_REASON, has_disconnected_args); - } - else - { - format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_DISCONNECTED_NO_REASON, &(has_disconnected_args[0])); - } - - chat_history_add(text); - Peep* pickup_peep = network_get_pickup_peep(connection_player->Id); - if (pickup_peep) - { - game_command_playerid = connection_player->Id; - game_do_command( - pickup_peep->sprite_index, GAME_COMMAND_FLAG_APPLY, 1, 0, - pickup_peep->type == PEEP_TYPE_GUEST ? GAME_COMMAND_PICKUP_GUEST : GAME_COMMAND_PICKUP_STAFF, - network_get_pickup_peep_old_x(connection_player->Id), 0); - } - gNetwork.Server_Send_EVENT_PLAYER_DISCONNECTED( - (char*)connection_player->Name.c_str(), connection->GetLastDisconnectReason()); - - // Log player disconnected event - AppendServerLog(text); + format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_DISCONNECTED_WITH_REASON, has_disconnected_args); } + else + { + format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_DISCONNECTED_NO_REASON, &(has_disconnected_args[0])); + } + + chat_history_add(text); + Peep* pickup_peep = network_get_pickup_peep(connection_player->Id); + if (pickup_peep) + { + PeepPickupAction pickupAction{ PeepPickupType::Cancel, + pickup_peep->sprite_index, + { network_get_pickup_peep_old_x(connection_player->Id), 0, 0 }, + network_get_current_player_id() }; + auto res = GameActions::Execute(&pickupAction); + } + gNetwork.Server_Send_EVENT_PLAYER_DISCONNECTED( + (char*)connection_player->Name.c_str(), connection->GetLastDisconnectReason()); + + // Log player disconnected event + AppendServerLog(text); +} + +void Network::RemovePlayer(std::unique_ptr& connection) +{ + NetworkPlayer* connection_player = connection->Player; + if (connection_player == nullptr) + return; + player_list.erase( std::remove_if( player_list.begin(), player_list.end(), [connection_player](std::unique_ptr& player) { return player.get() == connection_player; }), player_list.end()); - client_connection_list.remove(connection); - if (gConfigNetwork.pause_server_if_no_clients && game_is_not_paused() && client_connection_list.size() == 0) - { - auto pauseToggleAction = PauseToggleAction(); - GameActions::Execute(&pauseToggleAction); - } - Server_Send_PLAYERLIST(); + + // Send new player list. + _playerListInvalidated = true; } NetworkPlayer* Network::AddPlayer(const std::string& name, const std::string& keyhash) @@ -2100,7 +2109,7 @@ NetworkPlayer* Network::AddPlayer(const std::string& name, const std::string& ke if (networkUser == nullptr) { player->Group = GetDefaultGroup(); - if (name.empty() == false) + if (!name.empty()) { player->SetName(MakePlayerNameUnique(String::Trim(name))); } @@ -2110,6 +2119,9 @@ NetworkPlayer* Network::AddPlayer(const std::string& name, const std::string& ke player->Group = networkUser->GroupId.GetValueOrDefault(GetDefaultGroup()); player->SetName(networkUser->Name); } + + // Send new player list. + _playerListInvalidated = true; } else { @@ -2215,6 +2227,48 @@ void Network::Client_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& Client_Send_AUTH(gConfigNetwork.player_name.c_str(), password, pubkey.c_str(), signature); } +void Network::Server_Handle_REQUEST_GAMESTATE(NetworkConnection& connection, NetworkPacket& packet) +{ + uint32_t tick; + packet >> tick; + + if (_serverState.gamestateSnapshotsEnabled == false) + { + // Ignore this if this is off. + return; + } + + IGameStateSnapshots* snapshots = GetContext()->GetGameStateSnapshots(); + + const GameStateSnapshot_t* snapshot = snapshots->GetLinkedSnapshot(tick); + if (snapshot) + { + MemoryStream snapshotMemory; + DataSerialiser ds(true, snapshotMemory); + + snapshots->SerialiseSnapshot(const_cast(*snapshot), ds); + + uint32_t bytesSent = 0; + uint32_t length = (uint32_t)snapshotMemory.GetLength(); + while (bytesSent < length) + { + uint32_t dataSize = CHUNK_SIZE; + if (bytesSent + dataSize > snapshotMemory.GetLength()) + { + dataSize = snapshotMemory.GetLength() - bytesSent; + } + + std::unique_ptr gameStateChunk(NetworkPacket::Allocate()); + *gameStateChunk << (uint32_t)NETWORK_COMMAND_GAMESTATE << tick << length << bytesSent << dataSize; + gameStateChunk->Write((const uint8_t*)snapshotMemory.GetData() + bytesSent, dataSize); + + connection.QueuePacket(std::move(gameStateChunk)); + + bytesSent += dataSize; + } + } +} + void Network::Client_Handle_AUTH(NetworkConnection& connection, NetworkPacket& packet) { uint32_t auth_status; @@ -2283,8 +2337,6 @@ void Network::Server_Client_Joined(const char* name, const std::string& keyhash, player_name = (const char*)playerNameHash.c_str(); format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_JOINED_THE_GAME, &player_name); AppendServerLog(text); - - Server_Send_PLAYERLIST(); } } @@ -2338,6 +2390,73 @@ void Network::Client_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket Client_Send_OBJECTS(requested_objects); } +void Network::Client_Handle_GAMESTATE(NetworkConnection& connection, NetworkPacket& packet) +{ + uint32_t tick; + uint32_t totalSize; + uint32_t offset; + uint32_t dataSize; + + packet >> tick >> totalSize >> offset >> dataSize; + + if (offset == 0) + { + // Reset + _serverGameState = MemoryStream(); + } + + _serverGameState.SetPosition(offset); + + const uint8_t* data = packet.Read(dataSize); + _serverGameState.Write(data, dataSize); + + log_verbose("Received Game State %.02f%%", ((float)_serverGameState.GetLength() / (float)totalSize) * 100.0f); + + if (_serverGameState.GetLength() == totalSize) + { + _serverGameState.SetPosition(0); + DataSerialiser ds(false, _serverGameState); + + IGameStateSnapshots* snapshots = GetContext()->GetGameStateSnapshots(); + + GameStateSnapshot_t& serverSnapshot = snapshots->CreateSnapshot(); + snapshots->SerialiseSnapshot(serverSnapshot, ds); + + const GameStateSnapshot_t* desyncSnapshot = snapshots->GetLinkedSnapshot(tick); + if (desyncSnapshot) + { + GameStateCompareData_t cmpData = snapshots->Compare(serverSnapshot, *desyncSnapshot); + + std::string outputPath = GetContext()->GetPlatformEnvironment()->GetDirectoryPath( + DIRBASE::USER, DIRID::LOG_DESYNCS); + + platform_ensure_directory_exists(outputPath.c_str()); + + char uniqueFileName[128] = {}; + snprintf( + uniqueFileName, sizeof(uniqueFileName), "desync_%llu_%u.txt", + (long long unsigned)platform_get_datetime_now_utc(), tick); + + std::string outputFile = Path::Combine(outputPath, uniqueFileName); + + if (snapshots->LogCompareDataToFile(outputFile, cmpData)) + { + log_info("Wrote desync report to '%s'", outputFile.c_str()); + + uint8_t args[32]{}; + set_format_arg_on(args, 0, char*, uniqueFileName); + + char str_desync[1024]; + format_string(str_desync, sizeof(str_desync), STR_DESYNC_REPORT, args); + + auto intent = Intent(WC_NETWORK_STATUS); + intent.putExtra(INTENT_EXTRA_MESSAGE, std::string{ str_desync }); + context_open_intent(&intent); + } + } + } +} + void Network::Server_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet) { uint32_t size; @@ -2379,7 +2498,6 @@ void Network::Server_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket Server_Send_MAP(&connection); Server_Send_EVENT_PLAYER_JOINED(player_name); Server_Send_GROUPLIST(connection); - Server_Send_PLAYERLIST(); } void Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& packet) @@ -2498,6 +2616,16 @@ void Network::Client_Handle_MAP([[maybe_unused]] NetworkConnection& connection, { return; } + if (offset == 0) + { + // Start of a new map load, clear the queue now as we have to buffer them + // until the map is fully loaded. + GameActions::ClearQueue(); + GameActions::SuspendQueue(); + + _serverTickData.clear(); + _clientMapLoaded = false; + } if (size > chunk_buffer.size()) { chunk_buffer.resize(size); @@ -2517,6 +2645,9 @@ void Network::Client_Handle_MAP([[maybe_unused]] NetworkConnection& connection, std::memcpy(&chunk_buffer[offset], (void*)packet.Read(chunksize), chunksize); if (offset + chunksize == size) { + // Allow queue processing of game actions again. + GameActions::ResumeQueue(); + context_force_close_window_by_class(WC_NETWORK_STATUS); bool has_to_free = false; uint8_t* data = &chunk_buffer[0]; @@ -2544,11 +2675,10 @@ void Network::Client_Handle_MAP([[maybe_unused]] NetworkConnection& connection, if (LoadMap(&ms)) { game_load_init(); - game_command_queue.clear(); - server_tick = gCurrentTicks; - server_srand0_tick = 0; + _serverState.tick = gCurrentTicks; // window_network_status_open("Loaded new map from network"); - _desynchronised = false; + _serverState.state = NETWORK_SERVER_STATE_OK; + _clientMapLoaded = true; gFirstTimeSaving = true; // Notify user he is now online and which shortcut key enables chat @@ -2583,6 +2713,7 @@ bool Network::LoadMap(IStream* stream) importer->Import(); sprite_position_tween_reset(); + AutoCreateMapAnimations(); // Read checksum [[maybe_unused]] uint32_t checksum = stream->ReadValue(); @@ -2592,6 +2723,7 @@ bool Network::LoadMap(IStream* stream) gGamePaused = stream->ReadValue(); _guestGenerationProbability = stream->ReadValue(); _suggestedGuestMaximum = stream->ReadValue(); + gCheatsEnableAllDrawableTrackPieces = stream->ReadValue() != 0; gCheatsSandboxMode = stream->ReadValue() != 0; gCheatsDisableClearanceChecks = stream->ReadValue() != 0; gCheatsDisableSupportLimits = stream->ReadValue() != 0; @@ -2626,6 +2758,7 @@ bool Network::LoadMap(IStream* stream) bool Network::SaveMap(IStream* stream, const std::vector& objects) const { bool result = false; + map_reorganise_elements(); viewport_set_saved_view(); try { @@ -2639,6 +2772,7 @@ bool Network::SaveMap(IStream* stream, const std::vectorWriteValue(gGamePaused); stream->WriteValue(_guestGenerationProbability); stream->WriteValue(_suggestedGuestMaximum); + stream->WriteValue(gCheatsEnableAllDrawableTrackPieces); stream->WriteValue(gCheatsSandboxMode); stream->WriteValue(gCheatsDisableClearanceChecks); stream->WriteValue(gCheatsDisableSupportLimits); @@ -2697,17 +2831,6 @@ void Network::Server_Handle_CHAT(NetworkConnection& connection, NetworkPacket& p } } -void Network::Client_Handle_GAMECMD([[maybe_unused]] NetworkConnection& connection, NetworkPacket& packet) -{ - uint32_t tick; - uint32_t args[7]; - uint8_t playerid; - uint8_t callback; - packet >> tick >> args[0] >> args[1] >> args[2] >> args[3] >> args[4] >> args[5] >> args[6] >> playerid >> callback; - - game_command_queue.emplace(tick, args, playerid, callback, _commandId++); -} - void Network::Client_Handle_GAME_ACTION([[maybe_unused]] NetworkConnection& connection, NetworkPacket& packet) { uint32_t tick; @@ -2737,12 +2860,11 @@ void Network::Client_Handle_GAME_ACTION([[maybe_unused]] NetworkConnection& conn if (itr != _gameActionCallbacks.end()) { action->SetCallback(itr->second); - _gameActionCallbacks.erase(itr); } } - game_command_queue.emplace(tick, std::move(action), _commandId++); + GameActions::Enqueue(std::move(action), tick); } void Network::Server_Handle_GAME_ACTION(NetworkConnection& connection, NetworkPacket& packet) @@ -2750,58 +2872,29 @@ void Network::Server_Handle_GAME_ACTION(NetworkConnection& connection, NetworkPa uint32_t tick; uint32_t actionType; - if (!connection.Player) + NetworkPlayer* player = connection.Player; + if (player == nullptr) { return; } packet >> tick >> actionType; - // tick count is different by time last_action_time is set, keep same value + // Don't let clients send pause or quit + if (actionType == GAME_COMMAND_TOGGLE_PAUSE || actionType == GAME_COMMAND_LOAD_OR_QUIT) + { + return; + } + // Check if player's group permission allows command to run - uint32_t ticks = platform_get_ticks(); NetworkGroup* group = GetGroupByID(connection.Player->Group); - if (!group || !group->CanPerformCommand(actionType)) + if (group == nullptr || group->CanPerformCommand(actionType) == false) { Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_PERMISSION_DENIED); return; } - // In case someone modifies the code / memory to enable cluster build, - // require a small delay in between placing scenery to provide some security, as - // cluster mode is a for loop that runs the place_scenery code multiple times. - if (actionType == GAME_COMMAND_PLACE_SCENERY) - { - if (ticks - connection.Player->LastPlaceSceneryTime < ACTION_COOLDOWN_TIME_PLACE_SCENERY && - // Incase platform_get_ticks() wraps after ~49 days, ignore larger logged times. - ticks > connection.Player->LastPlaceSceneryTime) - { - if (!(group->CanPerformCommand(MISC_COMMAND_TOGGLE_SCENERY_CLUSTER))) - { - Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_NETWORK_ACTION_RATE_LIMIT_MESSAGE); - return; - } - } - } - // This is to prevent abuse of demolishing rides. Anyone that is not the server - // host will have to wait a small amount of time in between deleting rides. - else if (actionType == GAME_COMMAND_DEMOLISH_RIDE) - { - if (ticks - connection.Player->LastDemolishRideTime < ACTION_COOLDOWN_TIME_DEMOLISH_RIDE && - // Incase platform_get_ticks()() wraps after ~49 days, ignore larger logged times. - ticks > connection.Player->LastDemolishRideTime) - { - Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_NETWORK_ACTION_RATE_LIMIT_MESSAGE); - return; - } - } - // Don't let clients send pause or quit - else if (actionType == GAME_COMMAND_TOGGLE_PAUSE || actionType == GAME_COMMAND_LOAD_OR_QUIT) - { - return; - } - - // Run game command, and if it is successful send to clients + // Create and enqueue the action. GameAction::Ptr ga = GameActions::Create(actionType); if (ga == nullptr) { @@ -2811,6 +2904,26 @@ void Network::Server_Handle_GAME_ACTION(NetworkConnection& connection, NetworkPa return; } + // Player who is hosting is not affected by cooldowns. + if ((player->Flags & NETWORK_PLAYER_FLAG_ISSERVER) == 0) + { + auto cooldownIt = player->CooldownTime.find(actionType); + if (cooldownIt != std::end(player->CooldownTime)) + { + if (cooldownIt->second > 0) + { + Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_NETWORK_ACTION_RATE_LIMIT_MESSAGE); + return; + } + } + + uint32_t cooldownTime = ga->GetCooldownTime(); + if (cooldownTime > 0) + { + player->CooldownTime[actionType] = cooldownTime; + } + } + DataSerialiser stream(false); size_t size = packet.Size - packet.BytesRead; stream.GetStream().WriteArray(packet.Read(size), size); @@ -2820,94 +2933,49 @@ void Network::Server_Handle_GAME_ACTION(NetworkConnection& connection, NetworkPa // Set player to sender, should be 0 if sent from client. ga->SetPlayer(NetworkPlayerId_t{ connection.Player->Id }); - game_command_queue.emplace(tick, std::move(ga), _commandId++); -} - -void Network::Server_Handle_GAMECMD(NetworkConnection& connection, NetworkPacket& packet) -{ - uint32_t tick; - uint32_t args[7]; - uint8_t playerid; - uint8_t callback; - - if (!connection.Player) - { - return; - } - - playerid = connection.Player->Id; - - packet >> tick >> args[0] >> args[1] >> args[2] >> args[3] >> args[4] >> args[5] >> args[6] >> callback; - - int32_t commandCommand = args[4]; - - uint32_t ticks = platform_get_ticks(); // tick count is different by time last_action_time is set, keep same value. - - // Check if player's group permission allows command to run - NetworkGroup* group = GetGroupByID(connection.Player->Group); - if (!group || !group->CanPerformCommand(commandCommand)) - { - Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_PERMISSION_DENIED); - return; - } - - // In case someone modifies the code / memory to enable cluster build, - // require a small delay in between placing scenery to provide some security, as - // cluster mode is a for loop that runs the place_scenery code multiple times. - if (commandCommand == GAME_COMMAND_PLACE_SCENERY) - { - if (ticks - connection.Player->LastPlaceSceneryTime < ACTION_COOLDOWN_TIME_PLACE_SCENERY && - // In case platform_get_ticks() wraps after ~49 days, ignore larger logged times. - ticks > connection.Player->LastPlaceSceneryTime) - { - if (!(group->CanPerformCommand(MISC_COMMAND_TOGGLE_SCENERY_CLUSTER))) - { - Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_NETWORK_ACTION_RATE_LIMIT_MESSAGE); - return; - } - } - } - // This is to prevent abuse of demolishing rides. Anyone that is not the server - // host will have to wait a small amount of time in between deleting rides. - else if (commandCommand == GAME_COMMAND_DEMOLISH_RIDE) - { - if (ticks - connection.Player->LastDemolishRideTime < ACTION_COOLDOWN_TIME_DEMOLISH_RIDE && - // In case platform_get_ticks() wraps after ~49 days, ignore larger logged times. - ticks > connection.Player->LastDemolishRideTime) - { - Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_NETWORK_ACTION_RATE_LIMIT_MESSAGE); - return; - } - } - - game_command_queue.emplace(tick, args, playerid, callback, _commandId++); + GameActions::Enqueue(std::move(ga), tick); } void Network::Client_Handle_TICK([[maybe_unused]] NetworkConnection& connection, NetworkPacket& packet) { uint32_t srand0; uint32_t flags; - // Note: older server version may not advertise flags at all. - // NetworkPacket will return 0, if trying to read past end of buffer, - // so flags == 0 is expected in such cases. - packet >> server_tick >> srand0 >> flags; - if (server_srand0_tick == 0) + uint32_t serverTick; + + packet >> serverTick >> srand0 >> flags; + + ServerTickData_t tickData; + tickData.srand0 = srand0; + tickData.tick = serverTick; + + if (flags & NETWORK_TICK_FLAG_CHECKSUMS) { - server_srand0 = srand0; - server_srand0_tick = server_tick; - server_sprite_hash.resize(0); - if (flags & NETWORK_TICK_FLAG_CHECKSUMS) + const char* text = packet.ReadString(); + if (text != nullptr) { - const char* text = packet.ReadString(); - if (text != nullptr) - { - auto textLen = std::strlen(text); - server_sprite_hash.resize(textLen); - std::memcpy(server_sprite_hash.data(), text, textLen); - } + tickData.spriteHash = text; } } - game_commands_processed_this_tick = 0; + + // Don't let the history grow too much. + while (_serverTickData.size() >= 100) + { + _serverTickData.erase(_serverTickData.begin()); + } + + _serverState.tick = serverTick; + _serverTickData.emplace(serverTick, tickData); +} + +void Network::Client_Handle_PLAYERINFO([[maybe_unused]] NetworkConnection& connection, NetworkPacket& packet) +{ + uint32_t tick; + packet >> tick; + + NetworkPlayer playerInfo; + playerInfo.Read(packet); + + _pendingPlayerInfo.emplace(tick, playerInfo); } void Network::Client_Handle_PLAYERLIST([[maybe_unused]] NetworkConnection& connection, NetworkPacket& packet) @@ -2916,15 +2984,15 @@ void Network::Client_Handle_PLAYERLIST([[maybe_unused]] NetworkConnection& conne uint8_t size; packet >> tick >> size; - _pendingPlayerList.reset(); - _pendingPlayerList.tick = tick; + auto& pending = _pendingPlayerLists[tick]; + pending.players.clear(); for (uint32_t i = 0; i < size; i++) { NetworkPlayer tempplayer; tempplayer.Read(packet); - _pendingPlayerList.players.push_back(tempplayer); + pending.players.push_back(tempplayer); } } @@ -3052,6 +3120,7 @@ static std::string json_stdstring_value(const json_t* string) void Network::Client_Handle_GAMEINFO([[maybe_unused]] NetworkConnection& connection, NetworkPacket& packet) { const char* jsonString = packet.ReadString(); + packet >> _serverState.gamestateSnapshotsEnabled; json_error_t error; json_t* root = json_loads(jsonString, 0, &error); @@ -3082,9 +3151,14 @@ void network_close() gNetwork.Close(); } +void network_reconnect() +{ + gNetwork.Reconnect(); +} + void network_shutdown_client() { - gNetwork.ShutdownClient(); + gNetwork.ServerClientDisconnected(); } int32_t network_begin_client(const std::string& host, int32_t port) @@ -3122,11 +3196,21 @@ int32_t network_get_status() return gNetwork.GetStatus(); } -void network_check_desynchronization() +bool network_is_desynchronised() +{ + return gNetwork.IsDesynchronised(); +} + +bool network_check_desynchronisation() { return gNetwork.CheckDesynchronizaton(); } +void network_request_gamestate_snapshot() +{ + return gNetwork.RequestStateSnapshot(); +} + void network_send_tick() { gNetwork.Server_Send_TICK(); @@ -3293,61 +3377,51 @@ void network_chat_show_server_greeting() } } -void game_command_set_player_group( - [[maybe_unused]] int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, - [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) +GameActionResult::Ptr network_set_player_group( + NetworkPlayerId_t actionPlayerId, NetworkPlayerId_t playerId, uint8_t groupId, bool isExecuting) { - uint8_t playerid = (uint8_t)*ecx; - uint8_t groupid = (uint8_t)*edx; - NetworkPlayer* player = gNetwork.GetPlayerByID(playerid); - NetworkGroup* fromgroup = gNetwork.GetGroupByID(game_command_playerid); - if (!player) + NetworkPlayer* player = gNetwork.GetPlayerByID(playerId); + + NetworkGroup* fromgroup = gNetwork.GetGroupByID(actionPlayerId); + if (player == nullptr) { - gGameCommandErrorTitle = STR_CANT_DO_THIS; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_DO_THIS); } - if (!gNetwork.GetGroupByID(groupid)) + + if (!gNetwork.GetGroupByID(groupId)) { - gGameCommandErrorTitle = STR_CANT_DO_THIS; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_DO_THIS); } + if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER) { - gGameCommandErrorTitle = STR_CANT_CHANGE_GROUP_THAT_THE_HOST_BELONGS_TO; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_CHANGE_GROUP_THAT_THE_HOST_BELONGS_TO); } - if (groupid == 0 && fromgroup && fromgroup->Id != 0) + + if (groupId == 0 && fromgroup && fromgroup->Id != 0) { - gGameCommandErrorTitle = STR_CANT_SET_TO_THIS_GROUP; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_CANT_SET_TO_THIS_GROUP); } - if (*ebx & GAME_COMMAND_FLAG_APPLY) + + if (isExecuting) { - player->Group = groupid; + player->Group = groupId; if (network_get_mode() == NETWORK_MODE_SERVER) { // Add or update saved user NetworkUserManager* userManager = &gNetwork._userManager; NetworkUser* networkUser = userManager->GetOrAddUser(player->KeyHash); - networkUser->GroupId = groupid; + networkUser->GroupId = groupId; networkUser->Name = player->Name; userManager->Save(); } - window_invalidate_by_number(WC_PLAYER, playerid); + window_invalidate_by_number(WC_PLAYER, playerId); // Log set player group event - NetworkPlayer* game_command_player = gNetwork.GetPlayerByID(game_command_playerid); - NetworkGroup* new_player_group = gNetwork.GetGroupByID(groupid); + NetworkPlayer* game_command_player = gNetwork.GetPlayerByID(actionPlayerId); + NetworkGroup* new_player_group = gNetwork.GetGroupByID(groupId); char log_msg[256]; const char* args[3] = { player->Name.c_str(), @@ -3357,120 +3431,74 @@ void game_command_set_player_group( format_string(log_msg, 256, STR_LOG_SET_PLAYER_GROUP, args); network_append_server_log(log_msg); } - *ebx = 0; + return std::make_unique(); } -void game_command_modify_groups( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) +GameActionResult::Ptr network_modify_groups( + NetworkPlayerId_t actionPlayerId, ModifyGroupType type, uint8_t groupId, const std::string& name, uint32_t permissionIndex, + PermissionState permissionState, bool isExecuting) { - uint8_t action = (uint8_t)*eax; - uint8_t groupid = (uint8_t)(*eax >> 8); - uint8_t nameChunkIndex = (uint8_t)(*eax >> 16); - - switch (action) + switch (type) { - case 0: - { // add group - if (*ebx & GAME_COMMAND_FLAG_APPLY) + case ModifyGroupType::AddGroup: + { + if (isExecuting) { NetworkGroup* newgroup = gNetwork.AddGroup(); - if (!newgroup) + if (newgroup == nullptr) { - gGameCommandErrorTitle = STR_CANT_DO_THIS; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; - } - - // Log add player group event - NetworkPlayer* game_command_player = gNetwork.GetPlayerByID(game_command_playerid); - if (game_command_player) - { - char log_msg[256]; - const char* args[2] = { - game_command_player->Name.c_str(), - newgroup->GetName().c_str(), - }; - format_string(log_msg, 256, STR_LOG_ADD_PLAYER_GROUP, args); - network_append_server_log(log_msg); + return std::make_unique(GA_ERROR::UNKNOWN, STR_CANT_DO_THIS); } } } break; - case 1: - { // remove group - if (groupid == 0) + case ModifyGroupType::RemoveGroup: + { + if (groupId == 0) { - gGameCommandErrorTitle = STR_THIS_GROUP_CANNOT_BE_MODIFIED; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; + return std::make_unique(GA_ERROR::DISALLOWED, STR_THIS_GROUP_CANNOT_BE_MODIFIED); } - for (auto it = gNetwork.player_list.begin(); it != gNetwork.player_list.end(); it++) + for (const auto& it : gNetwork.player_list) { - if ((*it)->Group == groupid) + if ((it.get())->Group == groupId) { - gGameCommandErrorTitle = STR_CANT_REMOVE_GROUP_THAT_PLAYERS_BELONG_TO; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CANT_REMOVE_GROUP_THAT_PLAYERS_BELONG_TO); } } - if (*ebx & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { - // Log remove player group event - NetworkPlayer* game_command_player = gNetwork.GetPlayerByID(game_command_playerid); - NetworkGroup* group = gNetwork.GetGroupByID(groupid); - if (game_command_player && group) - { - char log_msg[256]; - const char* args[2] = { - game_command_player->Name.c_str(), - group->GetName().c_str(), - }; - format_string(log_msg, 256, STR_LOG_REMOVE_PLAYER_GROUP, args); - network_append_server_log(log_msg); - } - - gNetwork.RemoveGroup(groupid); + gNetwork.RemoveGroup(groupId); } } break; - case 2: - { // set permissions - int32_t index = *ecx; - bool all = *edx & 1; - bool allvalue = (*edx >> 1) & 1; - if (groupid == 0) + case ModifyGroupType::SetPermissions: + { + if (groupId == 0) { // cant change admin group permissions - gGameCommandErrorTitle = STR_THIS_GROUP_CANNOT_BE_MODIFIED; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; + return std::make_unique(GA_ERROR::DISALLOWED, STR_THIS_GROUP_CANNOT_BE_MODIFIED); } NetworkGroup* mygroup = nullptr; - NetworkPlayer* player = gNetwork.GetPlayerByID(game_command_playerid); - if (player && !all) + NetworkPlayer* player = gNetwork.GetPlayerByID(actionPlayerId); + if (player != nullptr && permissionState == PermissionState::Toggle) { mygroup = gNetwork.GetGroupByID(player->Group); - if (!mygroup || !mygroup->CanPerformAction(index)) + if (mygroup == nullptr || !mygroup->CanPerformAction(permissionIndex)) { - gGameCommandErrorTitle = STR_CANT_MODIFY_PERMISSION_THAT_YOU_DO_NOT_HAVE_YOURSELF; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; + return std::make_unique( + GA_ERROR::DISALLOWED, STR_CANT_MODIFY_PERMISSION_THAT_YOU_DO_NOT_HAVE_YOURSELF); } } - if (*ebx & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { - NetworkGroup* group = gNetwork.GetGroupByID(groupid); - if (group) + NetworkGroup* group = gNetwork.GetGroupByID(groupId); + if (group != nullptr) { - if (all) + if (permissionState != PermissionState::Toggle) { - if (mygroup) + if (mygroup != nullptr) { - if (allvalue) + if (permissionState == PermissionState::SetAll) { group->ActionsAllowed = mygroup->ActionsAllowed; } @@ -3482,155 +3510,87 @@ void game_command_modify_groups( } else { - group->ToggleActionPermission(index); + group->ToggleActionPermission(permissionIndex); } } - - // Log edit player group permissions event - char log_msg[256]; - const char* args[2] = { - player->Name.c_str(), - group->GetName().c_str(), - }; - format_string(log_msg, 256, STR_LOG_EDIT_PLAYER_GROUP_PERMISSIONS, args); - network_append_server_log(log_msg); } } break; - case 3: - { // set group name - NetworkGroup* group = gNetwork.GetGroupByID(groupid); + case ModifyGroupType::SetName: + { + NetworkGroup* group = gNetwork.GetGroupByID(groupId); const char* oldName = group->GetName().c_str(); - static char newName[128]; - size_t nameChunkOffset = nameChunkIndex - 1; - if (nameChunkIndex == 0) - nameChunkOffset = 2; - nameChunkOffset *= 12; - nameChunkOffset = (std::min)(nameChunkOffset, std::size(newName) - 12); - std::memcpy((void*)((uintptr_t)newName + (uintptr_t)nameChunkOffset + 0), edx, sizeof(uint32_t)); - std::memcpy((void*)((uintptr_t)newName + (uintptr_t)nameChunkOffset + 4), ebp, sizeof(uint32_t)); - std::memcpy((void*)((uintptr_t)newName + (uintptr_t)nameChunkOffset + 8), edi, sizeof(uint32_t)); - - if (nameChunkIndex != 0) + if (strcmp(oldName, name.c_str()) == 0) { - *ebx = 0; - return; + return std::make_unique(); } - if (strcmp(oldName, newName) == 0) + if (name.empty()) { - *ebx = 0; - return; + return std::make_unique( + GA_ERROR::INVALID_PARAMETERS, STR_CANT_RENAME_GROUP, STR_INVALID_GROUP_NAME); } - if (newName[0] == 0) + if (isExecuting) { - gGameCommandErrorTitle = STR_CANT_RENAME_GROUP; - gGameCommandErrorText = STR_INVALID_GROUP_NAME; - *ebx = MONEY32_UNDEFINED; - return; - } - - if (*ebx & GAME_COMMAND_FLAG_APPLY) - { - if (group) + if (group != nullptr) { - // Log edit player group name event - NetworkPlayer* player = gNetwork.GetPlayerByID(game_command_playerid); - char log_msg[256]; - const char* args[3] = { - player->Name.c_str(), - oldName, - newName, - }; - format_string(log_msg, 256, STR_LOG_EDIT_PLAYER_GROUP_NAME, args); - network_append_server_log(log_msg); - - group->SetName(newName); + group->SetName(name); } } } break; - case 4: - { // set default group - if (groupid == 0) + case ModifyGroupType::SetDefault: + { + if (groupId == 0) { - gGameCommandErrorTitle = STR_CANT_SET_TO_THIS_GROUP; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; + return std::make_unique(GA_ERROR::DISALLOWED, STR_CANT_SET_TO_THIS_GROUP); } - if (*ebx & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { - gNetwork.SetDefaultGroup(groupid); - - // Log edit default player group event - NetworkPlayer* player = gNetwork.GetPlayerByID(game_command_playerid); - NetworkGroup* group = gNetwork.GetGroupByID(groupid); - char log_msg[256]; - const char* args[2] = { - player->Name.c_str(), - group->GetName().c_str(), - }; - format_string(log_msg, 256, STR_LOG_EDIT_DEFAULT_PLAYER_GROUP, args); - network_append_server_log(log_msg); + gNetwork.SetDefaultGroup(groupId); } } break; + default: + log_error("Invalid Modify Group Type: %u", static_cast(type)); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_NONE); + break; } gNetwork.SaveGroups(); - *ebx = 0; + return std::make_unique(); } -void game_command_kick_player( - int32_t* eax, int32_t* ebx, [[maybe_unused]] int32_t* ecx, [[maybe_unused]] int32_t* edx, [[maybe_unused]] int32_t* esi, - [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) +GameActionResult::Ptr network_kick_player(NetworkPlayerId_t playerId, bool isExecuting) { - uint8_t playerid = (uint8_t)*eax; - NetworkPlayer* player = gNetwork.GetPlayerByID(playerid); - NetworkPlayer* kicker = gNetwork.GetPlayerByID(game_command_playerid); + NetworkPlayer* player = gNetwork.GetPlayerByID(playerId); if (player == nullptr) { // Player might be already removed by the PLAYERLIST command, need to refactor non-game commands executing too early. - return; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } if (player && player->Flags & NETWORK_PLAYER_FLAG_ISSERVER) { - gGameCommandErrorTitle = STR_CANT_KICK_THE_HOST; - gGameCommandErrorText = STR_NONE; - *ebx = MONEY32_UNDEFINED; - return; + return std::make_unique(GA_ERROR::DISALLOWED, STR_CANT_KICK_THE_HOST); } - if (*ebx & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { if (gNetwork.GetMode() == NETWORK_MODE_SERVER) { - gNetwork.KickPlayer(playerid); + gNetwork.KickPlayer(playerId); NetworkUserManager* networkUserManager = &gNetwork._userManager; networkUserManager->Load(); networkUserManager->RemoveUser(player->KeyHash); networkUserManager->Save(); } - - if (kicker != nullptr) - { - // Log kick player event - char log_msg[256]; - const char* args[2] = { - player->Name.c_str(), - kicker->Name.c_str(), - }; - format_string(log_msg, 256, STR_LOG_PLAYER_KICKED, args); - network_append_server_log(log_msg); - } } - *ebx = 0; + return std::make_unique(); } uint8_t network_get_default_group() @@ -3774,25 +3734,6 @@ void network_send_game_action(const GameAction* action) } } -void network_enqueue_game_action(const GameAction* action) -{ - gNetwork.EnqueueGameAction(action); -} - -void network_send_gamecmd( - uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi, uint32_t ebp, uint8_t callback) -{ - switch (gNetwork.GetMode()) - { - case NETWORK_MODE_SERVER: - gNetwork.Server_Send_GAMECMD(eax, ebx, ecx, edx, esi, edi, ebp, gNetwork.GetPlayerID(), callback); - break; - case NETWORK_MODE_CLIENT: - gNetwork.Client_Send_GAMECMD(eax, ebx, ecx, edx, esi, edi, ebp, callback); - break; - } -} - void network_send_password(const std::string& password) { utf8 keyPath[MAX_PATH]; @@ -3893,6 +3834,20 @@ NetworkStats_t network_get_stats() return gNetwork.GetStats(); } +NetworkServerState_t network_get_server_state() +{ + return gNetwork.GetServerState(); +} + +bool network_gamestate_snapshots_enabled() +{ + return network_get_server_state().gamestateSnapshotsEnabled; +} + +json_t* network_get_server_info_as_json() +{ + return gNetwork.GetServerInfoAsJson(); +} #else int32_t network_get_mode() { @@ -3916,14 +3871,19 @@ void network_flush() void network_send_tick() { } -void network_check_desynchronization() +bool network_is_desynchronised() { + return false; } -void network_enqueue_game_action(const GameAction* action) +bool network_gamestate_snapshots_enabled() { + return false; } -void network_send_gamecmd( - uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi, uint32_t ebp, uint8_t callback) +bool network_check_desynchronisation() +{ + return false; +} +void network_request_gamestate_snapshot() { } void network_send_game_action(const GameAction* action) @@ -4018,16 +3978,21 @@ const char* network_get_group_name(uint32_t index) { return ""; }; -void game_command_set_player_group( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp) + +GameActionResult::Ptr network_set_player_group( + NetworkPlayerId_t actionPlayerId, NetworkPlayerId_t playerId, uint8_t groupId, bool isExecuting) { + return std::make_unique(); } -void game_command_modify_groups( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp) +GameActionResult::Ptr network_modify_groups( + NetworkPlayerId_t actionPlayerId, ModifyGroupType type, uint8_t groupId, const std::string& name, uint32_t permissionIndex, + PermissionState permissionState, bool isExecuting) { + return std::make_unique(); } -void game_command_kick_player(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp) +GameActionResult::Ptr network_kick_player(NetworkPlayerId_t playerId, bool isExecuting) { + return std::make_unique(); } uint8_t network_get_default_group() { @@ -4074,6 +4039,9 @@ void network_send_password(const std::string& password) void network_close() { } +void network_reconnect() +{ +} void network_set_env(const std::shared_ptr&) { } @@ -4129,4 +4097,12 @@ NetworkStats_t network_get_stats() { return NetworkStats_t{}; } +NetworkServerState_t network_get_server_state() +{ + return NetworkServerState_t{}; +} +json_t* network_get_server_info_as_json() +{ + return nullptr; +} #endif /* DISABLE_NETWORK */ diff --git a/src/openrct2/network/NetworkAction.cpp b/src/openrct2/network/NetworkAction.cpp index af28b7e488..368b506f96 100644 --- a/src/openrct2/network/NetworkAction.cpp +++ b/src/openrct2/network/NetworkAction.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -171,6 +171,7 @@ const std::array NetworkActions::Action GAME_COMMAND_SET_GUEST_NAME, GAME_COMMAND_PICKUP_GUEST, GAME_COMMAND_BALLOON_PRESS, + GAME_COMMAND_GUEST_SET_FLAGS, }, }, NetworkAction{ @@ -236,6 +237,7 @@ const std::array NetworkActions::Action "PERMISSION_CHEAT", { GAME_COMMAND_CHEAT, + GAME_COMMAND_SET_DATE, }, }, NetworkAction{ diff --git a/src/openrct2/network/NetworkAction.h b/src/openrct2/network/NetworkAction.h index b3ba7277be..fa8cb27389 100644 --- a/src/openrct2/network/NetworkAction.h +++ b/src/openrct2/network/NetworkAction.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,7 @@ #include #include +#include enum MISC_COMMAND { @@ -55,7 +56,7 @@ class NetworkAction final public: rct_string_id Name; std::string PermissionName; - std::array Commands; + std::vector Commands; }; class NetworkActions final diff --git a/src/openrct2/network/NetworkConnection.cpp b/src/openrct2/network/NetworkConnection.cpp index 4236cd1c14..da9ab8d6cd 100644 --- a/src/openrct2/network/NetworkConnection.cpp +++ b/src/openrct2/network/NetworkConnection.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,7 +14,7 @@ # include "../core/String.hpp" # include "../localisation/Localisation.h" # include "../platform/platform.h" -# include "TcpSocket.h" +# include "Socket.h" # include "network.h" constexpr size_t NETWORK_DISCONNECT_REASON_BUFFER_SIZE = 256; @@ -191,7 +191,6 @@ void NetworkConnection::RecordPacketStats(const NetworkPacket& packet, bool send switch (packet.GetCommand()) { - case NETWORK_COMMAND_GAMECMD: case NETWORK_COMMAND_GAME_ACTION: trafficGroup = NETWORK_STATISTICS_GROUP_COMMANDS; break; diff --git a/src/openrct2/network/NetworkConnection.h b/src/openrct2/network/NetworkConnection.h index 473c0c3c67..26803aff16 100644 --- a/src/openrct2/network/NetworkConnection.h +++ b/src/openrct2/network/NetworkConnection.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,7 +14,7 @@ # include "NetworkKey.h" # include "NetworkPacket.h" # include "NetworkTypes.h" -# include "TcpSocket.h" +# include "Socket.h" # include # include @@ -35,6 +35,7 @@ public: NetworkKey Key; std::vector Challenge; std::vector RequestedObjects; + bool IsDisconnected = false; NetworkConnection(); ~NetworkConnection(); diff --git a/src/openrct2/network/NetworkGroup.cpp b/src/openrct2/network/NetworkGroup.cpp index c03bc4e203..318e6f2482 100644 --- a/src/openrct2/network/NetworkGroup.cpp +++ b/src/openrct2/network/NetworkGroup.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/NetworkGroup.h b/src/openrct2/network/NetworkGroup.h index f8e677ca67..07d7d5c878 100644 --- a/src/openrct2/network/NetworkGroup.h +++ b/src/openrct2/network/NetworkGroup.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/NetworkKey.cpp b/src/openrct2/network/NetworkKey.cpp index 22dc2c4af7..3312dc5284 100644 --- a/src/openrct2/network/NetworkKey.cpp +++ b/src/openrct2/network/NetworkKey.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/NetworkKey.h b/src/openrct2/network/NetworkKey.h index 382f83cc01..5811d5a845 100644 --- a/src/openrct2/network/NetworkKey.h +++ b/src/openrct2/network/NetworkKey.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/NetworkPacket.cpp b/src/openrct2/network/NetworkPacket.cpp index 90a3de78a6..f613a699fa 100644 --- a/src/openrct2/network/NetworkPacket.cpp +++ b/src/openrct2/network/NetworkPacket.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/NetworkPacket.h b/src/openrct2/network/NetworkPacket.h index 59aacdbf1f..6b9352c899 100644 --- a/src/openrct2/network/NetworkPacket.h +++ b/src/openrct2/network/NetworkPacket.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/NetworkPlayer.cpp b/src/openrct2/network/NetworkPlayer.cpp index c9ca397715..ccaee8f41c 100644 --- a/src/openrct2/network/NetworkPlayer.cpp +++ b/src/openrct2/network/NetworkPlayer.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/NetworkPlayer.h b/src/openrct2/network/NetworkPlayer.h index 355198fdea..6b128258ac 100644 --- a/src/openrct2/network/NetworkPlayer.h +++ b/src/openrct2/network/NetworkPlayer.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -15,6 +15,7 @@ #include "../world/Sprite.h" #include +#include class NetworkPacket; @@ -36,7 +37,7 @@ public: std::string KeyHash; uint32_t LastDemolishRideTime = 0; uint32_t LastPlaceSceneryTime = 0; - + std::unordered_map CooldownTime; NetworkPlayer() = default; void SetName(const std::string& name); diff --git a/src/openrct2/network/NetworkServerAdvertiser.cpp b/src/openrct2/network/NetworkServerAdvertiser.cpp index 97fba2a075..a18865d469 100644 --- a/src/openrct2/network/NetworkServerAdvertiser.cpp +++ b/src/openrct2/network/NetworkServerAdvertiser.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,21 +18,21 @@ # include "../localisation/Date.h" # include "../management/Finance.h" # include "../peep/Peep.h" +# include "../platform/Platform2.h" # include "../platform/platform.h" # include "../util/Util.h" # include "../world/Map.h" # include "../world/Park.h" # include "Http.h" +# include "Socket.h" # include "network.h" +# include # include # include +# include # include -# ifndef DISABLE_HTTP - -using namespace OpenRCT2::Network; - enum MASTER_SERVER_STATUS { MASTER_SERVER_STATUS_OK = 200, @@ -41,15 +41,22 @@ enum MASTER_SERVER_STATUS MASTER_SERVER_STATUS_INTERNAL_ERROR = 500 }; +# ifndef DISABLE_HTTP constexpr int32_t MASTER_SERVER_REGISTER_TIME = 120 * 1000; // 2 minutes constexpr int32_t MASTER_SERVER_HEARTBEAT_TIME = 60 * 1000; // 1 minute +# endif class NetworkServerAdvertiser final : public INetworkServerAdvertiser { private: uint16_t _port; + std::unique_ptr _lanListener; + uint32_t _lastListenTime{}; + ADVERTISE_STATUS _status = ADVERTISE_STATUS::UNREGISTERED; + +# ifndef DISABLE_HTTP uint32_t _lastAdvertiseTime = 0; uint32_t _lastHeartbeatTime = 0; @@ -61,12 +68,16 @@ private: // See https://github.com/OpenRCT2/OpenRCT2/issues/6277 and 4953 bool _forceIPv4 = false; +# endif public: explicit NetworkServerAdvertiser(uint16_t port) { _port = port; + _lanListener = CreateUdpSocket(); +# ifndef DISABLE_HTTP _key = GenerateAdvertiseKey(); +# endif } ADVERTISE_STATUS GetStatus() const override @@ -75,6 +86,61 @@ public: } void Update() override + { + UpdateLAN(); +# ifndef DISABLE_HTTP + if (gConfigNetwork.advertise) + { + UpdateWAN(); + } +# endif + } + +private: + void UpdateLAN() + { + auto ticks = Platform::GetTicks(); + if (ticks > _lastListenTime + 500) + { + if (_lanListener->GetStatus() != SOCKET_STATUS_LISTENING) + { + _lanListener->Listen(NETWORK_LAN_BROADCAST_PORT); + } + else + { + char buffer[256]{}; + size_t recievedBytes{}; + std::unique_ptr endpoint; + auto p = _lanListener->ReceiveData(buffer, sizeof(buffer) - 1, &recievedBytes, &endpoint); + if (p == NETWORK_READPACKET_SUCCESS) + { + std::string sender = endpoint->GetHostname(); + log_verbose("Received %zu bytes from %s on LAN broadcast port", recievedBytes, sender.c_str()); + if (String::Equals(buffer, NETWORK_LAN_BROADCAST_MSG)) + { + auto body = GetBroadcastJson(); + auto bodyDump = json_dumps(body, JSON_COMPACT); + size_t sendLen = strlen(bodyDump) + 1; + log_verbose("Sending %zu bytes back to %s", sendLen, sender.c_str()); + _lanListener->SendData(*endpoint, bodyDump, sendLen); + free(bodyDump); + json_decref(body); + } + } + } + _lastListenTime = ticks; + } + } + + json_t* GetBroadcastJson() + { + auto root = network_get_server_info_as_json(); + json_object_set(root, "port", json_integer(_port)); + return root; + } + +# ifndef DISABLE_HTTP + void UpdateWAN() { switch (_status) { @@ -96,9 +162,10 @@ public: } } -private: void SendRegistration(bool forceIPv4) { + using namespace OpenRCT2::Networking; + _lastAdvertiseTime = platform_get_ticks(); // Send the registration request @@ -132,6 +199,8 @@ private: void SendHeartbeat() { + using namespace OpenRCT2::Networking; + Http::Request request; request.url = GetMasterServerUrl(); request.method = Http::Method::PUT; @@ -241,10 +310,14 @@ private: static constexpr char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', }; + + std::random_device rd; + std::uniform_int_distribution dist(0, static_cast(std::size(hexChars) - 1)); + char key[17]; for (int32_t i = 0; i < 16; i++) { - int32_t hexCharIndex = util_rand() % std::size(hexChars); + int32_t hexCharIndex = dist(rd); key[i] = hexChars[hexCharIndex]; } key[std::size(key) - 1] = 0; @@ -254,12 +327,13 @@ private: static std::string GetMasterServerUrl() { std::string result = OPENRCT2_MASTER_SERVER_URL; - if (gConfigNetwork.master_server_url.empty() == false) + if (!gConfigNetwork.master_server_url.empty()) { result = gConfigNetwork.master_server_url; } return result; } +# endif }; std::unique_ptr CreateServerAdvertiser(uint16_t port) @@ -267,23 +341,4 @@ std::unique_ptr CreateServerAdvertiser(uint16_t port) return std::make_unique(port); } -# else // DISABLE_HTTP - -class DummyNetworkServerAdvertiser final : public INetworkServerAdvertiser -{ -public: - virtual ADVERTISE_STATUS GetStatus() const override - { - return ADVERTISE_STATUS::DISABLED; - }; - virtual void Update() override{}; -}; - -std::unique_ptr CreateServerAdvertiser(uint16_t port) -{ - return std::make_unique(); -} - -# endif // DISABLE_HTTP - #endif // DISABLE_NETWORK diff --git a/src/openrct2/network/NetworkServerAdvertiser.h b/src/openrct2/network/NetworkServerAdvertiser.h index 3ebfdc2dfd..0ab239acc2 100644 --- a/src/openrct2/network/NetworkServerAdvertiser.h +++ b/src/openrct2/network/NetworkServerAdvertiser.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/NetworkTypes.h b/src/openrct2/network/NetworkTypes.h index e48a3b6059..4feb491b2c 100644 --- a/src/openrct2/network/NetworkTypes.h +++ b/src/openrct2/network/NetworkTypes.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -52,8 +52,7 @@ enum NETWORK_COMMAND NETWORK_COMMAND_AUTH, NETWORK_COMMAND_MAP, NETWORK_COMMAND_CHAT, - NETWORK_COMMAND_GAMECMD, - NETWORK_COMMAND_TICK, + NETWORK_COMMAND_TICK = 4, NETWORK_COMMAND_PLAYERLIST, NETWORK_COMMAND_PING, NETWORK_COMMAND_PINGLIST, @@ -65,10 +64,30 @@ enum NETWORK_COMMAND NETWORK_COMMAND_TOKEN, NETWORK_COMMAND_OBJECTS, NETWORK_COMMAND_GAME_ACTION, + NETWORK_COMMAND_PLAYERINFO, + NETWORK_COMMAND_REQUEST_GAMESTATE, + NETWORK_COMMAND_GAMESTATE, NETWORK_COMMAND_MAX, NETWORK_COMMAND_INVALID = -1 }; +static_assert(NETWORK_COMMAND::NETWORK_COMMAND_GAMEINFO == 9, "Master server expects this to be 9"); + +enum NETWORK_SERVER_STATE +{ + NETWORK_SERVER_STATE_OK, + NETWORK_SERVER_STATE_DESYNCED, +}; + +struct NetworkServerState_t +{ + NETWORK_SERVER_STATE state = NETWORK_SERVER_STATE_OK; + uint32_t desyncTick = 0; + uint32_t tick = 0; + uint32_t srand0 = 0; + bool gamestateSnapshotsEnabled = false; +}; + // Structure is used for networking specific fields with meaning, // this structure can be used in combination with DataSerialiser // to provide extra details with template specialization. @@ -95,6 +114,7 @@ template struct NetworkObjectId_t // there is no way to specialize templates if they have the exact symbol. using NetworkPlayerId_t = NetworkObjectId_t; using NetworkRideId_t = NetworkObjectId_t; +using NetworkCheatType_t = NetworkObjectId_t; enum NetworkStatisticsGroup { diff --git a/src/openrct2/network/NetworkUser.cpp b/src/openrct2/network/NetworkUser.cpp index ffd21e5097..5540abf698 100644 --- a/src/openrct2/network/NetworkUser.cpp +++ b/src/openrct2/network/NetworkUser.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/NetworkUser.h b/src/openrct2/network/NetworkUser.h index f3e048f2ce..78a99289d0 100644 --- a/src/openrct2/network/NetworkUser.h +++ b/src/openrct2/network/NetworkUser.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/network/ServerList.cpp b/src/openrct2/network/ServerList.cpp index 0148492a37..7bc8c18e75 100644 --- a/src/openrct2/network/ServerList.cpp +++ b/src/openrct2/network/ServerList.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -7,22 +7,155 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ -#include "ServerList.h" +#ifndef DISABLE_NETWORK -#include "../Context.h" -#include "../PlatformEnvironment.h" -#include "../core/FileStream.hpp" -#include "../core/Memory.hpp" -#include "../core/Path.hpp" -#include "../core/String.hpp" -#include "../platform/platform.h" +# include "ServerList.h" + +# include "../Context.h" +# include "../PlatformEnvironment.h" +# include "../config/Config.h" +# include "../core/FileStream.hpp" +# include "../core/Json.hpp" +# include "../core/Memory.hpp" +# include "../core/Path.hpp" +# include "../core/String.hpp" +# include "../network/Http.h" +# include "../platform/platform.h" +# include "Socket.h" +# include "network.h" + +# include +# include using namespace OpenRCT2; -std::vector server_list_read() +int32_t ServerListEntry::CompareTo(const ServerListEntry& other) const +{ + const auto& a = *this; + const auto& b = other; + + // Order by favourite + if (a.favourite != b.favourite) + { + return a.favourite ? -1 : 1; + } + + // Order by local + if (a.local != b.local) + { + return a.local ? -1 : 1; + } + + // Then by version + bool serverACompatible = a.version == network_get_version(); + bool serverBCompatible = b.version == network_get_version(); + if (serverACompatible != serverBCompatible) + { + return serverACompatible ? -1 : 1; + } + + // Then by password protection + if (a.requiresPassword != b.requiresPassword) + { + return a.requiresPassword ? 1 : -1; + } + + // Then by number of players + if (a.players != b.players) + { + return a.players > b.players ? -1 : 1; + } + + // Then by name + return String::Compare(a.name, b.name, true); +} + +bool ServerListEntry::IsVersionValid() const +{ + return version.empty() || version == network_get_version(); +} + +opt::optional ServerListEntry::FromJson(const json_t* server) +{ + auto port = json_object_get(server, "port"); + auto name = json_object_get(server, "name"); + auto description = json_object_get(server, "description"); + auto requiresPassword = json_object_get(server, "requiresPassword"); + auto version = json_object_get(server, "version"); + auto players = json_object_get(server, "players"); + auto maxPlayers = json_object_get(server, "maxPlayers"); + auto ip = json_object_get(server, "ip"); + auto ip4 = json_object_get(ip, "v4"); + auto addressIp = json_array_get(ip4, 0); + + if (name == nullptr || version == nullptr) + { + log_verbose("Cowardly refusing to add server without name or version specified."); + return {}; + } + else + { + ServerListEntry entry; + entry.address = String::StdFormat("%s:%d", json_string_value(addressIp), (int32_t)json_integer_value(port)); + entry.name = (name == nullptr ? "" : json_string_value(name)); + entry.description = (description == nullptr ? "" : json_string_value(description)); + entry.version = json_string_value(version); + entry.requiresPassword = json_is_true(requiresPassword); + entry.players = (uint8_t)json_integer_value(players); + entry.maxplayers = (uint8_t)json_integer_value(maxPlayers); + return entry; + } +} + +void ServerList::Sort() +{ + _serverEntries.erase( + std::unique( + _serverEntries.begin(), _serverEntries.end(), + [](const ServerListEntry& a, const ServerListEntry& b) { + if (a.favourite == b.favourite) + { + return String::Equals(a.address, b.address, true); + } + return false; + }), + _serverEntries.end()); + std::sort(_serverEntries.begin(), _serverEntries.end(), [](const ServerListEntry& a, const ServerListEntry& b) { + return a.CompareTo(b) < 0; + }); +} + +ServerListEntry& ServerList::GetServer(size_t index) +{ + return _serverEntries[index]; +} + +size_t ServerList::GetCount() const +{ + return _serverEntries.size(); +} + +void ServerList::Add(const ServerListEntry& entry) +{ + _serverEntries.push_back(entry); + Sort(); +} + +void ServerList::AddRange(const std::vector& entries) +{ + _serverEntries.insert(_serverEntries.end(), entries.begin(), entries.end()); + Sort(); +} + +void ServerList::Clear() +{ + _serverEntries.clear(); +} + +std::vector ServerList::ReadFavourites() const { log_verbose("server_list_read(...)"); - std::vector entries; + std::vector entries; try { auto env = GetContext()->GetPlatformEnvironment(); @@ -33,7 +166,7 @@ std::vector server_list_read() auto numEntries = fs.ReadValue(); for (size_t i = 0; i < numEntries; i++) { - server_entry serverInfo; + ServerListEntry serverInfo; serverInfo.address = fs.ReadStdString(); serverInfo.name = fs.ReadStdString(); serverInfo.requiresPassword = false; @@ -49,12 +182,32 @@ std::vector server_list_read() catch (const std::exception& e) { log_error("Unable to read server list: %s", e.what()); - entries = std::vector(); + entries = std::vector(); } return entries; } -bool server_list_write(const std::vector& entries) +void ServerList::ReadAndAddFavourites() +{ + _serverEntries.erase( + std::remove_if( + _serverEntries.begin(), _serverEntries.end(), [](const ServerListEntry& entry) { return entry.favourite; }), + _serverEntries.end()); + auto entries = ReadFavourites(); + AddRange(entries); +} + +void ServerList::WriteFavourites() const +{ + // Save just favourite servers + std::vector favouriteServers; + std::copy_if( + _serverEntries.begin(), _serverEntries.end(), std::back_inserter(favouriteServers), + [](const ServerListEntry& entry) { return entry.favourite; }); + WriteFavourites(favouriteServers); +} + +bool ServerList::WriteFavourites(const std::vector& entries) const { log_verbose("server_list_write(%d, 0x%p)", entries.size(), entries.data()); @@ -80,3 +233,178 @@ bool server_list_write(const std::vector& entries) return false; } } + +std::future> ServerList::FetchLocalServerListAsync(const INetworkEndpoint& broadcastEndpoint) const +{ + auto broadcastAddress = broadcastEndpoint.GetHostname(); + return std::async(std::launch::async, [broadcastAddress] { + constexpr auto RECV_DELAY_MS = 10; + constexpr auto RECV_WAIT_MS = 2000; + + std::string_view msg = NETWORK_LAN_BROADCAST_MSG; + auto udpSocket = CreateUdpSocket(); + + log_verbose("Broadcasting %zu bytes to the LAN (%s)", msg.size(), broadcastAddress.c_str()); + auto len = udpSocket->SendData(broadcastAddress, NETWORK_LAN_BROADCAST_PORT, msg.data(), msg.size()); + if (len != msg.size()) + { + throw std::runtime_error("Unable to broadcast server query."); + } + + std::vector entries; + for (int i = 0; i < (RECV_WAIT_MS / RECV_DELAY_MS); i++) + { + try + { + // Start with initialised buffer in case we receive a non-terminated string + char buffer[1024]{}; + size_t recievedLen{}; + std::unique_ptr endpoint; + auto p = udpSocket->ReceiveData(buffer, sizeof(buffer) - 1, &recievedLen, &endpoint); + if (p == NETWORK_READPACKET_SUCCESS) + { + auto sender = endpoint->GetHostname(); + log_verbose("Received %zu bytes back from %s", recievedLen, sender.c_str()); + auto jinfo = Json::FromString(std::string_view(buffer)); + + auto ip4 = json_array(); + json_array_append_new(ip4, json_string(sender.c_str())); + auto ip = json_object(); + json_object_set_new(ip, "v4", ip4); + json_object_set_new(jinfo, "ip", ip); + + auto entry = ServerListEntry::FromJson(jinfo); + if (entry.has_value()) + { + (*entry).local = true; + entries.push_back(*entry); + } + + json_decref(jinfo); + } + } + catch (const std::exception& e) + { + log_warning("Error receiving data: %s", e.what()); + } + platform_sleep(RECV_DELAY_MS); + } + return entries; + }); +} + +std::future> ServerList::FetchLocalServerListAsync() const +{ + return std::async(std::launch::async, [&] { + // Get all possible LAN broadcast addresses + auto broadcastEndpoints = GetBroadcastAddresses(); + + // Spin off a fetch for each broadcast address + std::vector>> futures; + for (const auto& broadcastEndpoint : broadcastEndpoints) + { + auto f = FetchLocalServerListAsync(*broadcastEndpoint); + futures.push_back(std::move(f)); + } + + // Wait and merge all results + std::vector mergedEntries; + for (auto& f : futures) + { + try + { + auto entries = f.get(); + mergedEntries.insert(mergedEntries.begin(), entries.begin(), entries.end()); + } + catch (...) + { + // Ignore any exceptions from a particular broadcast fetch + } + } + return mergedEntries; + }); +} + +std::future> ServerList::FetchOnlineServerListAsync() const +{ +# ifdef DISABLE_HTTP + return {}; +# else + using namespace OpenRCT2::Networking; + + auto p = std::make_shared>>(); + auto f = p->get_future(); + + std::string masterServerUrl = OPENRCT2_MASTER_SERVER_URL; + if (!gConfigNetwork.master_server_url.empty()) + { + masterServerUrl = gConfigNetwork.master_server_url; + } + + Http::Request request; + request.url = masterServerUrl; + request.method = Http::Method::GET; + request.header["Accept"] = "application/json"; + Http::DoAsync(request, [p](Http::Response& response) -> void { + json_t* root{}; + try + { + if (response.status != Http::Status::OK) + { + throw MasterServerException(STR_SERVER_LIST_NO_CONNECTION); + } + + root = Json::FromString(response.body); + auto jsonStatus = json_object_get(root, "status"); + if (!json_is_number(jsonStatus)) + { + throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_NUMBER); + } + + auto status = (int32_t)json_integer_value(jsonStatus); + if (status != 200) + { + throw MasterServerException(STR_SERVER_LIST_MASTER_SERVER_FAILED); + } + + auto jServers = json_object_get(root, "servers"); + if (!json_is_array(jServers)) + { + throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_ARRAY); + } + + std::vector entries; + auto count = json_array_size(jServers); + for (size_t i = 0; i < count; i++) + { + auto jServer = json_array_get(jServers, i); + if (json_is_object(jServer)) + { + auto entry = ServerListEntry::FromJson(jServer); + if (entry.has_value()) + { + entries.push_back(*entry); + } + } + } + + p->set_value(entries); + } + catch (...) + { + p->set_exception(std::current_exception()); + } + json_decref(root); + }); + return f; +# endif +} + +uint32_t ServerList::GetTotalPlayerCount() const +{ + return std::accumulate(_serverEntries.begin(), _serverEntries.end(), 0, [](uint32_t acc, const ServerListEntry& entry) { + return acc + entry.players; + }); +} + +#endif diff --git a/src/openrct2/network/ServerList.h b/src/openrct2/network/ServerList.h index 8207d8d1a5..b0cb5e1bc2 100644 --- a/src/openrct2/network/ServerList.h +++ b/src/openrct2/network/ServerList.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,21 +10,66 @@ #pragma once #include "../common.h" +#include "../core/Optional.hpp" +#include +#include #include #include -struct server_entry +struct json_t; +struct INetworkEndpoint; + +struct ServerListEntry { std::string address; std::string name; std::string description; std::string version; - bool requiresPassword = false; - bool favourite = false; - uint8_t players = 0; - uint8_t maxplayers = 0; + bool requiresPassword{}; + bool favourite{}; + uint8_t players{}; + uint8_t maxplayers{}; + bool local{}; + + int32_t CompareTo(const ServerListEntry& other) const; + bool IsVersionValid() const; + + static opt::optional FromJson(const json_t* root); }; -std::vector server_list_read(); -bool server_list_write(const std::vector& entries); +class ServerList +{ +private: + std::vector _serverEntries; + + void Sort(); + std::vector ReadFavourites() const; + bool WriteFavourites(const std::vector& entries) const; + std::future> FetchLocalServerListAsync(const INetworkEndpoint& broadcastEndpoint) const; + +public: + ServerListEntry& GetServer(size_t index); + size_t GetCount() const; + void Add(const ServerListEntry& entry); + void AddRange(const std::vector& entries); + void Clear(); + + void ReadAndAddFavourites(); + void WriteFavourites() const; + + std::future> FetchLocalServerListAsync() const; + std::future> FetchOnlineServerListAsync() const; + uint32_t GetTotalPlayerCount() const; +}; + +class MasterServerException : public std::exception +{ +public: + rct_string_id StatusText; + + MasterServerException(rct_string_id statusText) + : StatusText(statusText) + { + } +}; diff --git a/src/openrct2/network/TcpSocket.cpp b/src/openrct2/network/Socket.cpp similarity index 57% rename from src/openrct2/network/TcpSocket.cpp rename to src/openrct2/network/Socket.cpp index 2da8ad3673..6ecda08baf 100644 --- a/src/openrct2/network/TcpSocket.cpp +++ b/src/openrct2/network/Socket.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -9,6 +9,7 @@ #ifndef DISABLE_NETWORK +# include # include # include # include @@ -36,13 +37,15 @@ #endif #define FLAG_NO_PIPE 0 #else - #include #include - #include - #include - #include - #include + #include #include + #include + #include + #include + #include + #include + #include #include "../common.h" using SOCKET = int32_t; #define SOCKET_ERROR -1 @@ -58,7 +61,7 @@ #endif // _WIN32 // clang-format on -# include "TcpSocket.h" +# include "Socket.h" constexpr auto CONNECT_TIMEOUT = std::chrono::milliseconds(3000); @@ -66,8 +69,6 @@ constexpr auto CONNECT_TIMEOUT = std::chrono::milliseconds(3000); static bool _wsaInitialised = false; # endif -class TcpSocket; - class SocketException : public std::runtime_error { public: @@ -77,10 +78,126 @@ public: } }; -class TcpSocket final : public ITcpSocket +class NetworkEndpoint final : public INetworkEndpoint { private: - SOCKET_STATUS _status = SOCKET_STATUS_CLOSED; + sockaddr _address{}; + socklen_t _addressLen{}; + +public: + NetworkEndpoint() + { + } + + NetworkEndpoint(const sockaddr* address, socklen_t addressLen) + { + std::memcpy(&_address, address, addressLen); + _addressLen = addressLen; + } + + const sockaddr& GetAddress() const + { + return _address; + } + + socklen_t GetAddressLen() const + { + return _addressLen; + } + + int32_t GetPort() const + { + if (_address.sa_family == AF_INET) + { + return ((sockaddr_in*)&_address)->sin_port; + } + else + { + return ((sockaddr_in6*)&_address)->sin6_port; + } + } + + std::string GetHostname() const override + { + char hostname[256]{}; + int res = getnameinfo(&_address, _addressLen, hostname, sizeof(hostname), nullptr, 0, NI_NUMERICHOST); + if (res == 0) + { + return hostname; + } + return {}; + } +}; + +class Socket +{ +protected: + static bool ResolveAddress(const std::string& address, uint16_t port, sockaddr_storage* ss, socklen_t* ss_len) + { + return ResolveAddress(AF_UNSPEC, address, port, ss, ss_len); + } + + static bool ResolveAddressIPv4(const std::string& address, uint16_t port, sockaddr_storage* ss, socklen_t* ss_len) + { + return ResolveAddress(AF_INET, address, port, ss, ss_len); + } + + static bool SetNonBlocking(SOCKET socket, bool on) + { +# ifdef _WIN32 + u_long nonBlocking = on; + return ioctlsocket(socket, FIONBIO, &nonBlocking) == 0; +# else + int32_t flags = fcntl(socket, F_GETFL, 0); + return fcntl(socket, F_SETFL, on ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK)) == 0; +# endif + } + + static bool SetOption(SOCKET socket, int32_t a, int32_t b, bool value) + { + int32_t ivalue = value ? 1 : 0; + return setsockopt(socket, a, b, (const char*)&ivalue, sizeof(ivalue)) == 0; + } + +private: + static bool ResolveAddress( + int32_t family, const std::string& address, uint16_t port, sockaddr_storage* ss, socklen_t* ss_len) + { + std::string serviceName = std::to_string(port); + + addrinfo hints = {}; + hints.ai_family = family; + if (address.empty()) + { + hints.ai_flags = AI_PASSIVE; + } + + addrinfo* result = nullptr; + int errorcode = getaddrinfo(address.empty() ? nullptr : address.c_str(), serviceName.c_str(), &hints, &result); + if (errorcode != 0) + { + log_error("Resolving address failed: Code %d.", errorcode); + log_error("Resolution error message: %s.", gai_strerror(errorcode)); + return false; + } + if (result == nullptr) + { + return false; + } + else + { + std::memcpy(ss, result->ai_addr, result->ai_addrlen); + *ss_len = (socklen_t)result->ai_addrlen; + freeaddrinfo(result); + return true; + } + } +}; + +class TcpSocket final : public ITcpSocket, protected Socket +{ +private: + std::atomic _status = ATOMIC_VAR_INIT(SOCKET_STATUS_CLOSED); uint16_t _listeningPort = 0; SOCKET _socket = INVALID_SOCKET; @@ -100,12 +217,12 @@ public: CloseSocket(); } - SOCKET_STATUS GetStatus() override + SOCKET_STATUS GetStatus() const override { return _status; } - const char* GetError() override + const char* GetError() const override { return _error.empty() ? nullptr : _error.c_str(); } @@ -123,7 +240,7 @@ public: } sockaddr_storage ss{}; - int32_t ss_len; + socklen_t ss_len; if (!ResolveAddress(address, port, &ss, &ss_len)) { throw SocketException("Unable to resolve address."); @@ -209,7 +326,7 @@ public: int32_t rc = getnameinfo( (struct sockaddr*)&client_addr, client_len, hostName, sizeof(hostName), nullptr, 0, NI_NUMERICHOST | NI_NUMERICSERV); - SetTCPNoDelay(socket, true); + SetOption(socket, IPPROTO_TCP, TCP_NODELAY, true); if (rc == 0) { tcpSocket = std::unique_ptr(new TcpSocket(socket, hostName)); @@ -236,7 +353,7 @@ public: _status = SOCKET_STATUS_RESOLVING; sockaddr_storage ss{}; - int32_t ss_len; + socklen_t ss_len; if (!ResolveAddress(address, port, &ss, &ss_len)) { throw SocketException("Unable to resolve address."); @@ -249,7 +366,7 @@ public: throw SocketException("Unable to create socket."); } - SetTCPNoDelay(_socket, true); + SetOption(_socket, IPPROTO_TCP, TCP_NODELAY, true); if (!SetNonBlocking(_socket, true)) { throw SocketException("Failed to set non-blocking mode."); @@ -446,61 +563,212 @@ private: } _status = SOCKET_STATUS_CLOSED; } +}; - bool ResolveAddress(const std::string& address, uint16_t port, sockaddr_storage* ss, int32_t* ss_len) +class UdpSocket final : public IUdpSocket, protected Socket +{ +private: + SOCKET_STATUS _status = SOCKET_STATUS_CLOSED; + uint16_t _listeningPort = 0; + SOCKET _socket = INVALID_SOCKET; + NetworkEndpoint _endpoint; + + std::string _hostName; + std::string _error; + +public: + UdpSocket() = default; + + ~UdpSocket() override { - std::string serviceName = std::to_string(port); + CloseSocket(); + } - addrinfo hints = {}; - hints.ai_family = AF_UNSPEC; - if (address.empty()) + SOCKET_STATUS GetStatus() const override + { + return _status; + } + + const char* GetError() const override + { + return _error.empty() ? nullptr : _error.c_str(); + } + + void Listen(uint16_t port) override + { + Listen("", port); + } + + void Listen(const std::string& address, uint16_t port) override + { + if (_status != SOCKET_STATUS_CLOSED) { - hints.ai_flags = AI_PASSIVE; + throw std::runtime_error("Socket not closed."); } - addrinfo* result = nullptr; - int errorcode = getaddrinfo(address.empty() ? nullptr : address.c_str(), serviceName.c_str(), &hints, &result); - if (errorcode != 0) + sockaddr_storage ss{}; + socklen_t ss_len; + if (!ResolveAddressIPv4(address, port, &ss, &ss_len)) { - log_error("Resolving address failed: Code %d.", errorcode); - log_error("Resolution error message: %s.", gai_strerror(errorcode)); - return false; + throw SocketException("Unable to resolve address."); } - if (result == nullptr) + + // Create the listening socket + _socket = CreateSocket(); + try { - return false; + // Bind to address:port and listen + if (bind(_socket, (sockaddr*)&ss, ss_len) != 0) + { + throw SocketException("Unable to bind to socket."); + } + } + catch (const std::exception&) + { + CloseSocket(); + throw; + } + + _listeningPort = port; + _status = SOCKET_STATUS_LISTENING; + } + + size_t SendData(const std::string& address, uint16_t port, const void* buffer, size_t size) override + { + sockaddr_storage ss{}; + socklen_t ss_len; + if (!ResolveAddressIPv4(address, port, &ss, &ss_len)) + { + throw SocketException("Unable to resolve address."); + } + NetworkEndpoint endpoint((const sockaddr*)&ss, ss_len); + return SendData(endpoint, buffer, size); + } + + size_t SendData(const INetworkEndpoint& destination, const void* buffer, size_t size) override + { + if (_socket == INVALID_SOCKET) + { + _socket = CreateSocket(); + } + + const auto& dest = dynamic_cast(&destination); + if (dest == nullptr) + { + throw std::invalid_argument("destination is not compatible."); + } + auto ss = &dest->GetAddress(); + auto ss_len = dest->GetAddressLen(); + + if (_status != SOCKET_STATUS_LISTENING) + { + _endpoint = *dest; + } + + size_t totalSent = 0; + do + { + const char* bufferStart = (const char*)buffer + totalSent; + size_t remainingSize = size - totalSent; + int32_t sentBytes = sendto(_socket, bufferStart, (int32_t)remainingSize, FLAG_NO_PIPE, (const sockaddr*)ss, ss_len); + if (sentBytes == SOCKET_ERROR) + { + return totalSent; + } + totalSent += sentBytes; + } while (totalSent < size); + return totalSent; + } + + NETWORK_READPACKET ReceiveData( + void* buffer, size_t size, size_t* sizeReceived, std::unique_ptr* sender) override + { + sockaddr_in senderAddr{}; + socklen_t senderAddrLen = sizeof(sockaddr_in); + if (_status != SOCKET_STATUS_LISTENING) + { + senderAddrLen = _endpoint.GetAddressLen(); + std::memcpy(&senderAddr, &_endpoint.GetAddress(), senderAddrLen); + } + auto readBytes = recvfrom(_socket, (char*)buffer, (int32_t)size, 0, (sockaddr*)&senderAddr, &senderAddrLen); + if (readBytes <= 0) + { + *sizeReceived = 0; + return NETWORK_READPACKET_NO_DATA; } else { - std::memcpy(ss, result->ai_addr, result->ai_addrlen); - *ss_len = (int32_t)result->ai_addrlen; - freeaddrinfo(result); - return true; + *sizeReceived = readBytes; + if (sender != nullptr) + { + *sender = std::make_unique((sockaddr*)&senderAddr, senderAddrLen); + } + return NETWORK_READPACKET_SUCCESS; } } - static bool SetNonBlocking(SOCKET socket, bool on) + void Close() override { -# ifdef _WIN32 - u_long nonBlocking = on; - return ioctlsocket(socket, FIONBIO, &nonBlocking) == 0; -# else - int32_t flags = fcntl(socket, F_GETFL, 0); - return fcntl(socket, F_SETFL, on ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK)) == 0; -# endif + CloseSocket(); } - static bool SetTCPNoDelay(SOCKET socket, bool enabled) + const char* GetHostName() const override { - return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&enabled, sizeof(enabled)) == 0; + return _hostName.empty() ? nullptr : _hostName.c_str(); + } + +private: + explicit UdpSocket(SOCKET socket, const std::string& hostName) + { + _socket = socket; + _hostName = hostName; + _status = SOCKET_STATUS_CONNECTED; + } + + SOCKET CreateSocket() + { + auto sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == INVALID_SOCKET) + { + throw SocketException("Unable to create socket."); + } + + // Enable send and receiving of broadcast messages + if (!SetOption(sock, SOL_SOCKET, SO_BROADCAST, true)) + { + log_warning("SO_BROADCAST failed. %d", LAST_SOCKET_ERROR()); + } + + // Turn off IPV6_V6ONLY so we can accept both v4 and v6 connections + if (!SetOption(sock, IPPROTO_IPV6, IPV6_V6ONLY, false)) + { + log_warning("IPV6_V6ONLY failed. %d", LAST_SOCKET_ERROR()); + } + + if (!SetOption(sock, SOL_SOCKET, SO_REUSEADDR, true)) + { + log_warning("SO_REUSEADDR failed. %d", LAST_SOCKET_ERROR()); + } + + if (!SetNonBlocking(sock, true)) + { + throw SocketException("Failed to set non-blocking mode."); + } + + return sock; + } + + void CloseSocket() + { + if (_socket != INVALID_SOCKET) + { + closesocket(_socket); + _socket = INVALID_SOCKET; + } + _status = SOCKET_STATUS_CLOSED; } }; -std::unique_ptr CreateTcpSocket() -{ - return std::make_unique(); -} - bool InitialiseWSA() { # ifdef _WIN32 @@ -532,6 +800,112 @@ void DisposeWSA() # endif } +std::unique_ptr CreateTcpSocket() +{ + return std::make_unique(); +} + +std::unique_ptr CreateUdpSocket() +{ + return std::make_unique(); +} + +# ifdef _WIN32 +static std::vector GetNetworkInterfaces() +{ + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + { + return {}; + } + + // Get all the network interfaces, requires a trial and error approch + // until we find the capacity required to store all of them. + DWORD len = 0; + size_t capacity = 16; + std::vector interfaces; + for (;;) + { + interfaces.resize(capacity); + if (WSAIoctl( + sock, SIO_GET_INTERFACE_LIST, nullptr, 0, interfaces.data(), (DWORD)(capacity * sizeof(INTERFACE_INFO)), &len, + nullptr, nullptr) + == 0) + { + break; + } + if (WSAGetLastError() != WSAEFAULT) + { + closesocket(sock); + return {}; + } + capacity *= 2; + } + interfaces.resize(len / sizeof(INTERFACE_INFO)); + interfaces.shrink_to_fit(); + return interfaces; +} +# endif + +std::vector> GetBroadcastAddresses() +{ + std::vector> baddresses; +# ifdef _WIN32 + auto interfaces = GetNetworkInterfaces(); + for (const auto& ifo : interfaces) + { + if (ifo.iiFlags & IFF_LOOPBACK) + continue; + if (!(ifo.iiFlags & IFF_BROADCAST)) + continue; + + // iiBroadcast is unusable, because it always seems to be set to 255.255.255.255. + sockaddr_storage address{}; + memcpy(&address, &ifo.iiAddress.Address, sizeof(sockaddr)); + ((sockaddr_in*)&address)->sin_addr.s_addr = ifo.iiAddress.AddressIn.sin_addr.s_addr + | ~ifo.iiNetmask.AddressIn.sin_addr.s_addr; + baddresses.push_back(std::make_unique((const sockaddr*)&address, (socklen_t)sizeof(sockaddr))); + } +# else + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + { + return baddresses; + } + + char buf[4 * 1024]{}; + ifconf ifconfx{}; + ifconfx.ifc_len = sizeof(buf); + ifconfx.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &ifconfx) == -1) + { + close(sock); + return baddresses; + } + + const char* buf_end = buf + ifconfx.ifc_len; + for (const char* p = buf; p < buf_end;) + { + auto req = (const ifreq*)p; + if (req->ifr_addr.sa_family == AF_INET) + { + ifreq r; + strcpy(r.ifr_name, req->ifr_name); + if (ioctl(sock, SIOCGIFFLAGS, &r) != -1 && (r.ifr_flags & IFF_BROADCAST) && ioctl(sock, SIOCGIFBRDADDR, &r) != -1) + { + baddresses.push_back(std::make_unique(&r.ifr_broadaddr, sizeof(sockaddr))); + } + } + p += sizeof(ifreq); +# if defined(AF_LINK) && !defined(SUNOS) + p += req->ifr_addr.sa_len - sizeof(struct sockaddr); +# endif + } + close(sock); +# endif + return baddresses; +} + namespace Convert { uint16_t HostToNetwork(uint16_t value) diff --git a/src/openrct2/network/TcpSocket.h b/src/openrct2/network/Socket.h similarity index 57% rename from src/openrct2/network/TcpSocket.h rename to src/openrct2/network/Socket.h index 00ae4b19d6..601168b31b 100644 --- a/src/openrct2/network/TcpSocket.h +++ b/src/openrct2/network/Socket.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,7 @@ #include #include +#include enum SOCKET_STATUS { @@ -31,18 +32,28 @@ enum NETWORK_READPACKET NETWORK_READPACKET_DISCONNECTED }; +/** + * Represents an address and port. + */ +interface INetworkEndpoint +{ + virtual ~INetworkEndpoint() + { + } + + virtual std::string GetHostname() const abstract; +}; + /** * Represents a TCP socket / connection or listener. */ interface ITcpSocket { public: - virtual ~ITcpSocket() - { - } + virtual ~ITcpSocket() = default; - virtual SOCKET_STATUS GetStatus() abstract; - virtual const char* GetError() abstract; + virtual SOCKET_STATUS GetStatus() const abstract; + virtual const char* GetError() const abstract; virtual const char* GetHostName() const abstract; virtual void Listen(uint16_t port) abstract; @@ -59,10 +70,34 @@ public: virtual void Close() abstract; }; -std::unique_ptr CreateTcpSocket(); +/** + * Represents a UDP socket / listener. + */ +interface IUdpSocket +{ +public: + virtual ~IUdpSocket() = default; + + virtual SOCKET_STATUS GetStatus() const abstract; + virtual const char* GetError() const abstract; + virtual const char* GetHostName() const abstract; + + virtual void Listen(uint16_t port) abstract; + virtual void Listen(const std::string& address, uint16_t port) abstract; + + virtual size_t SendData(const std::string& address, uint16_t port, const void* buffer, size_t size) abstract; + virtual size_t SendData(const INetworkEndpoint& destination, const void* buffer, size_t size) abstract; + virtual NETWORK_READPACKET ReceiveData( + void* buffer, size_t size, size_t* sizeReceived, std::unique_ptr* sender) abstract; + + virtual void Close() abstract; +}; bool InitialiseWSA(); void DisposeWSA(); +std::unique_ptr CreateTcpSocket(); +std::unique_ptr CreateUdpSocket(); +std::vector> GetBroadcastAddresses(); namespace Convert { diff --git a/src/openrct2/network/Twitch.cpp b/src/openrct2/network/Twitch.cpp index b3c5644737..1bbb234684 100644 --- a/src/openrct2/network/Twitch.cpp +++ b/src/openrct2/network/Twitch.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -9,11 +9,14 @@ #ifdef DISABLE_TWITCH -# include "twitch.h" +# include "Twitch.h" -void twitch_update() +namespace Twitch { -} + void Update() + { + } +} // namespace Twitch #else @@ -24,6 +27,7 @@ void twitch_update() # include "../Context.h" # include "../Game.h" # include "../OpenRCT2.h" +# include "../actions/GuestSetFlagsAction.hpp" # include "../config/Config.h" # include "../core/Json.hpp" # include "../core/String.hpp" @@ -36,14 +40,14 @@ void twitch_update() # include "../util/Util.h" # include "../world/Sprite.h" # include "Http.h" -# include "twitch.h" +# include "Twitch.h" # include # include # include using namespace OpenRCT2; -using namespace OpenRCT2::Network; +using namespace OpenRCT2::Networking; bool gTwitchEnable = false; @@ -131,7 +135,7 @@ namespace Twitch return true; } - static void Update() + void Update() { if (!_twitchIdle) return; @@ -269,10 +273,7 @@ namespace Twitch // }); } - /** - * GET /channel/:channel/audience - */ - static void GetFollowers() + static void Get(const std::string& requestFormat, int operation) { char url[256]; @@ -282,12 +283,12 @@ namespace Twitch context->WriteLine("API URL is empty! skipping request..."); return; } - snprintf(url, sizeof(url), "%s/channel/%s/audience", gConfigTwitch.api_url, gConfigTwitch.channel); + snprintf(url, sizeof(url), requestFormat.c_str(), gConfigTwitch.api_url, gConfigTwitch.channel); _twitchState = TWITCH_STATE_WAITING; _twitchIdle = false; - Http::DoAsync({ url }, [](Http::Response res) { + Http::DoAsync({ url }, [operation](Http::Response res) { std::shared_ptr _(nullptr, [&](...) { _twitchIdle = true; }); if (res.status != Http::Status::OK) @@ -297,40 +298,24 @@ namespace Twitch } _twitchJsonResponse = res; - _twitchState = TWITCH_STATE_GET_FOLLOWERS; + _twitchState = operation; }); } + /** + * GET /channel/:channel/audience + */ + static void GetFollowers() + { + Get("%s/channel/%s/audience", TWITCH_STATE_GET_FOLLOWERS); + } + /** * GET /channel/:channel/messages */ static void GetMessages() { - char url[256]; - - if (gConfigTwitch.api_url == nullptr || strlen(gConfigTwitch.api_url) == 0) - { - auto context = GetContext(); - context->WriteLine("API URL is empty! skipping request..."); - return; - } - snprintf(url, sizeof(url), "%s/channel/%s/messages", gConfigTwitch.api_url, gConfigTwitch.channel); - - _twitchState = TWITCH_STATE_WAITING; - _twitchIdle = false; - - Http::DoAsync({ url }, [](Http::Response res) { - std::shared_ptr _(nullptr, [&](...) { _twitchIdle = true; }); - - if (res.status != Http::Status::OK) - { - _twitchState = TWITCH_STATE_JOINED; - return; - } - - _twitchJsonResponse = res; - _twitchState = TWITCH_STATE_GET_MESSAGES; - }); + Get("%s/channel/%s/messages", TWITCH_STATE_GET_MESSAGES); } static void ParseFollowers() @@ -421,10 +406,12 @@ namespace Twitch Peep* peep; FOR_ALL_GUESTS (spriteIndex, peep) { - if (is_user_string_id(peep->name_string_idx)) + if (peep->name != nullptr) { - utf8 buffer[256]; - format_string(buffer, 256, peep->name_string_idx, nullptr); + uint8_t args[32]{}; + char buffer[256]{}; + peep->FormatNameTo(args); + format_string(buffer, sizeof(buffer), STR_STRINGID, args); AudienceMember* member = nullptr; for (AudienceMember& m : members) @@ -442,30 +429,41 @@ namespace Twitch if (member == nullptr) { // Member no longer peep name worthy - peep->peep_flags &= ~(PEEP_FLAGS_TRACKING | PEEP_FLAGS_TWITCH); + uint32_t flags = peep->peep_flags & ~(PEEP_FLAGS_TRACKING | PEEP_FLAGS_TWITCH); + + auto guestSetFlagsAction = GuestSetFlagsAction(peep->sprite_index, flags); + GameActions::Execute(&guestSetFlagsAction); // TODO set peep name back to number / real name } else { + uint32_t flags = peep->peep_flags; if (member->ShouldTrack) { - peep->peep_flags |= (PEEP_FLAGS_TRACKING); + flags |= (PEEP_FLAGS_TRACKING); } else if (!member->ShouldTrack) { - peep->peep_flags &= ~(PEEP_FLAGS_TRACKING); + flags &= ~(PEEP_FLAGS_TRACKING); + } + if (flags != peep->peep_flags) + { + auto guestSetFlagsAction = GuestSetFlagsAction(peep->sprite_index, flags); + GameActions::Execute(&guestSetFlagsAction); } } } else if (member != nullptr && !(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)) { // Peep with same name already exists but not twitch - peep->peep_flags |= PEEP_FLAGS_TWITCH; + uint32_t flags = peep->peep_flags | PEEP_FLAGS_TWITCH; if (member->ShouldTrack) { - peep->peep_flags |= PEEP_FLAGS_TRACKING; + flags |= PEEP_FLAGS_TRACKING; } + auto guestSetFlagsAction = GuestSetFlagsAction(peep->sprite_index, flags); + GameActions::Execute(&guestSetFlagsAction); } } } @@ -491,18 +489,23 @@ namespace Twitch } AudienceMember* member = &members[memberIndex]; - if (!is_user_string_id(peep->name_string_idx) && !(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)) + if (peep->name == nullptr && !(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)) { // Rename peep and add flags - rct_string_id newStringId = user_string_allocate(USER_STRING_HIGH_ID_NUMBER, member->Name); - if (newStringId != 0) + auto memLen = std::strlen(member->Name) + 1; + peep->name = (char*)std::malloc(memLen); + if (peep->name != nullptr) { - peep->name_string_idx = newStringId; - peep->peep_flags |= PEEP_FLAGS_TWITCH; + std::memcpy(peep->name, member->Name, memLen); + + uint32_t flags = peep->peep_flags | PEEP_FLAGS_TWITCH; if (member->ShouldTrack) { - peep->peep_flags |= PEEP_FLAGS_TRACKING; + flags |= PEEP_FLAGS_TRACKING; } + + auto guestSetFlagsAction = GuestSetFlagsAction(peep->sprite_index, flags); + GameActions::Execute(&guestSetFlagsAction); } } else @@ -565,9 +568,4 @@ namespace Twitch } } // namespace Twitch -void twitch_update() -{ - Twitch::Update(); -} - #endif diff --git a/src/openrct2/network/twitch.h b/src/openrct2/network/Twitch.h similarity index 84% rename from src/openrct2/network/twitch.h rename to src/openrct2/network/Twitch.h index 293293bb4f..09f9e62add 100644 --- a/src/openrct2/network/twitch.h +++ b/src/openrct2/network/Twitch.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,6 +14,9 @@ extern bool gTwitchEnable; -void twitch_update(); +namespace Twitch +{ + void Update(); +} #endif diff --git a/src/openrct2/network/network.h b/src/openrct2/network/network.h index b1f6a75a3e..12edc97dd3 100644 --- a/src/openrct2/network/network.h +++ b/src/openrct2/network/network.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,6 +10,8 @@ #pragma once #define NETWORK_DEFAULT_PORT 11753 +#define NETWORK_LAN_BROADCAST_PORT 11754 +#define NETWORK_LAN_BROADCAST_MSG "openrct2.server.query" #define MAX_SERVER_DESCRIPTION_LENGTH 256 #include "../common.h" @@ -19,9 +21,13 @@ #include #include +struct json_t; struct GameAction; struct Peep; struct LocationXYZ16; +class GameActionResult; +enum class ModifyGroupType : uint8_t; +enum class PermissionState : uint8_t; namespace OpenRCT2 { @@ -30,14 +36,18 @@ namespace OpenRCT2 void network_set_env(const std::shared_ptr& env); void network_close(); +void network_reconnect(); void network_shutdown_client(); int32_t network_begin_client(const std::string& host, int32_t port); int32_t network_begin_server(int32_t port, const std::string& address); int32_t network_get_mode(); int32_t network_get_status(); -void network_check_desynchronization(); +bool network_is_desynchronised(); +bool network_check_desynchronisation(); +void network_request_gamestate_snapshot(); void network_send_tick(); +bool network_gamestate_snapshots_enabled(); void network_update(); void network_process_pending(); void network_flush(); @@ -65,11 +75,12 @@ int32_t network_get_current_player_group_index(); uint8_t network_get_group_id(uint32_t index); int32_t network_get_num_groups(); const char* network_get_group_name(uint32_t index); -void game_command_set_player_group( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_modify_groups( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_kick_player(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); +std::unique_ptr network_set_player_group( + NetworkPlayerId_t actionPlayerId, NetworkPlayerId_t playerId, uint8_t groupId, bool isExecuting); +std::unique_ptr network_modify_groups( + NetworkPlayerId_t actionPlayerId, ModifyGroupType type, uint8_t groupId, const std::string& name, uint32_t permissionIndex, + PermissionState permissionState, bool isExecuting); +std::unique_ptr network_kick_player(NetworkPlayerId_t playerId, bool isExecuting); uint8_t network_get_default_group(); int32_t network_get_num_actions(); rct_string_id network_get_action_name_string_id(uint32_t index); @@ -82,8 +93,6 @@ int32_t network_get_pickup_peep_old_x(uint8_t playerid); void network_send_map(); void network_send_chat(const char* text); -void network_send_gamecmd( - uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi, uint32_t ebp, uint8_t callback); void network_send_game_action(const GameAction* action); void network_enqueue_game_action(const GameAction* action); void network_send_password(const std::string& password); @@ -103,3 +112,5 @@ const utf8* network_get_server_provider_website(); std::string network_get_version(); NetworkStats_t network_get_stats(); +NetworkServerState_t network_get_server_state(); +json_t* network_get_server_info_as_json(); diff --git a/src/openrct2/object/BannerObject.cpp b/src/openrct2/object/BannerObject.cpp index 9693afc893..dd9e06afe1 100644 --- a/src/openrct2/object/BannerObject.cpp +++ b/src/openrct2/object/BannerObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/BannerObject.h b/src/openrct2/object/BannerObject.h index 1792b0f747..8d59187d86 100644 --- a/src/openrct2/object/BannerObject.h +++ b/src/openrct2/object/BannerObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/DefaultObjects.cpp b/src/openrct2/object/DefaultObjects.cpp index 5f796d0dc9..e74a803490 100644 --- a/src/openrct2/object/DefaultObjects.cpp +++ b/src/openrct2/object/DefaultObjects.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/DefaultObjects.h b/src/openrct2/object/DefaultObjects.h index 7cb82271bb..65a2fbc5db 100644 --- a/src/openrct2/object/DefaultObjects.h +++ b/src/openrct2/object/DefaultObjects.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/EntranceObject.cpp b/src/openrct2/object/EntranceObject.cpp index cc6d021247..592caac883 100644 --- a/src/openrct2/object/EntranceObject.cpp +++ b/src/openrct2/object/EntranceObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -23,14 +23,6 @@ void EntranceObject::ReadLegacy(IReadObjectContext* context, IStream* stream) GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME); GetImageTable().Read(context, stream); - - // Fix issue #1705: The Medieval entrance from Time Twister has a straight banner, - // but scrolls its text as if it a curved one. - if (String::Equals(GetIdentifier(), "MEDIENTR")) - { - _legacyType.scrolling_mode = 32; - _legacyType.text_height += 1; - } } void EntranceObject::Load() diff --git a/src/openrct2/object/EntranceObject.h b/src/openrct2/object/EntranceObject.h index 78611dc92e..7580ba3ed1 100644 --- a/src/openrct2/object/EntranceObject.h +++ b/src/openrct2/object/EntranceObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/FootpathItemObject.cpp b/src/openrct2/object/FootpathItemObject.cpp index 96809bf306..0463b4bb62 100644 --- a/src/openrct2/object/FootpathItemObject.cpp +++ b/src/openrct2/object/FootpathItemObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/FootpathItemObject.h b/src/openrct2/object/FootpathItemObject.h index a27d65e4ec..47c5f161fe 100644 --- a/src/openrct2/object/FootpathItemObject.h +++ b/src/openrct2/object/FootpathItemObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/FootpathObject.cpp b/src/openrct2/object/FootpathObject.cpp index 7216586912..d32843f9c0 100644 --- a/src/openrct2/object/FootpathObject.cpp +++ b/src/openrct2/object/FootpathObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,7 +18,7 @@ void FootpathObject::ReadLegacy(IReadObjectContext* context, IStream* stream) { stream->Seek(10, STREAM_SEEK_CURRENT); - _legacyType.support_type = stream->ReadValue(); + _legacyType.support_type = static_cast(stream->ReadValue()); _legacyType.flags = stream->ReadValue(); _legacyType.scrolling_mode = stream->ReadValue(); stream->Seek(1, STREAM_SEEK_CURRENT); @@ -27,9 +27,9 @@ void FootpathObject::ReadLegacy(IReadObjectContext* context, IStream* stream) GetImageTable().Read(context, stream); // Validate properties - if (_legacyType.support_type >= FOOTPATH_ENTRY_SUPPORT_TYPE_COUNT) + if (_legacyType.support_type >= RailingEntrySupportType::Count) { - context->LogError(OBJECT_ERROR_INVALID_PROPERTY, "SUPPORT_TYPE not supported."); + context->LogError(OBJECT_ERROR_INVALID_PROPERTY, "RailingEntrySupportType not supported."); } } @@ -42,10 +42,14 @@ void FootpathObject::Load() _pathSurfaceEntry.string_idx = _legacyType.string_idx; _pathSurfaceEntry.image = _legacyType.image; - _pathSurfaceEntry.queue_image = _legacyType.image + 51; _pathSurfaceEntry.preview = _legacyType.image + 71; _pathSurfaceEntry.flags = _legacyType.flags; + _queueEntry.string_idx = _legacyType.string_idx; + _queueEntry.image = _legacyType.image + 51; + _queueEntry.preview = _legacyType.image + 72; + _queueEntry.flags = _legacyType.flags | FOOTPATH_ENTRY_FLAG_IS_QUEUE; + _pathRailingsEntry.string_idx = _legacyType.string_idx; _pathRailingsEntry.bridge_image = _legacyType.bridge_image; _pathRailingsEntry.preview = _legacyType.image + 71; @@ -69,15 +73,15 @@ void FootpathObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int32_t int32_t x = width / 2; int32_t y = height / 2; gfx_draw_sprite(dpi, _pathSurfaceEntry.preview, x - 49, y - 17, 0); - gfx_draw_sprite(dpi, _pathSurfaceEntry.preview + 1, x + 4, y - 17, 0); + gfx_draw_sprite(dpi, _queueEntry.preview, x + 4, y - 17, 0); } -static uint8_t ParseSupportType(const std::string& s) +static RailingEntrySupportType ParseSupportType(const std::string& s) { if (s == "pole") - return FOOTPATH_ENTRY_SUPPORT_TYPE_POLE; + return RailingEntrySupportType::Pole; else /* if (s == "box") */ - return FOOTPATH_ENTRY_SUPPORT_TYPE_BOX; + return RailingEntrySupportType::Box; } void FootpathObject::ReadJson(IReadObjectContext* context, const json_t* root) diff --git a/src/openrct2/object/FootpathObject.h b/src/openrct2/object/FootpathObject.h index 986962f94b..b48cbd7d6f 100644 --- a/src/openrct2/object/FootpathObject.h +++ b/src/openrct2/object/FootpathObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -17,6 +17,7 @@ class FootpathObject final : public Object private: rct_footpath_entry _legacyType = {}; PathSurfaceEntry _pathSurfaceEntry = {}; + PathSurfaceEntry _queueEntry = {}; PathRailingsEntry _pathRailingsEntry = {}; public: @@ -35,6 +36,11 @@ public: return &_pathSurfaceEntry; } + PathSurfaceEntry* GetQueueEntry() + { + return &_queueEntry; + } + PathRailingsEntry* GetPathRailingsEntry() { return &_pathRailingsEntry; diff --git a/src/openrct2/object/ImageTable.cpp b/src/openrct2/object/ImageTable.cpp index 35c364f2a3..adc9fa62bc 100644 --- a/src/openrct2/object/ImageTable.cpp +++ b/src/openrct2/object/ImageTable.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/ImageTable.h b/src/openrct2/object/ImageTable.h index 2bf37d5305..73065f51f0 100644 --- a/src/openrct2/object/ImageTable.h +++ b/src/openrct2/object/ImageTable.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/LargeSceneryObject.cpp b/src/openrct2/object/LargeSceneryObject.cpp index aeb9c18828..4fd2281baf 100644 --- a/src/openrct2/object/LargeSceneryObject.cpp +++ b/src/openrct2/object/LargeSceneryObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -263,7 +263,7 @@ std::vector LargeSceneryObject::ReadJsonGlyphs(con const json_t* jGlyph; json_array_foreach(jGlpyhs, index, jGlyph) { - rct_large_scenery_text_glyph glyph; + rct_large_scenery_text_glyph glyph = {}; glyph.image_offset = json_integer_value(json_object_get(jGlyph, "image")); glyph.width = json_integer_value(json_object_get(jGlyph, "width")); glyph.height = json_integer_value(json_object_get(jGlyph, "height")); diff --git a/src/openrct2/object/LargeSceneryObject.h b/src/openrct2/object/LargeSceneryObject.h index 1f18c26573..3642878106 100644 --- a/src/openrct2/object/LargeSceneryObject.h +++ b/src/openrct2/object/LargeSceneryObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/Object.cpp b/src/openrct2/object/Object.cpp index 3487129001..35526d4021 100644 --- a/src/openrct2/object/Object.cpp +++ b/src/openrct2/object/Object.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/Object.h b/src/openrct2/object/Object.h index 39b2a44a19..dfc6eb2d50 100644 --- a/src/openrct2/object/Object.h +++ b/src/openrct2/object/Object.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -89,6 +89,11 @@ struct rct_object_entry }; }; + std::string_view GetName() const + { + return std::string_view(name, std::size(name)); + } + void SetName(const char* value) { auto src = value; diff --git a/src/openrct2/object/ObjectFactory.cpp b/src/openrct2/object/ObjectFactory.cpp index 196ec92f3e..68ea0b21f3 100644 --- a/src/openrct2/object/ObjectFactory.cpp +++ b/src/openrct2/object/ObjectFactory.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/ObjectFactory.h b/src/openrct2/object/ObjectFactory.h index 22ea8bc2b4..8fb401cac2 100644 --- a/src/openrct2/object/ObjectFactory.h +++ b/src/openrct2/object/ObjectFactory.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/ObjectJsonHelpers.cpp b/src/openrct2/object/ObjectJsonHelpers.cpp index c7da2f99b1..cb14ae7a72 100644 --- a/src/openrct2/object/ObjectJsonHelpers.cpp +++ b/src/openrct2/object/ObjectJsonHelpers.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -366,7 +366,7 @@ namespace ObjectJsonHelpers if (is_csg_loaded()) { auto range = ParseRange(s.substr(4)); - if (range.size() > 0) + if (!range.empty()) { for (auto i : range) { @@ -380,7 +380,7 @@ namespace ObjectJsonHelpers else if (String::StartsWith(s, "$G1")) { auto range = ParseRange(s.substr(3)); - if (range.size() > 0) + if (!range.empty()) { for (auto i : range) { diff --git a/src/openrct2/object/ObjectJsonHelpers.h b/src/openrct2/object/ObjectJsonHelpers.h index 849a6df2c9..6f97a4791b 100644 --- a/src/openrct2/object/ObjectJsonHelpers.h +++ b/src/openrct2/object/ObjectJsonHelpers.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/ObjectLimits.h b/src/openrct2/object/ObjectLimits.h index 26408d4c4d..41f54ecdc8 100644 --- a/src/openrct2/object/ObjectLimits.h +++ b/src/openrct2/object/ObjectLimits.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/ObjectList.cpp b/src/openrct2/object/ObjectList.cpp index 7a849ddb65..45089060ac 100644 --- a/src/openrct2/object/ObjectList.cpp +++ b/src/openrct2/object/ObjectList.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/ObjectList.h b/src/openrct2/object/ObjectList.h index d214ae22fb..5f1f95be84 100644 --- a/src/openrct2/object/ObjectList.h +++ b/src/openrct2/object/ObjectList.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/ObjectManager.cpp b/src/openrct2/object/ObjectManager.cpp index 6e328e51f0..0cbe3cac9b 100644 --- a/src/openrct2/object/ObjectManager.cpp +++ b/src/openrct2/object/ObjectManager.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -329,7 +329,7 @@ private: void SetNewLoadedObjectList(const std::vector& newLoadedObjects) { - if (newLoadedObjects.size() == 0) + if (newLoadedObjects.empty()) { UnloadAll(); } @@ -530,7 +530,7 @@ private: requiredObjects.push_back(ori); } - if (missingObjects.size() > 0) + if (!missingObjects.empty()) { throw ObjectLoadException(std::move(missingObjects)); } @@ -605,7 +605,7 @@ private: obj->Load(); } - if (badObjects.size() > 0) + if (!badObjects.empty()) { // Unload all the new objects we loaded for (auto object : loadedObjects) diff --git a/src/openrct2/object/ObjectManager.h b/src/openrct2/object/ObjectManager.h index 263841f9af..83cf25cb69 100644 --- a/src/openrct2/object/ObjectManager.h +++ b/src/openrct2/object/ObjectManager.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index 0f4babf4bb..9d2e34b403 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/ObjectRepository.h b/src/openrct2/object/ObjectRepository.h index f73f2007bd..a439a8a8ec 100644 --- a/src/openrct2/object/ObjectRepository.h +++ b/src/openrct2/object/ObjectRepository.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/RideObject.cpp b/src/openrct2/object/RideObject.cpp index 034a4b9cca..0dfb8a8b56 100644 --- a/src/openrct2/object/RideObject.cpp +++ b/src/openrct2/object/RideObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,6 +12,7 @@ #include "RideObject.h" #include "../OpenRCT2.h" +#include "../audio/audio.h" #include "../core/IStream.hpp" #include "../core/Memory.hpp" #include "../core/String.hpp" @@ -85,6 +86,19 @@ void RideObject::ReadLegacy(IReadObjectContext* context, IStream* stream) _presetColours.list[i] = stream->ReadValue(); } + if (IsRideTypeShopOrFacility(_legacyType.ride_type[0])) + { + // This used to be hard-coded. JSON objects set this themselves. + _presetColours.count = 1; + _presetColours.list[0] = { COLOUR_BRIGHT_RED, COLOUR_BRIGHT_RED, COLOUR_BRIGHT_RED }; + + if (_legacyType.ride_type[0] == RIDE_TYPE_FOOD_STALL || _legacyType.ride_type[0] == RIDE_TYPE_DRINK_STALL) + { + // In RCT2, no food or drink stall could be recoloured. + _legacyType.flags |= RIDE_ENTRY_FLAG_DISABLE_COLOUR_TAB; + } + } + // Read peep loading positions for (int32_t i = 0; i < RCT2_MAX_VEHICLES_PER_RIDE_ENTRY; i++) { @@ -429,7 +443,7 @@ void RideObject::ReadLegacyVehicle( vehicle->no_seating_rows = stream->ReadValue(); vehicle->spinning_inertia = stream->ReadValue(); vehicle->spinning_friction = stream->ReadValue(); - vehicle->friction_sound_id = stream->ReadValue(); + vehicle->friction_sound_id = stream->ReadValue(); vehicle->log_flume_reverser_vehicle_type = stream->ReadValue(); vehicle->sound_range = stream->ReadValue(); vehicle->double_sound_frequency = stream->ReadValue(); @@ -550,6 +564,8 @@ void RideObject::ReadJson(IReadObjectContext* context, const json_t* root) _legacyType.shop_item = SHOP_ITEM_NONE; _legacyType.shop_item_secondary = SHOP_ITEM_NONE; + _presetColours = ReadJsonCarColours(json_object_get(properties, "carColours")); + if (IsRideTypeShopOrFacility(_legacyType.ride_type[0])) { // Standard car info for a shop @@ -561,7 +577,7 @@ void RideObject::ReadJson(IReadObjectContext* context, const json_t* root) car.sprite_height_positive = 1; car.flags = VEHICLE_ENTRY_FLAG_SPINNING; car.car_visual = VEHICLE_VISUAL_FLAT_RIDE_OR_CAR_RIDE; - car.friction_sound_id = 0xFF; + car.friction_sound_id = SoundId::Null; car.sound_range = 0xFF; car.draw_order = 6; @@ -623,7 +639,6 @@ void RideObject::ReadJson(IReadObjectContext* context, const json_t* root) } auto availableTrackPieces = ObjectJsonHelpers::GetJsonStringArray(json_object_get(properties, "availableTrackPieces")); - _presetColours = ReadJsonCarColours(json_object_get(properties, "carColours")); } _legacyType.flags |= ObjectJsonHelpers::GetFlags( @@ -749,7 +764,7 @@ rct_ride_entry_vehicle RideObject::ReadJsonCar(const json_t* jCar) car.no_seating_rows = ObjectJsonHelpers::GetInteger(jCar, "numSeatRows"); car.spinning_inertia = ObjectJsonHelpers::GetInteger(jCar, "spinningInertia"); car.spinning_friction = ObjectJsonHelpers::GetInteger(jCar, "spinningFriction"); - car.friction_sound_id = ObjectJsonHelpers::GetInteger(jCar, "frictionSoundId", 255); + car.friction_sound_id = static_cast(ObjectJsonHelpers::GetInteger(jCar, "frictionSoundId", 255)); car.log_flume_reverser_vehicle_type = ObjectJsonHelpers::GetInteger(jCar, "logFlumeReverserVehicleType"); car.sound_range = ObjectJsonHelpers::GetInteger(jCar, "soundRange", 255); car.double_sound_frequency = ObjectJsonHelpers::GetInteger(jCar, "doubleSoundFrequency"); diff --git a/src/openrct2/object/RideObject.h b/src/openrct2/object/RideObject.h index 3a20477f55..bb68f5d273 100644 --- a/src/openrct2/object/RideObject.h +++ b/src/openrct2/object/RideObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/SceneryGroupObject.cpp b/src/openrct2/object/SceneryGroupObject.cpp index e487646882..21b50d3705 100644 --- a/src/openrct2/object/SceneryGroupObject.cpp +++ b/src/openrct2/object/SceneryGroupObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/SceneryGroupObject.h b/src/openrct2/object/SceneryGroupObject.h index c2d5e836a6..738b0ed6b2 100644 --- a/src/openrct2/object/SceneryGroupObject.h +++ b/src/openrct2/object/SceneryGroupObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/SceneryObject.cpp b/src/openrct2/object/SceneryObject.cpp index 1160b94fbc..d048ea13f0 100644 --- a/src/openrct2/object/SceneryObject.cpp +++ b/src/openrct2/object/SceneryObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/SceneryObject.h b/src/openrct2/object/SceneryObject.h index dfc00b954a..ae69d137c3 100644 --- a/src/openrct2/object/SceneryObject.h +++ b/src/openrct2/object/SceneryObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/SmallSceneryObject.cpp b/src/openrct2/object/SmallSceneryObject.cpp index 2fb6f5f3f3..754d65295a 100644 --- a/src/openrct2/object/SmallSceneryObject.cpp +++ b/src/openrct2/object/SmallSceneryObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -46,6 +46,11 @@ void SmallSceneryObject::ReadLegacy(IReadObjectContext* context, IStream* stream { _frameOffsets = ReadFrameOffsets(stream); } + // This crude method was used by RCT2. JSON objects have a flag for this property. + if (_legacyType.small_scenery.height > 64) + { + _legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_IS_TREE; + } GetImageTable().Read(context, stream); @@ -270,6 +275,7 @@ void SmallSceneryObject::ReadJson(IReadObjectContext* context, const json_t* roo { "allowSupportsAbove", SMALL_SCENERY_FLAG_BUILD_DIRECTLY_ONTOP }, { "supportsHavePrimaryColour", SMALL_SCENERY_FLAG_PAINT_SUPPORTS }, { "SMALL_SCENERY_FLAG27", SMALL_SCENERY_FLAG27 }, + { "isTree", SMALL_SCENERY_FLAG_IS_TREE }, }); // Determine shape flags from a shape string diff --git a/src/openrct2/object/SmallSceneryObject.h b/src/openrct2/object/SmallSceneryObject.h index 13902fcf37..f68613444e 100644 --- a/src/openrct2/object/SmallSceneryObject.h +++ b/src/openrct2/object/SmallSceneryObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/StationObject.cpp b/src/openrct2/object/StationObject.cpp index d0d48eb874..29a95cf1a1 100644 --- a/src/openrct2/object/StationObject.cpp +++ b/src/openrct2/object/StationObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,7 @@ #include "../core/String.hpp" #include "../drawing/Drawing.h" #include "../localisation/Localisation.h" +#include "../world/Banner.h" #include "ObjectJsonHelpers.h" void StationObject::Load() @@ -82,7 +83,7 @@ void StationObject::ReadJson(IReadObjectContext* context, const json_t* root) { auto properties = json_object_get(root, "properties"); Height = ObjectJsonHelpers::GetInteger(properties, "height", 0); - ScrollingMode = ObjectJsonHelpers::GetInteger(properties, "scrollingMode", 0xFF); + ScrollingMode = ObjectJsonHelpers::GetInteger(properties, "scrollingMode", SCROLLING_MODE_NONE); Flags = ObjectJsonHelpers::GetFlags( properties, { { "hasPrimaryColour", STATION_OBJECT_FLAGS::HAS_PRIMARY_COLOUR }, diff --git a/src/openrct2/object/StationObject.h b/src/openrct2/object/StationObject.h index 85e4cd149a..74e9b5f087 100644 --- a/src/openrct2/object/StationObject.h +++ b/src/openrct2/object/StationObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/StringTable.cpp b/src/openrct2/object/StringTable.cpp index fa1fa84ff8..61f89ccc44 100644 --- a/src/openrct2/object/StringTable.cpp +++ b/src/openrct2/object/StringTable.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/StringTable.h b/src/openrct2/object/StringTable.h index cab99ebaa1..07e833185f 100644 --- a/src/openrct2/object/StringTable.h +++ b/src/openrct2/object/StringTable.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/TerrainEdgeObject.cpp b/src/openrct2/object/TerrainEdgeObject.cpp index 7182e9dec1..ea1e87dd46 100644 --- a/src/openrct2/object/TerrainEdgeObject.cpp +++ b/src/openrct2/object/TerrainEdgeObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/TerrainEdgeObject.h b/src/openrct2/object/TerrainEdgeObject.h index cdc6f729f1..01ef6c5237 100644 --- a/src/openrct2/object/TerrainEdgeObject.h +++ b/src/openrct2/object/TerrainEdgeObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/TerrainSurfaceObject.cpp b/src/openrct2/object/TerrainSurfaceObject.cpp index a5dec79c7e..6c6c618bfd 100644 --- a/src/openrct2/object/TerrainSurfaceObject.cpp +++ b/src/openrct2/object/TerrainSurfaceObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/TerrainSurfaceObject.h b/src/openrct2/object/TerrainSurfaceObject.h index 71f2fff106..37cd4e94b4 100644 --- a/src/openrct2/object/TerrainSurfaceObject.h +++ b/src/openrct2/object/TerrainSurfaceObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/WallObject.cpp b/src/openrct2/object/WallObject.cpp index 996495975f..dd2304f7e0 100644 --- a/src/openrct2/object/WallObject.cpp +++ b/src/openrct2/object/WallObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,6 +10,7 @@ #include "WallObject.h" #include "../core/IStream.hpp" +#include "../core/String.hpp" #include "../drawing/Drawing.h" #include "../interface/Cursors.h" #include "../localisation/Language.h" @@ -39,6 +40,14 @@ void WallObject::ReadLegacy(IReadObjectContext* context, IStream* stream) { context->LogError(OBJECT_ERROR_INVALID_PROPERTY, "Price can not be free or negative."); } + + // Autofix this object (will be turned into an official object later). + auto identifier = GetIdentifier(); + if (String::Equals(identifier, "XXWLBR03")) + { + _legacyType.wall.flags2 &= ~WALL_SCENERY_2_DOOR_SOUND_MASK; + _legacyType.wall.flags2 |= (1u << WALL_SCENERY_2_DOOR_SOUND_SHIFT) & WALL_SCENERY_2_DOOR_SOUND_MASK; + } } void WallObject::Load() @@ -137,7 +146,7 @@ void WallObject::ReadJson(IReadObjectContext* context, const json_t* root) } // Door sound - auto jDoorSound = json_object_get(properties, "scrollingMode"); + auto jDoorSound = json_object_get(properties, "doorSound"); if (jDoorSound != nullptr) { auto doorSound = json_integer_value(jDoorSound); diff --git a/src/openrct2/object/WallObject.h b/src/openrct2/object/WallObject.h index 5181176806..9ec22ae9e0 100644 --- a/src/openrct2/object/WallObject.h +++ b/src/openrct2/object/WallObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/WaterObject.cpp b/src/openrct2/object/WaterObject.cpp index 83dc980242..4bb59cf701 100644 --- a/src/openrct2/object/WaterObject.cpp +++ b/src/openrct2/object/WaterObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/object/WaterObject.h b/src/openrct2/object/WaterObject.h index 587d0af0cc..1e604dcd76 100644 --- a/src/openrct2/object/WaterObject.h +++ b/src/openrct2/object/WaterObject.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/paint/Paint.cpp b/src/openrct2/paint/Paint.cpp index 6e0f83aa8b..f620d904b1 100644 --- a/src/openrct2/paint/Paint.cpp +++ b/src/openrct2/paint/Paint.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -9,24 +9,27 @@ #include "Paint.h" +#include "../Context.h" #include "../config/Config.h" #include "../drawing/Drawing.h" #include "../interface/Viewport.h" #include "../localisation/Localisation.h" #include "../localisation/LocalisationService.h" +#include "../paint/Painter.h" #include "sprite/Paint.Sprite.h" #include "tile_element/Paint.TileElement.h" #include +#include +#include + +using namespace OpenRCT2; // Globals for paint clipping uint8_t gClipHeight = 128; // Default to middle value LocationXY8 gClipSelectionA = { 0, 0 }; LocationXY8 gClipSelectionB = { MAXIMUM_MAP_SIZE_TECHNICAL - 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1 }; -paint_session gPaintSession; -static bool _paintSessionInUse; - static constexpr const uint8_t BoundBoxDebugColours[] = { 0, // NONE 102, // TERRAIN @@ -47,34 +50,12 @@ bool gShowDirtyVisuals; bool gPaintBoundingBoxes; bool gPaintBlockedTiles; -static void paint_session_init(paint_session* session, rct_drawpixelinfo* dpi, uint32_t viewFlags); static void paint_attached_ps(rct_drawpixelinfo* dpi, paint_struct* ps, uint32_t viewFlags); static void paint_ps_image_with_bounding_boxes( rct_drawpixelinfo* dpi, paint_struct* ps, uint32_t imageId, int16_t x, int16_t y); static void paint_ps_image(rct_drawpixelinfo* dpi, paint_struct* ps, uint32_t imageId, int16_t x, int16_t y); static uint32_t paint_ps_colourify_image(uint32_t imageId, uint8_t spriteType, uint32_t viewFlags); -static void paint_session_init(paint_session* session, rct_drawpixelinfo* dpi, uint32_t viewFlags) -{ - session->DPI = dpi; - session->EndOfPaintStructArray = &session->PaintStructs[4000 - 1]; - session->NextFreePaintStruct = session->PaintStructs; - session->LastRootPS = nullptr; - session->UnkF1AD2C = nullptr; - session->ViewFlags = viewFlags; - for (auto& quadrant : session->Quadrants) - { - quadrant = nullptr; - } - session->QuadrantBackIndex = std::numeric_limits::max(); - session->QuadrantFrontIndex = 0; - session->PSStringHead = nullptr; - session->LastPSString = nullptr; - session->WoodenSupportsPrependTo = nullptr; - session->CurrentlyDrawnItem = nullptr; - session->SurfaceElement = nullptr; -} - static void paint_session_add_ps_to_quadrant(paint_session* session, paint_struct* ps, int32_t positionHash) { uint32_t paintQuadrantIndex = std::clamp(positionHash / 32, 0, MAX_PAINT_QUADRANTS - 1); @@ -90,7 +71,8 @@ static void paint_session_add_ps_to_quadrant(paint_session* session, paint_struc * Extracted from 0x0098196c, 0x0098197c, 0x0098198c, 0x0098199c */ static paint_struct* sub_9819_c( - paint_session* session, uint32_t image_id, LocationXYZ16 offset, LocationXYZ16 boundBoxSize, LocationXYZ16 boundBoxOffset) + paint_session* session, uint32_t image_id, const CoordsXYZ& offset, LocationXYZ16 boundBoxSize, + LocationXYZ16 boundBoxOffset) { if (session->NextFreePaintStruct >= session->EndOfPaintStructArray) return nullptr; @@ -103,36 +85,24 @@ static paint_struct* sub_9819_c( paint_struct* ps = &session->NextFreePaintStruct->basic; ps->image_id = image_id; - switch (session->CurrentRotation) - { - case 0: - rotate_map_coordinates(&offset.x, &offset.y, 0); - break; - case 1: - rotate_map_coordinates(&offset.x, &offset.y, 3); - break; - case 2: - rotate_map_coordinates(&offset.x, &offset.y, 2); - break; - case 3: - rotate_map_coordinates(&offset.x, &offset.y, 1); - break; - } - offset.x += session->SpritePosition.x; - offset.y += session->SpritePosition.y; + uint8_t swappedRotation = (session->CurrentRotation * 3) % 4; // swaps 1 and 3 + auto swappedRotCoord = CoordsXYZ{ offset.Rotate(swappedRotation), offset.z }; - LocationXY16 map = coordinate_3d_to_2d(&offset, session->CurrentRotation); + swappedRotCoord.x += session->SpritePosition.x; + swappedRotCoord.y += session->SpritePosition.y; - ps->x = map.x; - ps->y = map.y; + auto screenCoords = translate_3d_to_2d_with_z(session->CurrentRotation, swappedRotCoord); - int32_t left = map.x + g1->x_offset; - int32_t bottom = map.y + g1->y_offset; + ps->x = screenCoords.x; + ps->y = screenCoords.y; + + int32_t left = screenCoords.x + g1->x_offset; + int32_t bottom = screenCoords.y + g1->y_offset; int32_t right = left + g1->width; int32_t top = bottom + g1->height; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (right <= dpi->x) return nullptr; @@ -192,7 +162,7 @@ static paint_struct* sub_9819_c( */ void paint_session_generate(paint_session* session) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; LocationXY16 mapTile = { (int16_t)(dpi->x & 0xFFE0), (int16_t)((dpi->y - 16) & 0xFFE0) }; int16_t half_x = mapTile.x >> 1; @@ -458,7 +428,6 @@ void paint_session_arrange(paint_session* session) ps->next_quadrant_ps = nullptr; uint32_t quadrantIndex = session->QuadrantBackIndex; - const uint8_t rotation = get_current_rotation(); if (quadrantIndex != UINT32_MAX) { do @@ -477,19 +446,19 @@ void paint_session_arrange(paint_session* session) } while (++quadrantIndex <= session->QuadrantFrontIndex); paint_struct* ps_cache = paint_arrange_structs_helper( - psHead, session->QuadrantBackIndex & 0xFFFF, PAINT_QUADRANT_FLAG_NEXT, rotation); + psHead, session->QuadrantBackIndex & 0xFFFF, PAINT_QUADRANT_FLAG_NEXT, session->CurrentRotation); quadrantIndex = session->QuadrantBackIndex; while (++quadrantIndex < session->QuadrantFrontIndex) { - ps_cache = paint_arrange_structs_helper(ps_cache, quadrantIndex & 0xFFFF, 0, rotation); + ps_cache = paint_arrange_structs_helper(ps_cache, quadrantIndex & 0xFFFF, 0, session->CurrentRotation); } } } static void paint_draw_struct(paint_session* session, paint_struct* ps) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; int16_t x = ps->x; int16_t y = ps->y; @@ -574,61 +543,61 @@ static void paint_ps_image_with_bounding_boxes(rct_drawpixelinfo* dpi, paint_str const uint8_t colour = BoundBoxDebugColours[ps->sprite_type]; const uint8_t rotation = get_current_rotation(); - const LocationXYZ16 frontTop = { - (int16_t)ps->bounds.x_end, - (int16_t)ps->bounds.y_end, - (int16_t)ps->bounds.z_end, + const CoordsXYZ frontTop = { + ps->bounds.x_end, + ps->bounds.y_end, + ps->bounds.z_end, }; - const LocationXY16 screenCoordFrontTop = coordinate_3d_to_2d(&frontTop, rotation); + const auto screenCoordFrontTop = translate_3d_to_2d_with_z(rotation, frontTop); - const LocationXYZ16 frontBottom = { - (int16_t)ps->bounds.x_end, - (int16_t)ps->bounds.y_end, - (int16_t)ps->bounds.z, + const CoordsXYZ frontBottom = { + ps->bounds.x_end, + ps->bounds.y_end, + ps->bounds.z, }; - const LocationXY16 screenCoordFrontBottom = coordinate_3d_to_2d(&frontBottom, rotation); + const auto screenCoordFrontBottom = translate_3d_to_2d_with_z(rotation, frontBottom); - const LocationXYZ16 leftTop = { - (int16_t)ps->bounds.x, - (int16_t)ps->bounds.y_end, - (int16_t)ps->bounds.z_end, + const CoordsXYZ leftTop = { + ps->bounds.x, + ps->bounds.y_end, + ps->bounds.z_end, }; - const LocationXY16 screenCoordLeftTop = coordinate_3d_to_2d(&leftTop, rotation); + const auto screenCoordLeftTop = translate_3d_to_2d_with_z(rotation, leftTop); - const LocationXYZ16 leftBottom = { - (int16_t)ps->bounds.x, - (int16_t)ps->bounds.y_end, - (int16_t)ps->bounds.z, + const CoordsXYZ leftBottom = { + ps->bounds.x, + ps->bounds.y_end, + ps->bounds.z, }; - const LocationXY16 screenCoordLeftBottom = coordinate_3d_to_2d(&leftBottom, rotation); + const auto screenCoordLeftBottom = translate_3d_to_2d_with_z(rotation, leftBottom); - const LocationXYZ16 rightTop = { - (int16_t)ps->bounds.x_end, - (int16_t)ps->bounds.y, - (int16_t)ps->bounds.z_end, + const CoordsXYZ rightTop = { + ps->bounds.x_end, + ps->bounds.y, + ps->bounds.z_end, }; - const LocationXY16 screenCoordRightTop = coordinate_3d_to_2d(&rightTop, rotation); + const auto screenCoordRightTop = translate_3d_to_2d_with_z(rotation, rightTop); - const LocationXYZ16 rightBottom = { - (int16_t)ps->bounds.x_end, - (int16_t)ps->bounds.y, - (int16_t)ps->bounds.z, + const CoordsXYZ rightBottom = { + ps->bounds.x_end, + ps->bounds.y, + ps->bounds.z, }; - const LocationXY16 screenCoordRightBottom = coordinate_3d_to_2d(&rightBottom, rotation); + const auto screenCoordRightBottom = translate_3d_to_2d_with_z(rotation, rightBottom); - const LocationXYZ16 backTop = { - (int16_t)ps->bounds.x, - (int16_t)ps->bounds.y, - (int16_t)ps->bounds.z_end, + const CoordsXYZ backTop = { + ps->bounds.x, + ps->bounds.y, + ps->bounds.z_end, }; - const LocationXY16 screenCoordBackTop = coordinate_3d_to_2d(&backTop, rotation); + const auto screenCoordBackTop = translate_3d_to_2d_with_z(rotation, backTop); - const LocationXYZ16 backBottom = { - (int16_t)ps->bounds.x, - (int16_t)ps->bounds.y, - (int16_t)ps->bounds.z, + const CoordsXYZ backBottom = { + ps->bounds.x, + ps->bounds.y, + ps->bounds.z, }; - const LocationXY16 screenCoordBackBottom = coordinate_3d_to_2d(&backBottom, rotation); + const auto screenCoordBackBottom = translate_3d_to_2d_with_z(rotation, backBottom); // bottom square gfx_draw_line( @@ -732,18 +701,12 @@ static void draw_pixel_info_crop_by_zoom(rct_drawpixelinfo* dpi) paint_session* paint_session_alloc(rct_drawpixelinfo* dpi, uint32_t viewFlags) { - // Currently limited to just one session at a time - assert(!_paintSessionInUse); - _paintSessionInUse = true; - paint_session* session = &gPaintSession; - - paint_session_init(session, dpi, viewFlags); - return session; + return GetContext()->GetPainter()->CreateSession(dpi, viewFlags); } void paint_session_free([[maybe_unused]] paint_session* session) { - _paintSessionInUse = false; + GetContext()->GetPainter()->ReleaseSession(session); } /** @@ -782,7 +745,7 @@ paint_struct* sub_98196C( paint_struct* ps = &session->NextFreePaintStruct->basic; ps->image_id = image_id; - LocationXYZ16 coord_3d = { + CoordsXYZ coord_3d = { x_offset, // ax y_offset, // cx z_offset, @@ -797,7 +760,7 @@ paint_struct* sub_98196C( switch (session->CurrentRotation) { case 0: - rotate_map_coordinates(&coord_3d.x, &coord_3d.y, TILE_ELEMENT_DIRECTION_WEST); + coord_3d = CoordsXYZ{ coord_3d.Rotate(TILE_ELEMENT_DIRECTION_WEST), coord_3d.z }; boundBox.x--; boundBox.y--; @@ -805,19 +768,19 @@ paint_struct* sub_98196C( break; case 1: - rotate_map_coordinates(&coord_3d.x, &coord_3d.y, TILE_ELEMENT_DIRECTION_SOUTH); + coord_3d = CoordsXYZ{ coord_3d.Rotate(TILE_ELEMENT_DIRECTION_SOUTH), coord_3d.z }; boundBox.x--; rotate_map_coordinates(&boundBox.x, &boundBox.y, TILE_ELEMENT_DIRECTION_SOUTH); break; case 2: - rotate_map_coordinates(&coord_3d.x, &coord_3d.y, TILE_ELEMENT_DIRECTION_EAST); + coord_3d = CoordsXYZ{ coord_3d.Rotate(TILE_ELEMENT_DIRECTION_EAST), coord_3d.z }; rotate_map_coordinates(&boundBox.x, &boundBox.y, TILE_ELEMENT_DIRECTION_EAST); break; case 3: - rotate_map_coordinates(&coord_3d.x, &coord_3d.y, TILE_ELEMENT_DIRECTION_NORTH); + coord_3d = CoordsXYZ{ coord_3d.Rotate(TILE_ELEMENT_DIRECTION_NORTH), coord_3d.z }; boundBox.y--; rotate_map_coordinates(&boundBox.x, &boundBox.y, TILE_ELEMENT_DIRECTION_NORTH); @@ -834,7 +797,7 @@ paint_struct* sub_98196C( ps->bounds.z = coord_3d.z; ps->bounds.z_end = (boundBox.z + coord_3d.z); - LocationXY16 map = coordinate_3d_to_2d(&coord_3d, session->CurrentRotation); + auto map = translate_3d_to_2d_with_z(session->CurrentRotation, coord_3d); ps->x = map.x; ps->y = map.y; @@ -845,7 +808,7 @@ paint_struct* sub_98196C( int16_t right = left + g1Element->width; int16_t top = bottom + g1Element->height; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (right <= dpi->x) return nullptr; @@ -916,7 +879,7 @@ paint_struct* sub_98197C( session->LastRootPS = nullptr; session->UnkF1AD2C = nullptr; - LocationXYZ16 offset = { x_offset, y_offset, z_offset }; + CoordsXYZ offset = { x_offset, y_offset, z_offset }; LocationXYZ16 boundBoxSize = { bound_box_length_x, bound_box_length_y, bound_box_length_z }; LocationXYZ16 boundBoxOffset = { bound_box_offset_x, bound_box_offset_y, bound_box_offset_z }; paint_struct* ps = sub_9819_c(session, image_id, offset, boundBoxSize, boundBoxOffset); @@ -978,7 +941,7 @@ paint_struct* sub_98198C( session->LastRootPS = nullptr; session->UnkF1AD2C = nullptr; - LocationXYZ16 offset = { x_offset, y_offset, z_offset }; + CoordsXYZ offset = { x_offset, y_offset, z_offset }; LocationXYZ16 boundBoxSize = { bound_box_length_x, bound_box_length_y, bound_box_length_z }; LocationXYZ16 boundBoxOffset = { bound_box_offset_x, bound_box_offset_y, bound_box_offset_z }; paint_struct* ps = sub_9819_c(session, image_id, offset, boundBoxSize, boundBoxOffset); @@ -1024,7 +987,7 @@ paint_struct* sub_98199C( bound_box_offset_x, bound_box_offset_y, bound_box_offset_z); } - LocationXYZ16 offset = { x_offset, y_offset, z_offset }; + CoordsXYZ offset = { x_offset, y_offset, z_offset }; LocationXYZ16 boundBox = { bound_box_length_x, bound_box_length_y, bound_box_length_z }; LocationXYZ16 boundBoxOffset = { bound_box_offset_x, bound_box_offset_y, bound_box_offset_z }; paint_struct* ps = sub_9819_c(session, image_id, offset, boundBox, boundBoxOffset); @@ -1146,12 +1109,12 @@ void paint_floating_money_effect( ps->args[3] = 0; ps->y_offsets = (uint8_t*)y_offsets; - const LocationXYZ16 position = { + const CoordsXYZ position = { session->SpritePosition.x, session->SpritePosition.y, z, }; - const LocationXY16 coord = coordinate_3d_to_2d(&position, rotation); + const auto coord = translate_3d_to_2d_with_z(rotation, position); ps->x = coord.x + offset_x; ps->y = coord.y; diff --git a/src/openrct2/paint/Paint.h b/src/openrct2/paint/Paint.h index ca7ba8277a..3d7780b6af 100644 --- a/src/openrct2/paint/Paint.h +++ b/src/openrct2/paint/Paint.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -139,7 +139,7 @@ struct tunnel_entry struct paint_session { - rct_drawpixelinfo* DPI; + rct_drawpixelinfo DPI; paint_entry PaintStructs[4000]; paint_struct* Quadrants[MAX_PAINT_QUADRANTS]; paint_struct PaintHead; @@ -182,7 +182,7 @@ extern LocationXY8 gClipSelectionA; extern LocationXY8 gClipSelectionB; /** rct2: 0x00993CC4. The white ghost that indicates not-yet-built elements. */ -#define CONSTRUCTION_MARKER (COLOUR_DARK_GREEN << 19 | COLOUR_GREY << 24 | IMAGE_TYPE_REMAP); +#define CONSTRUCTION_MARKER (COLOUR_DARK_GREEN << 19 | COLOUR_GREY << 24 | IMAGE_TYPE_REMAP) extern bool gShowDirtyVisuals; extern bool gPaintBoundingBoxes; extern bool gPaintBlockedTiles; diff --git a/src/openrct2/paint/PaintHelpers.cpp b/src/openrct2/paint/PaintHelpers.cpp index 43a0f35f50..aa9a83eff3 100644 --- a/src/openrct2/paint/PaintHelpers.cpp +++ b/src/openrct2/paint/PaintHelpers.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/paint/Painter.cpp b/src/openrct2/paint/Painter.cpp index 0f047c14ec..e9498eff9e 100644 --- a/src/openrct2/paint/Painter.cpp +++ b/src/openrct2/paint/Painter.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -20,6 +20,7 @@ #include "../interface/InteractiveConsole.h" #include "../localisation/FormatCodes.h" #include "../localisation/Language.h" +#include "../paint/Paint.h" #include "../title/TitleScreen.h" #include "../ui/UiContext.h" @@ -140,3 +141,49 @@ void Painter::MeasureFPS() } _lastSecond = currentTime; } + +paint_session* Painter::CreateSession(rct_drawpixelinfo* dpi, uint32_t viewFlags) +{ + paint_session* session = nullptr; + + if (_freePaintSessions.empty() == false) + { + // Re-use. + const size_t idx = _freePaintSessions.size() - 1; + session = _freePaintSessions[idx]; + + // Shrink by one. + _freePaintSessions.pop_back(); + } + else + { + // Create new one in pool. + _paintSessionPool.emplace_back(std::make_unique()); + session = _paintSessionPool.back().get(); + } + + session->DPI = *dpi; + session->EndOfPaintStructArray = &session->PaintStructs[4000 - 1]; + session->NextFreePaintStruct = session->PaintStructs; + session->LastRootPS = nullptr; + session->UnkF1AD2C = nullptr; + session->ViewFlags = viewFlags; + for (auto& quadrant : session->Quadrants) + { + quadrant = nullptr; + } + session->QuadrantBackIndex = std::numeric_limits::max(); + session->QuadrantFrontIndex = 0; + session->PSStringHead = nullptr; + session->LastPSString = nullptr; + session->WoodenSupportsPrependTo = nullptr; + session->CurrentlyDrawnItem = nullptr; + session->SurfaceElement = nullptr; + + return session; +} + +void Painter::ReleaseSession(paint_session* session) +{ + _freePaintSessions.push_back(session); +} diff --git a/src/openrct2/paint/Painter.h b/src/openrct2/paint/Painter.h index eac7b6f139..c2b81e6b4b 100644 --- a/src/openrct2/paint/Painter.h +++ b/src/openrct2/paint/Painter.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,9 +10,11 @@ #pragma once #include "../common.h" +#include "Paint.h" #include #include +#include struct rct_drawpixelinfo; @@ -30,22 +32,26 @@ namespace OpenRCT2 namespace Paint { - class Painter final + interface Painter final { private: std::shared_ptr const _uiContext; - + std::vector> _paintSessionPool; + std::vector _freePaintSessions; time_t _lastSecond = 0; int32_t _currentFPS = 0; int32_t _frames = 0; public: explicit Painter(const std::shared_ptr& uiContext); - void Paint(Drawing::IDrawingEngine& de); + void Paint(Drawing::IDrawingEngine & de); + + paint_session* CreateSession(rct_drawpixelinfo * dpi, uint32_t viewFlags); + void ReleaseSession(paint_session * session); private: - void PaintReplayNotice(rct_drawpixelinfo* dpi, const char* text); - void PaintFPS(rct_drawpixelinfo* dpi); + void PaintReplayNotice(rct_drawpixelinfo * dpi, const char* text); + void PaintFPS(rct_drawpixelinfo * dpi); void MeasureFPS(); }; } // namespace Paint diff --git a/src/openrct2/paint/Supports.cpp b/src/openrct2/paint/Supports.cpp index b1938da17d..593e4e2370 100644 --- a/src/openrct2/paint/Supports.cpp +++ b/src/openrct2/paint/Supports.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -1320,7 +1320,7 @@ bool path_b_supports_paint_setup( baseHeight += z; } - if (keepGoing == false) + if (!keepGoing) { break; } diff --git a/src/openrct2/paint/Supports.h b/src/openrct2/paint/Supports.h index 6959b394e7..7f19ba795a 100644 --- a/src/openrct2/paint/Supports.h +++ b/src/openrct2/paint/Supports.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/paint/VirtualFloor.cpp b/src/openrct2/paint/VirtualFloor.cpp index 70973d86fb..ff86d15680 100644 --- a/src/openrct2/paint/VirtualFloor.cpp +++ b/src/openrct2/paint/VirtualFloor.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -105,12 +105,12 @@ void virtual_floor_invalidate() if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT) { - for (LocationXY16* tile = gMapSelectionTiles; tile->x != -1; tile++) + for (const auto& tile : gMapSelectionTiles) { - min_position.x = std::min(min_position.x, tile->x); - min_position.y = std::min(min_position.y, tile->y); - max_position.x = std::max(max_position.x, tile->x); - max_position.y = std::max(max_position.y, tile->y); + min_position.x = std::min(min_position.x, tile.x); + min_position.y = std::min(min_position.y, tile.y); + max_position.x = std::max(max_position.x, tile.x); + max_position.y = std::max(max_position.y, tile.y); } } @@ -149,7 +149,7 @@ void virtual_floor_invalidate() return; } - log_verbose("Min: %d %d, Max: %d %d\n", min_position.x, min_position.y, max_position.x, max_position.y); + log_verbose("Min: %d %d, Max: %d %d", min_position.x, min_position.y, max_position.x, max_position.y); // Invalidate new region if coordinates are set. if (min_position.x != std::numeric_limits::max() && min_position.y != std::numeric_limits::max() @@ -186,10 +186,10 @@ bool virtual_floor_tile_is_floor(int16_t x, int16_t y) else if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT) { // Check if we are anywhere near the selection tiles (larger scenery / rides) - for (LocationXY16* tile = gMapSelectionTiles; tile->x != -1; tile++) + for (const auto& tile : gMapSelectionTiles) { - if (x >= tile->x - _virtualFloorBaseSize && y >= tile->y - _virtualFloorBaseSize - && x <= tile->x + _virtualFloorBaseSize && y <= tile->y + _virtualFloorBaseSize) + if (x >= tile.x - _virtualFloorBaseSize && y >= tile.y - _virtualFloorBaseSize + && x <= tile.x + _virtualFloorBaseSize && y <= tile.y + _virtualFloorBaseSize) { return true; } @@ -223,9 +223,9 @@ static void virtual_floor_get_tile_properties( // See if we are on top of the selection tiles if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT) { - for (LocationXY16* tile = gMapSelectionTiles; tile->x != -1; tile++) + for (const auto& tile : gMapSelectionTiles) { - if (x == tile->x && y == tile->y) + if (x == tile.x && y == tile.y) { *outLit = true; break; @@ -233,7 +233,7 @@ static void virtual_floor_get_tile_properties( } } - *tileOwned = map_is_location_owned(x, y, height); + *tileOwned = map_is_location_owned({ x, y, height }); if (gCheatsSandboxMode) *tileOwned = true; @@ -243,6 +243,8 @@ static void virtual_floor_get_tile_properties( // * Walls / banners, which are displayed as occupied edges // * Ghost objects, which are displayed as lit squares TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { int32_t elementType = tileElement->GetType(); diff --git a/src/openrct2/paint/VirtualFloor.h b/src/openrct2/paint/VirtualFloor.h index 645bd7942d..126a96f92d 100644 --- a/src/openrct2/paint/VirtualFloor.h +++ b/src/openrct2/paint/VirtualFloor.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/paint/sprite/Paint.Litter.cpp b/src/openrct2/paint/sprite/Paint.Litter.cpp index fb36a79c76..ece57eb724 100644 --- a/src/openrct2/paint/sprite/Paint.Litter.cpp +++ b/src/openrct2/paint/sprite/Paint.Litter.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -69,7 +69,7 @@ void litter_paint(paint_session* session, const rct_litter* litter, int32_t imag { rct_drawpixelinfo* dpi; - dpi = session->DPI; + dpi = &session->DPI; if (dpi->zoom_level != 0) return; // If zoomed at all no litter drawn diff --git a/src/openrct2/paint/sprite/Paint.Misc.cpp b/src/openrct2/paint/sprite/Paint.Misc.cpp index 5d36cea41a..e4a3fe5e94 100644 --- a/src/openrct2/paint/sprite/Paint.Misc.cpp +++ b/src/openrct2/paint/sprite/Paint.Misc.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,6 +10,7 @@ #include "../../drawing/Drawing.h" #include "../../interface/Viewport.h" #include "../../localisation/StringIds.h" +#include "../../world/Fountain.h" #include "../../world/Sprite.h" #include "../Paint.h" #include "Paint.Sprite.h" @@ -30,7 +31,7 @@ const uint32_t vehicle_particle_base_sprites[] = { */ void misc_paint(paint_session* session, const rct_sprite* misc, int32_t imageDirection) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; switch (misc->steam_particle.type) { @@ -49,8 +50,7 @@ void misc_paint(paint_session* session, const rct_sprite* misc, int32_t imageDir } const rct_money_effect* moneyEffect = &misc->money_effect; - money32 value; - rct_string_id stringId = money_effect_get_string_id(moneyEffect, &value); + auto [stringId, value] = moneyEffect->GetStringId(); paint_floating_money_effect( session, value, stringId, moneyEffect->y, moneyEffect->z, (int8_t*)&money_wave[moneyEffect->wiggle % 22], moneyEffect->offset_x, session->CurrentRotation); @@ -103,12 +103,12 @@ void misc_paint(paint_session* session, const rct_sprite* misc, int32_t imageDir return; } - rct_jumping_fountain jumpingFountain = misc->jumping_fountain; + JumpingFountain jumpingFountain = misc->jumping_fountain; uint16_t height = jumpingFountain.z + 6; int32_t ebx = imageDirection / 8; - uint8_t al = (jumpingFountain.fountain_flags / 128) & 1; + uint8_t al = (jumpingFountain.FountainFlags / 128) & 1; uint8_t ah = (jumpingFountain.sprite_direction / 16) & 1; if (al == ah) @@ -184,7 +184,7 @@ void misc_paint(paint_session* session, const rct_sprite* misc, int32_t imageDir } case SPRITE_MISC_DUCK: - if (dpi->zoom_level == 0) + if (dpi->zoom_level <= 1) { const rct_duck* duck = &misc->duck; uint32_t imageId = duck_get_frame_image(&misc->duck, imageDirection); diff --git a/src/openrct2/paint/sprite/Paint.Peep.cpp b/src/openrct2/paint/sprite/Paint.Peep.cpp index 78399c376f..ffac5bf987 100644 --- a/src/openrct2/paint/sprite/Paint.Peep.cpp +++ b/src/openrct2/paint/sprite/Paint.Peep.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -48,7 +48,7 @@ void peep_paint(paint_session* session, const Peep* peep, int32_t imageDirection break; default: return; - }; + } lightfx_add_3d_light( peep->sprite_index, 0x0000 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, peep_x, peep_y, peep_z, LIGHTFX_LIGHT_TYPE_SPOT_1); @@ -56,7 +56,7 @@ void peep_paint(paint_session* session, const Peep* peep, int32_t imageDirection } #endif - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level > 2) { return; diff --git a/src/openrct2/paint/sprite/Paint.Sprite.cpp b/src/openrct2/paint/sprite/Paint.Sprite.cpp index 234b6c054e..6e5f3a9185 100644 --- a/src/openrct2/paint/sprite/Paint.Sprite.cpp +++ b/src/openrct2/paint/sprite/Paint.Sprite.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -40,7 +40,7 @@ void sprite_paint_setup(paint_session* session, const uint16_t x, const uint16_t return; } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level > 2) { return; @@ -89,7 +89,7 @@ void sprite_paint_setup(paint_session* session, const uint16_t x, const uint16_t } } - dpi = session->DPI; + dpi = &session->DPI; if (dpi->y + dpi->height <= spr->generic.sprite_top || spr->generic.sprite_bottom <= dpi->y || dpi->x + dpi->width <= spr->generic.sprite_left || spr->generic.sprite_right <= dpi->x) diff --git a/src/openrct2/paint/sprite/Paint.Sprite.h b/src/openrct2/paint/sprite/Paint.Sprite.h index 9d28015414..3a3950e217 100644 --- a/src/openrct2/paint/sprite/Paint.Sprite.h +++ b/src/openrct2/paint/sprite/Paint.Sprite.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/paint/tile_element/Paint.Banner.cpp b/src/openrct2/paint/tile_element/Paint.Banner.cpp index 263e5497ce..c0aec5b609 100644 --- a/src/openrct2/paint/tile_element/Paint.Banner.cpp +++ b/src/openrct2/paint/tile_element/Paint.Banner.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,6 +12,7 @@ #include "../../interface/Viewport.h" #include "../../localisation/Localisation.h" #include "../../ride/TrackDesign.h" +#include "../../sprites.h" #include "../../world/Banner.h" #include "../../world/Scenery.h" #include "../Paint.h" @@ -34,7 +35,7 @@ const LocationXY16 BannerBoundBoxes[][2] = { void banner_paint(paint_session* session, uint8_t direction, int32_t height, const TileElement* tile_element) { uint16_t boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; session->InteractionType = VIEWPORT_INTERACTION_ITEM_BANNER; @@ -43,14 +44,25 @@ void banner_paint(paint_session* session, uint8_t direction, int32_t height, con height -= 16; - rct_scenery_entry* banner_scenery = get_banner_entry(gBanners[tile_element->AsBanner()->GetIndex()].type); + auto bannerElement = tile_element->AsBanner(); + if (bannerElement == nullptr) + { + return; + } + auto banner = bannerElement->GetBanner(); + if (banner == nullptr) + { + return; + } + + auto banner_scenery = get_banner_entry(banner->type); if (banner_scenery == nullptr) { return; } - direction += tile_element->AsBanner()->GetPosition(); + direction += bannerElement->GetPosition(); direction &= 3; boundBoxOffsetX = BannerBoundBoxes[direction][0].x; @@ -67,7 +79,7 @@ void banner_paint(paint_session* session, uint8_t direction, int32_t height, con } else { - image_id |= (gBanners[tile_element->AsBanner()->GetIndex()].colour << 19) | IMAGE_TYPE_REMAP; + image_id |= (banner->colour << 19) | IMAGE_TYPE_REMAP; } sub_98197C(session, image_id, 0, 0, 1, 1, 0x15, height, boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ); @@ -92,30 +104,28 @@ void banner_paint(paint_session* session, uint8_t direction, int32_t height, con scrollingMode += direction; - set_format_arg(0, uint32_t, 0); - set_format_arg(4, uint32_t, 0); + // We need to get the text colour code into the beginning of the string, so use a temporary buffer + char colouredBannerText[32]{}; + utf8_write_codepoint(colouredBannerText, FORMAT_COLOUR_CODE_START + banner->text_colour); + + set_format_arg(0, rct_string_id, STR_STRING_STRINGID); + set_format_arg(2, const char*, &colouredBannerText); + banner->FormatTextTo(gCommonFormatArgs + 2 + sizeof(const char*)); - rct_string_id string_id = STR_NO_ENTRY; - if (!(gBanners[tile_element->AsBanner()->GetIndex()].flags & BANNER_FLAG_NO_ENTRY)) - { - set_format_arg(0, rct_string_id, gBanners[tile_element->AsBanner()->GetIndex()].string_idx); - string_id = STR_BANNER_TEXT_FORMAT; - } if (gConfigGeneral.upper_case_banners) { - format_string_to_upper(gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), string_id, gCommonFormatArgs); + format_string_to_upper( + gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), STR_BANNER_TEXT_FORMAT, gCommonFormatArgs); } else { - format_string(gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), string_id, gCommonFormatArgs); + format_string(gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), STR_BANNER_TEXT_FORMAT, gCommonFormatArgs); } gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; uint16_t string_width = gfx_get_string_width(gCommonStringFormatBuffer); uint16_t scroll = (gCurrentTicks / 2) % string_width; - - sub_98199C( - session, scrolling_text_setup(session, string_id, scroll, scrollingMode), 0, 0, 1, 1, 0x15, height + 22, - boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ); + auto scrollIndex = scrolling_text_setup(session, STR_BANNER_TEXT_FORMAT, scroll, scrollingMode, COLOUR_BLACK); + sub_98199C(session, scrollIndex, 0, 0, 1, 1, 0x15, height + 22, boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ); } diff --git a/src/openrct2/paint/tile_element/Paint.Entrance.cpp b/src/openrct2/paint/tile_element/Paint.Entrance.cpp index 4abac2a560..6173b05ec4 100644 --- a/src/openrct2/paint/tile_element/Paint.Entrance.cpp +++ b/src/openrct2/paint/tile_element/Paint.Entrance.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -7,7 +7,9 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ +#include "../../Context.h" #include "../../Game.h" +#include "../../GameState.h" #include "../../config/Config.h" #include "../../drawing/LightFX.h" #include "../../interface/Viewport.h" @@ -23,8 +25,6 @@ #include "../Supports.h" #include "Paint.TileElement.h" -static uint32_t _unk9E32BC; - /** * * rct2: 0x0066508C, 0x00665540 @@ -65,11 +65,16 @@ static void ride_entrance_exit_paint(paint_session* session, uint8_t direction, lightfx_add_3d_light_magic_from_drawing_tile( session->MapPosition, 0, 16, height + 16, LIGHTFX_LIGHT_TYPE_LANTERN_2); break; - }; + } } #endif - Ride* ride = get_ride(tile_element->AsEntrance()->GetRideIndex()); + auto ride = get_ride(tile_element->AsEntrance()->GetRideIndex()); + if (ride == nullptr) + { + return; + } + auto stationObj = ride_get_station_object(ride); if (stationObj == nullptr || stationObj->BaseImageId == 0) { @@ -89,13 +94,13 @@ static void ride_entrance_exit_paint(paint_session* session, uint8_t direction, image_id = (colour_1 << 19) | (colour_2 << 24) | IMAGE_TYPE_REMAP | IMAGE_TYPE_REMAP_2_PLUS; session->InteractionType = VIEWPORT_INTERACTION_ITEM_RIDE; - _unk9E32BC = 0; + uint32_t entranceImageId = 0; if (tile_element->IsGhost()) { session->InteractionType = VIEWPORT_INTERACTION_ITEM_NONE; image_id = CONSTRUCTION_MARKER; - _unk9E32BC = image_id; + entranceImageId = image_id; if (transparant_image_id) transparant_image_id = image_id; } @@ -160,27 +165,26 @@ static void ride_entrance_exit_paint(paint_session* session, uint8_t direction, if (!is_exit && !(tile_element->IsGhost()) && tile_element->AsEntrance()->GetRideIndex() != RIDE_ID_NULL && stationObj->ScrollingMode != SCROLLING_MODE_NONE) { - set_format_arg(0, uint32_t, 0); + set_format_arg(0, rct_string_id, STR_RIDE_ENTRANCE_NAME); set_format_arg(4, uint32_t, 0); - rct_string_id string_id = STR_RIDE_ENTRANCE_CLOSED; - if (ride->status == RIDE_STATUS_OPEN && !(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)) { - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); - - string_id = STR_RIDE_ENTRANCE_NAME; + ride->FormatNameTo(gCommonFormatArgs + 2); + } + else + { + set_format_arg(2, rct_string_id, STR_RIDE_ENTRANCE_CLOSED); } utf8 entrance_string[256]; if (gConfigGeneral.upper_case_banners) { - format_string_to_upper(entrance_string, sizeof(entrance_string), string_id, gCommonFormatArgs); + format_string_to_upper(entrance_string, sizeof(entrance_string), STR_BANNER_TEXT_FORMAT, gCommonFormatArgs); } else { - format_string(entrance_string, sizeof(entrance_string), string_id, gCommonFormatArgs); + format_string(entrance_string, sizeof(entrance_string), STR_BANNER_TEXT_FORMAT, gCommonFormatArgs); } gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; @@ -189,11 +193,11 @@ static void ride_entrance_exit_paint(paint_session* session, uint8_t direction, uint16_t scroll = (gCurrentTicks / 2) % string_width; sub_98199C( - session, scrolling_text_setup(session, string_id, scroll, stationObj->ScrollingMode), 0, 0, 0x1C, 0x1C, 0x33, - height + stationObj->Height, 2, 2, height + stationObj->Height); + session, scrolling_text_setup(session, STR_BANNER_TEXT_FORMAT, scroll, stationObj->ScrollingMode, COLOUR_BLACK), 0, + 0, 0x1C, 0x1C, 0x33, height + stationObj->Height, 2, 2, height + stationObj->Height); } - image_id = _unk9E32BC; + image_id = entranceImageId; if (image_id == 0) { image_id = SPRITE_ID_PALETTE_COLOUR_1(COLOUR_SATURATED_BROWN); @@ -223,13 +227,11 @@ static void park_entrance_paint(paint_session* session, uint8_t direction, int32 #endif session->InteractionType = VIEWPORT_INTERACTION_ITEM_PARK; - _unk9E32BC = 0; uint32_t image_id, ghost_id = 0; if (tile_element->IsGhost()) { session->InteractionType = VIEWPORT_INTERACTION_ITEM_NONE; ghost_id = CONSTRUCTION_MARKER; - _unk9E32BC = ghost_id; } // Index to which part of the entrance @@ -268,26 +270,30 @@ static void park_entrance_paint(paint_session* session, uint8_t direction, int32 break; { - rct_string_id park_text_id = STR_BANNER_TEXT_CLOSED; set_format_arg(0, uint32_t, 0); set_format_arg(4, uint32_t, 0); if (gParkFlags & PARK_FLAGS_PARK_OPEN) { - set_format_arg(0, rct_string_id, gParkName); - set_format_arg(2, uint32_t, gParkNameArgs); - - park_text_id = STR_BANNER_TEXT_FORMAT; + const auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto name = park.Name.c_str(); + set_format_arg(0, rct_string_id, STR_STRING); + set_format_arg(2, const char*, name); + } + else + { + set_format_arg(0, rct_string_id, STR_BANNER_TEXT_CLOSED); + set_format_arg(2, uint32_t, 0); } utf8 park_name[256]; if (gConfigGeneral.upper_case_banners) { - format_string_to_upper(park_name, sizeof(park_name), park_text_id, gCommonFormatArgs); + format_string_to_upper(park_name, sizeof(park_name), STR_BANNER_TEXT_FORMAT, gCommonFormatArgs); } else { - format_string(park_name, sizeof(park_name), park_text_id, gCommonFormatArgs); + format_string(park_name, sizeof(park_name), STR_BANNER_TEXT_FORMAT, gCommonFormatArgs); } gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; @@ -298,7 +304,8 @@ static void park_entrance_paint(paint_session* session, uint8_t direction, int32 if (entrance->scrolling_mode == SCROLLING_MODE_NONE) break; - int32_t stsetup = scrolling_text_setup(session, park_text_id, scroll, entrance->scrolling_mode + direction / 2); + int32_t stsetup = scrolling_text_setup( + session, STR_BANNER_TEXT_FORMAT, scroll, entrance->scrolling_mode + direction / 2, COLOUR_BLACK); int32_t text_height = height + entrance->text_height; sub_98199C(session, stsetup, 0, 0, 0x1C, 0x1C, 0x2F, text_height, 2, 2, text_height); } @@ -334,7 +341,7 @@ void entrance_paint(paint_session* session, uint8_t direction, int32_t height, c { session->InteractionType = VIEWPORT_INTERACTION_ITEM_LABEL; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (session->ViewFlags & VIEWPORT_FLAG_PATH_HEIGHTS && dpi->zoom_level == 0) { diff --git a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp index 9915f09fdf..75489de1eb 100644 --- a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -97,11 +97,10 @@ static int32_t large_scenery_sign_text_height(const utf8* str, rct_large_scenery return height; } -static const utf8* large_scenery_sign_fit_text(const utf8* str, rct_large_scenery_text* text, bool height) +static void large_scenery_sign_fit_text(const utf8* str, rct_large_scenery_text* text, bool height, utf8* fitStr, size_t bufLen) { - static utf8 fitStr[32]; utf8* fitStrEnd = fitStr; - safe_strcpy(fitStr, str, sizeof(fitStr)); + safe_strcpy(fitStr, str, bufLen); int32_t w = 0; uint32_t codepoint; while (w <= text->max_width && (codepoint = utf8_get_next(fitStrEnd, (const utf8**)&fitStrEnd)) != 0) @@ -116,7 +115,6 @@ static const utf8* large_scenery_sign_fit_text(const utf8* str, rct_large_scener } } *fitStrEnd = 0; - return fitStr; } static int32_t div_to_minus_infinity(int32_t a, int32_t b) @@ -128,7 +126,8 @@ static void large_scenery_sign_paint_line( paint_session* session, const utf8* str, rct_large_scenery_text* text, int32_t textImage, int32_t textColour, uint8_t direction, int32_t y_offset) { - const utf8* fitStr = large_scenery_sign_fit_text(str, text, false); + utf8 fitStr[32]; + large_scenery_sign_fit_text(str, text, false, fitStr, sizeof(fitStr)); int32_t width = large_scenery_sign_text_width(fitStr, text); int32_t x_offset = text->offset[(direction & 1)].x; int32_t acc = y_offset * ((direction & 1) ? -1 : 1); @@ -139,7 +138,8 @@ static void large_scenery_sign_paint_line( acc -= (width / 2); } uint32_t codepoint; - while ((codepoint = utf8_get_next(fitStr, &fitStr)) != 0) + const utf8* fitStrPtr = fitStr; + while ((codepoint = utf8_get_next(fitStrPtr, &fitStrPtr)) != 0) { int32_t glyph_offset = large_scenery_sign_get_glyph(text, codepoint)->image_offset; uint8_t glyph_type = direction & 1; @@ -293,7 +293,7 @@ void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t hei return; } } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level > 1) { large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile); @@ -309,98 +309,96 @@ void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t hei textColour = COLOUR_GREY; } textColour = (textColour << 19) | IMAGE_TYPE_REMAP; - BannerIndex bannerIndex = tileElement->AsLargeScenery()->GetBannerIndex(); - rct_banner* banner = &gBanners[bannerIndex]; - rct_string_id stringId = banner->string_idx; - if (banner->flags & BANNER_FLAG_LINKED_TO_RIDE) + auto banner = tileElement->AsLargeScenery()->GetBanner(); + if (banner != nullptr) { - Ride* ride = get_ride(banner->ride_index); - stringId = ride->name; - set_format_arg(0, uint32_t, ride->name_arguments); - } - utf8 signString[256]; - format_string(signString, sizeof(signString), stringId, gCommonFormatArgs); - rct_large_scenery_text* text = entry->large_scenery.text; - int32_t y_offset = (text->offset[(direction & 1)].y * 2); - if (text->flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL) - { - // Draw vertical sign: - y_offset += 1; - utf8 fitStr[32]; - const utf8* fitStrPtr = fitStr; - safe_strcpy(fitStr, large_scenery_sign_fit_text(signString, text, true), sizeof(fitStr)); - int32_t height2 = large_scenery_sign_text_height(fitStr, text); - uint32_t codepoint; - while ((codepoint = utf8_get_next(fitStrPtr, &fitStrPtr)) != 0) + banner->FormatTextTo(gCommonFormatArgs); + utf8 signString[256]; + format_string(signString, sizeof(signString), STR_STRINGID, gCommonFormatArgs); + rct_large_scenery_text* text = entry->large_scenery.text; + int32_t y_offset = (text->offset[(direction & 1)].y * 2); + if (text->flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL) { - utf8 str[5] = { 0 }; - utf8_write_codepoint(str, codepoint); - large_scenery_sign_paint_line( - session, str, entry->large_scenery.text, entry->large_scenery.text_image, textColour, direction, - y_offset - height2); - y_offset += large_scenery_sign_get_glyph(text, codepoint)->height * 2; - } - } - else - { - y_offset -= (direction & 1); - if (text->flags & LARGE_SCENERY_TEXT_FLAG_TWO_LINE) - { - // Draw two-line sign: - int32_t width = large_scenery_sign_text_width(signString, text); - if (width > text->max_width) + // Draw vertical sign: + y_offset += 1; + utf8 fitStr[32]; + large_scenery_sign_fit_text(signString, text, true, fitStr, sizeof(fitStr)); + safe_strcpy(fitStr, fitStr, sizeof(fitStr)); + const utf8* fitStrPtr = fitStr; + int32_t height2 = large_scenery_sign_text_height(fitStr, text); + uint32_t codepoint; + while ((codepoint = utf8_get_next(fitStrPtr, &fitStrPtr)) != 0) { - y_offset -= large_scenery_sign_get_glyph(text, 'A')->height + 1; - utf8* src = signString; - for (int32_t i = 0; i < 2; i++) + utf8 str[5] = { 0 }; + utf8_write_codepoint(str, codepoint); + large_scenery_sign_paint_line( + session, str, entry->large_scenery.text, entry->large_scenery.text_image, textColour, direction, + y_offset - height2); + y_offset += large_scenery_sign_get_glyph(text, codepoint)->height * 2; + } + } + else + { + y_offset -= (direction & 1); + if (text->flags & LARGE_SCENERY_TEXT_FLAG_TWO_LINE) + { + // Draw two-line sign: + int32_t width = large_scenery_sign_text_width(signString, text); + if (width > text->max_width) { - utf8 str1[64] = { 0 }; - utf8* dst = str1; - utf8* srcold = src; - utf8* spacesrc = nullptr; - utf8* spacedst = nullptr; - int32_t w = 0; - uint32_t codepoint = utf8_get_next(src, (const utf8**)&src); - do + y_offset -= large_scenery_sign_get_glyph(text, 'A')->height + 1; + utf8* src = signString; + for (int32_t i = 0; i < 2; i++) { - w += large_scenery_sign_get_glyph(text, codepoint)->width; - if (codepoint == ' ') + utf8 str1[64] = { 0 }; + utf8* dst = str1; + utf8* srcold = src; + utf8* spacesrc = nullptr; + utf8* spacedst = nullptr; + int32_t w = 0; + uint32_t codepoint = utf8_get_next(src, (const utf8**)&src); + do { - spacesrc = src; - spacedst = dst; + w += large_scenery_sign_get_glyph(text, codepoint)->width; + if (codepoint == ' ') + { + spacesrc = src; + spacedst = dst; + } + } while (w <= text->max_width && (dst = utf8_write_codepoint(dst, codepoint)) != nullptr + && (srcold = src) != nullptr + && (codepoint = utf8_get_next(src, (const utf8**)&src)) != '\0'); + src = srcold; + if (spacesrc && codepoint) + { + *spacedst = 0; + src = spacesrc; } - } while (w <= text->max_width && (dst = utf8_write_codepoint(dst, codepoint)) != nullptr - && (srcold = src) != nullptr && (codepoint = utf8_get_next(src, (const utf8**)&src)) != '\0'); - src = srcold; - if (spacesrc && codepoint) - { - *spacedst = 0; - src = spacesrc; + large_scenery_sign_paint_line( + session, str1, entry->large_scenery.text, entry->large_scenery.text_image, textColour, + direction, y_offset); + y_offset += (large_scenery_sign_get_glyph(text, 'A')->height + 1) * 2; } + } + else + { large_scenery_sign_paint_line( - session, str1, entry->large_scenery.text, entry->large_scenery.text_image, textColour, direction, - y_offset); - y_offset += (large_scenery_sign_get_glyph(text, 'A')->height + 1) * 2; + session, signString, entry->large_scenery.text, entry->large_scenery.text_image, textColour, + direction, y_offset); } } else { + // Draw one-line sign: large_scenery_sign_paint_line( session, signString, entry->large_scenery.text, entry->large_scenery.text_image, textColour, direction, y_offset); } } - else - { - // Draw one-line sign: - large_scenery_sign_paint_line( - session, signString, entry->large_scenery.text, entry->large_scenery.text_image, textColour, direction, - y_offset); - } } return; } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level > 0) { large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile); @@ -413,38 +411,31 @@ void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t hei return; } // Draw scrolling text: - set_format_arg(0, uint32_t, 0); - set_format_arg(4, uint32_t, 0); uint8_t textColour = tileElement->AsLargeScenery()->GetSecondaryColour(); if (dword_F4387C) { textColour = COLOUR_GREY; } - if (direction == 3) + if (direction == 0) { - textColour |= (1 << 7); - } - // 6B809A: - set_format_arg(7, uint8_t, textColour); - BannerIndex bannerIndex = tileElement->AsLargeScenery()->GetBannerIndex(); - uint16_t scrollMode = entry->large_scenery.scrolling_mode + ((direction + 1) & 0x3); - rct_banner* banner = &gBanners[bannerIndex]; - set_format_arg(0, rct_string_id, banner->string_idx); - if (banner->flags & BANNER_FLAG_LINKED_TO_RIDE) - { - Ride* ride = get_ride(banner->ride_index); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); - } - utf8 signString[256]; - rct_string_id stringId = STR_SCROLLING_SIGN_TEXT; - if (gConfigGeneral.upper_case_banners) - { - format_string_to_upper(signString, sizeof(signString), stringId, gCommonFormatArgs); + textColour = ColourMapA[textColour].mid_dark; } else { - format_string(signString, sizeof(signString), stringId, gCommonFormatArgs); + textColour = ColourMapA[textColour].light; + } + // 6B809A: + uint16_t scrollMode = entry->large_scenery.scrolling_mode + ((direction + 1) & 0x3); + auto banner = tileElement->AsLargeScenery()->GetBanner(); + banner->FormatTextTo(gCommonFormatArgs); + utf8 signString[256]; + if (gConfigGeneral.upper_case_banners) + { + format_string_to_upper(signString, sizeof(signString), STR_SCROLLING_SIGN_TEXT, gCommonFormatArgs); + } + else + { + format_string(signString, sizeof(signString), STR_SCROLLING_SIGN_TEXT, gCommonFormatArgs); } gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; @@ -452,8 +443,8 @@ void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t hei uint16_t string_width = gfx_get_string_width(signString); uint16_t scroll = (gCurrentTicks / 2) % string_width; sub_98199C( - session, scrolling_text_setup(session, stringId, scroll, scrollMode), 0, 0, 1, 1, 21, height + 25, boxoffset.x, - boxoffset.y, boxoffset.z); + session, scrolling_text_setup(session, STR_SCROLLING_SIGN_TEXT, scroll, scrollMode, textColour), 0, 0, 1, 1, 21, + height + 25, boxoffset.x, boxoffset.y, boxoffset.z); large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile); } diff --git a/src/openrct2/paint/tile_element/Paint.Path.cpp b/src/openrct2/paint/tile_element/Paint.Path.cpp index d1702af8b8..d16f50faa0 100644 --- a/src/openrct2/paint/tile_element/Paint.Path.cpp +++ b/src/openrct2/paint/tile_element/Paint.Path.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -442,7 +442,8 @@ static void sub_6A4101( direction--; // If text shown - if (direction < 2 && tile_element->AsPath()->GetRideIndex() != RIDE_ID_NULL && imageFlags == 0) + auto ride = get_ride(tile_element->AsPath()->GetRideIndex()); + if (direction < 2 && ride != nullptr && imageFlags == 0) { uint16_t scrollingMode = railingEntry->scrolling_mode; scrollingMode += direction; @@ -450,22 +451,24 @@ static void sub_6A4101( set_format_arg(0, uint32_t, 0); set_format_arg(4, uint32_t, 0); - Ride* ride = get_ride(tile_element->AsPath()->GetRideIndex()); - rct_string_id string_id = STR_RIDE_ENTRANCE_CLOSED; if (ride->status == RIDE_STATUS_OPEN && !(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)) { - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); - string_id = STR_RIDE_ENTRANCE_NAME; + set_format_arg(0, rct_string_id, STR_RIDE_ENTRANCE_NAME); + ride->FormatNameTo(gCommonFormatArgs + 2); + } + else + { + set_format_arg(0, rct_string_id, STR_RIDE_ENTRANCE_CLOSED); } if (gConfigGeneral.upper_case_banners) { format_string_to_upper( - gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), string_id, gCommonFormatArgs); + gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), STR_BANNER_TEXT_FORMAT, gCommonFormatArgs); } else { - format_string(gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), string_id, gCommonFormatArgs); + format_string( + gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), STR_BANNER_TEXT_FORMAT, gCommonFormatArgs); } gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; @@ -474,8 +477,8 @@ static void sub_6A4101( uint16_t scroll = (gCurrentTicks / 2) % string_width; sub_98199C( - session, scrolling_text_setup(session, string_id, scroll, scrollingMode), 0, 0, 1, 1, 21, height + 7, - boundBoxOffsets.x, boundBoxOffsets.y, boundBoxOffsets.z); + session, scrolling_text_setup(session, STR_BANNER_TEXT_FORMAT, scroll, scrollingMode, COLOUR_BLACK), 0, 0, 1, 1, + 21, height + 7, boundBoxOffsets.x, boundBoxOffsets.y, boundBoxOffsets.z); } session->InteractionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; @@ -682,10 +685,12 @@ static void sub_6A3F61( // Probably drawing benches etc. - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level <= 1) { + bool paintScenery = true; + if (!gTrackDesignSaveMode) { if (tile_element->AsPath()->HasAddition()) @@ -701,45 +706,50 @@ static void sub_6A3F61( // Can be null if the object is not loaded. if (sceneryEntry == nullptr) - return; - - if ((session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES) && !(tile_element->AsPath()->IsBroken()) + { + paintScenery = false; + } + else if ( + (session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES) && !(tile_element->AsPath()->IsBroken()) && !(sceneryEntry->path_bit.draw_type == PATH_BIT_DRAW_TYPE_BINS)) { - return; + paintScenery = false; } - - switch (sceneryEntry->path_bit.draw_type) + else { - case PATH_BIT_DRAW_TYPE_LIGHTS: - path_bit_lights_paint( - session, sceneryEntry, tile_element, height, (uint8_t)connectedEdges, sceneryImageFlags); - break; - case PATH_BIT_DRAW_TYPE_BINS: - path_bit_bins_paint( - session, sceneryEntry, tile_element, height, (uint8_t)connectedEdges, sceneryImageFlags); - break; - case PATH_BIT_DRAW_TYPE_BENCHES: - path_bit_benches_paint( - session, sceneryEntry, tile_element, height, (uint8_t)connectedEdges, sceneryImageFlags); - break; - case PATH_BIT_DRAW_TYPE_JUMPING_FOUNTAINS: - path_bit_jumping_fountains_paint(session, sceneryEntry, height, sceneryImageFlags, dpi); - break; - } + switch (sceneryEntry->path_bit.draw_type) + { + case PATH_BIT_DRAW_TYPE_LIGHTS: + path_bit_lights_paint( + session, sceneryEntry, tile_element, height, (uint8_t)connectedEdges, sceneryImageFlags); + break; + case PATH_BIT_DRAW_TYPE_BINS: + path_bit_bins_paint( + session, sceneryEntry, tile_element, height, (uint8_t)connectedEdges, sceneryImageFlags); + break; + case PATH_BIT_DRAW_TYPE_BENCHES: + path_bit_benches_paint( + session, sceneryEntry, tile_element, height, (uint8_t)connectedEdges, sceneryImageFlags); + break; + case PATH_BIT_DRAW_TYPE_JUMPING_FOUNTAINS: + path_bit_jumping_fountains_paint(session, sceneryEntry, height, sceneryImageFlags, dpi); + break; + } - session->InteractionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; + session->InteractionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; - if (sceneryImageFlags != 0) - { - session->InteractionType = VIEWPORT_INTERACTION_ITEM_NONE; + if (sceneryImageFlags != 0) + { + session->InteractionType = VIEWPORT_INTERACTION_ITEM_NONE; + } } } } // Redundant zoom-level check removed - sub_6A4101(session, tile_element, height, connectedEdges, word_F3F038, railingEntry, imageFlags); + if (paintScenery) + sub_6A4101(session, tile_element, height, connectedEdges, word_F3F038, railingEntry, imageFlags); } // This is about tunnel drawing @@ -849,7 +859,7 @@ void path_paint(paint_session* session, uint16_t height, const TileElement* tile int16_t x = session->MapPosition.x, y = session->MapPosition.y; - TileElement* surface = map_get_surface_element_at({ session->MapPosition.x, session->MapPosition.y }); + auto surface = map_get_surface_element_at({ session->MapPosition.x, session->MapPosition.y }); uint16_t bl = height / 8; if (surface == nullptr) @@ -866,14 +876,14 @@ void path_paint(paint_session* session, uint16_t height, const TileElement* tile { // Diagonal path - if (surface->AsSurface()->GetSlope() != PathSlopeToLandSlope[tile_element->AsPath()->GetSlopeDirection()]) + if (surface->GetSlope() != PathSlopeToLandSlope[tile_element->AsPath()->GetSlopeDirection()]) { hasSupports = true; } } else { - if (surface->AsSurface()->GetSlope() != TILE_ELEMENT_SLOPE_FLAT) + if (surface->GetSlope() != TILE_ELEMENT_SLOPE_FLAT) { hasSupports = true; } @@ -932,7 +942,7 @@ void path_paint(paint_session* session, uint16_t height, const TileElement* tile if (footpathEntry != nullptr && railingEntry != nullptr) { - if (railingEntry->support_type == FOOTPATH_ENTRY_SUPPORT_TYPE_POLE) + if (railingEntry->support_type == RailingEntrySupportType::Pole) { path_paint_pole_support( session, tile_element, height, footpathEntry, railingEntry, hasSupports, imageFlags, sceneryImageFlags); @@ -1008,14 +1018,7 @@ void path_paint_box_support( imageId = byte_98D6E0[edi]; } - if (pathElement->IsQueue()) - { - imageId += footpathEntry->queue_image; - } - else - { - imageId += footpathEntry->image; - } + imageId += footpathEntry->image; if (!session->DidPassSurface) { @@ -1164,14 +1167,7 @@ void path_paint_pole_support( imageId = byte_98D6E0[edi]; } - if (pathElement->IsQueue()) - { - imageId += footpathEntry->queue_image; - } - else - { - imageId += footpathEntry->image; - } + imageId += footpathEntry->image; // Below Surface if (!session->DidPassSurface) diff --git a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp index 02f53d3ff4..5c204c352f 100644 --- a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -176,7 +176,7 @@ void scenery_paint(paint_session* session, uint8_t direction, int32_t height, co if (scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_ANIMATED)) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if ((scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_VISIBLE_WHEN_ZOOMED)) || (dpi->zoom_level <= 1)) { // 6E01A9: diff --git a/src/openrct2/paint/tile_element/Paint.Surface.cpp b/src/openrct2/paint/tile_element/Paint.Surface.cpp index 243afdcd9b..193bc5df6a 100644 --- a/src/openrct2/paint/tile_element/Paint.Surface.cpp +++ b/src/openrct2/paint/tile_element/Paint.Surface.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -515,7 +515,8 @@ static void viewport_surface_smoothen_edge( static bool tile_is_inside_clip_view(const tile_descriptor& tile) { - Guard::ArgumentNotNull(tile.tile_element); + if (tile.tile_element == nullptr) + return false; if (tile.tile_element->base_height > gClipHeight) return false; @@ -584,7 +585,7 @@ static void viewport_surface_draw_tile_side_bottom( neighbourCornerHeight1 = MINIMUM_LAND_HEIGHT / 2; } - if (isWater) + if (isWater && neighbour.tile_element != nullptr) { uint8_t waterHeight = neighbour.tile_element->AsSurface()->GetWaterHeight(); if (waterHeight == height && !neighbourIsClippedAway) @@ -668,7 +669,7 @@ static void viewport_surface_draw_tile_side_bottom( tunnelIndex++; } - if (isWater == true || curHeight != tunnelArray[tunnelIndex].height) + if (isWater || curHeight != tunnelArray[tunnelIndex].height) { sub_98196C(session, base_image_id, offset.x, offset.y, bounds.x, bounds.y, 15, curHeight * 16); @@ -781,7 +782,7 @@ static void viewport_surface_draw_tile_side_top( return; } - if (isWater == false) + if (!isWater) dl = height; // save ecx @@ -907,7 +908,7 @@ static void viewport_surface_draw_water_side_top( */ void surface_paint(paint_session* session, uint8_t direction, uint16_t height, const TileElement* tileElement) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; session->InteractionType = VIEWPORT_INTERACTION_ITEM_TERRAIN; session->DidPassSurface = true; session->SurfaceElement = tileElement; @@ -951,19 +952,20 @@ void surface_paint(paint_session* session, uint8_t direction, uint16_t height, c continue; } - TileElement* surfaceElement = map_get_surface_element_at(position); + auto surfaceElement = map_get_surface_element_at(position); if (surfaceElement == nullptr) { continue; } - const uint32_t surfaceSlope = viewport_surface_paint_setup_get_relative_slope(surfaceElement, rotation); + const uint32_t surfaceSlope = viewport_surface_paint_setup_get_relative_slope( + reinterpret_cast(surfaceElement), rotation); const uint8_t baseHeight = surfaceElement->base_height / 2; const corner_height& ch = corner_heights[surfaceSlope]; descriptor.tile_coords = { position.x / 32, position.y / 32 }; - descriptor.tile_element = surfaceElement; - descriptor.terrain = surfaceElement->AsSurface()->GetSurfaceStyle(); + descriptor.tile_element = reinterpret_cast(surfaceElement); + descriptor.terrain = surfaceElement->GetSurfaceStyle(); descriptor.slope = surfaceSlope; descriptor.corner_heights.top = baseHeight + ch.top; descriptor.corner_heights.right = baseHeight + ch.right; @@ -976,7 +978,7 @@ void surface_paint(paint_session* session, uint8_t direction, uint16_t height, c const int16_t x = session->MapPosition.x; const int16_t y = session->MapPosition.y; - int32_t dx = tile_element_height(x + 16, y + 16) & 0xFFFF; + int32_t dx = tile_element_height({ x + 16, y + 16 }); dx += 3; int32_t image_id = (SPR_HEIGHT_MARKER_BASE + dx / 16) | 0x20780000; @@ -1087,7 +1089,7 @@ void surface_paint(paint_session* session, uint8_t direction, uint16_t height, c else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_AVAILABLE) { const LocationXY16& pos = session->MapPosition; - const int32_t height2 = (tile_element_height(pos.x + 16, pos.y + 16) & 0xFFFF) + 3; + const int32_t height2 = (tile_element_height({ pos.x + 16, pos.y + 16 })) + 3; paint_struct* backup = session->LastRootPS; sub_98196C(session, SPR_LAND_OWNERSHIP_AVAILABLE, 16, 16, 1, 1, 0, height2); session->LastRootPS = backup; @@ -1104,7 +1106,7 @@ void surface_paint(paint_session* session, uint8_t direction, uint16_t height, c else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) { const LocationXY16& pos = session->MapPosition; - const int32_t height2 = tile_element_height(pos.x + 16, pos.y + 16) & 0xFFFF; + const int32_t height2 = tile_element_height({ pos.x + 16, pos.y + 16 }); paint_struct* backup = session->LastRootPS; sub_98196C(session, SPR_LAND_CONSTRUCTION_RIGHTS_AVAILABLE, 16, 16, 1, 1, 0, height2 + 3); session->LastRootPS = backup; @@ -1193,9 +1195,9 @@ void surface_paint(paint_session* session, uint8_t direction, uint16_t height, c { const LocationXY16& pos = session->MapPosition; - for (const LocationXY16* tile = gMapSelectionTiles; tile->x != -1; tile++) + for (const auto& tile : gMapSelectionTiles) { - if (tile->x != pos.x || tile->y != pos.y) + if (tile.x != pos.x || tile.y != pos.y) { continue; } diff --git a/src/openrct2/paint/tile_element/Paint.Surface.h b/src/openrct2/paint/tile_element/Paint.Surface.h index bb78caba65..84332c0cd8 100644 --- a/src/openrct2/paint/tile_element/Paint.Surface.h +++ b/src/openrct2/paint/tile_element/Paint.Surface.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -100,25 +100,6 @@ enum SPR_TERRAIN_PATTERN_MARTIAN = 28995, SPR_TERRAIN_PATTERN_GRASS_CLUMPS = 29001, SPR_TERRAIN_PATTERN_ICE = 29007, - - SPR_CSG_TERRAIN_GRASS = SPR_CSG_BEGIN + 41862, - SPR_CSG_TERRAIN_GRASS_GRID = SPR_CSG_TERRAIN_GRASS + 19, - SPR_CSG_TERRAIN_GRASS_UNDERGROUND = SPR_CSG_TERRAIN_GRASS + 38, - SPR_CSG_TERRAIN_SAND = SPR_CSG_BEGIN + 41919, - SPR_CSG_TERRAIN_ICE = SPR_CSG_BEGIN + 41976, - SPR_CSG_TERRAIN_GRASS_CLUMPS = SPR_CSG_BEGIN + 42033, - SPR_CSG_TERRAIN_ROOF_TILES_RED = SPR_CSG_BEGIN + 42090, - SPR_CSG_TERRAIN_ROOF_TILES_GREY = SPR_CSG_BEGIN + 42147, - SPR_CSG_TERRAIN_GRID = SPR_CSG_BEGIN + 42204, - SPR_CSG_TERRAIN_WOOD = SPR_CSG_BEGIN + 42261, - SPR_CSG_TERRAIN_WOOD_INVERTED = SPR_CSG_BEGIN + 42318, - SPR_CSG_TERRAIN_RUST = SPR_CSG_BEGIN + 42375, - SPR_CSG_TERRAIN_RUST_INVERTED = SPR_CSG_BEGIN + 42432, - SPR_CSG_TERRAIN_MARTIAN = SPR_CSG_BEGIN + 42489, - SPR_CSG_TERRAIN_CHECKERBOARD = SPR_CSG_BEGIN + 42546, - SPR_CSG_TERRAIN_CHECKERBOARD_INVERTED = SPR_CSG_BEGIN + 42603, - SPR_CSG_TERRAIN_DIRT = SPR_CSG_BEGIN + 42660, - SPR_CSG_TERRAIN_ROCK = SPR_CSG_BEGIN + 42717, }; #endif //_PAINT_SURFACE_H diff --git a/src/openrct2/paint/tile_element/Paint.TileElement.cpp b/src/openrct2/paint/tile_element/Paint.TileElement.cpp index a9b87f8cf5..682172b474 100644 --- a/src/openrct2/paint/tile_element/Paint.TileElement.cpp +++ b/src/openrct2/paint/tile_element/Paint.TileElement.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -57,7 +57,7 @@ void tile_element_paint_setup(paint_session* session, int32_t x, int32_t y) sub_68B3FB(session, x, y); } - else + else if (!(session->ViewFlags & VIEWPORT_FLAG_TRANSPARENT_BACKGROUND)) { blank_tiles_paint(session, x, y); } @@ -78,7 +78,7 @@ void sub_68B2B7(paint_session* session, int32_t x, int32_t y) sub_68B3FB(session, x, y); } - else + else if (!(session->ViewFlags & VIEWPORT_FLAG_TRANSPARENT_BACKGROUND)) { blank_tiles_paint(session, x, y); } @@ -114,7 +114,7 @@ static void blank_tiles_paint(paint_session* session, int32_t x, int32_t y) dx -= 16; int32_t bx = dx + 32; - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (bx <= dpi->y) return; dx -= 20; @@ -125,7 +125,7 @@ static void blank_tiles_paint(paint_session* session, int32_t x, int32_t y) session->SpritePosition.x = x; session->SpritePosition.y = y; session->InteractionType = VIEWPORT_INTERACTION_ITEM_NONE; - sub_98196C(session, 3123, 0, 0, 32, 32, -1, 16); + sub_98196C(session, SPR_BLANK_TILE, 0, 0, 32, 32, -1, 16); } bool gShowSupportSegmentHeights = false; @@ -136,7 +136,7 @@ bool gShowSupportSegmentHeights = false; */ static void sub_68B3FB(paint_session* session, int32_t x, int32_t y) { - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if ((session->ViewFlags & VIEWPORT_FLAG_CLIP_VIEW)) { @@ -155,6 +155,8 @@ static void sub_68B3FB(paint_session* session, int32_t x, int32_t y) session->MapPosition.y = y; TileElement* tile_element = map_get_first_element_at(x >> 5, y >> 5); + if (tile_element == nullptr) + return; uint8_t rotation = session->CurrentRotation; bool partOfVirtualFloor = false; @@ -248,7 +250,7 @@ static void sub_68B3FB(paint_session* session, int32_t x, int32_t y) if ((session->ViewFlags & VIEWPORT_FLAG_CLIP_VIEW) && (tile_element->base_height > gClipHeight)) continue; - int32_t direction = tile_element->GetDirectionWithOffset(rotation); + Direction direction = tile_element->GetDirectionWithOffset(rotation); int32_t height = tile_element->base_height * 8; // If we are on a new height level, look through elements on the diff --git a/src/openrct2/paint/tile_element/Paint.TileElement.h b/src/openrct2/paint/tile_element/Paint.TileElement.h index ac6a3baf55..378189e7f7 100644 --- a/src/openrct2/paint/tile_element/Paint.TileElement.h +++ b/src/openrct2/paint/tile_element/Paint.TileElement.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/paint/tile_element/Paint.Wall.cpp b/src/openrct2/paint/tile_element/Paint.Wall.cpp index aa63cfd1f0..15fe860495 100644 --- a/src/openrct2/paint/tile_element/Paint.Wall.cpp +++ b/src/openrct2/paint/tile_element/Paint.Wall.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -187,7 +187,7 @@ void fence_paint(paint_session* session, uint8_t direction, int32_t height, cons imageColourFlags &= 0x0DFFFFFFF; } - paint_util_set_general_support_height(session, height, 0x20); + paint_util_set_general_support_height(session, 8 * tile_element->clearance_height, 0x20); uint32_t dword_141F710 = 0; if (gTrackDesignSaveMode || (session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES)) @@ -411,53 +411,42 @@ void fence_paint(paint_session* session, uint8_t direction, int32_t height, cons return; } - set_format_arg(0, uint32_t, 0); - set_format_arg(4, uint32_t, 0); - - uint8_t secondaryColour = tile_element->AsWall()->GetSecondaryColour(); - + auto secondaryColour = tile_element->AsWall()->GetSecondaryColour(); if (dword_141F710 != 0) { secondaryColour = COLOUR_GREY; } - if (direction == 0) { - secondaryColour |= 0x80; - } - - set_format_arg(7, uint8_t, secondaryColour); - - uint16_t scrollingMode = sceneryEntry->wall.scrolling_mode + ((direction + 1) & 0x3); - - uint8_t bannerIndex = tile_element->AsWall()->GetBannerIndex(); - rct_banner* banner = &gBanners[bannerIndex]; - - set_format_arg(0, rct_string_id, banner->string_idx); - if (banner->flags & BANNER_FLAG_LINKED_TO_RIDE) - { - Ride* ride = get_ride(banner->ride_index); - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); - } - - utf8 signString[256]; - rct_string_id stringId = STR_SCROLLING_SIGN_TEXT; - if (gConfigGeneral.upper_case_banners) - { - format_string_to_upper(signString, sizeof(signString), stringId, gCommonFormatArgs); + secondaryColour = ColourMapA[secondaryColour].light; } else { - format_string(signString, sizeof(signString), stringId, gCommonFormatArgs); + secondaryColour = ColourMapA[secondaryColour].mid_dark; } - gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; + uint16_t scrollingMode = sceneryEntry->wall.scrolling_mode + ((direction + 1) & 0x3); + auto banner = tile_element->AsWall()->GetBanner(); + if (banner != nullptr && !banner->IsNull()) + { + banner->FormatTextTo(gCommonFormatArgs); + utf8 signString[256]; + if (gConfigGeneral.upper_case_banners) + { + format_string_to_upper(signString, sizeof(signString), STR_SCROLLING_SIGN_TEXT, gCommonFormatArgs); + } + else + { + format_string(signString, sizeof(signString), STR_SCROLLING_SIGN_TEXT, gCommonFormatArgs); + } - uint16_t string_width = gfx_get_string_width(signString); - uint16_t scroll = (gCurrentTicks / 2) % string_width; + gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; - sub_98199C( - session, scrolling_text_setup(session, stringId, scroll, scrollingMode), 0, 0, 1, 1, 13, height + 8, boundsOffset.x, - boundsOffset.y, boundsOffset.z); + uint16_t string_width = gfx_get_string_width(signString); + uint16_t scroll = (gCurrentTicks / 2) % string_width; + + sub_98199C( + session, scrolling_text_setup(session, STR_SCROLLING_SIGN_TEXT, scroll, scrollingMode, secondaryColour), 0, 0, 1, 1, + 13, height + 8, boundsOffset.x, boundsOffset.y, boundsOffset.z); + } } diff --git a/src/openrct2/peep/Guest.cpp b/src/openrct2/peep/Guest.cpp index 597711ab71..8402351004 100644 --- a/src/openrct2/peep/Guest.cpp +++ b/src/openrct2/peep/Guest.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,7 @@ #include "../audio/audio.h" #include "../config/Config.h" #include "../core/Guard.hpp" +#include "../interface/Window_internal.h" #include "../localisation/Localisation.h" #include "../management/Finance.h" #include "../management/Marketing.h" @@ -110,7 +111,7 @@ static constexpr const CoordsXY SpiralSlideWalkingPath[64] = { }; /** rct2: 0x00981F4C, 0x00981F4E */ -static constexpr const LocationXY16 _WatchingPositionOffsets[] = { +static constexpr const CoordsXY _WatchingPositionOffsets[] = { { 7, 5 }, { 5, 25 }, { 25, 5 }, @@ -343,6 +344,33 @@ static constexpr const uint8_t peep_extra_item_containers[] = { 0xFF, // PEEP_ITEM_EMPTY_BOWL_BLUE }; +static constexpr const char *gPeepEasterEggNames[] = { + "MICHAEL SCHUMACHER", + "JACQUES VILLENEUVE", + "DAMON HILL", + "MR BEAN", + "CHRIS SAWYER", + "KATIE BRAYSHAW", + "MELANIE WARN", + "SIMON FOSTER", + "JOHN WARDLEY", + "LISA STIRLING", + "DONALD MACRAE", + "KATHERINE MCGOWAN", + "FRANCES MCGOWAN", + "CORINA MASSOURA", + "CAROL YOUNG", + "MIA SHERIDAN", + "KATIE RODGER", + "EMMA GARRELL", + "JOANNE BARTON", + "FELICITY ANDERSON", + "KATIE SMITH", + "EILIDH BELL", + "NANCY STILLWAGON", + "DAVID ELLIS" +}; + // These arrays contain the base minimum and maximum nausea ratings for peeps, based on their nausea tolerance level. static constexpr const ride_rating NauseaMinimumThresholds[] = { 0, 0, 200, 400 @@ -369,6 +397,298 @@ static void peep_head_for_nearest_ride_type(Guest* peep, int32_t rideType); static void peep_head_for_nearest_ride_with_flags(Guest* peep, int32_t rideTypeFlags); bool loc_690FD0(Peep* peep, uint8_t* rideToView, uint8_t* rideSeatToView, TileElement* tileElement); +bool Guest::GuestHasValidXY() const +{ + if (x != LOCATION_NULL) + { + if (x < (256 * 32) && y < (256 * 32)) + { + return true; + } + } + + return false; +} + +void Guest::ApplyEasterEggToNearbyGuests(easter_egg_function easter_egg) +{ + if (!GuestHasValidXY()) + return; + + uint16_t spriteIndex = sprite_get_first_in_quadrant(x, y); + if (spriteIndex == SPRITE_INDEX_NULL) + return; + + auto otherPeep = GET_PEEP(spriteIndex); + for (; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = otherPeep->next_in_quadrant) + { + otherPeep = GET_PEEP(spriteIndex); + auto otherGuest = otherPeep->AsGuest(); + if (otherGuest) + { + auto zDiff = std::abs(otherPeep->z - z); + if (zDiff <= 32) + { + (*this.*easter_egg)(otherGuest); + } + } + } +} + +void Guest::GivePassingPeepsPurpleClothes(Guest* passingPeep) +{ + passingPeep->tshirt_colour = COLOUR_BRIGHT_PURPLE; + passingPeep->trousers_colour = COLOUR_BRIGHT_PURPLE; + passingPeep->Invalidate(); +} + +void Guest::GivePassingPeepsPizza(Guest* passingPeep) +{ + if ((passingPeep->item_standard_flags & PEEP_ITEM_PIZZA)) + return; + + passingPeep->item_standard_flags |= PEEP_ITEM_PIZZA; + + int32_t peepDirection = (sprite_direction >> 3) ^ 2; + int32_t otherPeepOppositeDirection = passingPeep->sprite_direction >> 3; + if (peepDirection == otherPeepOppositeDirection) + { + if (passingPeep->action == PEEP_ACTION_NONE_1 || passingPeep->action == PEEP_ACTION_NONE_2) + { + passingPeep->action = PEEP_ACTION_WAVE_2; + passingPeep->action_frame = 0; + passingPeep->action_sprite_image_offset = 0; + passingPeep->UpdateCurrentActionSpriteType(); + } + } +} + +void Guest::MakePassingPeepsSick(Guest* passingPeep) +{ + if (this == passingPeep) + return; + if (passingPeep->state != PEEP_STATE_WALKING) + return; + + if (passingPeep->action == PEEP_ACTION_NONE_1 || passingPeep->action == PEEP_ACTION_NONE_2) + { + passingPeep->action = PEEP_ACTION_THROW_UP; + passingPeep->action_frame = 0; + passingPeep->action_sprite_image_offset = 0; + passingPeep->UpdateCurrentActionSpriteType(); + } +} + +void Guest::GivePassingPeepsIceCream(Guest* passingPeep) +{ + if (this == passingPeep) + return; + if (passingPeep->item_standard_flags & PEEP_ITEM_ICE_CREAM) + return; + + passingPeep->item_standard_flags |= PEEP_ITEM_ICE_CREAM; + passingPeep->UpdateSpriteType(); +} + +/** + * + * rct2: 0x0068FD3A + */ +void Guest::UpdateEasterEggInteractions() +{ + if (peep_flags & PEEP_FLAGS_PURPLE) + { + ApplyEasterEggToNearbyGuests(&Guest::GivePassingPeepsPurpleClothes); + } + + if (peep_flags & PEEP_FLAGS_PIZZA) + { + ApplyEasterEggToNearbyGuests(&Guest::GivePassingPeepsPizza); + } + + if (peep_flags & PEEP_FLAGS_CONTAGIOUS) + { + ApplyEasterEggToNearbyGuests(&Guest::MakePassingPeepsSick); + } + + if (peep_flags & PEEP_FLAGS_JOY) + { + if (scenario_rand() <= 1456) + { + if (action == PEEP_ACTION_NONE_1 || action == PEEP_ACTION_NONE_2) + { + action = PEEP_ACTION_JOY; + action_frame = 0; + action_sprite_image_offset = 0; + UpdateCurrentActionSpriteType(); + } + } + } + + if (peep_flags & PEEP_FLAGS_ICE_CREAM) + { + ApplyEasterEggToNearbyGuests(&Guest::GivePassingPeepsIceCream); + } +} + +int32_t Guest::GetEasterEggNameId() const +{ + uint8_t args[32]{}; + char buffer[256]{}; + + FormatNameTo(args); + format_string(buffer, sizeof(buffer), STR_STRINGID, args); + + for (uint32_t i = 0; i < std::size(gPeepEasterEggNames); i++) + if (_stricmp(buffer, gPeepEasterEggNames[i]) == 0) + return static_cast(i); + + return -1; +} + +void Guest::HandleEasterEggName() +{ + peep_flags &= ~PEEP_FLAGS_WAVING; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_KATIE_BRAYSHAW)) + { + peep_flags |= PEEP_FLAGS_WAVING; + } + + peep_flags &= ~PEEP_FLAGS_PHOTO; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_CHRIS_SAWYER)) + { + peep_flags |= PEEP_FLAGS_PHOTO; + } + + peep_flags &= ~PEEP_FLAGS_PAINTING; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_SIMON_FOSTER)) + { + peep_flags |= PEEP_FLAGS_PAINTING; + } + + peep_flags &= ~PEEP_FLAGS_WOW; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_JOHN_WARDLEY)) + { + peep_flags |= PEEP_FLAGS_WOW; + } + + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_MELANIE_WARN)) + { + happiness = 250; + happiness_target = 250; + energy = 127; + energy_target = 127; + nausea = 0; + nausea_target = 0; + } + + peep_flags &= ~PEEP_FLAGS_LITTER; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_LISA_STIRLING)) + { + peep_flags |= PEEP_FLAGS_LITTER; + } + + peep_flags &= ~PEEP_FLAGS_LOST; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_DONALD_MACRAE)) + { + peep_flags |= PEEP_FLAGS_LOST; + } + + peep_flags &= ~PEEP_FLAGS_HUNGER; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_KATHERINE_MCGOWAN)) + { + peep_flags |= PEEP_FLAGS_HUNGER; + } + + peep_flags &= ~PEEP_FLAGS_BATHROOM; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_FRANCES_MCGOWAN)) + { + peep_flags |= PEEP_FLAGS_BATHROOM; + } + + peep_flags &= ~PEEP_FLAGS_CROWDED; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_CORINA_MASSOURA)) + { + peep_flags |= PEEP_FLAGS_CROWDED; + } + + peep_flags &= ~PEEP_FLAGS_HAPPINESS; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_CAROL_YOUNG)) + { + peep_flags |= PEEP_FLAGS_HAPPINESS; + } + + peep_flags &= ~PEEP_FLAGS_NAUSEA; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_MIA_SHERIDAN)) + { + peep_flags |= PEEP_FLAGS_NAUSEA; + } + + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_KATIE_RODGER)) + { + peep_flags |= PEEP_FLAGS_LEAVING_PARK; + peep_flags &= ~PEEP_FLAGS_PARK_ENTRANCE_CHOSEN; + } + + peep_flags &= ~PEEP_FLAGS_PURPLE; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_EMMA_GARRELL)) + { + peep_flags |= PEEP_FLAGS_PURPLE; + } + + peep_flags &= ~PEEP_FLAGS_PIZZA; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_JOANNE_BARTON)) + { + peep_flags |= PEEP_FLAGS_PIZZA; + } + + peep_flags &= ~PEEP_FLAGS_CONTAGIOUS; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_FELICITY_ANDERSON)) + { + peep_flags |= PEEP_FLAGS_CONTAGIOUS; + } + + peep_flags &= ~PEEP_FLAGS_JOY; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_KATIE_SMITH)) + { + peep_flags |= PEEP_FLAGS_JOY; + } + + peep_flags &= ~PEEP_FLAGS_ANGRY; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_EILIDH_BELL)) + { + peep_flags |= PEEP_FLAGS_ANGRY; + } + + peep_flags &= ~PEEP_FLAGS_ICE_CREAM; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_NANCY_STILLWAGON)) + { + peep_flags |= PEEP_FLAGS_ICE_CREAM; + } + + peep_flags &= ~PEEP_FLAGS_HERE_WE_ARE; + if (CheckEasterEggName(EASTEREGG_PEEP_NAME_DAVID_ELLIS)) + { + peep_flags |= PEEP_FLAGS_HERE_WE_ARE; + } +} + +/** + * + * rct2: 0x0069A5A0 + * tests if a peep's name matches a cheat code, normally returns using a register flag + */ +int32_t Guest::CheckEasterEggName(int32_t index) const +{ + uint8_t args[32]{}; + char buffer[256]{}; + + FormatNameTo(args); + format_string(buffer, sizeof(buffer), STR_STRINGID, args); + + return _stricmp(buffer, gPeepEasterEggNames[index]) == 0; +} + void Guest::Tick128UpdateGuest(int32_t index) { if ((uint32_t)(index & 0x1FF) == (gCurrentTicks & 0x1FF)) @@ -382,7 +702,7 @@ void Guest::Tick128UpdateGuest(int32_t index) PeepThoughtType thought_type = crowded_thoughts[scenario_rand() & 0xF]; if (thought_type != PEEP_THOUGHT_TYPE_NONE) { - peep_insert_new_thought(this, thought_type, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(thought_type, PEEP_THOUGHT_ITEM_NONE); } } @@ -390,7 +710,7 @@ void Guest::Tick128UpdateGuest(int32_t index) { if (state == PEEP_STATE_WALKING || state == PEEP_STATE_SITTING) { - audio_play_sound_at_location(SOUND_CRASH, x, y, z); + audio_play_sound_at_location(SoundId::Crash, { x, y, z }); sprite_misc_explosion_cloud_create(x, y, z + 16); sprite_misc_explosion_flare_create(x, y, z + 16); @@ -443,7 +763,7 @@ void Guest::Tick128UpdateGuest(int32_t index) if (thought_type != PEEP_THOUGHT_TYPE_NONE) { - peep_insert_new_thought(this, thought_type, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(thought_type, PEEP_THOUGHT_ITEM_NONE); happiness_target = std::min(PEEP_MAX_HAPPINESS, happiness_target + 45); } } @@ -458,7 +778,7 @@ void Guest::Tick128UpdateGuest(int32_t index) if (peep_flags & PEEP_FLAGS_WOW) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_WOW2, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_WOW2, PEEP_THOUGHT_ITEM_NONE); } if (time_on_ride > 15) @@ -467,13 +787,15 @@ void Guest::Tick128UpdateGuest(int32_t index) if (time_on_ride > 22) { - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride != nullptr) + { + PeepThoughtType thought_type = ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE) + ? PEEP_THOUGHT_TYPE_GET_OUT + : PEEP_THOUGHT_TYPE_GET_OFF; - PeepThoughtType thought_type = ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE) - ? PEEP_THOUGHT_TYPE_GET_OUT - : PEEP_THOUGHT_TYPE_GET_OFF; - - peep_insert_new_thought(this, thought_type, current_ride); + InsertNewThought(thought_type, current_ride); + } } } } @@ -560,7 +882,7 @@ void Guest::Tick128UpdateGuest(int32_t index) { PeepThoughtType chosen_thought = possible_thoughts[scenario_rand() % num_thoughts]; - peep_insert_new_thought(this, chosen_thought, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(chosen_thought, PEEP_THOUGHT_ITEM_NONE); switch (chosen_thought) { @@ -596,7 +918,7 @@ void Guest::Tick128UpdateGuest(int32_t index) thought_type = PEEP_THOUGHT_TYPE_VERY_SICK; peep_head_for_nearest_ride_type(this, RIDE_TYPE_FIRST_AID); } - peep_insert_new_thought(this, thought_type, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(thought_type, PEEP_THOUGHT_ITEM_NONE); } } @@ -637,6 +959,8 @@ void Guest::Tick128UpdateGuest(int32_t index) bool found = false; do { + if (tileElement == nullptr) + break; if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) continue; if (tileElement->base_height != next_z) @@ -726,7 +1050,6 @@ void Guest::Tick128UpdateGuest(int32_t index) action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite*)this); } } } @@ -909,11 +1232,9 @@ void Guest::UpdateSitting() z, }; - Invalidate(); MoveTo(loc.x, loc.y, loc.z); sprite_direction = ((var_37 + 2) & 3) * 8; - Invalidate(); action = PEEP_ACTION_NONE_1; next_action_sprite_type = PEEP_ACTION_SPRITE_TYPE_SITTING_IDLE; SwitchNextActionSpriteType(); @@ -965,7 +1286,6 @@ void Guest::UpdateSitting() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); return; } @@ -994,7 +1314,6 @@ void Guest::UpdateSitting() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); return; } } @@ -1075,7 +1394,7 @@ void Guest::CheckIfLost() { if (!(peep_flags & PEEP_FLAGS_LOST)) { - if (gRideCount < 2) + if (ride_get_count() < 2) return; peep_flags ^= PEEP_FLAGS_21; @@ -1087,7 +1406,7 @@ void Guest::CheckIfLost() return; time_lost = 230; } - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_LOST, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_LOST, PEEP_THOUGHT_ITEM_NONE); happiness_target = std::max(happiness_target - 30, 0); } @@ -1105,7 +1424,7 @@ void Guest::CheckCantFindRide() // Peeps will think "I can't find ride X" twice before giving up completely. if (peep_is_lost_countdown == 30 || peep_is_lost_countdown == 60) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_CANT_FIND, guest_heading_to_ride_id); + InsertNewThought(PEEP_THOUGHT_TYPE_CANT_FIND, guest_heading_to_ride_id); happiness_target = std::max(happiness_target - 30, 0); } @@ -1137,7 +1456,7 @@ void Guest::CheckCantFindExit() // Peeps who can't find the park exit will continue to get less happy until they find it. if (peep_is_lost_countdown == 1) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_CANT_FIND_EXIT, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_CANT_FIND_EXIT, PEEP_THOUGHT_ITEM_NONE); happiness_target = std::max(happiness_target - 30, 0); } @@ -1172,7 +1491,7 @@ bool Guest::DecideAndBuyItem(Ride* ride, int32_t shopItem, money32 price) if (HasItem(shopItem)) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_ALREADY_GOT, shopItem); + InsertNewThought(PEEP_THOUGHT_TYPE_ALREADY_GOT, shopItem); return false; } @@ -1181,12 +1500,12 @@ bool Guest::DecideAndBuyItem(Ride* ride, int32_t shopItem, money32 price) int32_t food = -1; if ((food = HasFoodStandardFlag()) != 0) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_HAVENT_FINISHED, bitscanforward(food)); + InsertNewThought(PEEP_THOUGHT_TYPE_HAVENT_FINISHED, bitscanforward(food)); return false; } else if ((food = HasFoodExtraFlag()) != 0) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_HAVENT_FINISHED, bitscanforward(food) + 32); + InsertNewThought(PEEP_THOUGHT_TYPE_HAVENT_FINISHED, bitscanforward(food) + 32); return false; } else if (nausea >= 145) @@ -1208,13 +1527,13 @@ bool Guest::DecideAndBuyItem(Ride* ride, int32_t shopItem, money32 price) if (shop_item_is_food(shopItem) && (hunger > 75)) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_NOT_HUNGRY, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_NOT_HUNGRY, PEEP_THOUGHT_ITEM_NONE); return false; } if (shop_item_is_drink(shopItem) && (thirst > 75)) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_NOT_THIRSTY, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_NOT_THIRSTY, PEEP_THOUGHT_ITEM_NONE); return false; } @@ -1232,26 +1551,26 @@ bool Guest::DecideAndBuyItem(Ride* ride, int32_t shopItem, money32 price) loc_69B119: if (!hasVoucher) { - if (price != 0) + if (price != 0 && !(gParkFlags & PARK_FLAGS_NO_MONEY)) { if (cash_in_pocket == 0) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_SPENT_MONEY, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_SPENT_MONEY, PEEP_THOUGHT_ITEM_NONE); return false; } if (price > cash_in_pocket) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_CANT_AFFORD, shopItem); + InsertNewThought(PEEP_THOUGHT_TYPE_CANT_AFFORD, shopItem); return false; } } if (gClimateCurrent.Temperature >= 21) - itemValue = get_shop_hot_value(shopItem); + itemValue = ShopItems[shopItem].HotValue; else if (gClimateCurrent.Temperature <= 11) - itemValue = get_shop_cold_value(shopItem); + itemValue = ShopItems[shopItem].ColdValue; else - itemValue = get_shop_base_value(shopItem); + itemValue = ShopItems[shopItem].BaseValue; if (itemValue < price) { @@ -1275,7 +1594,7 @@ loc_69B119: PeepThoughtType thought_type = static_cast( (shopItem >= 32 ? (PEEP_THOUGHT_TYPE_PHOTO2_MUCH + (shopItem - 32)) : (PEEP_THOUGHT_TYPE_BALLOON_MUCH + shopItem))); - peep_insert_new_thought(this, thought_type, ride->id); + InsertNewThought(thought_type, ride->id); return false; } } @@ -1292,7 +1611,7 @@ loc_69B119: PeepThoughtType thought_item = static_cast( (shopItem >= 32 ? (PEEP_THOUGHT_TYPE_PHOTO2 + (shopItem - 32)) : (PEEP_THOUGHT_TYPE_BALLOON + shopItem))); - peep_insert_new_thought(this, thought_item, ride->id); + InsertNewThought(thought_item, ride->id); } } @@ -1306,11 +1625,11 @@ loc_69B221: if (!hasVoucher) { if (gClimateCurrent.Temperature >= 21) - itemValue = get_shop_hot_value(shopItem); + itemValue = ShopItems[shopItem].HotValue; else if (gClimateCurrent.Temperature <= 11) - itemValue = get_shop_cold_value(shopItem); + itemValue = ShopItems[shopItem].ColdValue; else - itemValue = get_shop_base_value(shopItem); + itemValue = ShopItems[shopItem].BaseValue; itemValue -= price; uint8_t satisfaction = 0; @@ -1369,9 +1688,8 @@ loc_69B221: UpdateSpriteType(); if (peep_flags & PEEP_FLAGS_TRACKING) { - set_format_arg(0, rct_string_id, name_string_idx); - set_format_arg(2, uint32_t, id); - set_format_arg(6, rct_string_id, ShopItemStringIds[shopItem].indefinite); + auto nameArgLen = FormatNameTo(gCommonFormatArgs); + set_format_arg(nameArgLen, rct_string_id, ShopItems[shopItem].Naming.Indefinite); if (gConfigNotifications.guest_bought_item) { news_item_add_to_queue(2, STR_PEEP_TRACKING_NOTIFICATION_BOUGHT_X, sprite_index); @@ -1403,7 +1721,7 @@ loc_69B221: } if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) - finance_payment(get_shop_item_cost(shopItem), gCommandExpenditureType); + finance_payment(ShopItems[shopItem].Cost, gCommandExpenditureType); // Sets the expenditure type to *_FOODDRINK_SALES or *_SHOP_SALES appropriately. gCommandExpenditureType--; @@ -1416,7 +1734,7 @@ loc_69B221: { SpendMoney(*expend_type, price); } - ride->total_profit += (price - get_shop_item_cost(shopItem)); + ride->total_profit += (price - ShopItems[shopItem].Cost); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; ride->cur_num_customers++; ride->total_customers++; @@ -1432,7 +1750,9 @@ loc_69B221: */ void Guest::OnEnterRide(ride_id_t rideIndex) { - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; // Calculate how satisfying the ride is for the peep. Can range from -140 to +105. int16_t satisfaction = peep_calculate_ride_satisfaction(this, ride); @@ -1464,8 +1784,7 @@ void Guest::OnEnterRide(ride_id_t rideIndex) */ void Guest::OnExitRide(ride_id_t rideIndex) { - Ride* ride = get_ride(rideIndex); - + auto ride = get_ride(rideIndex); if (peep_flags & PEEP_FLAGS_RIDE_SHOULD_BE_MARKED_AS_FAVOURITE) { peep_flags &= ~PEEP_FLAGS_RIDE_SHOULD_BE_MARKED_AS_FAVOURITE; @@ -1480,18 +1799,12 @@ void Guest::OnExitRide(ride_id_t rideIndex) if (peep_flags & PEEP_FLAGS_LEAVING_PARK) peep_flags &= ~(PEEP_FLAGS_PARK_ENTRANCE_CHOSEN); - if (peep_should_go_on_ride_again(this, ride)) + if (ride != nullptr && peep_should_go_on_ride_again(this, ride)) { guest_heading_to_ride_id = rideIndex; peep_is_lost_countdown = 200; peep_reset_pathfind_goal(this); - - rct_window* w = window_find_by_number(WC_PEEP, sprite_index); - if (w != nullptr) - { - window_event_invalidate_call(w); - widget_invalidate(w, WC_PEEP__WIDX_ACTION_LBL); - } + window_invalidate_flags |= PEEP_INVALIDATE_PEEP_ACTION; } if (peep_should_preferred_intensity_increase(this)) @@ -1502,19 +1815,23 @@ void Guest::OnExitRide(ride_id_t rideIndex) } } - if (peep_really_liked_ride(this, ride)) + if (ride != nullptr && peep_really_liked_ride(this, ride)) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_WAS_GREAT, rideIndex); + InsertNewThought(PEEP_THOUGHT_TYPE_WAS_GREAT, rideIndex); - int32_t laugh = scenario_rand() & 7; - if (laugh < 3) + SoundId laughs[3] = { SoundId::Laugh1, SoundId::Laugh2, SoundId::Laugh3 }; + int32_t laughType = scenario_rand() & 7; + if (laughType < 3) { - audio_play_sound_at_location(SOUND_LAUGH_1 + laugh, x, y, z); + audio_play_sound_at_location(laughs[laughType], { x, y, z }); } } - ride->total_customers++; - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER; + if (ride != nullptr) + { + ride->total_customers++; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER; + } } /** @@ -1550,14 +1867,7 @@ void Guest::PickRideToGoOn() guest_heading_to_ride_id = ride->id; peep_is_lost_countdown = 200; peep_reset_pathfind_goal(this); - - // Invalidate windows - auto w = window_find_by_number(WC_PEEP, sprite_index); - if (w != nullptr) - { - window_event_invalidate_call(w); - widget_invalidate(w, WC_PEEP__WIDX_ACTION_LBL); - } + window_invalidate_flags |= PEEP_INVALIDATE_PEEP_ACTION; // Make peep look at their map if they have one if (item_standard_flags & PEEP_ITEM_MAP) @@ -1572,18 +1882,17 @@ Ride* Guest::FindBestRideToGoOn() // Pick the most exciting ride auto rideConsideration = FindRidesToGoOn(); Ride* mostExcitingRide = nullptr; - for (int32_t i = 0; i < MAX_RIDES; i++) + for (auto& ride : GetRideManager()) { - if (rideConsideration[i]) + if (rideConsideration.size() > ride.id && rideConsideration[ride.id]) { - auto ride = get_ride(i); - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_QUEUE_FULL)) + if (!(ride.lifecycle_flags & RIDE_LIFECYCLE_QUEUE_FULL)) { - if (ShouldGoOnRide(ride, 0, false, true) && ride_has_ratings(ride)) + if (ShouldGoOnRide(&ride, 0, false, true) && ride_has_ratings(&ride)) { - if (mostExcitingRide == nullptr || ride->excitement > mostExcitingRide->excitement) + if (mostExcitingRide == nullptr || ride.excitement > mostExcitingRide->excitement) { - mostExcitingRide = ride; + mostExcitingRide = &ride; } } } @@ -1602,13 +1911,11 @@ std::bitset Guest::FindRidesToGoOn() if (item_standard_flags & PEEP_ITEM_MAP) { // Consider rides that peep hasn't been on yet - int32_t i; - Ride* ride; - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (!HasRidden(ride)) + if (!HasRidden(&ride)) { - rideConsideration[i] = true; + rideConsideration[ride.id] = true; } } } @@ -1641,13 +1948,11 @@ std::bitset Guest::FindRidesToGoOn() } // Always take the tall rides into consideration (realistic as you can usually see them from anywhere in the park) - int32_t i; - Ride* ride; - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (ride->highest_drop_height > 66 || ride->excitement >= RIDE_RATING(8, 00)) + if (ride.highest_drop_height > 66 || ride.excitement >= RIDE_RATING(8, 00)) { - rideConsideration[i] = true; + rideConsideration[ride.id] = true; } } } @@ -1749,7 +2054,7 @@ bool Guest::ShouldGoOnRide(Ride* ride, int32_t entranceNum, bool atQueue, bool t } // Basic price checks - if (ridePrice != 0 && !peep_has_voucher_for_free_ride(this, ride)) + if (ridePrice != 0 && !peep_has_voucher_for_free_ride(this, ride) && !(gParkFlags & PARK_FLAGS_NO_MONEY)) { if (ridePrice > cash_in_pocket) { @@ -1757,11 +2062,11 @@ bool Guest::ShouldGoOnRide(Ride* ride, int32_t entranceNum, bool atQueue, bool t { if (cash_in_pocket <= 0) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_SPENT_MONEY, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_SPENT_MONEY, PEEP_THOUGHT_ITEM_NONE); } else { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_CANT_AFFORD_0, ride->id); + InsertNewThought(PEEP_THOUGHT_TYPE_CANT_AFFORD_0, ride->id); } } ChoseNotToGoOnRide(ride, peepAtRide, true); @@ -1774,7 +2079,7 @@ bool Guest::ShouldGoOnRide(Ride* ride, int32_t entranceNum, bool atQueue, bool t { if (peepAtRide) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_NOT_SAFE, ride->id); + InsertNewThought(PEEP_THOUGHT_TYPE_NOT_SAFE, ride->id); if (happiness_target >= 64) { happiness_target -= 8; @@ -1800,11 +2105,11 @@ bool Guest::ShouldGoOnRide(Ride* ride, int32_t entranceNum, bool atQueue, bool t // Peeps won't go on rides that aren't sufficiently undercover while it's raining. // The threshold is fairly low and only requires about 10-15% of the ride to be undercover. - if (climate_is_raining() && (ride->sheltered_eighths >> 5) < 3) + if (climate_is_raining() && (ride->sheltered_eighths) < 3) { if (peepAtRide) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_NOT_WHILE_RAINING, ride->id); + InsertNewThought(PEEP_THOUGHT_TYPE_NOT_WHILE_RAINING, ride->id); if (happiness_target >= 64) { happiness_target -= 8; @@ -1826,7 +2131,7 @@ bool Guest::ShouldGoOnRide(Ride* ride, int32_t entranceNum, bool atQueue, bool t { if (peepAtRide) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_MORE_THRILLING, ride->id); + InsertNewThought(PEEP_THOUGHT_TYPE_MORE_THRILLING, ride->id); if (happiness_target >= 64) { happiness_target -= 8; @@ -1849,7 +2154,7 @@ bool Guest::ShouldGoOnRide(Ride* ride, int32_t entranceNum, bool atQueue, bool t { if (peepAtRide) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_SICKENING, ride->id); + InsertNewThought(PEEP_THOUGHT_TYPE_SICKENING, ride->id); if (happiness_target >= 64) { happiness_target -= 8; @@ -1905,7 +2210,7 @@ bool Guest::ShouldGoOnRide(Ride* ride, int32_t entranceNum, bool atQueue, bool t { if (peepAtRide) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_BAD_VALUE, ride->id); + InsertNewThought(PEEP_THOUGHT_TYPE_BAD_VALUE, ride->id); if (happiness_target >= 60) { happiness_target -= 16; @@ -1923,7 +2228,7 @@ bool Guest::ShouldGoOnRide(Ride* ride, int32_t entranceNum, bool atQueue, bool t { if (!(peep_flags & PEEP_FLAGS_HAS_PAID_FOR_PARK_ENTRY)) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_GOOD_VALUE, ride->id); + InsertNewThought(PEEP_THOUGHT_TYPE_GOOD_VALUE, ride->id); } } } @@ -1972,7 +2277,7 @@ bool Guest::ShouldGoToShop(Ride* ride, bool peepAtShop) { if (peepAtShop) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_NOT_PAYING, ride->id); + InsertNewThought(PEEP_THOUGHT_TYPE_NOT_PAYING, ride->id); if (happiness_target >= 60) { happiness_target -= 16; @@ -2000,11 +2305,11 @@ bool Guest::ShouldGoToShop(Ride* ride, bool peepAtShop) { if (cash_in_pocket <= 0) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_SPENT_MONEY, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_SPENT_MONEY, PEEP_THOUGHT_ITEM_NONE); } else { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_CANT_AFFORD_0, ride->id); + InsertNewThought(PEEP_THOUGHT_TYPE_CANT_AFFORD_0, ride->id); } } ChoseNotToGoOnRide(ride, peepAtShop, true); @@ -2045,7 +2350,6 @@ void Guest::SpendMoney(money16& peep_expend_type, money32 amount) window_invalidate_by_number(WC_PEEP, sprite_index); - gUnk141F568 = gUnk13CA740; finance_payment(-amount, gCommandExpenditureType); if (gConfigGeneral.show_guest_purchases && !(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO)) @@ -2054,20 +2358,20 @@ void Guest::SpendMoney(money16& peep_expend_type, money32 amount) // needing to be synchronised if (network_get_mode() == NETWORK_MODE_NONE && !gOpenRCT2Headless) { - money_effect_create_at(amount, x, y, z, true); + rct_money_effect::CreateAt(amount, x, y, z, true); } } - audio_play_sound_at_location(SOUND_PURCHASE, x, y, z); + audio_play_sound_at_location(SoundId::Purchase, { x, y, z }); } -void Guest::SetHasRidden(Ride* ride) +void Guest::SetHasRidden(const Ride* ride) { rides_been_on[ride->id / 8] |= 1 << (ride->id % 8); SetHasRiddenRideType(ride->type); } -bool Guest::HasRidden(Ride* ride) const +bool Guest::HasRidden(const Ride* ride) const { return rides_been_on[ride->id / 8] & (1 << (ride->id % 8)); } @@ -2104,7 +2408,6 @@ void Guest::ReadMap() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } } @@ -2133,22 +2436,15 @@ static void peep_tried_to_enter_full_queue(Peep* peep, Ride* ride) static void peep_reset_ride_heading(Peep* peep) { - rct_window* w; - peep->guest_heading_to_ride_id = RIDE_ID_NULL; - w = window_find_by_number(WC_PEEP, peep->sprite_index); - if (w != nullptr) - { - window_event_invalidate_call(w); - widget_invalidate(w, WC_PEEP__WIDX_ACTION_LBL); - } + peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_ACTION; } static void peep_ride_is_too_intense(Guest* peep, Ride* ride, bool peepAtRide) { if (peepAtRide) { - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_INTENSE, ride->id); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_INTENSE, ride->id); if (peep->happiness_target >= 64) { peep->happiness_target -= 8; @@ -2347,16 +2643,16 @@ static bool peep_check_ride_price_at_entrance(Guest* peep, Ride* ride, money32 r && peep->voucher_arguments == peep->current_ride) return true; - if (peep->cash_in_pocket <= 0) + if (peep->cash_in_pocket <= 0 && !(gParkFlags & PARK_FLAGS_NO_MONEY)) { - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_SPENT_MONEY, PEEP_THOUGHT_ITEM_NONE); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_SPENT_MONEY, PEEP_THOUGHT_ITEM_NONE); peep_update_ride_at_entrance_try_leave(peep); return false; } if (ridePrice > peep->cash_in_pocket) { - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CANT_AFFORD_0, peep->current_ride); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_CANT_AFFORD_0, peep->current_ride); peep_update_ride_at_entrance_try_leave(peep); return false; } @@ -2366,7 +2662,7 @@ static bool peep_check_ride_price_at_entrance(Guest* peep, Ride* ride, money32 r { if (value * 2 < ridePrice) { - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_BAD_VALUE, peep->current_ride); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_BAD_VALUE, peep->current_ride); peep_update_ride_at_entrance_try_leave(peep); return false; } @@ -2636,7 +2932,7 @@ static bool peep_really_liked_ride(Peep* peep, Ride* ride) */ static PeepThoughtType peep_assess_surroundings(int16_t centre_x, int16_t centre_y, int16_t centre_z) { - if ((tile_element_height(centre_x, centre_y) & 0xFFFF) > centre_z) + if ((tile_element_height({ centre_x, centre_y })) > centre_z) return PEEP_THOUGHT_TYPE_NONE; uint16_t num_scenery = 0; @@ -2654,7 +2950,8 @@ static PeepThoughtType peep_assess_surroundings(int16_t centre_x, int16_t centre for (int16_t y = initial_y; y < final_y; y += 32) { TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); - + if (tileElement == nullptr) + continue; do { Ride* ride; @@ -2691,25 +2988,28 @@ static PeepThoughtType peep_assess_surroundings(int16_t centre_x, int16_t centre break; case TILE_ELEMENT_TYPE_TRACK: ride = get_ride(tileElement->AsTrack()->GetRideIndex()); - if (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC && ride->status != RIDE_STATUS_CLOSED - && !(ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))) + if (ride != nullptr) { - if (ride->type == RIDE_TYPE_MERRY_GO_ROUND) + if (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC && ride->status != RIDE_STATUS_CLOSED + && !(ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))) { - nearby_music |= 1; - break; - } + if (ride->type == RIDE_TYPE_MERRY_GO_ROUND) + { + nearby_music |= 1; + break; + } - if (ride->music == MUSIC_STYLE_ORGAN) - { - nearby_music |= 1; - break; - } + if (ride->music == MUSIC_STYLE_ORGAN) + { + nearby_music |= 1; + break; + } - if (ride->type == RIDE_TYPE_DODGEMS) - { - // Dodgems drown out music? - nearby_music |= 2; + if (ride->type == RIDE_TYPE_DODGEMS) + { + // Dodgems drown out music? + nearby_music |= 2; + } } } break; @@ -2837,7 +3137,7 @@ static void peep_leave_park(Peep* peep) peep->peep_flags &= ~PEEP_FLAGS_PARK_ENTRANCE_CHOSEN; } - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_GO_HOME, PEEP_THOUGHT_ITEM_NONE); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_GO_HOME, PEEP_THOUGHT_ITEM_NONE); rct_window* w = window_find_by_number(WC_PEEP, peep->sprite_index); if (w != nullptr) @@ -2845,14 +3145,8 @@ static void peep_leave_park(Peep* peep) window_invalidate_by_number(WC_PEEP, peep->sprite_index); } -/** - * - * rct2: 0x00695B70 - */ -static void peep_head_for_nearest_ride_type(Guest* peep, int32_t rideType) +template static void peep_head_for_nearest_ride(Guest* peep, bool considerOnlyCloseRides, T predicate) { - Ride* ride; - if (peep->state != PEEP_STATE_SITTING && peep->state != PEEP_STATE_WATCHING && peep->state != PEEP_STATE_WALKING) { return; @@ -2863,238 +3157,119 @@ static void peep_head_for_nearest_ride_type(Guest* peep, int32_t rideType) return; if (peep->guest_heading_to_ride_id != RIDE_ID_NULL) { - ride = get_ride(peep->guest_heading_to_ride_id); - if (ride->type == rideType) + auto ride = get_ride(peep->guest_heading_to_ride_id); + if (ride != nullptr && predicate(*ride)) { return; } } - uint32_t rideConsideration[8]{}; - - // FIX Originally checked for a toy,.likely a mistake and should be a map - if ((peep->item_standard_flags & PEEP_ITEM_MAP) && rideType != RIDE_TYPE_FIRST_AID) + std::bitset rideConsideration; + if (!considerOnlyCloseRides && (peep->item_standard_flags & PEEP_ITEM_MAP)) { // Consider all rides in the park - int32_t i; - FOR_ALL_RIDES (i, ride) + for (const auto& ride : GetRideManager()) { - if (ride->type == rideType) + if (predicate(ride)) { - rideConsideration[i >> 5] |= (1u << (i & 0x1F)); + rideConsideration[ride.id] = true; } } } else { // Take nearby rides into consideration + constexpr auto searchRadius = 10 * 32; int32_t cx = floor2(peep->x, 32); int32_t cy = floor2(peep->y, 32); - for (int32_t x = cx - 320; x <= cx + 320; x += 32) + for (auto x = cx - searchRadius; x <= cx + searchRadius; x += 32) { - for (int32_t y = cy - 320; y <= cy + 320; y += 32) + for (auto y = cy - searchRadius; y <= cy + searchRadius; y += 32) { - if (x >= 0 && y >= 0 && x < (256 * 32) && y < (256 * 32)) + if (map_is_location_valid({ x, y })) { - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); - do + auto tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement != nullptr) { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - - ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - ride = get_ride(rideIndex); - if (ride->type == rideType) + do { - rideConsideration[rideIndex >> 5] |= (1u << (rideIndex & 0x1F)); - } - } while (!(tileElement++)->IsLastForTile()); + if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK) + { + auto rideIndex = tileElement->AsTrack()->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride != nullptr && predicate(*ride)) + { + rideConsideration[rideIndex] = true; + } + } + } while (!(tileElement++)->IsLastForTile()); + } } } } } // Filter the considered rides - uint8_t potentialRides[256]; - uint8_t* nextPotentialRide = &potentialRides[0]; - int32_t numPotentialRides = 0; - for (int32_t i = 0; i < MAX_RIDES; i++) + uint8_t potentialRides[MAX_RIDES]; + size_t numPotentialRides = 0; + for (auto& ride : GetRideManager()) { - if (!(rideConsideration[i >> 5] & (1u << (i & 0x1F)))) - continue; - - ride = get_ride(i); - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_QUEUE_FULL)) + if (rideConsideration[ride.id]) { - if (peep->ShouldGoOnRide(ride, 0, false, true)) + if (!(ride.lifecycle_flags & RIDE_LIFECYCLE_QUEUE_FULL)) { - *nextPotentialRide++ = i; - numPotentialRides++; + if (peep->ShouldGoOnRide(&ride, 0, false, true)) + { + potentialRides[numPotentialRides++] = ride.id; + } } } } // Pick the closest ride - ride_id_t closestRideIndex = RIDE_ID_NULL; - int32_t closestRideDistance = std::numeric_limits::max(); - for (int32_t i = 0; i < numPotentialRides; i++) + Ride* closestRide{}; + auto closestRideDistance = std::numeric_limits::max(); + for (size_t i = 0; i < numPotentialRides; i++) { - ride = get_ride(potentialRides[i]); - int32_t rideX = ride->stations[0].Start.x * 32; - int32_t rideY = ride->stations[0].Start.y * 32; - int32_t distance = abs(rideX - peep->x) + abs(rideY - peep->y); - if (distance < closestRideDistance) + auto ride = get_ride(potentialRides[i]); + if (ride != nullptr) { - closestRideIndex = potentialRides[i]; - closestRideDistance = distance; + int32_t rideX = ride->stations[0].Start.x * 32; + int32_t rideY = ride->stations[0].Start.y * 32; + int32_t distance = abs(rideX - peep->x) + abs(rideY - peep->y); + if (distance < closestRideDistance) + { + closestRide = ride; + closestRideDistance = distance; + } } } - if (closestRideIndex == RIDE_ID_NULL) - return; - - // Head to that ride - peep->guest_heading_to_ride_id = closestRideIndex; - peep->peep_is_lost_countdown = 200; - peep_reset_pathfind_goal(peep); - - // Invalidate windows - rct_window* w = window_find_by_number(WC_PEEP, peep->sprite_index); - if (w != nullptr) + if (closestRide != nullptr) { - window_event_invalidate_call(w); - widget_invalidate(w, WC_PEEP__WIDX_ACTION_LBL); + // Head to that ride + peep->guest_heading_to_ride_id = closestRide->id; + peep->peep_is_lost_countdown = 200; + peep_reset_pathfind_goal(peep); + peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_ACTION; + peep->time_lost = 0; } - - peep->time_lost = 0; } -/** - * - * rct2: 0x006958D0 - */ +static void peep_head_for_nearest_ride_type(Guest* peep, int32_t rideType) +{ + auto considerOnlyCloseRides = rideType == RIDE_TYPE_FIRST_AID; + return peep_head_for_nearest_ride( + peep, considerOnlyCloseRides, [rideType](const Ride& ride) { return ride.type == rideType; }); +} + static void peep_head_for_nearest_ride_with_flags(Guest* peep, int32_t rideTypeFlags) { - Ride* ride; - - if (peep->state != PEEP_STATE_SITTING && peep->state != PEEP_STATE_WATCHING && peep->state != PEEP_STATE_WALKING) - { - return; - } - if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK) - return; - if (peep->x == LOCATION_NULL) - return; - if (peep->guest_heading_to_ride_id != RIDE_ID_NULL) - { - ride = get_ride(peep->guest_heading_to_ride_id); - if (ride_type_has_flag( - ride->type, RIDE_TYPE_FLAG_IS_BATHROOM | RIDE_TYPE_FLAG_SELLS_DRINKS | RIDE_TYPE_FLAG_SELLS_FOOD)) - { - return; - } - } - if ((rideTypeFlags & RIDE_TYPE_FLAG_IS_BATHROOM) && peep->HasFood()) { return; } - - uint32_t rideConsideration[8]{}; - - // FIX Originally checked for a toy,.likely a mistake and should be a map - if (peep->item_standard_flags & PEEP_ITEM_MAP) - { - // Consider all rides in the park - int32_t i; - FOR_ALL_RIDES (i, ride) - { - if (ride_type_has_flag(ride->type, rideTypeFlags)) - { - rideConsideration[i >> 5] |= (1u << (i & 0x1F)); - } - } - } - else - { - // Take nearby rides into consideration - int32_t cx = floor2(peep->x, 32); - int32_t cy = floor2(peep->y, 32); - for (int32_t x = cx - 320; x <= cx + 320; x += 32) - { - for (int32_t y = cy - 320; y <= cy + 320; y += 32) - { - if (x >= 0 && y >= 0 && x < (256 * 32) && y < (256 * 32)) - { - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - - ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - ride = get_ride(rideIndex); - if (ride_type_has_flag(ride->type, rideTypeFlags)) - { - rideConsideration[rideIndex >> 5] |= (1u << (rideIndex & 0x1F)); - } - } while (!(tileElement++)->IsLastForTile()); - } - } - } - } - - // Filter the considered rides - uint8_t potentialRides[256]; - uint8_t* nextPotentialRide = &potentialRides[0]; - int32_t numPotentialRides = 0; - for (int32_t i = 0; i < MAX_RIDES; i++) - { - if (!(rideConsideration[i >> 5] & (1u << (i & 0x1F)))) - continue; - - ride = get_ride(i); - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_QUEUE_FULL)) - { - if (peep->ShouldGoOnRide(ride, 0, false, true)) - { - *nextPotentialRide++ = i; - numPotentialRides++; - } - } - } - - // Pick the closest ride - ride_id_t closestRideIndex = RIDE_ID_NULL; - int32_t closestRideDistance = std::numeric_limits::max(); - for (int32_t i = 0; i < numPotentialRides; i++) - { - ride = get_ride(potentialRides[i]); - int32_t rideX = ride->stations[0].Start.x * 32; - int32_t rideY = ride->stations[0].Start.y * 32; - int32_t distance = abs(rideX - peep->x) + abs(rideY - peep->y); - if (distance < closestRideDistance) - { - closestRideIndex = potentialRides[i]; - closestRideDistance = distance; - } - } - if (closestRideIndex == RIDE_ID_NULL) - return; - - // Head to that ride - peep->guest_heading_to_ride_id = closestRideIndex; - peep->peep_is_lost_countdown = 200; - peep_reset_pathfind_goal(peep); - - // Invalidate windows - rct_window* w = window_find_by_number(WC_PEEP, peep->sprite_index); - if (w != nullptr) - { - window_event_invalidate_call(w); - window_invalidate(w); - } - - peep->time_lost = 0; + peep_head_for_nearest_ride( + peep, false, [rideTypeFlags](const Ride& ride) { return ride_type_has_flag(ride.type, rideTypeFlags); }); } /** @@ -3165,11 +3340,14 @@ static bool peep_should_use_cash_machine(Peep* peep, ride_id_t rideIndex) if (peep->energy < 80) return false; - Ride* ride = get_ride(rideIndex); - ride_update_satisfaction(ride, peep->happiness >> 6); - ride->cur_num_customers++; - ride->total_customers++; - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER; + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + ride_update_satisfaction(ride, peep->happiness >> 6); + ride->cur_num_customers++; + ride->total_customers++; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER; + } return true; } @@ -3182,8 +3360,8 @@ void Guest::UpdateBuying() if (!CheckForPath()) return; - Ride* ride = get_ride(current_ride); - if (ride->type == RIDE_TYPE_NULL || ride->status != RIDE_STATUS_OPEN) + auto ride = get_ride(current_ride); + if (ride == nullptr || ride->status != RIDE_STATUS_OPEN) { SetState(PEEP_STATE_FALLING); return; @@ -3193,10 +3371,8 @@ void Guest::UpdateBuying() { if (action != PEEP_ACTION_NONE_2) { - int16_t actionX; - int16_t actionY; - int16_t xy_distance; - UpdateAction(&actionX, &actionY, &xy_distance); + UpdateAction(); + Invalidate(); return; } @@ -3236,7 +3412,6 @@ void Guest::UpdateBuying() action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); ride->no_primary_items_sold++; } @@ -3291,7 +3466,9 @@ void Guest::UpdateBuying() */ void Guest::UpdateRideAtEntrance() { - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; // The peep will keep advancing in the entranceway // whilst in this state. When it has reached the very @@ -3301,11 +3478,8 @@ void Guest::UpdateRideAtEntrance() // this is the state it will return to. if (destination_tolerance != 0) { - Invalidate(); - - int16_t actionX, actionY, xy_distance; - - if (UpdateAction(&actionX, &actionY, &xy_distance)) + int16_t xy_distance; + if (auto loc = UpdateAction(xy_distance)) { int16_t actionZ = z; if (xy_distance < 16) @@ -3313,13 +3487,13 @@ void Guest::UpdateRideAtEntrance() auto entrance = ride_get_entrance_location(ride, current_ride_station); actionZ = entrance.z * 8 + 2; } - MoveTo(actionX, actionY, actionZ); - Invalidate(); + MoveTo((*loc).x, (*loc).y, actionZ); } else { destination_tolerance = 0; sprite_direction ^= (1 << 4); + Invalidate(); } } @@ -3493,12 +3667,15 @@ static void peep_update_ride_leave_entrance_waypoints(Peep* peep, Ride* ride) */ void Guest::UpdateRideAdvanceThroughEntrance() { - int16_t actionX, actionY, actionZ, xy_distance; + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; - Ride* ride = get_ride(current_ride); - rct_ride_entry* ride_entry = get_ride_entry(ride->subtype); + int16_t actionZ, xy_distance; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + auto ride_entry = ride->GetRideEntry(); + + if (auto loc = UpdateAction(xy_distance)) { uint16_t distanceThreshold = 16; if (ride_entry != nullptr) @@ -3516,8 +3693,6 @@ void Guest::UpdateRideAdvanceThroughEntrance() sub_state = PEEP_RIDE_FREE_VEHICLE_CHECK; } - Invalidate(); - actionZ = ride->stations[current_ride_station].Height * 8; distanceThreshold += 4; @@ -3526,8 +3701,7 @@ void Guest::UpdateRideAdvanceThroughEntrance() actionZ += RideData5[ride->type].z; } - MoveTo(actionX, actionY, actionZ); - Invalidate(); + MoveTo((*loc).x, (*loc).y, actionZ); return; } @@ -3556,8 +3730,7 @@ void Guest::UpdateRideAdvanceThroughEntrance() ride->current_issues |= RIDE_ISSUE_GUESTS_STUCK; ride->last_issue_time = gCurrentTicks; - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs); if (gConfigNotifications.ride_warnings) { news_item_add_to_queue(NEWS_ITEM_RIDE, STR_GUESTS_GETTING_STUCK_ON_RIDE, current_ride); @@ -3637,8 +3810,7 @@ static void peep_go_to_ride_exit(Peep* peep, Ride* ride, int16_t x, int16_t y, i { z += RideData5[ride->type].z; - sprite_move(x, y, z, (rct_sprite*)peep); - peep->Invalidate(); + peep->MoveTo(x, y, z); Guard::Assert(peep->current_ride_station < MAX_STATIONS); auto exit = ride_get_exit_location(ride, peep->current_ride_station); @@ -3718,10 +3890,8 @@ void Guest::UpdateRideFreeVehicleEnterRide(Ride* ride) if (peep_flags & PEEP_FLAGS_TRACKING) { - set_format_arg(0, rct_string_id, name_string_idx); - set_format_arg(2, uint32_t, id); - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + auto nameArgLen = FormatNameTo(gCommonFormatArgs); + ride->FormatNameTo(gCommonFormatArgs + nameArgLen); rct_string_id msg_string; if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE)) @@ -3763,7 +3933,7 @@ static void peep_update_ride_no_free_vehicle_rejoin_queue(Peep* peep, Ride* ride peep->SetState(PEEP_STATE_QUEUING_FRONT); peep->sub_state = PEEP_RIDE_AT_ENTRANCE; - ride_queue_insert_guest_at_front(ride, peep->current_ride_station, peep); + ride->QueueInsertGuestAtFront(peep->current_ride_station, peep); } /** @@ -3777,7 +3947,9 @@ static void peep_update_ride_no_free_vehicle_rejoin_queue(Peep* peep, Ride* ride */ void Guest::UpdateRideFreeVehicleCheck() { - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_VEHICLES)) { @@ -3870,67 +4042,69 @@ void Guest::UpdateRideFreeVehicleCheck() void Guest::UpdateRideApproachVehicle() { - int16_t actionX, actionY, xy_distance; - if (!UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - sub_state = PEEP_RIDE_ENTER_VEHICLE; + MoveTo((*loc).x, (*loc).y, z); return; } - - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + sub_state = PEEP_RIDE_ENTER_VEHICLE; } void Guest::UpdateRideEnterVehicle() { - Ride* ride = get_ride(current_ride); - - rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[current_train]); - for (int32_t i = current_car; i != 0; --i) + auto* ride = get_ride(current_ride); + if (ride != nullptr) { - vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train); + auto* vehicle = GET_VEHICLE(ride->vehicles[current_train]); + if (vehicle != nullptr) + { + for (int32_t i = current_car; i != 0; --i) + { + vehicle = GET_VEHICLE(vehicle->next_vehicle_on_train); + } + + if (ride->mode != RIDE_MODE_FORWARD_ROTATION && ride->mode != RIDE_MODE_BACKWARD_ROTATION) + { + if (current_seat != vehicle->num_peeps) + return; + } + + if (vehicle_is_used_in_pairs(vehicle)) + { + auto* seatedPeep = GET_PEEP(vehicle->peep[current_seat ^ 1]); + if (seatedPeep != nullptr) + { + auto* seatedPeepAsGuest = seatedPeep->AsGuest(); + if (seatedPeepAsGuest == nullptr || seatedPeepAsGuest->sub_state != PEEP_RIDE_ENTER_VEHICLE) + return; + + vehicle->num_peeps++; + ride->cur_num_customers++; + + vehicle->mass += seatedPeepAsGuest->mass; + seatedPeepAsGuest->MoveTo(LOCATION_NULL, 0, 0); + seatedPeepAsGuest->SetState(PEEP_STATE_ON_RIDE); + seatedPeepAsGuest->time_on_ride = 0; + seatedPeepAsGuest->sub_state = PEEP_RIDE_ON_RIDE; + seatedPeepAsGuest->OnEnterRide(current_ride); + } + } + + vehicle->num_peeps++; + ride->cur_num_customers++; + + vehicle->mass += mass; + invalidate_sprite_2(reinterpret_cast(vehicle)); + + MoveTo(LOCATION_NULL, 0, 0); + + SetState(PEEP_STATE_ON_RIDE); + + time_on_ride = 0; + sub_state = PEEP_RIDE_ON_RIDE; + OnEnterRide(current_ride); + } } - - if (ride->mode != RIDE_MODE_FORWARD_ROTATION && ride->mode != RIDE_MODE_BACKWARD_ROTATION) - { - if (current_seat != vehicle->num_peeps) - return; - } - - if (vehicle_is_used_in_pairs(vehicle)) - { - auto seated_peep = (GET_PEEP(vehicle->peep[current_seat ^ 1]))->AsGuest(); - if (seated_peep == nullptr || seated_peep->sub_state != PEEP_RIDE_ENTER_VEHICLE) - return; - - vehicle->num_peeps++; - ride->cur_num_customers++; - - vehicle->mass += seated_peep->mass; - seated_peep->Invalidate(); - sprite_move(LOCATION_NULL, 0, 0, (rct_sprite*)seated_peep); - - seated_peep->SetState(PEEP_STATE_ON_RIDE); - seated_peep->time_on_ride = 0; - seated_peep->sub_state = PEEP_RIDE_ON_RIDE; - seated_peep->OnEnterRide(current_ride); - } - - vehicle->num_peeps++; - ride->cur_num_customers++; - - vehicle->mass += mass; - invalidate_sprite_2((rct_sprite*)vehicle); - - Invalidate(); - MoveTo(LOCATION_NULL, 0, 0); - - SetState(PEEP_STATE_ON_RIDE); - - time_on_ride = 0; - sub_state = PEEP_RIDE_ON_RIDE; - OnEnterRide(current_ride); } /** @@ -3939,7 +4113,9 @@ void Guest::UpdateRideEnterVehicle() */ void Guest::UpdateRideLeaveVehicle() { - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[current_train]); uint8_t ride_station = vehicle->current_station; @@ -4003,6 +4179,8 @@ void Guest::UpdateRideLeaveVehicle() continue; TileElement* inner_map = map_get_first_element_at(vehicle->track_x / 32, vehicle->track_y / 32); + if (inner_map == nullptr) + continue; for (;; inner_map++) { if (inner_map->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -4055,22 +4233,32 @@ void Guest::UpdateRideLeaveVehicle() platformLocation.x = vehicle->x + word_981D6C[platformLocation.direction].x * 12; platformLocation.y = vehicle->y + word_981D6C[platformLocation.direction].y * 12; - int8_t loadPosition = vehicle_entry->peep_loading_positions[current_seat]; - - switch (vehicle->sprite_direction / 8) + // This can evaluate to false with buggy custom rides. + if (current_seat < vehicle_entry->peep_loading_positions.size()) { - case 0: - platformLocation.x -= loadPosition; - break; - case 1: - platformLocation.y += loadPosition; - break; - case 2: - platformLocation.x += loadPosition; - break; - case 3: - platformLocation.y -= loadPosition; - break; + int8_t loadPosition = vehicle_entry->peep_loading_positions[current_seat]; + + switch (vehicle->sprite_direction / 8) + { + case 0: + platformLocation.x -= loadPosition; + break; + case 1: + platformLocation.y += loadPosition; + break; + case 2: + platformLocation.x += loadPosition; + break; + case 3: + platformLocation.y -= loadPosition; + break; + } + } + else + { + log_verbose( + "current_seat %d is too large! (Vehicle entry has room for %d.)", current_seat, + vehicle_entry->peep_loading_positions.size()); } platformLocation.z = ride->stations[current_ride_station].Height * 8; @@ -4116,7 +4304,6 @@ void Guest::UpdateRideLeaveVehicle() exitWaypointLoc.z += 15; MoveTo(exitWaypointLoc.x, exitWaypointLoc.y, exitWaypointLoc.z); - Invalidate(); waypointLoc.x += vehicleEntry->peep_loading_waypoints[var_37 / 4][1].x; waypointLoc.y += vehicleEntry->peep_loading_waypoints[var_37 / 4][1].y; @@ -4133,9 +4320,10 @@ void Guest::UpdateRideLeaveVehicle() */ static void peep_update_ride_prepare_for_exit(Peep* peep) { - Ride* ride = get_ride(peep->current_ride); + auto ride = get_ride(peep->current_ride); + if (ride == nullptr || peep->current_ride_station >= std::size(ride->stations)) + return; - Guard::Assert(peep->current_ride_station < std::size(ride->stations), GUARD_LINE); auto exit = ride_get_exit_location(ride, peep->current_ride_station); int16_t x = exit.x; int16_t y = exit.y; @@ -4179,12 +4367,9 @@ static void peep_update_ride_prepare_for_exit(Peep* peep) */ void Guest::UpdateRideApproachExit() { - int16_t actionX, actionY, xy_distance; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); return; } @@ -4197,26 +4382,25 @@ void Guest::UpdateRideApproachExit() */ void Guest::UpdateRideInExit() { - int16_t actionX, actionY, xy_distance; - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + int16_t xy_distance; + + if (auto loc = UpdateAction(xy_distance)) { - Invalidate(); - if (xy_distance >= 16) { int16_t actionZ = ride->stations[current_ride_station].Height * 8; actionZ += RideData5[ride->type].z; - MoveTo(actionX, actionY, actionZ); - Invalidate(); + MoveTo((*loc).x, (*loc).y, actionZ); return; } SwitchToSpecialSprite(0); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); } if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) @@ -4229,18 +4413,21 @@ void Guest::UpdateRideInExit() } sub_state = PEEP_RIDE_LEAVE_EXIT; } - +#pragma warning(default : 6011) /** * * rct2: 0x006926AD */ void Guest::UpdateRideApproachVehicleWaypoints() { - int16_t actionX, actionY, xy_distance; - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; + + int16_t xy_distance; uint8_t waypoint = var_37 & 3; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction(xy_distance)) { int16_t actionZ; // Motion simulators have steps this moves the peeps up the steps @@ -4264,9 +4451,7 @@ void Guest::UpdateRideApproachVehicleWaypoints() { actionZ = z; } - Invalidate(); - MoveTo(actionX, actionY, actionZ); - Invalidate(); + MoveTo((*loc).x, (*loc).y, actionZ); return; } @@ -4282,13 +4467,14 @@ void Guest::UpdateRideApproachVehicleWaypoints() rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[current_train]); - actionX = ride->stations[current_ride_station].Start.x * 32 + 16; - actionY = ride->stations[current_ride_station].Start.y * 32 + 16; + CoordsXY targetLoc; + targetLoc.x = ride->stations[current_ride_station].Start.x * 32 + 16; + targetLoc.y = ride->stations[current_ride_station].Start.y * 32 + 16; if (ride->type == RIDE_TYPE_ENTERPRISE) { - actionX = vehicle->x; - actionY = vehicle->y; + targetLoc.x = vehicle->x; + targetLoc.y = vehicle->y; } rct_ride_entry* ride_entry = get_ride_entry(vehicle->ride_subtype); @@ -4299,11 +4485,11 @@ void Guest::UpdateRideApproachVehicleWaypoints() rct_ride_entry_vehicle* vehicle_type = &ride_entry->vehicles[vehicle->vehicle_type]; Guard::Assert(waypoint < 3); - actionX += vehicle_type->peep_loading_waypoints[var_37 / 4][waypoint].x; - actionY += vehicle_type->peep_loading_waypoints[var_37 / 4][waypoint].y; + targetLoc.x += vehicle_type->peep_loading_waypoints[var_37 / 4][waypoint].x; + targetLoc.y += vehicle_type->peep_loading_waypoints[var_37 / 4][waypoint].y; - destination_x = actionX; - destination_y = actionY; + destination_x = targetLoc.x; + destination_y = targetLoc.y; } /** @@ -4312,10 +4498,13 @@ void Guest::UpdateRideApproachVehicleWaypoints() */ void Guest::UpdateRideApproachExitWaypoints() { - int16_t actionX, actionY, xy_distance; - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + int16_t xy_distance; + + if (auto loc = UpdateAction(xy_distance)) { int16_t actionZ; if (ride->type == RIDE_TYPE_MOTION_SIMULATOR) @@ -4334,9 +4523,7 @@ void Guest::UpdateRideApproachExitWaypoints() { actionZ = z; } - Invalidate(); - MoveTo(actionX, actionY, actionZ); - Invalidate(); + MoveTo((*loc).x, (*loc).y, actionZ); return; } @@ -4350,40 +4537,34 @@ void Guest::UpdateRideApproachExitWaypoints() var_37--; rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[current_train]); - - actionX = ride->stations[current_ride_station].Start.x * 32 + 16; - actionY = ride->stations[current_ride_station].Start.y * 32 + 16; + CoordsXY targetLoc; + targetLoc.x = ride->stations[current_ride_station].Start.x * 32 + 16; + targetLoc.y = ride->stations[current_ride_station].Start.y * 32 + 16; if (ride->type == RIDE_TYPE_ENTERPRISE) { - actionX = vehicle->x; - actionY = vehicle->y; + targetLoc.x = vehicle->x; + targetLoc.y = vehicle->y; } rct_ride_entry* rideEntry = get_ride_entry(vehicle->ride_subtype); rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; Guard::Assert((var_37 & 3) < 3); - actionX += vehicleEntry->peep_loading_waypoints[var_37 / 4][var_37 & 3].x; - actionY += vehicleEntry->peep_loading_waypoints[var_37 / 4][var_37 & 3].y; + targetLoc.x += vehicleEntry->peep_loading_waypoints[var_37 / 4][var_37 & 3].x; + targetLoc.y += vehicleEntry->peep_loading_waypoints[var_37 / 4][var_37 & 3].y; - destination_x = actionX; - destination_y = actionY; + destination_x = targetLoc.x; + destination_y = targetLoc.y; return; } var_37 |= 3; auto exit = ride_get_exit_location(ride, current_ride_station); - actionX = exit.x; - actionY = exit.y; + CoordsXY targetLoc = { exit.x * 32 + 16, exit.y * 32 + 16 }; uint8_t exit_direction = direction_reverse(exit.direction); - actionX *= 32; - actionY *= 32; - actionX += 16; - actionY += 16; - int16_t x_shift = word_981D6C[exit_direction].x; int16_t y_shift = word_981D6C[exit_direction].y; @@ -4402,11 +4583,11 @@ void Guest::UpdateRideApproachExitWaypoints() x_shift *= shift_multiplier; y_shift *= shift_multiplier; - actionX -= x_shift; - actionY -= y_shift; + targetLoc.x -= x_shift; + targetLoc.y -= y_shift; - destination_x = actionX; - destination_y = actionY; + destination_x = targetLoc.x; + destination_y = targetLoc.y; } /** @@ -4415,14 +4596,13 @@ void Guest::UpdateRideApproachExitWaypoints() */ void Guest::UpdateRideApproachSpiralSlide() { - int16_t actionX, actionY, xy_distance; - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); return; } @@ -4434,7 +4614,7 @@ void Guest::UpdateRideApproachSpiralSlide() destination_x = 0; destination_y = 0; var_37 = (var_37 / 4) & 0xC; - MoveTo(LOCATION_NULL, actionY, z); + MoveTo(LOCATION_NULL, y, z); return; } else if (waypoint == 2) @@ -4455,17 +4635,17 @@ void Guest::UpdateRideApproachSpiralSlide() auto exit = ride_get_exit_location(ride, current_ride_station); waypoint = 1; var_37 = (exit.direction * 4) | (var_37 & 0x30) | waypoint; - actionX = ride->stations[current_ride_station].Start.x * 32; - actionY = ride->stations[current_ride_station].Start.y * 32; + CoordsXY targetLoc; + targetLoc.x = ride->stations[current_ride_station].Start.x * 32; + targetLoc.y = ride->stations[current_ride_station].Start.y * 32; assert(ride->type == RIDE_TYPE_SPIRAL_SLIDE); const CoordsXY slidePlatformDestination = SpiralSlideWalkingPath[var_37]; - actionX += slidePlatformDestination.x; - actionY += slidePlatformDestination.y; + targetLoc += slidePlatformDestination; - destination_x = actionX; - destination_y = actionY; + destination_x = targetLoc.x; + destination_y = targetLoc.y; sub_state = PEEP_RIDE_LEAVE_SPIRAL_SLIDE; return; } @@ -4474,17 +4654,17 @@ void Guest::UpdateRideApproachSpiralSlide() // Actually increment the real peep waypoint var_37++; - actionX = ride->stations[current_ride_station].Start.x * 32; - actionY = ride->stations[current_ride_station].Start.y * 32; + CoordsXY targetLoc; + targetLoc.x = ride->stations[current_ride_station].Start.x * 32; + targetLoc.y = ride->stations[current_ride_station].Start.y * 32; assert(ride->type == RIDE_TYPE_SPIRAL_SLIDE); const CoordsXY slidePlatformDestination = SpiralSlideWalkingPath[var_37]; - actionX += slidePlatformDestination.x; - actionY += slidePlatformDestination.y; + targetLoc += slidePlatformDestination; - destination_x = actionX; - destination_y = actionY; + destination_x = targetLoc.x; + destination_y = targetLoc.y; } /** rct2: 0x00981F0C, 0x00981F0E */ @@ -4509,9 +4689,8 @@ static constexpr const CoordsXY _SpiralSlideEndWaypoint[] = { */ void Guest::UpdateRideOnSpiralSlide() { - Ride* ride = get_ride(current_ride); - - if (ride->type != RIDE_TYPE_SPIRAL_SLIDE) + auto ride = get_ride(current_ride); + if (ride == nullptr || ride->type != RIDE_TYPE_SPIRAL_SLIDE) return; if ((var_37 & 3) == 0) @@ -4552,7 +4731,6 @@ void Guest::UpdateRideOnSpiralSlide() MoveTo(newX, newY, z); sprite_direction = (var_37 & 0xC) * 2; - Invalidate(); var_37++; return; @@ -4562,29 +4740,26 @@ void Guest::UpdateRideOnSpiralSlide() } } - int16_t actionX, actionY, xy_distance; - - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); return; } + uint8_t waypoint = 2; var_37 = (var_37 * 4 & 0x30) + waypoint; - actionX = ride->stations[current_ride_station].Start.x * 32; - actionY = ride->stations[current_ride_station].Start.y * 32; + CoordsXY targetLoc; + targetLoc.x = ride->stations[current_ride_station].Start.x * 32; + targetLoc.y = ride->stations[current_ride_station].Start.y * 32; assert(ride->type == RIDE_TYPE_SPIRAL_SLIDE); const CoordsXY slidePlatformDestination = SpiralSlideWalkingPath[var_37]; - actionX += slidePlatformDestination.x; - actionY += slidePlatformDestination.y; + targetLoc += slidePlatformDestination; - destination_x = actionX; - destination_y = actionY; + destination_x = targetLoc.x; + destination_y = targetLoc.y; sub_state = PEEP_RIDE_APPROACH_SPIRAL_SLIDE; } @@ -4596,17 +4771,15 @@ void Guest::UpdateRideLeaveSpiralSlide() { // Iterates through the spiral slide waypoints until it reaches // waypoint 0. Then it readies to leave the ride by the entrance. - int16_t actionX, actionY, xy_distance; - - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); return; } - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; uint8_t waypoint = var_37 & 3; @@ -4621,17 +4794,17 @@ void Guest::UpdateRideLeaveSpiralSlide() waypoint--; // Actually decrement the peep waypoint var_37--; - actionX = ride->stations[current_ride_station].Start.x * 32; - actionY = ride->stations[current_ride_station].Start.y * 32; + CoordsXY targetLoc; + targetLoc.x = ride->stations[current_ride_station].Start.x * 32; + targetLoc.y = ride->stations[current_ride_station].Start.y * 32; assert(ride->type == RIDE_TYPE_SPIRAL_SLIDE); const CoordsXY slidePlatformDestination = SpiralSlideWalkingPath[var_37]; - actionX += slidePlatformDestination.x; - actionY += slidePlatformDestination.y; + targetLoc += slidePlatformDestination; - destination_x = actionX; - destination_y = actionY; + destination_x = targetLoc.x; + destination_y = targetLoc.y; return; } waypoint = 3; @@ -4639,24 +4812,21 @@ void Guest::UpdateRideLeaveSpiralSlide() var_37 |= 3; auto exit = ride_get_exit_location(ride, current_ride_station); - actionX = exit.x * 32 + 16; - actionY = exit.y * 32 + 16; + CoordsXY targetLoc{ exit.x * 32 + 16, exit.y * 32 + 16 }; - exit.direction = direction_reverse(exit.direction); - - int16_t xShift = word_981D6C[exit.direction].x; - int16_t yShift = word_981D6C[exit.direction].y; + int16_t xShift = word_981D6C[direction_reverse(exit.direction)].x; + int16_t yShift = word_981D6C[direction_reverse(exit.direction)].y; int16_t shiftMultiplier = 20; xShift *= shiftMultiplier; yShift *= shiftMultiplier; - actionX -= xShift; - actionY -= yShift; + targetLoc.x -= xShift; + targetLoc.y -= yShift; - destination_x = actionX; - destination_y = actionY; + destination_x = targetLoc.x; + destination_y = targetLoc.y; } /** rct2: 0x00981FE4 */ @@ -4681,16 +4851,16 @@ static constexpr const uint8_t _MazeCurrentDirectionToOpenHedge[][4] = { */ void Guest::UpdateRideMazePathfinding() { - int16_t actionX, actionY, xy_distance; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); return; } - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; + if (var_37 == 16) { peep_update_ride_prepare_for_exit(this); @@ -4705,29 +4875,20 @@ void Guest::UpdateRideMazePathfinding() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } } - actionX = destination_x & 0xFFE0; - actionY = destination_y & 0xFFE0; + CoordsXY targetLoc = { destination_x & 0xFFE0, destination_y & 0xFFE0 }; + int16_t stationHeight = ride->stations[0].Height; // Find the station track element - TileElement* tileElement = map_get_first_element_at(actionX / 32, actionY / 32); - do - { - if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK && stationHeight == tileElement->base_height) - break; - - } while (!(tileElement++)->IsLastForTile()); - - auto trackMazeEntry = tileElement->AsTrack(); - if (trackMazeEntry == nullptr) + auto trackElement = map_get_track_element_at(targetLoc.x, targetLoc.y, stationHeight); + if (trackElement == nullptr) { return; } - uint16_t mazeEntry = trackMazeEntry->GetMazeEntry(); + uint16_t mazeEntry = trackElement->GetMazeEntry(); uint16_t openHedges = 0; // var_37 is 3, 7, 11 or 15 @@ -4766,11 +4927,11 @@ void Guest::UpdateRideMazePathfinding() chosenEdge = (chosenEdge + 1) & 3; } - actionX = CoordsDirectionDelta[chosenEdge].x / 2; - actionY = CoordsDirectionDelta[chosenEdge].y / 2; + targetLoc.x = CoordsDirectionDelta[chosenEdge].x / 2; + targetLoc.y = CoordsDirectionDelta[chosenEdge].y / 2; - actionX += destination_x; - actionY += destination_y; + targetLoc.x += destination_x; + targetLoc.y += destination_y; enum class maze_type { @@ -4780,7 +4941,9 @@ void Guest::UpdateRideMazePathfinding() }; maze_type mazeType = maze_type::invalid; - tileElement = map_get_first_element_at(actionX / 32, actionY / 32); + auto tileElement = map_get_first_element_at(targetLoc.x / 32, targetLoc.y / 32); + if (tileElement == nullptr) + return; do { if (stationHeight != tileElement->base_height) @@ -4807,37 +4970,35 @@ void Guest::UpdateRideMazePathfinding() maze_last_edge &= 3; return; case maze_type::hedge: - destination_x = actionX; - destination_y = actionY; + destination_x = targetLoc.x; + destination_y = targetLoc.y; var_37 = _MazeGetNewDirectionFromEdge[var_37 / 4][chosenEdge]; maze_last_edge = chosenEdge; break; case maze_type::entrance_or_exit: - actionX = destination_x; - actionY = destination_y; + targetLoc.x = destination_x; + targetLoc.y = destination_y; if (chosenEdge & 1) { - actionX &= 0xFFE0; - actionX += 16; + targetLoc.x &= 0xFFE0; + targetLoc.x += 16; } else { - actionY &= 0xFFE0; - actionY += 16; + targetLoc.y &= 0xFFE0; + targetLoc.y += 16; } - destination_x = actionX; - destination_y = actionY; + destination_x = targetLoc.x; + destination_y = targetLoc.y; var_37 = 16; maze_last_edge = chosenEdge; break; } - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); return; } } @@ -4848,25 +5009,23 @@ void Guest::UpdateRideMazePathfinding() */ void Guest::UpdateRideLeaveExit() { - int16_t actionX, actionY, xy_distance; - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, ride->stations[current_ride_station].Height * 8); - Invalidate(); + if (ride != nullptr) + { + MoveTo((*loc).x, (*loc).y, ride->stations[current_ride_station].Height * 8); + } return; } OnExitRide(current_ride); - if (peep_flags & PEEP_FLAGS_TRACKING) + if (ride != nullptr && (peep_flags & PEEP_FLAGS_TRACKING)) { - set_format_arg(0, rct_string_id, name_string_idx); - set_format_arg(2, uint32_t, id); - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + auto nameArgLen = FormatNameTo(gCommonFormatArgs); + ride->FormatNameTo(gCommonFormatArgs + nameArgLen); if (gConfigNotifications.guest_left_ride) { @@ -4877,18 +5036,19 @@ void Guest::UpdateRideLeaveExit() interaction_ride_index = RIDE_ID_NULL; SetState(PEEP_STATE_FALLING); - actionX = x & 0xFFE0; - actionY = y & 0xFFE0; + CoordsXY targetLoc = { x, y }; // Find the station track element - TileElement* tileElement = map_get_first_element_at(actionX / 32, actionY / 32); + TileElement* tileElement = map_get_first_element_at(targetLoc.x / 32, targetLoc.y / 32); + if (tileElement == nullptr) + return; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) continue; int16_t height = map_height_from_slope( - { x, y }, tileElement->AsPath()->GetSlopeDirection(), tileElement->AsPath()->IsSloped()); + targetLoc, tileElement->AsPath()->GetSlopeDirection(), tileElement->AsPath()->IsSloped()); height += tileElement->base_height * 8; int16_t z_diff = z - height; @@ -4896,7 +5056,6 @@ void Guest::UpdateRideLeaveExit() continue; MoveTo(x, y, height); - Invalidate(); return; } while (!(tileElement++)->IsLastForTile()); } @@ -4907,13 +5066,9 @@ void Guest::UpdateRideLeaveExit() */ void Guest::UpdateRideShopApproach() { - int16_t actionX, actionY, xy_distance; - - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); return; } @@ -4926,10 +5081,12 @@ void Guest::UpdateRideShopApproach() */ void Guest::UpdateRideShopInteract() { + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; + const int16_t tileCenterX = next_x + 16; const int16_t tileCenterY = next_y + 16; - Ride* ride = get_ride(current_ride); - if (ride->type == RIDE_TYPE_FIRST_AID) { if (nausea <= 35) @@ -4959,7 +5116,7 @@ void Guest::UpdateRideShopInteract() // Do not play toilet flush sound on title screen as it's considered loud and annoying if (!(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO)) { - audio_play_sound_at_location(SOUND_TOILET_FLUSH, x, y, z); + audio_play_sound_at_location(SoundId::ToiletFlush, { x, y, z }); } sub_state = PEEP_SHOP_LEAVE; @@ -4979,29 +5136,25 @@ void Guest::UpdateRideShopInteract() */ void Guest::UpdateRideShopLeave() { - int16_t actionX, actionY, xy_distance; - - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); - actionX = x & 0xFFE0; - actionY = y & 0xFFE0; - if (actionX != next_x) + if ((x & 0xFFE0) != next_x) return; - if (actionY != next_y) + if ((y & 0xFFE0) != next_y) return; } SetState(PEEP_STATE_WALKING); - Ride* ride = get_ride(current_ride); - ride->total_customers++; - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER; - - ride_update_satisfaction(ride, happiness / 64); + auto ride = get_ride(current_ride); + if (ride != nullptr) + { + ride->total_customers++; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER; + ride_update_satisfaction(ride, happiness / 64); + } } void Guest::UpdateGuest() @@ -5143,14 +5296,11 @@ void Guest::UpdateWalking() { if ((0xFFFF & scenario_rand()) < 936) { - Invalidate(); - action = PEEP_ACTION_WAVE_2; action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } } } @@ -5161,14 +5311,11 @@ void Guest::UpdateWalking() { if ((0xFFFF & scenario_rand()) < 936) { - Invalidate(); - action = PEEP_ACTION_TAKE_PHOTO; action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } } } @@ -5179,14 +5326,11 @@ void Guest::UpdateWalking() { if ((0xFFFF & scenario_rand()) < 936) { - Invalidate(); - action = PEEP_ACTION_DRAW_PICTURE; action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } } } @@ -5203,12 +5347,12 @@ void Guest::UpdateWalking() LITTER_TYPE_EMPTY_BURGER_BOX, LITTER_TYPE_EMPTY_CUP, }; - int32_t ebp = litter_types[scenario_rand() & 0x3]; + int32_t litterType = litter_types[scenario_rand() & 0x3]; int32_t litterX = x + (scenario_rand() & 0x7) - 3; int32_t litterY = y + (scenario_rand() & 0x7) - 3; int32_t litterDirection = (scenario_rand() & 0x3); - litter_create(litterX, litterY, z, litterDirection, ebp); + litter_create(litterX, litterY, z, litterDirection, litterType); } } } @@ -5222,12 +5366,12 @@ void Guest::UpdateWalking() if (container & (1u << pos_stnd)) break; - int32_t bp = 0; + int32_t litterType = 0; if (pos_stnd != 32) { item_standard_flags &= ~(1u << pos_stnd); - bp = item_standard_litter[pos_stnd]; + litterType = item_standard_litter[pos_stnd]; } else { @@ -5236,7 +5380,7 @@ void Guest::UpdateWalking() if (container & (1u << pos_extr)) break; item_extra_flags &= ~(1u << pos_extr); - bp = item_extra_litter[pos_extr]; + litterType = item_extra_litter[pos_extr]; } window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; @@ -5246,7 +5390,7 @@ void Guest::UpdateWalking() int32_t litterY = y + (scenario_rand() & 0x7) - 3; int32_t litterDirection = (scenario_rand() & 0x3); - litter_create(litterX, litterY, z, litterDirection, bp); + litter_create(litterX, litterY, z, litterDirection, litterType); } } @@ -5269,16 +5413,13 @@ void Guest::UpdateWalking() if (GetNextIsSurface()) { - TileElement* tile_element = map_get_surface_element_at({ next_x, next_y }); + auto surfaceElement = map_get_surface_element_at({ next_x, next_y }); - int32_t water_height = tile_element->AsSurface()->GetWaterHeight(); + int32_t water_height = surfaceElement->GetWaterHeight(); if (water_height) { - Invalidate(); water_height *= 16; MoveTo(x, y, water_height); - Invalidate(); - SetState(PEEP_STATE_FALLING); return; } @@ -5320,6 +5461,8 @@ void Guest::UpdateWalking() return; TileElement* tileElement = map_get_first_element_at(next_x / 32, next_y / 32); + if (tileElement == nullptr) + return; for (;; tileElement++) { @@ -5370,7 +5513,7 @@ void Guest::UpdateWalking() { sprite = get_sprite(sprite_id); - if (sprite->generic.linked_list_type_offset != SPRITE_LIST_PEEP * 2) + if (sprite->generic.linked_list_index != SPRITE_LIST_PEEP) continue; if (sprite->peep.state != PEEP_STATE_WATCHING) @@ -5409,11 +5552,11 @@ void Guest::UpdateWalking() if (current_seat & 1) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_NEW_RIDE, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_NEW_RIDE, PEEP_THOUGHT_ITEM_NONE); } if (current_ride == RIDE_ID_NULL) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_SCENERY, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_SCENERY, PEEP_THOUGHT_ITEM_NONE); } } @@ -5428,8 +5571,8 @@ void Guest::UpdateQueuing() RemoveFromQueue(); return; } - Ride* ride = get_ride(current_ride); - if (ride->status == RIDE_STATUS_CLOSED || ride->status == RIDE_STATUS_TESTING) + auto ride = get_ride(current_ride); + if (ride == nullptr || ride->status != RIDE_STATUS_OPEN) { RemoveFromQueue(); SetState(PEEP_STATE_1); @@ -5483,12 +5626,11 @@ void Guest::UpdateQueuing() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } if (time_in_queue >= 3500 && (0xFFFF & scenario_rand()) <= 93) { // Create the I have been waiting in line ages thought - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_QUEUING_AGES, current_ride); + InsertNewThought(PEEP_THOUGHT_TYPE_QUEUING_AGES, current_ride); } } else @@ -5524,7 +5666,6 @@ void Guest::UpdateQueuing() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); break; default: break; @@ -5560,14 +5701,9 @@ void Guest::UpdateEnteringPark() } return; } - int16_t actionX = 0; - int16_t actionY = 0; - int16_t xy_distance; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); return; } SetState(PEEP_STATE_FALLING); @@ -5596,14 +5732,9 @@ void Guest::UpdateLeavingPark() return; } - int16_t actionX = 0; - int16_t actionY = 0; - int16_t xy_distance; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - Invalidate(); - MoveTo(actionX, actionY, z); - Invalidate(); + MoveTo((*loc).x, (*loc).y, z); return; } @@ -5619,7 +5750,7 @@ void Guest::UpdateLeavingPark() PerformNextAction(pathingResult); if (!(pathingResult & PATHING_OUTSIDE_PARK)) return; - peep_sprite_remove(this); + Remove(); } /** @@ -5641,7 +5772,6 @@ void Guest::UpdateWatching() destination_y = y; sprite_direction = (var_37 & 3) * 8; - Invalidate(); action = PEEP_ACTION_NONE_1; next_action_sprite_type = PEEP_ACTION_SPRITE_TYPE_WATCH_RIDE; @@ -5658,11 +5788,8 @@ void Guest::UpdateWatching() if (action < PEEP_ACTION_NONE_1) { // 6917F6 - int16_t actionX = 0; - int16_t actionY = 0; - int16_t xy_distance; - UpdateAction(&actionX, &actionY, &xy_distance); - + UpdateAction(); + Invalidate(); if (action != PEEP_ACTION_NONE_2) return; action = PEEP_ACTION_NONE_1; @@ -5677,7 +5804,6 @@ void Guest::UpdateWatching() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); return; } } @@ -5688,7 +5814,6 @@ void Guest::UpdateWatching() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); return; } @@ -5700,7 +5825,6 @@ void Guest::UpdateWatching() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); return; } } @@ -5749,12 +5873,14 @@ void Guest::UpdateUsingBin() { if (action != PEEP_ACTION_NONE_2) { - int16_t actionX, actionY, xy_distance; - UpdateAction(&actionX, &actionY, &xy_distance); + UpdateAction(); + Invalidate(); return; } TileElement* tileElement = map_get_first_element_at(next_x / 32, next_y / 32); + if (tileElement == nullptr) + return; for (;; tileElement++) { @@ -5822,12 +5948,12 @@ void Guest::UpdateUsingBin() UpdateSpriteType(); continue; } - uint8_t bp = item_standard_litter[cur_container]; + uint8_t litterType = item_standard_litter[cur_container]; int32_t litterX = x + (scenario_rand() & 7) - 3; int32_t litterY = y + (scenario_rand() & 7) - 3; - litter_create(litterX, litterY, z, scenario_rand() & 3, bp); + litter_create(litterX, litterY, z, scenario_rand() & 3, litterType); item_standard_flags &= ~(1 << cur_container); window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; @@ -5856,12 +5982,12 @@ void Guest::UpdateUsingBin() UpdateSpriteType(); continue; } - uint8_t bp = item_extra_litter[cur_container]; + uint8_t litterType = item_extra_litter[cur_container]; int32_t litterX = x + (scenario_rand() & 7) - 3; int32_t litterY = y + (scenario_rand() & 7) - 3; - litter_create(litterX, litterY, z, scenario_rand() & 3, bp); + litter_create(litterX, litterY, z, scenario_rand() & 3, litterType); item_extra_flags &= ~(1 << cur_container); window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; @@ -5909,12 +6035,7 @@ bool Guest::ShouldFindBench() return false; } - if (!GetNextIsSurface() && !GetNextIsSloped()) - { - return true; - } - - return false; + return !GetNextIsSurface() && !GetNextIsSloped(); } /** @@ -5928,6 +6049,8 @@ bool Guest::UpdateWalkingFindBench() return false; TileElement* tileElement = map_get_first_element_at(next_x / 32, next_y / 32); + if (tileElement == nullptr) + return false; for (;; tileElement++) { @@ -5972,7 +6095,7 @@ bool Guest::UpdateWalkingFindBench() { sprite = get_sprite(sprite_id); - if (sprite->generic.linked_list_type_offset != SPRITE_LIST_PEEP * 2) + if (sprite->generic.linked_list_index != SPRITE_LIST_PEEP) continue; if (sprite->peep.state != PEEP_STATE_SITTING) @@ -6023,6 +6146,8 @@ bool Guest::UpdateWalkingFindBin() return false; TileElement* tileElement = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32); + if (tileElement == nullptr) + return false; for (;; tileElement++) { @@ -6124,6 +6249,8 @@ static void peep_update_walking_break_scenery(Peep* peep) return; TileElement* tileElement = map_get_first_element_at(peep->next_x / 32, peep->next_y / 32); + if (tileElement == nullptr) + return; for (;; tileElement++) { @@ -6162,7 +6289,7 @@ static void peep_update_walking_break_scenery(Peep* peep) { sprite = get_sprite(sprite_id); - if ((sprite->generic.linked_list_type_offset != SPRITE_LIST_PEEP * 2) || (sprite->peep.state != PEEP_STATE_SITTING) + if ((sprite->generic.linked_list_index != SPRITE_LIST_PEEP) || (sprite->peep.state != PEEP_STATE_SITTING) || (peep->z != sprite->peep.z)) { continue; @@ -6203,8 +6330,6 @@ static void peep_update_walking_break_scenery(Peep* peep) */ static bool peep_should_watch_ride(TileElement* tileElement) { - Ride* ride = get_ride(tileElement->AsTrack()->GetRideIndex()); - // Ghosts are purely this-client-side and should not cause any interaction, // as that may lead to a desync. if (network_get_mode() != NETWORK_MODE_NONE) @@ -6213,7 +6338,8 @@ static bool peep_should_watch_ride(TileElement* tileElement) return false; } - if (gRideClassifications[ride->type] != RIDE_CLASS_RIDE) + auto ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + if (ride == nullptr || !ride->IsRide()) { return false; } @@ -6258,9 +6384,11 @@ static bool peep_should_watch_ride(TileElement* tileElement) bool loc_690FD0(Peep* peep, uint8_t* rideToView, uint8_t* rideSeatToView, TileElement* tileElement) { - Ride* ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + auto ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + if (ride == nullptr) + return false; - *rideToView = tileElement->AsTrack()->GetRideIndex(); + *rideToView = ride->id; if (ride->excitement == RIDE_RATING_UNDEFINED) { *rideSeatToView = 1; @@ -6303,11 +6431,16 @@ bool loc_690FD0(Peep* peep, uint8_t* rideToView, uint8_t* rideSeatToView, TileEl */ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToView, uint8_t* rideSeatToView) { - TileElement *tileElement, *surfaceElement; + TileElement* tileElement; - surfaceElement = map_get_surface_element_at({ peep->next_x, peep->next_y }); + auto surfaceElement = map_get_surface_element_at({ peep->next_x, peep->next_y }); + + tileElement = reinterpret_cast(surfaceElement); + if (tileElement == nullptr) + { + return false; + } - tileElement = surfaceElement; do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6341,7 +6474,12 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV surfaceElement = map_get_surface_element_at({ x, y }); - tileElement = surfaceElement; + tileElement = reinterpret_cast(surfaceElement); + if (tileElement == nullptr) + { + return false; + } + do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6353,7 +6491,7 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV } if (tileElement->GetType() != TILE_ELEMENT_TYPE_WALL) continue; - if (tileElement->GetDirectionWithOffset(2) != edge) + if (direction_reverse(tileElement->GetDirection()) != edge) continue; auto wallEntry = tileElement->AsWall()->GetEntry(); if (wallEntry == nullptr || (wallEntry->wall.flags2 & WALL_SCENERY_2_IS_OPAQUE)) @@ -6368,7 +6506,7 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV } while (!(tileElement++)->IsLastForTile()); // TODO: Extract loop B - tileElement = surfaceElement; + tileElement = reinterpret_cast(surfaceElement); do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6412,7 +6550,7 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV } while (!(tileElement++)->IsLastForTile()); // TODO: Extract loop C - tileElement = surfaceElement; + tileElement = reinterpret_cast(surfaceElement); do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6453,7 +6591,7 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV surfaceElement = map_get_surface_element_at({ x, y }); // TODO: extract loop A - tileElement = surfaceElement; + tileElement = reinterpret_cast(surfaceElement); if (tileElement == nullptr) { @@ -6471,7 +6609,7 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV } if (tileElement->GetType() != TILE_ELEMENT_TYPE_WALL) continue; - if (tileElement->GetDirectionWithOffset(2) != edge) + if (direction_reverse(tileElement->GetDirection()) != edge) continue; auto wallEntry = tileElement->AsWall()->GetEntry(); if (wallEntry == nullptr || (wallEntry->wall.flags2 & WALL_SCENERY_2_IS_OPAQUE)) @@ -6485,7 +6623,7 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV } while (!(tileElement++)->IsLastForTile()); // TODO: Extract loop B - tileElement = surfaceElement; + tileElement = reinterpret_cast(surfaceElement); do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6529,7 +6667,7 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV } while (!(tileElement++)->IsLastForTile()); // TODO: Extract loop C - tileElement = surfaceElement; + tileElement = reinterpret_cast(surfaceElement); do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6570,7 +6708,12 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV surfaceElement = map_get_surface_element_at({ x, y }); // TODO: extract loop A - tileElement = surfaceElement; + tileElement = reinterpret_cast(surfaceElement); + if (tileElement == nullptr) + { + return false; + } + do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6582,7 +6725,7 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV } if (tileElement->GetType() != TILE_ELEMENT_TYPE_WALL) continue; - if (tileElement->GetDirectionWithOffset(2) != edge) + if (direction_reverse(tileElement->GetDirection()) != edge) continue; auto wallEntry = tileElement->AsWall()->GetEntry(); if (wallEntry == nullptr || (wallEntry->wall.flags2 & WALL_SCENERY_2_IS_OPAQUE)) @@ -6596,7 +6739,7 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV } while (!(tileElement++)->IsLastForTile()); // TODO: Extract loop B - tileElement = surfaceElement; + tileElement = reinterpret_cast(surfaceElement); do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6696,7 +6839,7 @@ static item_pref_t item_order_preference[] = { { 0, PEEP_ITEM_CHICKEN, PEEP_SPRITE_TYPE_CHICKEN }, { 0, PEEP_ITEM_LEMONADE, PEEP_SPRITE_TYPE_LEMONADE }, { 0, PEEP_ITEM_CANDYFLOSS, PEEP_SPRITE_TYPE_CANDYFLOSS }, - { 0, PEEP_ITEM_POPCORN, PEEP_SPRITE_TYPE_PIZZA }, + { 0, PEEP_ITEM_POPCORN, PEEP_SPRITE_TYPE_POPCORN }, { 0, PEEP_ITEM_HOT_DOG, PEEP_SPRITE_TYPE_HOT_DOG }, { 0, PEEP_ITEM_TENTACLE, PEEP_SPRITE_TYPE_TENTACLE }, { 0, PEEP_ITEM_TOFFEE_APPLE, PEEP_SPRITE_TYPE_TOFFEE_APPLE }, @@ -6736,7 +6879,7 @@ void Guest::UpdateSpriteType() if ((scenario_rand() & 0xFFFF) <= 13107) { isBalloonPopped = true; - audio_play_sound_at_location(SOUND_BALLOON_POP, x, y, z); + audio_play_sound_at_location(SoundId::BalloonPop, { x, y, z }); } create_balloon(x, y, z + 9, balloon_colour, isBalloonPopped); } @@ -6751,6 +6894,8 @@ void Guest::UpdateSpriteType() TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); while (true) { + if (tileElement == nullptr) + break; if ((z / 8) < tileElement->base_height) break; diff --git a/src/openrct2/peep/GuestPathfinding.cpp b/src/openrct2/peep/GuestPathfinding.cpp index f667799221..8fb43c4f39 100644 --- a/src/openrct2/peep/GuestPathfinding.cpp +++ b/src/openrct2/peep/GuestPathfinding.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -7,6 +7,7 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ +#include "../core/Guard.hpp" #include "../ride/Station.h" #include "../ride/Track.h" #include "../scenario/Scenario.h" @@ -32,7 +33,7 @@ static int32_t guest_surface_path_finding(Peep* peep); static struct { TileCoordsXYZ location; - uint8_t direction; + Direction direction; } _peepPathFindHistory[16]; enum @@ -77,11 +78,11 @@ static TileElement* get_banner_on_path(TileElement* path_element) return nullptr; } -static int32_t banner_clear_path_edges(TileElement* tileElement, int32_t edges) +static int32_t banner_clear_path_edges(PathElement* pathElement, int32_t edges) { if (_peepPathFindIsStaff) return edges; - TileElement* bannerElement = get_banner_on_path(tileElement); + TileElement* bannerElement = get_banner_on_path(reinterpret_cast(pathElement)); if (bannerElement != nullptr) { do @@ -95,18 +96,18 @@ static int32_t banner_clear_path_edges(TileElement* tileElement, int32_t edges) /** * Gets the connected edges of a path that are permitted (i.e. no 'no entry' signs) */ -static int32_t path_get_permitted_edges(TileElement* tileElement) +static int32_t path_get_permitted_edges(PathElement* pathElement) { - return banner_clear_path_edges(tileElement, tileElement->AsPath()->GetEdgesAndCorners()) & 0x0F; + return banner_clear_path_edges(pathElement, pathElement->GetEdgesAndCorners()) & 0x0F; } /** * * rct2: 0x0069524E */ -static int32_t peep_move_one_tile(uint8_t direction, Peep* peep) +static int32_t peep_move_one_tile(Direction direction, Peep* peep) { - assert(direction <= 3); + assert(direction_valid(direction)); int16_t x = peep->next_x; int16_t y = peep->next_y; x += CoordsDirectionDelta[direction].x; @@ -138,13 +139,13 @@ static int32_t guest_surface_path_finding(Peep* peep) int16_t x = peep->next_x; int16_t y = peep->next_y; int16_t z = peep->next_z; - uint8_t randDirection = scenario_rand() & 3; + Direction randDirection = scenario_rand() & 3; if (!fence_in_the_way(x, y, z, z + 4, randDirection)) { x += CoordsDirectionDelta[randDirection].x; y += CoordsDirectionDelta[randDirection].y; - uint8_t backwardsDirection = direction_reverse(randDirection); + Direction backwardsDirection = direction_reverse(randDirection); if (!fence_in_the_way(x, y, z, z + 4, backwardsDirection)) { @@ -169,7 +170,7 @@ static int32_t guest_surface_path_finding(Peep* peep) { x += CoordsDirectionDelta[randDirection].x; y += CoordsDirectionDelta[randDirection].y; - uint8_t backwardsDirection = direction_reverse(randDirection); + Direction backwardsDirection = direction_reverse(randDirection); if (!fence_in_the_way(x, y, z, z + 4, backwardsDirection)) { @@ -189,7 +190,7 @@ static int32_t guest_surface_path_finding(Peep* peep) { x += CoordsDirectionDelta[randDirection].x; y += CoordsDirectionDelta[randDirection].y; - uint8_t backwardsDirection = direction_reverse(randDirection); + Direction backwardsDirection = direction_reverse(randDirection); if (!fence_in_the_way(x, y, z, z + 4, backwardsDirection)) { @@ -222,13 +223,13 @@ static int32_t guest_surface_path_finding(Peep* peep) * Returns the type of the next footpath tile a peep can get to from x,y,z / * inputTileElement in the given direction. */ -static uint8_t footpath_element_next_in_direction(TileCoordsXYZ loc, TileElement* tileElement, uint8_t chosenDirection) +static uint8_t footpath_element_next_in_direction(TileCoordsXYZ loc, PathElement* pathElement, Direction chosenDirection) { TileElement* nextTileElement; - if (tileElement->AsPath()->IsSloped()) + if (pathElement->IsSloped()) { - if (tileElement->AsPath()->GetSlopeDirection() == chosenDirection) + if (pathElement->GetSlopeDirection() == chosenDirection) { loc.z += 2; } @@ -238,6 +239,8 @@ static uint8_t footpath_element_next_in_direction(TileCoordsXYZ loc, TileElement nextTileElement = map_get_first_element_at(loc.x, loc.y); do { + if (nextTileElement == nullptr) + break; if (nextTileElement->IsGhost()) continue; if (nextTileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -275,11 +278,10 @@ static uint8_t footpath_element_next_in_direction(TileCoordsXYZ loc, TileElement * This is the recursive portion of footpath_element_destination_in_direction(). */ static uint8_t footpath_element_dest_in_dir( - TileCoordsXYZ loc, [[maybe_unused]] TileElement* inputTileElement, uint8_t chosenDirection, ride_id_t* outRideIndex, - int32_t level) + TileCoordsXYZ loc, Direction chosenDirection, ride_id_t* outRideIndex, int32_t level) { TileElement* tileElement; - int32_t direction; + Direction direction; if (level > 25) return PATH_SEARCH_LIMIT_REACHED; @@ -302,8 +304,8 @@ static uint8_t footpath_element_dest_in_dir( if (loc.z != tileElement->base_height) continue; ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) + auto ride = get_ride(rideIndex); + if (ride != nullptr && ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) { *outRideIndex = rideIndex; return PATH_SEARCH_SHOP_ENTRANCE; @@ -341,27 +343,27 @@ static uint8_t footpath_element_dest_in_dir( if (tileElement->AsPath()->IsWide()) return PATH_SEARCH_WIDE; - uint8_t edges = path_get_permitted_edges(tileElement); + uint8_t edges = path_get_permitted_edges(tileElement->AsPath()); edges &= ~(1 << direction_reverse(chosenDirection)); loc.z = tileElement->base_height; - for (direction = 0; direction < 4; direction++) + for (Direction dir : ALL_DIRECTIONS) { - if (!(edges & (1 << direction))) + if (!(edges & (1 << dir))) continue; - edges &= ~(1 << direction); + edges &= ~(1 << dir); if (edges != 0) return PATH_SEARCH_JUNCTION; if (tileElement->AsPath()->IsSloped()) { - if (tileElement->AsPath()->GetSlopeDirection() == direction) + if (tileElement->AsPath()->GetSlopeDirection() == dir) { loc.z += 2; } } - return footpath_element_dest_in_dir(loc, tileElement, direction, outRideIndex, level + 1); + return footpath_element_dest_in_dir(loc, dir, outRideIndex, level + 1); } return PATH_SEARCH_DEAD_END; } @@ -394,17 +396,17 @@ static uint8_t footpath_element_dest_in_dir( * width path, for example that leads from a ride exit back to the main path. */ static uint8_t footpath_element_destination_in_direction( - TileCoordsXYZ loc, TileElement* inputTileElement, uint8_t chosenDirection, ride_id_t* outRideIndex) + TileCoordsXYZ loc, PathElement* pathElement, Direction chosenDirection, ride_id_t* outRideIndex) { - if (inputTileElement->AsPath()->IsSloped()) + if (pathElement->IsSloped()) { - if (inputTileElement->AsPath()->GetSlopeDirection() == chosenDirection) + if (pathElement->GetSlopeDirection() == chosenDirection) { loc.z += 2; } } - return footpath_element_dest_in_dir(loc, inputTileElement, chosenDirection, outRideIndex, 0); + return footpath_element_dest_in_dir(loc, chosenDirection, outRideIndex, 0); } /** @@ -424,7 +426,7 @@ static int32_t guest_path_find_aimless(Peep* peep, uint8_t edges) while (true) { - uint8_t direction = scenario_rand() & 3; + Direction direction = scenario_rand() & 3; // Otherwise go in a random direction allowed from the tile. if (edges & (1 << direction)) { @@ -473,9 +475,9 @@ static uint8_t peep_pathfind_get_max_number_junctions(Peep* peep) * since entrances and ride queues coming off a path should not result in * the path being considered a junction. */ -static bool path_is_thin_junction(TileElement* path, TileCoordsXYZ loc) +static bool path_is_thin_junction(PathElement* path, TileCoordsXYZ loc) { - uint8_t edges = path->AsPath()->GetEdges(); + uint8_t edges = path->GetEdges(); int32_t test_edge = bitscanforward(edges); if (test_edge == -1) @@ -505,6 +507,20 @@ static bool path_is_thin_junction(TileElement* path, TileCoordsXYZ loc) return thin_junction; } +static int32_t CalculateHeuristicPathingScore(TileCoordsXYZ loc1, TileCoordsXYZ loc2) +{ + auto xDelta = abs(loc1.x - loc2.x) * 32; + auto yDelta = abs(loc1.y - loc2.y) * 32; + auto zDelta = abs(loc1.z - loc2.z) * 2; + + if (xDelta < yDelta) + xDelta >>= 4; + else + yDelta >>= 4; + + return xDelta + yDelta + zDelta; +} + /** * Searches for the tile with the best heuristic score within the search limits * starting from the given tile x,y,z and going in the given direction test_edge. @@ -583,8 +599,8 @@ static bool path_is_thin_junction(TileElement* path, TileCoordsXYZ loc) */ static void peep_pathfind_heuristic_search( TileCoordsXYZ loc, Peep* peep, TileElement* currentTileElement, bool inPatrolArea, uint8_t counter, uint16_t* endScore, - int32_t test_edge, uint8_t* endJunctions, TileCoordsXYZ junctionList[16], uint8_t directionList[16], TileCoordsXYZ* endXYZ, - uint8_t* endSteps) + Direction test_edge, uint8_t* endJunctions, TileCoordsXYZ junctionList[16], uint8_t directionList[16], + TileCoordsXYZ* endXYZ, uint8_t* endSteps) { uint8_t searchResult = PATH_SEARCH_FAILED; @@ -656,8 +672,8 @@ static void peep_pathfind_heuristic_search( /* For peeps heading for a shop, the goal is the shop * tile. */ rideIndex = tileElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) + auto ride = get_ride(rideIndex); + if (ride == nullptr || !ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) continue; found = true; @@ -667,7 +683,7 @@ static void peep_pathfind_heuristic_search( case TILE_ELEMENT_TYPE_ENTRANCE: if (loc.z != tileElement->base_height) continue; - int32_t direction; + Direction direction; searchResult = PATH_SEARCH_OTHER; switch (tileElement->AsEntrance()->GetEntranceType()) { @@ -784,16 +800,7 @@ static void peep_pathfind_heuristic_search( * Ignore for now. */ // Calculate the heuristic score of this map element. - uint16_t x_delta = abs(gPeepPathFindGoalPosition.x - loc.x) * 32; - uint16_t y_delta = abs(gPeepPathFindGoalPosition.y - loc.y) * 32; - if (x_delta < y_delta) - x_delta >>= 4; - else - y_delta >>= 4; - uint16_t new_score = x_delta + y_delta; - uint16_t z_delta = abs(gPeepPathFindGoalPosition.z - loc.z); - z_delta <<= 1; - new_score += z_delta; + uint16_t new_score = CalculateHeuristicPathingScore(loc, gPeepPathFindGoalPosition); /* If this map element is the search goal the current search path ends here. */ if (new_score == 0) @@ -888,7 +895,8 @@ static void peep_pathfind_heuristic_search( /* At this point the map element is a non-wide path.*/ /* Get all the permitted_edges of the map element. */ - uint8_t edges = path_get_permitted_edges(tileElement); + Guard::Assert(tileElement->AsPath() != nullptr); + uint8_t edges = path_get_permitted_edges(tileElement->AsPath()); #if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2 if (gPathFindDebug) @@ -900,7 +908,7 @@ static void peep_pathfind_heuristic_search( #endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2 /* Remove the reverse edge (i.e. the edge back to the previous map element.) */ - edges &= ~(1 << (test_edge ^ 2)); + edges &= ~(1 << direction_reverse(test_edge)); int32_t next_test_edge = bitscanforward(edges); @@ -959,7 +967,7 @@ static void peep_pathfind_heuristic_search( { /* Check if this is a thin junction. And perform additional * necessary checks. */ - thin_junction = path_is_thin_junction(tileElement, loc); + thin_junction = path_is_thin_junction(tileElement->AsPath(), loc); if (thin_junction) { @@ -1157,7 +1165,7 @@ static void peep_pathfind_heuristic_search( * * rct2: 0x0069A5F0 */ -int32_t peep_pathfind_choose_direction(TileCoordsXYZ loc, Peep* peep) +Direction peep_pathfind_choose_direction(TileCoordsXYZ loc, Peep* peep) { // The max number of thin junctions searched - a per-search-path limit. _peepPathFindMaxJunctions = peep_pathfind_get_max_number_junctions(peep); @@ -1203,6 +1211,8 @@ int32_t peep_pathfind_choose_direction(TileCoordsXYZ loc, Peep* peep) bool isThin = false; do { + if (dest_tile_element == nullptr) + break; if (dest_tile_element->base_height != loc.z) continue; if (dest_tile_element->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -1219,14 +1229,14 @@ int32_t peep_pathfind_choose_direction(TileCoordsXYZ loc, Peep* peep) * check if the combination is 'thin'! * The junction is considered 'thin' simply if any of the * overlaid path elements there is a 'thin junction'. */ - isThin = isThin || path_is_thin_junction(dest_tile_element, loc); + isThin = isThin || path_is_thin_junction(dest_tile_element->AsPath(), loc); // Collect the permitted edges of ALL matching path elements at this location. - permitted_edges |= path_get_permitted_edges(dest_tile_element); + permitted_edges |= path_get_permitted_edges(dest_tile_element->AsPath()); } while (!(dest_tile_element++)->IsLastForTile()); // Peep is not on a path. if (!found) - return -1; + return INVALID_DIRECTION; permitted_edges &= 0xF; uint8_t edges = permitted_edges; @@ -1297,7 +1307,7 @@ int32_t peep_pathfind_choose_direction(TileCoordsXYZ loc, Peep* peep) /* If this is a new goal for the peep. Store it and reset the peep's * pathfind_history. */ - if (peep->pathfind_goal.direction > 3 || peep->pathfind_goal.x != goal.x || peep->pathfind_goal.y != goal.y + if (!direction_valid(peep->pathfind_goal.direction) || peep->pathfind_goal.x != goal.x || peep->pathfind_goal.y != goal.y || peep->pathfind_goal.z != goal.z) { peep->pathfind_goal.x = goal.x; @@ -1317,7 +1327,7 @@ int32_t peep_pathfind_choose_direction(TileCoordsXYZ loc, Peep* peep) // Peep has tried all edges. if (edges == 0) - return -1; + return INVALID_DIRECTION; int32_t chosen_edge = bitscanforward(edges); @@ -1460,7 +1470,7 @@ int32_t peep_pathfind_choose_direction(TileCoordsXYZ loc, Peep* peep) log_verbose("Pathfind heuristic search failed."); } #endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 - return -1; + return INVALID_DIRECTION; } #if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 if (gPathFindDebug) @@ -1556,7 +1566,7 @@ static uint8_t get_nearest_park_entrance_index(uint16_t x, uint16_t y) * * rct2: 0x006952C0 */ -static int32_t guest_path_find_entering_park(Peep* peep, [[maybe_unused]] TileElement* tile_element, uint8_t edges) +static int32_t guest_path_find_entering_park(Peep* peep, uint8_t edges) { // Send peeps to the nearest park entrance. uint8_t chosenEntrance = get_nearest_park_entrance_index(peep->next_x, peep->next_y); @@ -1573,9 +1583,9 @@ static int32_t guest_path_find_entering_park(Peep* peep, [[maybe_unused]] TileEl gPeepPathFindIgnoreForeignQueues = true; gPeepPathFindQueueRideIndex = RIDE_ID_NULL; - int32_t chosenDirection = peep_pathfind_choose_direction({ peep->next_x / 32, peep->next_y / 32, peep->next_z }, peep); + Direction chosenDirection = peep_pathfind_choose_direction({ peep->next_x / 32, peep->next_y / 32, peep->next_z }, peep); - if (chosenDirection == -1) + if (chosenDirection == INVALID_DIRECTION) return guest_path_find_aimless(peep, edges); else return peep_move_one_tile(chosenDirection, peep); @@ -1609,7 +1619,7 @@ static uint8_t get_nearest_peep_spawn_index(uint16_t x, uint16_t y) * * rct2: 0x0069536C */ -static int32_t guest_path_find_leaving_park(Peep* peep, [[maybe_unused]] TileElement* tile_element, uint8_t edges) +static int32_t guest_path_find_leaving_park(Peep* peep, uint8_t edges) { // Send peeps to the nearest spawn point. uint8_t chosenSpawn = get_nearest_peep_spawn_index(peep->next_x, peep->next_y); @@ -1623,7 +1633,7 @@ static int32_t guest_path_find_leaving_park(Peep* peep, [[maybe_unused]] TileEle int16_t x = peepSpawn->x & 0xFFE0; int16_t y = peepSpawn->y & 0xFFE0; uint8_t z = peepSpawn->z / 8; - uint8_t direction = peepSpawn->direction; + Direction direction = peepSpawn->direction; gPeepPathFindGoalPosition = { x / 32, y / 32, z }; if (x == peep->next_x && y == peep->next_y) @@ -1634,7 +1644,7 @@ static int32_t guest_path_find_leaving_park(Peep* peep, [[maybe_unused]] TileEle gPeepPathFindIgnoreForeignQueues = true; gPeepPathFindQueueRideIndex = RIDE_ID_NULL; direction = peep_pathfind_choose_direction({ peep->next_x / 32, peep->next_y / 32, peep->next_z }, peep); - if (direction == 0xFF) + if (direction == INVALID_DIRECTION) return guest_path_find_aimless(peep, edges); else return peep_move_one_tile(direction, peep); @@ -1644,7 +1654,7 @@ static int32_t guest_path_find_leaving_park(Peep* peep, [[maybe_unused]] TileEle * * rct2: 0x00695161 */ -static int32_t guest_path_find_park_entrance(Peep* peep, [[maybe_unused]] TileElement* tile_element, uint8_t edges) +static int32_t guest_path_find_park_entrance(Peep* peep, uint8_t edges) { // If entrance no longer exists, choose a new one if ((peep->peep_flags & PEEP_FLAGS_PARK_ENTRANCE_CHOSEN) && peep->current_ride >= gParkEntrances.size()) @@ -1689,13 +1699,13 @@ static int32_t guest_path_find_park_entrance(Peep* peep, [[maybe_unused]] TileEl pathfind_logging_enable(peep); #endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 - int32_t chosenDirection = peep_pathfind_choose_direction({ peep->next_x / 32, peep->next_y / 32, peep->next_z }, peep); + Direction chosenDirection = peep_pathfind_choose_direction({ peep->next_x / 32, peep->next_y / 32, peep->next_z }, peep); #if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 pathfind_logging_disable(); #endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 - if (chosenDirection == -1) + if (chosenDirection == INVALID_DIRECTION) return guest_path_find_aimless(peep, edges); else return peep_move_one_tile(chosenDirection, peep); @@ -1735,7 +1745,7 @@ static void get_ride_queue_end(TileCoordsXYZ& loc) if (!found) return; - uint8_t direction = tileElement->GetDirectionWithOffset(2); + Direction direction = direction_reverse(tileElement->GetDirection()); TileElement* lastPathElement = nullptr; TileElement* firstPathElement = nullptr; @@ -1762,6 +1772,8 @@ static void get_ride_queue_end(TileCoordsXYZ& loc) tileElement = map_get_first_element_at(nextTile.x, nextTile.y); found = false; + if (tileElement == nullptr) + break; do { if (tileElement == firstPathElement) @@ -1797,7 +1809,7 @@ static void get_ride_queue_end(TileCoordsXYZ& loc) } } while (!(tileElement++)->IsLastForTile()); - if (found == false) + if (!found) break; if (!tileElement->AsPath()->IsQueue()) @@ -1865,14 +1877,14 @@ int32_t guest_path_finding(Guest* peep) TileCoordsXYZ loc = { peep->next_x / 32, peep->next_y / 32, peep->next_z }; - TileElement* tileElement = map_get_path_element_at(loc.x, loc.y, loc.z); - if (tileElement == nullptr) + auto* pathElement = map_get_path_element_at(loc); + if (pathElement == nullptr) { return 1; } _peepPathFindIsStaff = false; - uint8_t edges = path_get_permitted_edges(tileElement); + uint8_t edges = path_get_permitted_edges(pathElement); if (edges == 0) { @@ -1884,7 +1896,7 @@ int32_t guest_path_finding(Guest* peep) /* If this tileElement is adjacent to any non-wide paths, * remove all of the edges to wide paths. */ uint8_t adjustedEdges = edges; - for (int32_t chosenDirection = 0; chosenDirection < 4; chosenDirection++) + for (Direction chosenDirection : ALL_DIRECTIONS) { // If there is no path in that direction try another if (!(adjustedEdges & (1 << chosenDirection))) @@ -1892,7 +1904,7 @@ int32_t guest_path_finding(Guest* peep) /* If there is a wide path in that direction, remove that edge and try another */ - if (footpath_element_next_in_direction(loc, tileElement, chosenDirection) == PATH_SEARCH_WIDE) + if (footpath_element_next_in_direction(loc, pathElement, chosenDirection) == PATH_SEARCH_WIDE) { adjustedEdges &= ~(1 << chosenDirection); } @@ -1901,7 +1913,7 @@ int32_t guest_path_finding(Guest* peep) edges = adjustedEdges; } - int8_t direction = direction_reverse(peep->direction); + int32_t direction = direction_reverse(peep->direction); // Check if in a dead end (i.e. only edge is where the peep came from) if (!(edges & ~(1 << direction))) { @@ -1949,9 +1961,9 @@ int32_t guest_path_finding(Guest* peep) switch (peep->state) { case PEEP_STATE_ENTERING_PARK: - return guest_path_find_entering_park(peep, tileElement, edges); + return guest_path_find_entering_park(peep, edges); case PEEP_STATE_LEAVING_PARK: - return guest_path_find_leaving_park(peep, tileElement, edges); + return guest_path_find_leaving_park(peep, edges); default: return guest_path_find_aimless(peep, edges); } @@ -1966,14 +1978,14 @@ int32_t guest_path_finding(Guest* peep) if (!peep->HasFood() && (scenario_rand() & 0xFFFF) >= 2184) { uint8_t adjustedEdges = edges; - for (int32_t chosenDirection = 0; chosenDirection < 4; chosenDirection++) + for (Direction chosenDirection : ALL_DIRECTIONS) { // If there is no path in that direction try another if (!(adjustedEdges & (1 << chosenDirection))) continue; ride_id_t rideIndex, pathSearchResult; - pathSearchResult = footpath_element_destination_in_direction(loc, tileElement, chosenDirection, &rideIndex); + pathSearchResult = footpath_element_destination_in_direction(loc, pathElement, chosenDirection, &rideIndex); switch (pathSearchResult) { case PATH_SEARCH_DEAD_END: @@ -2016,7 +2028,7 @@ int32_t guest_path_finding(Guest* peep) } pathfind_logging_disable(); #endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 - return guest_path_find_park_entrance(peep, tileElement, edges); + return guest_path_find_park_entrance(peep, edges); } if (peep->guest_heading_to_ride_id == 0xFF) @@ -2033,9 +2045,8 @@ int32_t guest_path_finding(Guest* peep) // Peep is heading for a ride. ride_id_t rideIndex = peep->guest_heading_to_ride_id; - Ride* ride = get_ride(rideIndex); - - if (ride->status != RIDE_STATUS_OPEN) + auto ride = get_ride(rideIndex); + if (ride == nullptr || ride->status != RIDE_STATUS_OPEN) { #if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 if (gPathFindDebug) @@ -2054,7 +2065,7 @@ int32_t guest_path_finding(Guest* peep) /* Find the ride's closest entrance station to the peep. * At the same time, count how many entrance stations there are and * which stations are entrance stations. */ - uint16_t closestDist = 0xFFFF; + auto bestScore = std::numeric_limits::max(); uint8_t closestStationNum = 0; int32_t numEntranceStations = 0; @@ -2070,14 +2081,12 @@ int32_t guest_path_finding(Guest* peep) entranceStations |= (1 << stationNum); TileCoordsXYZD entranceLocation = ride_get_entrance_location(ride, stationNum); - - int16_t stationX = (int16_t)(entranceLocation.x * 32); - int16_t stationY = (int16_t)(entranceLocation.y * 32); - uint16_t dist = abs(stationX - peep->next_x) + abs(stationY - peep->next_y); - - if (dist < closestDist) + auto score = CalculateHeuristicPathingScore( + { entranceLocation.x, entranceLocation.y, entranceLocation.z }, + { peep->next_x / 32, peep->next_y / 32, peep->next_z }); + if (score < bestScore) { - closestDist = dist; + bestScore = score; closestStationNum = stationNum; continue; } @@ -2132,7 +2141,7 @@ int32_t guest_path_finding(Guest* peep) direction = peep_pathfind_choose_direction({ peep->next_x / 32, peep->next_y / 32, peep->next_z }, peep); - if (direction == -1) + if (direction == INVALID_DIRECTION) { /* Heuristic search failed for all directions. * Reset the pathfind_goal - this means that the pathfind_history diff --git a/src/openrct2/peep/Peep.cpp b/src/openrct2/peep/Peep.cpp index 4c92b2a201..7a1f9c1f9b 100644 --- a/src/openrct2/peep/Peep.cpp +++ b/src/openrct2/peep/Peep.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -79,37 +79,8 @@ static TileElement* _peepRideEntranceExitElement; static void* _crowdSoundChannel = nullptr; static void peep_128_tick_update(Peep* peep, int32_t index); -static void peep_easter_egg_peep_interactions(Guest* peep); -static void peep_give_real_name(Peep* peep); static void peep_release_balloon(Guest* peep, int16_t spawn_height); - // clang-format off -static constexpr const char *gPeepEasterEggNames[] = { - "MICHAEL SCHUMACHER", - "JACQUES VILLENEUVE", - "DAMON HILL", - "MR BEAN", - "CHRIS SAWYER", - "KATIE BRAYSHAW", - "MELANIE WARN", - "SIMON FOSTER", - "JOHN WARDLEY", - "LISA STIRLING", - "DONALD MACRAE", - "KATHERINE MCGOWAN", - "FRANCES MCGOWAN", - "CORINA MASSOURA", - "CAROL YOUNG", - "MIA SHERIDAN", - "KATIE RODGER", - "EMMA GARRELL", - "JOANNE BARTON", - "FELICITY ANDERSON", - "KATIE SMITH", - "EILIDH BELL", - "NANCY STILLWAGON", - "DAVID ELLIS" -}; /** rct2: 0x00981DB0 */ static struct @@ -376,7 +347,9 @@ void Peep::Invalidate() void Peep::MoveTo(int16_t destX, int16_t destY, int16_t destZ) { + Invalidate(); // Invalidate current position. sprite_move(destX, destY, destZ, (rct_sprite*)this); + Invalidate(); // Invalidate new position. } uint8_t Peep::GetNextDirection() const @@ -435,7 +408,7 @@ void peep_update_all() uint16_t spriteIndex; Peep* peep; - if (gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER)) + if (gScreenFlags & SCREEN_FLAGS_EDITOR) return; spriteIndex = gSpriteListHead[SPRITE_LIST_PEEP]; @@ -452,7 +425,7 @@ void peep_update_all() else { peep_128_tick_update(peep, i); - if (peep->linked_list_type_offset == SPRITE_LIST_PEEP * 2) + if (peep->linked_list_index == SPRITE_LIST_PEEP) { peep->Update(); } @@ -511,6 +484,8 @@ bool Peep::CheckForPath() do { + if (tile_element == nullptr) + break; if (tile_element->GetType() == map_type) { if (height == tile_element->base_height) @@ -593,12 +568,12 @@ void Peep::StateReset() } /** rct2: 0x00981D7C, 0x00981D7E */ -static constexpr const LocationXY16 word_981D7C[4] = { { -2, 0 }, { 0, 2 }, { 2, 0 }, { 0, -2 } }; +static constexpr const CoordsXY word_981D7C[4] = { { -2, 0 }, { 0, 2 }, { 2, 0 }, { 0, -2 } }; -bool Peep::UpdateAction() +std::optional Peep::UpdateAction() { - int16_t actionX, actionY, xy_distance; - return UpdateAction(&actionX, &actionY, &xy_distance); + int16_t xy_distance; + return UpdateAction(xy_distance); } /** @@ -609,7 +584,7 @@ bool Peep::UpdateAction() * has not yet been reached. xy_distance is how close the * peep is to the target. */ -bool Peep::UpdateAction(int16_t* actionX, int16_t* actionY, int16_t* xy_distance) +std::optional Peep::UpdateAction(int16_t& xy_distance) { _unk_F1AEF0 = action_sprite_image_offset; if (action == PEEP_ACTION_NONE_1) @@ -617,25 +592,24 @@ bool Peep::UpdateAction(int16_t* actionX, int16_t* actionY, int16_t* xy_distance action = PEEP_ACTION_NONE_2; } - *actionX = x - destination_x; - *actionY = y - destination_y; + CoordsXY diffrenceLoc = { x - destination_x, y - destination_y }; - int32_t x_delta = abs(*actionX); - int32_t y_delta = abs(*actionY); + int32_t x_delta = abs(diffrenceLoc.x); + int32_t y_delta = abs(diffrenceLoc.y); - *xy_distance = x_delta + y_delta; + xy_distance = x_delta + y_delta; if (action == PEEP_ACTION_NONE_1 || action == PEEP_ACTION_NONE_2) { - if (*xy_distance <= destination_tolerance) + if (xy_distance <= destination_tolerance) { - return false; + return {}; } int32_t nextDirection = 0; if (x_delta < y_delta) { nextDirection = 8; - if (*actionY >= 0) + if (diffrenceLoc.y >= 0) { nextDirection = 24; } @@ -643,14 +617,14 @@ bool Peep::UpdateAction(int16_t* actionX, int16_t* actionY, int16_t* xy_distance else { nextDirection = 16; - if (*actionX >= 0) + if (diffrenceLoc.x >= 0) { nextDirection = 0; } } sprite_direction = nextDirection; - *actionX = x + word_981D7C[nextDirection / 8].x; - *actionY = y + word_981D7C[nextDirection / 8].y; + CoordsXY loc = { x, y }; + loc += word_981D7C[nextDirection / 8]; no_action_frame_num++; const rct_peep_animation* peepAnimation = g_peep_animation_entries[sprite_type].sprite_animation; const uint8_t* imageOffset = peepAnimation[action_sprite_type].frame_offsets; @@ -659,7 +633,7 @@ bool Peep::UpdateAction(int16_t* actionX, int16_t* actionY, int16_t* xy_distance no_action_frame_num = 0; } action_sprite_image_offset = imageOffset[no_action_frame_num]; - return true; + return loc; } const rct_peep_animation* peepAnimation = g_peep_animation_entries[sprite_type].sprite_animation; @@ -671,20 +645,14 @@ bool Peep::UpdateAction(int16_t* actionX, int16_t* actionY, int16_t* xy_distance action_sprite_image_offset = 0; action = PEEP_ACTION_NONE_2; UpdateCurrentActionSpriteType(); - Invalidate(); - *actionX = x; - *actionY = y; - return true; + return { { x, y } }; } action_sprite_image_offset = peepAnimation[action_sprite_type].frame_offsets[action_frame]; // If not throwing up and not at the frame where sick appears. if (action != PEEP_ACTION_THROW_UP || action_frame != 15) { - Invalidate(); - *actionX = x; - *actionY = y; - return true; + return { { x, y } }; } // We are throwing up @@ -701,13 +669,11 @@ bool Peep::UpdateAction(int16_t* actionX, int16_t* actionY, int16_t* xy_distance // Create sick at location litter_create(x, y, z, sprite_direction, (sprite_index & 1) ? LITTER_TYPE_SICK_ALT : LITTER_TYPE_SICK); - int32_t sound_id = SOUND_COUGH_1 + (scenario_rand() & 3); - audio_play_sound_at_location(sound_id, x, y, z); + SoundId coughs[4] = { SoundId::Cough1, SoundId::Cough2, SoundId::Cough3, SoundId::Cough4 }; + auto soundId = coughs[scenario_rand() & 3]; + audio_play_sound_at_location(soundId, { x, y, z }); - Invalidate(); - *actionX = x; - *actionY = y; - return true; + return { { x, y } }; } /** @@ -718,9 +684,12 @@ void peep_decrement_num_riders(Peep* peep) { if (peep->state == PEEP_STATE_ON_RIDE || peep->state == PEEP_STATE_ENTERING_RIDE) { - Ride* ride = get_ride(peep->current_ride); - ride->num_riders = std::max(0, ride->num_riders - 1); - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; + auto ride = get_ride(peep->current_ride); + if (ride != nullptr) + { + ride->num_riders = std::max(0, ride->num_riders - 1); + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; + } } } @@ -739,9 +708,12 @@ void peep_window_state_update(Peep* peep) { if (peep->state == PEEP_STATE_ON_RIDE || peep->state == PEEP_STATE_ENTERING_RIDE) { - Ride* ride = get_ride(peep->current_ride); - ride->num_riders++; - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; + auto ride = get_ride(peep->current_ride); + if (ride != nullptr) + { + ride->num_riders++; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; + } } window_invalidate_by_number(WC_PEEP, peep->sprite_index); @@ -761,9 +733,7 @@ void Peep::Pickup() { guest->RemoveFromRide(); } - Invalidate(); - - sprite_move(LOCATION_NULL, y, z, (rct_sprite*)this); + MoveTo(LOCATION_NULL, y, z); SetState(PEEP_STATE_PICKED); sub_state = 0; } @@ -773,8 +743,7 @@ void Peep::PickupAbort(int32_t old_x) if (state != PEEP_STATE_PICKED) return; - sprite_move(old_x, y, z + 8, (rct_sprite*)this); - Invalidate(); + MoveTo(old_x, y, z + 8); if (x != (int16_t)LOCATION_NULL) { @@ -792,25 +761,21 @@ void Peep::PickupAbort(int32_t old_x) // Returns true when a peep can be dropped at the given location. When apply is set to true the peep gets dropped. bool Peep::Place(TileCoordsXYZ location, bool apply) { - TileElement* tileElement = map_get_path_element_at(location.x, location.y, location.z); - - if (!tileElement) + auto* pathElement = map_get_path_element_at(location); + TileElement* tileElement = reinterpret_cast(pathElement); + if (!pathElement) { - tileElement = map_get_surface_element_at(location.x, location.y); + tileElement = reinterpret_cast(map_get_surface_element_at(location.x, location.y)); } if (!tileElement) return false; - CoordsXYZ destination = { location.x * 32, location.y * 32, location.z * 8 }; - // Set the coordinate of destination to be exactly // in the middle of a tile. - destination.x += 16; - destination.y += 16; - destination.z = tileElement->base_height * 8 + 16; + CoordsXYZ destination = { location.x * 32 + 16, location.y * 32 + 16, tileElement->base_height * 8 + 16 }; - if (!map_is_location_owned(location.x * 32, location.y * 32, destination.z)) + if (!map_is_location_owned(destination)) { gGameCommandErrorTitle = STR_ERR_CANT_PLACE_PERSON_HERE; return false; @@ -830,8 +795,7 @@ bool Peep::Place(TileCoordsXYZ location, bool apply) if (apply) { - sprite_move(destination.x, destination.y, destination.z, (rct_sprite*)this); - Invalidate(); + MoveTo(destination.x, destination.y, destination.z); SetState(PEEP_STATE_FALLING); action = PEEP_ACTION_NONE_2; special_sprite = 0; @@ -846,106 +810,11 @@ bool Peep::Place(TileCoordsXYZ location, bool apply) happiness_target = std::max(happiness_target - 10, 0); UpdateCurrentActionSpriteType(); } - - network_set_pickup_peep(game_command_playerid, nullptr); } return true; } -bool peep_pickup_command(uint32_t peepnum, int32_t x, int32_t y, int32_t z, int32_t action, bool apply) -{ - if (peepnum >= MAX_SPRITES) - { - log_error("Failed to pick up peep for sprite %d", peepnum); - return false; - } - - Peep* const peep = GET_PEEP(peepnum); - if (!peep || peep->sprite_identifier != SPRITE_IDENTIFIER_PEEP) - { - return false; - } - - switch (action) - { - case 0: // pickup - { - if (!peep_can_be_picked_up(peep)) - { - return false; - } - Peep* existing = network_get_pickup_peep(game_command_playerid); - if (existing) - { - // already picking up a peep - bool result = peep_pickup_command( - existing->sprite_index, network_get_pickup_peep_old_x(game_command_playerid), 0, 0, 1, apply); - if (existing == peep) - { - return result; - } - if (game_command_playerid == network_get_current_player_id()) - { - // prevent tool_cancel() - input_set_flag(INPUT_FLAG_TOOL_ACTIVE, false); - } - } - - if (apply) - { - network_set_pickup_peep(game_command_playerid, peep); - network_set_pickup_peep_old_x(game_command_playerid, peep->x); - peep->Pickup(); - } - } - break; - case 1: // cancel - if (apply) - { - // TODO: Verify if this is really needed or that we can use `peep` instead - Peep* const pickedUpPeep = network_get_pickup_peep(game_command_playerid); - if (pickedUpPeep) - { - pickedUpPeep->PickupAbort(x); - } - - network_set_pickup_peep(game_command_playerid, nullptr); - } - break; - case 2: // place - if (network_get_pickup_peep(game_command_playerid) != peep) - { - return false; - } - - if (!peep->Place({ x / 32, y / 32, z }, apply)) - { - return false; - } - break; - } - return true; -} - -void game_command_pickup_guest( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - int32_t peepnum = *eax; - int32_t x = *edi; - int32_t y = *ebp; - int32_t z = *edx; - int32_t action = *ecx; - if (peep_pickup_command(peepnum, x, y, z, action, *ebx & GAME_COMMAND_FLAG_APPLY)) - { - *ebx = 0; - } - else - { - *ebx = MONEY32_UNDEFINED; - } -} - /** * * rct2: 0x0069A535 @@ -1013,17 +882,15 @@ void Peep::UpdateFalling() if (action == PEEP_ACTION_DROWNING) { // Check to see if we are ready to drown. - int16_t actionX, actionY, xy_distance; - - UpdateAction(&actionX, &actionY, &xy_distance); + UpdateAction(); + Invalidate(); if (action == PEEP_ACTION_DROWNING) return; if (gConfigNotifications.guest_died) { - set_format_arg(0, rct_string_id, name_string_idx); - set_format_arg(2, uint32_t, id); - news_item_add_to_queue(NEWS_ITEM_BLANK, STR_NEWS_ITEM_GUEST_DROWNED, actionX | (actionY << 16)); + FormatNameTo(gCommonFormatArgs); + news_item_add_to_queue(NEWS_ITEM_BLANK, STR_NEWS_ITEM_GUEST_DROWNED, x | (y << 16)); } gParkRatingCasualtyPenalty = std::min(gParkRatingCasualtyPenalty + 25, 1000); @@ -1064,7 +931,6 @@ void Peep::UpdateFalling() if (height - 4 >= z && height < z + 20) { // Looks like we are drowning! - Invalidate(); MoveTo(x, y, height); auto guest = AsGuest(); @@ -1074,19 +940,18 @@ void Peep::UpdateFalling() peep_release_balloon(guest, height); } - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_DROWNING, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_DROWNING, PEEP_THOUGHT_ITEM_NONE); action = PEEP_ACTION_DROWNING; action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); peep_window_state_update(this); return; } } - int32_t map_height = tile_element_height(0xFFFF & x, 0xFFFF & y) & 0xFFFF; + int32_t map_height = tile_element_height({ x, y }); if (map_height < z || map_height - 4 > z) continue; saved_height = map_height; @@ -1100,7 +965,6 @@ void Peep::UpdateFalling() // This will be null if peep is falling if (saved_map == nullptr) { - Invalidate(); if (z <= 1) { // Remove peep if it has gone to the void @@ -1108,13 +972,10 @@ void Peep::UpdateFalling() return; } MoveTo(x, y, z - 2); - Invalidate(); return; } - Invalidate(); MoveTo(x, y, saved_height); - Invalidate(); next_x = x & 0xFFE0; next_y = y & 0xFFE0; @@ -1173,7 +1034,7 @@ void Peep::UpdatePicked() sub_state++; if (sub_state == 13) { - peep_insert_new_thought(this, PEEP_THOUGHT_TYPE_HELP, PEEP_THOUGHT_ITEM_NONE); + InsertNewThought(PEEP_THOUGHT_TYPE_HELP, PEEP_THOUGHT_ITEM_NONE); } } @@ -1273,7 +1134,7 @@ void Peep::Update() auto guest = AsGuest(); if (guest != nullptr) { - peep_easter_egg_peep_interactions(guest); + guest->UpdateEasterEggInteractions(); } } else @@ -1332,8 +1193,6 @@ void peep_problem_warnings_update() disgust_counter = 0, bathroom_counter = 0, vandalism_counter = 0; uint8_t* warning_throttle = gPeepWarningThrottle; - gRideCount = ride_get_count(); // refactor this to somewhere else - FOR_ALL_GUESTS (spriteIndex, peep) { if (peep->outside_of_park != 0 || peep->thoughts[0].freshness > 5) @@ -1352,7 +1211,7 @@ void peep_problem_warnings_update() break; } ride = get_ride(peep->guest_heading_to_ride_id); - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) + if (ride != nullptr && !ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) hunger_counter++; break; @@ -1363,7 +1222,7 @@ void peep_problem_warnings_update() break; } ride = get_ride(peep->guest_heading_to_ride_id); - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_SELLS_DRINKS)) + if (ride != nullptr && !ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_SELLS_DRINKS)) thirst_counter++; break; @@ -1374,7 +1233,7 @@ void peep_problem_warnings_update() break; } ride = get_ride(peep->guest_heading_to_ride_id); - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_BATHROOM)) + if (ride != nullptr && !ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_BATHROOM)) bathroom_counter++; break; @@ -1599,12 +1458,11 @@ void peep_applause() peep->action_frame = 0; peep->action_sprite_image_offset = 0; peep->UpdateCurrentActionSpriteType(); - peep->Invalidate(); } } // Play applause noise - audio_play_sound(SOUND_APPLAUSE, 0, context_get_width() / 2); + audio_play_sound(SoundId::Applause, 0, context_get_width() / 2); } /** @@ -1704,19 +1562,64 @@ static constexpr const uint8_t tshirt_colours[] = { }; // clang-format on +/** + * + * rct2: 0x699F5A + * al:thoughtType + * ah:thoughtArguments + * esi: peep + */ +void Peep::InsertNewThought(PeepThoughtType thoughtType, uint8_t thoughtArguments) +{ + PeepActionType newAction = PeepThoughtToActionMap[thoughtType].action; + if (newAction != PEEP_ACTION_NONE_2 && this->action >= PEEP_ACTION_NONE_1) + { + action = newAction; + action_frame = 0; + action_sprite_image_offset = 0; + UpdateCurrentActionSpriteType(); + } + + for (int32_t i = 0; i < PEEP_MAX_THOUGHTS; ++i) + { + rct_peep_thought* thought = &thoughts[i]; + // Remove the oldest thought by setting it to NONE. + if (thought->type == PEEP_THOUGHT_TYPE_NONE) + break; + + if (thought->type == thoughtType && thought->item == thoughtArguments) + { + // If the thought type has not changed then we need to move + // it to the top of the thought list. This is done by first removing the + // existing thought and placing it at the top. + if (i < PEEP_MAX_THOUGHTS - 2) + { + memmove(thought, thought + 1, sizeof(rct_peep_thought) * (PEEP_MAX_THOUGHTS - i - 1)); + } + break; + } + } + + memmove(&thoughts[1], &thoughts[0], sizeof(rct_peep_thought) * (PEEP_MAX_THOUGHTS - 1)); + + thoughts[0].type = thoughtType; + thoughts[0].item = thoughtArguments; + thoughts[0].freshness = 0; + thoughts[0].fresh_timeout = 0; + + window_invalidate_flags |= PEEP_INVALIDATE_PEEP_THOUGHTS; +} + /** * * rct2: 0x0069A05D */ -Peep* peep_generate(int32_t x, int32_t y, int32_t z) +Peep* Peep::Generate(const CoordsXYZ coords) { - if (gSpriteListCount[SPRITE_LIST_NULL] < 400) + if (gSpriteListCount[SPRITE_LIST_FREE] < 400) return nullptr; - Peep* peep = (Peep*)create_sprite(1); - - move_sprite_to_list((rct_sprite*)peep, SPRITE_LIST_PEEP * 2); - + Peep* peep = &create_sprite(SPRITE_IDENTIFIER_PEEP)->peep; peep->sprite_identifier = SPRITE_IDENTIFIER_PEEP; peep->sprite_type = PEEP_SPRITE_TYPE_NORMAL; peep->outside_of_park = 1; @@ -1735,16 +1638,13 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) peep->sprite_height_negative = spriteBounds[peep->action_sprite_type].sprite_height_negative; peep->sprite_height_positive = spriteBounds[peep->action_sprite_type].sprite_height_positive; + peep->MoveTo(coords.x, coords.y, coords.z); peep->sprite_direction = 0; - - sprite_move(x, y, z, (rct_sprite*)peep); - peep->Invalidate(); - peep->mass = (scenario_rand() & 0x1F) + 45; peep->path_check_optimisation = 0; - peep->interaction_ride_index = 0xFF; + peep->interaction_ride_index = RIDE_ID_NULL; peep->type = PEEP_TYPE_GUEST; - peep->previous_ride = 0xFF; + peep->previous_ride = RIDE_ID_NULL; peep->thoughts->type = PEEP_THOUGHT_TYPE_NONE; peep->window_invalidate_flags = 0; @@ -1777,13 +1677,13 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) peep->intensity = (intensityHighest << 4) | intensityLowest; - uint8_t nausea_tolerance = scenario_rand() & 0x7; + uint8_t nauseaTolerance = scenario_rand() & 0x7; if (gParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES) { - nausea_tolerance += 4; + nauseaTolerance += 4; } - peep->nausea_tolerance = nausea_tolerance_distribution[nausea_tolerance]; + peep->nausea_tolerance = nausea_tolerance_distribution[nauseaTolerance]; /* Scenario editor limits initial guest happiness to between 37..253. * To be on the safe side, assume the value could have been hacked @@ -1794,9 +1694,9 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) if (gGuestInitialHappiness == 0) peep->happiness = 128; /* Initial value will vary by -15..16 */ - int8_t happiness_delta = (scenario_rand() & 0x1F) - 15; + int8_t happinessDelta = (scenario_rand() & 0x1F) - 15; /* Adjust by the delta, clamping at min=0 and max=255. */ - peep->happiness = std::clamp(peep->happiness + happiness_delta, 0, PEEP_MAX_HAPPINESS); + peep->happiness = std::clamp(peep->happiness + happinessDelta, 0, PEEP_MAX_HAPPINESS); peep->happiness_target = peep->happiness; peep->nausea = 0; peep->nausea_target = 0; @@ -1806,18 +1706,18 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) * to any value 0..255. */ peep->hunger = gGuestInitialHunger; /* Initial value will vary by -15..16 */ - int8_t hunger_delta = (scenario_rand() & 0x1F) - 15; + int8_t hungerDelta = (scenario_rand() & 0x1F) - 15; /* Adjust by the delta, clamping at min=0 and max=255. */ - peep->hunger = std::clamp(peep->hunger + hunger_delta, 0, 255); + peep->hunger = std::clamp(peep->hunger + hungerDelta, 0, PEEP_MAX_HUNGER); /* Scenario editor limits initial guest thirst to between 37..253. * To be on the safe side, assume the value could have been hacked * to any value 0..255. */ peep->thirst = gGuestInitialThirst; /* Initial value will vary by -15..16 */ - int8_t thirst_delta = (scenario_rand() & 0x1F) - 15; + int8_t thirstDelta = (scenario_rand() & 0x1F) - 15; /* Adjust by the delta, clamping at min=0 and max=255. */ - peep->thirst = std::clamp(peep->thirst + thirst_delta, 0, 0xFF); + peep->thirst = std::clamp(peep->thirst + thirstDelta, 0, PEEP_MAX_THIRST); peep->toilet = 0; peep->time_to_consume = 0; @@ -1826,7 +1726,7 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) peep->no_of_rides = 0; std::fill_n(peep->ride_types_been_on, 16, 0x00); peep->id = gNextGuestNumber++; - peep->name_string_idx = STR_GUEST_X; + peep->name = nullptr; money32 cash = (scenario_rand() & 0x3) * 100 - 100 + gGuestInitialCash; if (cash < 0) @@ -1842,7 +1742,7 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) cash = 0; } - if (gGuestInitialCash == (money16)(uint16_t)0xFFFF) + if (gGuestInitialCash == MONEY16_UNDEFINED) { cash = 0; } @@ -1856,7 +1756,7 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) peep->pathfind_goal.direction = 0xFF; peep->item_standard_flags = 0; peep->item_extra_flags = 0; - peep->guest_heading_to_ride_id = 0xFF; + peep->guest_heading_to_ride_id = RIDE_ID_NULL; peep->litter_count = 0; peep->disgusting_count = 0; peep->vandalism_seen = 0; @@ -1872,11 +1772,11 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) peep->angriness = 0; peep->time_lost = 0; - uint8_t tshirt_colour = static_cast(scenario_rand() % std::size(tshirt_colours)); - peep->tshirt_colour = tshirt_colours[tshirt_colour]; + uint8_t tshirtColour = static_cast(scenario_rand() % std::size(tshirt_colours)); + peep->tshirt_colour = tshirt_colours[tshirtColour]; - uint8_t trousers_colour = static_cast(scenario_rand() % std::size(trouser_colours)); - peep->trousers_colour = trouser_colours[trousers_colour]; + uint8_t trousersColour = static_cast(scenario_rand() % std::size(trouser_colours)); + peep->trousers_colour = trouser_colours[trousersColour]; /* Minimum energy is capped at 32 and maximum at 128, so this initialises * a peep with approx 34%-100% energy. (65 - 32) / (128 - 32) ≈ 34% */ @@ -1884,10 +1784,6 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) peep->energy = energy; peep->energy_target = energy; - if (gParkFlags & PARK_FLAGS_SHOW_REAL_GUEST_NAMES) - { - peep_give_real_name(peep); - } peep_update_name_sort(peep); increment_guests_heading_for_park(); @@ -1895,145 +1791,261 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) return peep; } -/** - * rct2: 0x00698B0D - * peep.sprite_index (eax) - * thought.type (ebx) - * argument_1 (ecx & ebx) - * argument_2 (edx) - */ -void get_arguments_from_action(Peep* peep, uint32_t* argument_1, uint32_t* argument_2) +void Peep::FormatActionTo(void* argsV) const { - Ride* ride; - - switch (peep->state) + auto args = (uint8_t*)argsV; + switch (state) { case PEEP_STATE_FALLING: - *argument_1 = peep->action == PEEP_ACTION_DROWNING ? STR_DROWNING : STR_WALKING; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, action == PEEP_ACTION_DROWNING ? STR_DROWNING : STR_WALKING); break; case PEEP_STATE_1: - *argument_1 = STR_WALKING; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_WALKING); break; case PEEP_STATE_ON_RIDE: case PEEP_STATE_LEAVING_RIDE: case PEEP_STATE_ENTERING_RIDE: - *argument_1 = STR_ON_RIDE; - ride = get_ride(peep->current_ride); - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE)) - *argument_1 = STR_IN_RIDE; - *argument_1 |= ((uint32_t)ride->name << 16); - *argument_2 = ride->name_arguments; - break; - case PEEP_STATE_BUYING: - ride = get_ride(peep->current_ride); - *argument_1 = STR_AT_RIDE | ((uint32_t)ride->name << 16); - *argument_2 = ride->name_arguments; - break; - case PEEP_STATE_WALKING: - case PEEP_STATE_USING_BIN: - if (peep->guest_heading_to_ride_id != 0xFF) + { + auto ride = get_ride(current_ride); + if (ride != nullptr) { - ride = get_ride(peep->guest_heading_to_ride_id); - *argument_1 = STR_HEADING_FOR | ((uint32_t)ride->name << 16); - *argument_2 = ride->name_arguments; + set_format_arg_on( + args, 0, rct_string_id, ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE) ? STR_IN_RIDE : STR_ON_RIDE); + ride->FormatNameTo(args + 2); } else { - *argument_1 = (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK) ? STR_LEAVING_PARK : STR_WALKING; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_ON_RIDE); + set_format_arg_on(args, 2, rct_string_id, STR_NONE); + } + break; + } + case PEEP_STATE_BUYING: + { + set_format_arg_on(args, 0, rct_string_id, STR_AT_RIDE); + auto ride = get_ride(current_ride); + if (ride != nullptr) + { + ride->FormatNameTo(args + 2); + } + else + { + set_format_arg_on(args, 2, rct_string_id, STR_NONE); + } + break; + } + case PEEP_STATE_WALKING: + case PEEP_STATE_USING_BIN: + if (guest_heading_to_ride_id != RIDE_ID_NULL) + { + auto ride = get_ride(guest_heading_to_ride_id); + if (ride != nullptr) + { + set_format_arg_on(args, 0, rct_string_id, STR_HEADING_FOR); + ride->FormatNameTo(args + 2); + } + } + else + { + set_format_arg_on( + args, 0, rct_string_id, (peep_flags & PEEP_FLAGS_LEAVING_PARK) ? STR_LEAVING_PARK : STR_WALKING); } break; case PEEP_STATE_QUEUING_FRONT: case PEEP_STATE_QUEUING: - ride = get_ride(peep->current_ride); - *argument_1 = STR_QUEUING_FOR | ((uint32_t)ride->name << 16); - *argument_2 = ride->name_arguments; + { + auto ride = get_ride(current_ride); + if (ride != nullptr) + { + set_format_arg_on(args, 0, rct_string_id, STR_QUEUING_FOR); + ride->FormatNameTo(args + 2); + } break; + } case PEEP_STATE_SITTING: - *argument_1 = STR_SITTING; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_SITTING); break; case PEEP_STATE_WATCHING: - if (peep->current_ride != 0xFF) + if (current_ride != RIDE_ID_NULL) { - ride = get_ride(peep->current_ride); - *argument_1 = STR_WATCHING_RIDE | ((uint32_t)ride->name << 16); - *argument_2 = ride->name_arguments; - if (peep->current_seat & 0x1) - *argument_1 = STR_WATCHING_CONSTRUCTION_OF | ((uint32_t)ride->name << 16); - else - *argument_1 = STR_WATCHING_RIDE | ((uint32_t)ride->name << 16); + auto ride = get_ride(current_ride); + if (ride != nullptr) + { + set_format_arg_on( + args, 0, rct_string_id, (current_seat & 0x1) ? STR_WATCHING_CONSTRUCTION_OF : STR_WATCHING_RIDE); + ride->FormatNameTo(args + 2); + } } else { - *argument_1 = (peep->current_seat & 0x1) ? STR_WATCHING_NEW_RIDE_BEING_CONSTRUCTED : STR_LOOKING_AT_SCENERY; - *argument_2 = 0; + set_format_arg_on( + args, 0, rct_string_id, + (current_seat & 0x1) ? STR_WATCHING_NEW_RIDE_BEING_CONSTRUCTED : STR_LOOKING_AT_SCENERY); } break; case PEEP_STATE_PICKED: - *argument_1 = STR_SELECT_LOCATION; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_SELECT_LOCATION); break; case PEEP_STATE_PATROLLING: case PEEP_STATE_ENTERING_PARK: case PEEP_STATE_LEAVING_PARK: - *argument_1 = STR_WALKING; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_WALKING); break; case PEEP_STATE_MOWING: - *argument_1 = STR_MOWING_GRASS; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_MOWING_GRASS); break; case PEEP_STATE_SWEEPING: - *argument_1 = STR_SWEEPING_FOOTPATH; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_SWEEPING_FOOTPATH); break; case PEEP_STATE_WATERING: - *argument_1 = STR_WATERING_GARDENS; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_WATERING_GARDENS); break; case PEEP_STATE_EMPTYING_BIN: - *argument_1 = STR_EMPTYING_LITTER_BIN; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_EMPTYING_LITTER_BIN); break; case PEEP_STATE_ANSWERING: - if (peep->sub_state == 0) + if (sub_state == 0) { - *argument_1 = STR_WALKING; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_WALKING); } - else if (peep->sub_state == 1) + else if (sub_state == 1) { - *argument_1 = STR_ANSWERING_RADIO_CALL; - *argument_2 = 0; + set_format_arg_on(args, 0, rct_string_id, STR_ANSWERING_RADIO_CALL); } else { - ride = get_ride(peep->current_ride); - *argument_1 = STR_RESPONDING_TO_RIDE_BREAKDOWN_CALL | ((uint32_t)ride->name << 16); - *argument_2 = ride->name_arguments; + set_format_arg_on(args, 0, rct_string_id, STR_RESPONDING_TO_RIDE_BREAKDOWN_CALL); + auto ride = get_ride(current_ride); + if (ride != nullptr) + { + ride->FormatNameTo(args + 2); + } + else + { + set_format_arg_on(args, 2, rct_string_id, STR_NONE); + } } break; case PEEP_STATE_FIXING: - ride = get_ride(peep->current_ride); - *argument_1 = STR_FIXING_RIDE | ((uint32_t)ride->name << 16); - *argument_2 = ride->name_arguments; + { + set_format_arg_on(args, 0, rct_string_id, STR_FIXING_RIDE); + auto ride = get_ride(current_ride); + if (ride != nullptr) + { + ride->FormatNameTo(args + 2); + } + else + { + set_format_arg_on(args, 2, rct_string_id, STR_NONE); + } break; + } case PEEP_STATE_HEADING_TO_INSPECTION: - ride = get_ride(peep->current_ride); - *argument_1 = STR_HEADING_TO_RIDE_FOR_INSPECTION | ((uint32_t)ride->name << 16); - *argument_2 = ride->name_arguments; + { + set_format_arg_on(args, 0, rct_string_id, STR_HEADING_TO_RIDE_FOR_INSPECTION); + auto ride = get_ride(current_ride); + if (ride != nullptr) + { + ride->FormatNameTo(args + 2); + } + else + { + set_format_arg_on(args, 2, rct_string_id, STR_NONE); + } break; + } case PEEP_STATE_INSPECTING: - ride = get_ride(peep->current_ride); - *argument_1 = STR_INSPECTING_RIDE | ((uint32_t)ride->name << 16); - *argument_2 = ride->name_arguments; + { + set_format_arg_on(args, 0, rct_string_id, STR_INSPECTING_RIDE); + auto ride = get_ride(current_ride); + if (ride != nullptr) + { + ride->FormatNameTo(args + 2); + } + else + { + set_format_arg_on(args, 2, rct_string_id, STR_NONE); + } break; + } } } +size_t Peep::FormatNameTo(void* argsV) const +{ + auto args = (uint8_t*)argsV; + if (name == nullptr) + { + if (type == PeepType::PEEP_TYPE_STAFF) + { + static constexpr const rct_string_id staffNames[] = { + STR_HANDYMAN_X, + STR_MECHANIC_X, + STR_SECURITY_GUARD_X, + STR_ENTERTAINER_X, + }; + + auto staffNameIndex = staff_type; + if (staffNameIndex > sizeof(staffNames)) + { + staffNameIndex = 0; + } + + set_format_arg_on(args, 0, rct_string_id, staffNames[staffNameIndex]); + set_format_arg_on(args, 2, uint32_t, id); + return sizeof(rct_string_id) + sizeof(uint32_t); + } + else if (gParkFlags & PARK_FLAGS_SHOW_REAL_GUEST_NAMES) + { + auto realNameStringId = get_real_name_string_id_from_id(id); + set_format_arg_on(args, 0, rct_string_id, realNameStringId); + return sizeof(rct_string_id); + } + else + { + set_format_arg_on(args, 0, rct_string_id, STR_GUEST_X); + set_format_arg_on(args, 2, uint32_t, id); + return sizeof(rct_string_id) + sizeof(uint32_t); + } + } + else + { + set_format_arg_on(args, 0, rct_string_id, STR_STRING); + set_format_arg_on(args, 2, const char*, name); + return sizeof(rct_string_id) + sizeof(const char*); + } +} + +std::string Peep::GetName() const +{ + uint8_t args[32]{}; + FormatNameTo(args); + return format_string(STR_STRINGID, args); +} + +bool Peep::SetName(const std::string_view& value) +{ + if (value.empty()) + { + std::free(name); + name = nullptr; + return true; + } + else + { + auto newNameMemory = (char*)std::malloc(value.size() + 1); + if (newNameMemory != nullptr) + { + std::memcpy(newNameMemory, value.data(), value.size()); + newNameMemory[value.size()] = '\0'; + std::free(name); + name = newNameMemory; + return true; + } + } + return false; +} + /** * rct2: 0x00698342 * thought.item (eax) @@ -2041,24 +2053,30 @@ void get_arguments_from_action(Peep* peep, uint32_t* argument_1, uint32_t* argum * argument_1 (esi & ebx) * argument_2 (esi+2) */ -void peep_thought_set_format_args(rct_peep_thought* thought) +void peep_thought_set_format_args(const rct_peep_thought* thought) { set_format_arg(0, rct_string_id, PeepThoughts[thought->type]); uint8_t flags = PeepThoughtToActionMap[thought->type].flags; if (flags & 1) { - Ride* ride = get_ride(thought->item); - set_format_arg(2, rct_string_id, ride->name); - set_format_arg(4, uint32_t, ride->name_arguments); + auto ride = get_ride(thought->item); + if (ride != nullptr) + { + ride->FormatNameTo(gCommonFormatArgs + 2); + } + else + { + set_format_arg(2, rct_string_id, STR_NONE); + } } else if (flags & 2) { - set_format_arg(2, rct_string_id, ShopItemStringIds[thought->item].singular); + set_format_arg(2, rct_string_id, ShopItems[thought->item].Naming.Singular); } else if (flags & 4) { - set_format_arg(2, rct_string_id, ShopItemStringIds[thought->item].indefinite); + set_format_arg(2, rct_string_id, ShopItems[thought->item].Naming.Indefinite); } } @@ -2203,81 +2221,6 @@ int32_t get_peep_face_sprite_large(Peep* peep) return face_sprite_large[get_face_sprite_offset(peep)]; } -/** - * - * rct2: 0x0069A5A0 - * tests if a peep's name matches a cheat code, normally returns using a register flag - */ -int32_t peep_check_easteregg_name(int32_t index, Peep* peep) -{ - char buffer[256]; - - format_string(buffer, 256, peep->name_string_idx, &peep->id); - return _stricmp(buffer, gPeepEasterEggNames[index]) == 0; -} - -int32_t peep_get_easteregg_name_id(Peep* peep) -{ - char buffer[256]; - - format_string(buffer, 256, peep->name_string_idx, &peep->id); - - for (uint32_t i = 0; i < std::size(gPeepEasterEggNames); i++) - if (_stricmp(buffer, gPeepEasterEggNames[i]) == 0) - return static_cast(i); - - return -1; -} - -/** - * - * rct2: 0x699F5A - * al:thought_type - * ah:thought_arguments - * esi: peep - */ -void peep_insert_new_thought(Peep* peep, PeepThoughtType thought_type, uint8_t thought_arguments) -{ - PeepActionType action = PeepThoughtToActionMap[thought_type].action; - if (action != PEEP_ACTION_NONE_2 && peep->action >= PEEP_ACTION_NONE_1) - { - peep->action = action; - peep->action_frame = 0; - peep->action_sprite_image_offset = 0; - peep->UpdateCurrentActionSpriteType(); - peep->Invalidate(); - } - - for (int32_t i = 0; i < PEEP_MAX_THOUGHTS; ++i) - { - rct_peep_thought* thought = &peep->thoughts[i]; - // Remove the oldest thought by setting it to NONE. - if (thought->type == PEEP_THOUGHT_TYPE_NONE) - break; - - if (thought->type == thought_type && thought->item == thought_arguments) - { - // If the thought type has not changed then we need to move - // it to the top of the thought list. This is done by first removing the - // existing thought and placing it at the top. - if (i < PEEP_MAX_THOUGHTS - 2) - { - memmove(thought, thought + 1, sizeof(rct_peep_thought) * (PEEP_MAX_THOUGHTS - i - 1)); - } - break; - } - } - - memmove(&peep->thoughts[1], &peep->thoughts[0], sizeof(rct_peep_thought) * (PEEP_MAX_THOUGHTS - 1)); - - peep->thoughts[0].type = thought_type; - peep->thoughts[0].item = thought_arguments; - peep->thoughts[0].freshness = 0; - peep->thoughts[0].fresh_timeout = 0; - - peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_THOUGHTS; -} - void peep_set_map_tooltip(Peep* peep) { if (peep->type == PEEP_TYPE_GUEST) @@ -2285,24 +2228,14 @@ void peep_set_map_tooltip(Peep* peep) set_map_tooltip_format_arg( 0, rct_string_id, (peep->peep_flags & PEEP_FLAGS_TRACKING) ? STR_TRACKED_GUEST_MAP_TIP : STR_GUEST_MAP_TIP); set_map_tooltip_format_arg(2, uint32_t, get_peep_face_sprite_small(peep)); - set_map_tooltip_format_arg(6, rct_string_id, peep->name_string_idx); - set_map_tooltip_format_arg(8, uint32_t, peep->id); - - uint32_t arg0 = 0, arg1 = 0; - get_arguments_from_action(peep, &arg0, &arg1); - set_map_tooltip_format_arg(12, uint32_t, arg0); - set_map_tooltip_format_arg(16, uint32_t, arg1); + auto nameArgLen = peep->FormatNameTo(gMapTooltipFormatArgs + 6); + peep->FormatActionTo(gMapTooltipFormatArgs + 6 + nameArgLen); } else { set_map_tooltip_format_arg(0, rct_string_id, STR_STAFF_MAP_TIP); - set_map_tooltip_format_arg(2, rct_string_id, peep->name_string_idx); - set_map_tooltip_format_arg(4, uint32_t, peep->id); - - uint32_t arg0 = 0, arg1 = 0; - get_arguments_from_action(peep, &arg0, &arg1); - set_map_tooltip_format_arg(8, uint32_t, arg0); - set_map_tooltip_format_arg(12, uint32_t, arg1); + auto nameArgLen = peep->FormatNameTo(gMapTooltipFormatArgs + 2); + peep->FormatActionTo(gMapTooltipFormatArgs + 2 + nameArgLen); } } @@ -2383,9 +2316,8 @@ static bool peep_update_queue_position(Peep* peep, uint8_t previous_action) } } - int16_t xy_dist, x, y; if (peep->action < PEEP_ACTION_NONE_1) - peep->UpdateAction(&x, &y, &xy_dist); + peep->UpdateAction(); if (peep->action != PEEP_ACTION_NONE_2) return true; @@ -2416,8 +2348,6 @@ static void peep_return_to_centre_of_tile(Peep* peep) static void peep_interact_with_entrance(Peep* peep, int16_t x, int16_t y, TileElement* tile_element, uint8_t& pathing_result) { uint8_t entranceType = tile_element->AsEntrance()->GetEntranceType(); - ride_id_t rideIndex = tile_element->AsEntrance()->GetRideIndex(); - auto ride = get_ride(rideIndex); // Store some details to determine when to override the default // behaviour (defined below) for when staff attempt to enter a ride @@ -2444,6 +2374,11 @@ static void peep_interact_with_entrance(Peep* peep, int16_t x, int16_t y, TileEl if (entranceType == ENTRANCE_TYPE_RIDE_ENTRANCE) { + auto rideIndex = tile_element->AsEntrance()->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + auto guest = peep->AsGuest(); if (guest == nullptr) { @@ -2504,10 +2439,8 @@ static void peep_interact_with_entrance(Peep* peep, int16_t x, int16_t y, TileEl peep->time_in_queue = 0; if (peep->peep_flags & PEEP_FLAGS_TRACKING) { - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + auto nameArgLen = peep->FormatNameTo(gCommonFormatArgs); + ride->FormatNameTo(gCommonFormatArgs + nameArgLen); if (gConfigNotifications.guest_queuing_for_ride) { news_item_add_to_queue(NEWS_ITEM_PEEP_ON_RIDE, STR_PEEP_TRACKING_PEEP_JOINED_QUEUE_FOR_X, peep->sprite_index); @@ -2561,17 +2494,13 @@ static void peep_interact_with_entrance(Peep* peep, int16_t x, int16_t y, TileEl peep->destination_x += CoordsDirectionDelta[peep->direction].x; peep->destination_y += CoordsDirectionDelta[peep->direction].y; peep->destination_tolerance = 9; - peep->Invalidate(); - sprite_move(x, y, peep->z, (rct_sprite*)peep); - peep->Invalidate(); - + peep->MoveTo(x, y, peep->z); peep->SetState(PEEP_STATE_LEAVING_PARK); peep->var_37 = 0; if (peep->peep_flags & PEEP_FLAGS_TRACKING) { - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); + peep->FormatNameTo(gCommonFormatArgs); if (gConfigNotifications.guest_left_park) { news_item_add_to_queue(NEWS_ITEM_PEEP_ON_RIDE, STR_PEEP_TRACKING_LEFT_PARK, peep->sprite_index); @@ -2614,6 +2543,8 @@ static void peep_interact_with_entrance(Peep* peep, int16_t x, int16_t y, TileEl TileElement* nextTileElement = map_get_first_element_at(next_x / 32, next_y / 32); do { + if (nextTileElement == nullptr) + break; if (nextTileElement->GetType() != TILE_ELEMENT_TYPE_PATH) continue; @@ -2704,10 +2635,7 @@ static void peep_interact_with_entrance(Peep* peep, int16_t x, int16_t y, TileEl peep->destination_x += CoordsDirectionDelta[peep->direction].x; peep->destination_y += CoordsDirectionDelta[peep->direction].y; peep->destination_tolerance = 7; - - peep->Invalidate(); - sprite_move(x, y, peep->z, (rct_sprite*)peep); - peep->Invalidate(); + peep->MoveTo(x, y, peep->z); } } @@ -2726,9 +2654,7 @@ static void peep_footpath_move_forward(Peep* peep, int16_t x, int16_t y, TileEle if (peep->type == PEEP_TYPE_STAFF) { - peep->Invalidate(); - sprite_move(x, y, z, (rct_sprite*)peep); - peep->Invalidate(); + peep->MoveTo(x, y, z); return; } @@ -2736,7 +2662,7 @@ static void peep_footpath_move_forward(Peep* peep, int16_t x, int16_t y, TileEle // Advance the vandalised tiles by 1 uint8_t vandalisedTiles = (peep->vandalism_seen * 2) & 0x3F; - if (vandalism == true) + if (vandalism) { // Add one more to the vandalised tiles vandalisedTiles |= 1; @@ -2745,7 +2671,7 @@ static void peep_footpath_move_forward(Peep* peep, int16_t x, int16_t y, TileEle { if ((scenario_rand() & 0xFFFF) <= 10922) { - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_VANDALISM, PEEP_THOUGHT_ITEM_NONE); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_VANDALISM, PEEP_THOUGHT_ITEM_NONE); peep->happiness_target = std::max(0, peep->happiness_target - 17); } vandalThoughtTimeout = 3; @@ -2793,7 +2719,7 @@ static void peep_footpath_move_forward(Peep* peep, int16_t x, int16_t y, TileEle if (crowded >= 10 && peep->state == PEEP_STATE_WALKING && (scenario_rand() & 0xFFFF) <= 21845) { - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CROWDED, PEEP_THOUGHT_ITEM_NONE); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_CROWDED, PEEP_THOUGHT_ITEM_NONE); peep->happiness_target = std::max(0, peep->happiness_target - 14); } @@ -2819,7 +2745,7 @@ static void peep_footpath_move_forward(Peep* peep, int16_t x, int16_t y, TileEle if (total_sick >= 3 && (scenario_rand() & 0xFFFF) <= 10922) { - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_PATH_DISGUSTING, PEEP_THOUGHT_ITEM_NONE); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_PATH_DISGUSTING, PEEP_THOUGHT_ITEM_NONE); peep->happiness_target = std::max(0, peep->happiness_target - 17); // Reset disgusting time peep->disgusting_count |= 0xC0; @@ -2845,16 +2771,14 @@ static void peep_footpath_move_forward(Peep* peep, int16_t x, int16_t y, TileEle if (total_litter >= 3 && (scenario_rand() & 0xFFFF) <= 10922) { - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_BAD_LITTER, PEEP_THOUGHT_ITEM_NONE); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_BAD_LITTER, PEEP_THOUGHT_ITEM_NONE); peep->happiness_target = std::max(0, peep->happiness_target - 17); // Reset litter time peep->litter_count |= 0xC0; } } - peep->Invalidate(); - sprite_move(x, y, z, (rct_sprite*)peep); - peep->Invalidate(); + peep->MoveTo(x, y, z); } /** @@ -2872,7 +2796,7 @@ static void peep_interact_with_path(Peep* peep, int16_t x, int16_t y, TileElemen } int16_t z = tile_element->base_height * 8; - if (map_is_location_owned(x, y, z)) + if (map_is_location_owned({ x, y, z })) { if (peep->outside_of_park == 1) { @@ -2948,10 +2872,8 @@ static void peep_interact_with_path(Peep* peep, int16_t x, int16_t y, TileElemen peep->time_in_queue = 0; if (peep->peep_flags & PEEP_FLAGS_TRACKING) { - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + auto nameArgLen = peep->FormatNameTo(gCommonFormatArgs); + ride->FormatNameTo(gCommonFormatArgs + nameArgLen); if (gConfigNotifications.guest_queuing_for_ride) { news_item_add_to_queue( @@ -2994,9 +2916,8 @@ static void peep_interact_with_path(Peep* peep, int16_t x, int16_t y, TileElemen static bool peep_interact_with_shop(Peep* peep, int16_t x, int16_t y, TileElement* tile_element) { ride_id_t rideIndex = tile_element->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) + auto ride = get_ride(rideIndex); + if (ride == nullptr || !ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) return false; auto guest = peep->AsGuest(); @@ -3058,10 +2979,8 @@ static bool peep_interact_with_shop(Peep* peep, int16_t x, int16_t y, TileElemen ride->cur_num_customers++; if (peep->peep_flags & PEEP_FLAGS_TRACKING) { - set_format_arg(0, rct_string_id, peep->name_string_idx); - set_format_arg(2, uint32_t, peep->id); - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + auto nameArgLen = peep->FormatNameTo(gCommonFormatArgs); + ride->FormatNameTo(gCommonFormatArgs + nameArgLen); rct_string_id string_id = ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IN_RIDE) ? STR_PEEP_TRACKING_PEEP_IS_IN_X : STR_PEEP_TRACKING_PEEP_IS_ON_X; if (gConfigNotifications.guest_used_facility) @@ -3134,8 +3053,8 @@ void Peep::PerformNextAction(uint8_t& pathing_result, TileElement*& tile_result) return; } - int16_t actionX, actionY, xy_dist; - if (!UpdateAction(&actionX, &actionY, &xy_dist)) + std::optional loc; + if (!(loc = UpdateAction())) { pathing_result |= PATHING_DESTINATION_REACHED; uint8_t result = 0; @@ -3154,20 +3073,19 @@ void Peep::PerformNextAction(uint8_t& pathing_result, TileElement*& tile_result) if (result != 0) return; - if (!UpdateAction(&actionX, &actionY, &xy_dist)) + if (!(loc = UpdateAction())) return; } - if ((actionX & 0xFFE0) == next_x && (actionY & 0xFFE0) == next_y) + auto newLoc = *loc; + if ((newLoc.x & 0xFFE0) == next_x && (newLoc.y & 0xFFE0) == next_y) { - int16_t height = GetZOnSlope(actionX, actionY); - Invalidate(); - MoveTo(actionX, actionY, height); - Invalidate(); + int16_t height = GetZOnSlope(newLoc.x, newLoc.y); + MoveTo(newLoc.x, newLoc.y, height); return; } - if (actionX < 32 || actionY < 32 || actionX >= gMapSizeUnits || actionY >= gMapSizeUnits) + if (newLoc.x < 32 || newLoc.y < 32 || newLoc.x >= gMapSizeUnits || newLoc.y >= gMapSizeUnits) { if (outside_of_park == 1) { @@ -3177,7 +3095,9 @@ void Peep::PerformNextAction(uint8_t& pathing_result, TileElement*& tile_result) return; } - TileElement* tileElement = map_get_first_element_at(actionX / 32, actionY / 32); + TileElement* tileElement = map_get_first_element_at(newLoc.x / 32, newLoc.y / 32); + if (tileElement == nullptr) + return; int16_t base_z = std::max(0, (z / 8) - 2); int16_t top_z = (z / 8) + 1; @@ -3192,13 +3112,13 @@ void Peep::PerformNextAction(uint8_t& pathing_result, TileElement*& tile_result) if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH) { - peep_interact_with_path(this, actionX, actionY, tileElement); + peep_interact_with_path(this, newLoc.x, newLoc.y, tileElement); tile_result = tileElement; return; } else if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK) { - if (peep_interact_with_shop(this, actionX, actionY, tileElement)) + if (peep_interact_with_shop(this, newLoc.x, newLoc.y, tileElement)) { tile_result = tileElement; return; @@ -3206,7 +3126,7 @@ void Peep::PerformNextAction(uint8_t& pathing_result, TileElement*& tile_result) } else if (tileElement->GetType() == TILE_ELEMENT_TYPE_ENTRANCE) { - peep_interact_with_entrance(this, actionX, actionY, tileElement, pathing_result); + peep_interact_with_entrance(this, newLoc.x, newLoc.y, tileElement, pathing_result); tile_result = tileElement; return; } @@ -3214,8 +3134,7 @@ void Peep::PerformNextAction(uint8_t& pathing_result, TileElement*& tile_result) if (type == PEEP_TYPE_STAFF || (GetNextIsSurface())) { - int16_t height = abs(tile_element_height(actionX, actionY) - z); - + int16_t height = abs(tile_element_height(newLoc) - z); if (height <= 3 || (type == PEEP_TYPE_STAFF && height <= 32)) { interaction_ride_index = 0xFF; @@ -3225,20 +3144,20 @@ void Peep::PerformNextAction(uint8_t& pathing_result, TileElement*& tile_result) SetState(PEEP_STATE_1); } - if (!map_is_location_in_park({ actionX & 0xFFE0, actionY & 0xFFE0 })) + if (!map_is_location_in_park(newLoc)) { peep_return_to_centre_of_tile(this); return; } - tileElement = map_get_surface_element_at({ actionX, actionY }); - if (tileElement == nullptr) + auto surfaceElement = map_get_surface_element_at(newLoc); + if (surfaceElement == nullptr) { peep_return_to_centre_of_tile(this); return; } - int16_t water_height = tileElement->AsSurface()->GetWaterHeight(); + int16_t water_height = surfaceElement->GetWaterHeight(); if (water_height) { peep_return_to_centre_of_tile(this); @@ -3256,15 +3175,13 @@ void Peep::PerformNextAction(uint8_t& pathing_result, TileElement*& tile_result) } // The peep is on a surface and not on a path - next_x = actionX & 0xFFE0; - next_y = actionY & 0xFFE0; - next_z = tileElement->base_height; + next_x = newLoc.x & 0xFFE0; + next_y = newLoc.y & 0xFFE0; + next_z = surfaceElement->base_height; SetNextFlags(0, false, true); - height = GetZOnSlope(actionX, actionY); - Invalidate(); - MoveTo(actionX, actionY, height); - Invalidate(); + height = GetZOnSlope(newLoc.x, newLoc.y); + MoveTo(newLoc.x, newLoc.y, height); return; } } @@ -3291,147 +3208,6 @@ void peep_reset_pathfind_goal(Peep* peep) peep->pathfind_goal.direction = 0xFF; } -static bool peep_has_valid_xy(Peep* peep) -{ - if (peep->x != LOCATION_NULL) - { - if (peep->x < (256 * 32) && peep->y < (256 * 32)) - { - return true; - } - } - - return false; -} - -using easter_egg_function = void (*)(Guest* peep, Guest* otherPeep); - -static void peep_apply_easter_egg_to_nearby_guests(Guest* peep, easter_egg_function easter_egg) -{ - if (!peep_has_valid_xy(peep)) - return; - - uint16_t spriteIndex = sprite_get_first_in_quadrant(peep->x, peep->y); - if (spriteIndex == SPRITE_INDEX_NULL) - return; - - auto otherPeep = GET_PEEP(spriteIndex); - for (; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = otherPeep->next_in_quadrant) - { - otherPeep = GET_PEEP(spriteIndex); - auto otheerGuest = otherPeep->AsGuest(); - if (otheerGuest != nullptr) - { - auto zDiff = std::abs(otherPeep->z - peep->z); - if (zDiff <= 32) - { - easter_egg(peep, otheerGuest); - } - } - } -} - -static void peep_give_passing_peeps_purple_clothes([[maybe_unused]] Guest* peep, Guest* otherPeep) -{ - otherPeep->tshirt_colour = COLOUR_BRIGHT_PURPLE; - otherPeep->trousers_colour = COLOUR_BRIGHT_PURPLE; - invalidate_sprite_2((rct_sprite*)otherPeep); -} - -static void peep_give_passing_peeps_pizza(Guest* peep, Guest* otherPeep) -{ - if ((otherPeep->item_standard_flags & PEEP_ITEM_PIZZA)) - return; - - otherPeep->item_standard_flags |= PEEP_ITEM_PIZZA; - - int32_t peepDirection = (peep->sprite_direction >> 3) ^ 2; - int32_t otherPeepOppositeDirection = otherPeep->sprite_direction >> 3; - if (peepDirection == otherPeepOppositeDirection) - { - if (otherPeep->action == PEEP_ACTION_NONE_1 || otherPeep->action == PEEP_ACTION_NONE_2) - { - peep->Invalidate(); - otherPeep->action = PEEP_ACTION_WAVE_2; - otherPeep->action_frame = 0; - otherPeep->action_sprite_image_offset = 0; - otherPeep->UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite*)otherPeep); - } - } - invalidate_sprite_2((rct_sprite*)otherPeep); -} - -static void peep_make_passing_peeps_sick(Guest* peep, Guest* otherPeep) -{ - if (peep == otherPeep) - return; - if (otherPeep->state != PEEP_STATE_WALKING) - return; - - if (otherPeep->action == PEEP_ACTION_NONE_1 || otherPeep->action == PEEP_ACTION_NONE_2) - { - otherPeep->action = PEEP_ACTION_THROW_UP; - otherPeep->action_frame = 0; - otherPeep->action_sprite_image_offset = 0; - otherPeep->UpdateCurrentActionSpriteType(); - invalidate_sprite_2((rct_sprite*)otherPeep); - } -} - -static void peep_give_passing_peeps_ice_cream(Guest* peep, Guest* otherPeep) -{ - if (peep == otherPeep) - return; - if (otherPeep->item_standard_flags & PEEP_ITEM_ICE_CREAM) - return; - - otherPeep->item_standard_flags |= PEEP_ITEM_ICE_CREAM; - otherPeep->UpdateSpriteType(); -} - -/** - * - * rct2: 0x0068FD3A - */ -static void peep_easter_egg_peep_interactions(Guest* peep) -{ - if (peep->peep_flags & PEEP_FLAGS_PURPLE) - { - peep_apply_easter_egg_to_nearby_guests(peep, &peep_give_passing_peeps_purple_clothes); - } - - if (peep->peep_flags & PEEP_FLAGS_PIZZA) - { - peep_apply_easter_egg_to_nearby_guests(peep, &peep_give_passing_peeps_pizza); - } - - if (peep->peep_flags & PEEP_FLAGS_CONTAGIOUS) - { - peep_apply_easter_egg_to_nearby_guests(peep, &peep_make_passing_peeps_sick); - } - - if (peep->peep_flags & PEEP_FLAGS_JOY) - { - if (scenario_rand() <= 1456) - { - if (peep->action == PEEP_ACTION_NONE_1 || peep->action == PEEP_ACTION_NONE_2) - { - peep->action = PEEP_ACTION_JOY; - peep->action_frame = 0; - peep->action_sprite_image_offset = 0; - peep->UpdateCurrentActionSpriteType(); - peep->Invalidate(); - } - } - } - - if (peep->peep_flags & PEEP_FLAGS_ICE_CREAM) - { - peep_apply_easter_egg_to_nearby_guests(peep, &peep_give_passing_peeps_ice_cream); - } -} - /** * Gets the height including the bit depending on how far up the slope the peep * is. @@ -3444,7 +3220,7 @@ int32_t Peep::GetZOnSlope(int32_t tile_x, int32_t tile_y) if (GetNextIsSurface()) { - return tile_element_height(tile_x, tile_y) & 0xFFFF; + return tile_element_height({ tile_x, tile_y }); } int32_t height = next_z * 8; @@ -3452,14 +3228,10 @@ int32_t Peep::GetZOnSlope(int32_t tile_x, int32_t tile_y) return height + map_height_from_slope({ tile_x, tile_y }, slope, GetNextIsSloped()); } -/** - * - * rct2: 0x0069C483 - */ -static void peep_give_real_name(Peep* peep) +rct_string_id get_real_name_string_id_from_id(uint32_t id) { // Generate a name_string_idx from the peep id using bit twiddling - uint16_t ax = (uint16_t)(peep->id + 0xF0B); + uint16_t ax = (uint16_t)(id + 0xF0B); uint16_t dx = 0; static constexpr uint16_t twiddlingBitOrder[] = { 4, 9, 3, 7, 5, 8, 2, 1, 6, 0, 12, 11, 13, 10 }; for (size_t i = 0; i < std::size(twiddlingBitOrder); i++) @@ -3476,7 +3248,7 @@ static void peep_give_real_name(Peep* peep) } dx /= 4; dx += REAL_NAME_START; - peep->name_string_idx = dx; + return dx; } static int32_t peep_compare(const void* sprite_index_a, const void* sprite_index_b) @@ -3490,46 +3262,30 @@ static int32_t peep_compare(const void* sprite_index_a, const void* sprite_index return peep_a->type - peep_b->type; } - // Simple ID comparison for when both peeps use a number or a generated name - const bool both_numbers - = (peep_a->name_string_idx >= 767 && peep_a->name_string_idx <= 771 && peep_b->name_string_idx >= 767 - && peep_b->name_string_idx <= 771); - if (both_numbers) + if (peep_a->name == nullptr && peep_b->name == nullptr) { - return peep_a->id - peep_b->id; - } - const bool both_have_generated_names - = (peep_a->name_string_idx >= REAL_NAME_START && peep_a->name_string_idx <= REAL_NAME_END - && peep_b->name_string_idx >= REAL_NAME_START && peep_b->name_string_idx <= REAL_NAME_END); - if (both_have_generated_names) - { - rct_string_id peep_a_format = peep_a->name_string_idx + REAL_NAME_START; - rct_string_id peep_b_format = peep_b->name_string_idx + REAL_NAME_START; - - uint16_t peep_a_name = (peep_a_format % std::size(real_names)); - uint16_t peep_b_name = (peep_b_format % std::size(real_names)); - - if (peep_a_name == peep_b_name) + if (gParkFlags & PARK_FLAGS_SHOW_REAL_GUEST_NAMES) { - uint16_t peep_a_initial = ((peep_a_format >> 10) % std::size(real_name_initials)); - uint16_t peep_b_initial = ((peep_b_format >> 10) % std::size(real_name_initials)); - return peep_a_initial - peep_b_initial; + // Potentially could find a more optional way of sorting dynamic real names } else { - return peep_a_name - peep_b_name; + // Simple ID comparison for when both peeps use a number or a generated name + return peep_a->id - peep_b->id; } } - // At least one of them has a custom name assigned // Compare their names as strings - utf8 name_a[256]; - utf8 name_b[256]; - uint32_t peepIndex = peep_a->id; - format_string(name_a, 256, peep_a->name_string_idx, &peepIndex); - peepIndex = peep_b->id; - format_string(name_b, 256, peep_b->name_string_idx, &peepIndex); - return strlogicalcmp(name_a, name_b); + uint8_t args[32]{}; + + char nameA[256]{}; + peep_a->FormatNameTo(args); + format_string(nameA, sizeof(nameA), STR_STRINGID, args); + + char nameB[256]{}; + peep_b->FormatNameTo(args); + format_string(nameB, sizeof(nameB), STR_STRINGID, args); + return strlogicalcmp(nameA, nameB); } /** @@ -3657,161 +3413,18 @@ void peep_update_names(bool realNames) if (realNames) { gParkFlags |= PARK_FLAGS_SHOW_REAL_GUEST_NAMES; - Peep* peep; - uint16_t spriteIndex; - FOR_ALL_GUESTS (spriteIndex, peep) - { - if (peep->name_string_idx == STR_GUEST_X) - { - peep_give_real_name(peep); - } - } + // Peep names are now dynamic } else { gParkFlags &= ~PARK_FLAGS_SHOW_REAL_GUEST_NAMES; - Peep* peep; - uint16_t spriteIndex; - FOR_ALL_GUESTS (spriteIndex, peep) - { - if (peep->name_string_idx >= REAL_NAME_START && peep->name_string_idx <= REAL_NAME_END) - { - peep->name_string_idx = STR_GUEST_X; - } - } + // Peep names are now dynamic } peep_sort(); gfx_invalidate_screen(); } -void peep_handle_easteregg_name(Peep* peep) -{ - peep->peep_flags &= ~PEEP_FLAGS_WAVING; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_KATIE_BRAYSHAW, peep)) - { - peep->peep_flags |= PEEP_FLAGS_WAVING; - } - - peep->peep_flags &= ~PEEP_FLAGS_PHOTO; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_CHRIS_SAWYER, peep)) - { - peep->peep_flags |= PEEP_FLAGS_PHOTO; - } - - peep->peep_flags &= ~PEEP_FLAGS_PAINTING; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_SIMON_FOSTER, peep)) - { - peep->peep_flags |= PEEP_FLAGS_PAINTING; - } - - peep->peep_flags &= ~PEEP_FLAGS_WOW; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_JOHN_WARDLEY, peep)) - { - peep->peep_flags |= PEEP_FLAGS_WOW; - } - - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_MELANIE_WARN, peep)) - { - peep->happiness = 250; - peep->happiness_target = 250; - peep->energy = 127; - peep->energy_target = 127; - peep->nausea = 0; - peep->nausea_target = 0; - } - - peep->peep_flags &= ~PEEP_FLAGS_LITTER; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_LISA_STIRLING, peep)) - { - peep->peep_flags |= PEEP_FLAGS_LITTER; - } - - peep->peep_flags &= ~PEEP_FLAGS_LOST; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_DONALD_MACRAE, peep)) - { - peep->peep_flags |= PEEP_FLAGS_LOST; - } - - peep->peep_flags &= ~PEEP_FLAGS_HUNGER; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_KATHERINE_MCGOWAN, peep)) - { - peep->peep_flags |= PEEP_FLAGS_HUNGER; - } - - peep->peep_flags &= ~PEEP_FLAGS_BATHROOM; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_FRANCES_MCGOWAN, peep)) - { - peep->peep_flags |= PEEP_FLAGS_BATHROOM; - } - - peep->peep_flags &= ~PEEP_FLAGS_CROWDED; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_CORINA_MASSOURA, peep)) - { - peep->peep_flags |= PEEP_FLAGS_CROWDED; - } - - peep->peep_flags &= ~PEEP_FLAGS_HAPPINESS; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_CAROL_YOUNG, peep)) - { - peep->peep_flags |= PEEP_FLAGS_HAPPINESS; - } - - peep->peep_flags &= ~PEEP_FLAGS_NAUSEA; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_MIA_SHERIDAN, peep)) - { - peep->peep_flags |= PEEP_FLAGS_NAUSEA; - } - - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_KATIE_RODGER, peep)) - { - peep->peep_flags |= PEEP_FLAGS_LEAVING_PARK; - peep->peep_flags &= ~PEEP_FLAGS_PARK_ENTRANCE_CHOSEN; - } - - peep->peep_flags &= ~PEEP_FLAGS_PURPLE; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_EMMA_GARRELL, peep)) - { - peep->peep_flags |= PEEP_FLAGS_PURPLE; - } - - peep->peep_flags &= ~PEEP_FLAGS_PIZZA; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_JOANNE_BARTON, peep)) - { - peep->peep_flags |= PEEP_FLAGS_PIZZA; - } - - peep->peep_flags &= ~PEEP_FLAGS_CONTAGIOUS; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_FELICITY_ANDERSON, peep)) - { - peep->peep_flags |= PEEP_FLAGS_CONTAGIOUS; - } - - peep->peep_flags &= ~PEEP_FLAGS_JOY; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_KATIE_SMITH, peep)) - { - peep->peep_flags |= PEEP_FLAGS_JOY; - } - - peep->peep_flags &= ~PEEP_FLAGS_ANGRY; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_EILIDH_BELL, peep)) - { - peep->peep_flags |= PEEP_FLAGS_ANGRY; - } - - peep->peep_flags &= ~PEEP_FLAGS_ICE_CREAM; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_NANCY_STILLWAGON, peep)) - { - peep->peep_flags |= PEEP_FLAGS_ICE_CREAM; - } - - peep->peep_flags &= ~PEEP_FLAGS_HERE_WE_ARE; - if (peep_check_easteregg_name(EASTEREGG_PEEP_NAME_DAVID_ELLIS, peep)) - { - peep->peep_flags |= PEEP_FLAGS_HERE_WE_ARE; - } -} - #if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 void pathfind_logging_enable([[maybe_unused]] Peep* peep) { @@ -3913,7 +3526,9 @@ static void peep_release_balloon(Guest* peep, int16_t spawn_height) */ void Peep::RemoveFromQueue() { - Ride* ride = get_ride(current_ride); + auto ride = get_ride(current_ride); + if (ride == nullptr) + return; auto& station = ride->stations[current_ride_station]; // Make sure we don't underflow, building while paused might reset it to 0 where peeps have diff --git a/src/openrct2/peep/Peep.h b/src/openrct2/peep/Peep.h index 1fb258cc98..4474244de6 100644 --- a/src/openrct2/peep/Peep.h +++ b/src/openrct2/peep/Peep.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,6 +11,7 @@ #define _PEEP_H_ #include "../common.h" +#include "../core/Optional.hpp" #include "../rct12/RCT12.h" #include "../ride/Ride.h" #include "../ride/RideTypes.h" @@ -35,6 +36,10 @@ #define PEEP_MIN_ENERGY 32 #define PEEP_MAX_ENERGY 128 #define PEEP_MAX_ENERGY_TARGET 255 // Oddly, this differs from max energy! +#define PEEP_MAX_HUNGER 255 +#define PEEP_MAX_BATHROOM 255 +#define PEEP_MAX_NAUSEA 255 +#define PEEP_MAX_THIRST 255 struct TileElement; struct Ride; @@ -511,6 +516,7 @@ enum PeepInvalidate PEEP_INVALIDATE_PEEP_2 = 1 << 2, PEEP_INVALIDATE_PEEP_INVENTORY = 1 << 3, PEEP_INVALIDATE_STAFF_STATS = 1 << 4, + PEEP_INVALIDATE_PEEP_ACTION = 1 << 5, // Currently set only when guest_heading_to_ride_id is changed }; // Flags used by peep_should_go_on_ride() @@ -533,16 +539,16 @@ struct Staff; struct Peep : rct_sprite_common { - rct_string_id name_string_idx; // 0x22 - uint16_t next_x; // 0x24 - uint16_t next_y; // 0x26 - uint8_t next_z; // 0x28 - uint8_t next_flags; // 0x29 - uint8_t outside_of_park; // 0x2A - PeepState state; // 0x2B - uint8_t sub_state; // 0x2C - PeepSpriteType sprite_type; // 0x2D - PeepType type; // 0x2E + char* name; + uint16_t next_x; // 0x24 + uint16_t next_y; // 0x26 + uint8_t next_z; // 0x28 + uint8_t next_flags; // 0x29 + uint8_t outside_of_park; // 0x2A + PeepState state; // 0x2B + uint8_t sub_state; // 0x2C + PeepSpriteType sprite_type; // 0x2D + PeepType type; // 0x2E union { uint8_t staff_type; // 0x2F @@ -609,7 +615,7 @@ struct Peep : rct_sprite_common union { uint8_t maze_last_edge; // 0x78 - uint8_t direction; // Direction ? + Direction direction; // Direction ? }; uint8_t interaction_ride_index; uint16_t time_in_queue; // 0x7A @@ -693,8 +699,8 @@ public: // Peep Staff* AsStaff(); void Update(); - bool UpdateAction(int16_t* actionX, int16_t* actionY, int16_t* xy_distance); - bool UpdateAction(); + std::optional UpdateAction(int16_t& xy_distance); + std::optional UpdateAction(); void SetState(PeepState new_state); void Remove(); void Invalidate(); @@ -709,8 +715,14 @@ public: // Peep void Pickup(); void PickupAbort(int32_t old_x); bool Place(TileCoordsXYZ location, bool apply); + static Peep* Generate(const CoordsXYZ coords); void RemoveFromQueue(); void RemoveFromRide(); + void InsertNewThought(PeepThoughtType thought_type, uint8_t thought_arguments); + void FormatActionTo(void* args) const; + size_t FormatNameTo(void* args) const; + std::string GetName() const; + bool SetName(const std::string_view& value); // TODO: Make these private again when done refactoring public: // Peep @@ -752,8 +764,8 @@ public: bool UpdateWalkingFindBin(); void SpendMoney(money16& peep_expend_type, money32 amount); void SpendMoney(money32 amount); - void SetHasRidden(Ride* ride); - bool HasRidden(Ride* ride) const; + void SetHasRidden(const Ride* ride); + bool HasRidden(const Ride* ride) const; void SetHasRiddenRideType(int32_t rideType); bool HasRiddenRideType(int32_t rideType) const; int32_t HasFoodStandardFlag() const; @@ -767,6 +779,9 @@ public: void CheckCantFindExit(); bool DecideAndBuyItem(Ride* ride, int32_t shopItem, money32 price); void SetSpriteType(PeepSpriteType new_sprite_type); + void HandleEasterEggName(); + int32_t GetEasterEggNameId() const; + void UpdateEasterEggInteractions(); private: void UpdateRide(); @@ -798,7 +813,14 @@ private: void UpdateRideShopApproach(); void UpdateRideShopInteract(); void UpdateRideShopLeave(); - + using easter_egg_function = void (Guest::*)(Guest* otherGuest); + int32_t CheckEasterEggName(int32_t index) const; + void ApplyEasterEggToNearbyGuests(easter_egg_function easter_egg); + bool GuestHasValidXY() const; + void GivePassingPeepsPurpleClothes(Guest* passingPeep); + void GivePassingPeepsPizza(Guest* passingPeep); + void MakePassingPeepsSick(Guest* passingPeep); + void GivePassingPeepsIceCream(Guest* passingPeep); Ride* FindBestRideToGoOn(); std::bitset FindRidesToGoOn(); }; @@ -834,6 +856,8 @@ private: void UpdateHeadingToInspect(); }; +static_assert(sizeof(Peep) <= 256); + struct rct_sprite_bounds { uint8_t sprite_width; // 0x00 @@ -940,27 +964,15 @@ void peep_stop_crowd_noise(); void peep_update_crowd_noise(); void peep_update_days_in_queue(); void peep_applause(); -Peep* peep_generate(int32_t x, int32_t y, int32_t z); -void get_arguments_from_action(Peep* peep, uint32_t* argument_1, uint32_t* argument_2); -void peep_thought_set_format_args(rct_peep_thought* thought); +void peep_thought_set_format_args(const rct_peep_thought* thought); int32_t get_peep_face_sprite_small(Peep* peep); int32_t get_peep_face_sprite_large(Peep* peep); -int32_t peep_check_easteregg_name(int32_t index, Peep* peep); -int32_t peep_get_easteregg_name_id(Peep* peep); -bool peep_pickup_command(uint32_t peepnum, int32_t x, int32_t y, int32_t z, int32_t action, bool apply); void game_command_pickup_guest( int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); void peep_sprite_remove(Peep* peep); void peep_window_state_update(Peep* peep); void peep_decrement_num_riders(Peep* peep); -/** - * rct2: 0x699F5A - * al:thought_type - * ah:thought_arguments - * esi: peep - */ -void peep_insert_new_thought(Peep* peep, PeepThoughtType thought_type, uint8_t thought_arguments); void peep_set_map_tooltip(Peep* peep); @@ -970,11 +982,8 @@ void peep_sort(); void peep_update_names(bool realNames); void guest_set_name(uint16_t spriteIndex, const char* name); -void peep_handle_easteregg_name(Peep* peep); -void game_command_set_guest_name( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -int32_t peep_pathfind_choose_direction(TileCoordsXYZ loc, Peep* peep); +Direction peep_pathfind_choose_direction(TileCoordsXYZ loc, Peep* peep); void peep_reset_pathfind_goal(Peep* peep); bool is_valid_path_z_and_direction(TileElement* tileElement, int32_t currentZ, int32_t currentDirection); @@ -999,4 +1008,6 @@ void increment_guests_heading_for_park(); void decrement_guests_in_park(); void decrement_guests_heading_for_park(); +rct_string_id get_real_name_string_id_from_id(uint32_t id); + #endif diff --git a/src/openrct2/peep/PeepData.cpp b/src/openrct2/peep/PeepData.cpp index c2d0d5b8e8..1bf7b8dc57 100644 --- a/src/openrct2/peep/PeepData.cpp +++ b/src/openrct2/peep/PeepData.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/peep/Staff.cpp b/src/openrct2/peep/Staff.cpp index 89e9e0351b..1f5dc7de70 100644 --- a/src/openrct2/peep/Staff.cpp +++ b/src/openrct2/peep/Staff.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,6 +12,7 @@ #include "../Context.h" #include "../Game.h" #include "../Input.h" +#include "../actions/StaffHireNewAction.hpp" #include "../actions/StaffSetOrdersAction.hpp" #include "../audio/audio.h" #include "../config/Config.h" @@ -81,410 +82,46 @@ void staff_reset_modes() staff_update_greyed_patrol_areas(); } -static inline void staff_autoposition_new_staff_member(Peep* newPeep) -{ - // Find a location to place new staff member - - newPeep->state = PEEP_STATE_FALLING; - - int16_t x, y, z; - uint32_t count = 0; - uint16_t sprite_index; - Peep* guest = nullptr; - TileElement* guest_tile = nullptr; - - // Count number of walking guests - FOR_ALL_GUESTS (sprite_index, guest) - { - if (guest->state == PEEP_STATE_WALKING) - { - // Check the walking guest's tile. Only count them if they're on a path tile. - guest_tile = map_get_path_element_at(guest->next_x / 32, guest->next_y / 32, guest->next_z); - if (guest_tile != nullptr) - ++count; - } - } - - if (count > 0) - { - // Place staff at a random guest - uint32_t rand = scenario_rand_max(count); - FOR_ALL_GUESTS (sprite_index, guest) - { - if (guest->state == PEEP_STATE_WALKING) - { - guest_tile = map_get_path_element_at(guest->next_x / 32, guest->next_y / 32, guest->next_z); - if (guest_tile != nullptr) - { - if (rand == 0) - break; - --rand; - } - } - } - - x = guest->x; - y = guest->y; - z = guest->z; - } - else - { - // No walking guests; pick random park entrance - if (gParkEntrances.size() > 0) - { - auto rand = scenario_rand_max((uint32_t)gParkEntrances.size()); - const auto& entrance = gParkEntrances[rand]; - auto dir = entrance.direction; - x = entrance.x; - y = entrance.y; - z = entrance.z; - x += 16 + ((dir & 1) == 0 ? ((dir & 2) ? 32 : -32) : 0); - y += 16 + ((dir & 1) == 1 ? ((dir & 2) ? -32 : 32) : 0); - } - else - { - // User must pick a location - newPeep->state = PEEP_STATE_PICKED; - x = newPeep->x; - y = newPeep->y; - z = newPeep->z; - } - } - - sprite_move(x, y, z + 16, (rct_sprite*)newPeep); - invalidate_sprite_2((rct_sprite*)newPeep); -} - -static money32 staff_hire_new_staff_member( - uint8_t staff_type, uint8_t flags, int16_t command_x, int16_t command_y, int16_t command_z, int32_t autoposition, - int32_t* newPeep_sprite_index) -{ - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_WAGES; - gCommandPosition.x = command_x; - gCommandPosition.y = command_y; - gCommandPosition.z = command_z; - - if (gSpriteListCount[SPRITE_LIST_NULL] < 400) - { - gGameCommandErrorText = STR_TOO_MANY_PEOPLE_IN_GAME; - return MONEY32_UNDEFINED; - } - - // Staff type matches STAFF_TYPE enum, but ENTERTAINER onwards will match - // the ENTERTAINER_COSTUME enum - uint8_t entertainerType = ENTERTAINER_COSTUME_PANDA; - if (staff_type >= STAFF_TYPE_ENTERTAINER) - { - entertainerType = staff_type - STAFF_TYPE_ENTERTAINER; - if (entertainerType >= ENTERTAINER_COSTUME_COUNT) - { - // Invalid entertainer costume - return MONEY32_UNDEFINED; - } - - uint32_t availableCostumes = staff_get_available_entertainer_costumes(); - if (!(availableCostumes & (1 << entertainerType))) - { - // Entertainer costume unavailable - return MONEY32_UNDEFINED; - } - - staff_type = STAFF_TYPE_ENTERTAINER; - } - - int32_t i; - for (i = 0; i < STAFF_MAX_COUNT; ++i) - { - if (!(gStaffModes[i] & 1)) - break; - } - - if (i == STAFF_MAX_COUNT) - { - gGameCommandErrorText = STR_TOO_MANY_STAFF_IN_GAME; - return MONEY32_UNDEFINED; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - int32_t newStaffId = i; - const rct_sprite_bounds* spriteBounds; - Peep* newPeep = &(create_sprite(flags)->peep); - - if (newPeep == nullptr) - { - gGameCommandErrorText = STR_TOO_MANY_PEOPLE_IN_GAME; - return MONEY32_UNDEFINED; - } - - if (flags == 0) - { - sprite_remove((rct_sprite*)newPeep); - } - else - { - move_sprite_to_list((rct_sprite*)newPeep, SPRITE_LIST_PEEP * 2); - - newPeep->sprite_identifier = 1; - newPeep->window_invalidate_flags = 0; - newPeep->action = PEEP_ACTION_NONE_2; - newPeep->special_sprite = 0; - newPeep->action_sprite_image_offset = 0; - newPeep->no_action_frame_num = 0; - newPeep->action_sprite_type = PEEP_ACTION_SPRITE_TYPE_NONE; - newPeep->path_check_optimisation = 0; - newPeep->type = PEEP_TYPE_STAFF; - newPeep->outside_of_park = 0; - newPeep->peep_flags = 0; - newPeep->paid_to_enter = 0; - newPeep->paid_on_rides = 0; - newPeep->paid_on_food = 0; - newPeep->paid_on_souvenirs = 0; - - if (staff_type == STAFF_TYPE_HANDYMAN) - newPeep->staff_orders = STAFF_ORDERS_SWEEPING | STAFF_ORDERS_WATER_FLOWERS | STAFF_ORDERS_EMPTY_BINS; - else if (staff_type == STAFF_TYPE_MECHANIC) - newPeep->staff_orders = STAFF_ORDERS_INSPECT_RIDES | STAFF_ORDERS_FIX_RIDES; - else - newPeep->staff_orders = 0; - - uint16_t idSearchSpriteIndex; - Peep* idSearchPeep; - - // We search for the first available id for a given staff type - uint32_t newStaffIndex = 0; - for (;;) - { - bool found = false; - ++newStaffIndex; - - FOR_ALL_STAFF (idSearchSpriteIndex, idSearchPeep) - { - if (idSearchPeep->staff_type != staff_type) - continue; - - if (idSearchPeep->id == newStaffIndex) - { - found = true; - break; - } - } - - if (!found) - break; - } - - newPeep->id = newStaffIndex; - newPeep->staff_type = staff_type; - - static constexpr const rct_string_id staffNames[] = { - STR_HANDYMAN_X, - STR_MECHANIC_X, - STR_SECURITY_GUARD_X, - STR_ENTERTAINER_X, - }; - - /* rct2: 0x009929FC */ - static constexpr const PeepSpriteType spriteTypes[] = { - PEEP_SPRITE_TYPE_HANDYMAN, - PEEP_SPRITE_TYPE_MECHANIC, - PEEP_SPRITE_TYPE_SECURITY, - PEEP_SPRITE_TYPE_ENTERTAINER_PANDA, - }; - - PeepSpriteType sprite_type = spriteTypes[staff_type]; - if (staff_type == STAFF_TYPE_ENTERTAINER) - { - sprite_type = static_cast(PEEP_SPRITE_TYPE_ENTERTAINER_PANDA + entertainerType); - } - newPeep->name_string_idx = staffNames[staff_type]; - newPeep->sprite_type = sprite_type; - - spriteBounds = g_peep_animation_entries[sprite_type].sprite_bounds; - newPeep->sprite_width = spriteBounds->sprite_width; - newPeep->sprite_height_negative = spriteBounds->sprite_height_negative; - newPeep->sprite_height_positive = spriteBounds->sprite_height_positive; - - if (autoposition) - { - staff_autoposition_new_staff_member(newPeep); - } - else - { - newPeep->state = PEEP_STATE_PICKED; - - sprite_move(newPeep->x, newPeep->y, newPeep->z, (rct_sprite*)newPeep); - invalidate_sprite_2((rct_sprite*)newPeep); - } - - newPeep->time_in_park = gDateMonthsElapsed; - newPeep->pathfind_goal.x = 0xFF; - newPeep->pathfind_goal.y = 0xFF; - newPeep->pathfind_goal.z = 0xFF; - newPeep->pathfind_goal.direction = 0xFF; - - uint8_t colour = staff_get_colour(staff_type); - newPeep->tshirt_colour = colour; - newPeep->trousers_colour = colour; - - // Staff energy determines their walking speed - newPeep->energy = 0x60; - newPeep->energy_target = 0x60; - newPeep->staff_mowing_timeout = 0; - - peep_update_name_sort(newPeep); - - newPeep->staff_id = newStaffId; - - gStaffModes[newStaffId] = STAFF_MODE_WALK; - - for (i = 0; i < STAFF_PATROL_AREA_SIZE; i++) - { - gStaffPatrolAreas[newStaffId * STAFF_PATROL_AREA_SIZE + i] = 0; - } - } - - *newPeep_sprite_index = newPeep->sprite_index; - } - return 0; -} - /** - * - * rct2: 0x006BEFA1 + * Hires a new staff member of the given type. */ -void game_command_hire_new_staff_member( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, - [[maybe_unused]] int32_t* ebp) +bool staff_hire_new_member(STAFF_TYPE staffType, ENTERTAINER_COSTUME entertainerType) { - *ebx = staff_hire_new_staff_member( - (*ebx & 0xFF00) >> 8, *ebx & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFFFF, (*ebx & 0xFF0000) >> 16, edi); -} - -/** - * - * rct2: 0x006C09D1 - */ -void game_command_set_staff_patrol( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, - [[maybe_unused]] int32_t* ebp) -{ - if (*ebx & GAME_COMMAND_FLAG_APPLY) - { - int32_t x = *eax; - int32_t y = *ecx; - uint16_t sprite_id = *edx; - if (sprite_id >= MAX_SPRITES) - { - *ebx = MONEY32_UNDEFINED; - log_warning("Invalid sprite id %u", sprite_id); - return; - } - rct_sprite* sprite = get_sprite(sprite_id); - if (sprite->generic.sprite_identifier != SPRITE_IDENTIFIER_PEEP || sprite->peep.type != PEEP_TYPE_STAFF) - { - *ebx = MONEY32_UNDEFINED; - log_warning("Invalid type of sprite %u for game command", sprite_id); - return; - } - Peep* peep = &sprite->peep; - int32_t patrolOffset = peep->staff_id * STAFF_PATROL_AREA_SIZE; - - staff_toggle_patrol_area(peep->staff_id, x, y); - - int32_t ispatrolling = 0; - for (int32_t i = 0; i < 128; i++) - { - ispatrolling |= gStaffPatrolAreas[patrolOffset + i]; - } - - gStaffModes[peep->staff_id] &= ~2; - if (ispatrolling) - { - gStaffModes[peep->staff_id] |= 2; - } - - for (int32_t y2 = 0; y2 < 4; y2++) - { - for (int32_t x2 = 0; x2 < 4; x2++) - { - map_invalidate_tile_full((x & 0x1F80) + (x2 * 32), (y & 0x1F80) + (y2 * 32)); - } - } - staff_update_greyed_patrol_areas(); - } - *ebx = 0; -} - -/** - * - * rct2: 0x006C0B83 - */ -void game_command_fire_staff_member( - [[maybe_unused]] int32_t* eax, int32_t* ebx, [[maybe_unused]] int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, - [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_WAGES; - if (*ebx & GAME_COMMAND_FLAG_APPLY) - { - window_close_by_class(WC_FIRE_PROMPT); - uint16_t sprite_id = *edx; - if (sprite_id >= MAX_SPRITES) - { - log_warning("Invalid game command, sprite_id = %u", sprite_id); - *ebx = MONEY32_UNDEFINED; - return; - } - Peep* peep = &get_sprite(sprite_id)->peep; - if (peep->sprite_identifier != SPRITE_IDENTIFIER_PEEP || peep->type != PEEP_TYPE_STAFF) - { - log_warning( - "Invalid game command, peep->sprite_identifier = %u, peep->type = %u", peep->sprite_identifier, peep->type); - *ebx = MONEY32_UNDEFINED; - return; - } - peep_sprite_remove(peep); - } - *ebx = 0; -} - -/** - * Hires a new staff member of the given type. If the hire cannot be completed (eg. the maximum number of staff is reached or - * there are too many peeps) it returns SPRITE_INDEX_NULL. - */ -uint16_t hire_new_staff_member(uint8_t staffType) -{ - gGameCommandErrorTitle = STR_CANT_HIRE_NEW_STAFF; - - int32_t command_x, ebx, command_y, command_z, esi, new_sprite_index, ebp; - command_y = command_z = esi = new_sprite_index = ebp = 0; - command_x = 0x8000; - - int32_t autoposition = gConfigGeneral.auto_staff_placement; + bool autoPosition = gConfigGeneral.auto_staff_placement; if (gInputPlaceObjectModifier & PLACE_OBJECT_MODIFIER_SHIFT_Z) { - autoposition = autoposition ^ 1; + autoPosition = autoPosition ^ 1; } - ebx = autoposition << 16 | staffType << 8 | GAME_COMMAND_FLAG_APPLY; + uint32_t staffOrders = 0; - game_command_callback = game_command_callback_hire_new_staff_member; - int32_t result = game_do_command_p( - GAME_COMMAND_HIRE_NEW_STAFF_MEMBER, &command_x, &ebx, &command_y, &command_z, &esi, &new_sprite_index, &ebp); - - if (result == MONEY32_UNDEFINED) - return SPRITE_INDEX_NULL; - - if ((staffType == STAFF_TYPE_HANDYMAN) && gConfigGeneral.handymen_mow_default) + if (staffType == STAFF_TYPE_HANDYMAN) { - Peep* newPeep = GET_PEEP(new_sprite_index); - uint8_t newOrders = newPeep->staff_orders | STAFF_ORDERS_MOWING; - - auto staffSetOrdersAction = StaffSetOrdersAction(new_sprite_index, newOrders); - GameActions::Execute(&staffSetOrdersAction); + staffOrders = STAFF_ORDERS_SWEEPING | STAFF_ORDERS_WATER_FLOWERS | STAFF_ORDERS_EMPTY_BINS; + if (gConfigGeneral.handymen_mow_default) + { + staffOrders |= STAFF_ORDERS_MOWING; + } + } + else if (staffType == STAFF_TYPE_MECHANIC) + { + staffOrders = STAFF_ORDERS_INSPECT_RIDES | STAFF_ORDERS_FIX_RIDES; } - return new_sprite_index; + auto hireStaffAction = StaffHireNewAction(autoPosition, staffType, entertainerType, staffOrders); + hireStaffAction.SetCallback([=](const GameAction*, const StaffHireNewActionResult* res) -> void { + if (res->Error != GA_ERROR::OK) + return; + + // Open window for new staff. + Peep* peep = &get_sprite(res->peepSriteIndex)->peep; + auto intent = Intent(WC_PEEP); + intent.putExtra(INTENT_EXTRA_PEEP, peep); + context_open_intent(&intent); + }); + + auto res = GameActions::Execute(&hireStaffAction); + return res->Error == GA_ERROR::OK; } /** @@ -535,7 +172,7 @@ static bool staff_is_location_in_patrol_area(Peep* peep, int32_t x, int32_t y) bool staff_is_location_in_patrol(Peep* staff, int32_t x, int32_t y) { // Check if location is in the park - if (!map_is_location_owned_or_has_rights(x, y)) + if (!map_is_location_owned_or_has_rights({ x, y })) return false; // Check if staff has patrol area @@ -597,7 +234,7 @@ bool staff_can_ignore_wide_flag(Peep* staff, int32_t x, int32_t y, uint8_t z, Ti uint8_t total = 0; uint8_t pathcount = 0; uint8_t widecount = 0; - for (int32_t adjac_dir = 0; adjac_dir <= 3; adjac_dir++) + for (Direction adjac_dir : ALL_DIRECTIONS) { int32_t adjac_x = x + CoordsDirectionDelta[adjac_dir].x; int32_t adjac_y = y + CoordsDirectionDelta[adjac_dir].y; @@ -631,6 +268,8 @@ bool staff_can_ignore_wide_flag(Peep* staff, int32_t x, int32_t y, uint8_t z, Ti /* Search through all adjacent map elements */ TileElement* test_element = map_get_first_element_at(adjac_x / 32, adjac_y / 32); + if (test_element == nullptr) + return false; bool pathfound = false; bool widefound = false; do @@ -837,7 +476,8 @@ static uint8_t staff_handyman_direction_to_nearest_litter(Peep* peep) int16_t nextZ = ((peep->z + 8) & 0xFFF0) / 8; TileElement* tileElement = map_get_first_element_at(nextTile.x / 32, nextTile.y / 32); - + if (tileElement == nullptr) + return 0xFF; do { if (tileElement->base_height != nextZ) @@ -852,6 +492,8 @@ static uint8_t staff_handyman_direction_to_nearest_litter(Peep* peep) nextTile.y = (peep->y & 0xFFE0) + CoordsDirectionDelta[nextDirection].y; tileElement = map_get_first_element_at(nextTile.x / 32, nextTile.y / 32); + if (tileElement == nullptr) + return 0xFF; do { @@ -874,18 +516,18 @@ static uint8_t staff_handyman_direction_to_uncut_grass(Peep* peep, uint8_t valid { if (!(peep->GetNextIsSurface())) { - TileElement* tileElement = map_get_surface_element_at({ peep->next_x, peep->next_y }); + auto surfaceElement = map_get_surface_element_at({ peep->next_x, peep->next_y }); - if (peep->next_z != tileElement->base_height) - return 0xFF; + if (peep->next_z != surfaceElement->base_height) + return INVALID_DIRECTION; if (peep->GetNextIsSloped()) { - if (tileElement->AsSurface()->GetSlope() != PathSlopeToLandSlope[peep->GetNextDirection()]) - return 0xFF; + if (surfaceElement->GetSlope() != PathSlopeToLandSlope[peep->GetNextDirection()]) + return INVALID_DIRECTION; } - else if (tileElement->AsSurface()->GetSlope() != TILE_ELEMENT_SLOPE_FLAT) - return 0xFF; + else if (surfaceElement->GetSlope() != TILE_ELEMENT_SLOPE_FLAT) + return INVALID_DIRECTION; } uint8_t chosenDirection = scenario_rand() & 0x3; @@ -904,7 +546,7 @@ static uint8_t staff_handyman_direction_to_uncut_grass(Peep* peep, uint8_t valid if (chosenTile.x > 0x1FFF || chosenTile.y > 0x1FFF) continue; - auto surfaceElement = map_get_surface_element_at(chosenTile)->AsSurface(); + auto surfaceElement = map_get_surface_element_at(chosenTile); if (surfaceElement != nullptr) { if (std::abs(surfaceElement->base_height - peep->next_z) <= 2) @@ -916,7 +558,7 @@ static uint8_t staff_handyman_direction_to_uncut_grass(Peep* peep, uint8_t valid } } } - return 0xFF; + return INVALID_DIRECTION; } /** @@ -963,13 +605,13 @@ static bool staff_path_finding_handyman(Peep* peep) litterDirection = staff_handyman_direction_to_nearest_litter(peep); } - uint8_t direction = 0xFF; + Direction direction = INVALID_DIRECTION; if (litterDirection == 0xFF && (peep->staff_orders & STAFF_ORDERS_MOWING) && peep->staff_mowing_timeout >= 12) { direction = staff_handyman_direction_to_uncut_grass(peep, validDirections); } - if (direction == 0xFF) + if (direction == INVALID_DIRECTION) { if (peep->GetNextIsSurface()) { @@ -977,12 +619,12 @@ static bool staff_path_finding_handyman(Peep* peep) } else { - TileElement* tileElement = map_get_path_element_at(peep->next_x / 32, peep->next_y / 32, peep->next_z); + auto* pathElement = map_get_path_element_at({ peep->next_x / 32, peep->next_y / 32, peep->next_z }); - if (tileElement == nullptr) + if (pathElement == nullptr) return true; - uint8_t pathDirections = (tileElement->AsPath()->GetEdges() & validDirections) & 0xF; + uint8_t pathDirections = (pathElement->GetEdges() & validDirections) & 0xF; if (pathDirections == 0) { direction = staff_handyman_direction_rand_surface(peep, validDirections); @@ -1007,7 +649,7 @@ static bool staff_path_finding_handyman(Peep* peep) } } - if (chooseRandom == true) + if (chooseRandom) { do { @@ -1064,16 +706,16 @@ static uint8_t staff_direction_surface(Peep* peep, uint8_t initialDirection) direction &= 3; - if (fence_in_the_way(peep->next_x, peep->next_y, peep->next_z, peep->next_z + 4, direction) == true) + if (fence_in_the_way(peep->next_x, peep->next_y, peep->next_z, peep->next_z + 4, direction)) continue; - if (fence_in_the_way(peep->next_x, peep->next_y, peep->next_z, peep->next_z + 4, direction_reverse(direction)) == true) + if (fence_in_the_way(peep->next_x, peep->next_y, peep->next_z, peep->next_z + 4, direction_reverse(direction))) continue; LocationXY16 chosenTile = { static_cast(peep->next_x + CoordsDirectionDelta[direction].x), static_cast(peep->next_y + CoordsDirectionDelta[direction].y) }; - if (map_surface_is_blocked(chosenTile.x, chosenTile.y) == false) + if (!map_surface_is_blocked(chosenTile.x, chosenTile.y)) { return direction; } @@ -1145,10 +787,10 @@ static uint8_t staff_mechanic_direction_path_rand(Peep* peep, uint8_t pathDirect * * rct2: 0x006C0121 */ -static uint8_t staff_mechanic_direction_path(Peep* peep, uint8_t validDirections, TileElement* pathElement) +static uint8_t staff_mechanic_direction_path(Peep* peep, uint8_t validDirections, PathElement* pathElement) { - uint8_t direction = 0xFF; - uint8_t pathDirections = pathElement->AsPath()->GetEdges(); + Direction direction = INVALID_DIRECTION; + uint8_t pathDirections = pathElement->GetEdges(); pathDirections &= validDirections; if (pathDirections == 0) @@ -1210,14 +852,14 @@ static uint8_t staff_mechanic_direction_path(Peep* peep, uint8_t validDirections pathfind_logging_enable(peep); #endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 - int32_t pathfindDirection = peep_pathfind_choose_direction( + Direction pathfindDirection = peep_pathfind_choose_direction( { peep->next_x / 32, peep->next_y / 32, peep->next_z }, peep); #if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 pathfind_logging_disable(); #endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1 - if (pathfindDirection == -1) + if (pathfindDirection == INVALID_DIRECTION) { /* Heuristic search failed for all directions. * Reset the pathfind_goal - this means that the pathfind_history @@ -1241,14 +883,14 @@ static uint8_t staff_mechanic_direction_path(Peep* peep, uint8_t validDirections static bool staff_path_finding_mechanic(Peep* peep) { uint8_t validDirections = staff_get_valid_patrol_directions(peep, peep->next_x, peep->next_y); - uint8_t direction = 0xFF; + Direction direction = INVALID_DIRECTION; if (peep->GetNextIsSurface()) { direction = staff_mechanic_direction_surface(peep); } else { - TileElement* pathElement = map_get_path_element_at(peep->next_x / 32, peep->next_y / 32, peep->next_z); + auto* pathElement = map_get_path_element_at({ peep->next_x / 32, peep->next_y / 32, peep->next_z }); if (pathElement == nullptr) return true; @@ -1280,10 +922,10 @@ static bool staff_path_finding_mechanic(Peep* peep) * * rct2: 0x006C050B */ -static uint8_t staff_direction_path(Peep* peep, uint8_t validDirections, TileElement* pathElement) +static uint8_t staff_direction_path(Peep* peep, uint8_t validDirections, PathElement* pathElement) { - uint8_t direction = 0xFF; - uint8_t pathDirections = pathElement->AsPath()->GetEdges(); + Direction direction = INVALID_DIRECTION; + uint8_t pathDirections = pathElement->GetEdges(); if (peep->state != PEEP_STATE_ANSWERING && peep->state != PEEP_STATE_HEADING_TO_INSPECTION) { pathDirections &= validDirections; @@ -1329,14 +971,14 @@ static bool staff_path_finding_misc(Peep* peep) { uint8_t validDirections = staff_get_valid_patrol_directions(peep, peep->next_x, peep->next_y); - uint8_t direction = 0xFF; + Direction direction = INVALID_DIRECTION; if (peep->GetNextIsSurface()) { direction = staff_direction_surface(peep, scenario_rand() & 3); } else { - TileElement* pathElement = map_get_path_element_at(peep->next_x / 32, peep->next_y / 32, peep->next_z); + auto* pathElement = map_get_path_element_at({ peep->next_x / 32, peep->next_y / 32, peep->next_z }); if (pathElement == nullptr) return true; @@ -1415,14 +1057,11 @@ static int32_t staff_path_finding_entertainer(Peep* peep) { if (((scenario_rand() & 0xFFFF) <= 0x4000) && (peep->action == PEEP_ACTION_NONE_1 || peep->action == PEEP_ACTION_NONE_2)) { - peep->Invalidate(); - peep->action = (scenario_rand() & 1) ? PEEP_ACTION_WAVE_2 : PEEP_ACTION_JOY; peep->action_frame = 0; peep->action_sprite_image_offset = 0; peep->UpdateCurrentActionSpriteType(); - peep->Invalidate(); staff_entertainer_update_nearby_peeps(peep); } @@ -1452,24 +1091,6 @@ int32_t staff_path_finding(Staff* peep) } } -void game_command_pickup_staff( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - int32_t peepnum = *eax; - int32_t x = *edi; - int32_t y = *ebp; - int32_t z = *edx; - int32_t action = *ecx; - if (peep_pickup_command(peepnum, x, y, z, action, *ebx & GAME_COMMAND_FLAG_APPLY)) - { - *ebx = 0; - } - else - { - *ebx = MONEY32_UNDEFINED; - } -} - colour_t staff_get_colour(uint8_t staffType) { switch (staffType) @@ -1557,17 +1178,12 @@ void Staff::UpdateMowing() if (!CheckForPath()) return; - Invalidate(); while (true) { - int16_t actionX = 0; - int16_t actionY = 0; - int16_t xy_distance; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - int16_t checkZ = tile_element_height(actionX, actionY) & 0xFFFF; - MoveTo(actionX, actionY, checkZ); - Invalidate(); + int16_t checkZ = tile_element_height(*loc); + MoveTo((*loc).x, (*loc).y, checkZ); return; } @@ -1590,7 +1206,7 @@ void Staff::UpdateMowing() if (var_37 != 7) continue; - auto surfaceElement = map_get_surface_element_at(next_x / 32, next_y / 32)->AsSurface(); + auto surfaceElement = map_get_surface_element_at(next_x / 32, next_y / 32); if (surfaceElement != nullptr && surfaceElement->CanGrassGrow()) { surfaceElement->SetGrassLength(GRASS_LENGTH_MOWED); @@ -1623,7 +1239,6 @@ void Staff::UpdateWatering() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); sub_state = 1; } @@ -1631,8 +1246,8 @@ void Staff::UpdateWatering() { if (action != PEEP_ACTION_NONE_2) { - int16_t actionX, actionY, xy_distance; - UpdateAction(&actionX, &actionY, &xy_distance); + UpdateAction(); + Invalidate(); return; } @@ -1640,6 +1255,8 @@ void Staff::UpdateWatering() int32_t actionY = next_y + CoordsDirectionDelta[var_37].y; TileElement* tile_element = map_get_first_element_at(actionX / 32, actionY / 32); + if (tile_element == nullptr) + return; do { @@ -1676,6 +1293,7 @@ void Staff::UpdateEmptyingBin() { if (!CheckForPath()) return; + uint8_t pathingResult; PerformNextAction(pathingResult); if (!(pathingResult & PATHING_DESTINATION_REACHED)) @@ -1686,7 +1304,6 @@ void Staff::UpdateEmptyingBin() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); sub_state = 1; } @@ -1698,15 +1315,15 @@ void Staff::UpdateEmptyingBin() return; } - int16_t actionX = 0; - int16_t actionY = 0; - int16_t xy_distance; - UpdateAction(&actionX, &actionY, &xy_distance); + UpdateAction(); + Invalidate(); if (action_frame != 11) return; TileElement* tile_element = map_get_first_element_at(next_x / 32, next_y / 32); + if (tile_element == nullptr) + return; for (;; tile_element++) { @@ -1756,8 +1373,6 @@ void Staff::UpdateSweeping() if (!CheckForPath()) return; - Invalidate(); - if (action == PEEP_ACTION_STAFF_SWEEP && action_frame == 8) { // Remove sick at this location @@ -1765,14 +1380,10 @@ void Staff::UpdateSweeping() staff_litter_swept++; window_invalidate_flags |= PEEP_INVALIDATE_STAFF_STATS; } - int16_t actionX = 0; - int16_t actionY = 0; - int16_t xy_distance; - if (UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - int16_t actionZ = GetZOnSlope(actionX, actionY); - MoveTo(actionX, actionY, actionZ); - Invalidate(); + int16_t actionZ = GetZOnSlope((*loc).x, (*loc).y); + MoveTo((*loc).x, (*loc).y, actionZ); return; } @@ -1783,7 +1394,6 @@ void Staff::UpdateSweeping() action_frame = 0; action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); return; } StateReset(); @@ -1795,9 +1405,8 @@ void Staff::UpdateSweeping() */ void Staff::UpdateHeadingToInspect() { - Ride* ride = get_ride(current_ride); - - if (ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(current_ride); + if (ride == nullptr) { SetState(PEEP_STATE_FALLING); return; @@ -1879,27 +1488,22 @@ void Staff::UpdateHeadingToInspect() // Falls through into sub_state 4 } - Invalidate(); - int16_t delta_y = abs(y - destination_y); - - int16_t actionX, actionY, xy_distance; - if (!UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - SetState(PEEP_STATE_INSPECTING); - sub_state = 0; + int32_t newZ = ride->stations[current_ride_station].Height * 8; + + if (delta_y < 20) + { + newZ += RideData5[ride->type].z; + } + + MoveTo((*loc).x, (*loc).y, newZ); return; } - int32_t newZ = ride->stations[current_ride_station].Height * 8; - - if (delta_y < 20) - { - newZ += RideData5[ride->type].z; - } - - MoveTo(actionX, actionY, newZ); - Invalidate(); + SetState(PEEP_STATE_INSPECTING); + sub_state = 0; } /** @@ -1908,9 +1512,8 @@ void Staff::UpdateHeadingToInspect() */ void Staff::UpdateAnswering() { - Ride* ride = get_ride(current_ride); - - if (ride->type == RIDE_TYPE_NULL || ride->mechanic_status != RIDE_MECHANIC_STATUS_HEADING) + auto ride = get_ride(current_ride); + if (ride == nullptr || ride->mechanic_status != RIDE_MECHANIC_STATUS_HEADING) { SetState(PEEP_STATE_FALLING); return; @@ -1923,7 +1526,6 @@ void Staff::UpdateAnswering() action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); sub_state = 1; peep_window_state_update(this); @@ -1939,8 +1541,8 @@ void Staff::UpdateAnswering() peep_reset_pathfind_goal(this); return; } - int16_t actionX, actionY, xy_distance; - UpdateAction(&actionX, &actionY, &xy_distance); + UpdateAction(); + Invalidate(); return; } else if (sub_state <= 3) @@ -1997,27 +1599,22 @@ void Staff::UpdateAnswering() // Falls through into sub_state 4 } - Invalidate(); - int16_t delta_y = abs(y - destination_y); - - int16_t actionX, actionY, xy_distance; - if (!UpdateAction(&actionX, &actionY, &xy_distance)) + if (auto loc = UpdateAction()) { - SetState(PEEP_STATE_FIXING); - sub_state = 0; + int32_t newZ = ride->stations[current_ride_station].Height * 8; + + if (delta_y < 20) + { + newZ += RideData5[ride->type].z; + } + + MoveTo((*loc).x, (*loc).y, newZ); return; } - int32_t newZ = ride->stations[current_ride_station].Height * 8; - - if (delta_y < 20) - { - newZ += RideData5[ride->type].z; - } - - MoveTo(actionX, actionY, newZ); - Invalidate(); + SetState(PEEP_STATE_FIXING); + sub_state = 0; } /** rct2: 0x00992A5C */ @@ -2126,6 +1723,8 @@ static int32_t peep_update_patrolling_find_bin(Peep* peep) if (!tileElement->AsPath()->HasAddition()) return 0; rct_scenery_entry* sceneryEntry = tileElement->AsPath()->GetAdditionEntry(); + if (sceneryEntry == nullptr) + return 0; if (!(sceneryEntry->path_bit.flags & PATH_BIT_FLAG_IS_BIN)) return 0; @@ -2176,7 +1775,7 @@ static int32_t peep_update_patrolling_find_grass(Peep* peep) if (!(peep->GetNextIsSurface())) return 0; - auto surfaceElement = map_get_surface_element_at({ peep->next_x, peep->next_y })->AsSurface(); + auto surfaceElement = map_get_surface_element_at({ peep->next_x, peep->next_y }); if (surfaceElement != nullptr && surfaceElement->CanGrassGrow()) { if ((surfaceElement->GetGrassLength() & 0x7) >= GRASS_LENGTH_CLEAR_1) @@ -2208,7 +1807,7 @@ static int32_t peep_update_patrolling_find_sweeping(Peep* peep) { sprite = get_sprite(sprite_id); - if (sprite->generic.linked_list_type_offset != SPRITE_LIST_LITTER * 2) + if (sprite->generic.linked_list_index != SPRITE_LIST_LITTER) continue; uint16_t z_diff = abs(peep->z - sprite->litter.z); @@ -2314,18 +1913,15 @@ void Staff::UpdatePatrolling() if (GetNextIsSurface()) { - TileElement* tile_element = map_get_surface_element_at({ next_x, next_y }); + auto surfaceElement = map_get_surface_element_at({ next_x, next_y }); - if (tile_element != nullptr) + if (surfaceElement != nullptr) { - int32_t water_height = tile_element->AsSurface()->GetWaterHeight(); + int32_t water_height = surfaceElement->GetWaterHeight(); if (water_height) { - Invalidate(); water_height *= 16; MoveTo(x, y, water_height); - Invalidate(); - SetState(PEEP_STATE_FALLING); return; } @@ -2454,9 +2050,8 @@ static constexpr const uint32_t FixingSubstatesForBreakdown[9] = { */ void Staff::UpdateFixing(int32_t steps) { - Ride* ride = get_ride(current_ride); - - if (ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(current_ride); + if (ride == nullptr) { SetState(PEEP_STATE_FALLING); return; @@ -2617,12 +2212,9 @@ bool Staff::UpdateFixingMoveToBrokenDownVehicle(bool firstRun, Ride* ride) destination_tolerance = 2; } - Invalidate(); - int16_t actionX, actionY, tmp_xy_distance; - if (UpdateAction(&actionX, &actionY, &tmp_xy_distance)) + if (auto loc = UpdateAction()) { - sprite_move(actionX, actionY, z, (rct_sprite*)this); - Invalidate(); + MoveTo(loc->x, loc->y, z); return false; } @@ -2648,7 +2240,6 @@ bool Staff::UpdateFixingFixVehicle(bool firstRun, Ride* ride) action_sprite_image_offset = 0; action_frame = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } if (action == PEEP_ACTION_NONE_2) @@ -2657,6 +2248,7 @@ bool Staff::UpdateFixingFixVehicle(bool firstRun, Ride* ride) } UpdateAction(); + Invalidate(); uint8_t actionFrame = (action == PEEP_ACTION_STAFF_FIX) ? 0x25 : 0x50; if (action_frame != actionFrame) @@ -2690,7 +2282,6 @@ bool Staff::UpdateFixingFixVehicleMalfunction(bool firstRun, Ride* ride) action_frame = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } if (action == PEEP_ACTION_NONE_2) @@ -2699,6 +2290,8 @@ bool Staff::UpdateFixingFixVehicleMalfunction(bool firstRun, Ride* ride) } UpdateAction(); + Invalidate(); + if (action_frame != 0x65) { return false; @@ -2748,7 +2341,7 @@ bool Staff::UpdateFixingMoveToStationEnd(bool firstRun, Ride* ride) uint16_t stationX = stationPosition.x * 32; uint16_t stationY = stationPosition.y * 32; - TileElement* tileElement = map_get_track_element_at(stationX, stationY, stationZ); + auto tileElement = map_get_track_element_at(stationX, stationY, stationZ); if (tileElement == nullptr) { log_error("Couldn't find tile_element"); @@ -2775,17 +2368,13 @@ bool Staff::UpdateFixingMoveToStationEnd(bool firstRun, Ride* ride) destination_tolerance = 2; } - Invalidate(); - int16_t actionX, actionY, tmp_distance; - if (!UpdateAction(&actionX, &actionY, &tmp_distance)) + if (auto loc = UpdateAction()) { - return true; + MoveTo(loc->x, loc->y, z); + return false; } - sprite_move(actionX, actionY, z, (rct_sprite*)this); - Invalidate(); - - return false; + return true; } /** @@ -2804,7 +2393,6 @@ bool Staff::UpdateFixingFixStationEnd(bool firstRun) action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } if (action == PEEP_ACTION_NONE_2) @@ -2813,6 +2401,7 @@ bool Staff::UpdateFixingFixStationEnd(bool firstRun) } UpdateAction(); + Invalidate(); return false; } @@ -2891,17 +2480,13 @@ bool Staff::UpdateFixingMoveToStationStart(bool firstRun, Ride* ride) destination_tolerance = 2; } - Invalidate(); - int16_t actionX, actionY, tmp_xy_distance; - if (!UpdateAction(&actionX, &actionY, &tmp_xy_distance)) + if (auto loc = UpdateAction()) { - return true; + MoveTo(loc->x, loc->y, z); + return false; } - sprite_move(actionX, actionY, z, (rct_sprite*)this); - Invalidate(); - - return false; + return true; } /** @@ -2927,7 +2512,6 @@ bool Staff::UpdateFixingFixStationStart(bool firstRun, Ride* ride) action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } if (action == PEEP_ACTION_NONE_2) @@ -2956,7 +2540,6 @@ bool Staff::UpdateFixingFixStationBrakes(bool firstRun, Ride* ride) action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } if (action == PEEP_ACTION_NONE_2) @@ -2965,6 +2548,8 @@ bool Staff::UpdateFixingFixStationBrakes(bool firstRun, Ride* ride) } UpdateAction(); + Invalidate(); + if (action_frame == 0x28) { ride->mechanic_status = RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES; @@ -2973,7 +2558,7 @@ bool Staff::UpdateFixingFixStationBrakes(bool firstRun, Ride* ride) if (action_frame == 0x13 || action_frame == 0x19 || action_frame == 0x1F || action_frame == 0x25 || action_frame == 0x2B) { - audio_play_sound_at_location(SOUND_MECHANIC_FIX, x, y, z); + audio_play_sound_at_location(SoundId::MechanicFix, { x, y, z }); } return false; @@ -3014,19 +2599,15 @@ bool Staff::UpdateFixingMoveToStationExit(bool firstRun, Ride* ride) destination_tolerance = 2; } - Invalidate(); - int16_t actionX, actionY, tmp_xy_distance; - if (!UpdateAction(&actionX, &actionY, &tmp_xy_distance)) + if (auto loc = UpdateAction()) { - return true; + MoveTo(loc->x, loc->y, z); + return false; } else { - sprite_move(actionX, actionY, z, (rct_sprite*)this); - Invalidate(); + return true; } - - return false; } /** @@ -3059,12 +2640,12 @@ bool Staff::UpdateFixingFinishFixOrInspect(bool firstRun, int32_t steps, Ride* r action_sprite_image_offset = 0; UpdateCurrentActionSpriteType(); - Invalidate(); } if (action != PEEP_ACTION_NONE_2) { UpdateAction(); + Invalidate(); return false; } @@ -3109,24 +2690,20 @@ bool Staff::UpdateFixingLeaveByEntranceExit(bool firstRun, Ride* ride) destination_tolerance = 2; } - Invalidate(); - int16_t actionX, actionY, xy_distance; - if (!UpdateAction(&actionX, &actionY, &xy_distance)) + int16_t xy_distance; + if (auto loc = UpdateAction(xy_distance)) { - SetState(PEEP_STATE_FALLING); + uint16_t stationHeight = ride->stations[current_ride_station].Height * 8; + + if (xy_distance >= 16) + { + stationHeight += RideData5[ride->type].z; + } + + MoveTo(loc->x, loc->y, stationHeight); return false; } - - uint16_t stationHeight = ride->stations[current_ride_station].Height * 8; - - if (xy_distance >= 16) - { - stationHeight += RideData5[ride->type].z; - } - - sprite_move(actionX, actionY, stationHeight, (rct_sprite*)this); - Invalidate(); - + SetState(PEEP_STATE_FALLING); return false; } @@ -3135,10 +2712,13 @@ bool Staff::UpdateFixingLeaveByEntranceExit(bool firstRun, Ride* ride) */ void Staff::UpdateRideInspected(ride_id_t rideIndex) { - Ride* ride = get_ride(rideIndex); - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_DUE_INSPECTION; - - ride->reliability += ((100 - ride->reliability_percentage) / 4) * (scenario_rand() & 0xFF); - ride->last_inspection = 0; - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAINTENANCE | RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_DUE_INSPECTION; + ride->reliability += ((100 - ride->reliability_percentage) / 4) * (scenario_rand() & 0xFF); + ride->last_inspection = 0; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAINTENANCE | RIDE_INVALIDATE_RIDE_MAIN + | RIDE_INVALIDATE_RIDE_LIST; + } } diff --git a/src/openrct2/peep/Staff.h b/src/openrct2/peep/Staff.h index 61c15896a3..775ec75e1d 100644 --- a/src/openrct2/peep/Staff.h +++ b/src/openrct2/peep/Staff.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -25,7 +25,7 @@ enum STAFF_MODE STAFF_MODE_PATROL = 3 }; -enum STAFF_TYPE +enum STAFF_TYPE : uint8_t { STAFF_TYPE_HANDYMAN, STAFF_TYPE_MECHANIC, @@ -45,7 +45,7 @@ enum STAFF_ORDERS STAFF_ORDERS_FIX_RIDES = (1 << 1) }; -enum ENTERTAINER_COSTUME +enum ENTERTAINER_COSTUME : uint8_t { ENTERTAINER_COSTUME_PANDA, ENTERTAINER_COSTUME_TIGER, @@ -71,22 +71,9 @@ extern colour_t gStaffHandymanColour; extern colour_t gStaffMechanicColour; extern colour_t gStaffSecurityColour; -void game_command_hire_new_staff_member( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_callback_hire_new_staff_member( - int32_t eax, int32_t ebx, int32_t ecx, int32_t edx, int32_t esi, int32_t edi, int32_t ebp); -void game_command_set_staff_patrol( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_fire_staff_member( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_set_staff_name( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_pickup_staff( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); - void staff_reset_modes(); void staff_set_name(uint16_t spriteIndex, const char* name); -uint16_t hire_new_staff_member(uint8_t staffType); +bool staff_hire_new_member(STAFF_TYPE staffType, ENTERTAINER_COSTUME entertainerType); void staff_update_greyed_patrol_areas(); bool staff_is_location_in_patrol(Peep* mechanic, int32_t x, int32_t y); bool staff_is_location_on_patrol_edge(Peep* mechanic, int32_t x, int32_t y); diff --git a/src/openrct2/platform/Android.cpp b/src/openrct2/platform/Android.cpp index f9d2c50eee..b88f782e48 100644 --- a/src/openrct2/platform/Android.cpp +++ b/src/openrct2/platform/Android.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/platform/Crash.cpp b/src/openrct2/platform/Crash.cpp index 1083587c7e..b6a5db9fac 100644 --- a/src/openrct2/platform/Crash.cpp +++ b/src/openrct2/platform/Crash.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,6 +10,7 @@ #include "Crash.h" #ifdef USE_BREAKPAD +# include # include # include # include @@ -57,7 +58,7 @@ static bool UploadMinidump(const std::map& files, in wprintf(L"files[%s] = %s\n", file.first.c_str(), file.second.c_str()); } std::wstring url(L"https://openrct2.sp.backtrace.io:6098/" - L"post?format=minidump&token=27bfc474b8739e7c1df37180727e717a0a95d3bf3f2a8eaaf17ad321fb179c6f"); + L"post?format=minidump&token=ac62db34d5bf1b2f14da40572a829af476c8f465ca7a4193dad6f172ca89eb7a"); std::map parameters; parameters[L"product_name"] = L"openrct2"; // In case of releases this can be empty @@ -67,7 +68,7 @@ static bool UploadMinidump(const std::map& files, in } else { - parameters[L"commit"] = String::ToUtf16(gVersionInfoFull); + parameters[L"commit"] = String::ToWideChar(gVersionInfoFull); } int timeout = 10000; bool success = google_breakpad::HTTPUpload::SendRequest(url, parameters, files, &timeout, &response, &error); @@ -99,18 +100,18 @@ static bool OnCrash( wchar_t saveFilePath[MAX_PATH]; wchar_t configFilePath[MAX_PATH]; wchar_t saveFilePathGZIP[MAX_PATH]; - swprintf_s(dumpFilePath, sizeof(dumpFilePath), L"%s\\%s.dmp", dumpPath, miniDumpId); - swprintf_s(saveFilePath, sizeof(saveFilePath), L"%s\\%s.sv6", dumpPath, miniDumpId); - swprintf_s(configFilePath, sizeof(configFilePath), L"%s\\%s.ini", dumpPath, miniDumpId); - swprintf_s(saveFilePathGZIP, sizeof(saveFilePathGZIP), L"%s\\%s.sv6.gz", dumpPath, miniDumpId); + swprintf_s(dumpFilePath, std::size(dumpFilePath), L"%s\\%s.dmp", dumpPath, miniDumpId); + swprintf_s(saveFilePath, std::size(saveFilePath), L"%s\\%s.sv6", dumpPath, miniDumpId); + swprintf_s(configFilePath, std::size(configFilePath), L"%s\\%s.ini", dumpPath, miniDumpId); + swprintf_s(saveFilePathGZIP, std::size(saveFilePathGZIP), L"%s\\%s.sv6.gz", dumpPath, miniDumpId); wchar_t dumpFilePathNew[MAX_PATH]; swprintf_s( - dumpFilePathNew, sizeof(dumpFilePathNew), L"%s\\%s(%s_%s).dmp", dumpPath, miniDumpId, _wszCommitSha1Short, + dumpFilePathNew, std::size(dumpFilePathNew), L"%s\\%s(%s_%s).dmp", dumpPath, miniDumpId, _wszCommitSha1Short, _wszArchitecture); wchar_t dumpFilePathGZIP[MAX_PATH]; - swprintf_s(dumpFilePathGZIP, sizeof(dumpFilePathGZIP), L"%s.gz", dumpFilePathNew); + swprintf_s(dumpFilePathGZIP, std::size(dumpFilePathGZIP), L"%s.gz", dumpFilePathNew); // Compress the dump { @@ -149,18 +150,17 @@ static bool OnCrash( wprintf(L"Commit: %s\n", _wszCommitSha1Short); bool savedGameDumped = false; - utf8* saveFilePathUTF8 = widechar_to_utf8(saveFilePath); + auto saveFilePathUTF8 = String::ToUtf8(saveFilePath); try { auto exporter = std::make_unique(); exporter->Export(); - exporter->SaveGame(saveFilePathUTF8); + exporter->SaveGame(saveFilePathUTF8.c_str()); savedGameDumped = true; } catch (const std::exception&) { } - free(saveFilePathUTF8); // Compress the save if (savedGameDumped) @@ -180,19 +180,16 @@ static bool OnCrash( fclose(dest); } - utf8* configFilePathUTF8 = widechar_to_utf8(configFilePath); - if (config_save(configFilePathUTF8)) + auto configFilePathUTF8 = String::ToUtf8(configFilePath); + if (config_save(configFilePathUTF8.c_str())) { uploadFiles[L"attachment_config.ini"] = configFilePath; } - free(configFilePathUTF8); std::string screenshotPath = screenshot_dump(); if (!screenshotPath.empty()) { - wchar_t* screenshotPathWchar = utf8_to_widechar(screenshotPath.c_str()); - auto screenshotPathW = std::wstring(screenshotPathWchar); - free(screenshotPathWchar); + auto screenshotPathW = String::ToWideChar(screenshotPath.c_str()); uploadFiles[L"attachment_screenshot.png"] = screenshotPathW; } @@ -269,11 +266,7 @@ static std::wstring GetDumpDirectory() { char userDirectory[MAX_PATH]; platform_get_user_directory(userDirectory, nullptr, sizeof(userDirectory)); - - wchar_t* userDirectoryW = utf8_to_widechar(userDirectory); - auto result = std::wstring(userDirectoryW); - free(userDirectoryW); - + auto result = String::ToWideChar(userDirectory); return result; } diff --git a/src/openrct2/platform/Crash.h b/src/openrct2/platform/Crash.h index 06a56b03c4..f8e6b0184c 100644 --- a/src/openrct2/platform/Crash.h +++ b/src/openrct2/platform/Crash.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/platform/Linux.cpp b/src/openrct2/platform/Linux.cpp index 91e25d09a8..eed86f4e68 100644 --- a/src/openrct2/platform/Linux.cpp +++ b/src/openrct2/platform/Linux.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/platform/Platform.Android.cpp b/src/openrct2/platform/Platform.Android.cpp index 797196131e..4fe6738839 100644 --- a/src/openrct2/platform/Platform.Android.cpp +++ b/src/openrct2/platform/Platform.Android.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/platform/Platform.Linux.cpp b/src/openrct2/platform/Platform.Linux.cpp index 27b95a5a4e..7421b647a5 100644 --- a/src/openrct2/platform/Platform.Linux.cpp +++ b/src/openrct2/platform/Platform.Linux.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/platform/Platform.Posix.cpp b/src/openrct2/platform/Platform.Posix.cpp index 48b6bc2ade..7b3ee5c491 100644 --- a/src/openrct2/platform/Platform.Posix.cpp +++ b/src/openrct2/platform/Platform.Posix.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/platform/Platform.Win32.cpp b/src/openrct2/platform/Platform.Win32.cpp index 1b51233b21..b23ab59d5d 100644 --- a/src/openrct2/platform/Platform.Win32.cpp +++ b/src/openrct2/platform/Platform.Win32.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -50,7 +50,7 @@ namespace Platform std::string GetEnvironmentVariable(const std::string& name) { std::wstring result; - auto wname = String::ToUtf16(name); + auto wname = String::ToWideChar(name); wchar_t wvalue[256]; auto valueSize = GetEnvironmentVariableW(wname.c_str(), wvalue, (DWORD)std::size(wvalue)); if (valueSize < std::size(wvalue)) @@ -179,7 +179,7 @@ namespace Platform # ifdef __USE_GETDATEFORMATEX__ wchar_t date[20]; - GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_SHORTDATE, &st, nullptr, date, sizeof(date), nullptr); + GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_SHORTDATE, &st, nullptr, date, (int)std::size(date), nullptr); std::string result = String::ToUtf8(std::wstring(date)); # else char date[20]; @@ -196,7 +196,7 @@ namespace Platform # ifdef __USE_GETDATEFORMATEX__ wchar_t time[20]; - GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, &st, nullptr, time, sizeof(time)); + GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, &st, nullptr, time, (int)std::size(time)); std::string result = String::ToUtf8(std::wstring(time)); # else char time[20]; diff --git a/src/openrct2/platform/Platform.macOS.mm b/src/openrct2/platform/Platform.macOS.mm index d2b9b3535b..e523ab6199 100644 --- a/src/openrct2/platform/Platform.macOS.mm +++ b/src/openrct2/platform/Platform.macOS.mm @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/platform/Platform2.h b/src/openrct2/platform/Platform2.h index f61a17b38b..3793d20d1c 100644 --- a/src/openrct2/platform/Platform2.h +++ b/src/openrct2/platform/Platform2.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/platform/Posix.cpp b/src/openrct2/platform/Posix.cpp index 1724ae1abf..a458d753de 100644 --- a/src/openrct2/platform/Posix.cpp +++ b/src/openrct2/platform/Posix.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -21,6 +21,7 @@ # include "../OpenRCT2.h" # include "../config/Config.h" # include "../core/Path.hpp" +# include "../core/String.hpp" # include "../localisation/Date.h" # include "../localisation/Language.h" # include "../util/Util.h" @@ -95,54 +96,25 @@ void platform_get_time_local(rct2_time* out_time) out_time->hour = timeinfo->tm_hour; } -static size_t platform_utf8_to_multibyte(const utf8* path, char* buffer, size_t buffer_size) -{ - wchar_t* wpath = utf8_to_widechar(path); - setlocale(LC_CTYPE, "UTF-8"); - size_t len = wcstombs(NULL, wpath, 0); - bool truncated = false; - if (len > buffer_size - 1) - { - truncated = true; - len = buffer_size - 1; - } - wcstombs(buffer, wpath, len); - buffer[len] = '\0'; - if (truncated) - log_warning("truncated string %s", buffer); - free(wpath); - return len; -} - bool platform_file_exists(const utf8* path) { - char buffer[MAX_PATH]; - platform_utf8_to_multibyte(path, buffer, MAX_PATH); - bool exists = access(buffer, F_OK) != -1; - log_verbose("file '%s' exists = %i", buffer, exists); + bool exists = access(path, F_OK) != -1; + log_verbose("file '%s' exists = %i", path, exists); return exists; } bool platform_directory_exists(const utf8* path) { - char buffer[MAX_PATH]; - platform_utf8_to_multibyte(path, buffer, MAX_PATH); struct stat dirinfo; - int32_t result = stat(buffer, &dirinfo); - log_verbose("checking dir %s, result = %d, is_dir = %d", buffer, result, S_ISDIR(dirinfo.st_mode)); - if ((result != 0) || !S_ISDIR(dirinfo.st_mode)) - { - return false; - } - return true; + int32_t result = stat(path, &dirinfo); + log_verbose("checking dir %s, result = %d, is_dir = %d", path, result, S_ISDIR(dirinfo.st_mode)); + return result == 0 && S_ISDIR(dirinfo.st_mode); } bool platform_original_game_data_exists(const utf8* path) { - char buffer[MAX_PATH]; - platform_utf8_to_multibyte(path, buffer, MAX_PATH); char checkPath[MAX_PATH]; - safe_strcpy(checkPath, buffer, MAX_PATH); + safe_strcpy(checkPath, path, MAX_PATH); safe_strcat_path(checkPath, "Data", MAX_PATH); safe_strcat_path(checkPath, "g1.dat", MAX_PATH); return platform_file_exists(checkPath); @@ -151,7 +123,7 @@ bool platform_original_game_data_exists(const utf8* path) bool platform_original_rct1_data_exists(const utf8* path) { char buffer[MAX_PATH], checkPath1[MAX_PATH], checkPath2[MAX_PATH]; - platform_utf8_to_multibyte(path, buffer, MAX_PATH); + safe_strcpy(buffer, path, sizeof(path)); safe_strcat_path(buffer, "Data", MAX_PATH); safe_strcpy(checkPath1, buffer, MAX_PATH); safe_strcpy(checkPath2, buffer, MAX_PATH); @@ -190,7 +162,7 @@ bool platform_ensure_directory_exists(const utf8* path) { mode_t mask = openrct2_getumask(); char buffer[MAX_PATH]; - platform_utf8_to_multibyte(path, buffer, MAX_PATH); + safe_strcpy(buffer, path, sizeof(buffer)); log_verbose("Create directory: %s", buffer); for (char* p = buffer + 1; *p != '\0'; p++) @@ -290,19 +262,29 @@ bool platform_directory_delete(const utf8* path) return true; } -utf8* platform_get_absolute_path(const utf8* relative_path, const utf8* base_path) +std::string platform_get_absolute_path(const utf8* relative_path, const utf8* base_path) { - utf8 path[MAX_PATH]; + std::string result; + if (relative_path != nullptr) + { + std::string pathToResolve; + if (base_path == nullptr) + { + pathToResolve = std::string(relative_path); + } + else + { + pathToResolve = std::string(base_path) + std::string("/") + relative_path; + } - if (base_path != nullptr) - { - snprintf(path, MAX_PATH, "%s/%s", base_path, relative_path); + auto realpathResult = realpath(pathToResolve.c_str(), nullptr); + if (realpathResult != nullptr) + { + result = std::string(realpathResult); + free(realpathResult); + } } - else - { - safe_strcpy(path, base_path, MAX_PATH); - } - return realpath(path, NULL); + return result; } bool platform_lock_single_instance() @@ -486,18 +468,15 @@ datetime64 platform_get_datetime_now_utc() return utcNow; } -utf8* platform_get_username() +std::string platform_get_username() { - struct passwd* pw = getpwuid(getuid()); - - if (pw) + std::string result; + auto pw = getpwuid(getuid()); + if (pw != nullptr) { - return pw->pw_name; - } - else - { - return nullptr; + result = std::string(pw->pw_name); } + return result; } bool platform_process_is_elevated() diff --git a/src/openrct2/platform/Shared.cpp b/src/openrct2/platform/Shared.cpp index 1955bdba5f..7672bf71d0 100644 --- a/src/openrct2/platform/Shared.cpp +++ b/src/openrct2/platform/Shared.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -28,6 +28,7 @@ #include "../world/Climate.h" #include "platform.h" +#include #include #include @@ -198,6 +199,21 @@ uint8_t platform_get_currency_value(const char* currCode) return CURRENCY_POUNDS; } +#ifndef _WIN32 +std::string platform_sanitise_filename(const std::string& path) +{ + static const std::array prohibited = { '/' }; + auto sanitised = path; + std::replace_if( + sanitised.begin(), sanitised.end(), + [](const std::string::value_type& ch) -> bool { + return std::find(prohibited.begin(), prohibited.end(), ch) != prohibited.end(); + }, + '_'); + return sanitised; +} +#endif + #ifndef __ANDROID__ float platform_get_default_scale() { diff --git a/src/openrct2/platform/Windows.cpp b/src/openrct2/platform/Windows.cpp index 34f0eb62f7..e602d1eb70 100644 --- a/src/openrct2/platform/Windows.cpp +++ b/src/openrct2/platform/Windows.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -22,14 +22,17 @@ # include "../OpenRCT2.h" # include "../Version.h" # include "../config/Config.h" +# include "../core/String.hpp" # include "../localisation/Date.h" # include "../localisation/Language.h" # include "../rct2/RCT2.h" # include "../util/Util.h" # include "platform.h" +# include # include # include +# include # include # include # include @@ -82,18 +85,16 @@ void platform_get_time_local(rct2_time* out_time) bool platform_file_exists(const utf8* path) { - wchar_t* wPath = utf8_to_widechar(path); - DWORD result = GetFileAttributesW(wPath); + auto wPath = String::ToWideChar(path); + DWORD result = GetFileAttributesW(wPath.c_str()); DWORD error = GetLastError(); - free(wPath); return !(result == INVALID_FILE_ATTRIBUTES && (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND)); } bool platform_directory_exists(const utf8* path) { - wchar_t* wPath = utf8_to_widechar(path); - DWORD dwAttrib = GetFileAttributesW(wPath); - free(wPath); + auto wPath = String::ToWideChar(path); + DWORD dwAttrib = GetFileAttributesW(wPath.c_str()); return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); } @@ -124,27 +125,20 @@ bool platform_ensure_directory_exists(const utf8* path) if (platform_directory_exists(path)) return 1; - wchar_t* wPath = utf8_to_widechar(path); - BOOL success = CreateDirectoryW(wPath, nullptr); - free(wPath); - return success == TRUE; + auto wPath = String::ToWideChar(path); + auto success = CreateDirectoryW(wPath.c_str(), nullptr); + return success != FALSE; } bool platform_directory_delete(const utf8* path) { - wchar_t pszFrom[MAX_PATH]; - - wchar_t* wPath = utf8_to_widechar(path); - wcsncpy(pszFrom, wPath, MAX_PATH); - - // Needs to be double-null terminated for some weird reason - pszFrom[wcslen(wPath) + 1] = 0; - free(wPath); + // Needs to be double-null terminated as pFrom is a null terminated array of strings + auto wPath = String::ToWideChar(path) + L"\0"; SHFILEOPSTRUCTW fileop; fileop.hwnd = nullptr; // no status display fileop.wFunc = FO_DELETE; // delete operation - fileop.pFrom = pszFrom; // source file name as double null terminated string + fileop.pFrom = wPath.c_str(); // source file name as double null terminated string fileop.pTo = nullptr; // no destination needed fileop.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; // do not prompt the user @@ -167,7 +161,7 @@ bool platform_lock_single_instance() // Create new mutex status = CreateMutex(nullptr, FALSE, SINGLE_INSTANCE_MUTEX_NAME); if (status == nullptr) - log_error("unable to create mutex\n"); + log_error("unable to create mutex"); return true; } @@ -186,30 +180,25 @@ int32_t platform_get_drives() bool platform_file_copy(const utf8* srcPath, const utf8* dstPath, bool overwrite) { - wchar_t* wSrcPath = utf8_to_widechar(srcPath); - wchar_t* wDstPath = utf8_to_widechar(dstPath); - BOOL success = CopyFileW(wSrcPath, wDstPath, overwrite ? FALSE : TRUE); - free(wSrcPath); - free(wDstPath); - return success == TRUE; + auto wSrcPath = String::ToWideChar(srcPath); + auto wDstPath = String::ToWideChar(dstPath); + auto success = CopyFileW(wSrcPath.c_str(), wDstPath.c_str(), overwrite ? FALSE : TRUE); + return success != FALSE; } bool platform_file_move(const utf8* srcPath, const utf8* dstPath) { - wchar_t* wSrcPath = utf8_to_widechar(srcPath); - wchar_t* wDstPath = utf8_to_widechar(dstPath); - BOOL success = MoveFileW(wSrcPath, wDstPath); - free(wSrcPath); - free(wDstPath); - return success == TRUE; + auto wSrcPath = String::ToWideChar(srcPath); + auto wDstPath = String::ToWideChar(dstPath); + auto success = MoveFileW(wSrcPath.c_str(), wDstPath.c_str()); + return success != FALSE; } bool platform_file_delete(const utf8* path) { - wchar_t* wPath = utf8_to_widechar(path); - BOOL success = DeleteFileW(wPath); - free(wPath); - return success == TRUE; + auto wPath = String::ToWideChar(path); + auto success = DeleteFileW(wPath.c_str()); + return success != FALSE; } bool platform_get_steam_path(utf8* outPath, size_t outSize) @@ -233,10 +222,9 @@ bool platform_get_steam_path(utf8* outPath, size_t outSize) result = RegQueryValueExW(hKey, L"SteamPath", nullptr, &type, (LPBYTE)wSteamPath, &size); if (result == ERROR_SUCCESS) { - utf8* utf8SteamPath = widechar_to_utf8(wSteamPath); - safe_strcpy(outPath, utf8SteamPath, outSize); + auto utf8SteamPath = String::ToUtf8(wSteamPath); + safe_strcpy(outPath, utf8SteamPath.c_str(), outSize); safe_strcat_path(outPath, "steamapps\\common", outSize); - free(utf8SteamPath); } free(wSteamPath); RegCloseKey(hKey); @@ -253,6 +241,20 @@ std::string platform_get_rct2_steam_dir() return "Rollercoaster Tycoon 2"; } +std::string platform_sanitise_filename(const std::string& path) +{ + static const std::array prohibited = { '<', '>', '*', '\\', ':', '|', '?', '"', '/' }; + auto sanitised = path; + std::replace_if( + sanitised.begin(), sanitised.end(), + [](const std::string::value_type& ch) -> bool { + return std::find(prohibited.begin(), prohibited.end(), ch) != prohibited.end(); + }, + '_'); + sanitised = String::Trim(sanitised); + return sanitised; +} + uint16_t platform_get_locale_language() { CHAR langCode[4]; @@ -323,24 +325,22 @@ uint16_t platform_get_locale_language() time_t platform_file_get_modified_time(const utf8* path) { - WIN32_FILE_ATTRIBUTE_DATA data; - - wchar_t* wPath = utf8_to_widechar(path); - BOOL result = GetFileAttributesExW(wPath, GetFileExInfoStandard, &data); - free(wPath); - - if (!result) - return 0; - - FILETIME localFileTime; - result = FileTimeToLocalFileTime(&data.ftLastWriteTime, &localFileTime); - if (!result) - return 0; - - ULARGE_INTEGER ull; - ull.LowPart = localFileTime.dwLowDateTime; - ull.HighPart = localFileTime.dwHighDateTime; - return ull.QuadPart / 10000000ULL - 11644473600ULL; + WIN32_FILE_ATTRIBUTE_DATA data{}; + auto wPath = String::ToWideChar(path); + auto result = GetFileAttributesExW(wPath.c_str(), GetFileExInfoStandard, &data); + if (result != FALSE) + { + FILETIME localFileTime{}; + result = FileTimeToLocalFileTime(&data.ftLastWriteTime, &localFileTime); + if (result != FALSE) + { + ULARGE_INTEGER ull{}; + ull.LowPart = localFileTime.dwLowDateTime; + ull.HighPart = localFileTime.dwHighDateTime; + return ull.QuadPart / 10000000ULL - 11644473600ULL; + } + } + return 0; } uint8_t platform_get_locale_currency() @@ -396,7 +396,7 @@ uint8_t platform_get_locale_date_format() { // Retrieve short date format, eg "MM/dd/yyyy" wchar_t dateFormat[20]; - if (GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SSHORTDATE, dateFormat, sizeof(dateFormat)) == 0) + if (GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SSHORTDATE, dateFormat, (int)std::size(dateFormat)) == 0) { return DATE_FORMAT_DAY_MONTH_YEAR; } @@ -451,9 +451,8 @@ bool platform_get_font_path(TTFFontDescriptor* font, utf8* buffer, size_t size) if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Fonts, 0, nullptr, &fontFolder))) { // Convert wchar to utf8, then copy the font folder path to the buffer. - utf8* outPathTemp = widechar_to_utf8(fontFolder); - safe_strcpy(buffer, outPathTemp, size); - free(outPathTemp); + auto outPathTemp = String::ToUtf8(fontFolder); + safe_strcpy(buffer, outPathTemp.c_str(), size); CoTaskMemFree(fontFolder); @@ -474,24 +473,30 @@ bool platform_get_font_path(TTFFontDescriptor* font, utf8* buffer, size_t size) } # endif // NO_TTF -utf8* platform_get_absolute_path(const utf8* relativePath, const utf8* basePath) +std::string platform_get_absolute_path(const utf8* relativePath, const utf8* basePath) { - utf8 path[MAX_PATH]; - safe_strcpy(path, basePath, sizeof(path)); - safe_strcat_path(path, relativePath, sizeof(path)); - - wchar_t* pathW = utf8_to_widechar(path); - wchar_t fullPathW[MAX_PATH]; - DWORD fullPathLen = GetFullPathNameW(pathW, (DWORD)std::size(fullPathW), fullPathW, nullptr); - - free(pathW); - - if (fullPathLen == 0) + std::string result; + if (relativePath != nullptr) { - return nullptr; - } + std::string pathToResolve; + if (basePath == nullptr) + { + pathToResolve = std::string(relativePath); + } + else + { + pathToResolve = std::string(basePath) + std::string("\\") + relativePath; + } - return widechar_to_utf8(fullPathW); + auto pathToResolveW = String::ToWideChar(pathToResolve); + wchar_t fullPathW[MAX_PATH]{}; + auto fullPathLen = GetFullPathNameW(pathToResolveW.c_str(), (DWORD)std::size(fullPathW), fullPathW, nullptr); + if (fullPathLen != 0) + { + result = String::ToUtf8(fullPathW); + } + } + return result; } datetime64 platform_get_datetime_now_utc() @@ -507,18 +512,16 @@ datetime64 platform_get_datetime_now_utc() return utcNow; } -utf8* platform_get_username() +std::string platform_get_username() { - static wchar_t usernameW[UNLEN + 1]; + std::string result; + wchar_t usernameW[UNLEN + 1]{}; DWORD usernameLength = UNLEN + 1; - if (!GetUserNameW(usernameW, &usernameLength)) + if (GetUserNameW(usernameW, &usernameLength)) { - return nullptr; + result = String::ToUtf8(usernameW); } - - static std::string username; - username = widechar_to_utf8(usernameW); - return username.data(); + return result; } bool platform_process_is_elevated() @@ -548,15 +551,11 @@ bool platform_process_is_elevated() # define SOFTWARE_CLASSES L"Software\\Classes" # define MUI_CACHE L"Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache" -static void get_progIdName(wchar_t* dst, const utf8* extension) +static std::wstring get_progIdName(const std::string_view& extension) { - utf8 progIdName[128]; - safe_strcpy(progIdName, OPENRCT2_NAME, sizeof(progIdName)); - safe_strcat(progIdName, extension, sizeof(progIdName)); - - wchar_t* progIdNameW = utf8_to_widechar(progIdName); - lstrcpyW(dst, progIdNameW); - free(progIdNameW); + auto progIdName = std::string(OPENRCT2_NAME) + std::string(extension); + auto progIdNameW = String::ToWideChar(progIdName); + return progIdNameW; } static bool windows_setup_file_association( @@ -567,16 +566,14 @@ static bool windows_setup_file_association( [[maybe_unused]] int32_t printResult; - GetModuleFileNameW(nullptr, exePathW, sizeof(exePathW)); - GetModuleFileNameW(plaform_get_dll_module(), dllPathW, sizeof(dllPathW)); + GetModuleFileNameW(nullptr, exePathW, (DWORD)std::size(exePathW)); + GetModuleFileNameW(plaform_get_dll_module(), dllPathW, (DWORD)std::size(dllPathW)); - wchar_t* extensionW = utf8_to_widechar(extension); - wchar_t* fileTypeTextW = utf8_to_widechar(fileTypeText); - wchar_t* commandTextW = utf8_to_widechar(commandText); - wchar_t* commandArgsW = utf8_to_widechar(commandArgs); - - wchar_t progIdNameW[128]; - get_progIdName(progIdNameW, extension); + auto extensionW = String::ToWideChar(extension); + auto fileTypeTextW = String::ToWideChar(fileTypeText); + auto commandTextW = String::ToWideChar(commandText); + auto commandArgsW = String::ToWideChar(commandArgs); + auto progIdNameW = get_progIdName(extension); bool result = false; HKEY hKey = nullptr; @@ -589,18 +586,18 @@ static bool windows_setup_file_association( } // [hRootKey\.ext] - if (RegSetValueW(hRootKey, extensionW, REG_SZ, progIdNameW, 0) != ERROR_SUCCESS) + if (RegSetValueW(hRootKey, extensionW.c_str(), REG_SZ, progIdNameW.c_str(), 0) != ERROR_SUCCESS) { goto fail; } - if (RegCreateKeyW(hRootKey, progIdNameW, &hKey) != ERROR_SUCCESS) + if (RegCreateKeyW(hRootKey, progIdNameW.c_str(), &hKey) != ERROR_SUCCESS) { goto fail; } // [hRootKey\OpenRCT2.ext] - if (RegSetValueW(hKey, nullptr, REG_SZ, fileTypeTextW, 0) != ERROR_SUCCESS) + if (RegSetValueW(hKey, nullptr, REG_SZ, fileTypeTextW.c_str(), 0) != ERROR_SUCCESS) { goto fail; } @@ -620,14 +617,14 @@ static bool windows_setup_file_association( } // [hRootKey\OpenRCT2.sv6\shell\open] - if (RegSetValueW(hKey, L"shell\\open", REG_SZ, commandTextW, 0) != ERROR_SUCCESS) + if (RegSetValueW(hKey, L"shell\\open", REG_SZ, commandTextW.c_str(), 0) != ERROR_SUCCESS) { goto fail; } // [hRootKey\OpenRCT2.sv6\shell\open\command] wchar_t szCommandW[MAX_PATH]; - printResult = swprintf_s(szCommandW, MAX_PATH, L"\"%s\" %s", exePathW, commandArgsW); + printResult = swprintf_s(szCommandW, MAX_PATH, L"\"%s\" %s", exePathW, commandArgsW.c_str()); assert(printResult >= 0); if (RegSetValueW(hKey, L"shell\\open\\command", REG_SZ, szCommandW, 0) != ERROR_SUCCESS) { @@ -636,10 +633,6 @@ static bool windows_setup_file_association( result = true; fail: - free(extensionW); - free(fileTypeTextW); - free(commandTextW); - free(commandArgsW); RegCloseKey(hKey); RegCloseKey(hRootKey); return result; @@ -655,9 +648,8 @@ static void windows_remove_file_association(const utf8* extension) RegDeleteTreeA(hRootKey, extension); // [hRootKey\OpenRCT2.ext] - wchar_t progIdName[128]; - get_progIdName(progIdName, extension); - RegDeleteTreeW(hRootKey, progIdName); + auto progIdName = get_progIdName(extension); + RegDeleteTreeW(hRootKey, progIdName.c_str()); RegCloseKey(hRootKey); } @@ -720,7 +712,7 @@ bool platform_setup_uri_protocol() GetModuleFileNameW(nullptr, exePath, MAX_PATH); wchar_t buffer[512]; - swprintf_s(buffer, sizeof(buffer), L"\"%s\" handle-uri \"%%1\"", exePath); + swprintf_s(buffer, std::size(buffer), L"\"%s\" handle-uri \"%%1\"", exePath); if (RegSetValueW(hClassKey, L"shell\\open\\command", REG_SZ, buffer, 0) == ERROR_SUCCESS) { // Not compulsory, but gives the application a nicer name @@ -728,11 +720,11 @@ bool platform_setup_uri_protocol() HKEY hMuiCacheKey; if (RegCreateKeyW(hRootKey, MUI_CACHE, &hMuiCacheKey) == ERROR_SUCCESS) { - swprintf_s(buffer, sizeof(buffer), L"%s.FriendlyAppName", exePath); + swprintf_s(buffer, std::size(buffer), L"%s.FriendlyAppName", exePath); // mingw-w64 used to define RegSetKeyValueW's signature incorrectly // You need at least mingw-w64 5.0 including this commit: // https://sourceforge.net/p/mingw-w64/mingw-w64/ci/da9341980a4b70be3563ac09b5927539e7da21f7/ - RegSetKeyValueW(hMuiCacheKey, nullptr, buffer, REG_SZ, L"OpenRCT2", sizeof(L"OpenRCT2") + 1); + RegSetKeyValueW(hMuiCacheKey, nullptr, buffer, REG_SZ, L"OpenRCT2", sizeof(L"OpenRCT2")); } log_verbose("URI protocol setup successful"); diff --git a/src/openrct2/platform/macos.mm b/src/openrct2/platform/macos.mm index 45fa6087b4..58a5813bd4 100644 --- a/src/openrct2/platform/macos.mm +++ b/src/openrct2/platform/macos.mm @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/platform/platform.h b/src/openrct2/platform/platform.h index 6726dcb734..5a43d04b73 100644 --- a/src/openrct2/platform/platform.h +++ b/src/openrct2/platform/platform.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -99,7 +99,7 @@ bool platform_original_rct1_data_exists(const utf8* path); time_t platform_file_get_modified_time(const utf8* path); bool platform_ensure_directory_exists(const utf8* path); bool platform_directory_delete(const utf8* path); -utf8* platform_get_absolute_path(const utf8* relative_path, const utf8* base_path); +std::string platform_get_absolute_path(const utf8* relative_path, const utf8* base_path); bool platform_lock_single_instance(); bool platform_place_string_on_clipboard(utf8* target); @@ -113,7 +113,7 @@ uint32_t platform_get_ticks(); void platform_sleep(uint32_t ms); void platform_get_openrct_data_path(utf8* outPath, size_t outSize); void platform_get_user_directory(utf8* outPath, const utf8* subDirectory, size_t outSize); -utf8* platform_get_username(); +std::string platform_get_username(); bool platform_open_common_file_dialog(utf8* outFilename, file_dialog_desc* desc, size_t outSize); utf8* platform_open_directory_browser(const utf8* title); uint8_t platform_get_locale_currency(); @@ -126,6 +126,7 @@ bool platform_process_is_elevated(); bool platform_get_steam_path(utf8* outPath, size_t outSize); std::string platform_get_rct1_steam_dir(); std::string platform_get_rct2_steam_dir(); +std::string platform_sanitise_filename(const std::string&); #ifndef NO_TTF bool platform_get_font_path(TTFFontDescriptor* font, utf8* buffer, size_t size); diff --git a/src/openrct2/rct1/RCT1.h b/src/openrct2/rct1/RCT1.h index f8d5ac6a04..f4f5c8f00d 100644 --- a/src/openrct2/rct1/RCT1.h +++ b/src/openrct2/rct1/RCT1.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -27,6 +27,7 @@ #define RCT1_MAX_STAFF 116 #define RCT1_RESEARCH_FLAGS_SEPARATOR 0xFF #define RCT1_MAX_ANIMATED_OBJECTS 1000 +#define RCT1_MAX_BANNERS 100 struct ParkLoadResult; @@ -428,8 +429,8 @@ struct rct1_peep : RCT12SpriteBase uint8_t pad_C4; union { - uint8_t staff_id; // 0xC5 - ride_id_t guest_heading_to_ride_id; // 0xC5 + uint8_t staff_id; // 0xC5 + uint8_t guest_heading_to_ride_id; // 0xC5 }; union { @@ -665,7 +666,7 @@ struct rct1_s4 uint16_t unk_199C9A; rct1_research_item research_items_LL[180]; uint8_t unk_19A020[5468]; - rct_banner banners[100]; + RCT12Banner banners[RCT1_MAX_BANNERS]; char string_table[RCT12_MAX_USER_STRINGS][RCT12_USER_STRING_MAX_LENGTH]; uint32_t game_time_counter; rct1_ride rides[RCT12_MAX_RIDES_IN_PARK]; @@ -674,14 +675,14 @@ struct rct1_s4 uint16_t view_y; uint8_t view_zoom; uint8_t view_rotation; - rct_map_animation map_animations[RCT1_MAX_ANIMATED_OBJECTS]; + RCT12MapAnimation map_animations[RCT1_MAX_ANIMATED_OBJECTS]; uint32_t num_map_animations; uint8_t unk_1CADBC[12]; uint16_t scrolling_text_step; uint32_t unk_1CADCA; uint16_t unk_1CADCE; uint8_t unk_1CADD0[116]; - rct_ride_measurement ride_measurements[8]; + RCT12RideMeasurement ride_measurements[8]; uint32_t next_guest_index; uint16_t game_counter_5; uint8_t patrol_areas[(RCT1_MAX_STAFF + RCT12_STAFF_TYPE_COUNT) * RCT12_PATROL_AREA_SIZE]; @@ -711,7 +712,7 @@ struct rct1_s4 assert_struct_size(rct1_s4, 0x1F850C); /** - * Track design structure. + * Track design structure. Only for base RCT1 * size: 0x2006 */ struct rct_track_td4 @@ -754,18 +755,25 @@ struct rct_track_td4 uint8_t intensity; // 0x34 uint8_t nausea; // 0x35 money16 upkeep_cost; // 0x36 +}; - // Added Attractions / Loopy Landscapes only +assert_struct_size(rct_track_td4, 0x38); + +/** + * Track design structure for Added Attractions / Loopy Landscapes + * size: 0x2006 + */ +struct rct_track_td4_aa : public rct_track_td4 +{ uint8_t track_spine_colour[RCT12_NUM_COLOUR_SCHEMES]; // 0x38 uint8_t track_rail_colour[RCT12_NUM_COLOUR_SCHEMES]; // 0x3C uint8_t track_support_colour[RCT12_NUM_COLOUR_SCHEMES]; // 0x40 uint8_t flags2; // 0x44 - uint8_t var_45[0x7F]; // 0x45 - - void* elements; // 0xC4 (data starts here in file, 38 for original RCT1) - size_t elementsSize; + uint8_t pad_45[0x7F]; // 0x45 }; + +assert_struct_size(rct_track_td4_aa, 0xC4); #pragma pack(pop) enum diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 27317c6021..9d2fdfb7cd 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,7 @@ #include "../Game.h" #include "../GameState.h" #include "../ParkImporter.h" +#include "../actions/WallPlaceAction.hpp" #include "../audio/audio.h" #include "../core/Collections.hpp" #include "../core/Console.hpp" @@ -103,6 +104,7 @@ private: rct1_s4 _s4 = {}; uint8_t _gameVersion = 0; uint8_t _parkValueConversionFactor = 0; + bool _isScenario = false; // Lists of dynamic object entries EntryList _rideEntries; @@ -168,6 +170,7 @@ public: { _s4 = *ReadAndDecodeS4(stream, isScenario); _s4Path = path; + _isScenario = isScenario; // Only determine what objects we required to import this saved game InitialiseEntryMaps(); @@ -186,7 +189,6 @@ public: ImportRideMeasurements(); ImportSprites(); ImportTileElements(); - ImportMapAnimations(); ImportPeepSpawns(); ImportFinance(); ImportResearch(); @@ -197,11 +199,11 @@ public: ImportScenarioObjective(); ImportSavedView(); FixLandOwnership(); + FixUrbanPark(); CountBlockSections(); + SetDefaultNames(); determine_ride_entrance_and_exit_locations(); - // Importing the strings is done later on, although that approach needs looking at. - // game_convert_strings_to_utf8(); game_convert_news_items_to_utf8(); map_count_remaining_land_rights(); } @@ -543,7 +545,7 @@ private: { for (size_t i = 0; i < std::size(_s4.rides); i++) { - rct1_ride* ride = &_s4.rides[i]; + auto ride = &_s4.rides[i]; if (ride->type != RCT1_RIDE_TYPE_NULL) { if (RCT1::RideTypeUsesVehicles(ride->type)) @@ -737,7 +739,7 @@ private: { if (_s4.rides[i].type != RIDE_TYPE_NULL) { - ImportRide(get_ride(i), &_s4.rides[i], i); + ImportRide(GetOrAllocateRide(i), &_s4.rides[i], i); } } } @@ -776,22 +778,9 @@ private: } // Ride name - dst->name = 0; if (is_user_string_id(src->name)) { - std::string rideName = GetUserString(src->name); - if (!rideName.empty()) - { - rct_string_id rideNameStringId = user_string_allocate(USER_STRING_HIGH_ID_NUMBER, rideName.c_str()); - if (rideNameStringId != 0) - { - dst->name = rideNameStringId; - } - } - } - if (dst->name == 0) - { - ride_set_name_to_default(dst, rideEntry); + dst->custom_name = GetUserString(src->name); } dst->status = src->status; @@ -944,10 +933,13 @@ private: dst->drops = src->num_drops; dst->start_drop_height = src->start_drop_height / 2; dst->highest_drop_height = src->highest_drop_height / 2; - dst->inversions = src->num_inversions; + if (dst->type == RIDE_TYPE_MINI_GOLF) + dst->holes = src->num_inversions & 0x1F; + else + dst->inversions = src->num_inversions & 0x1F; + dst->sheltered_eighths = src->num_inversions >> 5; dst->boat_hire_return_direction = src->boat_hire_return_direction; dst->boat_hire_return_position = src->boat_hire_return_position; - dst->measurement_index = src->data_logging_index; dst->chairlift_bullwheel_rotation = src->chairlift_bullwheel_rotation; for (int i = 0; i < 2; i++) { @@ -1086,16 +1078,14 @@ private: void FixRideVehicleLinks(const uint16_t* spriteIndexMap) { - uint8_t i; - Ride* ride; - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - for (uint8_t j = 0; j < std::size(ride->vehicles); j++) + for (uint8_t j = 0; j < std::size(ride.vehicles); j++) { - uint16_t originalIndex = ride->vehicles[j]; + uint16_t originalIndex = ride.vehicles[j]; if (originalIndex != SPRITE_INDEX_NULL) { - ride->vehicles[j] = spriteIndexMap[originalIndex]; + ride.vehicles[j] = spriteIndexMap[originalIndex]; } } } @@ -1103,23 +1093,34 @@ private: void ImportRideMeasurements() { - for (int32_t i = 0; i < MAX_RIDE_MEASUREMENTS; i++) + for (const auto& src : _s4.ride_measurements) { - rct_ride_measurement* dst = get_ride_measurement(i); - rct_ride_measurement* src = &_s4.ride_measurements[i]; - ImportRideMeasurement(dst, src); + if (src.ride_index != RCT12_RIDE_ID_NULL) + { + auto ride = get_ride(src.ride_index); + if (ride != nullptr) + { + ride->measurement = std::make_unique(); + ImportRideMeasurement(*ride->measurement, src); + } + } } } - void ImportRideMeasurement(rct_ride_measurement* dst, rct_ride_measurement* src) + void ImportRideMeasurement(RideMeasurement& dst, const RCT12RideMeasurement& src) { - *dst = *src; - for (int32_t i = 0; i < RIDE_MEASUREMENT_MAX_ITEMS; i++) + dst.flags = src.flags; + dst.last_use_tick = src.last_use_tick; + dst.num_items = src.num_items; + dst.current_item = src.current_item; + dst.vehicle_index = src.vehicle_index; + dst.current_station = src.current_station; + for (size_t i = 0; i < std::size(src.velocity); i++) { - dst->velocity[i] /= 2; - dst->altitude[i] /= 2; - dst->vertical[i] /= 2; - dst->lateral[i] /= 2; + dst.velocity[i] = src.velocity[i] / 2; + dst.altitude[i] = src.altitude[i] / 2; + dst.vertical[i] = src.vertical[i] / 2; + dst.lateral[i] = src.lateral[i] / 2; } } @@ -1152,7 +1153,7 @@ private: // If vehicle is the first car on a train add to train list if (vehicle->IsHead()) { - move_sprite_to_list((rct_sprite*)vehicle, SPRITE_LIST_TRAIN * 2); + move_sprite_to_list((rct_sprite*)vehicle, SPRITE_LIST_VEHICLE_HEAD); } } } @@ -1166,7 +1167,10 @@ private: void ImportVehicle(rct_vehicle* dst, rct1_vehicle* src) { - Ride* ride = get_ride(src->ride); + auto ride = get_ride(src->ride); + if (ride == nullptr) + return; + uint8_t vehicleEntryIndex = RCT1::GetVehicleSubEntryIndex(src->vehicle_type); dst->sprite_identifier = SPRITE_IDENTIFIER_VEHICLE; @@ -1205,8 +1209,8 @@ private: dst->sound_vector_factor = src->sound_vector_factor; dst->spin_speed = src->spin_speed; dst->sound2_flags = src->sound2_flags; - dst->sound1_id = RCT12_SOUND_ID_NULL; - dst->sound2_id = RCT12_SOUND_ID_NULL; + dst->sound1_id = SoundId::Null; + dst->sound2_id = SoundId::Null; dst->var_C0 = src->var_C0; dst->var_C4 = src->var_C4; dst->animation_frame = src->animation_frame; @@ -1214,7 +1218,7 @@ private: dst->var_CA = src->var_CA; dst->var_CE = src->var_CE; dst->var_D3 = src->var_D3; - dst->scream_sound_id = 255; + dst->scream_sound_id = SoundId::Null; dst->vehicle_sprite_type = src->vehicle_sprite_type; dst->bank_rotation = src->bank_rotation; @@ -1238,6 +1242,12 @@ private: } } + VEHICLE_STATUS statusSrc = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; + if (src->status <= static_cast(VEHICLE_STATUS_STOPPED_BY_BLOCK_BRAKES)) + { + statusSrc = static_cast(src->status); + } + dst->status = statusSrc; dst->var_CD = src->var_CD; dst->track_x = src->track_x; dst->track_y = src->track_y; @@ -1246,7 +1256,6 @@ private: dst->track_type = src->track_type; dst->track_progress = src->track_progress; dst->vertical_drop_countdown = src->vertical_drop_countdown; - dst->status = src->status; dst->sub_state = src->sub_state; dst->update_flags = src->update_flags; @@ -1344,7 +1353,6 @@ private: { rct1_peep* srcPeep = &_s4.sprites[i].peep; Peep* peep = (Peep*)create_sprite(SPRITE_IDENTIFIER_PEEP); - move_sprite_to_list((rct_sprite*)peep, SPRITE_LIST_PEEP * 2); spriteIndexMap[i] = peep->sprite_index; ImportPeep(peep, srcPeep); @@ -1360,15 +1368,13 @@ private: } } - int i; - Ride* ride; - Peep* peep; - - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - FixRidePeepLinks(ride, spriteIndexMap); + FixRidePeepLinks(&ride, spriteIndexMap); } + int32_t i; + Peep* peep; FOR_ALL_GUESTS (i, peep) { FixPeepNextInQueue(peep, spriteIndexMap); @@ -1423,18 +1429,9 @@ private: dst->sprite_direction = src->sprite_direction; // Peep name - dst->name_string_idx = src->name_string_idx; if (is_user_string_id(src->name_string_idx)) { - std::string peepName = GetUserString(src->name_string_idx); - if (!peepName.empty()) - { - rct_string_id peepNameStringId = user_string_allocate(USER_STRING_HIGH_ID_NUMBER, peepName.c_str()); - if (peepNameStringId != 0) - { - dst->name_string_idx = peepNameStringId; - } - } + dst->SetName(GetUserString(src->name_string_idx)); } dst->outside_of_park = src->outside_of_park; @@ -1660,8 +1657,6 @@ private: const auto* srcLitter = &sprite.litter; rct_litter* litter = (rct_litter*)create_sprite(SPRITE_IDENTIFIER_LITTER); - move_sprite_to_list((rct_sprite*)litter, SPRITE_LIST_LITTER * 2); - litter->sprite_identifier = srcLitter->sprite_identifier; litter->type = srcLitter->type; @@ -1687,8 +1682,6 @@ private: { rct1_unk_sprite* src = &sprite.unknown; rct_sprite_generic* dst = (rct_sprite_generic*)create_sprite(SPRITE_IDENTIFIER_MISC); - move_sprite_to_list((rct_sprite*)dst, SPRITE_LIST_MISC * 2); - dst->sprite_identifier = src->sprite_identifier; dst->type = src->type; dst->flags = src->flags; @@ -1716,7 +1709,7 @@ private: case SPRITE_MISC_EXPLOSION_FLARE: break; case SPRITE_MISC_JUMPING_FOUNTAIN_WATER: - ImportJumpingFountainWater((rct_jumping_fountain*)dst, (rct_jumping_fountain*)src); + ImportJumpingFountainWater((JumpingFountain*)dst, (JumpingFountain*)src); break; case SPRITE_MISC_BALLOON: ImportBalloon((rct_balloon*)dst, (rct_balloon*)src); @@ -1746,11 +1739,11 @@ private: dst->frame = src->frame; } - void ImportJumpingFountainWater(rct_jumping_fountain* dst, rct_jumping_fountain* src) + void ImportJumpingFountainWater(JumpingFountain* dst, JumpingFountain* src) { - dst->fountain_flags = src->fountain_flags; - dst->iteration = src->iteration; - dst->num_ticks_alive = src->num_ticks_alive; + dst->FountainFlags = src->FountainFlags; + dst->Iteration = src->Iteration; + dst->NumTicksAlive = src->NumTicksAlive; dst->frame = src->frame; } @@ -1804,18 +1797,6 @@ private: } } - void ImportMapAnimations() - { - // This is sketchy, ideally we should try to re-create them - rct_map_animation* s4Animations = _s4.map_animations; - for (size_t i = 0; i < RCT1_MAX_ANIMATED_OBJECTS; i++) - { - gAnimatedObjects[i] = s4Animations[i]; - gAnimatedObjects[i].baseZ /= 2; - } - gNumMapAnimations = _s4.num_map_animations; - } - void ImportFinance() { gParkEntranceFee = _s4.park_entrance_fee; @@ -2031,7 +2012,7 @@ private: void ImportTileElement(TileElement* dst, const RCT12TileElement* src) { - // Todo: allow for changing defition of OpenRCT2 tile element types - replace with a map + // Todo: allow for changing definition of OpenRCT2 tile element types - replace with a map uint8_t tileElementType = src->GetType(); dst->ClearAs(tileElementType); dst->SetDirection(src->GetDirection()); @@ -2137,18 +2118,29 @@ private: dst2->SetSequenceIndex(src2->GetSequenceIndex()); dst2->SetRideIndex(src2->GetRideIndex()); dst2->SetColourScheme(src2->GetColourScheme()); - dst2->SetStationIndex(src2->GetStationIndex()); dst2->SetHasChain(src2->HasChain()); dst2->SetHasCableLift(false); dst2->SetInverted(src2->IsInverted()); - dst2->SetBrakeBoosterSpeed(src2->GetBrakeBoosterSpeed()); + dst2->SetDoorAState(src2->GetDoorAState()); + dst2->SetDoorBState(src2->GetDoorBState()); + dst2->SetStationIndex(src2->GetStationIndex()); dst2->SetHasGreenLight(src2->HasGreenLight()); - dst2->SetSeatRotation(4); - dst2->SetMazeEntry(src2->GetMazeEntry()); - dst2->SetPhotoTimeout(src2->GetPhotoTimeout()); - // Skipping IsHighlighted() - // TODO: Import Door A and Door B states. + auto trackType = dst2->GetTrackType(); + if (track_element_has_speed_setting(trackType)) + { + dst2->SetBrakeBoosterSpeed(src2->GetBrakeBoosterSpeed()); + } + else if (trackType == TRACK_ELEM_ON_RIDE_PHOTO) + { + dst2->SetPhotoTimeout(src2->GetPhotoTimeout()); + } + + if (_s4.rides[src2->GetRideIndex()].type == RIDE_TYPE_MAZE) + { + dst2->SetMazeEntry(src2->GetMazeEntry()); + } + // Skipping IsHighlighted() break; } @@ -2241,10 +2233,12 @@ private: dst2->SetPosition(src2->GetPosition()); dst2->SetAllowedEdges(src2->GetAllowedEdges()); - rct_banner* srcBanner = &_s4.banners[index]; - rct_banner* dstBanner = &gBanners[index]; - ImportBanner(dstBanner, srcBanner); - + if (index < std::size(_s4.banners)) + { + auto srcBanner = &_s4.banners[index]; + auto dstBanner = GetBanner(index); + ImportBanner(dstBanner, srcBanner); + } break; } default: @@ -2458,12 +2452,8 @@ private: } } - rct_string_id stringId = user_string_allocate(USER_STRING_HIGH_ID_NUMBER, parkName.c_str()); - if (stringId != 0) - { - gParkName = stringId; - gParkNameArgs = 0; - } + auto& park = GetContext()->GetGameState()->GetPark(); + park.Name = parkName; } void ImportParkFlags() @@ -2514,7 +2504,7 @@ private: uint8_t researchItem = src->Assoc & 0x000000FF; uint8_t researchType = (src->Assoc & 0x00FF0000) >> 16; - rct_research_item tmpResearchItem = {}; + ResearchItem tmpResearchItem = {}; ConvertResearchEntry(&tmpResearchItem, researchItem, researchType); dst->Assoc = (uint32_t)tmpResearchItem.rawValue; } @@ -2558,7 +2548,7 @@ private: gTotalRideValueForMoney = _s4.total_ride_value_for_money; } - void ConvertResearchEntry(rct_research_item* dst, uint8_t srcItem, uint8_t srcType) + void ConvertResearchEntry(ResearchItem* dst, uint8_t srcItem, uint8_t srcType) { dst->rawValue = RESEARCHED_ITEMS_SEPARATOR; if (srcType == RCT1_RESEARCH_TYPE_RIDE) @@ -2636,7 +2626,7 @@ private: if (scNumber != -1) { source_desc sourceDesc; - if (scenario_get_source_desc_by_id(scNumber, &sourceDesc)) + if (ScenarioSources::TryGetById(scNumber, &sourceDesc)) { rct_string_id localisedStringIds[3]; if (language_get_localised_scenario_strings(sourceDesc.title, localisedStringIds)) @@ -2712,7 +2702,7 @@ private: for (int32_t y = 0; y < RCT1_MAX_MAP_SIZE; y++) { nextFreeTileElement->ClearAs(TILE_ELEMENT_TYPE_SURFACE); - nextFreeTileElement->flags = TILE_ELEMENT_FLAG_LAST_TILE; + nextFreeTileElement->SetLastForTile(true); nextFreeTileElement->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT); nextFreeTileElement->AsSurface()->SetSurfaceStyle(TERRAIN_GRASS); nextFreeTileElement->AsSurface()->SetEdgeStyle(TERRAIN_EDGE_ROCK); @@ -2726,7 +2716,7 @@ private: for (int32_t y = 0; y < 128 * 256; y++) { nextFreeTileElement->ClearAs(TILE_ELEMENT_TYPE_SURFACE); - nextFreeTileElement->flags = TILE_ELEMENT_FLAG_LAST_TILE; + nextFreeTileElement->SetLastForTile(true); nextFreeTileElement->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT); nextFreeTileElement->AsSurface()->SetSurfaceStyle(TERRAIN_GRASS); nextFreeTileElement->AsSurface()->SetEdgeStyle(TERRAIN_EDGE_ROCK); @@ -2750,6 +2740,8 @@ private: for (int32_t y = 0; y < RCT1_MAX_MAP_SIZE; y++) { TileElement* tileElement = map_get_first_element_at(x, y); + if (tileElement == nullptr) + continue; do { if (tileElement->GetType() == TILE_ELEMENT_TYPE_WALL) @@ -2769,10 +2761,13 @@ private: ConvertWall(&type, &colourA, &colourB); type = _wallTypeToEntryMap[type]; - const uint8_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED - | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_PATH_SCENERY; - wall_place(type, x * 32, y * 32, 0, edge, colourA, colourB, colourC, flags); + auto wallPlaceAction = WallPlaceAction( + type, { x * 32, y * 32, 0 }, edge, colourA, colourB, colourC); + wallPlaceAction.SetFlags( + GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND + | GAME_COMMAND_FLAG_PATH_SCENERY); + GameActions::Execute(&wallPlaceAction); } } break; @@ -2819,24 +2814,26 @@ private: } } - void ImportBanner(rct_banner* dst, rct_banner* src) + void ImportBanner(Banner* dst, const RCT12Banner* src) { - *dst = *src; - dst->colour = RCT1::GetColour(src->colour); + *dst = {}; + dst->type = src->type; + + dst->flags = 0; + if (src->flags & BANNER_FLAG_NO_ENTRY) + { + dst->flags |= BANNER_FLAG_NO_ENTRY; + } - dst->string_idx = STR_DEFAULT_SIGN; if (is_user_string_id(src->string_idx)) { - std::string bannerText = GetUserString(src->string_idx); - if (!bannerText.empty()) - { - rct_string_id bannerTextStringId = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, bannerText.c_str()); - if (bannerTextStringId != 0) - { - dst->string_idx = bannerTextStringId; - } - } + dst->text = GetUserString(src->string_idx); } + + dst->colour = RCT1::GetColour(src->colour); + dst->text_colour = src->text_colour; + dst->position.x = src->x; + dst->position.y = src->y; } void FixEntrancePositions() @@ -2905,8 +2902,10 @@ private: std::string GetUserString(rct_string_id stringId) { - const char* originalString = _s4.string_table[(stringId - USER_STRING_START) % 1024]; - return rct2_to_utf8(originalString, RCT2_LANGUAGE_ID_ENGLISH_UK); + const auto originalString = _s4.string_table[(stringId - USER_STRING_START) % 1024]; + std::string_view originalStringView(originalString, USER_STRING_MAX_LENGTH); + auto withoutFormatCodes = RCT12::RemoveFormatCodes(originalStringView); + return rct2_to_utf8(withoutFormatCodes, RCT2_LANGUAGE_ID_ENGLISH_UK); } void FixLandOwnership() @@ -2940,6 +2939,53 @@ private: } } + /** + * In Urban Park, the entrance and exit of the merry-go-round are the wrong way round. This code fixes that. + * To avoid messing up saves (in which this problem is most likely solved by the user), only carry out this + * fix when loading from a scenario. + */ + void FixUrbanPark() + { + if (_s4.scenario_slot_index == SC_URBAN_PARK && _isScenario) + { + // First, make the queuing peep exit + int32_t i; + Peep* peep; + FOR_ALL_GUESTS (i, peep) + { + if (peep->state == PEEP_STATE_QUEUING_FRONT && peep->current_ride == 0) + { + peep->RemoveFromQueue(); + peep->SetState(PEEP_STATE_FALLING); + break; + } + } + + // Now, swap the entrance and exit. + auto ride = get_ride(0); + if (ride != nullptr) + { + auto entranceCoords = ride->stations[0].Exit; + auto exitCoords = ride->stations[0].Entrance; + ride->stations[0].Entrance = entranceCoords; + ride->stations[0].Exit = exitCoords; + + auto entranceElement = map_get_ride_exit_element_at( + entranceCoords.x * 32, entranceCoords.y * 32, entranceCoords.z, false); + entranceElement->SetEntranceType(ENTRANCE_TYPE_RIDE_ENTRANCE); + auto exitElement = map_get_ride_entrance_element_at(exitCoords.x * 32, exitCoords.y * 32, exitCoords.z, false); + exitElement->SetEntranceType(ENTRANCE_TYPE_RIDE_EXIT); + + // Trigger footpath update + footpath_queue_chain_reset(); + footpath_connect_edges( + entranceCoords.x * 32, (entranceCoords.y) * 32, (TileElement*)entranceElement, + GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); + footpath_update_queue_chains(); + } + } + } + /** * Counts the block sections. The reason this iterates over the map is to avoid getting into infinite loops, * which can happen with hacked parks. @@ -2951,6 +2997,8 @@ private: for (int32_t y = 0; y < RCT1_MAX_MAP_SIZE; y++) { TileElement* tileElement = map_get_first_element_at(x, y); + if (tileElement == nullptr) + continue; do { if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK) @@ -2972,13 +3020,31 @@ private: } ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - ride->num_block_brakes++; + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + ride->num_block_brakes++; + } } } while (!(tileElement++)->IsLastForTile()); } } } + + /** + * This has to be done after importing tile elements, because it needs those to detect if a pre-existing ride + * name should be considered reserved. + */ + void SetDefaultNames() + { + for (auto& ride : GetRideManager()) + { + if (ride.custom_name.empty()) + { + ride.SetNameToDefault(); + } + } + } }; std::unique_ptr ParkImporter::CreateS4() diff --git a/src/openrct2/rct1/T4Importer.cpp b/src/openrct2/rct1/T4Importer.cpp new file mode 100644 index 0000000000..094f0fe272 --- /dev/null +++ b/src/openrct2/rct1/T4Importer.cpp @@ -0,0 +1,294 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include "../TrackImporter.h" +#include "../config/Config.h" +#include "../core/FileStream.hpp" +#include "../core/MemoryStream.h" +#include "../core/Path.hpp" +#include "../core/String.hpp" +#include "../rct1/RCT1.h" +#include "../rct1/Tables.h" +#include "../rct12/SawyerChunkReader.h" +#include "../rct12/SawyerEncoding.h" +#include "../ride/Ride.h" +#include "../ride/TrackDesign.h" +#include "../ride/TrackDesignRepository.h" + +/** + * Class to import RollerCoaster Tycoon 1 track designs (*.TD4). + */ +class TD4Importer final : public ITrackImporter +{ +private: + MemoryStream _stream; + std::string _name; + +public: + TD4Importer() + { + } + + bool Load(const utf8* path) override + { + const utf8* extension = Path::GetExtension(path); + if (String::Equals(extension, ".td4", true)) + { + _name = GetNameFromTrackPath(path); + auto fs = FileStream(path, FILE_MODE_OPEN); + return LoadFromStream(&fs); + } + else + { + throw std::runtime_error("Invalid RCT1 track extension."); + } + } + + bool LoadFromStream(IStream* stream) override + { + auto checksumType = SawyerEncoding::ValidateTrackChecksum(stream); + if (!gConfigGeneral.allow_loading_with_incorrect_checksum && checksumType == RCT12TrackDesignVersion::unknown) + { + throw IOException("Invalid checksum."); + } + + auto chunkReader = SawyerChunkReader(stream); + auto data = chunkReader.ReadChunkTrack(); + _stream.WriteArray(reinterpret_cast(data->GetData()), data->GetLength()); + _stream.SetPosition(0); + return true; + } + + std::unique_ptr Import() override + { + std::unique_ptr td = std::make_unique(); + + _stream.SetPosition(7); + RCT12TrackDesignVersion version = static_cast(_stream.ReadValue() >> 2); + + if (version != RCT12TrackDesignVersion::TD4 && version != RCT12TrackDesignVersion::TD4_AA) + { + throw IOException("Version number incorrect."); + } + _stream.SetPosition(0); + + if (version == RCT12TrackDesignVersion::TD4_AA) + { + return ImportAA(); + } + else + { + return ImportTD4(); + } + } + +private: + std::unique_ptr ImportAA() + { + std::unique_ptr td = std::make_unique(); + rct_track_td4_aa td4aa{}; + _stream.Read(&td4aa, sizeof(rct_track_td4_aa)); + + for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) + { + td->track_spine_colour[i] = RCT1::GetColour(td4aa.track_spine_colour[i]); + td->track_rail_colour[i] = RCT1::GetColour(td4aa.track_rail_colour[i]); + td->track_support_colour[i] = RCT1::GetColour(td4aa.track_support_colour[i]); + } + + td->flags2 = td4aa.flags2; + + return ImportTD4Base(std::move(td), td4aa); + } + + std::unique_ptr ImportTD4() + { + std::unique_ptr td = std::make_unique(); + rct_track_td4 td4{}; + _stream.Read(&td4, sizeof(rct_track_td4)); + for (int32_t i = 0; i < NUM_COLOUR_SCHEMES; i++) + { + td->track_spine_colour[i] = RCT1::GetColour(td4.track_spine_colour_v0); + td->track_rail_colour[i] = RCT1::GetColour(td4.track_rail_colour_v0); + td->track_support_colour[i] = RCT1::GetColour(td4.track_support_colour_v0); + + // Mazes were only hedges + switch (td4.type) + { + case RCT1_RIDE_TYPE_HEDGE_MAZE: + td->track_support_colour[i] = MAZE_WALL_TYPE_HEDGE; + break; + case RCT1_RIDE_TYPE_RIVER_RAPIDS: + td->track_spine_colour[i] = COLOUR_WHITE; + td->track_rail_colour[i] = COLOUR_WHITE; + break; + } + } + td->flags2 = 0; + return ImportTD4Base(std::move(td), td4); + } + + std::unique_ptr ImportTD4Base(std::unique_ptr td, rct_track_td4& td4Base) + { + td->type = RCT1::GetRideType(td4Base.type); + + // All TD4s that use powered launch use the type that doesn't pass the station. + td->ride_mode = td4Base.mode; + if (td4Base.mode == RCT1_RIDE_MODE_POWERED_LAUNCH) + { + td->ride_mode = RIDE_MODE_POWERED_LAUNCH; + } + + // Convert RCT1 vehicle type to RCT2 vehicle type. Intialise with an string consisting of 8 spaces. + rct_object_entry vehicleObject = { 0x80, " " }; + if (td4Base.type == RIDE_TYPE_MAZE) + { + const char* vehObjName = RCT1::GetRideTypeObject(td4Base.type); + assert(vehObjName != nullptr); + std::memcpy(vehicleObject.name, vehObjName, std::min(String::SizeOf(vehObjName), (size_t)8)); + } + else + { + const char* vehObjName = RCT1::GetVehicleObject(td4Base.vehicle_type); + assert(vehObjName != nullptr); + std::memcpy(vehicleObject.name, vehObjName, std::min(String::SizeOf(vehObjName), (size_t)8)); + } + std::memcpy(&td->vehicle_object, &vehicleObject, sizeof(rct_object_entry)); + td->vehicle_type = td4Base.vehicle_type; + + td->flags = td4Base.flags; + td->colour_scheme = td4Base.version_and_colour_scheme & 0x3; + + // Vehicle colours + for (int32_t i = 0; i < RCT1_MAX_TRAINS_PER_RIDE; i++) + { + // RCT1 had no third colour + RCT1::RCT1VehicleColourSchemeCopyDescriptor colourSchemeCopyDescriptor = RCT1::GetColourSchemeCopyDescriptor( + td4Base.vehicle_type); + if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_1) + { + td->vehicle_colours[i].body_colour = RCT1::GetColour(td4Base.vehicle_colours[i].body_colour); + } + else if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_2) + { + td->vehicle_colours[i].body_colour = RCT1::GetColour(td4Base.vehicle_colours[i].trim_colour); + } + else + { + td->vehicle_colours[i].body_colour = colourSchemeCopyDescriptor.colour1; + } + + if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_1) + { + td->vehicle_colours[i].trim_colour = RCT1::GetColour(td4Base.vehicle_colours[i].body_colour); + } + else if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_2) + { + td->vehicle_colours[i].trim_colour = RCT1::GetColour(td4Base.vehicle_colours[i].trim_colour); + } + else + { + td->vehicle_colours[i].trim_colour = colourSchemeCopyDescriptor.colour2; + } + + if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_1) + { + td->vehicle_additional_colour[i] = RCT1::GetColour(td4Base.vehicle_colours[i].body_colour); + } + else if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_2) + { + td->vehicle_additional_colour[i] = RCT1::GetColour(td4Base.vehicle_colours[i].trim_colour); + } + else + { + td->vehicle_additional_colour[i] = colourSchemeCopyDescriptor.colour3; + } + } + // Set remaining vehicles to same colour as first vehicle + for (int32_t i = RCT1_MAX_TRAINS_PER_RIDE; i < MAX_VEHICLES_PER_RIDE; i++) + { + td->vehicle_colours[i] = td->vehicle_colours[0]; + td->vehicle_additional_colour[i] = td->vehicle_additional_colour[0]; + } + + td->depart_flags = td4Base.depart_flags; + td->number_of_trains = td4Base.number_of_trains; + td->number_of_cars_per_train = td4Base.number_of_cars_per_train; + td->min_waiting_time = td4Base.min_waiting_time; + td->max_waiting_time = td4Base.max_waiting_time; + td->operation_setting = std::min(td4Base.operation_setting, RideProperties[td->type].max_value); + td->max_speed = td4Base.max_speed; + td->average_speed = td4Base.average_speed; + td->ride_length = td4Base.ride_length; + td->max_positive_vertical_g = td4Base.max_positive_vertical_g; + td->max_negative_vertical_g = td4Base.max_negative_vertical_g; + td->max_lateral_g = td4Base.max_lateral_g; + + if (td->type == RIDE_TYPE_MINI_GOLF) + { + td->holes = td4Base.num_holes; + } + else + { + td->inversions = td4Base.num_inversions; + } + + td->drops = td4Base.num_drops; + td->highest_drop_height = td4Base.highest_drop_height / 2; + td->excitement = td4Base.excitement; + td->intensity = td4Base.intensity; + td->nausea = td4Base.nausea; + td->upkeep_cost = td4Base.upkeep_cost; + td->space_required_x = 255; + td->space_required_y = 255; + td->lift_hill_speed = 5; + td->num_circuits = 0; + td->operation_setting = std::min(td->operation_setting, RideProperties[td->type].max_value); + + if (td->type == RIDE_TYPE_MAZE) + { + rct_td46_maze_element t4MazeElement{}; + t4MazeElement.all = !0; + while (t4MazeElement.all != 0) + { + _stream.Read(&t4MazeElement, sizeof(rct_td46_maze_element)); + if (t4MazeElement.all != 0) + { + TrackDesignMazeElement mazeElement{}; + mazeElement.x = t4MazeElement.x; + mazeElement.y = t4MazeElement.y; + mazeElement.direction = t4MazeElement.direction; + mazeElement.type = t4MazeElement.type; + td->maze_elements.push_back(mazeElement); + } + } + } + else + { + rct_td46_track_element t4TrackElement{}; + for (uint8_t endFlag = _stream.ReadValue(); endFlag != 0xFF; endFlag = _stream.ReadValue()) + { + _stream.SetPosition(_stream.GetPosition() - 1); + _stream.Read(&t4TrackElement, sizeof(rct_td46_track_element)); + TrackDesignTrackElement trackElement{}; + trackElement.type = t4TrackElement.type; + trackElement.flags = t4TrackElement.flags; + td->track_elements.push_back(trackElement); + } + } + + td->name = _name; + return td; + } +}; + +std::unique_ptr TrackImporter::CreateTD4() +{ + return std::make_unique(); +} diff --git a/src/openrct2/rct1/Tables.cpp b/src/openrct2/rct1/Tables.cpp index 3132b86571..242630c663 100644 --- a/src/openrct2/rct1/Tables.cpp +++ b/src/openrct2/rct1/Tables.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/rct1/Tables.h b/src/openrct2/rct1/Tables.h index f77df9c440..3fccd2bc11 100644 --- a/src/openrct2/rct1/Tables.h +++ b/src/openrct2/rct1/Tables.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/rct12/RCT12.cpp b/src/openrct2/rct12/RCT12.cpp index 03f0bde5b2..12caae2835 100644 --- a/src/openrct2/rct12/RCT12.cpp +++ b/src/openrct2/rct12/RCT12.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -9,8 +9,11 @@ #include "RCT12.h" +#include "../localisation/Localisation.h" #include "../ride/Track.h" +#include "../world/Banner.h" #include "../world/Footpath.h" +#include "../world/LargeScenery.h" #include "../world/SmallScenery.h" #include "../world/Surface.h" #include "../world/TileElement.h" @@ -172,7 +175,7 @@ uint8_t RCT12TrackElement::GetTrackType() const uint8_t RCT12TrackElement::GetSequenceIndex() const { - return sequence & MAP_ELEM_TRACK_SEQUENCE_SEQUENCE_MASK; + return sequence & RCT12_TRACK_ELEMENT_SEQUENCE_SEQUENCE_MASK; } uint8_t RCT12TrackElement::GetRideIndex() const @@ -187,32 +190,44 @@ uint8_t RCT12TrackElement::GetColourScheme() const uint8_t RCT12TrackElement::GetStationIndex() const { - return (sequence & MAP_ELEM_TRACK_SEQUENCE_STATION_INDEX_MASK) >> 4; + if (trackType == TRACK_ELEM_END_STATION || trackType == TRACK_ELEM_BEGIN_STATION || trackType == TRACK_ELEM_MIDDLE_STATION) + { + return (sequence & RCT12_TRACK_ELEMENT_SEQUENCE_STATION_INDEX_MASK) >> 4; + } + return 0; } bool RCT12TrackElement::HasChain() const { - return type & TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; + return type & RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; } bool RCT12TrackElement::HasCableLift() const { - return colour & TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; + return colour & RCT12_TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; } bool RCT12TrackElement::IsInverted() const { - return colour & TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + return colour & RCT12_TRACK_ELEMENT_COLOUR_FLAG_INVERTED; } uint8_t RCT12TrackElement::GetBrakeBoosterSpeed() const { - return (sequence >> 4) << 1; + if (track_element_has_speed_setting(GetTrackType())) + { + return (sequence >> 4) << 1; + } + return 0; } bool RCT12TrackElement::HasGreenLight() const { - return (sequence & MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT) != 0; + if (trackType == TRACK_ELEM_END_STATION || trackType == TRACK_ELEM_BEGIN_STATION || trackType == TRACK_ELEM_MIDDLE_STATION) + { + return (sequence & MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT) != 0; + } + return false; } uint8_t RCT12TrackElement::GetSeatRotation() const @@ -227,17 +242,21 @@ uint16_t RCT12TrackElement::GetMazeEntry() const uint8_t RCT12TrackElement::GetPhotoTimeout() const { - return sequence >> 4; + if (GetTrackType() == TRACK_ELEM_ON_RIDE_PHOTO) + { + return sequence >> 4; + } + return 0; } uint8_t RCT12TrackElement::GetDoorAState() const { - return (colour & TRACK_ELEMENT_DOOR_A_MASK) >> 2; + return (colour & RCT12_TRACK_ELEMENT_DOOR_A_MASK) >> 2; } uint8_t RCT12TrackElement::GetDoorBState() const { - return (colour & TRACK_ELEMENT_DOOR_B_MASK) >> 5; + return (colour & RCT12_TRACK_ELEMENT_DOOR_B_MASK) >> 5; } uint8_t RCT12SmallSceneryElement::GetEntryIndex() const @@ -408,3 +427,534 @@ uint8_t RCT12BannerElement::GetAllowedEdges() const { return flags & 0b00001111; } + +bool is_user_string_id(rct_string_id stringId) +{ + return stringId >= 0x8000 && stringId < 0x9000; +} + +std::string RCT12::RemoveFormatCodes(const std::string_view& s) +{ + constexpr auto RCT12_MULTIBYTE_PREFIX = (char)(uint8_t)0xFF; + + std::string result; + result.reserve(s.size()); + + // Append each character that is not a format code + for (size_t i = 0; i < s.size(); i++) + { + auto c = s[i]; + if (c == '\0') + { + break; + } + else if (c == RCT12_MULTIBYTE_PREFIX) + { + // Multi-byte, assume not a format code + result.push_back(c); + if (i + 1 < s.size()) + { + result.push_back(s[i + 1]); + } + if (i + 2 < s.size()) + { + result.push_back(s[i + 2]); + } + i += 2; + } + else if (!utf8_is_format_code(c)) + { + result.push_back(c); + } + } + + return result; +} + +uint8_t RCT12TileElement::GetBannerIndex() +{ + rct_scenery_entry* sceneryEntry; + + switch (GetType()) + { + case TILE_ELEMENT_TYPE_LARGE_SCENERY: + sceneryEntry = get_large_scenery_entry(AsLargeScenery()->GetEntryIndex()); + if (sceneryEntry->large_scenery.scrolling_mode == SCROLLING_MODE_NONE) + return BANNER_INDEX_NULL; + + return AsLargeScenery()->GetBannerIndex(); + case TILE_ELEMENT_TYPE_WALL: + sceneryEntry = get_wall_entry(AsWall()->GetEntryIndex()); + if (sceneryEntry == nullptr || sceneryEntry->wall.scrolling_mode == SCROLLING_MODE_NONE) + return BANNER_INDEX_NULL; + + return AsWall()->GetBannerIndex(); + case TILE_ELEMENT_TYPE_BANNER: + return AsBanner()->GetIndex(); + default: + return BANNER_INDEX_NULL; + } +} + +void RCT12TileElementBase::SetDirection(uint8_t direction) +{ + this->type &= ~TILE_ELEMENT_DIRECTION_MASK; + this->type |= (direction & TILE_ELEMENT_DIRECTION_MASK); +} + +void RCT12TileElement::ClearAs(uint8_t newType) +{ + type = newType; + flags = 0; + base_height = 2; + clearance_height = 2; + std::fill_n(pad_04, sizeof(pad_04), 0x00); +} + +void RCT12LargeSceneryElement::SetEntryIndex(uint32_t newIndex) +{ + entryIndex &= ~TILE_ELEMENT_LARGE_TYPE_MASK; + entryIndex |= (newIndex & TILE_ELEMENT_LARGE_TYPE_MASK); +} + +void RCT12LargeSceneryElement::SetSequenceIndex(uint16_t sequence) +{ + entryIndex &= TILE_ELEMENT_LARGE_TYPE_MASK; + entryIndex |= (sequence << 10); +} + +void RCT12LargeSceneryElement::SetPrimaryColour(colour_t newColour) +{ + assert(newColour <= 31); + colour[0] &= ~TILE_ELEMENT_COLOUR_MASK; + colour[0] |= newColour; +} + +void RCT12LargeSceneryElement::SetSecondaryColour(colour_t newColour) +{ + assert(newColour <= 31); + colour[1] &= ~TILE_ELEMENT_COLOUR_MASK; + colour[1] |= newColour; +} + +void RCT12LargeSceneryElement::SetBannerIndex(BannerIndex newIndex) +{ + type |= newIndex & 0xC0; + colour[0] |= (newIndex & 0x38) << 2; + colour[1] |= (newIndex & 7) << 5; +} + +void RCT12SurfaceElement::SetSurfaceStyle(uint32_t newStyle) +{ + // Bit 3 for terrain is stored in element.type bit 0 + if (newStyle & 8) + type |= 1; + else + type &= ~1; + + // Bits 0, 1, 2 for terrain are stored in element.terrain bit 5, 6, 7 + terrain &= ~0xE0; + terrain |= (newStyle & 7) << 5; +} + +void RCT12SurfaceElement::SetEdgeStyle(uint32_t newStyle) +{ + // Bit 3 for terrain is stored in element.type bit 7 + if (newStyle & 8) + type |= 128; + else + type &= ~128; + + // Bits 0, 1, 2 for terrain are stored in element.slope bit 5, 6, 7 + slope &= ~TILE_ELEMENT_SURFACE_EDGE_STYLE_MASK; + slope |= (newStyle & 7) << 5; +} + +void RCT12SurfaceElement::SetWaterHeight(uint32_t newWaterHeight) +{ + newWaterHeight &= 0x1F; + terrain &= ~TILE_ELEMENT_SURFACE_WATER_HEIGHT_MASK; + terrain |= newWaterHeight; +} + +void RCT12SurfaceElement::SetGrassLength(uint8_t newLength) +{ + grass_length = newLength; +} + +void RCT12SurfaceElement::SetOwnership(uint8_t newOwnership) +{ + ownership &= ~TILE_ELEMENT_SURFACE_OWNERSHIP_MASK; + ownership |= (newOwnership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK); +} + +void RCT12SurfaceElement::SetParkFences(uint8_t newParkFences) +{ + ownership &= ~TILE_ELEMENT_SURFACE_PARK_FENCE_MASK; + ownership |= (newParkFences & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK); +} + +void RCT12SurfaceElement::SetSlope(uint8_t newSlope) +{ + slope &= ~TILE_ELEMENT_SURFACE_SLOPE_MASK; + slope |= (newSlope & TILE_ELEMENT_SURFACE_SLOPE_MASK); +} + +void RCT12SurfaceElement::SetHasTrackThatNeedsWater(bool on) +{ + type &= ~SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER; + if (on) + type |= SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER; +} + +void RCT12PathElement::SetPathEntryIndex(uint8_t newEntryIndex) +{ + entryIndex &= ~FOOTPATH_PROPERTIES_TYPE_MASK; + entryIndex |= (newEntryIndex << 4); +} + +void RCT12PathElement::SetQueueBannerDirection(uint8_t direction) +{ + type &= ~FOOTPATH_ELEMENT_TYPE_DIRECTION_MASK; + type |= (direction << 6); +} + +void RCT12PathElement::SetRideIndex(uint8_t newRideIndex) +{ + rideIndex = newRideIndex; +} + +void RCT12PathElement::SetAdditionStatus(uint8_t newStatus) +{ + additionStatus = newStatus; +} + +void RCT12PathElement::SetEdges(uint8_t newEdges) +{ + edges &= ~FOOTPATH_PROPERTIES_EDGES_EDGES_MASK; + edges |= (newEdges & FOOTPATH_PROPERTIES_EDGES_EDGES_MASK); +} + +void RCT12PathElement::SetCorners(uint8_t newCorners) +{ + edges &= ~FOOTPATH_PROPERTIES_EDGES_CORNERS_MASK; + edges |= (newCorners << 4); +} + +void RCT12PathElement::SetSloped(bool isSloped) +{ + entryIndex &= ~FOOTPATH_PROPERTIES_FLAG_IS_SLOPED; + if (isSloped) + entryIndex |= FOOTPATH_PROPERTIES_FLAG_IS_SLOPED; +} + +void RCT12PathElement::SetSlopeDirection(uint8_t newSlope) +{ + entryIndex &= ~FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK; + entryIndex |= newSlope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK; +} + +void RCT12PathElement::SetStationIndex(uint8_t newStationIndex) +{ + additions &= ~FOOTPATH_PROPERTIES_ADDITIONS_STATION_INDEX_MASK; + additions |= ((newStationIndex << 4) & FOOTPATH_PROPERTIES_ADDITIONS_STATION_INDEX_MASK); +} + +void RCT12PathElement::SetWide(bool isWide) +{ + type &= ~FOOTPATH_ELEMENT_TYPE_FLAG_IS_WIDE; + if (isWide) + type |= FOOTPATH_ELEMENT_TYPE_FLAG_IS_WIDE; +} + +void RCT12PathElement::SetIsQueue(bool isQueue) +{ + type &= ~FOOTPATH_ELEMENT_TYPE_FLAG_IS_QUEUE; + if (isQueue) + type |= FOOTPATH_ELEMENT_TYPE_FLAG_IS_QUEUE; +} + +void RCT12PathElement::SetHasQueueBanner(bool hasQueueBanner) +{ + entryIndex &= ~FOOTPATH_PROPERTIES_FLAG_HAS_QUEUE_BANNER; + if (hasQueueBanner) + entryIndex |= FOOTPATH_PROPERTIES_FLAG_HAS_QUEUE_BANNER; +} + +void RCT12PathElement::SetAddition(uint8_t newAddition) +{ + additions &= ~FOOTPATH_PROPERTIES_ADDITIONS_TYPE_MASK; + additions |= newAddition; +} + +void RCT12PathElement::SetAdditionIsGhost(bool isGhost) +{ + additions &= ~FOOTPATH_ADDITION_FLAG_IS_GHOST; + if (isGhost) + additions |= FOOTPATH_ADDITION_FLAG_IS_GHOST; +} + +void RCT12TrackElement::SetTrackType(uint8_t newType) +{ + trackType = newType; +} + +void RCT12TrackElement::SetSequenceIndex(uint8_t newSequenceIndex) +{ + sequence &= ~RCT12_TRACK_ELEMENT_SEQUENCE_SEQUENCE_MASK; + sequence |= (newSequenceIndex & RCT12_TRACK_ELEMENT_SEQUENCE_SEQUENCE_MASK); +} + +void RCT12TrackElement::SetStationIndex(uint8_t newStationIndex) +{ + if (trackType == TRACK_ELEM_END_STATION || trackType == TRACK_ELEM_BEGIN_STATION || trackType == TRACK_ELEM_MIDDLE_STATION) + { + sequence &= ~RCT12_TRACK_ELEMENT_SEQUENCE_STATION_INDEX_MASK; + sequence |= (newStationIndex << 4); + } +} + +void RCT12TrackElement::SetRideIndex(uint8_t newRideIndex) +{ + rideIndex = newRideIndex; +} + +void RCT12TrackElement::SetColourScheme(uint8_t newColourScheme) +{ + colour &= ~0x3; + colour |= (newColourScheme & 0x3); +} + +void RCT12TrackElement::SetHasCableLift(bool on) +{ + colour &= ~RCT12_TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; + if (on) + colour |= RCT12_TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; +} + +void RCT12TrackElement::SetInverted(bool inverted) +{ + if (inverted) + { + colour |= RCT12_TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + } + else + { + colour &= ~RCT12_TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + } +} + +void RCT12TrackElement::SetBrakeBoosterSpeed(uint8_t speed) +{ + if (track_element_has_speed_setting(GetTrackType())) + { + sequence &= ~0b11110000; + sequence |= ((speed >> 1) << 4); + } +} + +void RCT12TrackElement::SetBlockBrakeClosed(bool isClosed) +{ + if (isClosed) + { + flags |= TILE_ELEMENT_FLAG_BLOCK_BRAKE_CLOSED; + } + else + { + flags &= ~TILE_ELEMENT_FLAG_BLOCK_BRAKE_CLOSED; + } +} + +void RCT12TrackElement::SetHasGreenLight(uint8_t greenLight) +{ + if (trackType == TRACK_ELEM_END_STATION || trackType == TRACK_ELEM_BEGIN_STATION || trackType == TRACK_ELEM_MIDDLE_STATION) + { + sequence &= ~MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT; + if (greenLight) + { + sequence |= MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT; + } + } +} + +void RCT12TrackElement::SetHasChain(bool on) +{ + if (on) + { + type |= RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; + } + else + { + type &= ~RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; + } +} + +void RCT12TrackElement::SetSeatRotation(uint8_t newSeatRotation) +{ + colour &= 0x0F; + colour |= (newSeatRotation << 4); +} + +void RCT12TrackElement::SetMazeEntry(uint16_t newMazeEntry) +{ + mazeEntry = newMazeEntry; +} + +void RCT12TrackElement::SetPhotoTimeout(uint8_t value) +{ + if (GetTrackType() == TRACK_ELEM_ON_RIDE_PHOTO) + { + sequence &= RCT12_TRACK_ELEMENT_SEQUENCE_SEQUENCE_MASK; + sequence |= (value << 4); + } +} + +void RCT12SmallSceneryElement::SetEntryIndex(uint8_t newIndex) +{ + this->entryIndex = newIndex; +} + +void RCT12SmallSceneryElement::SetAge(uint8_t newAge) +{ + this->age = newAge; +} + +void RCT12SmallSceneryElement::SetPrimaryColour(colour_t colour) +{ + assert(colour <= 31); + colour_1 &= ~TILE_ELEMENT_COLOUR_MASK; + colour_1 |= colour; +} + +void RCT12SmallSceneryElement::SetSecondaryColour(colour_t colour) +{ + assert(colour <= 31); + colour_2 &= ~TILE_ELEMENT_COLOUR_MASK; + colour_2 |= colour; +} + +void RCT12SmallSceneryElement::SetSceneryQuadrant(uint8_t newQuadrant) +{ + type &= ~TILE_ELEMENT_QUADRANT_MASK; + type |= (newQuadrant << 6); +} + +void RCT12SmallSceneryElement::SetNeedsSupports() +{ + colour_1 |= MAP_ELEM_SMALL_SCENERY_COLOUR_FLAG_NEEDS_SUPPORTS; +} + +void RCT12WallElement::SetEntryIndex(uint8_t newIndex) +{ + entryIndex = newIndex; +} + +void RCT12WallElement::SetBannerIndex(BannerIndex newIndex) +{ + banner_index = newIndex; +} + +void RCT12WallElement::SetAcrossTrack(bool acrossTrack) +{ + animation &= ~WALL_ANIMATION_FLAG_ACROSS_TRACK; + if (acrossTrack) + animation |= WALL_ANIMATION_FLAG_ACROSS_TRACK; +} + +void RCT12WallElement::SetAnimationIsBackwards(bool isBackwards) +{ + animation &= ~WALL_ANIMATION_FLAG_DIRECTION_BACKWARD; + if (isBackwards) + animation |= WALL_ANIMATION_FLAG_DIRECTION_BACKWARD; +} +void RCT12WallElement::SetSlope(uint8_t newSlope) +{ + type &= ~TILE_ELEMENT_QUADRANT_MASK; + type |= (newSlope << 6); +} + +void RCT12WallElement::SetPrimaryColour(colour_t newColour) +{ + assert(newColour <= 31); + colour_1 &= ~TILE_ELEMENT_COLOUR_MASK; + colour_1 |= newColour; +} + +void RCT12WallElement::SetSecondaryColour(colour_t newColour) +{ + colour_1 &= TILE_ELEMENT_COLOUR_MASK; + colour_1 |= (newColour & 0x7) << 5; + flags &= ~0x60; + flags |= (newColour & 0x18) << 2; +} + +void RCT12WallElement::SetTertiaryColour(colour_t newColour) +{ + assert(newColour <= 31); + colour_3 &= ~TILE_ELEMENT_COLOUR_MASK; + colour_3 |= newColour; +} + +void RCT12WallElement::SetAnimationFrame(uint8_t frameNum) +{ + animation &= WALL_ANIMATION_FLAG_ALL_FLAGS; + animation |= (frameNum & 0xF) << 3; +} + +void RCT12EntranceElement::SetEntranceType(uint8_t newType) +{ + entranceType = newType; +} + +void RCT12EntranceElement::SetRideIndex(uint8_t newRideIndex) +{ + rideIndex = newRideIndex; +} + +void RCT12EntranceElement::SetPathType(uint8_t newPathType) +{ + pathType = newPathType; +} + +void RCT12EntranceElement::SetSequenceIndex(uint8_t newSequenceIndex) +{ + index &= ~0xF; + index |= (newSequenceIndex & 0xF); +} + +void RCT12EntranceElement::SetStationIndex(uint8_t stationIndex) +{ + index &= ~MAP_ELEM_TRACK_SEQUENCE_STATION_INDEX_MASK; + index |= (stationIndex << 4); +} + +void RCT12BannerElement::SetIndex(BannerIndex newIndex) +{ + index = newIndex; +} + +void RCT12BannerElement::SetPosition(uint8_t newPosition) +{ + position = newPosition; +} + +void RCT12BannerElement::SetAllowedEdges(uint8_t newEdges) +{ + flags &= ~0b00001111; + flags |= (newEdges & 0b00001111); +} + +bool RCT12ResearchItem::IsInventedEndMarker() const +{ + return rawValue == RCT12_RESEARCHED_ITEMS_SEPARATOR; +} + +bool RCT12ResearchItem::IsUninventedEndMarker() const +{ + return rawValue == RCT12_RESEARCHED_ITEMS_END; +} + +bool RCT12ResearchItem::IsRandomEndMarker() const +{ + return rawValue == RCT12_RESEARCHED_ITEMS_END_2; +} diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h index d8277f102f..25dbb0c462 100644 --- a/src/openrct2/rct12/RCT12.h +++ b/src/openrct2/rct12/RCT12.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,6 +14,9 @@ #include "../common.h" #include "../world/Location.hpp" +#include +#include + #define RCT12_MAX_RIDES_IN_PARK 255 #define RCT12_MAX_AWARDS 4 #define RCT12_MAX_NEWS_ITEMS 61 @@ -39,8 +42,89 @@ #define RCT12_PEEP_MAX_THOUGHTS 5 +#define RCT12_RIDE_ID_NULL 255 +#define RCT12_RIDE_MEASUREMENT_MAX_ITEMS 4800 + +constexpr uint16_t const RCT12_MAX_INVERSIONS = 31; +constexpr uint16_t const RCT12_MAX_GOLF_HOLES = 31; +constexpr uint16_t const RCT12_MAX_HELICES = 31; + +enum class RCT12TrackDesignVersion : uint8_t +{ + TD4, + TD4_AA, + TD6, + unknown +}; + +enum +{ + RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT = 1 << 7, +}; + +enum +{ + RCT12_TRACK_ELEMENT_SEQUENCE_STATION_INDEX_MASK = 0b01110000, + RCT12_TRACK_ELEMENT_SEQUENCE_SEQUENCE_MASK = 0b00001111, + RCT12_TRACK_ELEMENT_SEQUENCE_TAKING_PHOTO_MASK = 0b11110000, +}; + +enum +{ + // Not anything to do with colour but uses + // that field in the tile element + + // Used for multi-dimension coaster + RCT12_TRACK_ELEMENT_COLOUR_FLAG_INVERTED = (1 << 2), + + // Used for giga coaster + RCT12_TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT = (1 << 3), + + RCT12_TRACK_ELEMENT_DOOR_A_MASK = 0b00011100, + RCT12_TRACK_ELEMENT_DOOR_B_MASK = 0b11100000, +}; + +// Everything before this point has been researched +#define RCT12_RESEARCHED_ITEMS_SEPARATOR (-1) +// Everything before this point and after separator still requires research +#define RCT12_RESEARCHED_ITEMS_END (-2) +// Extra end of list entry. Leftover from RCT1. +#define RCT12_RESEARCHED_ITEMS_END_2 (-3) + #pragma pack(push, 1) +/* Maze Element entry size: 0x04 */ +struct rct_td46_maze_element +{ + union + { + uint32_t all; + struct + { + int8_t x; + int8_t y; + union + { + uint16_t maze_entry; + struct + { + uint8_t direction; + uint8_t type; + }; + }; + }; + }; +}; +assert_struct_size(rct_td46_maze_element, 0x04); + +/* Track Element entry size: 0x02 */ +struct rct_td46_track_element +{ + uint8_t type; // 0x00 + uint8_t flags; // 0x01 +}; +assert_struct_size(rct_td46_track_element, 0x02); + struct rct12_award { uint16_t time; @@ -109,13 +193,14 @@ struct RCT12EightCarsCorruptElement15; struct RCT12TileElementBase { uint8_t type; // 0 - uint8_t flags; // 1 + uint8_t flags; // 1. Upper nibble: flags. Lower nibble: occupied quadrants (one bit per quadrant). uint8_t base_height; // 2 uint8_t clearance_height; // 3 uint8_t GetType() const; uint8_t GetDirection() const; bool IsLastForTile() const; bool IsGhost() const; + void SetDirection(uint8_t direction); }; /** * Map element structure @@ -162,6 +247,7 @@ struct RCT12TileElement : public RCT12TileElementBase return as(); } void ClearAs(uint8_t newType); + uint8_t GetBannerIndex(); }; assert_struct_size(RCT12TileElement, 8); struct RCT12SurfaceElement : RCT12TileElementBase @@ -180,6 +266,15 @@ public: uint32_t GetWaterHeight() const; uint8_t GetParkFences() const; bool HasTrackThatNeedsWater() const; + + void SetSlope(uint8_t newSlope); + void SetSurfaceStyle(uint32_t newStyle); + void SetEdgeStyle(uint32_t newStyle); + void SetGrassLength(uint8_t newLength); + void SetOwnership(uint8_t newOwnership); + void SetWaterHeight(uint32_t newWaterHeight); + void SetParkFences(uint8_t newParkFences); + void SetHasTrackThatNeedsWater(bool on); }; assert_struct_size(RCT12SurfaceElement, 8); struct RCT12PathElement : RCT12TileElementBase @@ -206,14 +301,26 @@ public: bool HasQueueBanner() const; uint8_t GetEdges() const; uint8_t GetCorners() const; - uint8_t GetEdgesAndCorners() const; - bool HasAddition() const; uint8_t GetAddition() const; - uint8_t GetAdditionEntryIndex() const; bool AdditionIsGhost() const; uint8_t GetAdditionStatus() const; uint8_t GetRCT1PathType() const; uint8_t GetRCT1SupportType() const; + + void SetPathEntryIndex(uint8_t newIndex); + void SetQueueBannerDirection(uint8_t direction); + void SetSloped(bool isSloped); + void SetSlopeDirection(uint8_t newSlope); + void SetRideIndex(uint8_t newRideIndex); + void SetStationIndex(uint8_t newStationIndex); + void SetWide(bool isWide); + void SetIsQueue(bool isQueue); + void SetHasQueueBanner(bool hasQueueBanner); + void SetEdges(uint8_t newEdges); + void SetCorners(uint8_t newCorners); + void SetAddition(uint8_t newAddition); + void SetAdditionIsGhost(bool isGhost); + void SetAdditionStatus(uint8_t newStatus); }; assert_struct_size(RCT12PathElement, 8); struct RCT12TrackElement : RCT12TileElementBase @@ -254,13 +361,26 @@ public: bool HasGreenLight() const; uint8_t GetSeatRotation() const; uint16_t GetMazeEntry() const; - bool IsTakingPhoto() const; uint8_t GetPhotoTimeout() const; - bool IsHighlighted() const; // Used in RCT1, will be reintroduced at some point. // (See https://github.com/OpenRCT2/OpenRCT2/issues/7059) uint8_t GetDoorAState() const; uint8_t GetDoorBState() const; + + void SetTrackType(uint8_t newEntryIndex); + void SetSequenceIndex(uint8_t newSequenceIndex); + void SetRideIndex(uint8_t newRideIndex); + void SetColourScheme(uint8_t newColourScheme); + void SetStationIndex(uint8_t newStationIndex); + void SetHasChain(bool on); + void SetHasCableLift(bool on); + void SetInverted(bool inverted); + void SetBlockBrakeClosed(bool isClosed); + void SetBrakeBoosterSpeed(uint8_t speed); + void SetHasGreenLight(uint8_t greenLight); + void SetSeatRotation(uint8_t newSeatRotation); + void SetMazeEntry(uint16_t newMazeEntry); + void SetPhotoTimeout(uint8_t newValue); }; assert_struct_size(RCT12TrackElement, 8); struct RCT12SmallSceneryElement : RCT12TileElementBase @@ -277,6 +397,13 @@ public: colour_t GetPrimaryColour() const; colour_t GetSecondaryColour() const; bool NeedsSupports() const; + + void SetEntryIndex(uint8_t newIndex); + void SetAge(uint8_t newAge); + void SetSceneryQuadrant(uint8_t newQuadrant); + void SetPrimaryColour(colour_t colour); + void SetSecondaryColour(colour_t colour); + void SetNeedsSupports(); }; assert_struct_size(RCT12SmallSceneryElement, 8); struct RCT12LargeSceneryElement : RCT12TileElementBase @@ -290,6 +417,12 @@ public: colour_t GetPrimaryColour() const; colour_t GetSecondaryColour() const; BannerIndex GetBannerIndex() const; + + void SetEntryIndex(uint32_t newIndex); + void SetSequenceIndex(uint16_t sequence); + void SetPrimaryColour(colour_t colour); + void SetSecondaryColour(colour_t colour); + void SetBannerIndex(BannerIndex newIndex); }; assert_struct_size(RCT12LargeSceneryElement, 8); struct RCT12WallElement : RCT12TileElementBase @@ -316,6 +449,16 @@ public: uint32_t GetRawRCT1WallTypeData() const; int32_t GetRCT1WallType(int32_t edge) const; colour_t GetRCT1WallColour() const; + + void SetEntryIndex(uint8_t newIndex); + void SetSlope(uint8_t newslope); + void SetPrimaryColour(colour_t newColour); + void SetSecondaryColour(colour_t newColour); + void SetTertiaryColour(colour_t newColour); + void SetAnimationFrame(uint8_t frameNum); + void SetBannerIndex(BannerIndex newIndex); + void SetAcrossTrack(bool acrossTrack); + void SetAnimationIsBackwards(bool isBackwards); }; assert_struct_size(RCT12WallElement, 8); struct RCT12EntranceElement : RCT12TileElementBase @@ -331,6 +474,12 @@ public: uint8_t GetStationIndex() const; uint8_t GetSequenceIndex() const; uint8_t GetPathType() const; + + void SetEntranceType(uint8_t newType); + void SetRideIndex(uint8_t newRideIndex); + void SetStationIndex(uint8_t stationIndex); + void SetSequenceIndex(uint8_t newSequenceIndex); + void SetPathType(uint8_t newPathType); }; assert_struct_size(RCT12EntranceElement, 8); struct RCT12BannerElement : RCT12TileElementBase @@ -347,6 +496,10 @@ public: BannerIndex GetIndex() const; uint8_t GetPosition() const; uint8_t GetAllowedEdges() const; + + void SetIndex(BannerIndex newIndex); + void SetPosition(uint8_t newPosition); + void SetAllowedEdges(uint8_t newEdges); }; assert_struct_size(RCT12BannerElement, 8); @@ -498,4 +651,74 @@ struct RCT12PeepThought }; assert_struct_size(RCT12PeepThought, 4); +struct RCT12RideMeasurement +{ + uint8_t ride_index; // 0x0000 + uint8_t flags; // 0x0001 + uint32_t last_use_tick; // 0x0002 + uint16_t num_items; // 0x0006 + uint16_t current_item; // 0x0008 + uint8_t vehicle_index; // 0x000A + uint8_t current_station; // 0x000B + int8_t vertical[RCT12_RIDE_MEASUREMENT_MAX_ITEMS]; // 0x000C + int8_t lateral[RCT12_RIDE_MEASUREMENT_MAX_ITEMS]; // 0x12CC + uint8_t velocity[RCT12_RIDE_MEASUREMENT_MAX_ITEMS]; // 0x258C + uint8_t altitude[RCT12_RIDE_MEASUREMENT_MAX_ITEMS]; // 0x384C +}; +assert_struct_size(RCT12RideMeasurement, 0x4B0C); + +struct RCT12Banner +{ + uint8_t type; + uint8_t flags; // 0x01 + rct_string_id string_idx; // 0x02 + union + { + uint8_t colour; // 0x04 + uint8_t ride_index; // 0x04 + }; + uint8_t text_colour; // 0x05 + uint8_t x; // 0x06 + uint8_t y; // 0x07 +}; +assert_struct_size(RCT12Banner, 8); + +struct RCT12MapAnimation +{ + uint8_t baseZ; + uint8_t type; + uint16_t x; + uint16_t y; +}; +assert_struct_size(RCT12MapAnimation, 6); + +struct RCT12ResearchItem +{ + // Bit 16 (0: scenery entry, 1: ride entry) + union + { + int32_t rawValue; + struct + { + uint8_t entryIndex; + uint8_t baseRideType; + uint8_t type; // 0: scenery entry, 1: ride entry + uint8_t flags; + }; + }; + uint8_t category; + + bool IsInventedEndMarker() const; + bool IsRandomEndMarker() const; + bool IsUninventedEndMarker() const; +}; +assert_struct_size(RCT12ResearchItem, 5); + #pragma pack(pop) + +bool is_user_string_id(rct_string_id stringId); + +namespace RCT12 +{ + std::string RemoveFormatCodes(const std::string_view& s); +} diff --git a/src/openrct2/rct12/SawyerChunk.cpp b/src/openrct2/rct12/SawyerChunk.cpp index 84db1bb0dc..f516f562f9 100644 --- a/src/openrct2/rct12/SawyerChunk.cpp +++ b/src/openrct2/rct12/SawyerChunk.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/rct12/SawyerChunk.h b/src/openrct2/rct12/SawyerChunk.h index 64c7073e51..d54fcc93cc 100644 --- a/src/openrct2/rct12/SawyerChunk.h +++ b/src/openrct2/rct12/SawyerChunk.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/rct12/SawyerChunkReader.cpp b/src/openrct2/rct12/SawyerChunkReader.cpp index 8ef527b042..5764a0f917 100644 --- a/src/openrct2/rct12/SawyerChunkReader.cpp +++ b/src/openrct2/rct12/SawyerChunkReader.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -68,6 +68,9 @@ std::shared_ptr SawyerChunkReader::ReadChunk() try { auto header = _stream->ReadValue(); + if (header.length >= MAX_UNCOMPRESSED_CHUNK_SIZE) + throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE); + switch (header.encoding) { case CHUNK_ENCODING_NONE: @@ -102,6 +105,43 @@ std::shared_ptr SawyerChunkReader::ReadChunk() } } +std::shared_ptr SawyerChunkReader::ReadChunkTrack() +{ + uint64_t originalPosition = _stream->GetPosition(); + try + { + // Remove 4 as we don't want to touch the checksum at the end of the file + int64_t compressedDataLength64 = _stream->GetLength() - _stream->GetPosition() - 4; + if (compressedDataLength64 < 0 || compressedDataLength64 > std::numeric_limits::max()) + { + throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK); + } + uint32_t compressedDataLength = compressedDataLength64; + auto compressedData = std::make_unique(compressedDataLength); + + if (_stream->TryRead(compressedData.get(), compressedDataLength) != compressedDataLength) + { + throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE); + } + + auto buffer = (uint8_t*)AllocateLargeTempBuffer(); + sawyercoding_chunk_header header{ CHUNK_ENCODING_RLE, compressedDataLength }; + size_t uncompressedLength = DecodeChunk(buffer, MAX_UNCOMPRESSED_CHUNK_SIZE, compressedData.get(), header); + if (uncompressedLength == 0) + { + throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK); + } + buffer = (uint8_t*)FinaliseLargeTempBuffer(buffer, uncompressedLength); + return std::make_shared(SAWYER_ENCODING::RLE, buffer, uncompressedLength); + } + catch (const std::exception&) + { + // Rewind stream back to original position + _stream->SetPosition(originalPosition); + throw; + } +} + void SawyerChunkReader::ReadChunk(void* dst, size_t length) { auto chunk = ReadChunk(); diff --git a/src/openrct2/rct12/SawyerChunkReader.h b/src/openrct2/rct12/SawyerChunkReader.h index a1407cbae1..2d68a1c501 100644 --- a/src/openrct2/rct12/SawyerChunkReader.h +++ b/src/openrct2/rct12/SawyerChunkReader.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -40,6 +40,11 @@ public: */ std::shared_ptr ReadChunk(); + /** + * As above but for chunks without a header + */ + std::shared_ptr ReadChunkTrack(); + /** * Reads the next chunk from the stream and copies it directly to the * destination buffer. If the chunk is larger than length, only length diff --git a/src/openrct2/rct12/SawyerChunkWriter.cpp b/src/openrct2/rct12/SawyerChunkWriter.cpp index 4a298b5bd5..306b774b3e 100644 --- a/src/openrct2/rct12/SawyerChunkWriter.cpp +++ b/src/openrct2/rct12/SawyerChunkWriter.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -36,3 +36,73 @@ void SawyerChunkWriter::WriteChunk(const void* src, size_t length, SAWYER_ENCODI _stream->Write(data.get(), dataLength); } + +/** + * Ensure dst_buffer is bigger than src_buffer then resize afterwards + * returns length of dst_buffer + */ +static size_t EncodeChunkRLE(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length) +{ + const uint8_t* src = src_buffer; + uint8_t* dst = dst_buffer; + const uint8_t* end_src = src + length; + uint8_t count = 0; + const uint8_t* src_norm_start = src; + + while (src < end_src - 1) + { + if ((count && *src == src[1]) || count > 125) + { + *dst++ = count - 1; + std::memcpy(dst, src_norm_start, count); + dst += count; + src_norm_start += count; + count = 0; + } + if (*src == src[1]) + { + for (; (count < 125) && ((src + count) < end_src); count++) + { + if (*src != src[count]) + break; + } + *dst++ = 257 - count; + *dst++ = *src; + src += count; + src_norm_start = src; + count = 0; + } + else + { + count++; + src++; + } + } + if (src == end_src - 1) + count++; + if (count) + { + *dst++ = count - 1; + std::memcpy(dst, src_norm_start, count); + dst += count; + } + return dst - dst_buffer; +} + +void SawyerChunkWriter::WriteChunkTrack(const void* src, size_t length) +{ + auto data = std::make_unique(MAX_COMPRESSED_CHUNK_SIZE); + size_t dataLength = EncodeChunkRLE((const uint8_t*)src, data.get(), length); + + uint32_t checksum = 0; + for (size_t i = 0; i < dataLength; i++) + { + uint8_t newByte = ((checksum & 0xFF) + data[i]) & 0xFF; + checksum = (checksum & 0xFFFFFF00) + newByte; + checksum = rol32(checksum, 3); + } + checksum -= 0x1D4C1; + + _stream->Write(data.get(), dataLength); + _stream->WriteValue(checksum); +} diff --git a/src/openrct2/rct12/SawyerChunkWriter.h b/src/openrct2/rct12/SawyerChunkWriter.h index 9fbfc156b9..4a49dd62f6 100644 --- a/src/openrct2/rct12/SawyerChunkWriter.h +++ b/src/openrct2/rct12/SawyerChunkWriter.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -40,6 +40,13 @@ public: */ void WriteChunk(const void* src, size_t length, SAWYER_ENCODING encoding); + /** + * Writes a track chunk to the stream containing the given buffer. + * @param src The source buffer. + * @param length The size of the source buffer. + */ + void WriteChunkTrack(const void* src, size_t length); + /** * Writes a chunk to the stream containing the given type. */ diff --git a/src/openrct2/rct12/SawyerEncoding.cpp b/src/openrct2/rct12/SawyerEncoding.cpp index 55758dc54a..4e7c4fe5e1 100644 --- a/src/openrct2/rct12/SawyerEncoding.cpp +++ b/src/openrct2/rct12/SawyerEncoding.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,6 +10,7 @@ #include "SawyerEncoding.h" #include "../core/IStream.hpp" +#include "RCT12.h" #include @@ -57,4 +58,48 @@ namespace SawyerEncoding return false; } } + + // Returns version number + RCT12TrackDesignVersion ValidateTrackChecksum(IStream* stream) + { + uint64_t initialPosition = stream->GetPosition(); + uint64_t dataSize = stream->GetLength() - initialPosition; + + if (dataSize < 4) + { + return RCT12TrackDesignVersion::unknown; + } + dataSize -= 4; + + try + { + auto data{ stream->ReadArray(dataSize) }; + uint32_t checksum = 0; + for (size_t i = 0; i < dataSize; i++, ++data) + { + uint8_t newByte = ((checksum & 0xFF) + *data) & 0xFF; + checksum = (checksum & 0xFFFFFF00) + newByte; + checksum = rol32(checksum, 3); + } + + uint32_t fileChecksum = stream->ReadValue(); + // Rewind back to original position + stream->SetPosition(initialPosition); + + if (checksum - 0x1D4C1 == fileChecksum) + return RCT12TrackDesignVersion::TD6; + else if (checksum - 0x1A67C == fileChecksum) + return RCT12TrackDesignVersion::TD4; + else if (checksum - 0x1A650 == fileChecksum) + return RCT12TrackDesignVersion::TD4; + else + return RCT12TrackDesignVersion::unknown; + } + catch (const std::exception&) + { + // Rewind back to original position + stream->SetPosition(initialPosition); + return RCT12TrackDesignVersion::unknown; + } + } } // namespace SawyerEncoding diff --git a/src/openrct2/rct12/SawyerEncoding.h b/src/openrct2/rct12/SawyerEncoding.h index 1a0ba30541..bf2265dc3f 100644 --- a/src/openrct2/rct12/SawyerEncoding.h +++ b/src/openrct2/rct12/SawyerEncoding.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,7 +13,10 @@ interface IStream; +enum class RCT12TrackDesignVersion : uint8_t; + namespace SawyerEncoding { bool ValidateChecksum(IStream* stream); -} + RCT12TrackDesignVersion ValidateTrackChecksum(IStream* stream); +} // namespace SawyerEncoding diff --git a/src/openrct2/rct2/RCT2.h b/src/openrct2/rct2/RCT2.h index f42fc51457..c26f52d091 100644 --- a/src/openrct2/rct2/RCT2.h +++ b/src/openrct2/rct2/RCT2.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,6 +11,7 @@ #define _RCT2_H_ #include "../common.h" +#include "../object/Object.h" #include "../rct12/RCT12.h" #include "../ride/RideRatings.h" #include "../ride/Vehicle.h" @@ -268,6 +269,104 @@ struct rct2_ride }; assert_struct_size(rct2_ride, 0x260); +/* Track Entrance entry size: 0x06 */ +struct rct_td6_entrance_element +{ + int8_t z; // 0x00 + uint8_t direction; // 0x01 + int16_t x; // 0x02 + int16_t y; // 0x04 +}; +assert_struct_size(rct_td6_entrance_element, 0x06); + +/* Track Scenery entry size: 0x16 */ +struct rct_td6_scenery_element +{ + rct_object_entry scenery_object; // 0x00 + int8_t x; // 0x10 + int8_t y; // 0x11 + int8_t z; // 0x12 + uint8_t flags; // 0x13 direction quadrant tertiary colour + uint8_t primary_colour; // 0x14 + uint8_t secondary_colour; // 0x15 +}; +assert_struct_size(rct_td6_scenery_element, 0x16); + +/** + * Track design structure. + * size: 0xA3 + */ +struct rct_track_td6 +{ + uint8_t type; // 0x00 + uint8_t vehicle_type; + union + { + // After loading the track this is converted to + // a cost but before its a flags register + money32 cost; // 0x02 + uint32_t flags; // 0x02 + }; + union + { + // After loading the track this is converted to + // a flags register + uint8_t ride_mode; // 0x06 + uint8_t track_flags; // 0x06 + }; + uint8_t version_and_colour_scheme; // 0x07 0b0000_VVCC + rct_vehicle_colour vehicle_colours[RCT2_MAX_CARS_PER_TRAIN]; // 0x08 + union + { + uint8_t pad_48; + uint8_t track_spine_colour_rct1; // 0x48 + }; + union + { + uint8_t entrance_style; // 0x49 + uint8_t track_rail_colour_rct1; // 0x49 + }; + union + { + uint8_t total_air_time; // 0x4A + uint8_t track_support_colour_rct1; // 0x4A + }; + uint8_t depart_flags; // 0x4B + uint8_t number_of_trains; // 0x4C + uint8_t number_of_cars_per_train; // 0x4D + uint8_t min_waiting_time; // 0x4E + uint8_t max_waiting_time; // 0x4F + uint8_t operation_setting; + int8_t max_speed; // 0x51 + int8_t average_speed; // 0x52 + uint16_t ride_length; // 0x53 + uint8_t max_positive_vertical_g; // 0x55 + int8_t max_negative_vertical_g; // 0x56 + uint8_t max_lateral_g; // 0x57 + union + { + uint8_t inversions; // 0x58 + uint8_t holes; // 0x58 + }; + uint8_t drops; // 0x59 + uint8_t highest_drop_height; // 0x5A + uint8_t excitement; // 0x5B + uint8_t intensity; // 0x5C + uint8_t nausea; // 0x5D + money16 upkeep_cost; // 0x5E + uint8_t track_spine_colour[RCT12_NUM_COLOUR_SCHEMES]; // 0x60 + uint8_t track_rail_colour[RCT12_NUM_COLOUR_SCHEMES]; // 0x64 + uint8_t track_support_colour[RCT12_NUM_COLOUR_SCHEMES]; // 0x68 + uint32_t flags2; // 0x6C + rct_object_entry vehicle_object; // 0x70 + uint8_t space_required_x; // 0x80 + uint8_t space_required_y; // 0x81 + uint8_t vehicle_additional_colour[RCT2_MAX_CARS_PER_TRAIN]; // 0x82 + uint8_t lift_hill_speed_num_circuits; // 0xA2 0bCCCL_LLLL + // 0xA3 (data starts here in file) +}; +assert_struct_size(rct_track_td6, 0xA3); + /** * scores.dat file header. * size: 0x10 @@ -310,7 +409,7 @@ struct RCT2SpriteVehicle : RCT12SpriteBase int32_t remaining_distance; // 0x24 int32_t velocity; // 0x28 int32_t acceleration; // 0x2C - ride_id_t ride; // 0x30 + uint8_t ride; // 0x30 uint8_t vehicle_type; // 0x31 rct_vehicle_colour colours; // 0x32 union @@ -593,6 +692,26 @@ public: }; assert_struct_size(RCT2Sprite, 0x100); +struct RCT2RideRatingCalculationData +{ + uint16_t proximity_x; + uint16_t proximity_y; + uint16_t proximity_z; + uint16_t proximity_start_x; + uint16_t proximity_start_y; + uint16_t proximity_start_z; + uint8_t current_ride; + uint8_t state; + uint8_t proximity_track_type; + uint8_t proximity_base_height; + uint16_t proximity_total; + uint16_t proximity_scores[26]; + uint16_t num_brakes; + uint16_t num_reversers; + uint16_t station_flags; +}; +assert_struct_size(RCT2RideRatingCalculationData, 76); + #pragma pack(pop) #endif diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index 21849810c3..35622d6c10 100644 --- a/src/openrct2/rct2/S6Exporter.cpp +++ b/src/openrct2/rct2/S6Exporter.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,6 +11,7 @@ #include "../Context.h" #include "../Game.h" +#include "../GameState.h" #include "../OpenRCT2.h" #include "../common.h" #include "../config/Config.h" @@ -193,15 +194,10 @@ void S6Exporter::Export() _s6.scenario_srand_0 = state.s0; _s6.scenario_srand_1 = state.s1; - std::memcpy(_s6.tile_elements, gTileElements, sizeof(_s6.tile_elements)); - - _s6.next_free_tile_element_pointer_index = gNextFreeTileElementPointerIndex; - + ExportTileElements(); ExportSprites(); + ExportParkName(); - _s6.park_name = gParkName; - // pad_013573D6 - _s6.park_name_args = gParkNameArgs; _s6.initial_cash = gInitialCash; _s6.current_loan = gBankLoan; _s6.park_flags = gParkFlags; @@ -304,7 +300,7 @@ void S6Exporter::Export() // _s6.game_version_number _s6.completed_company_value_record = gScenarioCompanyValueRecord; _s6.loan_hash = GetLoanHash(gInitialCash, gBankLoan, gMaxBankLoan); - _s6.ride_count = gRideCount; + _s6.ride_count = ride_get_count(); // pad_013587CA _s6.historical_profit = gHistoricalProfit; // pad_013587D4 @@ -316,7 +312,7 @@ void S6Exporter::Export() _s6.map_size_minus_2 = gMapSizeMinus2; _s6.map_size = gMapSize; _s6.map_max_xy = gMapSizeMaxXY; - _s6.same_price_throughout = gSamePriceThroughoutParkA; + _s6.same_price_throughout = gSamePriceThroughoutPark & 0xFFFFFFFF; _s6.suggested_max_guests = _suggestedGuestMaximum; _s6.park_rating_warning_days = gScenarioParkRatingWarningDays; _s6.last_entrance_style = gLastEntranceStyle; @@ -328,7 +324,7 @@ void S6Exporter::Export() String::Set(_s6.scenario_description, sizeof(_s6.scenario_description), gScenarioDetails.c_str()); _s6.current_interest_rate = gBankLoanInterestRate; // pad_0135934B - _s6.same_price_throughout_extended = gSamePriceThroughoutParkB; + _s6.same_price_throughout_extended = gSamePriceThroughoutPark >> 32; // Preserve compatibility with vanilla RCT2's save format. for (uint8_t i = 0; i < RCT12_MAX_PARK_ENTRANCES; i++) { @@ -344,8 +340,7 @@ void S6Exporter::Export() } safe_strcpy(_s6.scenario_filename, gScenarioFileName, sizeof(_s6.scenario_filename)); std::memcpy(_s6.saved_expansion_pack_names, gScenarioExpansionPacks, sizeof(_s6.saved_expansion_pack_names)); - std::memcpy(_s6.banners, gBanners, sizeof(_s6.banners)); - std::memcpy(_s6.custom_strings, gUserStrings, sizeof(_s6.custom_strings)); + ExportBanners(); _s6.game_ticks_1 = gCurrentTicks; this->ExportRides(); @@ -355,19 +350,18 @@ void S6Exporter::Export() _s6.saved_view_y = gSavedViewY; _s6.saved_view_zoom = gSavedViewZoom; _s6.saved_view_rotation = gSavedViewRotation; - std::memcpy(_s6.map_animations, gAnimatedObjects, sizeof(_s6.map_animations)); - _s6.num_map_animations = gNumMapAnimations; - // pad_0138B582 - _s6.ride_ratings_calc_data = gRideRatingsCalcData; - std::memcpy(_s6.ride_measurements, gRideMeasurements, sizeof(_s6.ride_measurements)); + ExportMapAnimations(); + + ExportRideRatingsCalcData(); + ExportRideMeasurements(); _s6.next_guest_index = gNextGuestNumber; _s6.grass_and_scenery_tilepos = gGrassSceneryTileLoopPosition; std::memcpy(_s6.patrol_areas, gStaffPatrolAreas, sizeof(_s6.patrol_areas)); std::memcpy(_s6.staff_modes, gStaffModes, sizeof(_s6.staff_modes)); // unk_13CA73E // pad_13CA73F - _s6.byte_13CA740 = gUnk13CA740; + // unk_13CA740 _s6.climate = gClimate; // pad_13CA741; // byte_13CA742 @@ -414,6 +408,8 @@ void S6Exporter::Export() scenario_fix_ghosts(&_s6); game_convert_strings_to_rct2(&_s6); + + ExportUserStrings(); } void S6Exporter::ExportPeepSpawns() @@ -444,11 +440,33 @@ uint32_t S6Exporter::GetLoanHash(money32 initialCash, money32 bankLoan, uint32_t return value; } +void S6Exporter::ExportParkName() +{ + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + auto stringId = AllocateUserString(park.Name); + if (stringId != opt::nullopt) + { + _s6.park_name = *stringId; + _s6.park_name_args = 0; + } + else + { + log_warning("Unable to allocate user string for park name during S6 export."); + _s6.park_name = STR_UNNAMED_PARK; + _s6.park_name_args = 0; + } +} + void S6Exporter::ExportRides() { + const Ride nullRide{}; for (int32_t index = 0; index < RCT12_MAX_RIDES_IN_PARK; index++) { - auto src = get_ride(index); + const auto* src = get_ride(index); + if (src == nullptr) + { + src = &nullRide; + } auto dst = &_s6.rides[index]; *dst = {}; if (src->type == RIDE_TYPE_NULL) @@ -480,8 +498,29 @@ void S6Exporter::ExportRide(rct2_ride* dst, const Ride* src) // pad_046; dst->status = src->status; - dst->name = src->name; - dst->name_arguments = src->name_arguments; + + bool useDefaultName = true; + if (!src->custom_name.empty()) + { + // Custom name, allocate user string for ride + auto stringId = AllocateUserString(src->custom_name); + if (stringId != opt::nullopt) + { + dst->name = *stringId; + dst->name_arguments = 0; + useDefaultName = false; + } + else + { + log_warning("Unable to allocate user string for ride #%d (%s).", (int)src->id, src->custom_name.c_str()); + } + } + if (useDefaultName) + { + // Default name with number + dst->name = RideNaming[src->type].name; + dst->name_arguments_number = src->default_name_number; + } dst->overall_view = src->overall_view; @@ -538,8 +577,6 @@ void S6Exporter::ExportRide(rct2_ride* dst, const Ride* src) dst->boat_hire_return_direction = src->boat_hire_return_direction; dst->boat_hire_return_position = src->boat_hire_return_position; - dst->measurement_index = src->measurement_index; - dst->special_track_elements = src->special_track_elements; // pad_0D6[2]; @@ -560,8 +597,11 @@ void S6Exporter::ExportRide(rct2_ride* dst, const Ride* src) dst->turn_count_default = src->turn_count_default; dst->turn_count_banked = src->turn_count_banked; dst->turn_count_sloped = src->turn_count_sloped; - // Includes holes and (for some strange reason?!) sheltered_eights - dst->inversions = src->inversions; + if (dst->type == RIDE_TYPE_MINI_GOLF) + dst->inversions = (uint8_t)std::min(src->holes, RCT12_MAX_GOLF_HOLES); + else + dst->inversions = (uint8_t)std::min(src->inversions, RCT12_MAX_INVERSIONS); + dst->inversions |= (src->sheltered_eighths << 5); dst->drops = src->drops; dst->start_drop_height = src->start_drop_height; dst->highest_drop_height = src->highest_drop_height; @@ -683,6 +723,84 @@ void S6Exporter::ExportRide(rct2_ride* dst, const Ride* src) // pad_208[0x58]; } +void S6Exporter::ExportRideRatingsCalcData() +{ + const auto& src = gRideRatingsCalcData; + auto& dst = _s6.ride_ratings_calc_data; + dst.proximity_x = src.proximity_x; + dst.proximity_y = src.proximity_y; + dst.proximity_z = src.proximity_z; + dst.proximity_start_x = src.proximity_start_x; + dst.proximity_start_y = src.proximity_start_y; + dst.proximity_start_z = src.proximity_start_z; + dst.current_ride = src.current_ride; + dst.state = src.state; + dst.proximity_track_type = src.proximity_track_type; + dst.proximity_base_height = src.proximity_base_height; + dst.proximity_total = src.proximity_total; + for (size_t i = 0; i < std::size(dst.proximity_scores); i++) + { + dst.proximity_scores[i] = src.proximity_scores[i]; + } + dst.num_brakes = src.num_brakes; + dst.num_reversers = src.num_reversers; + dst.station_flags = src.station_flags; +} + +void S6Exporter::ExportRideMeasurements() +{ + // Get all the ride measurements + std::vector ridesWithMeasurements; + for (ride_id_t i = 0; i < RCT12_MAX_RIDES_IN_PARK; i++) + { + auto ride = get_ride(i); + if (ride != nullptr && ride->measurement != nullptr) + { + ridesWithMeasurements.push_back(ride); + } + } + + // If there are more than S6 can hold, trim it by LRU + if (ridesWithMeasurements.size() > RCT12_RIDE_MEASUREMENT_MAX_ITEMS) + { + // Sort in order of last recently used + std::sort(ridesWithMeasurements.begin(), ridesWithMeasurements.end(), [](const Ride* a, const Ride* b) { + return a->measurement->last_use_tick > b->measurement->last_use_tick; + }); + ridesWithMeasurements.resize(RCT12_RIDE_MEASUREMENT_MAX_ITEMS); + } + + // Convert ride measurements to S6 format + uint8_t i{}; + for (auto src : ridesWithMeasurements) + { + auto& dst = _s6.ride_measurements[i]; + ExportRideMeasurement(_s6.ride_measurements[i], *src->measurement.get()); + + auto rideId = src->id; + dst.ride_index = rideId; + _s6.rides[rideId].measurement_index = i; + i++; + } +} + +void S6Exporter::ExportRideMeasurement(RCT12RideMeasurement& dst, const RideMeasurement& src) +{ + dst.flags = src.flags; + dst.last_use_tick = src.last_use_tick; + dst.num_items = src.num_items; + dst.current_item = src.current_item; + dst.vehicle_index = src.vehicle_index; + dst.current_station = src.current_station; + for (size_t i = 0; i < std::size(src.velocity); i++) + { + dst.velocity[i] = src.velocity[i]; + dst.altitude[i] = src.altitude[i]; + dst.vertical[i] = src.vertical[i]; + dst.lateral[i] = src.lateral[i]; + } +} + void S6Exporter::ExportResearchedRideTypes() { std::fill(std::begin(_s6.researched_ride_types), std::end(_s6.researched_ride_types), false); @@ -730,7 +848,18 @@ void S6Exporter::ExportResearchedSceneryItems() void S6Exporter::ExportResearchList() { - std::memcpy(_s6.research_items, gResearchItems, sizeof(_s6.research_items)); + size_t i = 0; + for (const auto& researchItem : gResearchItemsInvented) + { + _s6.research_items[i++] = RCT12ResearchItem{ researchItem.rawValue, researchItem.category }; + } + _s6.research_items[i++] = { RCT12_RESEARCHED_ITEMS_SEPARATOR, 0 }; + for (const auto& researchItem : gResearchItemsUninvented) + { + _s6.research_items[i++] = RCT12ResearchItem{ researchItem.rawValue, researchItem.category }; + } + _s6.research_items[i++] = { RCT12_RESEARCHED_ITEMS_END, 0 }; + _s6.research_items[i] = { RCT12_RESEARCHED_ITEMS_END_2, 0 }; } void S6Exporter::ExportMarketingCampaigns() @@ -739,7 +868,9 @@ void S6Exporter::ExportMarketingCampaigns() std::memset(_s6.campaign_ride_index, 0, sizeof(_s6.campaign_ride_index)); for (const auto& campaign : gMarketingCampaigns) { - _s6.campaign_weeks_left[campaign.Type] = campaign.WeeksLeft; + _s6.campaign_weeks_left[campaign.Type] = campaign.WeeksLeft | CAMPAIGN_ACTIVE_FLAG; + if ((campaign.Flags & MarketingCampaignFlags::FIRST_WEEK)) + _s6.campaign_weeks_left[campaign.Type] |= CAMPAIGN_FIRST_WEEK_FLAG; if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE) { _s6.campaign_ride_index[campaign.Type] = campaign.RideId; @@ -763,7 +894,7 @@ void S6Exporter::ExportSprites() ExportSprite(&_s6.sprites[i], get_sprite(i)); } - for (int32_t i = 0; i < NUM_SPRITE_LISTS; i++) + for (int32_t i = 0; i < SPRITE_LIST_COUNT; i++) { _s6.sprite_lists_head[i] = gSpriteListHead[i]; _s6.sprite_lists_count[i] = gSpriteListCount[i]; @@ -804,7 +935,7 @@ void S6Exporter::ExportSpriteCommonProperties(RCT12SpriteBase* dst, const rct_sp dst->next_in_quadrant = src->next_in_quadrant; dst->next = src->next; dst->previous = src->previous; - dst->linked_list_type_offset = src->linked_list_type_offset; + dst->linked_list_type_offset = src->linked_list_index * 2; dst->sprite_height_negative = src->sprite_height_negative; dst->sprite_index = src->sprite_index; dst->flags = src->flags; @@ -861,9 +992,9 @@ void S6Exporter::ExportSpriteVehicle(RCT2SpriteVehicle* dst, const rct_vehicle* dst->crash_x = src->crash_x; dst->sound2_flags = src->sound2_flags; dst->spin_sprite = src->spin_sprite; - dst->sound1_id = src->sound1_id; + dst->sound1_id = static_cast(src->sound1_id); dst->sound1_volume = src->sound1_volume; - dst->sound2_id = src->sound2_id; + dst->sound2_id = static_cast(src->sound2_id); dst->sound2_volume = src->sound2_volume; dst->sound_vector_factor = src->sound_vector_factor; dst->time_waiting = src->time_waiting; @@ -873,7 +1004,7 @@ void S6Exporter::ExportSpriteVehicle(RCT2SpriteVehicle* dst, const rct_vehicle* dst->animation_frame = src->animation_frame; dst->var_C8 = src->var_C8; dst->var_CA = src->var_CA; - dst->scream_sound_id = src->scream_sound_id; + dst->scream_sound_id = static_cast(src->scream_sound_id); dst->var_CD = src->var_CD; dst->var_CE = src->var_CE; dst->var_CF = src->var_CF; @@ -891,7 +1022,44 @@ void S6Exporter::ExportSpriteVehicle(RCT2SpriteVehicle* dst, const rct_vehicle* void S6Exporter::ExportSpritePeep(RCT2SpritePeep* dst, const Peep* src) { ExportSpriteCommonProperties(dst, (const rct_sprite_common*)src); - dst->name_string_idx = src->name_string_idx; + + auto generateName = true; + if (src->name != nullptr) + { + auto stringId = AllocateUserString(src->name); + if (stringId != opt::nullopt) + { + dst->name_string_idx = *stringId; + generateName = false; + } + else + { + log_warning( + "Unable to allocate user string for peep #%d (%s) during S6 export.", (int)src->sprite_index, src->name); + } + } + if (generateName) + { + if (src->type == PeepType::PEEP_TYPE_STAFF) + { + static constexpr const rct_string_id staffNames[] = { + STR_HANDYMAN_X, + STR_MECHANIC_X, + STR_SECURITY_GUARD_X, + STR_ENTERTAINER_X, + }; + dst->name_string_idx = staffNames[src->staff_type % sizeof(staffNames)]; + } + else if (gParkFlags & PARK_FLAGS_SHOW_REAL_GUEST_NAMES) + { + dst->name_string_idx = get_real_name_string_id_from_id(src->id); + } + else + { + dst->name_string_idx = STR_GUEST_X; + } + } + dst->next_x = src->next_x; dst->next_y = src->next_y; dst->next_z = src->next_z; @@ -1009,16 +1177,16 @@ void S6Exporter::ExportSpriteMisc(RCT12SpriteBase* cdst, const rct_sprite_common { case SPRITE_MISC_STEAM_PARTICLE: { - auto src = (const RCT12SpriteSteamParticle*)csrc; - auto dst = (rct_steam_particle*)cdst; + auto src = (const rct_steam_particle*)csrc; + auto dst = (RCT12SpriteSteamParticle*)cdst; dst->time_to_move = src->time_to_move; dst->frame = src->frame; break; } case SPRITE_MISC_MONEY_EFFECT: { - auto src = (const RCT12SpriteMoneyEffect*)csrc; - auto dst = (rct_money_effect*)cdst; + auto src = (const rct_money_effect*)csrc; + auto dst = (RCT12SpriteMoneyEffect*)cdst; dst->move_delay = src->move_delay; dst->num_movements = src->num_movements; dst->vertical = src->vertical; @@ -1029,8 +1197,8 @@ void S6Exporter::ExportSpriteMisc(RCT12SpriteBase* cdst, const rct_sprite_common } case SPRITE_MISC_CRASHED_VEHICLE_PARTICLE: { - auto src = (const RCT12SpriteCrashedVehicleParticle*)csrc; - auto dst = (rct_crashed_vehicle_particle*)cdst; + auto src = (const rct_crashed_vehicle_particle*)csrc; + auto dst = (RCT12SpriteCrashedVehicleParticle*)cdst; dst->frame = src->frame; dst->time_to_live = src->time_to_live; dst->frame = src->frame; @@ -1049,22 +1217,23 @@ void S6Exporter::ExportSpriteMisc(RCT12SpriteBase* cdst, const rct_sprite_common case SPRITE_MISC_EXPLOSION_FLARE: case SPRITE_MISC_CRASH_SPLASH: { - auto src = (const rct_sprite_generic*)csrc; - auto dst = (RCT12SpriteParticle*)cdst; + auto src = (const RCT12SpriteParticle*)csrc; + auto dst = (rct_sprite_generic*)cdst; dst->frame = src->frame; break; } case SPRITE_MISC_JUMPING_FOUNTAIN_WATER: case SPRITE_MISC_JUMPING_FOUNTAIN_SNOW: { - auto src = (const rct_jumping_fountain*)csrc; - auto dst = (RCT12SpriteJumpingFountain*)cdst; - dst->num_ticks_alive = src->num_ticks_alive; + auto* src = (const JumpingFountain*)csrc; + auto* dst = (RCT12SpriteJumpingFountain*)cdst; + dst->num_ticks_alive = src->NumTicksAlive; dst->frame = src->frame; - dst->fountain_flags = src->fountain_flags; - dst->target_x = src->target_x; - dst->target_y = src->target_y; - dst->iteration = src->iteration; + dst->fountain_flags = src->FountainFlags; + dst->target_x = src->TargetX; + dst->target_y = src->TargetY; + dst->target_y = src->TargetY; + dst->iteration = src->Iteration; break; } case SPRITE_MISC_BALLOON: @@ -1099,6 +1268,304 @@ void S6Exporter::ExportSpriteLitter(RCT12SpriteLitter* dst, const rct_litter* sr dst->creationTick = src->creationTick; } +void S6Exporter::ExportBanners() +{ + for (BannerIndex i = 0; i < RCT2_MAX_BANNERS_IN_PARK; i++) + { + auto src = GetBanner(i); + auto dst = &_s6.banners[i]; + ExportBanner(*dst, *src); + } +} + +void S6Exporter::ExportBanner(RCT12Banner& dst, const Banner& src) +{ + dst = {}; + dst.type = src.type; + + if (!src.IsNull()) + { + dst.flags = src.flags; + + dst.string_idx = STR_DEFAULT_SIGN; + + auto bannerText = src.text; + if (!(src.flags & BANNER_FLAG_IS_WALL) && !(src.flags & BANNER_FLAG_IS_LARGE_SCENERY)) + { + char codeBuffer[32]{}; + utf8_write_codepoint(codeBuffer, FORMAT_COLOUR_CODE_START + src.text_colour); + bannerText = codeBuffer + bannerText; + } + + auto stringId = AllocateUserString(bannerText); + if (stringId != opt::nullopt) + { + dst.string_idx = *stringId; + } + + if (src.flags & BANNER_FLAG_LINKED_TO_RIDE) + { + dst.ride_index = src.ride_index; + } + else + { + dst.colour = src.colour; + } + dst.text_colour = src.text_colour; + dst.x = src.position.x; + dst.y = src.position.y; + } +} + +void S6Exporter::ExportMapAnimations() +{ + const auto& mapAnimations = GetMapAnimations(); + auto numAnimations = std::min(mapAnimations.size(), std::size(_s6.map_animations)); + _s6.num_map_animations = (uint16_t)numAnimations; + for (size_t i = 0; i < numAnimations; i++) + { + const auto& src = mapAnimations[i]; + auto& dst = _s6.map_animations[i]; + + dst.type = src.type; + dst.x = src.location.x; + dst.y = src.location.y; + dst.baseZ = src.location.z; + } +} + +void S6Exporter::ExportTileElements() +{ + for (uint32_t index = 0; index < RCT2_MAX_TILE_ELEMENTS; index++) + { + auto src = &gTileElements[index]; + auto dst = &_s6.tile_elements[index]; + if (src->base_height == 0xFF) + { + std::memcpy(dst, src, sizeof(*dst)); + } + else + { + auto tileElementType = (RCT12TileElementType)src->GetType(); + if (tileElementType == RCT12TileElementType::Corrupt || tileElementType == RCT12TileElementType::EightCarsCorrupt14 + || tileElementType == RCT12TileElementType::EightCarsCorrupt15) + std::memcpy(dst, src, sizeof(*dst)); + else + ExportTileElement(dst, src); + } + } + _s6.next_free_tile_element_pointer_index = gNextFreeTileElementPointerIndex; +} + +void S6Exporter::ExportTileElement(RCT12TileElement* dst, TileElement* src) +{ + // Todo: allow for changing defition of OpenRCT2 tile element types - replace with a map + uint8_t tileElementType = src->GetType(); + dst->ClearAs(tileElementType); + dst->SetDirection(src->GetDirection()); + dst->flags = src->flags; + dst->base_height = src->base_height; + dst->clearance_height = src->clearance_height; + + switch (tileElementType) + { + case TILE_ELEMENT_TYPE_SURFACE: + { + auto dst2 = dst->AsSurface(); + auto src2 = src->AsSurface(); + + dst2->SetSlope(src2->GetSlope()); + dst2->SetSurfaceStyle(src2->GetSurfaceStyle()); + dst2->SetEdgeStyle(src2->GetEdgeStyle()); + dst2->SetGrassLength(src2->GetGrassLength()); + dst2->SetOwnership(src2->GetOwnership()); + dst2->SetParkFences(src2->GetParkFences()); + dst2->SetWaterHeight(src2->GetWaterHeight()); + dst2->SetHasTrackThatNeedsWater(src2->HasTrackThatNeedsWater()); + + break; + } + case TILE_ELEMENT_TYPE_PATH: + { + auto dst2 = dst->AsPath(); + auto src2 = src->AsPath(); + + dst2->SetPathEntryIndex(src2->GetPathEntryIndex()); + dst2->SetQueueBannerDirection(src2->GetQueueBannerDirection()); + dst2->SetSloped(src2->IsSloped()); + dst2->SetSlopeDirection(src2->GetSlopeDirection()); + dst2->SetRideIndex(src2->GetRideIndex()); + dst2->SetStationIndex(src2->GetStationIndex()); + dst2->SetWide(src2->IsWide()); + dst2->SetIsQueue(src2->IsQueue()); + dst2->SetHasQueueBanner(src2->HasQueueBanner()); + dst2->SetEdges(src2->GetEdges()); + dst2->SetCorners(src2->GetCorners()); + dst2->SetAddition(src2->GetAddition()); + dst2->SetAdditionIsGhost(src2->AdditionIsGhost()); + dst2->SetAdditionStatus(src2->GetAdditionStatus()); + + break; + } + case TILE_ELEMENT_TYPE_TRACK: + { + auto dst2 = dst->AsTrack(); + auto src2 = src->AsTrack(); + + dst2->SetTrackType(src2->GetTrackType()); + dst2->SetSequenceIndex(src2->GetSequenceIndex()); + dst2->SetRideIndex(src2->GetRideIndex()); + dst2->SetColourScheme(src2->GetColourScheme()); + dst2->SetStationIndex(src2->GetStationIndex()); + dst2->SetHasGreenLight(src2->HasGreenLight()); + dst2->SetHasChain(src2->HasChain()); + dst2->SetHasCableLift(src2->HasCableLift()); + dst2->SetInverted(src2->IsInverted()); + dst2->SetBrakeBoosterSpeed(src2->GetBrakeBoosterSpeed()); + dst2->SetPhotoTimeout(src2->GetPhotoTimeout()); + + auto ride = get_ride(dst2->GetRideIndex()); + if (ride) + { + if (ride->type == RIDE_TYPE_MULTI_DIMENSION_ROLLER_COASTER) + { + dst2->SetSeatRotation(src2->GetSeatRotation()); + } + else if (ride->type == RIDE_TYPE_MAZE) + { + dst2->SetMazeEntry(src2->GetMazeEntry()); + } + } + // Skipping IsHighlighted() + + break; + } + case TILE_ELEMENT_TYPE_SMALL_SCENERY: + { + auto dst2 = dst->AsSmallScenery(); + auto src2 = src->AsSmallScenery(); + + dst2->SetEntryIndex(src2->GetEntryIndex()); + dst2->SetAge(src2->GetAge()); + dst2->SetSceneryQuadrant(src2->GetSceneryQuadrant()); + dst2->SetPrimaryColour(src2->GetPrimaryColour()); + dst2->SetSecondaryColour(src2->GetSecondaryColour()); + if (src2->NeedsSupports()) + dst2->SetNeedsSupports(); + + break; + } + case TILE_ELEMENT_TYPE_ENTRANCE: + { + auto dst2 = dst->AsEntrance(); + auto src2 = src->AsEntrance(); + + dst2->SetEntranceType(src2->GetEntranceType()); + dst2->SetRideIndex(src2->GetRideIndex()); + dst2->SetStationIndex(src2->GetStationIndex()); + dst2->SetSequenceIndex(src2->GetSequenceIndex()); + dst2->SetPathType(src2->GetPathType()); + + break; + } + case TILE_ELEMENT_TYPE_WALL: + { + auto dst2 = dst->AsWall(); + auto src2 = src->AsWall(); + + dst2->SetEntryIndex(src2->GetEntryIndex()); + dst2->SetSlope(src2->GetSlope()); + dst2->SetPrimaryColour(src2->GetPrimaryColour()); + dst2->SetSecondaryColour(src2->GetSecondaryColour()); + dst2->SetTertiaryColour(src2->GetTertiaryColour()); + dst2->SetAnimationFrame(src2->GetAnimationFrame()); + dst2->SetBannerIndex(src2->GetBannerIndex()); + dst2->SetAcrossTrack(src2->IsAcrossTrack()); + dst2->SetAnimationIsBackwards(src2->AnimationIsBackwards()); + break; + } + case TILE_ELEMENT_TYPE_LARGE_SCENERY: + { + auto dst2 = dst->AsLargeScenery(); + auto src2 = src->AsLargeScenery(); + + dst2->SetEntryIndex(src2->GetEntryIndex()); + dst2->SetSequenceIndex(src2->GetSequenceIndex()); + dst2->SetPrimaryColour(src2->GetPrimaryColour()); + dst2->SetSecondaryColour(src2->GetSecondaryColour()); + dst2->SetBannerIndex(src2->GetBannerIndex()); + break; + } + case TILE_ELEMENT_TYPE_BANNER: + { + auto dst2 = dst->AsBanner(); + auto src2 = src->AsBanner(); + + dst2->SetIndex(src2->GetIndex()); + dst2->SetPosition(src2->GetPosition()); + dst2->SetAllowedEdges(src2->GetAllowedEdges()); + break; + } + default: + assert(false); + } +} + +opt::optional S6Exporter::AllocateUserString(const std::string_view& value) +{ + auto nextId = _userStrings.size(); + if (nextId < RCT12_MAX_USER_STRINGS) + { + _userStrings.emplace_back(value); + return (uint16_t)(USER_STRING_START + nextId); + } + return {}; +} + +static std::string GetTruncatedRCT2String(const std::string_view& src) +{ + auto rct2encoded = utf8_to_rct2(src); + if (rct2encoded.size() > RCT12_USER_STRING_MAX_LENGTH - 1) + { + log_warning( + "The user string '%s' is too long for the S6 file format and has therefore been truncated.", + std::string(src).c_str()); + + rct2encoded.resize(RCT12_USER_STRING_MAX_LENGTH - 1); + for (size_t i = 0; i < rct2encoded.size(); i++) + { + if (rct2encoded[i] == (char)(uint8_t)0xFF) + { + if (i > RCT12_USER_STRING_MAX_LENGTH - 4) + { + // This codepoint was truncated, remove codepoint altogether + rct2encoded.resize(i); + break; + } + else + { + // Skip the next two bytes which represent the unicode character + i += 2; + } + } + } + } + return rct2encoded; +} + +void S6Exporter::ExportUserStrings() +{ + auto numUserStrings = std::min(_userStrings.size(), RCT12_MAX_USER_STRINGS); + for (size_t i = 0; i < numUserStrings; i++) + { + auto dst = _s6.custom_strings[i]; + const auto& src = _userStrings[i]; + auto encodedSrc = GetTruncatedRCT2String(src); + auto stringLen = std::min(encodedSrc.size(), RCT12_USER_STRING_MAX_LENGTH - 1); + std::memcpy(dst, encodedSrc.data(), stringLen); + } +} + enum : uint32_t { S6_SAVE_FLAG_EXPORT = 1 << 0, @@ -1115,11 +1582,11 @@ int32_t scenario_save(const utf8* path, int32_t flags) { if (flags & S6_SAVE_FLAG_SCENARIO) { - log_verbose("saving scenario"); + log_verbose("scenario_save(%s, SCENARIO)", path); } else { - log_verbose("saving game"); + log_verbose("scenario_save(%s, SAVED GAME)", path); } if (!(flags & S6_SAVE_FLAG_AUTOMATIC)) @@ -1151,8 +1618,9 @@ int32_t scenario_save(const utf8* path, int32_t flags) } result = true; } - catch (const std::exception&) + catch (const std::exception& e) { + log_error("Unable to save park: '%s'", e.what()); } delete s6exporter; diff --git a/src/openrct2/rct2/S6Exporter.h b/src/openrct2/rct2/S6Exporter.h index 5c167a2b3a..d87e56f204 100644 --- a/src/openrct2/rct2/S6Exporter.h +++ b/src/openrct2/rct2/S6Exporter.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -10,9 +10,12 @@ #pragma once #include "../common.h" +#include "../core/Optional.hpp" #include "../object/ObjectList.h" #include "../scenario/Scenario.h" +#include +#include #include interface IStream; @@ -36,6 +39,7 @@ public: void SaveScenario(const utf8* path); void SaveScenario(IStream* stream); void Export(); + void ExportParkName(); void ExportRides(); void ExportRide(rct2_ride* dst, const Ride* src); void ExportSprites(); @@ -48,6 +52,7 @@ public: private: rct_s6_data _s6{}; + std::vector _userStrings; void Save(IStream* stream, bool isScenario); static uint32_t GetLoanHash(money32 initialCash, money32 bankLoan, uint32_t maxBankLoan); @@ -57,4 +62,16 @@ private: void ExportResearchList(); void ExportMarketingCampaigns(); void ExportPeepSpawns(); + void ExportRideRatingsCalcData(); + void ExportRideMeasurements(); + void ExportRideMeasurement(RCT12RideMeasurement& dst, const RideMeasurement& src); + void ExportBanners(); + void ExportBanner(RCT12Banner& dst, const Banner& src); + void ExportMapAnimations(); + + void ExportTileElements(); + void ExportTileElement(RCT12TileElement* dst, TileElement* src); + + opt::optional AllocateUserString(const std::string_view& value); + void ExportUserStrings(); }; diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 1cd13ac8f0..d5ab46e17e 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -8,6 +8,7 @@ *****************************************************************************/ #include "../Context.h" +#include "../Diagnostic.h" #include "../Game.h" #include "../GameState.h" #include "../OpenRCT2.h" @@ -38,6 +39,7 @@ #include "../ride/RideRatings.h" #include "../ride/ShopItem.h" #include "../ride/Station.h" +#include "../ride/Track.h" #include "../scenario/Scenario.h" #include "../scenario/ScenarioRepository.h" #include "../util/SawyerCoding.h" @@ -46,6 +48,7 @@ #include "../world/Entrance.h" #include "../world/MapAnimation.h" #include "../world/Park.h" +#include "../world/Scenery.h" #include "../world/Sprite.h" #include "../world/Surface.h" @@ -114,7 +117,7 @@ public: auto chunkReader = SawyerChunkReader(stream); chunkReader.ReadChunk(&_s6.header, sizeof(_s6.header)); - log_verbose("saved game classic_flag = 0x%02x\n", _s6.header.classic_flag); + log_verbose("saved game classic_flag = 0x%02x", _s6.header.classic_flag); if (isScenario) { if (_s6.header.type != S6_TYPE_SCENARIO) @@ -214,9 +217,6 @@ public: ImportTileElements(); ImportSprites(); - gParkName = _s6.park_name; - // pad_013573D6 - gParkNameArgs = _s6.park_name_args; gInitialCash = _s6.initial_cash; gBankLoan = _s6.current_loan; gParkFlags = _s6.park_flags; @@ -322,7 +322,6 @@ public: _gameVersion = _s6.game_version_number; gScenarioCompanyValueRecord = _s6.completed_company_value_record; // _s6.loan_hash; - gRideCount = _s6.ride_count; // pad_013587CA gHistoricalProfit = _s6.historical_profit; // pad_013587D4 @@ -334,7 +333,8 @@ public: gMapSizeMinus2 = _s6.map_size_minus_2; gMapSize = _s6.map_size; gMapSizeMaxXY = _s6.map_max_xy; - gSamePriceThroughoutParkA = _s6.same_price_throughout; + gSamePriceThroughoutPark = _s6.same_price_throughout + | (static_cast(_s6.same_price_throughout_extended) << 32); _suggestedGuestMaximum = _s6.suggested_max_guests; gScenarioParkRatingWarningDays = _s6.park_rating_warning_days; gLastEntranceStyle = _s6.last_entrance_style; @@ -346,7 +346,6 @@ public: gScenarioDetails = std::string_view(_s6.scenario_description, sizeof(_s6.scenario_description)); gBankLoanInterestRate = _s6.current_interest_rate; // pad_0135934B - gSamePriceThroughoutParkB = _s6.same_price_throughout_extended; // Preserve compatibility with vanilla RCT2's save format. gParkEntrances.clear(); for (uint8_t i = 0; i < RCT12_MAX_PARK_ENTRANCES; i++) @@ -372,10 +371,6 @@ public: String::Set(gScenarioFileName, sizeof(gScenarioFileName), _s6.scenario_filename); } std::memcpy(gScenarioExpansionPacks, _s6.saved_expansion_pack_names, sizeof(_s6.saved_expansion_pack_names)); - std::memcpy(gBanners, _s6.banners, sizeof(_s6.banners)); - // Clear all of the strings, since we will probably have a higher limit on user strings in the future than RCT2. - user_string_clear_all(); - std::memcpy(gUserStrings, _s6.custom_strings, sizeof(_s6.custom_strings)); gCurrentTicks = _s6.game_ticks_1; gCurrentRealTimeTicks = 0; @@ -387,22 +382,15 @@ public: gSavedViewZoom = _s6.saved_view_zoom; gSavedViewRotation = _s6.saved_view_rotation; - for (size_t i = 0; i < RCT2_MAX_ANIMATED_OBJECTS; i++) - { - gAnimatedObjects[i] = _s6.map_animations[i]; - } - gNumMapAnimations = _s6.num_map_animations; - // pad_0138B582 - - gRideRatingsCalcData = _s6.ride_ratings_calc_data; - std::memcpy(gRideMeasurements, _s6.ride_measurements, sizeof(_s6.ride_measurements)); + ImportRideRatingsCalcData(); + ImportRideMeasurements(); gNextGuestNumber = _s6.next_guest_index; gGrassSceneryTileLoopPosition = _s6.grass_and_scenery_tilepos; std::memcpy(gStaffPatrolAreas, _s6.patrol_areas, sizeof(_s6.patrol_areas)); std::memcpy(gStaffModes, _s6.staff_modes, sizeof(_s6.staff_modes)); // unk_13CA73E // pad_13CA73F - gUnk13CA740 = _s6.byte_13CA740; + // unk_13CA740 gClimate = _s6.climate; // pad_13CA741; // byte_13CA742 @@ -458,6 +446,9 @@ public: map_count_remaining_land_rights(); determine_ride_entrance_and_exit_locations(); + auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark(); + park.Name = GetUserString(_s6.park_name); + // We try to fix the cycles on import, hence the 'true' parameter check_for_sprite_list_cycles(true); check_for_spatial_index_cycles(true); @@ -491,7 +482,7 @@ public: auto src = &_s6.rides[index]; if (src->type != RIDE_TYPE_NULL) { - auto dst = get_ride(index); + auto dst = GetOrAllocateRide(index); ImportRide(dst, src, index); } } @@ -515,8 +506,16 @@ public: // pad_046; dst->status = src->status; - dst->name = src->name; - dst->name_arguments = src->name_arguments; + + dst->default_name_number = src->name_arguments_number; + if (is_user_string_id(src->name)) + { + dst->custom_name = GetUserString(src->name); + } + else + { + dst->default_name_number = src->name_arguments_number; + } dst->overall_view = src->overall_view; @@ -537,7 +536,7 @@ public: if (src->exits[i].xy == RCT_XY8_UNDEFINED) ride_clear_exit_location(dst, i); else - ride_set_exit_location(dst, i, { src->entrances[i].x, src->entrances[i].y, src->station_heights[i], 0 }); + ride_set_exit_location(dst, i, { src->exits[i].x, src->exits[i].y, src->station_heights[i], 0 }); dst->stations[i].LastPeepInQueue = src->last_peep_in_queue[i]; @@ -585,8 +584,6 @@ public: dst->boat_hire_return_direction = src->boat_hire_return_direction; dst->boat_hire_return_position = src->boat_hire_return_position; - dst->measurement_index = src->measurement_index; - dst->special_track_elements = src->special_track_elements; // pad_0D6[2]; @@ -607,8 +604,11 @@ public: dst->turn_count_default = src->turn_count_default; dst->turn_count_banked = src->turn_count_banked; dst->turn_count_sloped = src->turn_count_sloped; - // Includes holes and (for some strange reason?!) sheltered_eights - dst->inversions = src->inversions; + if (dst->type == RIDE_TYPE_MINI_GOLF) + dst->holes = src->inversions & 0x1F; + else + dst->inversions = src->inversions & 0x1F; + dst->sheltered_eighths = src->inversions >> 5; dst->drops = src->drops; dst->start_drop_height = src->start_drop_height; dst->highest_drop_height = src->highest_drop_height; @@ -706,6 +706,20 @@ public: dst->track_colour[i].additional = src->track_colour_additional[i]; dst->track_colour[i].supports = src->track_colour_supports[i]; } + // This stall was not colourable in RCT2. + if (dst->type == RIDE_TYPE_FOOD_STALL) + { + auto entry = object_entry_get_entry(OBJECT_TYPE_RIDE, dst->subtype); + if (entry != nullptr) + { + char name[DAT_NAME_LENGTH + 1]; + object_entry_get_name_fixed(name, sizeof(name), entry); + if (strncmp(name, "ICECR1 ", DAT_NAME_LENGTH) == 0) + { + dst->track_colour[0].main = COLOUR_LIGHT_BLUE; + } + } + } dst->music = src->music; dst->entrance_style = src->entrance_style; @@ -732,6 +746,64 @@ public: // pad_208[0x58]; } + void ImportRideRatingsCalcData() + { + const auto& src = _s6.ride_ratings_calc_data; + auto& dst = gRideRatingsCalcData; + dst = {}; + dst.proximity_x = src.proximity_x; + dst.proximity_y = src.proximity_y; + dst.proximity_z = src.proximity_z; + dst.proximity_start_x = src.proximity_start_x; + dst.proximity_start_y = src.proximity_start_y; + dst.proximity_start_z = src.proximity_start_z; + dst.current_ride = src.current_ride; + dst.state = src.state; + dst.proximity_track_type = src.proximity_track_type; + dst.proximity_base_height = src.proximity_base_height; + dst.proximity_total = src.proximity_total; + for (size_t i = 0; i < std::size(src.proximity_scores); i++) + { + dst.proximity_scores[i] = src.proximity_scores[i]; + } + dst.num_brakes = src.num_brakes; + dst.num_reversers = src.num_reversers; + dst.station_flags = src.station_flags; + } + + void ImportRideMeasurements() + { + for (const auto& src : _s6.ride_measurements) + { + if (src.ride_index != RCT12_RIDE_ID_NULL) + { + auto ride = get_ride(src.ride_index); + if (ride != nullptr) + { + ride->measurement = std::make_unique(); + ImportRideMeasurement(*ride->measurement, src); + } + } + } + } + + void ImportRideMeasurement(RideMeasurement& dst, const RCT12RideMeasurement& src) + { + dst.flags = src.flags; + dst.last_use_tick = src.last_use_tick; + dst.num_items = src.num_items; + dst.current_item = src.current_item; + dst.vehicle_index = src.vehicle_index; + dst.current_station = src.current_station; + for (size_t i = 0; i < std::size(src.velocity); i++) + { + dst.velocity[i] = src.velocity[i]; + dst.altitude[i] = src.altitude[i]; + dst.vertical[i] = src.vertical[i]; + dst.lateral[i] = src.lateral[i]; + } + } + void ImportResearchedRideTypes() { set_every_ride_type_not_invented(); @@ -779,7 +851,50 @@ public: void ImportResearchList() { - std::memcpy(gResearchItems, _s6.research_items, sizeof(_s6.research_items)); + bool invented = true; + for (size_t i = 0; i < sizeof(_s6.research_items); i++) + { + if (_s6.research_items[i].IsInventedEndMarker()) + { + invented = false; + continue; + } + else if (_s6.research_items[i].IsUninventedEndMarker() || _s6.research_items[i].IsRandomEndMarker()) + { + break; + } + + RCT12ResearchItem* ri = &_s6.research_items[i]; + if (invented) + gResearchItemsInvented.push_back(ResearchItem{ ri->rawValue, ri->category }); + else + gResearchItemsUninvented.push_back(ResearchItem{ ri->rawValue, ri->category }); + } + } + + void ImportBanner(Banner* dst, const RCT12Banner* src) + { + *dst = {}; + dst->type = src->type; + dst->flags = src->flags; + + if (!(src->flags & BANNER_FLAG_LINKED_TO_RIDE) && is_user_string_id(src->string_idx)) + { + dst->text = GetUserString(src->string_idx); + } + + if (src->flags & BANNER_FLAG_LINKED_TO_RIDE) + { + dst->ride_index = src->ride_index; + } + else + { + dst->colour = src->colour; + } + + dst->text_colour = src->text_colour; + dst->position.x = src->x; + dst->position.y = src->y; } void Initialise() @@ -815,6 +930,12 @@ public: { _s6.peep_spawns[0].y = 1296; } + // #9926: Africa - Oasis has peeps spawning on the edge underground near the entrance + else if (String::Equals(_s6.scenario_filename, "Africa - Oasis.SC6")) + { + _s6.peep_spawns[0].y = 2128; + _s6.peep_spawns[0].z = 7; + } gPeepSpawns.clear(); for (size_t i = 0; i < RCT12_MAX_PEEP_SPAWNS; i++) @@ -930,16 +1051,32 @@ public: dst2->SetSequenceIndex(src2->GetSequenceIndex()); dst2->SetRideIndex(src2->GetRideIndex()); dst2->SetColourScheme(src2->GetColourScheme()); - dst2->SetStationIndex(src2->GetStationIndex()); dst2->SetHasChain(src2->HasChain()); dst2->SetHasCableLift(src2->HasCableLift()); dst2->SetInverted(src2->IsInverted()); - dst2->SetBrakeBoosterSpeed(src2->GetBrakeBoosterSpeed()); + dst2->SetStationIndex(src2->GetStationIndex()); dst2->SetHasGreenLight(src2->HasGreenLight()); - dst2->SetSeatRotation(src2->GetSeatRotation()); - dst2->SetMazeEntry(src2->GetMazeEntry()); - dst2->SetPhotoTimeout(src2->GetPhotoTimeout()); + + auto trackType = dst2->GetTrackType(); + if (track_element_has_speed_setting(trackType)) + { + dst2->SetBrakeBoosterSpeed(src2->GetBrakeBoosterSpeed()); + } + else if (trackType == TRACK_ELEM_ON_RIDE_PHOTO) + { + dst2->SetPhotoTimeout(src2->GetPhotoTimeout()); + } + // Skipping IsHighlighted() + auto rideType = _s6.rides[src2->GetRideIndex()].type; + if (rideType == RIDE_TYPE_MULTI_DIMENSION_ROLLER_COASTER) + { + dst2->SetSeatRotation(src2->GetSeatRotation()); + } + else if (rideType == RIDE_TYPE_MAZE) + { + dst2->SetMazeEntry(src2->GetMazeEntry()); + } break; } @@ -986,6 +1123,22 @@ public: dst2->SetAcrossTrack(src2->IsAcrossTrack()); dst2->SetAnimationIsBackwards(src2->AnimationIsBackwards()); + // Import banner information + auto entry = dst2->GetEntry(); + if (entry != nullptr && entry->wall.scrolling_mode != SCROLLING_MODE_NONE) + { + auto bannerIndex = dst2->GetBannerIndex(); + if (bannerIndex < std::size(_s6.banners)) + { + auto srcBanner = &_s6.banners[bannerIndex]; + auto dstBanner = GetBanner(bannerIndex); + ImportBanner(dstBanner, srcBanner); + } + else + { + dst2->SetBannerIndex(BANNER_INDEX_NULL); + } + } break; } case TILE_ELEMENT_TYPE_LARGE_SCENERY: @@ -999,6 +1152,22 @@ public: dst2->SetSecondaryColour(src2->GetSecondaryColour()); dst2->SetBannerIndex(src2->GetBannerIndex()); + // Import banner information + auto entry = dst2->GetEntry(); + if (entry != nullptr && entry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE) + { + auto bannerIndex = dst2->GetBannerIndex(); + if (bannerIndex < std::size(_s6.banners)) + { + auto srcBanner = &_s6.banners[bannerIndex]; + auto dstBanner = GetBanner(bannerIndex); + ImportBanner(dstBanner, srcBanner); + } + else + { + dst2->SetBannerIndex(BANNER_INDEX_NULL); + } + } break; } case TILE_ELEMENT_TYPE_BANNER: @@ -1010,6 +1179,17 @@ public: dst2->SetPosition(src2->GetPosition()); dst2->SetAllowedEdges(src2->GetAllowedEdges()); + auto bannerIndex = src2->GetIndex(); + if (bannerIndex < std::size(_s6.banners)) + { + auto srcBanner = &_s6.banners[bannerIndex]; + auto dstBanner = GetBanner(bannerIndex); + ImportBanner(dstBanner, srcBanner); + } + else + { + dst2->SetIndex(BANNER_INDEX_NULL); + } break; } default: @@ -1025,7 +1205,11 @@ public: { MarketingCampaign campaign{}; campaign.Type = (uint8_t)i; - campaign.WeeksLeft = _s6.campaign_weeks_left[i] & ~CAMPAIGN_ACTIVE_FLAG; + campaign.WeeksLeft = _s6.campaign_weeks_left[i] & ~(CAMPAIGN_ACTIVE_FLAG | CAMPAIGN_FIRST_WEEK_FLAG); + if ((_s6.campaign_weeks_left[i] & CAMPAIGN_FIRST_WEEK_FLAG) != 0) + { + campaign.Flags |= MarketingCampaignFlags::FIRST_WEEK; + } if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE) { campaign.RideId = _s6.campaign_ride_index[i]; @@ -1048,13 +1232,13 @@ public: ImportSprite(dst, src); } - for (int32_t i = 0; i < NUM_SPRITE_LISTS; i++) + for (int32_t i = 0; i < SPRITE_LIST_COUNT; i++) { gSpriteListHead[i] = _s6.sprite_lists_head[i]; gSpriteListCount[i] = _s6.sprite_lists_count[i]; } // This list contains the number of free slots. Increase it according to our own sprite limit. - gSpriteListCount[SPRITE_LIST_NULL] += (MAX_SPRITES - RCT2_MAX_SPRITES); + gSpriteListCount[SPRITE_LIST_FREE] += (MAX_SPRITES - RCT2_MAX_SPRITES); } void ImportSprite(rct_sprite* dst, const RCT2Sprite* src) @@ -1111,7 +1295,14 @@ public: dst->current_station = src->current_station; dst->current_time = src->current_time; dst->crash_z = src->crash_z; - dst->status = src->status; + + VEHICLE_STATUS statusSrc = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; + if (src->status <= static_cast(VEHICLE_STATUS_STOPPED_BY_BLOCK_BRAKES)) + { + statusSrc = static_cast(src->status); + } + + dst->status = statusSrc; dst->sub_state = src->sub_state; for (size_t i = 0; i < std::size(src->peep); i++) { @@ -1125,9 +1316,9 @@ public: dst->crash_x = src->crash_x; dst->sound2_flags = src->sound2_flags; dst->spin_sprite = src->spin_sprite; - dst->sound1_id = src->sound1_id; + dst->sound1_id = static_cast(src->sound1_id); dst->sound1_volume = src->sound1_volume; - dst->sound2_id = src->sound2_id; + dst->sound2_id = static_cast(src->sound2_id); dst->sound2_volume = src->sound2_volume; dst->sound_vector_factor = src->sound_vector_factor; dst->time_waiting = src->time_waiting; @@ -1137,7 +1328,7 @@ public: dst->animation_frame = src->animation_frame; dst->var_C8 = src->var_C8; dst->var_CA = src->var_CA; - dst->scream_sound_id = src->scream_sound_id; + dst->scream_sound_id = static_cast(src->scream_sound_id); dst->var_CD = src->var_CD; dst->var_CE = src->var_CE; dst->var_CF = src->var_CF; @@ -1155,7 +1346,10 @@ public: void ImportSpritePeep(Peep* dst, const RCT2SpritePeep* src) { ImportSpriteCommonProperties((rct_sprite_common*)dst, src); - dst->name_string_idx = src->name_string_idx; + if (is_user_string_id(src->name_string_idx)) + { + dst->SetName(GetUserString(src->name_string_idx)); + } dst->next_x = src->next_x; dst->next_y = src->next_y; dst->next_z = src->next_z; @@ -1273,16 +1467,16 @@ public: { case SPRITE_MISC_STEAM_PARTICLE: { - auto src = (const rct_steam_particle*)csrc; - auto dst = (RCT12SpriteSteamParticle*)cdst; + auto src = (const RCT12SpriteSteamParticle*)csrc; + auto dst = (rct_steam_particle*)cdst; dst->time_to_move = src->time_to_move; dst->frame = src->frame; break; } case SPRITE_MISC_MONEY_EFFECT: { - auto src = (const rct_money_effect*)csrc; - auto dst = (RCT12SpriteMoneyEffect*)cdst; + auto src = (const RCT12SpriteMoneyEffect*)csrc; + auto dst = (rct_money_effect*)cdst; dst->move_delay = src->move_delay; dst->num_movements = src->num_movements; dst->vertical = src->vertical; @@ -1293,8 +1487,8 @@ public: } case SPRITE_MISC_CRASHED_VEHICLE_PARTICLE: { - auto src = (const rct_crashed_vehicle_particle*)csrc; - auto dst = (RCT12SpriteCrashedVehicleParticle*)cdst; + auto src = (const RCT12SpriteCrashedVehicleParticle*)csrc; + auto dst = (rct_crashed_vehicle_particle*)cdst; dst->frame = src->frame; dst->time_to_live = src->time_to_live; dst->frame = src->frame; @@ -1321,14 +1515,14 @@ public: case SPRITE_MISC_JUMPING_FOUNTAIN_WATER: case SPRITE_MISC_JUMPING_FOUNTAIN_SNOW: { - auto src = (const RCT12SpriteJumpingFountain*)csrc; - auto dst = (rct_jumping_fountain*)cdst; - dst->num_ticks_alive = src->num_ticks_alive; + auto* src = (const RCT12SpriteJumpingFountain*)csrc; + auto* dst = (JumpingFountain*)cdst; + dst->NumTicksAlive = src->num_ticks_alive; dst->frame = src->frame; - dst->fountain_flags = src->fountain_flags; - dst->target_x = src->target_x; - dst->target_y = src->target_y; - dst->iteration = src->iteration; + dst->FountainFlags = src->fountain_flags; + dst->TargetX = src->target_x; + dst->TargetY = src->target_y; + dst->Iteration = src->iteration; break; } case SPRITE_MISC_BALLOON: @@ -1370,7 +1564,7 @@ public: dst->next_in_quadrant = src->next_in_quadrant; dst->next = src->next; dst->previous = src->previous; - dst->linked_list_type_offset = src->linked_list_type_offset; + dst->linked_list_index = src->linked_list_type_offset >> 1; dst->sprite_height_negative = src->sprite_height_negative; dst->sprite_index = src->sprite_index; dst->flags = src->flags; @@ -1385,6 +1579,14 @@ public: dst->sprite_bottom = src->sprite_bottom; dst->sprite_direction = src->sprite_direction; } + + std::string GetUserString(rct_string_id stringId) + { + const auto originalString = _s6.custom_strings[(stringId - USER_STRING_START) % 1024]; + std::string_view originalStringView(originalString, USER_STRING_MAX_LENGTH); + auto withoutFormatCodes = RCT12::RemoveFormatCodes(originalStringView); + return rct2_to_utf8(withoutFormatCodes, RCT2_LANGUAGE_ID_ENGLISH_UK); + } }; std::unique_ptr ParkImporter::CreateS6(IObjectRepository& objectRepository) @@ -1392,6 +1594,15 @@ std::unique_ptr ParkImporter::CreateS6(IObjectRepository& objectR return std::make_unique(objectRepository); } +static void show_error(uint8_t errorType, rct_string_id errorStringId) +{ + if (errorType == ERROR_TYPE_GENERIC) + { + context_show_error(errorStringId, 0xFFFF); + } + context_show_error(STR_UNABLE_TO_LOAD_FILE, errorStringId); +} + void load_from_sv6(const char* path) { auto context = OpenRCT2::GetContext(); @@ -1403,24 +1614,23 @@ void load_from_sv6(const char* path) objectMgr.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); s6Importer->Import(); game_fix_save_vars(); + AutoCreateMapAnimations(); sprite_position_tween_reset(); gScreenAge = 0; gLastAutoSaveUpdate = AUTOSAVE_PAUSE; } catch (const ObjectLoadException&) { - gErrorType = ERROR_TYPE_FILE_LOAD; - gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; + show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_INVALID_DATA); } - catch (const IOException&) + catch (const IOException& loadError) { - gErrorType = ERROR_TYPE_FILE_LOAD; - gErrorStringId = STR_GAME_SAVE_FAILED; + log_error("Error loading: %s", loadError.what()); + show_error(ERROR_TYPE_FILE_LOAD, STR_GAME_SAVE_FAILED); } catch (const std::exception&) { - gErrorType = ERROR_TYPE_FILE_LOAD; - gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; + show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_INVALID_DATA); } } @@ -1440,23 +1650,23 @@ void load_from_sc6(const char* path) objManager.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); s6Importer->Import(); game_fix_save_vars(); + AutoCreateMapAnimations(); sprite_position_tween_reset(); return; } - catch (const ObjectLoadException&) + catch (const ObjectLoadException& loadError) { - gErrorType = ERROR_TYPE_FILE_LOAD; - gErrorStringId = STR_GAME_SAVE_FAILED; + log_error("Error loading: %s", loadError.what()); + show_error(ERROR_TYPE_FILE_LOAD, STR_GAME_SAVE_FAILED); } - catch (const IOException&) + catch (const IOException& loadError) { - gErrorType = ERROR_TYPE_FILE_LOAD; - gErrorStringId = STR_GAME_SAVE_FAILED; + log_error("Error loading: %s", loadError.what()); + show_error(ERROR_TYPE_FILE_LOAD, STR_GAME_SAVE_FAILED); } catch (const std::exception&) { - gErrorType = ERROR_TYPE_FILE_LOAD; - gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; + show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_INVALID_DATA); } gScreenAge = 0; gLastAutoSaveUpdate = AUTOSAVE_PAUSE; diff --git a/src/openrct2/rct2/T6Exporter.cpp b/src/openrct2/rct2/T6Exporter.cpp new file mode 100644 index 0000000000..5c61948e19 --- /dev/null +++ b/src/openrct2/rct2/T6Exporter.cpp @@ -0,0 +1,135 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include "T6Exporter.h" + +#include "../Context.h" +#include "../core/FileStream.hpp" +#include "../core/MemoryStream.h" +#include "../localisation/Localisation.h" +#include "../localisation/StringIds.h" +#include "../object/ObjectList.h" +#include "../rct12/SawyerChunkWriter.h" +#include "../ride/Ride.h" +#include "../ride/Station.h" +#include "../ride/Track.h" +#include "../ride/TrackData.h" +#include "../ride/TrackDesign.h" +#include "../ride/TrackDesignRepository.h" +#include "../windows/Intent.h" + +#include + +T6Exporter::T6Exporter(TrackDesign* trackDesign) + : _trackDesign(trackDesign) +{ +} + +bool T6Exporter::SaveTrack(const utf8* path) +{ + try + { + auto fs = FileStream(path, FILE_MODE_WRITE); + return SaveTrack(&fs); + } + catch (const std::exception& e) + { + log_error("Unable to save track design: %s", e.what()); + return false; + } +} + +bool T6Exporter::SaveTrack(IStream* stream) +{ + MemoryStream tempStream; + tempStream.WriteValue(_trackDesign->type); + tempStream.WriteValue(_trackDesign->vehicle_type); + tempStream.WriteValue(_trackDesign->flags); + tempStream.WriteValue(_trackDesign->ride_mode); + tempStream.WriteValue((_trackDesign->colour_scheme & 0x3) | (2 << 2)); + tempStream.WriteArray(_trackDesign->vehicle_colours, RCT12_MAX_VEHICLE_COLOURS); + tempStream.WriteValue(0); + tempStream.WriteValue(_trackDesign->entrance_style); + tempStream.WriteValue(_trackDesign->total_air_time); + tempStream.WriteValue(_trackDesign->depart_flags); + tempStream.WriteValue(_trackDesign->number_of_trains); + tempStream.WriteValue(_trackDesign->number_of_cars_per_train); + tempStream.WriteValue(_trackDesign->min_waiting_time); + tempStream.WriteValue(_trackDesign->max_waiting_time); + tempStream.WriteValue(_trackDesign->operation_setting); + tempStream.WriteValue(_trackDesign->max_speed); + tempStream.WriteValue(_trackDesign->average_speed); + tempStream.WriteValue(_trackDesign->ride_length); + tempStream.WriteValue(_trackDesign->max_positive_vertical_g); + tempStream.WriteValue(_trackDesign->max_negative_vertical_g); + tempStream.WriteValue(_trackDesign->max_lateral_g); + tempStream.WriteValue(_trackDesign->type == RIDE_TYPE_MINI_GOLF ? _trackDesign->holes : _trackDesign->inversions); + tempStream.WriteValue(_trackDesign->drops); + tempStream.WriteValue(_trackDesign->highest_drop_height); + tempStream.WriteValue(_trackDesign->excitement); + tempStream.WriteValue(_trackDesign->intensity); + tempStream.WriteValue(_trackDesign->nausea); + tempStream.WriteValue(_trackDesign->upkeep_cost); + tempStream.WriteArray(_trackDesign->track_spine_colour, RCT12_NUM_COLOUR_SCHEMES); + tempStream.WriteArray(_trackDesign->track_rail_colour, RCT12_NUM_COLOUR_SCHEMES); + tempStream.WriteArray(_trackDesign->track_support_colour, RCT12_NUM_COLOUR_SCHEMES); + tempStream.WriteValue(_trackDesign->flags2); + tempStream.Write(&_trackDesign->vehicle_object, sizeof(rct_object_entry)); + tempStream.WriteValue(_trackDesign->space_required_x); + tempStream.WriteValue(_trackDesign->space_required_y); + tempStream.WriteArray(_trackDesign->vehicle_additional_colour, RCT2_MAX_CARS_PER_TRAIN); + tempStream.WriteValue(_trackDesign->lift_hill_speed | (_trackDesign->num_circuits << 5)); + + if (_trackDesign->type == RIDE_TYPE_MAZE) + { + for (const auto& mazeElement : _trackDesign->maze_elements) + { + tempStream.WriteValue(mazeElement.all); + } + + tempStream.WriteValue(0); + } + else + { + for (const auto& trackElement : _trackDesign->track_elements) + { + tempStream.WriteValue(trackElement.type); + tempStream.WriteValue(trackElement.flags); + } + + tempStream.WriteValue(0xFF); + + for (const auto& entranceElement : _trackDesign->entrance_elements) + { + tempStream.WriteValue(entranceElement.z == -1 ? (uint8_t)0x80 : entranceElement.z); + tempStream.WriteValue(entranceElement.direction | (entranceElement.isExit << 7)); + tempStream.WriteValue(entranceElement.x); + tempStream.WriteValue(entranceElement.y); + } + + tempStream.WriteValue(0xFF); + } + + for (const auto& sceneryElement : _trackDesign->scenery_elements) + { + tempStream.Write(&sceneryElement.scenery_object, sizeof(rct_object_entry)); + tempStream.WriteValue(sceneryElement.x); + tempStream.WriteValue(sceneryElement.y); + tempStream.WriteValue(sceneryElement.z); + tempStream.WriteValue(sceneryElement.flags); + tempStream.WriteValue(sceneryElement.primary_colour); + tempStream.WriteValue(sceneryElement.secondary_colour); + } + + tempStream.WriteValue(0xFF); + + SawyerChunkWriter sawyerCoding(stream); + sawyerCoding.WriteChunkTrack(tempStream.GetData(), tempStream.GetLength()); + return true; +} diff --git a/src/openrct2/rct2/T6Exporter.h b/src/openrct2/rct2/T6Exporter.h new file mode 100644 index 0000000000..5302a8ef3e --- /dev/null +++ b/src/openrct2/rct2/T6Exporter.h @@ -0,0 +1,32 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../common.h" +#include "../ride/TrackDesign.h" + +#include + +interface IStream; + +/** + * Class to export RollerCoaster Tycoon 2 track designs (*.TD6). + */ +class T6Exporter final +{ +public: + T6Exporter(TrackDesign* trackDesign); + + bool SaveTrack(const utf8* path); + bool SaveTrack(IStream* stream); + +private: + TrackDesign* _trackDesign; +}; diff --git a/src/openrct2/rct2/T6Importer.cpp b/src/openrct2/rct2/T6Importer.cpp new file mode 100644 index 0000000000..6e995a4df6 --- /dev/null +++ b/src/openrct2/rct2/T6Importer.cpp @@ -0,0 +1,209 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include "../TrackImporter.h" +#include "../config/Config.h" +#include "../core/FileStream.hpp" +#include "../core/MemoryStream.h" +#include "../core/Path.hpp" +#include "../core/String.hpp" +#include "../rct12/SawyerChunkReader.h" +#include "../rct12/SawyerEncoding.h" +#include "../ride/Ride.h" +#include "../ride/TrackDesign.h" +#include "../ride/TrackDesignRepository.h" + +/** + * Class to import RollerCoaster Tycoon 2 track designs (*.TD6). + */ +class TD6Importer final : public ITrackImporter +{ +private: + MemoryStream _stream; + std::string _name; + +public: + TD6Importer() + { + } + + bool Load(const utf8* path) override + { + const utf8* extension = Path::GetExtension(path); + if (String::Equals(extension, ".td6", true)) + { + _name = GetNameFromTrackPath(path); + auto fs = FileStream(path, FILE_MODE_OPEN); + return LoadFromStream(&fs); + } + else + { + throw std::runtime_error("Invalid RCT2 track extension."); + } + } + + bool LoadFromStream(IStream* stream) override + { + if (!gConfigGeneral.allow_loading_with_incorrect_checksum + && SawyerEncoding::ValidateTrackChecksum(stream) != RCT12TrackDesignVersion::TD6) + { + throw IOException("Invalid checksum."); + } + + auto chunkReader = SawyerChunkReader(stream); + auto data = chunkReader.ReadChunkTrack(); + _stream.WriteArray(reinterpret_cast(data->GetData()), data->GetLength()); + _stream.SetPosition(0); + return true; + } + + std::unique_ptr Import() override + { + std::unique_ptr td = std::make_unique(); + + rct_track_td6 td6{}; + // Rework td6 so that it is just the fields + _stream.Read(&td6, 0xA3); + + td->type = td6.type; // 0x00 + td->vehicle_type = td6.vehicle_type; + + td->cost = 0; + td->flags = td6.flags; + td->ride_mode = td6.ride_mode; + td->track_flags = 0; + td->colour_scheme = td6.version_and_colour_scheme & 0x3; + for (auto i = 0; i < RCT2_MAX_CARS_PER_TRAIN; ++i) + { + td->vehicle_colours[i] = td6.vehicle_colours[i]; + td->vehicle_additional_colour[i] = td6.vehicle_additional_colour[i]; + } + td->entrance_style = td6.entrance_style; + td->total_air_time = td6.total_air_time; + td->depart_flags = td6.depart_flags; + td->number_of_trains = td6.number_of_trains; + td->number_of_cars_per_train = td6.number_of_cars_per_train; + td->min_waiting_time = td6.min_waiting_time; + td->max_waiting_time = td6.max_waiting_time; + td->operation_setting = td6.operation_setting; + td->max_speed = td6.max_speed; + td->average_speed = td6.average_speed; + td->ride_length = td6.ride_length; + td->max_positive_vertical_g = td6.max_positive_vertical_g; + td->max_negative_vertical_g = td6.max_negative_vertical_g; + td->max_lateral_g = td6.max_lateral_g; + + if (td->type == RIDE_TYPE_MINI_GOLF) + { + td->holes = td6.holes; + } + else + { + td->inversions = td6.inversions; + } + + td->drops = td6.drops; + td->highest_drop_height = td6.highest_drop_height; + td->excitement = td6.excitement; + td->intensity = td6.intensity; + td->nausea = td6.nausea; + td->upkeep_cost = td6.upkeep_cost; + for (auto i = 0; i < RCT12_NUM_COLOUR_SCHEMES; ++i) + { + td->track_spine_colour[i] = td6.track_spine_colour[i]; + td->track_rail_colour[i] = td6.track_rail_colour[i]; + td->track_support_colour[i] = td6.track_support_colour[i]; + } + td->flags2 = td6.flags2; + td->vehicle_object = td6.vehicle_object; + td->space_required_x = td6.space_required_x; + td->space_required_y = td6.space_required_y; + td->lift_hill_speed = td6.lift_hill_speed_num_circuits & 0b00011111; + td->num_circuits = td6.lift_hill_speed_num_circuits >> 5; + + auto version = static_cast((td6.version_and_colour_scheme >> 2) & 3); + if (version != RCT12TrackDesignVersion::TD6) + { + log_error("Unsupported track design."); + return nullptr; + } + + td->operation_setting = std::min(td->operation_setting, RideProperties[td->type].max_value); + + if (td->type == RIDE_TYPE_MAZE) + { + rct_td46_maze_element t6MazeElement{}; + t6MazeElement.all = !0; + while (t6MazeElement.all != 0) + { + _stream.Read(&t6MazeElement, sizeof(rct_td46_maze_element)); + if (t6MazeElement.all != 0) + { + TrackDesignMazeElement mazeElement{}; + mazeElement.x = t6MazeElement.x; + mazeElement.y = t6MazeElement.y; + mazeElement.direction = t6MazeElement.direction; + mazeElement.type = t6MazeElement.type; + td->maze_elements.push_back(mazeElement); + } + } + } + else + { + rct_td46_track_element t6TrackElement{}; + for (uint8_t endFlag = _stream.ReadValue(); endFlag != 0xFF; endFlag = _stream.ReadValue()) + { + _stream.SetPosition(_stream.GetPosition() - 1); + _stream.Read(&t6TrackElement, sizeof(rct_td46_track_element)); + TrackDesignTrackElement trackElement{}; + trackElement.type = t6TrackElement.type; + trackElement.flags = t6TrackElement.flags; + td->track_elements.push_back(trackElement); + } + + rct_td6_entrance_element t6EntranceElement{}; + for (uint8_t endFlag = _stream.ReadValue(); endFlag != 0xFF; endFlag = _stream.ReadValue()) + { + _stream.SetPosition(_stream.GetPosition() - 1); + _stream.Read(&t6EntranceElement, sizeof(rct_td6_entrance_element)); + TrackDesignEntranceElement entranceElement{}; + entranceElement.z = (t6EntranceElement.z == (int8_t)(uint8_t)0x80) ? -1 : t6EntranceElement.z; + entranceElement.direction = t6EntranceElement.direction & 0x7F; + entranceElement.x = t6EntranceElement.x; + entranceElement.y = t6EntranceElement.y; + entranceElement.isExit = t6EntranceElement.direction >> 7; + td->entrance_elements.push_back(entranceElement); + } + } + + for (uint8_t endFlag = _stream.ReadValue(); endFlag != 0xFF; endFlag = _stream.ReadValue()) + { + _stream.SetPosition(_stream.GetPosition() - 1); + rct_td6_scenery_element t6SceneryElement{}; + _stream.Read(&t6SceneryElement, sizeof(rct_td6_scenery_element)); + TrackDesignSceneryElement sceneryElement{}; + sceneryElement.scenery_object = t6SceneryElement.scenery_object; + sceneryElement.x = t6SceneryElement.x; + sceneryElement.y = t6SceneryElement.y; + sceneryElement.z = t6SceneryElement.z; + sceneryElement.flags = t6SceneryElement.flags; + sceneryElement.primary_colour = t6SceneryElement.primary_colour; + sceneryElement.secondary_colour = t6SceneryElement.secondary_colour; + td->scenery_elements.push_back(sceneryElement); + } + + td->name = _name; + return td; + } +}; + +std::unique_ptr TrackImporter::CreateTD6() +{ + return std::make_unique(); +} diff --git a/src/openrct2/ride/CableLift.cpp b/src/openrct2/ride/CableLift.cpp index 3474ef89e2..b3212ceecb 100644 --- a/src/openrct2/ride/CableLift.cpp +++ b/src/openrct2/ride/CableLift.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -9,6 +9,7 @@ #include "CableLift.h" +#include "../audio/audio.h" #include "../rct12/RCT12.h" #include "../util/Util.h" #include "../world/Sprite.h" @@ -26,18 +27,16 @@ static void cable_lift_update_travelling(rct_vehicle* vehicle); static void cable_lift_update_arriving(rct_vehicle* vehicle); rct_vehicle* cable_lift_segment_create( - ride_id_t rideIndex, int32_t x, int32_t y, int32_t z, int32_t direction, uint16_t var_44, int32_t remaining_distance, - bool head) + Ride& ride, int32_t x, int32_t y, int32_t z, int32_t direction, uint16_t var_44, int32_t remaining_distance, bool head) { - Ride* ride = get_ride(rideIndex); - rct_vehicle* current = &(create_sprite(1)->vehicle); + rct_vehicle* current = &(create_sprite(SPRITE_IDENTIFIER_VEHICLE)->vehicle); current->sprite_identifier = SPRITE_IDENTIFIER_VEHICLE; - current->ride = rideIndex; + current->ride = ride.id; current->ride_subtype = RIDE_ENTRY_INDEX_NULL; if (head) { - move_sprite_to_list((rct_sprite*)current, SPRITE_LIST_TRAIN * 2); - ride->cable_lift = current->sprite_index; + move_sprite_to_list((rct_sprite*)current, SPRITE_LIST_VEHICLE_HEAD); + ride.cable_lift = current->sprite_index; } current->type = head ? VEHICLE_TYPE_HEAD : VEHICLE_TYPE_TAIL; current->var_44 = var_44; @@ -58,13 +57,13 @@ rct_vehicle* cable_lift_segment_create( current->spin_sprite = 0; current->spin_speed = 0; current->sound2_flags = 0; - current->sound1_id = RCT12_SOUND_ID_NULL; - current->sound2_id = RCT12_SOUND_ID_NULL; + current->sound1_id = SoundId::Null; + current->sound2_id = SoundId::Null; current->var_C4 = 0; current->animation_frame = 0; current->var_C8 = 0; current->var_CA = 0; - current->scream_sound_id = 0xFF; + current->scream_sound_id = SoundId::Null; current->vehicle_sprite_type = 0; current->bank_rotation = 0; for (auto& peep : current->peep) @@ -78,14 +77,13 @@ rct_vehicle* cable_lift_segment_create( z = z * 8; current->track_z = z; - z += RideData5[ride->type].z_offset; + z += RideData5[ride.type].z_offset; sprite_move(16, 16, z, (rct_sprite*)current); current->track_type = (TRACK_ELEM_CABLE_LIFT_HILL << 2) | (current->sprite_direction >> 3); current->track_progress = 164; current->update_flags = VEHICLE_UPDATE_FLAG_1; - current->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; - current->sub_state = 0; + current->SetState(VEHICLE_STATUS_MOVING_TO_END_OF_STATION, 0); current->num_peeps = 0; current->next_free_seat = 0; return current; @@ -113,6 +111,8 @@ void cable_lift_update(rct_vehicle* vehicle) case VEHICLE_STATUS_ARRIVING: cable_lift_update_arriving(vehicle); break; + default: + break; } } @@ -136,7 +136,7 @@ static void cable_lift_update_moving_to_end_of_station(rct_vehicle* vehicle) vehicle->velocity = 0; vehicle->acceleration = 0; - vehicle->status = VEHICLE_STATUS_WAITING_FOR_PASSENGERS; + vehicle->SetState(VEHICLE_STATUS_WAITING_FOR_PASSENGERS, vehicle->sub_state); } /** @@ -170,8 +170,7 @@ static void cable_lift_update_waiting_to_depart(rct_vehicle* vehicle) vehicle->velocity = 0; vehicle->acceleration = 0; - vehicle->status = VEHICLE_STATUS_DEPARTING; - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_DEPARTING, 0); } /** @@ -185,8 +184,8 @@ static void cable_lift_update_departing(rct_vehicle* vehicle) return; rct_vehicle* passengerVehicle = GET_VEHICLE(vehicle->cable_lift_target); - vehicle->status = VEHICLE_STATUS_TRAVELLING; - passengerVehicle->status = VEHICLE_STATUS_TRAVELLING_CABLE_LIFT; + vehicle->SetState(VEHICLE_STATUS_TRAVELLING, vehicle->sub_state); + passengerVehicle->SetState(VEHICLE_STATUS_TRAVELLING_CABLE_LIFT, passengerVehicle->sub_state); } /** @@ -207,8 +206,7 @@ static void cable_lift_update_travelling(rct_vehicle* vehicle) vehicle->velocity = 0; vehicle->acceleration = 0; - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING, 0); } /** @@ -219,12 +217,15 @@ static void cable_lift_update_arriving(rct_vehicle* vehicle) { vehicle->sub_state++; if (vehicle->sub_state >= 64) - vehicle->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; + vehicle->SetState(VEHICLE_STATUS_MOVING_TO_END_OF_STATION, vehicle->sub_state); } static bool sub_6DF01A_loop(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return false; + for (; vehicle->remaining_distance >= 13962; _vehicleUnkF64E10++) { uint8_t trackType = vehicle->track_type >> 2; @@ -304,7 +305,10 @@ static bool sub_6DF01A_loop(rct_vehicle* vehicle) static bool sub_6DF21B_loop(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return false; + for (; vehicle->remaining_distance < 0; _vehicleUnkF64E10++) { uint16_t trackProgress = vehicle->track_progress - 1; @@ -423,7 +427,7 @@ int32_t cable_lift_update_track_motion(rct_vehicle* cableLift) { if (vehicle->remaining_distance < 0) { - if (sub_6DF21B_loop(vehicle) == true) + if (sub_6DF21B_loop(vehicle)) { break; } @@ -439,7 +443,7 @@ int32_t cable_lift_update_track_motion(rct_vehicle* cableLift) } else { - if (sub_6DF01A_loop(vehicle) == true) + if (sub_6DF01A_loop(vehicle)) { break; } diff --git a/src/openrct2/ride/CableLift.h b/src/openrct2/ride/CableLift.h index 0f1315bac3..2a868fa674 100644 --- a/src/openrct2/ride/CableLift.h +++ b/src/openrct2/ride/CableLift.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,8 +14,7 @@ #include "Vehicle.h" rct_vehicle* cable_lift_segment_create( - ride_id_t rideIndex, int32_t x, int32_t y, int32_t z, int32_t direction, uint16_t var_44, int32_t remaining_distance, - bool head); + Ride& ride, int32_t x, int32_t y, int32_t z, int32_t direction, uint16_t var_44, int32_t remaining_distance, bool head); void cable_lift_update(rct_vehicle* vehicle); int32_t cable_lift_update_track_motion(rct_vehicle* cableLift); diff --git a/src/openrct2/ride/MusicList.cpp b/src/openrct2/ride/MusicList.cpp index 83c0dcb53b..75f37d32c2 100644 --- a/src/openrct2/ride/MusicList.cpp +++ b/src/openrct2/ride/MusicList.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/MusicList.h b/src/openrct2/ride/MusicList.h index 01fd2e158e..4240b7680f 100644 --- a/src/openrct2/ride/MusicList.h +++ b/src/openrct2/ride/MusicList.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/Ride.cpp b/src/openrct2/ride/Ride.cpp index cadff985bd..fdfdee2452 100644 --- a/src/openrct2/ride/Ride.cpp +++ b/src/openrct2/ride/Ride.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -24,6 +24,7 @@ #include "../common.h" #include "../config/Config.h" #include "../core/Guard.hpp" +#include "../core/Optional.hpp" #include "../interface/Window.h" #include "../localisation/Date.h" #include "../localisation/Localisation.h" @@ -59,6 +60,7 @@ #include "Station.h" #include "Track.h" #include "TrackData.h" +#include "TrackDesign.h" #include #include @@ -69,91 +71,13 @@ using namespace OpenRCT2; -#pragma region Ride classification table - -/** rct2: 0x0097C3AF */ -// clang-format off -const uint8_t gRideClassifications[255] = { - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_SHOP_OR_STALL, RIDE_CLASS_SHOP_OR_STALL, RIDE_CLASS_SHOP_OR_STALL, - RIDE_CLASS_SHOP_OR_STALL, RIDE_CLASS_SHOP_OR_STALL, RIDE_CLASS_RIDE, RIDE_CLASS_SHOP_OR_STALL, - RIDE_CLASS_KIOSK_OR_FACILITY, RIDE_CLASS_KIOSK_OR_FACILITY, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_KIOSK_OR_FACILITY, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_KIOSK_OR_FACILITY, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_SHOP_OR_STALL, RIDE_CLASS_RIDE, RIDE_CLASS_SHOP_OR_STALL, - RIDE_CLASS_SHOP_OR_STALL, RIDE_CLASS_SHOP_OR_STALL, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, - RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE, RIDE_CLASS_RIDE -}; -// clang-format on - uint8_t gTypeToRideEntryIndexMap[TYPE_TO_RIDE_ENTRY_SLOTS]; - -#pragma endregion - static constexpr const int32_t RideInspectionInterval[] = { 10, 20, 30, 45, 60, 120, 0, 0, }; -Ride gRideList[MAX_RIDES]; +static std::vector _rides; -rct_ride_measurement gRideMeasurements[MAX_RIDE_MEASUREMENTS]; - -uint16_t gRideCount; bool gGotoStartPlacementMode = false; money16 gTotalRideValueForMoney; @@ -205,30 +129,84 @@ static void ride_breakdown_status_update(Ride* ride); static void ride_breakdown_update(Ride* ride); static void ride_call_closest_mechanic(Ride* ride); static void ride_call_mechanic(Ride* ride, Peep* mechanic, int32_t forInspection); -static void ride_chairlift_update(Ride* ride); static void ride_entrance_exit_connected(Ride* ride); -static void ride_set_name_to_vehicle_default(Ride* ride, rct_ride_entry* rideEntry); static int32_t ride_get_new_breakdown_problem(Ride* ride); static void ride_inspection_update(Ride* ride); static void ride_mechanic_status_update(Ride* ride, int32_t mechanicStatus); static void ride_music_update(Ride* ride); static void ride_shop_connected(Ride* ride); -static void ride_spiral_slide_update(Ride* ride); -static void ride_update(Ride* ride); void loc_6DDF9C(Ride* ride, TileElement* tileElement); -Ride* get_ride(int32_t index) +RideManager GetRideManager() { - if (index < 0 || index >= MAX_RIDES) + return {}; +} + +size_t RideManager::size() const +{ + size_t count = 0; + for (size_t i = 0; i < _rides.size(); i++) { - log_error("invalid index %d for ride", index); - return nullptr; + if (_rides[i].type != RIDE_TYPE_NULL) + { + count++; + } } - auto ride = &gRideList[index]; -#ifdef DEBUG - assert(ride->id == index); -#endif - return ride; + return count; +} + +RideManager::Iterator RideManager::begin() +{ + return RideManager::Iterator(*this, 0, _rides.size()); +} + +RideManager::Iterator RideManager::end() +{ + return RideManager::Iterator(*this, _rides.size(), _rides.size()); +} + +ride_id_t GetNextFreeRideId() +{ + size_t result = _rides.size(); + for (size_t i = 0; i < _rides.size(); i++) + { + if (_rides[i].type == RIDE_TYPE_NULL) + { + result = i; + break; + } + } + if (result >= RIDE_ID_NULL) + { + return RIDE_ID_NULL; + } + return (ride_id_t)result; +} + +Ride* GetOrAllocateRide(ride_id_t index) +{ + if (_rides.size() <= index) + { + _rides.resize(index + 1); + } + + auto result = &_rides[index]; + result->id = index; + return result; +} + +Ride* get_ride(ride_id_t index) +{ + if (index < _rides.size()) + { + auto& ride = _rides[index]; + if (ride.type != RIDE_TYPE_NULL) + { + assert(ride.id == index); + return &ride; + } + } + return nullptr; } rct_ride_entry* get_ride_entry(int32_t index) @@ -245,34 +223,25 @@ rct_ride_entry* get_ride_entry(int32_t index) return result; } -void get_ride_entry_name(char* name, int32_t index) +std::string_view get_ride_entry_name(size_t index) { - if (index < 0 || index >= object_entry_group_counts[OBJECT_TYPE_RIDE]) + if (index >= (size_t)object_entry_group_counts[OBJECT_TYPE_RIDE]) { log_error("invalid index %d for ride type", index); - return; + return {}; } - const auto entryName = object_entry_get_entry(OBJECT_TYPE_RIDE, index)->name; - std::memcpy(name, entryName, 8); - name[8] = '\0'; -} - -rct_ride_measurement* get_ride_measurement(int32_t index) -{ - return &gRideMeasurements[index]; -} - -rct_ride_entry* get_ride_entry_by_ride(const Ride* ride) -{ - rct_ride_entry* type = get_ride_entry(ride->subtype); - if (type == nullptr) + auto objectEntry = object_entry_get_entry(OBJECT_TYPE_RIDE, index); + if (objectEntry != nullptr) { - char oldname[128]; - format_string(oldname, 128, ride->name, &ride->name_arguments); - log_error("Invalid ride subtype for ride %s", oldname); + return objectEntry->GetName(); } - return type; + return {}; +} + +rct_ride_entry* Ride::GetRideEntry() const +{ + return get_ride_entry(subtype); } /** @@ -335,38 +304,32 @@ uint8_t* get_ride_entry_indices_for_ride_type(uint8_t rideType) int32_t ride_get_count() { - Ride* ride; - int32_t i, count = 0; - - FOR_ALL_RIDES (i, ride) - count++; - - return count; + return (int32_t)GetRideManager().size(); } -int32_t ride_get_total_queue_length(Ride* ride) +int32_t Ride::GetTotalQueueLength() const { int32_t i, queueLength = 0; for (i = 0; i < MAX_STATIONS; i++) - if (!ride_get_entrance_location(ride, i).isNull()) - queueLength += ride->stations[i].QueueLength; + if (!ride_get_entrance_location(this, i).isNull()) + queueLength += stations[i].QueueLength; return queueLength; } -int32_t ride_get_max_queue_time(Ride* ride) +int32_t Ride::GetMaxQueueTime() const { uint8_t i, queueTime = 0; for (i = 0; i < MAX_STATIONS; i++) - if (!ride_get_entrance_location(ride, i).isNull()) - queueTime = std::max(queueTime, ride->stations[i].QueueTime); + if (!ride_get_entrance_location(this, i).isNull()) + queueTime = std::max(queueTime, stations[i].QueueTime); return (int32_t)queueTime; } -Peep* ride_get_queue_head_guest(Ride* ride, int32_t stationIndex) +Peep* Ride::GetQueueHeadGuest(int32_t stationIndex) const { Peep* peep; Peep* result = nullptr; - uint16_t spriteIndex = ride->stations[stationIndex].LastPeepInQueue; + uint16_t spriteIndex = stations[stationIndex].LastPeepInQueue; while ((peep = try_get_guest(spriteIndex)) != nullptr) { spriteIndex = peep->next_in_queue; @@ -375,36 +338,35 @@ Peep* ride_get_queue_head_guest(Ride* ride, int32_t stationIndex) return result; } -static void ride_update_queue_length(Ride* ride, int32_t stationIndex) +void Ride::UpdateQueueLength(int32_t stationIndex) { uint16_t count = 0; Peep* peep; - uint16_t spriteIndex = ride->stations[stationIndex].LastPeepInQueue; + uint16_t spriteIndex = stations[stationIndex].LastPeepInQueue; while ((peep = try_get_guest(spriteIndex)) != nullptr) { spriteIndex = peep->next_in_queue; count++; } - ride->stations[stationIndex].QueueLength = count; + stations[stationIndex].QueueLength = count; } -void ride_queue_insert_guest_at_front(Ride* ride, int32_t stationIndex, Peep* peep) +void Ride::QueueInsertGuestAtFront(int32_t stationIndex, Peep* peep) { - assert(ride != nullptr); assert(stationIndex < MAX_STATIONS); assert(peep != nullptr); peep->next_in_queue = SPRITE_INDEX_NULL; - Peep* queueHeadGuest = ride_get_queue_head_guest(ride, peep->current_ride_station); + Peep* queueHeadGuest = GetQueueHeadGuest(peep->current_ride_station); if (queueHeadGuest == nullptr) { - ride->stations[peep->current_ride_station].LastPeepInQueue = peep->sprite_index; + stations[peep->current_ride_station].LastPeepInQueue = peep->sprite_index; } else { queueHeadGuest->next_in_queue = peep->sprite_index; } - ride_update_queue_length(ride, peep->current_ride_station); + UpdateQueueLength(peep->current_ride_station); } /** @@ -413,23 +375,24 @@ void ride_queue_insert_guest_at_front(Ride* ride, int32_t stationIndex, Peep* pe */ void ride_update_favourited_stat() { - int32_t i; - Ride* ride; uint16_t spriteIndex; Peep* peep; - FOR_ALL_RIDES (i, ride) - ride->guests_favourite = 0; + for (auto& ride : GetRideManager()) + ride.guests_favourite = 0; FOR_ALL_PEEPS (spriteIndex, peep) { - if (peep->linked_list_type_offset != SPRITE_LIST_PEEP * 2) + if (peep->linked_list_index != SPRITE_LIST_PEEP) return; if (peep->favourite_ride != RIDE_ID_NULL) { - ride = get_ride(peep->favourite_ride); - ride->guests_favourite++; - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER; + auto ride = get_ride(peep->favourite_ride); + if (ride != nullptr) + { + ride->guests_favourite++; + ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER; + } } } @@ -440,30 +403,29 @@ void ride_update_favourited_stat() * * rct2: 0x006AC3AB */ -static money32 ride_calculate_income_per_hour(Ride* ride) +money32 Ride::CalculateIncomePerHour() const { // Get entry by ride to provide better reporting - rct_ride_entry* entry = get_ride_entry_by_ride(ride); + rct_ride_entry* entry = GetRideEntry(); if (entry == nullptr) { return 0; } - money32 customersPerHour = ride_customers_per_hour(ride); - money32 priceMinusCost = ride_get_price(ride); + money32 customersPerHour = ride_customers_per_hour(this); + money32 priceMinusCost = ride_get_price(this); int32_t currentShopItem = entry->shop_item; if (currentShopItem != SHOP_ITEM_NONE) { - priceMinusCost -= get_shop_item_cost(currentShopItem); + priceMinusCost -= ShopItems[currentShopItem].Cost; } - currentShopItem = (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) ? RidePhotoItems[ride->type] - : entry->shop_item_secondary; + currentShopItem = (lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) ? RidePhotoItems[type] : entry->shop_item_secondary; if (currentShopItem != SHOP_ITEM_NONE) { - priceMinusCost += ride->price_secondary; - priceMinusCost -= get_shop_item_cost(currentShopItem); + priceMinusCost += price_secondary; + priceMinusCost -= ShopItems[currentShopItem].Cost; if (entry->shop_item != SHOP_ITEM_NONE) priceMinusCost /= 2; @@ -551,21 +513,26 @@ bool track_block_get_next_from_zero( do { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) + auto trackElement = tileElement->AsTrack(); + if (trackElement == nullptr) continue; - if (tileElement->AsTrack()->GetRideIndex() != ride->id) + if (trackElement->GetRideIndex() != ride->id) continue; - if (tileElement->AsTrack()->GetSequenceIndex() != 0) + if (trackElement->GetSequenceIndex() != 0) continue; if (tileElement->IsGhost() != isGhost) continue; - const rct_preview_track* nextTrackBlock = get_track_def_from_ride(ride, tileElement->AsTrack()->GetTrackType()); - const rct_track_coordinates* nextTrackCoordinate = get_track_coord_from_ride( - ride, tileElement->AsTrack()->GetTrackType()); + auto nextTrackBlock = get_track_def_from_ride(ride, trackElement->GetTrackType()); + if (nextTrackBlock == nullptr) + continue; + + auto nextTrackCoordinate = get_track_coord_from_ride(ride, trackElement->GetTrackType()); + if (nextTrackCoordinate == nullptr) + continue; uint8_t nextRotation = tileElement->GetDirectionWithOffset(nextTrackCoordinate->rotation_begin) | (nextTrackCoordinate->rotation_begin & (1 << 2)); @@ -603,47 +570,36 @@ bool track_block_get_next_from_zero( */ bool track_block_get_next(CoordsXYE* input, CoordsXYE* output, int32_t* z, int32_t* direction) { - ride_id_t rideIndex = input->element->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); + auto inputElement = input->element->AsTrack(); + if (inputElement == nullptr) + return false; - const rct_preview_track* trackBlock = get_track_def_from_ride(ride, input->element->AsTrack()->GetTrackType()); - uint8_t sequence = input->element->AsTrack()->GetSequenceIndex(); - trackBlock += sequence; + auto rideIndex = inputElement->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return false; - const rct_track_coordinates* trackCoordinate = get_track_coord_from_ride(ride, input->element->AsTrack()->GetTrackType()); + auto trackBlock = get_track_def_from_ride(ride, inputElement->GetTrackType()); + if (trackBlock == nullptr) + return false; + + trackBlock += inputElement->GetSequenceIndex(); + + auto trackCoordinate = get_track_coord_from_ride(ride, inputElement->GetTrackType()); + if (trackCoordinate == nullptr) + return false; int32_t x = input->x; int32_t y = input->y; - int32_t OriginZ = input->element->base_height * 8; + int32_t OriginZ = inputElement->base_height * 8; - uint8_t rotation = input->element->GetDirection(); - switch (rotation) - { - case 0: - x += trackCoordinate->x; - x -= trackBlock->x; - y += trackCoordinate->y; - y -= trackBlock->y; - break; - case 1: - x += trackCoordinate->y; - x -= trackBlock->y; - y -= trackCoordinate->x; - y += trackBlock->x; - break; - case 2: - x -= trackCoordinate->x; - x += trackBlock->x; - y -= trackCoordinate->y; - y += trackBlock->y; - break; - case 3: - x -= trackCoordinate->y; - x += trackBlock->y; - y += trackCoordinate->x; - y -= trackBlock->x; - break; - } + uint8_t rotation = inputElement->GetDirection(); + + CoordsXY coords = { x, y }; + CoordsXY trackCoordOffset = { trackCoordinate->x, trackCoordinate->y }; + CoordsXY trackBlockOffset = { trackBlock->x, trackBlock->y }; + coords += trackCoordOffset.Rotate(rotation); + coords += trackBlockOffset.Rotate(direction_reverse(rotation)); OriginZ -= trackBlock->z; OriginZ += trackCoordinate->z_end; @@ -651,7 +607,7 @@ bool track_block_get_next(CoordsXYE* input, CoordsXYE* output, int32_t* z, int32 uint8_t directionStart = ((trackCoordinate->rotation_end + rotation) & TILE_ELEMENT_DIRECTION_MASK) | (trackCoordinate->rotation_end & (1 << 2)); - return track_block_get_next_from_zero(x, y, OriginZ, ride, directionStart, output, z, direction, false); + return track_block_get_next_from_zero(coords.x, coords.y, OriginZ, ride, directionStart, output, z, direction, false); } /** @@ -681,22 +637,27 @@ bool track_block_get_previous_from_zero( outTrackBeginEnd->end_y = y; outTrackBeginEnd->begin_element = nullptr; outTrackBeginEnd->begin_direction = direction_reverse(directionStart); - return 0; + return false; } do { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) + auto trackElement = tileElement->AsTrack(); + if (trackElement == nullptr) continue; - if (tileElement->AsTrack()->GetRideIndex() != ride->id) + if (trackElement->GetRideIndex() != ride->id) continue; - const rct_preview_track* nextTrackBlock = get_track_def_from_ride(ride, tileElement->AsTrack()->GetTrackType()); - const rct_track_coordinates* nextTrackCoordinate = get_track_coord_from_ride( - ride, tileElement->AsTrack()->GetTrackType()); + auto nextTrackBlock = get_track_def_from_ride(ride, trackElement->GetTrackType()); + if (nextTrackBlock == nullptr) + continue; - nextTrackBlock += tileElement->AsTrack()->GetSequenceIndex(); + auto nextTrackCoordinate = get_track_coord_from_ride(ride, trackElement->GetTrackType()); + if (nextTrackCoordinate == nullptr) + continue; + + nextTrackBlock += trackElement->GetSequenceIndex(); if ((nextTrackBlock + 1)->index != 255) continue; @@ -717,33 +678,23 @@ bool track_block_get_previous_from_zero( outTrackBeginEnd->begin_y = y; outTrackBeginEnd->end_x = x; outTrackBeginEnd->end_y = y; - switch (nextRotation & 3) - { - case 0: - outTrackBeginEnd->begin_x -= nextTrackCoordinate->x; - outTrackBeginEnd->begin_y -= nextTrackCoordinate->y; - break; - case 1: - outTrackBeginEnd->begin_x -= nextTrackCoordinate->y; - outTrackBeginEnd->begin_y += nextTrackCoordinate->x; - break; - case 2: - outTrackBeginEnd->begin_x += nextTrackCoordinate->x; - outTrackBeginEnd->begin_y += nextTrackCoordinate->y; - break; - case 3: - outTrackBeginEnd->begin_x += nextTrackCoordinate->y; - outTrackBeginEnd->begin_y -= nextTrackCoordinate->x; - break; - } + + CoordsXY coords = { outTrackBeginEnd->begin_x, outTrackBeginEnd->begin_y }; + CoordsXY offsets = { nextTrackCoordinate->x, nextTrackCoordinate->y }; + coords += offsets.Rotate(direction_reverse(nextRotation)); + outTrackBeginEnd->begin_x = coords.x; + outTrackBeginEnd->begin_y = coords.y; outTrackBeginEnd->begin_z = tileElement->base_height * 8; - outTrackBeginEnd->begin_z += get_track_def_from_ride(ride, tileElement->AsTrack()->GetTrackType())->z - - nextTrackBlock->z; + auto nextTrackBlock2 = get_track_def_from_ride(ride, trackElement->GetTrackType()); + if (nextTrackBlock2 == nullptr) + continue; + + outTrackBeginEnd->begin_z += nextTrackBlock2->z - nextTrackBlock->z; outTrackBeginEnd->begin_direction = nextRotation; outTrackBeginEnd->end_direction = direction_reverse(directionStart); - return 1; + return true; } while (!(tileElement++)->IsLastForTile()); outTrackBeginEnd->end_x = x; @@ -751,7 +702,7 @@ bool track_block_get_previous_from_zero( outTrackBeginEnd->begin_z = z; outTrackBeginEnd->begin_element = nullptr; outTrackBeginEnd->end_direction = direction_reverse(directionStart); - return 0; + return false; } /** @@ -764,37 +715,31 @@ bool track_block_get_previous_from_zero( */ bool track_block_get_previous(int32_t x, int32_t y, TileElement* tileElement, track_begin_end* outTrackBeginEnd) { - ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); + auto trackElement = tileElement->AsTrack(); + if (trackElement == nullptr) + return false; - const rct_preview_track* trackBlock = get_track_def_from_ride(ride, tileElement->AsTrack()->GetTrackType()); - uint8_t sequence = tileElement->AsTrack()->GetSequenceIndex(); - trackBlock += sequence; + auto rideIndex = trackElement->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return false; - const rct_track_coordinates* trackCoordinate = get_track_coord_from_ride(ride, tileElement->AsTrack()->GetTrackType()); + auto trackBlock = get_track_def_from_ride(ride, trackElement->GetTrackType()); + if (trackBlock == nullptr) + return false; - int32_t z = tileElement->base_height * 8; + trackBlock += trackElement->GetSequenceIndex(); - uint8_t rotation = tileElement->GetDirection(); - switch (rotation) - { - case 0: - x -= trackBlock->x; - y -= trackBlock->y; - break; - case 1: - x -= trackBlock->y; - y += trackBlock->x; - break; - case 2: - x += trackBlock->x; - y += trackBlock->y; - break; - case 3: - x += trackBlock->y; - y -= trackBlock->x; - break; - } + auto trackCoordinate = get_track_coord_from_ride(ride, trackElement->GetTrackType()); + if (trackCoordinate == nullptr) + return false; + + int32_t z = trackElement->base_height * 8; + + uint8_t rotation = trackElement->GetDirection(); + CoordsXY coords = { x, y }; + CoordsXY offsets = { trackBlock->x, trackBlock->y }; + coords += offsets.Rotate(direction_reverse(rotation)); z -= trackBlock->z; z += trackCoordinate->z_begin; @@ -802,7 +747,7 @@ bool track_block_get_previous(int32_t x, int32_t y, TileElement* tileElement, tr rotation = ((trackCoordinate->rotation_begin + rotation) & TILE_ELEMENT_DIRECTION_MASK) | (trackCoordinate->rotation_begin & (1 << 2)); - return track_block_get_previous_from_zero(x, y, z, ride, rotation, outTrackBeginEnd); + return track_block_get_previous_from_zero(coords.x, coords.y, z, ride, rotation, outTrackBeginEnd); } /** @@ -815,12 +760,9 @@ bool track_block_get_previous(int32_t x, int32_t y, TileElement* tileElement, tr */ int32_t ride_find_track_gap(const Ride* ride, CoordsXYE* input, CoordsXYE* output) { - assert(input->element->GetType() == TILE_ELEMENT_TYPE_TRACK); - if (ride == nullptr) - { - log_error("Trying to access invalid ride %d", ride->id); + if (ride == nullptr || input == nullptr || input->element == nullptr + || input->element->GetType() != TILE_ELEMENT_TYPE_TRACK) return 0; - } if (ride->type == RIDE_TYPE_MAZE) { @@ -865,79 +807,67 @@ int32_t ride_find_track_gap(const Ride* ride, CoordsXYE* input, CoordsXYE* outpu return 0; } -/** - * - * rct2: 0x006AF561 - */ -void ride_get_status(const Ride* ride, rct_string_id* formatSecondary, int32_t* argument) +void Ride::FormatStatusTo(void* argsV) const { - if (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) - { - *formatSecondary = STR_CRASHED; - return; - } - if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) - { - *formatSecondary = STR_BROKEN_DOWN; - return; - } - if (ride->status == RIDE_STATUS_CLOSED) - { - *formatSecondary = STR_CLOSED; + auto args = (uint8_t*)argsV; - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) + if (lifecycle_flags & RIDE_LIFECYCLE_CRASHED) + { + set_format_arg_on(args, 0, rct_string_id, STR_CRASHED); + } + else if (lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + { + set_format_arg_on(args, 0, rct_string_id, STR_BROKEN_DOWN); + } + else if (status == RIDE_STATUS_CLOSED) + { + set_format_arg_on(args, 0, rct_string_id, STR_CLOSED); + if (!ride_type_has_flag(type, RIDE_TYPE_FLAG_IS_SHOP)) { - *argument = ride->num_riders; - - if (*argument == 1) + if (num_riders != 0) { - *formatSecondary = STR_CLOSED_WITH_PERSON; - } - else if (*argument > 1) - { - *formatSecondary = STR_CLOSED_WITH_PEOPLE; + set_format_arg_on(args, 0, rct_string_id, num_riders == 1 ? STR_CLOSED_WITH_PERSON : STR_CLOSED_WITH_PEOPLE); + set_format_arg_on(args, 2, uint16_t, num_riders); } } - - return; } - if (ride->status == RIDE_STATUS_TESTING) + else if (status == RIDE_STATUS_SIMULATING) { - *formatSecondary = STR_TEST_RUN; - return; + set_format_arg_on(args, 0, rct_string_id, STR_SIMULATING); } - if (ride->mode == RIDE_MODE_RACE && !(ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) - && ride->race_winner != SPRITE_INDEX_NULL && (GET_PEEP(ride->race_winner))->sprite_identifier == SPRITE_IDENTIFIER_PEEP) + else if (status == RIDE_STATUS_TESTING) { - Peep* peep = GET_PEEP(ride->race_winner); - if (peep->name_string_idx == STR_GUEST_X) + set_format_arg_on(args, 0, rct_string_id, STR_TEST_RUN); + } + else if ( + mode == RIDE_MODE_RACE && !(lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) + && race_winner != SPRITE_INDEX_NULL) + { + auto sprite = get_sprite(race_winner); + if (sprite != nullptr && sprite->IsPeep()) { - *argument = peep->id; - *formatSecondary = STR_RACE_WON_BY_GUEST; + auto peep = sprite->AsPeep(); + set_format_arg_on(args, 0, rct_string_id, STR_RACE_WON_BY); + peep->FormatNameTo(args + 2); } else { - *argument = peep->name_string_idx; - *formatSecondary = STR_RACE_WON_BY; + set_format_arg_on(args, 0, rct_string_id, STR_RACE_WON_BY); + set_format_arg_on(args, 2, rct_string_id, STR_NONE); } } + else if (!ride_type_has_flag(type, RIDE_TYPE_FLAG_IS_SHOP)) + { + set_format_arg_on(args, 0, rct_string_id, num_riders == 1 ? STR_PERSON_ON_RIDE : STR_PEOPLE_ON_RIDE); + set_format_arg_on(args, 2, uint16_t, num_riders); + } else { - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) - { - *argument = ride->num_riders; - *formatSecondary = STR_PERSON_ON_RIDE; - if (*argument != 1) - *formatSecondary = STR_PEOPLE_ON_RIDE; - } - else - { - *formatSecondary = STR_OPEN; - } + set_format_arg_on(args, 0, rct_string_id, STR_OPEN); } } -int32_t ride_get_total_length(Ride* ride) +int32_t ride_get_total_length(const Ride* ride) { int32_t i, totalLength = 0; for (i = 0; i < ride->num_stations; i++) @@ -953,23 +883,40 @@ int32_t ride_get_total_time(Ride* ride) return totalTime; } -int32_t ride_can_have_multiple_circuits(Ride* ride) +bool Ride::CanHaveMultipleCircuits() const { - if (!(RideData4[ride->type].flags & RIDE_TYPE_FLAG4_ALLOW_MULTIPLE_CIRCUITS)) - return 0; + if (!(RideData4[type].flags & RIDE_TYPE_FLAG4_ALLOW_MULTIPLE_CIRCUITS)) + return false; // Only allow circuit or launch modes - if (ride->mode != RIDE_MODE_CONTINUOUS_CIRCUIT && ride->mode != RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE - && ride->mode != RIDE_MODE_POWERED_LAUNCH_PASSTROUGH) + if (mode != RIDE_MODE_CONTINUOUS_CIRCUIT && mode != RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE + && mode != RIDE_MODE_POWERED_LAUNCH_PASSTROUGH) { - return 0; + return false; } // Must have no more than one vehicle and one station - if (ride->num_vehicles > 1 || ride->num_stations > 1) - return 0; + if (num_vehicles > 1 || num_stations > 1) + return false; - return 1; + return true; +} + +bool Ride::SupportsStatus(int32_t s) const +{ + switch (s) + { + case RIDE_STATUS_CLOSED: + case RIDE_STATUS_OPEN: + return true; + case RIDE_STATUS_SIMULATING: + return ( + !ride_type_has_flag(type, RIDE_TYPE_FLAG_NO_TEST_MODE) && ride_type_has_flag(type, RIDE_TYPE_FLAG_HAS_TRACK)); + case RIDE_STATUS_TESTING: + return !ride_type_has_flag(type, RIDE_TYPE_FLAG_NO_TEST_MODE); + default: + return false; + } } #pragma region Initialisation functions @@ -980,19 +927,8 @@ int32_t ride_can_have_multiple_circuits(Ride* ride) */ void ride_init_all() { - for (int32_t i = 0; i < MAX_RIDES; i++) - { - auto ride = &gRideList[i]; - *ride = {}; - ride->id = i; - ride->type = RIDE_TYPE_NULL; - } - - for (int32_t i = 0; i < MAX_RIDE_MEASUREMENTS; i++) - { - rct_ride_measurement* ride_measurement = get_ride_measurement(i); - ride_measurement->ride_index = RIDE_ID_NULL; - } + _rides.clear(); + _rides.shrink_to_fit(); } /** @@ -1001,11 +937,8 @@ void ride_init_all() */ void reset_all_ride_build_dates() { - int32_t i; - Ride* ride; - - FOR_ALL_RIDES (i, ride) - ride->build_date -= gDateMonthsElapsed; + for (auto& ride : GetRideManager()) + ride.build_date -= gDateMonthsElapsed; } #pragma endregion @@ -1014,7 +947,7 @@ void reset_all_ride_build_dates() static int32_t ride_check_if_construction_allowed(Ride* ride) { - rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride); + rct_ride_entry* rideEntry = ride->GetRideEntry(); if (rideEntry == nullptr) { context_show_error(STR_INVALID_RIDE_TYPE, STR_CANT_EDIT_INVALID_RIDE_TYPE); @@ -1022,16 +955,14 @@ static int32_t ride_check_if_construction_allowed(Ride* ride) } if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) { - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs + 6); context_show_error(STR_CANT_START_CONSTRUCTION_ON, STR_HAS_BROKEN_DOWN_AND_REQUIRES_FIXING); return 0; } - if (ride->status != RIDE_STATUS_CLOSED) + if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING) { - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs + 6); context_show_error(STR_CANT_START_CONSTRUCTION_ON, STR_MUST_BE_CLOSED_FIRST); return 0; } @@ -1125,7 +1056,7 @@ static void ride_remove_vehicles(Ride* ride) */ void ride_clear_for_construction(Ride* ride) { - ride_measurement_clear(ride); + ride->measurement = {}; ride->lifecycle_flags &= ~(RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST; @@ -1271,145 +1202,81 @@ int32_t sub_6C683D( int32_t* x, int32_t* y, int32_t* z, int32_t direction, int32_t type, uint16_t extra_params, TileElement** output_element, uint16_t flags) { - TileElement* tileElement = map_get_first_element_at(*x / 32, *y / 32); - TileElement* successTileElement = nullptr; - - do + // Find the relevant track piece, prefer sequence 0 (this ensures correct behaviour for diagonal track pieces) + auto location = CoordsXYZD{ *x, *y, *z, (Direction)direction }; + auto trackElement = map_get_track_element_at_of_type_seq(location, type, 0); + if (trackElement == nullptr) { - if (tileElement->base_height != *z / 8) - continue; - - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - - if ((tileElement->GetDirection()) != direction) - continue; - - if (type != tileElement->AsTrack()->GetTrackType()) - continue; - - successTileElement = tileElement; - if (tileElement->AsTrack()->GetSequenceIndex() == 0) - break; - } while (!(tileElement++)->IsLastForTile()); - - tileElement = successTileElement; - if (tileElement == nullptr) - return 1; + trackElement = map_get_track_element_at_of_type(location, type); + if (trackElement == nullptr) + { + return 1; + } + } // Possibly z should be & 0xF8 - const rct_preview_track* trackBlock = get_track_def_from_ride_index(tileElement->AsTrack()->GetRideIndex(), type); + auto trackBlock = get_track_def_from_ride_index(trackElement->GetRideIndex(), type); + if (trackBlock == nullptr) + return 1; - int32_t sequence = tileElement->AsTrack()->GetSequenceIndex(); - uint8_t mapDirection = tileElement->GetDirection(); + // Now find all the elements that belong to this track piece + int32_t sequence = trackElement->GetSequenceIndex(); + uint8_t mapDirection = trackElement->GetDirection(); - switch (mapDirection) - { - case TILE_ELEMENT_DIRECTION_WEST: - *x -= trackBlock[sequence].x; - *y -= trackBlock[sequence].y; - break; - case TILE_ELEMENT_DIRECTION_NORTH: - *x -= trackBlock[sequence].y; - *y += trackBlock[sequence].x; - break; - case TILE_ELEMENT_DIRECTION_EAST: - *x += trackBlock[sequence].x; - *y += trackBlock[sequence].y; - break; - case TILE_ELEMENT_DIRECTION_SOUTH: - *x += trackBlock[sequence].y; - *y -= trackBlock[sequence].x; - break; - } + CoordsXY offsets = { trackBlock[sequence].x, trackBlock[sequence].y }; + CoordsXY newCoords = { *x, *y }; + newCoords += offsets.Rotate(direction_reverse(mapDirection)); + + *x = newCoords.x; + *y = newCoords.y; *z -= trackBlock[sequence].z; int32_t start_x = *x, start_y = *y, start_z = *z; *z += trackBlock[0].z; for (int32_t i = 0; trackBlock[i].index != 0xFF; ++i) { - int32_t cur_x = start_x, cur_y = start_y, cur_z = start_z; - switch (mapDirection) - { - case TILE_ELEMENT_DIRECTION_WEST: - cur_x += trackBlock[i].x; - cur_y += trackBlock[i].y; - break; - case TILE_ELEMENT_DIRECTION_NORTH: - cur_x += trackBlock[i].y; - cur_y -= trackBlock[i].x; - break; - case TILE_ELEMENT_DIRECTION_EAST: - cur_x -= trackBlock[i].x; - cur_y -= trackBlock[i].y; - break; - case TILE_ELEMENT_DIRECTION_SOUTH: - cur_x -= trackBlock[i].y; - cur_y += trackBlock[i].x; - break; - } - cur_z += trackBlock[i].z; + CoordsXY cur = { start_x, start_y }; + offsets = { trackBlock[i].x, trackBlock[i].y }; + cur += offsets.Rotate(mapDirection); + int32_t cur_z = start_z + trackBlock[i].z; - map_invalidate_tile_full(cur_x, cur_y); + map_invalidate_tile_full(cur.x, cur.y); - tileElement = map_get_first_element_at(cur_x / 32, cur_y / 32); - successTileElement = nullptr; - do - { - if (tileElement->base_height != cur_z / 8) - continue; - - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - - if ((tileElement->GetDirection()) != direction) - continue; - - if (tileElement->AsTrack()->GetSequenceIndex() != trackBlock[i].index) - continue; - - if (type == tileElement->AsTrack()->GetTrackType()) - { - successTileElement = tileElement; - break; - } - } while (!(tileElement++)->IsLastForTile()); - - if (successTileElement == nullptr) + trackElement = map_get_track_element_at_of_type_seq( + { cur.x, cur.y, cur_z, (Direction)direction }, type, trackBlock[i].index); + if (trackElement == nullptr) { return 1; } if (i == 0 && output_element != nullptr) { - *output_element = tileElement; + *output_element = (TileElement*)trackElement; } if (flags & (1 << 0)) { - tileElement->AsTrack()->SetHighlight(false); + trackElement->SetHighlight(false); } if (flags & (1 << 1)) { - tileElement->AsTrack()->SetHighlight(true); + trackElement->SetHighlight(true); } if (flags & (1 << 2)) { - tileElement->AsTrack()->SetColourScheme((uint8_t)(extra_params & 0xFF)); + trackElement->SetColourScheme((uint8_t)(extra_params & 0xFF)); } if (flags & (1 << 5)) { - tileElement->AsTrack()->SetSeatRotation((uint8_t)(extra_params & 0xFF)); + trackElement->SetSeatRotation((uint8_t)(extra_params & 0xFF)); } - if (flags & (1 << 3)) { - tileElement->AsTrack()->SetHasCableLift(true); + trackElement->SetHasCableLift(true); } if (flags & (1 << 4)) { - tileElement->AsTrack()->SetHasCableLift(false); + trackElement->SetHasCableLift(false); } } - return 0; } @@ -1435,23 +1302,19 @@ void ride_restore_provisional_track_piece() void ride_remove_provisional_track_piece() { - if (!(_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK)) + auto rideIndex = _currentRideIndex; + auto ride = get_ride(rideIndex); + if (ride == nullptr || !(_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_TRACK)) { return; } - Ride* ride; - int32_t x, y, z, direction; - ride_id_t rideIndex = _currentRideIndex; - - x = _unkF440C5.x; - y = _unkF440C5.y; - z = _unkF440C5.z; - - ride = get_ride(rideIndex); + int32_t x = _unkF440C5.x; + int32_t y = _unkF440C5.y; + int32_t z = _unkF440C5.z; if (ride->type == RIDE_TYPE_MAZE) { - int32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 + int32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; maze_set_track(x, y, z, flags, false, 0, rideIndex, GC_SET_MAZE_TRACK_FILL); maze_set_track(x, y + 16, z, flags, false, 1, rideIndex, GC_SET_MAZE_TRACK_FILL); @@ -1460,7 +1323,7 @@ void ride_remove_provisional_track_piece() } else { - direction = _unkF440C5.direction; + int32_t direction = _unkF440C5.direction; if (!(direction & 4)) { x -= CoordsDirectionDelta[direction].x; @@ -1474,7 +1337,8 @@ void ride_remove_provisional_track_piece() auto trackRemoveAction = TrackRemoveAction{ trackType, trackSequence, { next_track.x, next_track.y, z, static_cast(direction) } }; - trackRemoveAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_GHOST); + trackRemoveAction.SetFlags( + GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); GameActions::Execute(&trackRemoveAction); } } @@ -1540,9 +1404,10 @@ void ride_construction_invalidate_current_track() */ static void ride_construction_reset_current_piece() { - Ride* ride; + auto ride = get_ride(_currentRideIndex); + if (ride == nullptr) + return; - ride = get_ride(_currentRideIndex); if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_NO_TRACK) || ride->num_stations == 0) { _currentTrackCurve = RideConstructionDefaultTrackType[ride->type] | 0x100; @@ -1570,20 +1435,19 @@ static void ride_construction_reset_current_piece() */ void ride_construction_set_default_next_piece() { - ride_id_t rideIndex; + auto rideIndex = _currentRideIndex; + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + int32_t x, y, z, direction, trackType, curve, bank, slope; - Ride* ride; track_begin_end trackBeginEnd; CoordsXYE xyElement; TileElement* tileElement; - _currentTrackPrice = MONEY32_UNDEFINED; switch (_rideConstructionState) { case RIDE_CONSTRUCTION_STATE_FRONT: - rideIndex = _currentRideIndex; - ride = get_ride(rideIndex); - x = _currentTrackBegin.x; y = _currentTrackBegin.y; z = _currentTrackBegin.z; @@ -1654,9 +1518,6 @@ void ride_construction_set_default_next_piece() && slope != TRACK_SLOPE_DOWN_60; break; case RIDE_CONSTRUCTION_STATE_BACK: - rideIndex = _currentRideIndex; - ride = get_ride(rideIndex); - x = _currentTrackBegin.x; y = _currentTrackBegin.y; z = _currentTrackBegin.z; @@ -1867,30 +1728,36 @@ void ride_select_previous_section() * * rct2: 0x006CC2CA */ -static int32_t ride_modify_entrance_or_exit(TileElement* tileElement, int32_t x, int32_t y) +static bool ride_modify_entrance_or_exit(const CoordsXYE& tileElement) { - int32_t entranceType; - rct_window* constructionWindow; + if (tileElement.element == nullptr) + return false; - ride_id_t rideIndex = tileElement->AsEntrance()->GetRideIndex(); + auto entranceElement = tileElement.element->AsEntrance(); + if (entranceElement == nullptr) + return false; + + auto rideIndex = entranceElement->GetRideIndex(); auto ride = get_ride(rideIndex); + if (ride == nullptr) + return false; - entranceType = tileElement->AsEntrance()->GetEntranceType(); + auto entranceType = entranceElement->GetEntranceType(); if (entranceType != ENTRANCE_TYPE_RIDE_ENTRANCE && entranceType != ENTRANCE_TYPE_RIDE_EXIT) - return 0; + return false; - int32_t stationIndex = tileElement->AsEntrance()->GetStationIndex(); + int32_t stationIndex = entranceElement->GetStationIndex(); // Get or create construction window for ride - constructionWindow = window_find_by_class(WC_RIDE_CONSTRUCTION); + auto constructionWindow = window_find_by_class(WC_RIDE_CONSTRUCTION); if (constructionWindow == nullptr) { if (!ride_initialise_construction_window(ride)) - return 0; + return false; constructionWindow = window_find_by_class(WC_RIDE_CONSTRUCTION); if (constructionWindow == nullptr) - return 0; + return false; } ride_construction_invalidate_current_track(); @@ -1919,7 +1786,7 @@ static int32_t ride_modify_entrance_or_exit(TileElement* tileElement, int32_t x, { // Remove entrance / exit auto rideEntranceExitRemove = RideEntranceExitRemoveAction( - { x, y }, rideIndex, stationIndex, entranceType == ENTRANCE_TYPE_RIDE_EXIT); + { tileElement.x, tileElement.y }, rideIndex, stationIndex, entranceType == ENTRANCE_TYPE_RIDE_EXIT); rideEntranceExitRemove.SetCallback([=](const GameAction* ga, const GameActionResult* result) { gCurrentToolWidget.widget_index = entranceType == ENTRANCE_TYPE_RIDE_ENTRANCE ? WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE @@ -1932,87 +1799,100 @@ static int32_t ride_modify_entrance_or_exit(TileElement* tileElement, int32_t x, } window_invalidate_by_class(WC_RIDE_CONSTRUCTION); - return 1; + return true; } /** * * rct2: 0x006CC287 */ -static int32_t ride_modify_maze(TileElement* tileElement, int32_t x, int32_t y) +static bool ride_modify_maze(const CoordsXYE& tileElement) { - _currentRideIndex = tileElement->AsTrack()->GetRideIndex(); - _rideConstructionState = RIDE_CONSTRUCTION_STATE_MAZE_BUILD; - _currentTrackBegin.x = x; - _currentTrackBegin.y = y; - _currentTrackBegin.z = tileElement->base_height * 8; - _currentTrackSelectionFlags = 0; - _rideConstructionArrowPulseTime = 0; + if (tileElement.element != nullptr) + { + auto trackElement = tileElement.element->AsTrack(); + if (trackElement != nullptr) + { + _currentRideIndex = trackElement->GetRideIndex(); + _rideConstructionState = RIDE_CONSTRUCTION_STATE_MAZE_BUILD; + _currentTrackBegin.x = tileElement.x; + _currentTrackBegin.y = tileElement.y; + _currentTrackBegin.z = trackElement->base_height * 8; + _currentTrackSelectionFlags = 0; + _rideConstructionArrowPulseTime = 0; - auto intent = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION); - context_broadcast_intent(&intent); - - return 1; + auto intent = Intent(INTENT_ACTION_UPDATE_MAZE_CONSTRUCTION); + context_broadcast_intent(&intent); + return true; + } + } + return false; } /** * * rct2: 0x006CC056 */ -int32_t ride_modify(CoordsXYE* input) +bool ride_modify(CoordsXYE* input) { - int32_t x, y, z, direction, type; - CoordsXYE tileElement, endOfTrackElement; - Ride* ride; - rct_ride_entry* rideEntry; + auto tileElement = *input; + if (tileElement.element == nullptr) + return false; - tileElement = *input; - ride_id_t rideIndex = tile_element_get_ride_index(tileElement.element); - ride = get_ride(rideIndex); + auto rideIndex = tile_element_get_ride_index(tileElement.element); + auto ride = get_ride(rideIndex); if (ride == nullptr) { - return 0; + return false; } - rideEntry = get_ride_entry_by_ride(ride); - if ((rideEntry == nullptr) || !ride_check_if_construction_allowed(ride)) - return 0; + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr || !ride_check_if_construction_allowed(ride)) + return false; if (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE) { - set_format_arg(6, rct_string_id, ride->name); - set_format_arg(8, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs + 6); context_show_error( STR_CANT_START_CONSTRUCTION_ON, STR_LOCAL_AUTHORITY_FORBIDS_DEMOLITION_OR_MODIFICATIONS_TO_THIS_RIDE); - return 0; + return false; } // Stop the ride again to clear all vehicles and peeps (compatible with network games) - ride_set_status(ride, RIDE_STATUS_CLOSED); + if (ride->status != RIDE_STATUS_SIMULATING) + { + ride_set_status(ride, RIDE_STATUS_CLOSED); + } // Check if element is a station entrance or exit if (tileElement.element->GetType() == TILE_ELEMENT_TYPE_ENTRANCE) - return ride_modify_entrance_or_exit(tileElement.element, tileElement.x, tileElement.y); + return ride_modify_entrance_or_exit(tileElement); ride_create_or_find_construction_window(rideIndex); if (ride->type == RIDE_TYPE_MAZE) - return ride_modify_maze(tileElement.element, tileElement.x, tileElement.y); + { + return ride_modify_maze(tileElement); + } if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_CANNOT_HAVE_GAPS)) { + CoordsXYE endOfTrackElement{}; if (ride_find_track_gap(ride, &tileElement, &endOfTrackElement)) tileElement = endOfTrackElement; } - x = tileElement.x; - y = tileElement.y; - z = tileElement.element->base_height * 8; - direction = tileElement.element->GetDirection(); - type = tileElement.element->AsTrack()->GetTrackType(); + if (tileElement.element == nullptr || tileElement.element->GetType() != TILE_ELEMENT_TYPE_TRACK) + return false; + + int32_t x = tileElement.x; + int32_t y = tileElement.y; + int32_t z = tileElement.element->base_height * 8; + int32_t direction = tileElement.element->GetDirection(); + int32_t type = tileElement.element->AsTrack()->GetTrackType(); if (sub_6C683D(&x, &y, &z, direction, type, 0, nullptr, 0)) - return 0; + return false; _currentRideIndex = rideIndex; _rideConstructionState = RIDE_CONSTRUCTION_STATE_SELECTED; @@ -2027,14 +1907,14 @@ int32_t ride_modify(CoordsXYE* input) if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_NO_TRACK)) { window_ride_construction_update_active_elements(); - return 1; + return true; } ride_select_next_section(); if (_rideConstructionState == RIDE_CONSTRUCTION_STATE_FRONT) { window_ride_construction_update_active_elements(); - return 1; + return true; } _rideConstructionState = RIDE_CONSTRUCTION_STATE_SELECTED; @@ -2061,7 +1941,7 @@ int32_t ride_modify(CoordsXYE* input) } window_ride_construction_update_active_elements(); - return 1; + return true; } /** @@ -2086,6 +1966,8 @@ int32_t ride_initialise_construction_window(Ride* ride) input_set_flag(INPUT_FLAG_6, true); ride = get_ride(_currentRideIndex); + if (ride == nullptr) + return 0; _currentTrackCurve = RideConstructionDefaultTrackType[ride->type] | 0x100; _currentTrackSlopeEnd = 0; @@ -2116,103 +1998,111 @@ int32_t ride_initialise_construction_window(Ride* ride) * * rct2: 0x006ABE4C */ -void ride_update_all() +void Ride::UpdateAll() { - Ride* ride; - int32_t i; - // Remove all rides if scenario editor if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) { if (gS6Info.editor_step <= EDITOR_STEP_INVENTIONS_LIST_SET_UP) - FOR_ALL_RIDES (i, ride) - ride->type = RIDE_TYPE_NULL; + for (auto& ride : GetRideManager()) + ride.Delete(); return; } window_update_viewport_ride_music(); // Update rides - FOR_ALL_RIDES (i, ride) - ride_update(ride); + for (auto& ride : GetRideManager()) + ride.Update(); ride_music_update_final(); } +std::unique_ptr Ride::SaveToTrackDesign() const +{ + if (!(lifecycle_flags & RIDE_LIFECYCLE_TESTED)) + { + context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE); + return nullptr; + } + + if (!ride_has_ratings(this)) + { + context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE); + return nullptr; + } + + auto td = std::make_unique(); + auto errMessage = td->CreateTrackDesign(*this); + if (errMessage != STR_NONE) + { + context_show_error(STR_CANT_SAVE_TRACK_DESIGN, errMessage); + return nullptr; + } + + return td; +} + /** * * rct2: 0x006ABE73 */ -static void ride_update(Ride* ride) +void Ride::Update() { - if (ride->vehicle_change_timeout != 0) - ride->vehicle_change_timeout--; + if (vehicle_change_timeout != 0) + vehicle_change_timeout--; - ride_music_update(ride); + ride_music_update(this); // Update stations - if (ride->type != RIDE_TYPE_MAZE) + if (type != RIDE_TYPE_MAZE) for (int32_t i = 0; i < MAX_STATIONS; i++) - ride_update_station(ride, i); + ride_update_station(this, i); // Update financial statistics - ride->num_customers_timeout++; + num_customers_timeout++; - if (ride->num_customers_timeout >= 960) + if (num_customers_timeout >= 960) { // This is meant to update about every 30 seconds - ride->num_customers_timeout = 0; + num_customers_timeout = 0; // Shift number of customers history, start of the array is the most recent one for (int32_t i = CUSTOMER_HISTORY_SIZE - 1; i > 0; i--) { - ride->num_customers[i] = ride->num_customers[i - 1]; + num_customers[i] = num_customers[i - 1]; } - ride->num_customers[0] = ride->cur_num_customers; + num_customers[0] = cur_num_customers; - ride->cur_num_customers = 0; - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER; + cur_num_customers = 0; + window_invalidate_flags |= RIDE_INVALIDATE_RIDE_CUSTOMER; - ride->income_per_hour = ride_calculate_income_per_hour(ride); - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; + income_per_hour = CalculateIncomePerHour(); + window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - if (ride->upkeep_cost != MONEY16_UNDEFINED) - ride->profit = (ride->income_per_hour - ((money32)ride->upkeep_cost * 16)); + if (upkeep_cost != MONEY16_UNDEFINED) + profit = (income_per_hour - ((money32)upkeep_cost * 16)); } // Ride specific updates - if (ride->type == RIDE_TYPE_CHAIRLIFT) - ride_chairlift_update(ride); - else if (ride->type == RIDE_TYPE_SPIRAL_SLIDE) - ride_spiral_slide_update(ride); + if (type == RIDE_TYPE_CHAIRLIFT) + UpdateChairlift(); + else if (type == RIDE_TYPE_SPIRAL_SLIDE) + UpdateSpiralSlide(); - ride_breakdown_update(ride); + ride_breakdown_update(this); // Various things include news messages - if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION)) - if (((gCurrentTicks >> 1) & 255) == (uint32_t)ride->id) - ride_breakdown_status_update(ride); + if (lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION)) + if (((gCurrentTicks >> 1) & 255) == (uint32_t)id) + ride_breakdown_status_update(this); - ride_inspection_update(ride); + ride_inspection_update(this); - if (ride->status == RIDE_STATUS_TESTING && gConfigGeneral.no_test_crashes) + // If ride is simulating but crashed, reset the vehicles + if (status == RIDE_STATUS_SIMULATING && (lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { - for (int32_t i = 0; i < ride->num_vehicles; i++) - { - uint16_t spriteIndex = ride->vehicles[i]; - if (spriteIndex == SPRITE_INDEX_NULL) - continue; - - rct_vehicle* vehicle = GET_VEHICLE(spriteIndex); - - if (vehicle->status == VEHICLE_STATUS_CRASHED || vehicle->status == VEHICLE_STATUS_CRASHING) - { - ride_set_status(ride, RIDE_STATUS_CLOSED); - ride_set_status(ride, RIDE_STATUS_CLOSED); - ride_set_status(ride, RIDE_STATUS_TESTING); - break; - } - } + ride_set_status(this, RIDE_STATUS_SIMULATING); } } @@ -2220,29 +2110,29 @@ static void ride_update(Ride* ride) * * rct2: 0x006AC489 */ -static void ride_chairlift_update(Ride* ride) +void Ride::UpdateChairlift() { int32_t x, y, z; - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) + if (!(lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) return; - if ((ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) - && ride->breakdown_reason_pending == 0) + if ((lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) + && breakdown_reason_pending == 0) return; - uint16_t old_chairlift_bullwheel_rotation = ride->chairlift_bullwheel_rotation >> 14; - ride->chairlift_bullwheel_rotation += ride->speed * 2048; - if (old_chairlift_bullwheel_rotation == ride->speed / 8) + uint16_t old_chairlift_bullwheel_rotation = chairlift_bullwheel_rotation >> 14; + chairlift_bullwheel_rotation += speed * 2048; + if (old_chairlift_bullwheel_rotation == speed / 8) return; - x = ride->chairlift_bullwheel_location[0].x * 32; - y = ride->chairlift_bullwheel_location[0].y * 32; - z = ride->chairlift_bullwheel_z[0] * 8; + x = chairlift_bullwheel_location[0].x * 32; + y = chairlift_bullwheel_location[0].y * 32; + z = chairlift_bullwheel_z[0] * 8; map_invalidate_tile_zoom1(x, y, z, z + (4 * 8)); - x = ride->chairlift_bullwheel_location[1].x * 32; - y = ride->chairlift_bullwheel_location[1].y * 32; - z = ride->chairlift_bullwheel_z[1] * 8; + x = chairlift_bullwheel_location[1].x * 32; + y = chairlift_bullwheel_location[1].y * 32; + z = chairlift_bullwheel_z[1] * 8; map_invalidate_tile_zoom1(x, y, z, z + (4 * 8)); } @@ -2318,19 +2208,19 @@ static constexpr const LocationXY16 ride_spiral_slide_main_tile_offset[][4] = { * * rct2: 0x006AC545 */ -static void ride_spiral_slide_update(Ride* ride) +void Ride::UpdateSpiralSlide() { if (gCurrentTicks & 3) return; - if (ride->slide_in_use == 0) + if (slide_in_use == 0) return; - ride->spiral_slide_progress++; - if (ride->spiral_slide_progress >= 48) + spiral_slide_progress++; + if (spiral_slide_progress >= 48) { - ride->slide_in_use--; + slide_in_use--; - Peep* peep = GET_PEEP(ride->slide_peep); + Peep* peep = GET_PEEP(slide_peep); peep->destination_x++; } @@ -2338,13 +2228,13 @@ static void ride_spiral_slide_update(Ride* ride) // Invalidate something related to station start for (int32_t i = 0; i < MAX_STATIONS; i++) { - if (ride->stations[i].Start.xy == RCT_XY8_UNDEFINED) + if (stations[i].Start.xy == RCT_XY8_UNDEFINED) continue; - int32_t x = ride->stations[i].Start.x; - int32_t y = ride->stations[i].Start.y; + int32_t x = stations[i].Start.x; + int32_t y = stations[i].Start.y; - TileElement* tileElement = ride_get_station_start_track_element(ride, i); + TileElement* tileElement = ride_get_station_start_track_element(this, i); if (tileElement == nullptr) continue; @@ -2475,7 +2365,7 @@ static void ride_breakdown_update(Ride* ride) if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) return; - if (ride->status == RIDE_STATUS_CLOSED) + if (ride->status == RIDE_STATUS_CLOSED || ride->status == RIDE_STATUS_SIMULATING) return; if (!ride->CanBreakDown()) @@ -2572,13 +2462,8 @@ bool Ride::CanBreakDown() const return false; } - rct_ride_entry* entry = get_ride_entry_by_ride(this); - if (entry == nullptr || entry->flags & RIDE_ENTRY_FLAG_CANNOT_BREAK_DOWN) - { - return false; - } - - return true; + rct_ride_entry* entry = GetRideEntry(); + return entry != nullptr && !(entry->flags & RIDE_ENTRY_FLAG_CANNOT_BREAK_DOWN); } static void choose_random_train_to_breakdown_safe(Ride* ride) @@ -2691,8 +2576,7 @@ void ride_prepare_breakdown(Ride* ride, int32_t breakdownReason) */ void ride_breakdown_add_news_item(Ride* ride) { - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs); if (gConfigNotifications.ride_broken_down) { news_item_add_to_queue(NEWS_ITEM_RIDE, STR_RIDE_IS_BROKEN_DOWN, ride->id); @@ -2718,8 +2602,7 @@ static void ride_breakdown_status_update(Ride* ride) if (!(ride->not_fixed_timeout & 15) && ride->mechanic_status != RIDE_MECHANIC_STATUS_FIXING && ride->mechanic_status != RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES) { - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs); if (gConfigNotifications.ride_warnings) { news_item_add_to_queue(NEWS_ITEM_RIDE, STR_RIDE_IS_STILL_NOT_FIXED, ride->id); @@ -3037,53 +2920,36 @@ static void ride_music_update(Ride* ride) #pragma region Measurement functions -/** - * - * rct2: 0x006B642B - */ -void ride_measurement_clear(Ride* ride) -{ - rct_ride_measurement* measurement; - - if (ride->measurement_index == 255) - return; - - measurement = get_ride_measurement(ride->measurement_index); - measurement->ride_index = RIDE_ID_NULL; - ride->measurement_index = 255; -} - /** * * rct2: 0x006B64F2 */ -static void ride_measurement_update(rct_ride_measurement* measurement) +static void ride_measurement_update(Ride& ride, RideMeasurement& measurement) { - uint16_t spriteIndex; - Ride* ride; - rct_vehicle* vehicle; - int32_t velocity, altitude, verticalG, lateralG; + if (measurement.vehicle_index >= std::size(ride.vehicles)) + return; - ride = get_ride(measurement->ride_index); - spriteIndex = ride->vehicles[measurement->vehicle_index]; + auto spriteIndex = ride.vehicles[measurement.vehicle_index]; if (spriteIndex == SPRITE_INDEX_NULL) return; - vehicle = GET_VEHICLE(spriteIndex); + auto vehicle = GET_VEHICLE(spriteIndex); + if (vehicle == nullptr) + return; - if (measurement->flags & RIDE_MEASUREMENT_FLAG_UNLOADING) + if (measurement.flags & RIDE_MEASUREMENT_FLAG_UNLOADING) { if (vehicle->status != VEHICLE_STATUS_DEPARTING && vehicle->status != VEHICLE_STATUS_TRAVELLING_CABLE_LIFT) return; - measurement->flags &= ~RIDE_MEASUREMENT_FLAG_UNLOADING; - if (measurement->current_station == vehicle->current_station) - measurement->current_item = 0; + measurement.flags &= ~RIDE_MEASUREMENT_FLAG_UNLOADING; + if (measurement.current_station == vehicle->current_station) + measurement.current_item = 0; } if (vehicle->status == VEHICLE_STATUS_UNLOADING_PASSENGERS) { - measurement->flags |= RIDE_MEASUREMENT_FLAG_UNLOADING; + measurement.flags |= RIDE_MEASUREMENT_FLAG_UNLOADING; return; } @@ -3094,41 +2960,41 @@ static void ride_measurement_update(rct_ride_measurement* measurement) if (vehicle->velocity == 0) return; - if (measurement->current_item >= RIDE_MEASUREMENT_MAX_ITEMS) + if (measurement.current_item >= RideMeasurement::MAX_ITEMS) return; - if (measurement->flags & RIDE_MEASUREMENT_FLAG_G_FORCES) + if (measurement.flags & RIDE_MEASUREMENT_FLAG_G_FORCES) { - vehicle_get_g_forces(vehicle, &verticalG, &lateralG); - verticalG = std::clamp(verticalG / 8, -127, 127); - lateralG = std::clamp(lateralG / 8, -127, 127); + auto gForces = vehicle_get_g_forces(vehicle); + gForces.VerticalG = std::clamp(gForces.VerticalG / 8, -127, 127); + gForces.LateralG = std::clamp(gForces.LateralG / 8, -127, 127); if (gScenarioTicks & 1) { - verticalG = (verticalG + measurement->vertical[measurement->current_item]) / 2; - lateralG = (lateralG + measurement->lateral[measurement->current_item]) / 2; + gForces.VerticalG = (gForces.VerticalG + measurement.vertical[measurement.current_item]) / 2; + gForces.LateralG = (gForces.LateralG + measurement.lateral[measurement.current_item]) / 2; } - measurement->vertical[measurement->current_item] = verticalG & 0xFF; - measurement->lateral[measurement->current_item] = lateralG & 0xFF; + measurement.vertical[measurement.current_item] = gForces.VerticalG & 0xFF; + measurement.lateral[measurement.current_item] = gForces.LateralG & 0xFF; } - velocity = std::min(std::abs((vehicle->velocity * 5) >> 16), 255); - altitude = std::min(vehicle->z / 8, 255); + auto velocity = std::min(std::abs((vehicle->velocity * 5) >> 16), 255); + auto altitude = std::min(vehicle->z / 8, 255); if (gScenarioTicks & 1) { - velocity = (velocity + measurement->velocity[measurement->current_item]) / 2; - altitude = (altitude + measurement->altitude[measurement->current_item]) / 2; + velocity = (velocity + measurement.velocity[measurement.current_item]) / 2; + altitude = (altitude + measurement.altitude[measurement.current_item]) / 2; } - measurement->velocity[measurement->current_item] = velocity & 0xFF; - measurement->altitude[measurement->current_item] = altitude & 0xFF; + measurement.velocity[measurement.current_item] = velocity & 0xFF; + measurement.altitude[measurement.current_item] = altitude & 0xFF; if (gScenarioTicks & 1) { - measurement->current_item++; - measurement->num_items = std::max(measurement->num_items, measurement->current_item); + measurement.current_item++; + measurement.num_items = std::max(measurement.num_items, measurement.current_item); } } @@ -3142,136 +3008,101 @@ void ride_measurements_update() return; // For each ride measurement - for (int32_t i = 0; i < MAX_RIDE_MEASUREMENTS; i++) + for (auto& ride : GetRideManager()) { - rct_ride_measurement* measurement = get_ride_measurement(i); - if (measurement->ride_index == RIDE_ID_NULL) - continue; - - Ride* ride = get_ride(measurement->ride_index); - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) - continue; - - if (measurement->flags & RIDE_MEASUREMENT_FLAG_RUNNING) + auto measurement = ride.measurement.get(); + if (measurement != nullptr && (ride.lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK) && ride.status != RIDE_STATUS_SIMULATING) { - ride_measurement_update(measurement); - } - else - { - // For each vehicle - for (int32_t j = 0; j < ride->num_vehicles; j++) + if (measurement->flags & RIDE_MEASUREMENT_FLAG_RUNNING) { - uint16_t vehicleSpriteIdx = ride->vehicles[j]; - if (vehicleSpriteIdx == SPRITE_INDEX_NULL) - continue; - - rct_vehicle* vehicle = GET_VEHICLE(vehicleSpriteIdx); - if (vehicle->status == VEHICLE_STATUS_DEPARTING || vehicle->status == VEHICLE_STATUS_TRAVELLING_CABLE_LIFT) + ride_measurement_update(ride, *measurement); + } + else + { + // For each vehicle + for (int32_t j = 0; j < ride.num_vehicles; j++) { - measurement->vehicle_index = j; - measurement->current_station = vehicle->current_station; - measurement->flags |= RIDE_MEASUREMENT_FLAG_RUNNING; - measurement->flags &= ~RIDE_MEASUREMENT_FLAG_UNLOADING; - ride_measurement_update(measurement); - break; + uint16_t vehicleSpriteIdx = ride.vehicles[j]; + if (vehicleSpriteIdx != SPRITE_INDEX_NULL) + { + auto vehicle = GET_VEHICLE(vehicleSpriteIdx); + if (vehicle->status == VEHICLE_STATUS_DEPARTING + || vehicle->status == VEHICLE_STATUS_TRAVELLING_CABLE_LIFT) + { + measurement->vehicle_index = j; + measurement->current_station = vehicle->current_station; + measurement->flags |= RIDE_MEASUREMENT_FLAG_RUNNING; + measurement->flags &= ~RIDE_MEASUREMENT_FLAG_UNLOADING; + ride_measurement_update(ride, *measurement); + break; + } + } } } } } } -static rct_ride_measurement* ride_get_existing_measurement(ride_id_t rideIndex) -{ - for (int32_t i = 0; i < MAX_RIDE_MEASUREMENTS; i++) - { - rct_ride_measurement* measurement = get_ride_measurement(i); - if (measurement->ride_index == rideIndex) - return measurement; - } - - return nullptr; -} - -static int32_t ride_get_free_measurement() -{ - for (int32_t i = 0; i < MAX_RIDE_MEASUREMENTS; i++) - { - rct_ride_measurement* measurement = get_ride_measurement(i); - if (measurement->ride_index == RIDE_ID_NULL) - return i; - } - - return -1; -} - /** - * - * rct2: 0x006B66D9 + * If there are more than the threshold of allowed ride measurements, free the non-LRU one. */ -rct_ride_measurement* ride_get_measurement(Ride* ride, rct_string_id* message) +static void ride_free_old_measurements() +{ + size_t numRideMeasurements; + do + { + Ride* lruRide{}; + numRideMeasurements = 0; + for (auto& ride : GetRideManager()) + { + if (ride.measurement != nullptr) + { + if (lruRide == nullptr || ride.measurement->last_use_tick > lruRide->measurement->last_use_tick) + { + lruRide = &ride; + } + numRideMeasurements++; + } + } + if (numRideMeasurements > MAX_RIDE_MEASUREMENTS && lruRide != nullptr) + { + lruRide->measurement = {}; + numRideMeasurements--; + } + } while (numRideMeasurements > MAX_RIDE_MEASUREMENTS); +} + +std::pair ride_get_measurement(Ride* ride) { // Check if ride type supports data logging if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_DATA_LOGGING)) { - if (message != nullptr) - *message = STR_DATA_LOGGING_NOT_AVAILABLE_FOR_THIS_TYPE_OF_RIDE; - return nullptr; + return { nullptr, STR_DATA_LOGGING_NOT_AVAILABLE_FOR_THIS_TYPE_OF_RIDE }; } // Check if a measurement already exists for this ride - rct_ride_measurement* measurement = ride_get_existing_measurement(ride->id); + auto& measurement = ride->measurement; if (measurement == nullptr) { - // Find a free measurement - int32_t i = ride_get_free_measurement(); - if (i == -1) - { - // Use last recently used measurement for some other ride - int32_t lruIndex = 0; - uint32_t lruTicks = 0xFFFFFFFF; - for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) - { - measurement = get_ride_measurement(i); - - if (measurement->last_use_tick <= lruTicks) - { - lruTicks = measurement->last_use_tick; - lruIndex = i; - } - } - - i = lruIndex; - measurement = get_ride_measurement(i); - get_ride(measurement->ride_index)->measurement_index = 255; - } - else - { - measurement = get_ride_measurement(i); - } - - measurement->ride_index = ride->id; - ride->measurement_index = i; - measurement->flags = 0; + measurement = std::make_unique(); if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) + { measurement->flags |= RIDE_MEASUREMENT_FLAG_G_FORCES; - measurement->num_items = 0; - measurement->current_item = 0; + } + ride_free_old_measurements(); + assert(ride->measurement != nullptr); } measurement->last_use_tick = gScenarioTicks; if (measurement->flags & 1) { - if (message != nullptr) - *message = STR_EMPTY; - return measurement; + return { measurement.get(), STR_EMPTY }; } else { set_format_arg(0, rct_string_id, RideComponentNames[RideNameConvention[ride->type].vehicle].singular); set_format_arg(2, rct_string_id, RideComponentNames[RideNameConvention[ride->type].station].singular); - if (message != nullptr) - *message = STR_DATA_LOGGING_WILL_START_WHEN_NEXT_LEAVES; - return nullptr; + return { measurement.get(), STR_DATA_LOGGING_WILL_START_WHEN_NEXT_LEAVES }; } } @@ -3306,13 +3137,11 @@ vehicle_colour ride_get_vehicle_colour(Ride* ride, int32_t vehicleIndex) static bool ride_does_vehicle_colour_exist(uint8_t ride_sub_type, vehicle_colour* vehicleColour) { - int32_t i; - Ride* ride2; - FOR_ALL_RIDES (i, ride2) + for (auto& ride : GetRideManager()) { - if (ride2->subtype != ride_sub_type) + if (ride.subtype != ride_sub_type) continue; - if (ride2->vehicle_colours[0].Body != vehicleColour->main) + if (ride.vehicle_colours[0].Body != vehicleColour->main) continue; return false; } @@ -3393,48 +3222,34 @@ void ride_set_vehicle_colours_to_random_preset(Ride* ride, uint8_t preset_index) */ void ride_check_all_reachable() { - Ride* ride; - int32_t i; - - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (ride->connected_message_throttle != 0) - ride->connected_message_throttle--; - if (ride->status != RIDE_STATUS_OPEN || ride->connected_message_throttle != 0) + if (ride.connected_message_throttle != 0) + ride.connected_message_throttle--; + if (ride.status != RIDE_STATUS_OPEN || ride.connected_message_throttle != 0) continue; - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) - ride_shop_connected(ride); + if (ride_type_has_flag(ride.type, RIDE_TYPE_FLAG_IS_SHOP)) + ride_shop_connected(&ride); else - ride_entrance_exit_connected(ride); + ride_entrance_exit_connected(&ride); } } /** * * rct2: 0x006B7C59 - * @return 1 if the coordinate is reachable or has no entrance, 0 otherwise + * @return true if the coordinate is reachable or has no entrance, false otherwise */ -static int32_t ride_entrance_exit_is_reachable(TileCoordsXYZD coordinates) +static bool ride_entrance_exit_is_reachable(TileCoordsXYZD coordinates) { - int32_t x, y, z; - if (coordinates.isNull()) - return 1; + return true; - x = coordinates.x; - y = coordinates.y; - z = coordinates.z; - uint8_t face_direction = coordinates.direction; + TileCoordsXYZ loc{ coordinates.x, coordinates.y, coordinates.z }; + loc -= TileDirectionDelta[coordinates.direction]; - x *= 32; - y *= 32; - x -= CoordsDirectionDelta[face_direction].x; - y -= CoordsDirectionDelta[face_direction].y; - x /= 32; - y /= 32; - - return map_coord_is_connected(x, y, z, face_direction); + return map_coord_is_connected(loc, coordinates.direction); } static void ride_entrance_exit_connected(Ride* ride) @@ -3450,8 +3265,7 @@ static void ride_entrance_exit_connected(Ride* ride) if (!entrance.isNull() && !ride_entrance_exit_is_reachable(entrance)) { // name of ride is parameter of the format string - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs); if (gConfigNotifications.ride_warnings) { news_item_add_to_queue(1, STR_ENTRANCE_NOT_CONNECTED, ride->id); @@ -3462,8 +3276,7 @@ static void ride_entrance_exit_connected(Ride* ride) if (!exit.isNull() && !ride_entrance_exit_is_reachable(exit)) { // name of ride is parameter of the format string - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs); if (gConfigNotifications.ride_warnings) { news_item_add_to_queue(1, STR_EXIT_NOT_CONNECTED, ride->id); @@ -3475,17 +3288,14 @@ static void ride_entrance_exit_connected(Ride* ride) static void ride_shop_connected(Ride* ride) { - int32_t x, y, count; - LocationXY8 coordinates = ride->stations[0].Start; if (coordinates.xy == RCT_XY8_UNDEFINED) return; - x = coordinates.x; - y = coordinates.y; + TileCoordsXY loc = { coordinates.x, coordinates.y }; TrackElement* trackElement = nullptr; - TileElement* tileElement = map_get_first_element_at(x, y); + TileElement* tileElement = map_get_first_element_at(loc.x, loc.y); do { if (tileElement == nullptr) @@ -3500,7 +3310,7 @@ static void ride_shop_connected(Ride* ride) if (trackElement == nullptr) return; - uint16_t entrance_directions = 0; + uint8_t entrance_directions = 0; uint8_t track_type = trackElement->GetTrackType(); ride = get_ride(trackElement->GetRideIndex()); if (ride == nullptr) @@ -3509,26 +3319,21 @@ static void ride_shop_connected(Ride* ride) } if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) { - entrance_directions = FlatRideTrackSequenceProperties[track_type][0]; + entrance_directions = FlatRideTrackSequenceProperties[track_type][0] & 0xF; } else { - entrance_directions = TrackSequenceProperties[track_type][0]; + entrance_directions = TrackSequenceProperties[track_type][0] & 0xF; } uint8_t tile_direction = trackElement->GetDirection(); - entrance_directions <<= tile_direction; - entrance_directions = ((entrance_directions >> 12) | entrance_directions) & 0xF; + entrance_directions = rol4(entrance_directions, tile_direction); // Now each bit in entrance_directions stands for an entrance direction to check if (entrance_directions == 0) return; - // Turn x, y from tiles into units - x *= 32; - y *= 32; - - for (count = 0; entrance_directions != 0; count++) + for (auto count = 0; entrance_directions != 0; count++) { if (!(entrance_directions & 1)) { @@ -3540,16 +3345,15 @@ static void ride_shop_connected(Ride* ride) // Flip direction north<->south, east<->west uint8_t face_direction = direction_reverse(count); - int32_t y2 = y - CoordsDirectionDelta[face_direction].y; - int32_t x2 = x - CoordsDirectionDelta[face_direction].x; + int32_t y2 = loc.y - TileDirectionDelta[face_direction].y; + int32_t x2 = loc.x - TileDirectionDelta[face_direction].x; - if (map_coord_is_connected(x2 / 32, y2 / 32, tileElement->base_height, face_direction)) + if (map_coord_is_connected({ x2, y2, tileElement->base_height }, face_direction)) return; } // Name of ride is parameter of the format string - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs); if (gConfigNotifications.ride_warnings) { news_item_add_to_queue(1, STR_ENTRANCE_NOT_CONNECTED, ride->id); @@ -3564,108 +3368,120 @@ static void ride_shop_connected(Ride* ride) static void ride_track_set_map_tooltip(TileElement* tileElement) { - ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); + auto rideIndex = tileElement->AsTrack()->GetRideIndex(); auto ride = get_ride(rideIndex); - - set_map_tooltip_format_arg(0, rct_string_id, STR_RIDE_MAP_TIP); - set_map_tooltip_format_arg(2, rct_string_id, ride->name); - set_map_tooltip_format_arg(4, uint32_t, ride->name_arguments); - - rct_string_id formatSecondary; - int32_t arg1 = 0; - ride_get_status(ride, &formatSecondary, &arg1); - set_map_tooltip_format_arg(8, rct_string_id, formatSecondary); - set_map_tooltip_format_arg(10, uint32_t, arg1); + if (ride != nullptr) + { + set_map_tooltip_format_arg(0, rct_string_id, STR_RIDE_MAP_TIP); + auto nameArgLen = ride->FormatNameTo(gMapTooltipFormatArgs + 2); + ride->FormatStatusTo(gMapTooltipFormatArgs + 2 + nameArgLen); + } } static void ride_queue_banner_set_map_tooltip(TileElement* tileElement) { - ride_id_t rideIndex = tileElement->AsPath()->GetRideIndex(); + auto rideIndex = tileElement->AsPath()->GetRideIndex(); auto ride = get_ride(rideIndex); - - set_map_tooltip_format_arg(0, rct_string_id, STR_RIDE_MAP_TIP); - set_map_tooltip_format_arg(2, rct_string_id, ride->name); - set_map_tooltip_format_arg(4, uint32_t, ride->name_arguments); - - rct_string_id formatSecondary; - int32_t arg1 = 0; - ride_get_status(ride, &formatSecondary, &arg1); - set_map_tooltip_format_arg(8, rct_string_id, formatSecondary); - set_map_tooltip_format_arg(10, uint32_t, arg1); + if (ride != nullptr) + { + set_map_tooltip_format_arg(0, rct_string_id, STR_RIDE_MAP_TIP); + auto nameArgLen = ride->FormatNameTo(gMapTooltipFormatArgs + 2); + ride->FormatStatusTo(gMapTooltipFormatArgs + 2 + nameArgLen); + } } static void ride_station_set_map_tooltip(TileElement* tileElement) { - ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); + auto rideIndex = tileElement->AsTrack()->GetRideIndex(); auto ride = get_ride(rideIndex); - auto stationIndex = tileElement->AsTrack()->GetStationIndex(); - for (int32_t i = stationIndex; i >= 0; i--) - if (ride->stations[i].Start.xy == RCT_XY8_UNDEFINED) - stationIndex--; - - set_map_tooltip_format_arg(0, rct_string_id, STR_RIDE_MAP_TIP); - set_map_tooltip_format_arg(2, rct_string_id, ride->num_stations <= 1 ? STR_RIDE_STATION : STR_RIDE_STATION_X); - set_map_tooltip_format_arg(4, rct_string_id, ride->name); - set_map_tooltip_format_arg(6, uint32_t, ride->name_arguments); - set_map_tooltip_format_arg(10, rct_string_id, RideComponentNames[RideNameConvention[ride->type].station].capitalised); - set_map_tooltip_format_arg(12, uint16_t, stationIndex + 1); - - rct_string_id formatSecondary; - int32_t arg1; - ride_get_status(ride, &formatSecondary, &arg1); - set_map_tooltip_format_arg(14, rct_string_id, formatSecondary); - set_map_tooltip_format_arg(16, uint32_t, arg1); -} - -static void ride_entrance_set_map_tooltip(TileElement* tileElement) -{ - ride_id_t rideIndex = tileElement->AsEntrance()->GetRideIndex(); - auto ride = get_ride(rideIndex); - - // Get the station - auto stationIndex = tileElement->AsEntrance()->GetStationIndex(); - for (int32_t i = stationIndex; i >= 0; i--) - if (ride->stations[i].Start.xy == RCT_XY8_UNDEFINED) - stationIndex--; - - if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) + if (ride != nullptr) { - // Get the queue length - int32_t queueLength = 0; - if (!ride_get_entrance_location(ride, stationIndex).isNull()) - queueLength = ride->stations[stationIndex].QueueLength; - - set_map_tooltip_format_arg(0, rct_string_id, STR_RIDE_MAP_TIP); - set_map_tooltip_format_arg(2, rct_string_id, ride->num_stations <= 1 ? STR_RIDE_ENTRANCE : STR_RIDE_STATION_X_ENTRANCE); - set_map_tooltip_format_arg(4, rct_string_id, ride->name); - set_map_tooltip_format_arg(6, uint32_t, ride->name_arguments); - set_map_tooltip_format_arg(12, uint16_t, stationIndex + 1); - if (queueLength == 0) - { - set_map_tooltip_format_arg(14, rct_string_id, STR_QUEUE_EMPTY); - } - else if (queueLength == 1) - { - set_map_tooltip_format_arg(14, rct_string_id, STR_QUEUE_ONE_PERSON); - } - else - { - set_map_tooltip_format_arg(14, rct_string_id, STR_QUEUE_PEOPLE); - } - set_map_tooltip_format_arg(16, uint16_t, queueLength); - } - else - { - // Get the station - stationIndex = tileElement->AsEntrance()->GetStationIndex(); + auto stationIndex = tileElement->AsTrack()->GetStationIndex(); for (int32_t i = stationIndex; i >= 0; i--) if (ride->stations[i].Start.xy == RCT_XY8_UNDEFINED) stationIndex--; - set_map_tooltip_format_arg(0, rct_string_id, ride->num_stations <= 1 ? STR_RIDE_EXIT : STR_RIDE_STATION_X_EXIT); - set_map_tooltip_format_arg(2, rct_string_id, ride->name); - set_map_tooltip_format_arg(4, uint32_t, ride->name_arguments); - set_map_tooltip_format_arg(10, uint16_t, stationIndex + 1); + size_t argPos = 0; + set_map_tooltip_format_arg(argPos, rct_string_id, STR_RIDE_MAP_TIP); + argPos += sizeof(rct_string_id); + set_map_tooltip_format_arg(argPos, rct_string_id, ride->num_stations <= 1 ? STR_RIDE_STATION : STR_RIDE_STATION_X); + argPos += sizeof(rct_string_id); + argPos += ride->FormatNameTo(gMapTooltipFormatArgs + argPos); + set_map_tooltip_format_arg( + argPos, rct_string_id, RideComponentNames[RideNameConvention[ride->type].station].capitalised); + argPos += sizeof(rct_string_id); + set_map_tooltip_format_arg(argPos, uint16_t, stationIndex + 1); + argPos += sizeof(uint16_t); + ride->FormatStatusTo(gMapTooltipFormatArgs + argPos); + } +} + +static void ride_entrance_set_map_tooltip(TileElement* tileElement) +{ + auto rideIndex = tileElement->AsEntrance()->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + // Get the station + auto stationIndex = tileElement->AsEntrance()->GetStationIndex(); + for (int32_t i = stationIndex; i >= 0; i--) + if (ride->stations[i].Start.xy == RCT_XY8_UNDEFINED) + stationIndex--; + + if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) + { + // Get the queue length + int32_t queueLength = 0; + if (!ride_get_entrance_location(ride, stationIndex).isNull()) + queueLength = ride->stations[stationIndex].QueueLength; + + size_t argPos = 0; + set_map_tooltip_format_arg(argPos, rct_string_id, STR_RIDE_MAP_TIP); + argPos += sizeof(rct_string_id); + set_map_tooltip_format_arg( + argPos, rct_string_id, ride->num_stations <= 1 ? STR_RIDE_ENTRANCE : STR_RIDE_STATION_X_ENTRANCE); + argPos += sizeof(rct_string_id); + argPos += ride->FormatNameTo(gMapTooltipFormatArgs + argPos); + + // String IDs have an extra pop16 for some reason + argPos += sizeof(uint16_t); + + set_map_tooltip_format_arg(argPos, uint16_t, stationIndex + 1); + argPos += sizeof(uint16_t); + if (queueLength == 0) + { + set_map_tooltip_format_arg(argPos, rct_string_id, STR_QUEUE_EMPTY); + } + else if (queueLength == 1) + { + set_map_tooltip_format_arg(argPos, rct_string_id, STR_QUEUE_ONE_PERSON); + } + else + { + set_map_tooltip_format_arg(argPos, rct_string_id, STR_QUEUE_PEOPLE); + } + argPos += sizeof(rct_string_id); + set_map_tooltip_format_arg(argPos, uint16_t, queueLength); + } + else + { + // Get the station + stationIndex = tileElement->AsEntrance()->GetStationIndex(); + for (int32_t i = stationIndex; i >= 0; i--) + if (ride->stations[i].Start.xy == RCT_XY8_UNDEFINED) + stationIndex--; + + size_t argPos = 0; + set_map_tooltip_format_arg( + argPos, rct_string_id, ride->num_stations <= 1 ? STR_RIDE_EXIT : STR_RIDE_STATION_X_EXIT); + argPos += sizeof(rct_string_id); + argPos += ride->FormatNameTo(gMapTooltipFormatArgs + 2); + + // String IDs have an extra pop16 for some reason + argPos += sizeof(uint16_t); + + set_map_tooltip_format_arg(argPos, uint16_t, stationIndex + 1); + } } } @@ -3886,131 +3702,96 @@ int32_t ride_music_params_update( */ void ride_music_update_final() { - rct_ride_music_params* edi = nullptr; - int32_t ebx = 0; - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR)) + if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0 || (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) != 0) + return; + + // TODO Allow circus music (CSS24) to play if ride music is disabled (that should be sound) + if (gGameSoundsOff || !gConfigSound.ride_music_enabled) + return; + + // Stop currently playing music that is not in music params list or not playing? + for (auto& rideMusic : gRideMusicList) { - // TODO Allow circus music (CSS24) to play if ride music is disabled (that should be sound) - if (!gGameSoundsOff && gConfigSound.ride_music_enabled && !(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO)) + if (rideMusic.ride_id != RIDE_ID_NULL) { - while (1) + rct_ride_music_params* rideMusicParams = &gRideMusicParamsList[0]; + int32_t isPlaying = 0; + while (rideMusicParams < gRideMusicParamsListEnd && !isPlaying) { - int32_t v8 = 0; - int32_t v9 = 1; - rct_ride_music_params* ride_music_params = &gRideMusicParamsList[0]; - while (ride_music_params < gRideMusicParamsListEnd) - { - if (ride_music_params->ride_id != (uint8_t)-1) - { - v8++; - if (v9 >= ride_music_params->volume) - { - v9 = ride_music_params->volume; - edi = ride_music_params; - } - } - ride_music_params++; - } - if (v8 <= 2) + if (rideMusicParams->ride_id == rideMusic.ride_id && rideMusicParams->tune_id == rideMusic.tune_id) { + isPlaying = Mixer_Channel_IsPlaying(rideMusic.sound_channel); break; } - edi->ride_id = RIDE_ID_NULL; + rideMusicParams++; + } + if (!isPlaying) + { + Mixer_Stop_Channel(rideMusic.sound_channel); + rideMusic.ride_id = RIDE_ID_NULL; + } + } + } + + int32_t freeChannelIndex = 0; + for (rct_ride_music_params* rideMusicParams = &gRideMusicParamsList[0]; rideMusicParams < gRideMusicParamsListEnd; + rideMusicParams++) + { + if (rideMusicParams->ride_id != RIDE_ID_NULL) + { + rct_ride_music* rideMusic = &gRideMusicList[0]; + int32_t channelIndex = 0; + // Look for existing entry, if not found start playing the sound, otherwise update parameters. + while (rideMusicParams->ride_id != rideMusic->ride_id || rideMusicParams->tune_id != rideMusic->tune_id) + { + if (rideMusic->ride_id == RIDE_ID_NULL) + { + freeChannelIndex = channelIndex; + } + rideMusic++; + channelIndex++; + if (channelIndex >= AUDIO_MAX_RIDE_MUSIC) + { + rct_ride_music_info* ride_music_info = &gRideMusicInfoList[rideMusicParams->tune_id]; + rct_ride_music* ride_music_3 = &gRideMusicList[freeChannelIndex]; + ride_music_3->sound_channel = Mixer_Play_Music(ride_music_info->path_id, MIXER_LOOP_NONE, true); + if (ride_music_3->sound_channel) + { + ride_music_3->volume = rideMusicParams->volume; + ride_music_3->pan = rideMusicParams->pan; + ride_music_3->frequency = rideMusicParams->frequency; + ride_music_3->ride_id = rideMusicParams->ride_id; + ride_music_3->tune_id = rideMusicParams->tune_id; + Mixer_Channel_Volume(ride_music_3->sound_channel, DStoMixerVolume(ride_music_3->volume)); + Mixer_Channel_Pan(ride_music_3->sound_channel, DStoMixerPan(ride_music_3->pan)); + Mixer_Channel_Rate(ride_music_3->sound_channel, DStoMixerRate(ride_music_3->frequency)); + int32_t offset = std::max(0, rideMusicParams->offset - 10000); + Mixer_Channel_SetOffset(ride_music_3->sound_channel, offset); + + // Move circus music to the sound mixer group + if (ride_music_info->path_id == PATH_ID_CSS24) + { + Mixer_Channel_SetGroup(ride_music_3->sound_channel, MIXER_GROUP_SOUND); + } + } + return; + } } - // stop currently playing music that is not in music params list or not playing? - rct_ride_music* ride_music = &gRideMusicList[0]; - int32_t channel = 0; - do + if (rideMusicParams->volume != rideMusic->volume) { - if (ride_music->ride_id != RIDE_ID_NULL) - { - rct_ride_music_params* ride_music_params = &gRideMusicParamsList[0]; - int32_t isplaying = 0; - while (ride_music_params < gRideMusicParamsListEnd && !isplaying) - { - if (ride_music_params->ride_id == ride_music->ride_id - && ride_music_params->tune_id == ride_music->tune_id) - { - isplaying = Mixer_Channel_IsPlaying(gRideMusicList[channel].sound_channel); - break; - } - ride_music_params++; - } - if (!isplaying) - { - Mixer_Stop_Channel(gRideMusicList[channel].sound_channel); - ride_music->ride_id = RIDE_ID_NULL; - } - } - ride_music++; - channel++; - } while (channel < AUDIO_MAX_RIDE_MUSIC); - - for (rct_ride_music_params* ride_music_params = &gRideMusicParamsList[0]; - ride_music_params < gRideMusicParamsListEnd; ride_music_params++) + rideMusic->volume = rideMusicParams->volume; + Mixer_Channel_Volume(rideMusic->sound_channel, DStoMixerVolume(rideMusic->volume)); + } + if (rideMusicParams->pan != rideMusic->pan) { - if (ride_music_params->ride_id != RIDE_ID_NULL) - { - rct_ride_music* ride_music_2 = &gRideMusicList[0]; - int32_t channel2 = 0; - while (ride_music_params->ride_id != ride_music_2->ride_id - || ride_music_params->tune_id != ride_music_2->tune_id) - { - if (ride_music_2->ride_id == RIDE_ID_NULL) - { - ebx = channel2; - } - ride_music_2++; - channel2++; - if (channel2 >= AUDIO_MAX_RIDE_MUSIC) - { - rct_ride_music_info* ride_music_info = &gRideMusicInfoList[ride_music_params->tune_id]; - rct_ride_music* ride_music_3 = &gRideMusicList[ebx]; - ride_music_3->sound_channel = Mixer_Play_Music(ride_music_info->path_id, MIXER_LOOP_NONE, true); - if (ride_music_3->sound_channel) - { - ride_music_3->volume = ride_music_params->volume; - ride_music_3->pan = ride_music_params->pan; - ride_music_3->frequency = ride_music_params->frequency; - ride_music_3->ride_id = ride_music_params->ride_id; - ride_music_3->tune_id = ride_music_params->tune_id; - Mixer_Channel_Volume(ride_music_3->sound_channel, DStoMixerVolume(ride_music_3->volume)); - Mixer_Channel_Pan(ride_music_3->sound_channel, DStoMixerPan(ride_music_3->pan)); - Mixer_Channel_Rate(ride_music_3->sound_channel, DStoMixerRate(ride_music_3->frequency)); - int32_t offset = ride_music_params->offset - 10000; - if (offset < 0) - { - offset = 0; - } - Mixer_Channel_SetOffset(ride_music_3->sound_channel, offset); - - // Move circus music to the sound mixer group - if (ride_music_info->path_id == PATH_ID_CSS24) - { - Mixer_Channel_SetGroup(ride_music_3->sound_channel, MIXER_GROUP_SOUND); - } - } - return; - } - } - - if (ride_music_params->volume != ride_music_2->volume) - { - ride_music_2->volume = ride_music_params->volume; - Mixer_Channel_Volume(ride_music_2->sound_channel, DStoMixerVolume(ride_music_2->volume)); - } - if (ride_music_params->pan != ride_music_2->pan) - { - ride_music_2->pan = ride_music_params->pan; - Mixer_Channel_Pan(ride_music_2->sound_channel, DStoMixerPan(ride_music_2->pan)); - } - if (ride_music_params->frequency != ride_music_2->frequency) - { - ride_music_2->frequency = ride_music_params->frequency; - Mixer_Channel_Rate(ride_music_2->sound_channel, DStoMixerRate(ride_music_2->frequency)); - } - } + rideMusic->pan = rideMusicParams->pan; + Mixer_Channel_Pan(rideMusic->sound_channel, DStoMixerPan(rideMusic->pan)); + } + if (rideMusicParams->frequency != rideMusic->frequency) + { + rideMusic->frequency = rideMusicParams->frequency; + Mixer_Channel_Rate(rideMusic->sound_channel, DStoMixerRate(rideMusic->frequency)); } } } @@ -4107,7 +3888,9 @@ static int32_t ride_mode_check_station_present(Ride* ride) */ static int32_t ride_check_for_entrance_exit(ride_id_t rideIndex) { - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return 0; if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) return 1; @@ -4252,8 +4035,8 @@ static int32_t ride_check_block_brakes(CoordsXYE* input, CoordsXYE* output) static bool ride_check_track_contains_inversions(CoordsXYE* input, CoordsXYE* output) { ride_id_t rideIndex = input->element->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - if (ride->type == RIDE_TYPE_MAZE) + auto ride = get_ride(rideIndex); + if (ride != nullptr && ride->type == RIDE_TYPE_MAZE) return true; rct_window* w = window_find_by_class(WC_RIDE_CONSTRUCTION); @@ -4299,8 +4082,11 @@ static bool ride_check_track_contains_inversions(CoordsXYE* input, CoordsXYE* ou */ static bool ride_check_track_contains_banked(CoordsXYE* input, CoordsXYE* output) { - ride_id_t rideIndex = input->element->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); + auto rideIndex = input->element->AsTrack()->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return false; + if (ride->type == RIDE_TYPE_MAZE) return true; @@ -4403,6 +4189,8 @@ static bool ride_check_start_and_end_is_station(CoordsXYE* input) ride_id_t rideIndex = input->element->AsTrack()->GetRideIndex(); auto ride = get_ride(rideIndex); + if (ride == nullptr) + return false; auto w = window_find_by_class(WC_RIDE_CONSTRUCTION); if (w != nullptr && _rideConstructionState != RIDE_CONSTRUCTION_STATE_0 && rideIndex == _currentRideIndex) @@ -4557,7 +4345,9 @@ static void ride_set_block_points(CoordsXYE* startElement) */ static void ride_set_start_finish_points(ride_id_t rideIndex, CoordsXYE* startElement) { - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; switch (ride->type) { @@ -4569,7 +4359,7 @@ static void ride_set_start_finish_points(ride_id_t rideIndex, CoordsXYE* startEl break; } - if (ride_is_block_sectioned(ride) && !(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) + if (ride->IsBlockSectioned() && !(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) { ride_set_block_points(startElement); } @@ -4582,7 +4372,7 @@ static void ride_set_start_finish_points(ride_id_t rideIndex, CoordsXYE* startEl static int32_t count_free_misc_sprite_slots() { int32_t miscSpriteCount = gSpriteListCount[SPRITE_LIST_MISC]; - int32_t remainingSpriteCount = gSpriteListCount[SPRITE_LIST_NULL]; + int32_t remainingSpriteCount = gSpriteListCount[SPRITE_LIST_FREE]; return std::max(0, miscSpriteCount + remainingSpriteCount - 300); } @@ -4615,12 +4405,19 @@ static rct_vehicle* vehicle_create_car( ride_id_t rideIndex, int32_t vehicleEntryIndex, int32_t carIndex, int32_t vehicleIndex, int32_t x, int32_t y, int32_t z, int32_t* remainingDistance, TileElement* tileElement) { - Ride* ride = get_ride(rideIndex); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicleEntryIndex]; - int32_t edx; + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return nullptr; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return nullptr; + + auto vehicleEntry = &rideEntry->vehicles[vehicleEntryIndex]; + auto vehicle = &create_sprite(SPRITE_IDENTIFIER_VEHICLE)->vehicle; + if (vehicle == nullptr) + return nullptr; - rct_vehicle* vehicle = (rct_vehicle*)create_sprite(1); vehicle->sprite_identifier = SPRITE_IDENTIFIER_VEHICLE; vehicle->ride = rideIndex; vehicle->ride_subtype = ride->subtype; @@ -4628,7 +4425,7 @@ static rct_vehicle* vehicle_create_car( vehicle->vehicle_type = vehicleEntryIndex; vehicle->type = carIndex == 0 ? VEHICLE_TYPE_HEAD : VEHICLE_TYPE_TAIL; vehicle->var_44 = ror32(vehicleEntry->spacing, 10) & 0xFFFF; - edx = vehicleEntry->spacing >> 1; + auto edx = vehicleEntry->spacing >> 1; *remainingDistance -= edx; vehicle->remaining_distance = *remainingDistance; if (!(vehicleEntry->flags & VEHICLE_ENTRY_FLAG_GO_KART)) @@ -4653,13 +4450,13 @@ static rct_vehicle* vehicle_create_car( vehicle->spin_sprite = 0; vehicle->spin_speed = 0; vehicle->sound2_flags = 0; - vehicle->sound1_id = RCT12_SOUND_ID_NULL; - vehicle->sound2_id = RCT12_SOUND_ID_NULL; + vehicle->sound1_id = SoundId::Null; + vehicle->sound2_id = SoundId::Null; vehicle->next_vehicle_on_train = SPRITE_INDEX_NULL; vehicle->var_C4 = 0; vehicle->animation_frame = 0; vehicle->var_C8 = 0; - vehicle->scream_sound_id = 255; + vehicle->scream_sound_id = SoundId::Null; vehicle->vehicle_sprite_type = 0; vehicle->bank_rotation = 0; vehicle->target_seat_rotation = 4; @@ -4686,8 +4483,7 @@ static rct_vehicle* vehicle_create_car( vehicle->track_type = tileElement->AsTrack()->GetTrackType() << 2; vehicle->track_progress = 0; - vehicle->status = 0; - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_MOVING_TO_END_OF_STATION); vehicle->update_flags = 0; LocationXY16 chosenLoc; @@ -4792,8 +4588,7 @@ static rct_vehicle* vehicle_create_car( vehicle->update_flags |= VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES; } } - vehicle->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_MOVING_TO_END_OF_STATION); } // loc_6DDD5E: @@ -4810,40 +4605,47 @@ static train_ref vehicle_create_train( ride_id_t rideIndex, int32_t x, int32_t y, int32_t z, int32_t vehicleIndex, int32_t* remainingDistance, TileElement* tileElement) { - Ride* ride = get_ride(rideIndex); - train_ref train = { nullptr, nullptr }; - for (int32_t carIndex = 0; carIndex < ride->num_cars_per_train; carIndex++) + auto ride = get_ride(rideIndex); + if (ride != nullptr) { - const uint8_t vehicle = ride_entry_get_vehicle_at_position(ride->subtype, ride->num_cars_per_train, carIndex); - rct_vehicle* car = vehicle_create_car( - rideIndex, vehicle, carIndex, vehicleIndex, x, y, z, remainingDistance, tileElement); - if (carIndex == 0) + for (int32_t carIndex = 0; carIndex < ride->num_cars_per_train; carIndex++) { - train.head = car; + auto vehicle = ride_entry_get_vehicle_at_position(ride->subtype, ride->num_cars_per_train, carIndex); + auto car = vehicle_create_car(rideIndex, vehicle, carIndex, vehicleIndex, x, y, z, remainingDistance, tileElement); + if (car == nullptr) + break; + + if (carIndex == 0) + { + train.head = car; + } + else + { + // Link the previous car with this car + train.tail->next_vehicle_on_train = car->sprite_index; + train.tail->next_vehicle_on_ride = car->sprite_index; + car->prev_vehicle_on_ride = train.tail->sprite_index; + } + train.tail = car; } - else - { - // Link the previous car with this car - train.tail->next_vehicle_on_train = car->sprite_index; - train.tail->next_vehicle_on_ride = car->sprite_index; - car->prev_vehicle_on_ride = train.tail->sprite_index; - } - train.tail = car; } return train; } static void vehicle_create_trains(ride_id_t rideIndex, int32_t x, int32_t y, int32_t z, TileElement* tileElement) { - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + train_ref firstTrain = {}; train_ref lastTrain = {}; int32_t remainingDistance = 0; for (int32_t vehicleIndex = 0; vehicleIndex < ride->num_vehicles; vehicleIndex++) { - if (ride_is_block_sectioned(ride)) + if (ride->IsBlockSectioned()) { remainingDistance = 0; } @@ -4861,7 +4663,7 @@ static void vehicle_create_trains(ride_id_t rideIndex, int32_t x, int32_t y, int lastTrain = train; // Add train to ride vehicle list - move_sprite_to_list((rct_sprite*)train.head, SPRITE_LIST_TRAIN * 2); + move_sprite_to_list((rct_sprite*)train.head, SPRITE_LIST_VEHICLE_HEAD); for (int32_t i = 0; i < MAX_VEHICLES_PER_RIDE; i++) { if (ride->vehicles[i] == SPRITE_INDEX_NULL) @@ -4904,57 +4706,51 @@ static void ride_create_vehicles_find_first_block(Ride* ride, CoordsXYE* outXYEl int32_t firstX = vehicle->track_x; int32_t firstY = vehicle->track_y; int32_t firstZ = vehicle->track_z; - TileElement* firstElement = map_get_track_element_at(firstX, firstY, firstZ / 8); + auto firstElement = map_get_track_element_at(firstX, firstY, firstZ / 8); assert(firstElement != nullptr); int32_t x = firstX; int32_t y = firstY; - TileElement* trackElement = firstElement; + auto trackElement = firstElement; track_begin_end trackBeginEnd; - while (track_block_get_previous(x, y, trackElement, &trackBeginEnd)) + while (track_block_get_previous(x, y, reinterpret_cast(trackElement), &trackBeginEnd)) { x = trackBeginEnd.end_x; y = trackBeginEnd.end_y; - trackElement = trackBeginEnd.begin_element; + trackElement = trackBeginEnd.begin_element->AsTrack(); if (x == firstX && y == firstY && trackElement == firstElement) { break; } - int32_t trackType = trackElement->AsTrack()->GetTrackType(); + int32_t trackType = trackElement->GetTrackType(); switch (trackType) { case TRACK_ELEM_25_DEG_UP_TO_FLAT: case TRACK_ELEM_60_DEG_UP_TO_FLAT: - if (trackElement->AsTrack()->HasChain()) + if (trackElement->HasChain()) { outXYElement->x = x; outXYElement->y = y; - outXYElement->element = trackElement; + outXYElement->element = reinterpret_cast(trackElement); return; } break; case TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT: case TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT: - if (trackElement->AsTrack()->HasChain()) + if (trackElement->HasChain()) { - TileElement* tileElement = map_get_first_element_at(trackBeginEnd.begin_x >> 5, trackBeginEnd.begin_y >> 5); - do + TileElement* tileElement = map_get_track_element_at_of_type_seq( + trackBeginEnd.begin_x, trackBeginEnd.begin_y, trackBeginEnd.begin_z / 8, trackType, 0); + + if (tileElement != nullptr) { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - if (tileElement->AsTrack()->GetTrackType() != trackType) - continue; - if (tileElement->AsTrack()->GetSequenceIndex() != 0) - continue; - if (tileElement->base_height != trackBeginEnd.begin_z / 8) - continue; outXYElement->x = trackBeginEnd.begin_x; outXYElement->y = trackBeginEnd.begin_y; outXYElement->element = tileElement; return; - } while (!(tileElement++)->IsLastForTile()); + } } break; case TRACK_ELEM_END_STATION: @@ -4962,14 +4758,14 @@ static void ride_create_vehicles_find_first_block(Ride* ride, CoordsXYE* outXYEl case TRACK_ELEM_BLOCK_BRAKES: outXYElement->x = x; outXYElement->y = y; - outXYElement->element = trackElement; + outXYElement->element = reinterpret_cast(trackElement); return; } } outXYElement->x = firstX; outXYElement->y = firstY; - outXYElement->element = firstElement; + outXYElement->element = reinterpret_cast(firstElement); } /** @@ -4978,7 +4774,7 @@ static void ride_create_vehicles_find_first_block(Ride* ride, CoordsXYE* outXYEl */ static bool ride_create_vehicles(Ride* ride, CoordsXYE* element, int32_t isApplying) { - ride_update_max_vehicles(ride); + ride->UpdateMaxVehicles(); if (ride->subtype == RIDE_ENTRY_INDEX_NULL) { return true; @@ -5009,15 +4805,7 @@ static bool ride_create_vehicles(Ride* ride, CoordsXYE* element, int32_t isApply x = element->x - CoordsDirectionDelta[direction].x; y = element->y - CoordsDirectionDelta[direction].y; - tileElement = map_get_first_element_at(x >> 5, y >> 5); - do - { - if (tileElement->base_height != z) - continue; - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - break; - } while (!(tileElement++)->IsLastForTile()); + tileElement = reinterpret_cast(map_get_track_element_at(x, y, z)); z = tileElement->base_height; direction = tileElement->GetDirection(); @@ -5037,7 +4825,7 @@ static bool ride_create_vehicles(Ride* ride, CoordsXYE* element, int32_t isApply // if (ride->type != RIDE_TYPE_SPACE_RINGS && !ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_16)) { - if (ride_is_block_sectioned(ride)) + if (ride->IsBlockSectioned()) { CoordsXYE firstBlock; ride_create_vehicles_find_first_block(ride, &firstBlock); @@ -5113,10 +4901,10 @@ void loc_6DDF9C(Ride* ride, TileElement* tileElement) while (true) { car->update_flags &= ~VEHICLE_UPDATE_FLAG_1; - car->status = VEHICLE_STATUS_TRAVELLING; + car->SetState(VEHICLE_STATUS_TRAVELLING, car->sub_state); if ((car->track_type >> 2) == TRACK_ELEM_END_STATION) { - car->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; + car->SetState(VEHICLE_STATUS_MOVING_TO_END_OF_STATION, car->sub_state); } uint16_t spriteIndex = car->next_vehicle_on_train; @@ -5156,6 +4944,8 @@ static bool ride_initialise_cable_lift_track(Ride* ride, bool isApplying) bool success = false; TileElement* tileElement = map_get_first_element_at(location.x, location.y); + if (tileElement == nullptr) + return success; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -5242,7 +5032,9 @@ static bool ride_initialise_cable_lift_track(Ride* ride, bool isApplying) */ static bool ride_create_cable_lift(ride_id_t rideIndex, bool isApplying) { - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return false; if (ride->mode != RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED && ride->mode != RIDE_MODE_CONTINUOUS_CIRCUIT) { @@ -5275,15 +5067,7 @@ static bool ride_create_cable_lift(ride_id_t rideIndex, bool isApplying) int32_t x = ride->cable_lift_x; int32_t y = ride->cable_lift_y; int32_t z = ride->cable_lift_z; - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - if (tileElement->base_height != z) - continue; - break; - } while (!(tileElement++)->IsLastForTile()); + auto tileElement = map_get_track_element_at(x, y, z); int32_t direction = tileElement->GetDirection(); rct_vehicle* head = nullptr; @@ -5298,7 +5082,7 @@ static bool ride_create_cable_lift(ride_id_t rideIndex, bool isApplying) int32_t remaining_distance = ebx; ebx -= edx; - rct_vehicle* current = cable_lift_segment_create(rideIndex, x, y, z, direction, var_44, remaining_distance, i == 0); + rct_vehicle* current = cable_lift_segment_create(*ride, x, y, z, direction, var_44, remaining_distance, i == 0); current->next_vehicle_on_train = SPRITE_INDEX_NULL; if (i == 0) { @@ -5326,9 +5110,6 @@ static bool ride_create_cable_lift(ride_id_t rideIndex, bool isApplying) */ static void loc_6B51C0(const Ride* ride) { - if (gUnk141F568 != gUnk13CA740) - return; - rct_window* w = window_get_main(); if (w == nullptr) return; @@ -5384,14 +5165,11 @@ static void loc_6B51C0(const Ride* ride) */ static void ride_scroll_to_track_error(CoordsXYE* trackElement) { - if (!gGameCommandIsNetworked && gUnk141F568 == gUnk13CA740) + rct_window* w = window_get_main(); + if (w != nullptr) { - rct_window* w = window_get_main(); - if (w != nullptr) - { - window_scroll_to_location(w, trackElement->x, trackElement->y, trackElement->element->base_height * 8); - ride_modify(trackElement); - } + window_scroll_to_location(w, trackElement->x, trackElement->y, trackElement->element->base_height * 8); + ride_modify(trackElement); } } @@ -5401,8 +5179,13 @@ static void ride_scroll_to_track_error(CoordsXYE* trackElement) */ static TileElement* loc_6B4F6B(ride_id_t rideIndex, int32_t x, int32_t y) { - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return nullptr; + TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -5426,7 +5209,7 @@ static TileElement* loc_6B4F6B(ride_id_t rideIndex, int32_t x, int32_t y) return nullptr; } -int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isApplying) +int32_t ride_is_valid_for_test(Ride* ride, int32_t status, bool isApplying) { int32_t stationIndex; CoordsXYE trackElement, problematicTrackElement = {}; @@ -5437,7 +5220,10 @@ int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isAppl return 0; } - window_close_by_number(WC_RIDE_CONSTRUCTION, ride->id); + if (status != RIDE_STATUS_SIMULATING) + { + window_close_by_number(WC_RIDE_CONSTRUCTION, ride->id); + } stationIndex = ride_mode_check_station_present(ride); if (stationIndex == -1) @@ -5446,13 +5232,13 @@ int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isAppl if (!ride_mode_check_valid_station_numbers(ride)) return 0; - if (!ride_check_for_entrance_exit(ride->id)) + if (status != RIDE_STATUS_SIMULATING && !ride_check_for_entrance_exit(ride->id)) { loc_6B51C0(ride); return 0; } - if (goingToBeOpen && isApplying) + if (status == RIDE_STATUS_OPEN && isApplying) { sub_6B5952(ride); ride->lifecycle_flags |= RIDE_LIFECYCLE_EVER_BEEN_OPENED; @@ -5473,7 +5259,7 @@ int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isAppl || ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) { if (ride_find_track_gap(ride, &trackElement, &problematicTrackElement) - && (!gConfigGeneral.test_unfinished_tracks || ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED + && (status != RIDE_STATUS_SIMULATING || ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED)) { gGameCommandErrorText = STR_TRACK_IS_NOT_A_COMPLETE_CIRCUIT; @@ -5562,7 +5348,7 @@ int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isAppl * * rct2: 0x006B4EEA */ -int32_t ride_is_valid_for_open(Ride* ride, int32_t goingToBeOpen, int32_t isApplying) +int32_t ride_is_valid_for_open(Ride* ride, int32_t goingToBeOpen, bool isApplying) { int32_t stationIndex; CoordsXYE trackElement, problematicTrackElement = {}; @@ -5798,7 +5584,7 @@ int32_t ride_get_refund_price(const Ride* ride) * * rct2: 0x00696707 */ -void ride_stop_peeps_queuing(Ride* ride) +void Ride::StopGuestsQueuing() { uint16_t spriteIndex; Peep* peep; @@ -5807,7 +5593,7 @@ void ride_stop_peeps_queuing(Ride* ride) { if (peep->state != PEEP_STATE_QUEUING) continue; - if (peep->current_ride != ride->id) + if (peep->current_ride != id) continue; peep->RemoveFromQueue(); @@ -5815,27 +5601,14 @@ void ride_stop_peeps_queuing(Ride* ride) } } -ride_id_t ride_get_empty_slot() +uint8_t Ride::GetDefaultMode() const { - for (ride_id_t i = 0; i < MAX_RIDES; i++) - { - Ride* ride = get_ride(i); - if (ride->type == RIDE_TYPE_NULL) - { - return i; - } - } - return RIDE_ID_NULL; -} - -int32_t ride_get_default_mode(Ride* ride) -{ - const rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); + const rct_ride_entry* rideEntry = get_ride_entry(subtype); const uint8_t* availableModes = RideAvailableModes; - for (int32_t i = 0; i < ride->type; i++) + for (int32_t i = 0; i < type; i++) { - while (*(availableModes++) != 255) + while (*(availableModes++) != RIDE_MODE_NULL) { } } @@ -5850,18 +5623,15 @@ int32_t ride_get_default_mode(Ride* ride) static bool ride_with_colour_config_exists(uint8_t ride_type, const TrackColour* colours) { - Ride* ride; - int32_t i; - - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (ride->type != ride_type) + if (ride.type != ride_type) continue; - if (ride->track_colour[0].main != colours->main) + if (ride.track_colour[0].main != colours->main) continue; - if (ride->track_colour[0].additional != colours->additional) + if (ride.track_colour[0].additional != colours->additional) continue; - if (ride->track_colour[0].supports != colours->supports) + if (ride.track_colour[0].supports != colours->supports) continue; return true; @@ -5869,17 +5639,21 @@ static bool ride_with_colour_config_exists(uint8_t ride_type, const TrackColour* return false; } -static bool ride_name_exists(char* name) +bool Ride::NameExists(const std::string_view& name, ride_id_t excludeRideId) { - char buffer[256]; - Ride* ride; - int32_t i; - FOR_ALL_RIDES (i, ride) + char buffer[256]{}; + uint32_t formatArgs[32]{}; + + for (auto& ride : GetRideManager()) { - format_string(buffer, 256, ride->name, &ride->name_arguments); - if ((strcmp(buffer, name) == 0) && ride_has_any_track_elements(ride)) + if (ride.id != excludeRideId) { - return true; + ride.FormatNameTo(formatArgs); + format_string(buffer, 256, STR_STRINGID, formatArgs); + if (std::string_view(buffer) == name && ride_has_any_track_elements(&ride)) + { + return true; + } } } return false; @@ -5916,109 +5690,60 @@ int32_t ride_get_random_colour_preset_index(uint8_t ride_type) * * Based on rct2: 0x006B4776 */ -void ride_set_colour_preset(Ride* ride, uint8_t index) +void Ride::SetColourPreset(uint8_t index) { - const track_colour_preset_list* colourPresets = &RideColourPresets[ride->type]; + const track_colour_preset_list* colourPresets = &RideColourPresets[type]; TrackColour colours = { COLOUR_BLACK, COLOUR_BLACK, COLOUR_BLACK }; - if (index < colourPresets->count) + // Stalls save their default colour in the vehicle settings (since they share a common ride type) + if (!IsRide()) + { + auto rideEntry = get_ride_entry(subtype); + if (rideEntry != nullptr && rideEntry->vehicle_preset_list->count > 0) + { + auto list = rideEntry->vehicle_preset_list->list[0]; + colours = { list.main, list.additional_1, list.additional_2 }; + } + } + else if (index < colourPresets->count) { colours = colourPresets->list[index]; } for (int32_t i = 0; i < NUM_COLOUR_SCHEMES; i++) { - ride->track_colour[i].main = colours.main; - ride->track_colour[i].additional = colours.additional; - ride->track_colour[i].supports = colours.supports; + track_colour[i].main = colours.main; + track_colour[i].additional = colours.additional; + track_colour[i].supports = colours.supports; } - ride->colour_scheme_type = 0; + colour_scheme_type = 0; } money32 ride_get_common_price(Ride* forRide) { - Ride* ride; - int32_t i; - - FOR_ALL_RIDES (i, ride) + for (const auto& ride : GetRideManager()) { - if (ride->type == forRide->type && ride != forRide) + if (ride.type == forRide->type && &ride != forRide) { - return ride->price; + return ride.price; } } return MONEY32_UNDEFINED; } -void ride_set_name_to_default(Ride* ride, rct_ride_entry* rideEntry) +void Ride::SetNameToDefault() { - if (RideGroupManager::RideTypeIsIndependent(ride->type)) - { - ride_set_name_to_vehicle_default(ride, rideEntry); - } - else - { - ride_set_name_to_track_default(ride, rideEntry); - } -} + char rideNameBuffer[256]{}; + uint8_t rideNameArgs[32]{}; -void ride_set_name_to_track_default(Ride* ride, rct_ride_entry* rideEntry) -{ - char rideNameBuffer[256]; - ride_name_args name_args; - - ride->name = STR_NONE; - - if (RideGroupManager::RideTypeHasRideGroups(ride->type)) - { - const RideGroup* rideGroup = RideGroupManager::GetRideGroup(ride->type, rideEntry); - name_args.type_name = rideGroup->Naming.name; - } - else - { - name_args.type_name = RideNaming[ride->type].name; - } - - name_args.number = 0; + // Increment default name number until we find a unique name + custom_name = {}; + default_name_number = 0; do { - name_args.number++; - format_string(rideNameBuffer, 256, 1, &name_args); - } while (ride_name_exists(rideNameBuffer)); - - ride->name = 1; - ride->name_arguments_type_name = name_args.type_name; - ride->name_arguments_number = name_args.number; -} - -static void ride_set_name_to_vehicle_default(Ride* ride, rct_ride_entry* rideEntry) -{ - char rideNameBuffer[256]; - ride_name_args name_args; - - ride->name = 1; - ride->name_arguments_type_name = rideEntry->naming.name; - rct_string_id rideNameStringId = 0; - name_args.type_name = rideEntry->naming.name; - name_args.number = 0; - - do - { - name_args.number++; - format_string(rideNameBuffer, 256, ride->name, &name_args); - } while (ride_name_exists(rideNameBuffer)); - - ride->name_arguments_type_name = name_args.type_name; - ride->name_arguments_number = name_args.number; - - rideNameStringId = user_string_allocate(USER_STRING_HIGH_ID_NUMBER | USER_STRING_DUPLICATION_PERMITTED, rideNameBuffer); - if (rideNameStringId != 0) - { - ride->name = rideNameStringId; - } - else - { - ride_set_name_to_track_default(ride, rideEntry); - } + default_name_number++; + FormatNameTo(rideNameArgs); + format_string(rideNameBuffer, 256, STR_STRINGID, &rideNameArgs); + } while (Ride::NameExists(rideNameBuffer, id)); } /** @@ -6240,34 +5965,34 @@ int32_t get_turn_count_4_plus_elements(Ride* ride, uint8_t type) return ((*turn_count) & TURN_MASK_4_PLUS_ELEMENTS) >> 11; } -bool ride_has_spinning_tunnel(Ride* ride) +bool Ride::HasSpinningTunnel() const { - return ride->special_track_elements & RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS; + return special_track_elements & RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS; } -bool ride_has_water_splash(Ride* ride) +bool Ride::HasWaterSplash() const { - return ride->special_track_elements & RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS; + return special_track_elements & RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS; } -bool ride_has_rapids(Ride* ride) +bool Ride::HasRapids() const { - return ride->special_track_elements & RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS; + return special_track_elements & RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS; } -bool ride_has_log_reverser(Ride* ride) +bool Ride::HasLogReverser() const { - return ride->special_track_elements & RIDE_ELEMENT_REVERSER_OR_WATERFALL; + return special_track_elements & RIDE_ELEMENT_REVERSER_OR_WATERFALL; } -bool ride_has_waterfall(Ride* ride) +bool Ride::HasWaterfall() const { - return ride->special_track_elements & RIDE_ELEMENT_REVERSER_OR_WATERFALL; + return special_track_elements & RIDE_ELEMENT_REVERSER_OR_WATERFALL; } -bool ride_has_whirlpool(Ride* ride) +bool Ride::HasWhirlpool() const { - return ride->special_track_elements & RIDE_ELEMENT_WHIRLPOOL; + return special_track_elements & RIDE_ELEMENT_WHIRLPOOL; } uint8_t ride_get_helix_sections(Ride* ride) @@ -6276,15 +6001,15 @@ uint8_t ride_get_helix_sections(Ride* ride) return ride->special_track_elements & 0x1F; } -bool ride_is_powered_launched(Ride* ride) +bool Ride::IsPoweredLaunched() const { - return ride->mode == RIDE_MODE_POWERED_LAUNCH_PASSTROUGH || ride->mode == RIDE_MODE_POWERED_LAUNCH - || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED; + return mode == RIDE_MODE_POWERED_LAUNCH_PASSTROUGH || mode == RIDE_MODE_POWERED_LAUNCH + || mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED; } -bool ride_is_block_sectioned(Ride* ride) +bool Ride::IsBlockSectioned() const { - return ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED; + return mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED; } bool ride_has_any_track_elements(const Ride* ride) @@ -6307,24 +6032,6 @@ bool ride_has_any_track_elements(const Ride* ride) return false; } -void ride_all_has_any_track_elements(bool* rideIndexArray) -{ - tile_element_iterator it; - - std::fill_n(rideIndexArray, MAX_RIDES, false); - - tile_element_iterator_begin(&it); - while (tile_element_iterator_next(&it)) - { - if (it.element->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - if (it.element->IsGhost()) - continue; - - rideIndexArray[it.element->AsTrack()->GetRideIndex()] = true; - } -} - /** * * rct2: 0x006847BA @@ -6345,7 +6052,7 @@ void set_vehicle_type_image_max_sizes(rct_ride_entry_vehicle* vehicle_type, int3 for (int32_t i = 0; i < num_images; ++i) { - gfx_draw_sprite_software(&dpi, vehicle_type->base_image_id + i, 0, 0, 0); + gfx_draw_sprite_software(&dpi, ImageId::FromUInt32(vehicle_type->base_image_id + i), 0, 0); } int32_t al = -1; for (int32_t i = 99; i != 0; --i) @@ -6500,6 +6207,9 @@ void ride_get_entrance_or_exit_position_from_screen_position( } ride = get_ride(gRideEntranceExitPlaceRideIndex); + if (ride == nullptr) + return; + stationHeight = ride->stations[gRideEntranceExitPlaceStationIndex].Height; screen_get_map_xy_with_z(screenX, screenY, stationHeight * 8, &mapX, &mapY); @@ -6545,6 +6255,8 @@ void ride_get_entrance_or_exit_position_from_screen_position( if (mapX >= 0 && mapY >= 0 && mapX < (256 * 32) && mapY < (256 * 32)) { tileElement = map_get_first_element_at(mapX >> 5, mapY >> 5); + if (tileElement == nullptr) + continue; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -6553,7 +6265,7 @@ void ride_get_entrance_or_exit_position_from_screen_position( continue; if (tileElement->AsTrack()->GetRideIndex() != gRideEntranceExitPlaceRideIndex) continue; - if (tileElement->AsTrack()->GetTrackType() == TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP) + if (tileElement->AsTrack()->GetTrackType() == TRACK_ELEM_MAZE) { gRideEntranceExitPlaceDirection = direction_reverse(direction); *outDirection = direction_reverse(direction); @@ -6600,6 +6312,8 @@ void ride_get_entrance_or_exit_position_from_screen_position( mapX -= CoordsDirectionDelta[direction].x; mapY -= CoordsDirectionDelta[direction].y; tileElement = map_get_first_element_at(mapX >> 5, mapY >> 5); + if (tileElement == nullptr) + break; bool goToNextTile = false; do @@ -6736,7 +6450,7 @@ bool ride_are_all_possible_entrances_and_exits_built(Ride* ride) */ void invalidate_test_results(Ride* ride) { - ride_measurement_clear(ride); + ride->measurement = {}; ride->excitement = RIDE_RATING_UNDEFINED; ride->lifecycle_flags &= ~RIDE_LIFECYCLE_TESTED; ride->lifecycle_flags &= ~RIDE_LIFECYCLE_TEST_IN_PROGRESS; @@ -6876,8 +6590,7 @@ uint8_t ride_entry_get_vehicle_at_position(int32_t rideEntryIndex, int32_t numCa // Finds track pieces that a given ride entry has sprites for uint64_t ride_entry_get_supported_track_pieces(const rct_ride_entry* rideEntry) { - uint64_t supportedPieces = 0xFFFFFFFFFFFFFFFFULL; - uint16_t trackPieceRequiredSprites[55] = { + static constexpr uint16_t trackPieceRequiredSprites[55] = { 0x0001u, 0x0001u, 0x0001u, 0x0000u, 0x0006u, 0x0002u, 0x0020u, 0x000E, 0x0003u, 0x0006u, 0x0007u, 0x0002u, 0x0004u, 0x0001u, 0x0001u, 0x0001u, 0x0001u, 0x0061u, 0x000E, 0x1081u, 0x0001u, 0x0020u, 0x0020u, 0x0001u, 0x0001u, 0x0000u, 0x0001u, 0x0001u, 0x000C, 0x0061u, 0x0002u, 0x000E, 0x0480u, @@ -6887,24 +6600,33 @@ uint64_t ride_entry_get_supported_track_pieces(const rct_ride_entry* rideEntry) // Only check default vehicle; it's assumed the others will have correct sprites if this one does (I've yet to find an // exception, at least) - for (int32_t j = 0; j < 55; j++) + auto supportedPieces = std::numeric_limits::max(); + auto defaultVehicle = rideEntry->GetDefaultVehicle(); + if (defaultVehicle != nullptr) { - if ((rideEntry->vehicles[rideEntry->default_vehicle].sprite_flags & trackPieceRequiredSprites[j]) - != trackPieceRequiredSprites[j]) - supportedPieces &= ~(1ULL << j); + const auto defaultSpriteFlags = defaultVehicle->sprite_flags; + for (size_t i = 0; i < std::size(trackPieceRequiredSprites); i++) + { + if ((defaultSpriteFlags & trackPieceRequiredSprites[i]) != trackPieceRequiredSprites[i]) + { + supportedPieces &= ~(1ULL << i); + } + } } - return supportedPieces; } -static int32_t ride_get_smallest_station_length(Ride* ride) +static opt::optional ride_get_smallest_station_length(Ride* ride) { - auto result = std::numeric_limits::max(); - for (int32_t i = 0; i < MAX_STATIONS; i++) + opt::optional result; + for (const auto& station : ride->stations) { - if (ride->stations[i].Start.xy != RCT_XY8_UNDEFINED) + if (station.Start.xy != RCT_XY8_UNDEFINED) { - result = std::min(result, ride->stations[i].Length); + if (!result.has_value() || station.Length < *result) + { + result = station.Length; + } } } return result; @@ -6934,6 +6656,8 @@ static int32_t ride_get_track_length(Ride* ride) z = ride->stations[i].Height; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + continue; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -6991,12 +6715,12 @@ static int32_t ride_get_track_length(Ride* ride) * * rct2: 0x006DD57D */ -void ride_update_max_vehicles(Ride* ride) +void Ride::UpdateMaxVehicles() { - if (ride->subtype == RIDE_ENTRY_INDEX_NULL) + if (subtype == RIDE_ENTRY_INDEX_NULL) return; - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); + rct_ride_entry* rideEntry = get_ride_entry(subtype); if (rideEntry == nullptr) { return; @@ -7008,16 +6732,16 @@ void ride_update_max_vehicles(Ride* ride) if (rideEntry->cars_per_flat_ride == 0xFF) { int32_t trainLength; - ride->num_cars_per_train = std::max(rideEntry->min_cars_in_train, ride->num_cars_per_train); - ride->min_max_cars_per_train = rideEntry->max_cars_in_train | (rideEntry->min_cars_in_train << 4); + num_cars_per_train = std::max(rideEntry->min_cars_in_train, num_cars_per_train); + min_max_cars_per_train = rideEntry->max_cars_in_train | (rideEntry->min_cars_in_train << 4); // Calculate maximum train length based on smallest station length - int32_t stationLength = ride_get_smallest_station_length(ride); - if (stationLength == -1) + auto stationNumTiles = ride_get_smallest_station_length(this); + if (!stationNumTiles.has_value()) return; - stationLength = (stationLength * 0x44180) - 0x16B2A; - int32_t maxMass = RideData5[ride->type].max_mass << 8; + auto stationLength = (*stationNumTiles * 0x44180) - 0x16B2A; + int32_t maxMass = RideData5[type].max_mass << 8; int32_t maxCarsPerTrain = 1; for (int32_t numCars = rideEntry->max_cars_in_train; numCars > 0; numCars--) { @@ -7025,7 +6749,7 @@ void ride_update_max_vehicles(Ride* ride) int32_t totalMass = 0; for (int32_t i = 0; i < numCars; i++) { - vehicleEntry = &rideEntry->vehicles[ride_entry_get_vehicle_at_position(ride->subtype, numCars, i)]; + vehicleEntry = &rideEntry->vehicles[ride_entry_get_vehicle_at_position(subtype, numCars, i)]; trainLength += vehicleEntry->spacing; totalMass += vehicleEntry->car_mass; } @@ -7036,19 +6760,19 @@ void ride_update_max_vehicles(Ride* ride) break; } } - int32_t newCarsPerTrain = std::max(ride->proposed_num_cars_per_train, rideEntry->min_cars_in_train); + int32_t newCarsPerTrain = std::max(proposed_num_cars_per_train, rideEntry->min_cars_in_train); maxCarsPerTrain = std::max(maxCarsPerTrain, (int32_t)rideEntry->min_cars_in_train); if (!gCheatsDisableTrainLengthLimit) { newCarsPerTrain = std::min(maxCarsPerTrain, newCarsPerTrain); } - ride->min_max_cars_per_train = maxCarsPerTrain | (rideEntry->min_cars_in_train << 4); + min_max_cars_per_train = maxCarsPerTrain | (rideEntry->min_cars_in_train << 4); - switch (ride->mode) + switch (mode) { case RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED: case RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED: - maxNumTrains = std::clamp(ride->num_stations + ride->num_block_brakes - 1, 1, 31); + maxNumTrains = std::clamp(num_stations + num_block_brakes - 1, 1, 31); break; case RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE: case RIDE_MODE_POWERED_LAUNCH_PASSTROUGH: @@ -7062,7 +6786,7 @@ void ride_update_max_vehicles(Ride* ride) trainLength = 0; for (int32_t i = 0; i < newCarsPerTrain; i++) { - vehicleEntry = &rideEntry->vehicles[ride_entry_get_vehicle_at_position(ride->subtype, newCarsPerTrain, i)]; + vehicleEntry = &rideEntry->vehicles[ride_entry_get_vehicle_at_position(subtype, newCarsPerTrain, i)]; trainLength += vehicleEntry->spacing; } @@ -7077,31 +6801,30 @@ void ride_update_max_vehicles(Ride* ride) totalLength += trainLength; } while (totalLength <= stationLength); - if ((ride->mode != RIDE_MODE_STATION_TO_STATION && ride->mode != RIDE_MODE_CONTINUOUS_CIRCUIT) - || !(RideData4[ride->type].flags & RIDE_TYPE_FLAG4_ALLOW_MORE_VEHICLES_THAN_STATION_FITS)) + if ((mode != RIDE_MODE_STATION_TO_STATION && mode != RIDE_MODE_CONTINUOUS_CIRCUIT) + || !(RideData4[type].flags & RIDE_TYPE_FLAG4_ALLOW_MORE_VEHICLES_THAN_STATION_FITS)) { maxNumTrains = std::min(maxNumTrains, 31); } else { - vehicleEntry = &rideEntry->vehicles[ride_entry_get_vehicle_at_position(ride->subtype, newCarsPerTrain, 0)]; - int32_t speed = vehicleEntry->powered_max_speed; + vehicleEntry = &rideEntry->vehicles[ride_entry_get_vehicle_at_position(subtype, newCarsPerTrain, 0)]; + int32_t poweredMaxSpeed = vehicleEntry->powered_max_speed; int32_t totalSpacing = 0; for (int32_t i = 0; i < newCarsPerTrain; i++) { - vehicleEntry = &rideEntry - ->vehicles[ride_entry_get_vehicle_at_position(ride->subtype, newCarsPerTrain, i)]; + vehicleEntry = &rideEntry->vehicles[ride_entry_get_vehicle_at_position(subtype, newCarsPerTrain, i)]; totalSpacing += vehicleEntry->spacing; } totalSpacing >>= 13; - int32_t trackLength = ride_get_track_length(ride) / 4; - if (speed > 10) + int32_t trackLength = ride_get_track_length(this) / 4; + if (poweredMaxSpeed > 10) trackLength = (trackLength * 3) / 4; - if (speed > 25) + if (poweredMaxSpeed > 25) trackLength = (trackLength * 3) / 4; - if (speed > 40) + if (poweredMaxSpeed > 40) trackLength = (trackLength * 3) / 4; maxNumTrains = 0; @@ -7114,14 +6837,14 @@ void ride_update_max_vehicles(Ride* ride) } break; } - ride->max_trains = maxNumTrains; + max_trains = maxNumTrains; - numCarsPerTrain = std::min(ride->proposed_num_cars_per_train, (uint8_t)newCarsPerTrain); + numCarsPerTrain = std::min(proposed_num_cars_per_train, (uint8_t)newCarsPerTrain); } else { - ride->max_trains = rideEntry->cars_per_flat_ride; - ride->min_max_cars_per_train = rideEntry->max_cars_in_train | (rideEntry->min_cars_in_train << 4); + max_trains = rideEntry->cars_per_flat_ride; + min_max_cars_per_train = rideEntry->max_cars_in_train | (rideEntry->min_cars_in_train << 4); numCarsPerTrain = rideEntry->max_cars_in_train; maxNumTrains = rideEntry->cars_per_flat_ride; } @@ -7130,33 +6853,41 @@ void ride_update_max_vehicles(Ride* ride) { maxNumTrains = 31; } - numVehicles = std::min(ride->proposed_num_vehicles, (uint8_t)maxNumTrains); + numVehicles = std::min(proposed_num_vehicles, (uint8_t)maxNumTrains); // Refresh new current num vehicles / num cars per vehicle - if (numVehicles != ride->num_vehicles || numCarsPerTrain != ride->num_cars_per_train) + if (numVehicles != num_vehicles || numCarsPerTrain != num_cars_per_train) { - ride->num_cars_per_train = numCarsPerTrain; - ride->num_vehicles = numVehicles; - window_invalidate_by_number(WC_RIDE, ride->id); + num_cars_per_train = numCarsPerTrain; + num_vehicles = numVehicles; + window_invalidate_by_number(WC_RIDE, id); } } -void ride_set_ride_entry(Ride* ride, int32_t rideEntry) +void Ride::UpdateNumberOfCircuits() +{ + if (!CanHaveMultipleCircuits()) + { + num_circuits = 1; + } +} + +void Ride::SetRideEntry(int32_t rideEntry) { auto colour = ride_get_unused_preset_vehicle_colour(rideEntry); - auto rideSetVehicleAction = RideSetVehicleAction(ride->id, RideSetVehicleType::RideEntry, rideEntry, colour); + auto rideSetVehicleAction = RideSetVehicleAction(id, RideSetVehicleType::RideEntry, rideEntry, colour); GameActions::Execute(&rideSetVehicleAction); } -void ride_set_num_vehicles(Ride* ride, int32_t numVehicles) +void Ride::SetNumVehicles(int32_t numVehicles) { - auto rideSetVehicleAction = RideSetVehicleAction(ride->id, RideSetVehicleType::NumTrains, numVehicles); + auto rideSetVehicleAction = RideSetVehicleAction(id, RideSetVehicleType::NumTrains, numVehicles); GameActions::Execute(&rideSetVehicleAction); } -void ride_set_num_cars_per_vehicle(Ride* ride, int32_t numCarsPerVehicle) +void Ride::SetNumCarsPerVehicle(int32_t numCarsPerVehicle) { - auto rideSetVehicleAction = RideSetVehicleAction(ride->id, RideSetVehicleType::NumCarsPerTrain, numCarsPerVehicle); + auto rideSetVehicleAction = RideSetVehicleAction(id, RideSetVehicleType::NumCarsPerTrain, numCarsPerVehicle); GameActions::Execute(&rideSetVehicleAction); } @@ -7191,6 +6922,8 @@ void sub_6CB945(Ride* ride) location.y -= CoordsDirectionDelta[direction].y; } tileElement = map_get_first_element_at(location.x >> 5, location.y >> 5); + if (tileElement == nullptr) + break; bool trackFound = false; do @@ -7210,7 +6943,7 @@ void sub_6CB945(Ride* ride) break; } while (!(tileElement++)->IsLastForTile()); - if (trackFound == false) + if (!trackFound) { break; } @@ -7225,7 +6958,7 @@ void sub_6CB945(Ride* ride) } } - if (specialTrack == false) + if (!specialTrack) { continue; } @@ -7238,6 +6971,8 @@ void sub_6CB945(Ride* ride) bool trackFound = false; tileElement = map_get_first_element_at(blockLocation.x >> 5, blockLocation.y >> 5); + if (tileElement == nullptr) + break; do { if (blockLocation.z != tileElement->base_height) @@ -7261,51 +6996,50 @@ void sub_6CB945(Ride* ride) } } - // Needs room for an entrance and an exit per station, plus one position for the list terminator. - TileCoordsXYZD locations[(MAX_STATIONS * 2) + 1]; - TileCoordsXYZD* locationList = locations; + std::vector locations; for (uint8_t stationId = 0; stationId < MAX_STATIONS; ++stationId) { auto entrance = ride_get_entrance_location(ride, stationId); if (!entrance.isNull()) { - *locationList++ = entrance; + locations.push_back(entrance); ride_clear_entrance_location(ride, stationId); } auto exit = ride_get_exit_location(ride, stationId); if (!exit.isNull()) { - *locationList++ = exit; + locations.push_back(exit); ride_clear_exit_location(ride, stationId); } } - (*locationList++).x = COORDS_NULL; - locationList = locations; - for (; !(*locationList).isNull(); locationList++) + auto locationListIter = locations.cbegin(); + for (const TileCoordsXYZD& locationCoords : locations) { - TileCoordsXYZD* locationList2 = locationList; - locationList2++; + auto locationList = ++locationListIter; bool duplicateLocation = false; - do + while (locationList != locations.cend()) { - if ((*locationList).x == (*locationList2).x && (*locationList).y == (*locationList2).y) + const TileCoordsXYZD& locationCoords2 = *locationList++; + if (locationCoords.x == locationCoords2.x && locationCoords.y == locationCoords2.y) { duplicateLocation = true; break; } - } while (!(*locationList2++).isNull()); + } if (duplicateLocation) { continue; } - CoordsXY location = { (*locationList).x * 32, (*locationList).y * 32 }; + CoordsXY location = { locationCoords.x * 32, locationCoords.y * 32 }; TileElement* tileElement = map_get_first_element_at(location.x >> 5, location.y >> 5); + if (tileElement == nullptr) + continue; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) @@ -7321,6 +7055,8 @@ void sub_6CB945(Ride* ride) bool shouldRemove = true; TileElement* trackElement = map_get_first_element_at(nextLocation.x >> 5, nextLocation.y >> 5); + if (trackElement == nullptr) + continue; do { if (trackElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -7333,7 +7069,7 @@ void sub_6CB945(Ride* ride) uint8_t trackType = trackElement->AsTrack()->GetTrackType(); uint8_t trackSequence = trackElement->AsTrack()->GetSequenceIndex(); - uint8_t direction = (tileElement->GetDirection() - trackElement->GetDirectionWithOffset(2)) & 3; + Direction direction = (tileElement->GetDirection() - direction_reverse(trackElement->GetDirection())) & 3; if (!(TrackSequenceProperties[trackType][trackSequence] & (1 << direction))) { @@ -7341,7 +7077,7 @@ void sub_6CB945(Ride* ride) } uint8_t stationId = 0; - if (trackType != TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP) + if (trackType != TRACK_ELEM_MAZE) { stationId = trackElement->AsTrack()->GetStationIndex(); } @@ -7374,7 +7110,7 @@ void sub_6CB945(Ride* ride) shouldRemove = false; } while (!(trackElement++)->IsLastForTile()); - if (shouldRemove == true) + if (shouldRemove) { footpath_queue_chain_reset(); maze_entrance_hedge_replacement(location.x, location.y, tileElement); @@ -7388,14 +7124,14 @@ void sub_6CB945(Ride* ride) } } -void ride_set_to_default_inspection_interval(Ride* ride) +void Ride::SetToDefaultInspectionInterval() { uint8_t defaultInspectionInterval = gConfigGeneral.default_inspection_interval; - if (ride->inspection_interval != defaultInspectionInterval) + if (inspection_interval != defaultInspectionInterval) { if (defaultInspectionInterval <= RIDE_INSPECTION_NEVER) { - set_operating_setting(ride->id, RideSetSetting::InspectionInterval, defaultInspectionInterval); + set_operating_setting(id, RideSetSetting::InspectionInterval, defaultInspectionInterval); } } } @@ -7404,9 +7140,9 @@ void ride_set_to_default_inspection_interval(Ride* ride) * * rct2: 0x006B752C */ -void ride_crash(Ride* ride, uint8_t vehicleIndex) +void Ride::Crash(uint8_t vehicleIndex) { - rct_vehicle* vehicle = GET_VEHICLE(ride->vehicles[vehicleIndex]); + rct_vehicle* vehicle = GET_VEHICLE(vehicles[vehicleIndex]); if (!(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO)) { @@ -7422,36 +7158,18 @@ void ride_crash(Ride* ride, uint8_t vehicleIndex) } } - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + FormatNameTo(gCommonFormatArgs); if (gConfigNotifications.ride_crashed) { - news_item_add_to_queue(NEWS_ITEM_RIDE, STR_RIDE_HAS_CRASHED, ride->id); + news_item_add_to_queue(NEWS_ITEM_RIDE, STR_RIDE_HAS_CRASHED, id); } } void ride_reset_all_names() { - int32_t i; - Ride* ride; - char rideNameBuffer[256]; - ride_name_args name_args; - - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - ride->name = STR_NONE; - - name_args.type_name = RideNaming[ride->type].name; - name_args.number = 0; - do - { - name_args.number++; - format_string(rideNameBuffer, 256, 1, &name_args); - } while (ride_name_exists(rideNameBuffer)); - - ride->name = 1; - ride->name_arguments_type_name = name_args.type_name; - ride->name_arguments_number = name_args.number; + ride.SetNameToDefault(); } } @@ -7519,22 +7237,23 @@ rct_vehicle* ride_get_broken_vehicle(Ride* ride) * * rct2: 0x006D235B */ -void ride_delete(Ride* ride) +void Ride::Delete() { - user_string_free(ride->name); - ride->type = RIDE_TYPE_NULL; + custom_name = {}; + measurement = {}; + type = RIDE_TYPE_NULL; } -void ride_renew(Ride* ride) +void Ride::Renew() { // Set build date to current date (so the ride is brand new) - ride->build_date = gDateMonthsElapsed; - ride->reliability = RIDE_INITIAL_RELIABILITY; + build_date = gDateMonthsElapsed; + reliability = RIDE_INITIAL_RELIABILITY; } -static bool ride_is_ride(Ride* ride) +RideClassification Ride::GetClassification() const { - switch (ride->type) + switch (type) { case RIDE_TYPE_FOOD_STALL: case RIDE_TYPE_1D: @@ -7542,21 +7261,31 @@ static bool ride_is_ride(Ride* ride) case RIDE_TYPE_1F: case RIDE_TYPE_SHOP: case RIDE_TYPE_22: + case RIDE_TYPE_50: + case RIDE_TYPE_52: + case RIDE_TYPE_53: + case RIDE_TYPE_54: + return RideClassification::ShopOrStall; case RIDE_TYPE_INFORMATION_KIOSK: case RIDE_TYPE_TOILETS: case RIDE_TYPE_CASH_MACHINE: case RIDE_TYPE_FIRST_AID: - return false; + return RideClassification::KioskOrFacility; default: - return true; + return RideClassification::Ride; } } -money16 ride_get_price(Ride* ride) +bool Ride::IsRide() const +{ + return GetClassification() == RideClassification::Ride; +} + +money16 ride_get_price(const Ride* ride) { if (gParkFlags & PARK_FLAGS_NO_MONEY) return 0; - if (ride_is_ride(ride)) + if (ride->IsRide()) { if (!park_ride_prices_unlocked()) { @@ -7618,9 +7347,9 @@ static bool check_for_adjacent_station(int32_t x, int32_t y, int32_t z, uint8_t TileElement* stationElement = get_station_platform(adjX, adjY, z, 2); if (stationElement != nullptr) { - ride_id_t rideIndex = stationElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS) + auto rideIndex = stationElement->AsTrack()->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride != nullptr && (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS)) { found = true; } @@ -7670,6 +7399,11 @@ bool ride_has_adjacent_station(Ride* ride) bool ride_has_station_shelter(Ride* ride) { auto stationObj = ride_get_station_object(ride); + if (network_get_mode() != NETWORK_MODE_NONE) + { + // The server might run in headless mode so no images will be loaded, only check for stations. + return stationObj != nullptr; + } return stationObj != nullptr && stationObj->BaseImageId != 0; } @@ -7795,13 +7529,9 @@ uint8_t ride_entry_get_first_non_null_ride_type(const rct_ride_entry* rideEntry) bool ride_type_supports_boosters(uint8_t rideType) { - if (rideType == RIDE_TYPE_LOOPING_ROLLER_COASTER || rideType == RIDE_TYPE_CORKSCREW_ROLLER_COASTER + return rideType == RIDE_TYPE_LOOPING_ROLLER_COASTER || rideType == RIDE_TYPE_CORKSCREW_ROLLER_COASTER || rideType == RIDE_TYPE_TWISTER_ROLLER_COASTER || rideType == RIDE_TYPE_VERTICAL_DROP_ROLLER_COASTER - || rideType == RIDE_TYPE_GIGA_COASTER || rideType == RIDE_TYPE_JUNIOR_ROLLER_COASTER) - { - return true; - } - return false; + || rideType == RIDE_TYPE_GIGA_COASTER || rideType == RIDE_TYPE_JUNIOR_ROLLER_COASTER; } int32_t get_booster_speed(uint8_t rideType, int32_t rawSpeed) @@ -7825,13 +7555,11 @@ int32_t get_booster_speed(uint8_t rideType, int32_t rawSpeed) void fix_invalid_vehicle_sprite_sizes() { - Ride* ride; - uint16_t i; - FOR_ALL_RIDES (i, ride) + for (const auto& ride : GetRideManager()) { for (uint16_t j = 0; j < MAX_VEHICLES_PER_RIDE; j++) { - uint16_t rideSpriteIndex = ride->vehicles[j]; + uint16_t rideSpriteIndex = ride.vehicles[j]; while (rideSpriteIndex != SPRITE_INDEX_NULL) { rct_vehicle* vehicle = try_get_vehicle(rideSpriteIndex); @@ -7921,28 +7649,9 @@ StationObject* ride_get_station_object(const Ride* ride) LocationXY16 ride_get_rotated_coords(int16_t x, int16_t y, int16_t z) { - LocationXY16 rotatedCoords = { 0, 0 }; - - switch (get_current_rotation()) - { - case 0: - rotatedCoords.x = y - x; - rotatedCoords.y = ((y + x) / 2) - z; - break; - case 1: - rotatedCoords.x = -x - y; - rotatedCoords.y = ((y - x) / 2) - z; - break; - case 2: - rotatedCoords.x = x - y; - rotatedCoords.y = ((-y - x) / 2) - z; - break; - case 3: - rotatedCoords.x = y + x; - rotatedCoords.y = ((x - y) / 2) - z; - break; - } - + CoordsXYZ coords3d = { x, y, z }; + auto screenCoords = translate_3d_to_2d_with_z(get_current_rotation(), coords3d); + LocationXY16 rotatedCoords = { (int16_t)screenCoords.x, (int16_t)screenCoords.y }; return rotatedCoords; } @@ -7956,14 +7665,12 @@ void determine_ride_entrance_and_exit_locations() { log_verbose("Inspecting ride entrance / exit locations"); - ride_id_t rideIndex; - Ride* ride; - FOR_ALL_RIDES (rideIndex, ride) + for (auto& ride : GetRideManager()) { for (int32_t stationIndex = 0; stationIndex < MAX_STATIONS; stationIndex++) { - TileCoordsXYZD entranceLoc = ride->stations[stationIndex].Entrance; - TileCoordsXYZD exitLoc = ride->stations[stationIndex].Exit; + TileCoordsXYZD entranceLoc = ride.stations[stationIndex].Entrance; + TileCoordsXYZD exitLoc = ride.stations[stationIndex].Exit; bool fixEntrance = false; bool fixExit = false; @@ -7973,30 +7680,30 @@ void determine_ride_entrance_and_exit_locations() const EntranceElement* entranceElement = map_get_ride_entrance_element_at( entranceLoc.x * 32, entranceLoc.y * 32, entranceLoc.z, false); - if (entranceElement == nullptr || entranceElement->GetRideIndex() != rideIndex + if (entranceElement == nullptr || entranceElement->GetRideIndex() != ride.id || entranceElement->GetStationIndex() != stationIndex) { fixEntrance = true; } else { - ride->stations[stationIndex].Entrance.direction = (uint8_t)entranceElement->GetDirection(); + ride.stations[stationIndex].Entrance.direction = (uint8_t)entranceElement->GetDirection(); } } if (!exitLoc.isNull()) { const EntranceElement* entranceElement = map_get_ride_exit_element_at( - exitLoc.x * 32, exitLoc.y * 32, entranceLoc.z, false); + exitLoc.x * 32, exitLoc.y * 32, exitLoc.z, false); - if (entranceElement == nullptr || entranceElement->GetRideIndex() != rideIndex + if (entranceElement == nullptr || entranceElement->GetRideIndex() != ride.id || entranceElement->GetStationIndex() != stationIndex) { fixExit = true; } else { - ride->stations[stationIndex].Exit.direction = (uint8_t)entranceElement->GetDirection(); + ride.stations[stationIndex].Exit.direction = (uint8_t)entranceElement->GetDirection(); } } @@ -8024,7 +7731,7 @@ void determine_ride_entrance_and_exit_locations() continue; } const EntranceElement* entranceElement = tileElement->AsEntrance(); - if (entranceElement->GetRideIndex() != rideIndex) + if (entranceElement->GetRideIndex() != ride.id) { continue; } @@ -8034,15 +7741,15 @@ void determine_ride_entrance_and_exit_locations() } // The expected height is where entrances and exit reside in non-hacked parks. - const uint8_t expectedHeight = ride->stations[stationIndex].Height; + const uint8_t expectedHeight = ride.stations[stationIndex].Height; if (fixEntrance && entranceElement->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) { if (alreadyFoundEntrance) { - if (ride->stations[stationIndex].Entrance.z == expectedHeight) + if (ride.stations[stationIndex].Entrance.z == expectedHeight) continue; - if (ride->stations[stationIndex].Entrance.z > entranceElement->base_height) + if (ride.stations[stationIndex].Entrance.z > entranceElement->base_height) continue; } @@ -8053,31 +7760,31 @@ void determine_ride_entrance_and_exit_locations() entranceElement->base_height, (uint8_t)entranceElement->GetDirection(), }; - ride_set_entrance_location(ride, stationIndex, newEntranceLoc); + ride_set_entrance_location(&ride, stationIndex, newEntranceLoc); alreadyFoundEntrance = true; log_verbose( - "Fixed disconnected entrance of ride %d, station %d to x = %d, y = %d and z = %d.", - rideIndex, stationIndex, x, y, entranceElement->base_height); + "Fixed disconnected entrance of ride %d, station %d to x = %d, y = %d and z = %d.", ride.id, + stationIndex, x, y, entranceElement->base_height); } else if (fixExit && entranceElement->GetEntranceType() == ENTRANCE_TYPE_RIDE_EXIT) { if (alreadyFoundExit) { - if (ride->stations[stationIndex].Exit.z == expectedHeight) + if (ride.stations[stationIndex].Exit.z == expectedHeight) continue; - if (ride->stations[stationIndex].Exit.z > entranceElement->base_height) + if (ride.stations[stationIndex].Exit.z > entranceElement->base_height) continue; } // Found our exit ride_set_exit_location( - ride, stationIndex, + &ride, stationIndex, { x, y, entranceElement->base_height, (uint8_t)entranceElement->GetDirection() }); alreadyFoundExit = true; log_verbose( - "Fixed disconnected exit of ride %d, station %d to x = %d, y = %d and z = %d.", rideIndex, + "Fixed disconnected exit of ride %d, station %d to x = %d, y = %d and z = %d.", ride.id, stationIndex, x, y, entranceElement->base_height); } } while (!(tileElement++)->IsLastForTile()); @@ -8087,13 +7794,13 @@ void determine_ride_entrance_and_exit_locations() if (fixEntrance && !alreadyFoundEntrance) { - ride_clear_entrance_location(ride, stationIndex); - log_verbose("Cleared disconnected entrance of ride %d, station %d.", rideIndex, stationIndex); + ride_clear_entrance_location(&ride, stationIndex); + log_verbose("Cleared disconnected entrance of ride %d, station %d.", ride.id, stationIndex); } if (fixExit && !alreadyFoundExit) { - ride_clear_exit_location(ride, stationIndex); - log_verbose("Cleared disconnected exit of ride %d, station %d.", rideIndex, stationIndex); + ride_clear_exit_location(&ride, stationIndex); + log_verbose("Cleared disconnected exit of ride %d, station %d.", ride.id, stationIndex); } } } @@ -8115,3 +7822,50 @@ void ride_clear_leftover_entrances(Ride* ride) } } } + +std::string Ride::GetName() const +{ + uint8_t args[32]{}; + FormatNameTo(args); + return format_string(STR_STRINGID, args); +} + +size_t Ride::FormatNameTo(void* argsV) const +{ + auto args = (uint8_t*)argsV; + if (!custom_name.empty()) + { + auto str = custom_name.c_str(); + set_format_arg_on(args, 0, rct_string_id, STR_STRING); + set_format_arg_on(args, 2, void*, str); + return sizeof(rct_string_id) + sizeof(void*); + } + else + { + auto rideTypeName = RideNaming[type].name; + if (RideGroupManager::RideTypeIsIndependent(type)) + { + auto rideEntry = GetRideEntry(); + if (rideEntry != nullptr) + { + rideTypeName = rideEntry->naming.name; + } + } + else if (RideGroupManager::RideTypeHasRideGroups(type)) + { + auto rideEntry = GetRideEntry(); + if (rideEntry != nullptr) + { + auto rideGroup = RideGroupManager::GetRideGroup(type, rideEntry); + if (rideGroup != nullptr) + { + rideTypeName = rideGroup->Naming.name; + } + } + } + set_format_arg_on(args, 0, rct_string_id, 1); + set_format_arg_on(args, 2, rct_string_id, rideTypeName); + set_format_arg_on(args, 4, uint16_t, default_name_number); + return sizeof(rct_string_id) + sizeof(rct_string_id) + sizeof(uint16_t); + } +} diff --git a/src/openrct2/ride/Ride.h b/src/openrct2/ride/Ride.h index 65e347e8c9..3246426718 100644 --- a/src/openrct2/ride/Ride.h +++ b/src/openrct2/ride/Ride.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,9 +18,13 @@ #include "RideTypes.h" #include "Vehicle.h" +#include +#include + interface IObjectManager; class StationObject; struct Peep; +struct Ride; struct Staff; #define MAX_RIDE_TYPES_PER_RIDE_ENTRY 3 @@ -35,11 +39,15 @@ struct Staff; #define CUSTOMER_HISTORY_SIZE 10 #define MAX_CARS_PER_TRAIN 255 #define MAX_STATIONS 4 -#define RIDE_MEASUREMENT_MAX_ITEMS 4800 #define MAX_RIDES 255 -#define RIDE_ID_NULL 255 +#define RIDE_TYPE_NULL 255 #define RIDE_ADJACENCY_CHECK_DISTANCE 5 +constexpr ride_id_t RIDE_ID_NULL = std::numeric_limits::max(); +constexpr uint16_t const MAX_INVERSIONS = RCT12_MAX_INVERSIONS; +constexpr uint16_t const MAX_GOLF_HOLES = RCT12_MAX_GOLF_HOLES; +constexpr uint16_t const MAX_HELICES = RCT12_MAX_HELICES; + #pragma pack(push, 1) /** @@ -136,6 +144,20 @@ struct rct_ride_entry uint8_t shop_item_secondary; // 0x1C1 rct_string_id capacity; void* obj; + + const rct_ride_entry_vehicle* GetVehicle(size_t id) const + { + if (id < std::size(vehicles)) + { + return &vehicles[id]; + } + return nullptr; + } + + const rct_ride_entry_vehicle* GetDefaultVehicle() const + { + return GetVehicle(default_vehicle); + } }; struct RideStation @@ -156,6 +178,31 @@ struct RideStation static constexpr uint8_t NO_TRAIN = std::numeric_limits::max(); }; +struct RideMeasurement +{ + static constexpr size_t MAX_ITEMS = 4800; + + uint8_t flags{}; + uint32_t last_use_tick{}; + uint16_t num_items{}; + uint16_t current_item{}; + uint8_t vehicle_index{}; + uint8_t current_station{}; + int8_t vertical[MAX_ITEMS]{}; + int8_t lateral[MAX_ITEMS]{}; + uint8_t velocity[MAX_ITEMS]{}; + uint8_t altitude[MAX_ITEMS]{}; +}; + +enum class RideClassification +{ + Ride, + ShopOrStall, + KioskOrFacility +}; + +struct TrackDesign; + /** * Ride structure. * @@ -164,8 +211,8 @@ struct RideStation */ struct Ride { - ride_id_t id; - uint8_t type; + ride_id_t id = RIDE_ID_NULL; + uint8_t type = RIDE_TYPE_NULL; // pointer to static info. for example, wild mouse type is 0x36, subtype is // 0x4c. uint8_t subtype; @@ -174,16 +221,8 @@ struct Ride VehicleColour vehicle_colours[MAX_CARS_PER_TRAIN]; // 0 = closed, 1 = open, 2 = test uint8_t status; - rct_string_id name; - union - { - uint32_t name_arguments; - struct - { - rct_string_id name_arguments_type_name; - uint16_t name_arguments_number; - }; - }; + std::string custom_name; + uint16_t default_name_number; LocationXY8 overall_view; uint16_t vehicles[MAX_VEHICLES_PER_RIDE]; // Points to the first car in the train uint8_t depart_flags; @@ -208,7 +247,6 @@ struct Ride uint8_t boat_hire_return_direction; LocationXY8 boat_hire_return_position; - uint8_t measurement_index; // bits 0 through 4 are the number of helix sections // bit 5: spinning tunnel, water splash, or rapids // bit 6: log reverser, waterfall @@ -233,14 +271,6 @@ struct Ride uint16_t turn_count_default; // X = current turn count uint16_t turn_count_banked; uint16_t turn_count_sloped; // X = number turns > 3 elements - union - { - uint8_t inversions; // (???X XXXX) - uint8_t holes; // (???X XXXX) - // This is a very rough approximation of how much of the ride is undercover. - // It reaches the maximum value of 7 at about 50% undercover and doesn't increase beyond that. - uint8_t sheltered_eighths; // (XXX?-????) - }; // Y is number of powered lifts, X is drops uint8_t drops; // (YYXX XXXX) uint8_t start_drop_height; @@ -352,32 +382,73 @@ struct Ride uint8_t current_issues; uint32_t last_issue_time; RideStation stations[MAX_STATIONS]; + uint16_t inversions; + uint16_t holes; + uint8_t sheltered_eighths; + std::unique_ptr measurement; + +private: + void Update(); + void UpdateChairlift(); + void UpdateSpiralSlide(); + void UpdateQueueLength(int32_t stationIndex); + money32 CalculateIncomePerHour() const; + +public: bool CanBreakDown() const; + RideClassification GetClassification() const; + bool IsRide() const; + void Renew(); + void Delete(); + void Crash(uint8_t vehicleIndex); + void SetToDefaultInspectionInterval(); + void SetRideEntry(int32_t rideEntry); + + void SetNumVehicles(int32_t numVehicles); + void SetNumCarsPerVehicle(int32_t numCarsPerVehicle); + void UpdateMaxVehicles(); + void UpdateNumberOfCircuits(); + + bool HasSpinningTunnel() const; + bool HasWaterSplash() const; + bool HasRapids() const; + bool HasLogReverser() const; + bool HasWaterfall() const; + bool HasWhirlpool() const; + + bool IsPoweredLaunched() const; + bool IsBlockSectioned() const; + bool CanHaveMultipleCircuits() const; + bool SupportsStatus(int32_t s) const; + + void StopGuestsQueuing(); + + uint8_t GetDefaultMode() const; + + void SetColourPreset(uint8_t index); + + rct_ride_entry* GetRideEntry() const; + + int32_t GetTotalQueueLength() const; + int32_t GetMaxQueueTime() const; + + void QueueInsertGuestAtFront(int32_t stationIndex, Peep* peep); + Peep* GetQueueHeadGuest(int32_t stationIndex) const; + + void SetNameToDefault(); + std::string GetName() const; + size_t FormatNameTo(void* args) const; + void FormatStatusTo(void* args) const; + + static void UpdateAll(); + static bool NameExists(const std::string_view& name, ride_id_t excludeRideId = RIDE_ID_NULL); + + std::unique_ptr SaveToTrackDesign() const; }; #pragma pack(push, 1) -/** - * Ride measurement structure. - * size: 0x04B0C - */ -struct rct_ride_measurement -{ - ride_id_t ride_index; // 0x0000 - uint8_t flags; // 0x0001 - uint32_t last_use_tick; // 0x0002 - uint16_t num_items; // 0x0006 - uint16_t current_item; // 0x0008 - uint8_t vehicle_index; // 0x000A - uint8_t current_station; // 0x000B - int8_t vertical[RIDE_MEASUREMENT_MAX_ITEMS]; // 0x000C - int8_t lateral[RIDE_MEASUREMENT_MAX_ITEMS]; // 0x12CC - uint8_t velocity[RIDE_MEASUREMENT_MAX_ITEMS]; // 0x258C - uint8_t altitude[RIDE_MEASUREMENT_MAX_ITEMS]; // 0x384C -}; -assert_struct_size(rct_ride_measurement, 0x4b0c); - struct track_begin_end { int32_t begin_x; @@ -412,13 +483,6 @@ assert_struct_size(ride_name_args, 4); #define TYPE_TO_RIDE_ENTRY_SLOTS 492 extern uint8_t gTypeToRideEntryIndexMap[TYPE_TO_RIDE_ENTRY_SLOTS]; -enum -{ - RIDE_CLASS_RIDE, - RIDE_CLASS_SHOP_OR_STALL, - RIDE_CLASS_KIOSK_OR_FACILITY -}; - // Constants used by the lifecycle_flags property at 0x1D0 enum { @@ -487,7 +551,6 @@ enum enum { - RIDE_TYPE_NULL = 255, RIDE_TYPE_SPIRAL_ROLLER_COASTER = 0, RIDE_TYPE_STAND_UP_ROLLER_COASTER, RIDE_TYPE_SUSPENDED_SWINGING_COASTER, @@ -587,7 +650,8 @@ enum { RIDE_STATUS_CLOSED, RIDE_STATUS_OPEN, - RIDE_STATUS_TESTING + RIDE_STATUS_TESTING, + RIDE_STATUS_SIMULATING, }; enum @@ -628,7 +692,10 @@ enum RIDE_MODE_FREEFALL_DROP, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, RIDE_MODE_POWERED_LAUNCH, // RCT1 style, don't pass through station - RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED + RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED, + + RIDE_MOUNT_COUNT, + RIDE_MODE_NULL = 255, }; enum @@ -929,27 +996,100 @@ struct rct_ride_properties extern const rct_ride_properties RideProperties[RIDE_TYPE_COUNT]; -/** Helper macros until rides are stored in this module. */ -Ride* get_ride(int32_t index); -rct_ride_entry* get_ride_entry(int32_t index); -void get_ride_entry_name(char* name, int32_t index); -rct_ride_measurement* get_ride_measurement(int32_t index); +Ride* get_ride(ride_id_t index); -/** - * Helper macro loop for enumerating through all the non null rides. - */ -#define FOR_ALL_RIDES(i, ride) \ - for (i = 0; i < MAX_RIDES; i++) \ - if ((ride = get_ride(i))->type != RIDE_TYPE_NULL) +struct RideManager +{ + const Ride* operator[](ride_id_t id) const + { + return get_ride(id); + } + + Ride* operator[](ride_id_t id) + { + return get_ride(id); + } + + class Iterator + { + friend RideManager; + + private: + RideManager& _rideManager; + size_t _index{}; + size_t _endIndex{}; + + public: + using difference_type = intptr_t; + using value_type = Ride; + using pointer = const Ride*; + using reference = const Ride&; + using iterator_category = std::forward_iterator_tag; + + private: + Iterator(RideManager& rideManager, size_t beginIndex, size_t endIndex) + : _rideManager(rideManager) + , _index(beginIndex) + , _endIndex(endIndex) + { + if (_index < _endIndex && _rideManager[(ride_id_t)_index] == nullptr) + { + ++(*this); + } + } + + public: + Iterator& operator++() + { + do + { + _index++; + } while (_index < _endIndex && _rideManager[(ride_id_t)_index] == nullptr); + return *this; + } + Iterator operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + bool operator==(Iterator other) const + { + return _index == other._index; + } + bool operator!=(Iterator other) const + { + return !(*this == other); + } + Ride& operator*() + { + return *_rideManager[(ride_id_t)_index]; + } + }; + + size_t size() const; + Iterator begin(); + Iterator end(); + Iterator begin() const + { + return ((RideManager*)this)->begin(); + } + Iterator end() const + { + return ((RideManager*)this)->end(); + } +}; + +RideManager GetRideManager(); +ride_id_t GetNextFreeRideId(); +Ride* GetOrAllocateRide(ride_id_t index); +rct_ride_entry* get_ride_entry(int32_t index); +std::string_view get_ride_entry_name(size_t index); +RideMeasurement* get_ride_measurement(int32_t index); extern money16 gTotalRideValueForMoney; -extern const uint8_t gRideClassifications[MAX_RIDES]; - -extern Ride gRideList[MAX_RIDES]; - -extern rct_ride_measurement gRideMeasurements[MAX_RIDE_MEASUREMENTS]; -extern uint16_t gRideCount; +extern const rct_string_id ColourSchemeNames[4]; extern money32 _currentTrackPrice; @@ -995,16 +1135,10 @@ extern bool gGotoStartPlacementMode; extern uint8_t gLastEntranceStyle; ride_id_t ride_get_empty_slot(); -int32_t ride_get_default_mode(Ride* ride); int32_t ride_get_count(); -int32_t ride_get_total_queue_length(Ride* ride); -int32_t ride_get_max_queue_time(Ride* ride); -Peep* ride_get_queue_head_guest(Ride* ride, int32_t stationIndex); -void ride_queue_insert_guest_at_front(Ride* ride, int32_t stationIndex, Peep* peep); void ride_init_all(); void reset_all_ride_build_dates(); void ride_update_favourited_stat(); -void ride_update_all(); void ride_check_all_reachable(); void ride_update_satisfaction(Ride* ride, uint8_t happiness); void ride_update_popularity(Ride* ride, uint8_t pop_amount); @@ -1012,29 +1146,25 @@ bool ride_try_get_origin_element(const Ride* ride, CoordsXYE* output); int32_t ride_find_track_gap(const Ride* ride, CoordsXYE* input, CoordsXYE* output); void ride_construct_new(ride_list_item listItem); void ride_construct(Ride* ride); -int32_t ride_modify(CoordsXYE* input); +bool ride_modify(CoordsXYE* input); void ride_remove_peeps(Ride* ride); void ride_clear_blocked_tiles(Ride* ride); -void ride_get_status(const Ride* ride, rct_string_id* formatSecondary, int32_t* argument); Staff* ride_get_mechanic(Ride* ride); Staff* ride_get_assigned_mechanic(Ride* ride); -int32_t ride_get_total_length(Ride* ride); +int32_t ride_get_total_length(const Ride* ride); int32_t ride_get_total_time(Ride* ride); -int32_t ride_can_have_multiple_circuits(Ride* ride); TrackColour ride_get_track_colour(Ride* ride, int32_t colourScheme); vehicle_colour ride_get_vehicle_colour(Ride* ride, int32_t vehicleIndex); int32_t ride_get_unused_preset_vehicle_colour(uint8_t ride_sub_type); void ride_set_vehicle_colours_to_random_preset(Ride* ride, uint8_t preset_index); -rct_ride_entry* get_ride_entry_by_ride(const Ride* ride); uint8_t* get_ride_entry_indices_for_ride_type(uint8_t rideType); void reset_type_to_ride_entry_index_map(IObjectManager& objectManager); -void ride_measurement_clear(Ride* ride); void ride_measurements_update(); -rct_ride_measurement* ride_get_measurement(Ride* ride, rct_string_id* message); +std::pair ride_get_measurement(Ride* ride); void ride_breakdown_add_news_item(Ride* ride); Peep* ride_find_closest_mechanic(Ride* ride, int32_t forInspection); -int32_t ride_is_valid_for_open(Ride* ride, int32_t goingToBeOpen, int32_t isApplying); -int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isApplying); +int32_t ride_is_valid_for_open(Ride* ride, int32_t goingToBeOpen, bool isApplying); +int32_t ride_is_valid_for_test(Ride* ride, int32_t status, bool isApplying); int32_t ride_initialise_construction_window(Ride* ride); void ride_construction_invalidate_current_track(); int32_t sub_6C683D( @@ -1048,25 +1178,13 @@ void ride_prepare_breakdown(Ride* ride, int32_t breakdownReason); TileElement* ride_get_station_start_track_element(Ride* ride, int32_t stationIndex); TileElement* ride_get_station_exit_element(int32_t x, int32_t y, int32_t z); void ride_set_status(Ride* ride, int32_t status); -void game_command_set_ride_status( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); void ride_set_name(Ride* ride, const char* name, uint32_t flags); -void game_command_set_ride_name( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); int32_t ride_get_refund_price(const Ride* ride); int32_t ride_get_random_colour_preset_index(uint8_t ride_type); -void ride_set_colour_preset(Ride* ride, uint8_t index); money32 ride_get_common_price(Ride* forRide); rct_ride_name get_ride_naming(const uint8_t rideType, rct_ride_entry* rideEntry); -void game_command_create_ride(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_callback_ride_construct_new( - int32_t eax, int32_t ebx, int32_t ecx, int32_t edx, int32_t esi, int32_t edi, int32_t ebp); -void game_command_demolish_ride( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); money32 ride_create_command(int32_t type, int32_t subType, int32_t flags, ride_id_t* outRideIndex, uint8_t* outRideColour); -void ride_set_name_to_default(Ride* ride, rct_ride_entry* rideEntry); -void ride_set_name_to_track_default(Ride* ride, rct_ride_entry* rideEntry); void ride_clear_for_construction(Ride* ride); void ride_entrance_exit_place_provisional_ghost(); void ride_entrance_exit_remove_ghost(); @@ -1088,18 +1206,9 @@ int32_t get_turn_count_3_elements(Ride* ride, uint8_t type); int32_t get_turn_count_4_plus_elements(Ride* ride, uint8_t type); uint8_t ride_get_helix_sections(Ride* ride); -bool ride_has_spinning_tunnel(Ride* ride); -bool ride_has_water_splash(Ride* ride); -bool ride_has_rapids(Ride* ride); -bool ride_has_log_reverser(Ride* ride); -bool ride_has_waterfall(Ride* ride); -bool ride_has_whirlpool(Ride* ride); bool ride_type_has_flag(int32_t rideType, uint32_t flag); -bool ride_is_powered_launched(Ride* ride); -bool ride_is_block_sectioned(Ride* ride); bool ride_has_any_track_elements(const Ride* ride); -void ride_all_has_any_track_elements(bool* rideIndexArray); void ride_construction_set_default_next_piece(); @@ -1129,28 +1238,14 @@ void ride_fix_breakdown(Ride* ride, int32_t reliabilityIncreaseFactor); void ride_entry_get_train_layout(int32_t rideEntryIndex, int32_t numCarsPerTrain, uint8_t* trainLayout); uint8_t ride_entry_get_vehicle_at_position(int32_t rideEntryIndex, int32_t numCarsPerTrain, int32_t position); -void ride_update_max_vehicles(Ride* ride); void ride_update_vehicle_colours(Ride* ride); uint64_t ride_entry_get_supported_track_pieces(const rct_ride_entry* rideEntry); -void ride_set_ride_entry(Ride* ride, int32_t rideEntry); -void ride_set_num_vehicles(Ride* ride, int32_t numVehicles); -void ride_set_num_cars_per_vehicle(Ride* ride, int32_t numCarsPerVehicle); - enum class RideSetSetting : uint8_t; money32 set_operating_setting(ride_id_t rideId, RideSetSetting setting, uint8_t value); money32 set_operating_setting_nested(ride_id_t rideId, RideSetSetting setting, uint8_t value, uint8_t flags); -void game_command_set_ride_vehicles( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); - -void game_command_place_ride_entrance_or_exit( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); - -void ride_set_to_default_inspection_interval(Ride* ride); - void sub_6CB945(Ride* ride); -void ride_crash(Ride* ride, uint8_t vehicleIndex); void sub_6C94D8(); @@ -1167,9 +1262,7 @@ rct_vehicle* ride_get_broken_vehicle(Ride* ride); void window_ride_construction_do_station_check(); void window_ride_construction_do_entrance_exit_check(); -void ride_delete(Ride* ride); -void ride_renew(Ride* ride); -money16 ride_get_price(Ride* ride); +money16 ride_get_price(const Ride* ride); TileElement* get_station_platform(int32_t x, int32_t y, int32_t z, int32_t z_tolerance); bool ride_has_adjacent_station(Ride* ride); @@ -1188,7 +1281,6 @@ int32_t ride_get_entry_index(int32_t rideType, int32_t rideSubType); StationObject* ride_get_station_object(const Ride* ride); void ride_action_modify(Ride* ride, int32_t modifyType, int32_t flags); -void ride_stop_peeps_queuing(Ride* ride); LocationXY16 ride_get_rotated_coords(int16_t x, int16_t y, int16_t z); diff --git a/src/openrct2/ride/RideData.cpp b/src/openrct2/ride/RideData.cpp index 23aa033e92..aafe797fa6 100644 --- a/src/openrct2/ride/RideData.cpp +++ b/src/openrct2/ride/RideData.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -1423,7 +1423,7 @@ const rct_ride_entry_vehicle CableLiftVehicle = { /* .no_seating_rows = */ 0, /* .spinning_inertia = */ 0, /* .spinning_friction = */ 255, - /* .friction_sound_id = */ 0, + /* .friction_sound_id = */ SoundId::LiftClassic, /* .log_flume_reverser_vehicle_type = */ 0, /* .sound_range = */ 0, /* .double_sound_frequency = */ 0, @@ -1450,97 +1450,97 @@ const uint16_t RideCrookedHouseLength[1] = { /* rct2: 0x0097D7C8, 0x0097D7C9, 0x0097D7CA */ const rct_ride_lift_data RideLiftData[] = { - { SOUND_LIFT_3, 7, 7 }, // Spiral Roller coaster - { SOUND_LIFT_1, 4, 6 }, // Stand Up Coaster - { SOUND_LIFT_1, 4, 6 }, // Suspended Swinging - { SOUND_LIFT_7, 5, 7 }, // Inverted - { SOUND_LIFT_3, 4, 6 }, // Steel Mini Coaster - { 255, 5, 5 }, // Mini Railway - { 255, 5, 5 }, // Monorail - { SOUND_LIFT_3, 4, 5 }, // Mini Suspended Coaster - { 255, 5, 5 }, // Boat Hire - { SOUND_LIFT_1, 4, 5 }, // Wooden Wild Mine/Mouse - { SOUND_LIFT_1, 4, 5 }, // Steeplechase/Motorbike/Soap Box D - { 255, 5, 5 }, // Car Ride - { 255, 5, 5 }, // Launched Freefall - { SOUND_LIFT_3, 4, 5 }, // Bobsleigh Coaster - { 255, 5, 5 }, // Observation Tower - { SOUND_LIFT_1, 4, 6 }, // Looping Roller Coaster - { SOUND_LIFT_3, 4, 5 }, // Dinghy Slide - { SOUND_LIFT_4, 4, 6 }, // Mine Train Coaster - { 255, 5, 5 }, // Chairlift - { SOUND_LIFT_4, 4, 6 }, // Corkscrew Roller Coaster - { 255, 5, 5 }, // Maze - { 255, 5, 5 }, // Spiral Slide - { 255, 5, 5 }, // Go Karts - { 255, 5, 5 }, // Log Flume - { 255, 5, 5 }, // River Rapids - { 255, 5, 5 }, // Dodgems - { 255, 5, 5 }, // Pirate Ship - { 255, 5, 5 }, // Swinging Inverter Ship - { 255, 5, 5 }, // Food Stall - { 255, 5, 5 }, // (none) - { 255, 5, 5 }, // Drink Stall - { 255, 5, 5 }, // (none) - { 255, 5, 5 }, // Shop (all types) - { 255, 5, 5 }, // Merry Go Round - { 255, 5, 5 }, // Balloon Stall (maybe) - { 255, 5, 5 }, // Information Kiosk - { 255, 5, 5 }, // Bathroom - { 255, 5, 5 }, // Ferris Wheel - { 255, 5, 5 }, // Motion Simulator - { 255, 5, 5 }, // 3D Cinema - { 255, 5, 5 }, // Topspin - { 255, 5, 5 }, // Space Rings - { 255, 5, 5 }, // Reverse Freefall Coaster - { 255, 5, 5 }, // Elevator - { SOUND_LIFT_7, 4, 5 }, // Vertical Drop Roller Coaster - { 255, 5, 5 }, // ATM - { 255, 5, 5 }, // Twist - { 255, 5, 5 }, // Haunted House - { 255, 5, 5 }, // First Aid - { 255, 5, 5 }, // Circus Show - { 255, 5, 5 }, // Ghost Train - { SOUND_LIFT_7, 5, 8 }, // Twister Roller Coaster - { SOUND_LIFT_5, 5, 7 }, // Wooden Roller Coaster - { SOUND_LIFT_5, 3, 4 }, // Side-Friction Roller Coaster - { SOUND_LIFT_6, 4, 6 }, // Wild Mouse - { SOUND_LIFT_3, 4, 6 }, // Multi Dimension Coaster - { SOUND_LIFT_3, 4, 6 }, // (none) - { SOUND_LIFT_7, 4, 6 }, // Flying Roller Coaster - { SOUND_LIFT_7, 4, 6 }, // (none) - { SOUND_LIFT_1, 3, 4 }, // Virginia Reel - { 255, 5, 5 }, // Splash Boats - { 255, 5, 5 }, // Mini Helicopters - { SOUND_LIFT_1, 4, 6 }, // Lay-down Roller Coaster - { 255, 5, 5 }, // Suspended Monorail - { SOUND_LIFT_1, 4, 6 }, // (none) - { SOUND_LIFT_1, 3, 4 }, // Reverser Roller Coaster - { SOUND_LIFT_1, 4, 6 }, // Heartline Twister Roller Coaster - { 255, 5, 5 }, // Mini Golf - { SOUND_LIFT_1, 5, 8 }, // Giga Coaster - { 255, 5, 5 }, // Roto-Drop - { 255, 5, 5 }, // Flying Saucers - { 255, 5, 5 }, // Crooked House - { 255, 5, 5 }, // Monorail Cycles - { SOUND_LIFT_3, 4, 6 }, // Compact Inverted Coaster - { SOUND_LIFT_1, 4, 6 }, // Water Coaster - { 255, 5, 5 }, // Air Powered Vertical Coaster - { SOUND_LIFT_6, 4, 6 }, // Inverted Hairpin Coaster - { 255, 5, 5 }, // Magic Carpet - { 255, 5, 5 }, // Submarine Ride - { 255, 5, 5 }, // River Rafts - { 255, 5, 5 }, // (none) - { 255, 5, 5 }, // Enterprise - { 255, 5, 5 }, // (none) - { 255, 5, 5 }, // (none) - { 255, 5, 5 }, // (none) - { SOUND_LIFT_4, 4, 7 }, // (none) - { SOUND_LIFT_1, 4, 7 }, // Inverted Impulse Coaster - { SOUND_LIFT_1, 4, 6 }, // Mini Roller Coaster - { 255, 5, 5 }, // Mine Ride - { SOUND_LIFT_6, 4, 6 }, // (none) - { 255, 4, 6 } // LIM Launched Roller Coaster + { SoundId::LiftFrictionWheels, 7, 7 }, // Spiral Roller coaster + { SoundId::LiftClassic, 4, 6 }, // Stand Up Coaster + { SoundId::LiftClassic, 4, 6 }, // Suspended Swinging + { SoundId::LiftBM, 5, 7 }, // Inverted + { SoundId::LiftFrictionWheels, 4, 6 }, // Steel Mini Coaster + { SoundId::Null, 5, 5 }, // Mini Railway + { SoundId::Null, 5, 5 }, // Monorail + { SoundId::LiftFrictionWheels, 4, 5 }, // Mini Suspended Coaster + { SoundId::Null, 5, 5 }, // Boat Hire + { SoundId::LiftClassic, 4, 5 }, // Wooden Wild Mine/Mouse + { SoundId::LiftClassic, 4, 5 }, // Steeplechase/Motorbike/Soap Box D + { SoundId::Null, 5, 5 }, // Car Ride + { SoundId::Null, 5, 5 }, // Launched Freefall + { SoundId::LiftFrictionWheels, 4, 5 }, // Bobsleigh Coaster + { SoundId::Null, 5, 5 }, // Observation Tower + { SoundId::LiftClassic, 4, 6 }, // Looping Roller Coaster + { SoundId::LiftFrictionWheels, 4, 5 }, // Dinghy Slide + { SoundId::LiftArrow, 4, 6 }, // Mine Train Coaster + { SoundId::Null, 5, 5 }, // Chairlift + { SoundId::LiftArrow, 4, 6 }, // Corkscrew Roller Coaster + { SoundId::Null, 5, 5 }, // Maze + { SoundId::Null, 5, 5 }, // Spiral Slide + { SoundId::Null, 5, 5 }, // Go Karts + { SoundId::Null, 5, 5 }, // Log Flume + { SoundId::Null, 5, 5 }, // River Rapids + { SoundId::Null, 5, 5 }, // Dodgems + { SoundId::Null, 5, 5 }, // Pirate Ship + { SoundId::Null, 5, 5 }, // Swinging Inverter Ship + { SoundId::Null, 5, 5 }, // Food Stall + { SoundId::Null, 5, 5 }, // (none) + { SoundId::Null, 5, 5 }, // Drink Stall + { SoundId::Null, 5, 5 }, // (none) + { SoundId::Null, 5, 5 }, // Shop (all types) + { SoundId::Null, 5, 5 }, // Merry Go Round + { SoundId::Null, 5, 5 }, // Balloon Stall (maybe) + { SoundId::Null, 5, 5 }, // Information Kiosk + { SoundId::Null, 5, 5 }, // Bathroom + { SoundId::Null, 5, 5 }, // Ferris Wheel + { SoundId::Null, 5, 5 }, // Motion Simulator + { SoundId::Null, 5, 5 }, // 3D Cinema + { SoundId::Null, 5, 5 }, // Topspin + { SoundId::Null, 5, 5 }, // Space Rings + { SoundId::Null, 5, 5 }, // Reverse Freefall Coaster + { SoundId::Null, 5, 5 }, // Elevator + { SoundId::LiftBM, 4, 5 }, // Vertical Drop Roller Coaster + { SoundId::Null, 5, 5 }, // ATM + { SoundId::Null, 5, 5 }, // Twist + { SoundId::Null, 5, 5 }, // Haunted House + { SoundId::Null, 5, 5 }, // First Aid + { SoundId::Null, 5, 5 }, // Circus Show + { SoundId::Null, 5, 5 }, // Ghost Train + { SoundId::LiftBM, 5, 8 }, // Twister Roller Coaster + { SoundId::LiftWood, 5, 7 }, // Wooden Roller Coaster + { SoundId::LiftWood, 3, 4 }, // Side-Friction Roller Coaster + { SoundId::LiftWildMouse, 4, 6 }, // Wild Mouse + { SoundId::LiftFrictionWheels, 4, 6 }, // Multi Dimension Coaster + { SoundId::LiftFrictionWheels, 4, 6 }, // (none) + { SoundId::LiftBM, 4, 6 }, // Flying Roller Coaster + { SoundId::LiftBM, 4, 6 }, // (none) + { SoundId::LiftClassic, 3, 4 }, // Virginia Reel + { SoundId::Null, 5, 5 }, // Splash Boats + { SoundId::Null, 5, 5 }, // Mini Helicopters + { SoundId::LiftClassic, 4, 6 }, // Lay-down Roller Coaster + { SoundId::Null, 5, 5 }, // Suspended Monorail + { SoundId::LiftClassic, 4, 6 }, // (none) + { SoundId::LiftClassic, 3, 4 }, // Reverser Roller Coaster + { SoundId::LiftClassic, 4, 6 }, // Heartline Twister Roller Coaster + { SoundId::Null, 5, 5 }, // Mini Golf + { SoundId::LiftClassic, 5, 8 }, // Giga Coaster + { SoundId::Null, 5, 5 }, // Roto-Drop + { SoundId::Null, 5, 5 }, // Flying Saucers + { SoundId::Null, 5, 5 }, // Crooked House + { SoundId::Null, 5, 5 }, // Monorail Cycles + { SoundId::LiftFrictionWheels, 4, 6 }, // Compact Inverted Coaster + { SoundId::LiftClassic, 4, 6 }, // Water Coaster + { SoundId::Null, 5, 5 }, // Air Powered Vertical Coaster + { SoundId::LiftWildMouse, 4, 6 }, // Inverted Hairpin Coaster + { SoundId::Null, 5, 5 }, // Magic Carpet + { SoundId::Null, 5, 5 }, // Submarine Ride + { SoundId::Null, 5, 5 }, // River Rafts + { SoundId::Null, 5, 5 }, // (none) + { SoundId::Null, 5, 5 }, // Enterprise + { SoundId::Null, 5, 5 }, // (none) + { SoundId::Null, 5, 5 }, // (none) + { SoundId::Null, 5, 5 }, // (none) + { SoundId::LiftArrow, 4, 7 }, // (none) + { SoundId::LiftClassic, 4, 7 }, // Inverted Impulse Coaster + { SoundId::LiftClassic, 4, 6 }, // Mini Roller Coaster + { SoundId::Null, 5, 5 }, // Mine Ride + { SoundId::LiftWildMouse, 4, 6 }, // (none) + { SoundId::Null, 4, 6 } // LIM Launched Roller Coaster }; /** rct2: 0x0097D7CB */ diff --git a/src/openrct2/ride/RideData.h b/src/openrct2/ride/RideData.h index 80442b2ed0..6aedfd626e 100644 --- a/src/openrct2/ride/RideData.h +++ b/src/openrct2/ride/RideData.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -80,7 +80,7 @@ struct rct_ride_data_5 struct rct_ride_lift_data { - uint8_t sound_id; + SoundId sound_id; uint8_t minimum_speed; uint8_t maximum_speed; }; diff --git a/src/openrct2/ride/RideGroupManager.cpp b/src/openrct2/ride/RideGroupManager.cpp index ffc89f32c6..c219b757a3 100644 --- a/src/openrct2/ride/RideGroupManager.cpp +++ b/src/openrct2/ride/RideGroupManager.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -36,7 +36,7 @@ static constexpr const RideGroup ride_group_corkscrew_rc = { static constexpr const RideGroup ride_group_hypercoaster = { /*.RideType =*/RIDE_TYPE_CORKSCREW_ROLLER_COASTER, - /*.MaximumHeight =*/45, + /*.MaximumHeight =*/55, /*.AvailableTrackPieces =*/(1ULL << TRACK_STRAIGHT) | (1ULL << TRACK_STATION_END) | (1ULL << TRACK_LIFT_HILL) | (1ULL << TRACK_FLAT_ROLL_BANKING) | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_SLOPE_STEEP) | (1ULL << TRACK_SLOPE_CURVE) | (1ULL << TRACK_SLOPE_CURVE_STEEP) | (1ULL << TRACK_S_BEND) | (1ULL << TRACK_CURVE_SMALL) | (1ULL << TRACK_CURVE) @@ -132,9 +132,8 @@ static constexpr const RideGroup ride_group_spinning_wild_mouse = { /*.RideType =*/RIDE_TYPE_STEEL_WILD_MOUSE, /*.MaximumHeight =*/16, /*.AvailableTrackPieces =*/(1ULL << TRACK_STRAIGHT) | (1ULL << TRACK_STATION_END) | (1ULL << TRACK_LIFT_HILL) - | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_SLOPE_LONG) | (1ULL << TRACK_SLOPE_CURVE) | (1ULL << TRACK_CURVE_VERY_SMALL) - | (1ULL << TRACK_CURVE_SMALL) | (1ULL << TRACK_BRAKES) | (1ULL << TRACK_BLOCK_BRAKES) - | (1ULL << TRACK_ROTATION_CONTROL_TOGGLE), + | (1ULL << TRACK_SLOPE) | (1ULL << TRACK_SLOPE_LONG) | (1ULL << TRACK_CURVE_VERY_SMALL) | (1ULL << TRACK_CURVE_SMALL) + | (1ULL << TRACK_BRAKES) | (1ULL << TRACK_BLOCK_BRAKES) | (1ULL << TRACK_ROTATION_CONTROL_TOGGLE), /*.Naming =*/{ STR_SPINNING_WILD_MOUSE_GROUP, STR_SPINNING_WILD_MOUSE_GROUP_DESC }, /*.Flags =*/0, }; diff --git a/src/openrct2/ride/RideGroupManager.h b/src/openrct2/ride/RideGroupManager.h index d917788dfd..0d66fbe931 100644 --- a/src/openrct2/ride/RideGroupManager.h +++ b/src/openrct2/ride/RideGroupManager.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/RideRatings.cpp b/src/openrct2/ride/RideRatings.cpp index 8ed07476ad..26af55bf46 100644 --- a/src/openrct2/ride/RideRatings.cpp +++ b/src/openrct2/ride/RideRatings.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -65,9 +65,15 @@ enum PROXIMITY_COUNT }; +struct ShelteredEights +{ + uint8_t TrackShelteredEighths; + uint8_t TotalShelteredEighths; +}; + using ride_ratings_calculation = void (*)(Ride* ride); -rct_ride_rating_calc_data gRideRatingsCalcData; +RideRatingCalculationData gRideRatingsCalcData; static ride_ratings_calculation ride_ratings_get_calculate_func(uint8_t rideType); @@ -91,12 +97,11 @@ static void ride_ratings_add(rating_tuple* rating, int32_t excitement, int32_t i * processed will be overwritten. * Only purpose of this function currently is for testing. */ -void ride_ratings_update_ride(ride_id_t rideIndex) +void ride_ratings_update_ride(const Ride& ride) { - Ride* ride = get_ride(rideIndex); - if (ride->type != RIDE_TYPE_NULL && ride->status != RIDE_STATUS_CLOSED) + if (ride.status != RIDE_STATUS_CLOSED) { - gRideRatingsCalcData.current_ride = rideIndex; + gRideRatingsCalcData.current_ride = ride.id; gRideRatingsCalcData.state = RIDE_RATINGS_STATE_INITIALISE; while (gRideRatingsCalcData.state != RIDE_RATINGS_STATE_FIND_NEXT_RIDE) { @@ -156,8 +161,8 @@ static void ride_ratings_update_state_0() currentRide = 0; } - Ride* ride = get_ride(currentRide); - if (ride->type != RIDE_TYPE_NULL && ride->status != RIDE_STATUS_CLOSED) + auto ride = get_ride(currentRide); + if (ride != nullptr && ride->status != RIDE_STATUS_CLOSED) { gRideRatingsCalcData.state = RIDE_RATINGS_STATE_INITIALISE; } @@ -189,8 +194,8 @@ static void ride_ratings_update_state_1() static void ride_ratings_update_state_2() { const ride_id_t rideIndex = gRideRatingsCalcData.current_ride; - Ride* ride = get_ride(rideIndex); - if (ride->type == RIDE_TYPE_NULL || ride->status == RIDE_STATUS_CLOSED) + auto ride = get_ride(rideIndex); + if (ride == nullptr || ride->status == RIDE_STATUS_CLOSED) { gRideRatingsCalcData.state = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; return; @@ -202,8 +207,15 @@ static void ride_ratings_update_state_2() int32_t trackType = gRideRatingsCalcData.proximity_track_type; TileElement* tileElement = map_get_first_element_at(x, y); + if (tileElement == nullptr) + { + gRideRatingsCalcData.state = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + return; + } do { + if (tileElement->IsGhost()) + continue; if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) continue; if (tileElement->base_height != z) @@ -263,8 +275,8 @@ static void ride_ratings_update_state_2() */ static void ride_ratings_update_state_3() { - Ride* ride = get_ride(gRideRatingsCalcData.current_ride); - if (ride->type == RIDE_TYPE_NULL || ride->status == RIDE_STATUS_CLOSED) + auto ride = get_ride(gRideRatingsCalcData.current_ride); + if (ride == nullptr || ride->status == RIDE_STATUS_CLOSED) { gRideRatingsCalcData.state = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; return; @@ -293,8 +305,8 @@ static void ride_ratings_update_state_4() */ static void ride_ratings_update_state_5() { - Ride* ride = get_ride(gRideRatingsCalcData.current_ride); - if (ride->type == RIDE_TYPE_NULL || ride->status == RIDE_STATUS_CLOSED) + auto ride = get_ride(gRideRatingsCalcData.current_ride); + if (ride == nullptr || ride->status == RIDE_STATUS_CLOSED) { gRideRatingsCalcData.state = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; return; @@ -306,8 +318,15 @@ static void ride_ratings_update_state_5() int32_t trackType = gRideRatingsCalcData.proximity_track_type; TileElement* tileElement = map_get_first_element_at(x, y); + if (tileElement == nullptr) + { + gRideRatingsCalcData.state = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + return; + } do { + if (tileElement->IsGhost()) + continue; if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) continue; if (tileElement->base_height != z) @@ -352,9 +371,8 @@ static void ride_ratings_update_state_5() */ static void ride_ratings_begin_proximity_loop() { - const ride_id_t rideIndex = gRideRatingsCalcData.current_ride; - Ride* ride = get_ride(rideIndex); - if (ride->type == RIDE_TYPE_NULL || ride->status == RIDE_STATUS_CLOSED) + auto ride = get_ride(gRideRatingsCalcData.current_ride); + if (ride == nullptr || ride->status == RIDE_STATUS_CLOSED) { gRideRatingsCalcData.state = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; return; @@ -411,8 +429,13 @@ static void ride_ratings_score_close_proximity_in_direction(TileElement* inputTi return; TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { + if (tileElement->IsGhost()) + continue; + switch (tileElement->GetType()) { case TILE_ELEMENT_TYPE_SURFACE: @@ -460,8 +483,13 @@ static void ride_ratings_score_close_proximity_in_direction(TileElement* inputTi static void ride_ratings_score_close_proximity_loops_helper(TileElement* inputTileElement, int32_t x, int32_t y) { TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { + if (tileElement->IsGhost()) + continue; + switch (tileElement->GetType()) { case TILE_ELEMENT_TYPE_PATH: @@ -531,8 +559,13 @@ static void ride_ratings_score_close_proximity(TileElement* inputTileElement) int32_t x = gRideRatingsCalcData.proximity_x; int32_t y = gRideRatingsCalcData.proximity_y; TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { + if (tileElement->IsGhost()) + continue; + int32_t waterHeight; switch (tileElement->GetType()) { @@ -729,7 +762,7 @@ static void ride_ratings_calculate_value(Ride* ride) { int32_t months, multiplier, divisor, summand; }; - static const row age_table_new[] = { + static const row ageTableNew[] = { { 5, 3, 2, 0 }, // 1.5x { 13, 6, 5, 0 }, // 1.2x { 40, 1, 1, 0 }, // 1x @@ -743,7 +776,7 @@ static void ride_ratings_calculate_value(Ride* ride) }; #ifdef ORIGINAL_RATINGS - static const row age_table_old[] = { + static const row ageTableOld[] = { { 5, 1, 1, 30 }, // +30 { 13, 1, 1, 10 }, // +10 { 40, 1, 1, 0 }, // 1x @@ -773,27 +806,27 @@ static void ride_ratings_calculate_value(Ride* ride) monthsOld = gDateMonthsElapsed - ride->build_date; } - const row* age_table = age_table_new; - size_t table_size = std::size(age_table_new); + const row* ageTable = ageTableNew; + size_t tableSize = std::size(ageTableNew); #ifdef ORIGINAL_RATINGS - age_table = age_table_old; - table_size = std::size(age_table_old); + ageTable = ageTableOld; + tableSize = std::size(ageTableOld); #endif - row last_row = age_table[table_size - 1]; + row lastRow = ageTable[tableSize - 1]; // Ride is older than oldest age in the table? - if (monthsOld >= last_row.months) + if (monthsOld >= lastRow.months) { - value = (value * last_row.multiplier) / last_row.divisor + last_row.summand; + value = (value * lastRow.multiplier) / lastRow.divisor + lastRow.summand; } else { // Find the first hit in the table that matches this ride's age - for (size_t it = 0; it < table_size; it++) + for (size_t it = 0; it < tableSize; it++) { - row curr = age_table[it]; + row curr = ageTable[it]; if (monthsOld < curr.months) { @@ -804,14 +837,10 @@ static void ride_ratings_calculate_value(Ride* ride) } // Other ride of same type penalty - int32_t otherRidesOfSameType = 0; - Ride* ride2; - int32_t i; - FOR_ALL_RIDES (i, ride2) - { - if (ride2->type == ride->type && ride2->status == RIDE_STATUS_OPEN) - otherRidesOfSameType++; - } + const auto& rideManager = GetRideManager(); + auto otherRidesOfSameType = std::count_if(rideManager.begin(), rideManager.end(), [ride](const Ride& r) { + return r.status == RIDE_STATUS_OPEN && r.type == ride->type; + }); if (otherRidesOfSameType > 1) value -= value / 4; @@ -830,11 +859,11 @@ static uint16_t ride_compute_upkeep(Ride* ride) uint16_t upkeep = initialUpkeepCosts[ride->type]; uint16_t trackCost = costPerTrackPiece[ride->type]; - uint8_t dl = ride->drops; + uint8_t dropFactor = ride->drops; - dl = dl >> 6; - dl = dl & 3; - upkeep += trackCost * dl; + dropFactor >>= 6; + dropFactor &= 3; + upkeep += trackCost * dropFactor; uint32_t totalLength = ride_get_total_length(ride) >> 16; @@ -902,8 +931,8 @@ static uint16_t ride_compute_upkeep(Ride* ride) } // multiply by 5/8 - upkeep = upkeep * 10; - upkeep = upkeep >> 4; + upkeep *= 10; + upkeep >>= 4; return upkeep; } @@ -998,8 +1027,8 @@ static void ride_ratings_apply_intensity_penalty(rating_tuple* ratings) static void set_unreliability_factor(Ride* ride) { // The bigger the difference in lift speed and minimum the higher the unreliability - uint8_t lift_speed_adjustment = RideLiftData[ride->type].minimum_speed; - ride->unreliability_factor += (ride->lift_hill_speed - lift_speed_adjustment) * 2; + uint8_t minLiftSpeed = RideLiftData[ride->type].minimum_speed; + ride->unreliability_factor += (ride->lift_hill_speed - minLiftSpeed) * 2; } static uint32_t get_proximity_score_helper_1(uint16_t x, uint16_t max, uint32_t multiplier) @@ -1062,13 +1091,13 @@ static uint32_t ride_ratings_get_proximity_score() * Calculates how much of the track is sheltered in eighths. * rct2: 0x0065E72D */ -static int32_t get_num_of_sheltered_eighths(Ride* ride) +static ShelteredEights get_num_of_sheltered_eighths(Ride* ride) { int32_t totalLength = ride_get_total_length(ride); int32_t shelteredLength = ride->sheltered_length; int32_t lengthEighth = totalLength / 8; int32_t lengthCounter = lengthEighth; - int32_t numShelteredEighths = 0; + uint8_t numShelteredEighths = 0; for (int32_t i = 0; i < 7; i++) { if (shelteredLength >= lengthCounter) @@ -1078,36 +1107,36 @@ static int32_t get_num_of_sheltered_eighths(Ride* ride) } } - int32_t dh = numShelteredEighths; + uint8_t trackShelteredEighths = numShelteredEighths; rct_ride_entry* rideType = get_ride_entry(ride->subtype); if (rideType == nullptr) { - return 0; + return { 0, 0 }; } if (rideType->flags & RIDE_ENTRY_FLAG_COVERED_RIDE) numShelteredEighths = 7; - return (dh << 8) | numShelteredEighths; + return { trackShelteredEighths, numShelteredEighths }; } static rating_tuple get_flat_turns_rating(Ride* ride) { - int32_t no_3_plus_turns = get_turn_count_3_elements(ride, 0); - int32_t no_2_turns = get_turn_count_2_elements(ride, 0); - int32_t no_1_turns = get_turn_count_1_element(ride, 0); + int32_t num3PlusTurns = get_turn_count_3_elements(ride, 0); + int32_t num2Turns = get_turn_count_2_elements(ride, 0); + int32_t num1Turns = get_turn_count_1_element(ride, 0); rating_tuple rating; - rating.excitement = (no_3_plus_turns * 0x28000) >> 16; - rating.excitement += (no_2_turns * 0x30000) >> 16; - rating.excitement += (no_1_turns * 63421) >> 16; + rating.excitement = (num3PlusTurns * 0x28000) >> 16; + rating.excitement += (num2Turns * 0x30000) >> 16; + rating.excitement += (num1Turns * 63421) >> 16; - rating.intensity = (no_3_plus_turns * 81920) >> 16; - rating.intensity += (no_2_turns * 49152) >> 16; - rating.intensity += (no_1_turns * 21140) >> 16; + rating.intensity = (num3PlusTurns * 81920) >> 16; + rating.intensity += (num2Turns * 49152) >> 16; + rating.intensity += (num1Turns * 21140) >> 16; - rating.nausea = (no_3_plus_turns * 0x50000) >> 16; - rating.nausea += (no_2_turns * 0x32000) >> 16; - rating.nausea += (no_1_turns * 42281) >> 16; + rating.nausea = (num3PlusTurns * 0x50000) >> 16; + rating.nausea += (num2Turns * 0x32000) >> 16; + rating.nausea += (num1Turns * 42281) >> 16; return rating; } @@ -1118,22 +1147,22 @@ static rating_tuple get_flat_turns_rating(Ride* ride) */ static rating_tuple get_banked_turns_rating(Ride* ride) { - int32_t no_3_plus_turns = get_turn_count_3_elements(ride, 1); - int32_t no_2_turns = get_turn_count_2_elements(ride, 1); - int32_t no_1_turns = get_turn_count_1_element(ride, 1); + int32_t num3PlusTurns = get_turn_count_3_elements(ride, 1); + int32_t num2Turns = get_turn_count_2_elements(ride, 1); + int32_t num1Turns = get_turn_count_1_element(ride, 1); rating_tuple rating; - rating.excitement = (no_3_plus_turns * 0x3C000) >> 16; - rating.excitement += (no_2_turns * 0x3C000) >> 16; - rating.excitement += (no_1_turns * 73992) >> 16; + rating.excitement = (num3PlusTurns * 0x3C000) >> 16; + rating.excitement += (num2Turns * 0x3C000) >> 16; + rating.excitement += (num1Turns * 73992) >> 16; - rating.intensity = (no_3_plus_turns * 0x14000) >> 16; - rating.intensity += (no_2_turns * 49152) >> 16; - rating.intensity += (no_1_turns * 21140) >> 16; + rating.intensity = (num3PlusTurns * 0x14000) >> 16; + rating.intensity += (num2Turns * 49152) >> 16; + rating.intensity += (num1Turns * 21140) >> 16; - rating.nausea = (no_3_plus_turns * 0x50000) >> 16; - rating.nausea += (no_2_turns * 0x32000) >> 16; - rating.nausea += (no_1_turns * 48623) >> 16; + rating.nausea = (num3PlusTurns * 0x50000) >> 16; + rating.nausea += (num2Turns * 0x32000) >> 16; + rating.nausea += (num1Turns * 48623) >> 16; return rating; } @@ -1146,17 +1175,17 @@ static rating_tuple get_sloped_turns_rating(Ride* ride) { rating_tuple rating; - int32_t no_4_plus_turns = get_turn_count_4_plus_elements(ride, 2); - int32_t no_3_turns = get_turn_count_3_elements(ride, 2); - int32_t no_2_turns = get_turn_count_2_elements(ride, 2); - int32_t no_1_turns = get_turn_count_1_element(ride, 2); + int32_t num4PlusTurns = get_turn_count_4_plus_elements(ride, 2); + int32_t num3Turns = get_turn_count_3_elements(ride, 2); + int32_t num2Turns = get_turn_count_2_elements(ride, 2); + int32_t num1Turns = get_turn_count_1_element(ride, 2); - rating.excitement = (std::min(no_4_plus_turns, 4) * 0x78000) >> 16; - rating.excitement += (std::min(no_3_turns, 6) * 273066) >> 16; - rating.excitement += (std::min(no_2_turns, 6) * 0x3AAAA) >> 16; - rating.excitement += (std::min(no_1_turns, 7) * 187245) >> 16; + rating.excitement = (std::min(num4PlusTurns, 4) * 0x78000) >> 16; + rating.excitement += (std::min(num3Turns, 6) * 273066) >> 16; + rating.excitement += (std::min(num2Turns, 6) * 0x3AAAA) >> 16; + rating.excitement += (std::min(num1Turns, 7) * 187245) >> 16; rating.intensity = 0; - rating.nausea = (std::min(no_4_plus_turns, 8) * 0x78000) >> 16; + rating.nausea = (std::min(num4PlusTurns, 8) * 0x78000) >> 16; return rating; } @@ -1165,7 +1194,7 @@ static rating_tuple get_sloped_turns_rating(Ride* ride) * * rct2: 0x0065E0F2 */ -static rating_tuple get_inversions_ratings(uint8_t inversions) +static rating_tuple get_inversions_ratings(uint16_t inversions) { rating_tuple rating; @@ -1179,9 +1208,10 @@ static rating_tuple get_inversions_ratings(uint8_t inversions) static rating_tuple get_special_track_elements_rating(uint8_t type, Ride* ride) { int32_t excitement = 0, intensity = 0, nausea = 0; + if (type == RIDE_TYPE_GHOST_TRAIN) { - if (ride_has_spinning_tunnel(ride)) + if (ride->HasSpinningTunnel()) { excitement += 40; intensity += 25; @@ -1190,8 +1220,7 @@ static rating_tuple get_special_track_elements_rating(uint8_t type, Ride* ride) } else if (type == RIDE_TYPE_LOG_FLUME) { - // Reverser for log flume - if (ride_has_log_reverser(ride)) + if (ride->HasLogReverser()) { excitement += 48; intensity += 55; @@ -1200,34 +1229,35 @@ static rating_tuple get_special_track_elements_rating(uint8_t type, Ride* ride) } else { - if (ride_has_water_splash(ride)) + if (ride->HasWaterSplash()) { excitement += 50; intensity += 30; nausea += 20; } - if (ride_has_waterfall(ride)) + if (ride->HasWaterfall()) { excitement += 55; intensity += 30; } - if (ride_has_whirlpool(ride)) + if (ride->HasWhirlpool()) { excitement += 35; intensity += 20; nausea += 23; } } - uint8_t helix_sections = ride_get_helix_sections(ride); - int32_t al = std::min(helix_sections, 9); - excitement += (al * 254862) >> 16; - al = std::min(helix_sections, 11); - intensity += (al * 148945) >> 16; + uint8_t helixSections = ride_get_helix_sections(ride); - al = std::max(helix_sections - 5, 0); - al = std::min(al, 10); - nausea += (al * 0x140000) >> 16; + int32_t helixesUpTo9 = std::min(helixSections, 9); + excitement += (helixesUpTo9 * 254862) >> 16; + + int32_t helixesUpTo11 = std::min(helixSections, 11); + intensity += (helixesUpTo11 * 148945) >> 16; + + int32_t helixesOver5UpTo10 = std::clamp(helixSections - 5, 0, 10); + nausea += (helixesOver5UpTo10 * 0x140000) >> 16; rating_tuple rating = { (ride_rating)excitement, (ride_rating)intensity, (ride_rating)nausea }; return rating; @@ -1241,30 +1271,31 @@ static rating_tuple ride_ratings_get_turns_ratings(Ride* ride) { int32_t excitement = 0, intensity = 0, nausea = 0; - rating_tuple special_track_element_rating = get_special_track_elements_rating(ride->type, ride); - excitement += special_track_element_rating.excitement; - intensity += special_track_element_rating.intensity; - nausea += special_track_element_rating.nausea; + rating_tuple specialTrackElementsRating = get_special_track_elements_rating(ride->type, ride); + excitement += specialTrackElementsRating.excitement; + intensity += specialTrackElementsRating.intensity; + nausea += specialTrackElementsRating.nausea; - rating_tuple var_10E_rating = get_flat_turns_rating(ride); - excitement += var_10E_rating.excitement; - intensity += var_10E_rating.intensity; - nausea += var_10E_rating.nausea; + rating_tuple flatTurnsRating = get_flat_turns_rating(ride); + excitement += flatTurnsRating.excitement; + intensity += flatTurnsRating.intensity; + nausea += flatTurnsRating.nausea; - rating_tuple var_110_rating = get_banked_turns_rating(ride); - excitement += var_110_rating.excitement; - intensity += var_110_rating.intensity; - nausea += var_110_rating.nausea; + rating_tuple bankedTurnsRating = get_banked_turns_rating(ride); + excitement += bankedTurnsRating.excitement; + intensity += bankedTurnsRating.intensity; + nausea += bankedTurnsRating.nausea; - rating_tuple var_112_rating = get_sloped_turns_rating(ride); - excitement += var_112_rating.excitement; - intensity += var_112_rating.intensity; - nausea += var_112_rating.nausea; + rating_tuple slopedTurnsRating = get_sloped_turns_rating(ride); + excitement += slopedTurnsRating.excitement; + intensity += slopedTurnsRating.intensity; + nausea += slopedTurnsRating.nausea; - rating_tuple inversions_rating = get_inversions_ratings(ride->inversions & 0x1F); - excitement += inversions_rating.excitement; - intensity += inversions_rating.intensity; - nausea += inversions_rating.nausea; + auto inversions = (ride->type == RIDE_TYPE_MINI_GOLF) ? ride->holes : ride->inversions; + rating_tuple inversionsRating = get_inversions_ratings(inversions); + excitement += inversionsRating.excitement; + intensity += inversionsRating.intensity; + nausea += inversionsRating.nausea; rating_tuple rating = { (ride_rating)excitement, (ride_rating)intensity, (ride_rating)nausea }; return rating; @@ -1276,15 +1307,14 @@ static rating_tuple ride_ratings_get_turns_ratings(Ride* ride) */ static rating_tuple ride_ratings_get_sheltered_ratings(Ride* ride) { - int32_t sheltered_length_shifted = (ride->sheltered_length) >> 16; - uint32_t eax = std::min(sheltered_length_shifted, 1000); - int32_t excitement = (eax * 9175) >> 16; + int32_t shelteredLengthShifted = (ride->sheltered_length) >> 16; - eax = std::min(sheltered_length_shifted, 2000); - int32_t intensity = (eax * 0x2666) >> 16; + uint32_t shelteredLengthUpTo1000 = std::min(shelteredLengthShifted, 1000); + uint32_t shelteredLengthUpTo2000 = std::min(shelteredLengthShifted, 2000); - eax = std::min(sheltered_length_shifted, 1000); - int32_t nausea = (eax * 0x4000) >> 16; + int32_t excitement = (shelteredLengthUpTo1000 * 9175) >> 16; + int32_t intensity = (shelteredLengthUpTo2000 * 0x2666) >> 16; + int32_t nausea = (shelteredLengthUpTo1000 * 0x4000) >> 16; /*eax = (ride->var_11C * 30340) >> 16;*/ /*nausea += eax;*/ @@ -1301,9 +1331,9 @@ static rating_tuple ride_ratings_get_sheltered_ratings(Ride* ride) nausea += 15; } - uint8_t lowerval = ride->num_sheltered_sections & 0x1F; - lowerval = std::min(lowerval, 11); - excitement += (lowerval * 774516) >> 16; + uint8_t lowerVal = ride->num_sheltered_sections & 0x1F; + lowerVal = std::min(lowerVal, 11); + excitement += (lowerVal * 774516) >> 16; rating_tuple rating = { (ride_rating)excitement, (ride_rating)intensity, (ride_rating)nausea }; return rating; @@ -1408,7 +1438,7 @@ static int32_t ride_ratings_get_scenery_score(Ride* ride) y = location.y; } - int32_t z = tile_element_height(x * 32, y * 32) & 0xFFFF; + int32_t z = tile_element_height({ x * 32, y * 32 }); // Check if station is underground, returns a fixed mediocre score since you can't have scenery underground if (z > ride->stations[i].Height * 8) @@ -1424,6 +1454,8 @@ static int32_t ride_ratings_get_scenery_score(Ride* ride) { // Count scenery items on this tile TileElement* tileElement = map_get_first_element_at(xx, yy); + if (tileElement == nullptr) + continue; do { if (tileElement->IsGhost()) @@ -1623,18 +1655,44 @@ static void ride_ratings_apply_max_lateral_g_penalty( } } -static void ride_ratings_apply_excessive_lateral_g_penalty(rating_tuple* ratings, Ride* ride) +static rating_tuple ride_ratings_get_excessive_lateral_g_penalty(Ride* ride) { -#ifndef ORIGINAL_RATINGS + rating_tuple result{}; if (ride->max_lateral_g > FIXED_2DP(2, 80)) { - ride_ratings_add(ratings, 0, FIXED_2DP(3, 75), FIXED_2DP(2, 00)); + result.intensity = FIXED_2DP(3, 75); + result.nausea = FIXED_2DP(2, 00); } + if (ride->max_lateral_g > FIXED_2DP(3, 10)) { - ratings->excitement /= 2; - ride_ratings_add(ratings, 0, FIXED_2DP(8, 50), FIXED_2DP(4, 00)); + // Remove half of the ride_ratings_get_gforce_ratings + result.excitement = (ride->max_positive_vertical_g * 5242) >> 16; + + // Apply maximum negative G force factor + fixed16_2dp gforce = ride->max_negative_vertical_g; + result.excitement += (std::clamp(gforce, -FIXED_2DP(2, 50), FIXED_2DP(0, 00)) * -15728) >> 16; + + // Apply lateral G force factor + result.excitement += (std::min(FIXED_2DP(1, 50), ride->max_lateral_g) * 26214) >> 16; + + // Remove half of the ride_ratings_get_gforce_ratings + result.excitement /= 2; + result.excitement *= -1; + result.intensity = FIXED_2DP(12, 25); + result.nausea = FIXED_2DP(6, 00); } + return result; +} + +static void ride_ratings_apply_excessive_lateral_g_penalty( + rating_tuple* ratings, Ride* ride, int32_t excitementMultiplier, int32_t intensityMultiplier, int32_t nauseaMultiplier) +{ +#ifndef ORIGINAL_RATINGS + rating_tuple subRating = ride_ratings_get_excessive_lateral_g_penalty(ride); + ride_ratings_add( + ratings, (subRating.excitement * excitementMultiplier) >> 16, (subRating.intensity * intensityMultiplier) >> 16, + (subRating.nausea * nauseaMultiplier) >> 16); #endif } @@ -1677,18 +1735,18 @@ static void ride_ratings_calculate_spiral_roller_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 20130); ride_ratings_apply_scenery(&ratings, ride, 6693); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_highest_drop_height_penalty(&ratings, ride, 12, 2, 2, 2); ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) { ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 40), 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); } - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 36864, 30384, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -1697,8 +1755,7 @@ static void ride_ratings_calculate_spiral_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_stand_up_roller_coaster(Ride* ride) @@ -1727,7 +1784,7 @@ static void ride_ratings_calculate_stand_up_roller_coaster(Ride* ride) ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 50), 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 35746, 59578); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -1736,8 +1793,7 @@ static void ride_ratings_calculate_stand_up_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_suspended_swinging_coaster(Ride* ride) @@ -1768,7 +1824,7 @@ static void ride_ratings_calculate_suspended_swinging_coaster(Ride* ride) ride_ratings_apply_max_lateral_g_penalty(&ratings, ride, FIXED_2DP(1, 50), 2, 2, 2); ride_ratings_apply_first_length_penalty(&ratings, ride, 0x1720000, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 32768, 23831, 79437); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -1777,8 +1833,7 @@ static void ride_ratings_calculate_suspended_swinging_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_inverted_roller_coaster(Ride* ride) @@ -1804,15 +1859,15 @@ static void ride_ratings_calculate_inverted_roller_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 15657); ride_ratings_apply_scenery(&ratings, ride, 8366); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_highest_drop_height_penalty(&ratings, ride, 12, 2, 2, 2); ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 30), 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 29789, 55606); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -1821,8 +1876,7 @@ static void ride_ratings_calculate_inverted_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_junior_roller_coaster(Ride* ride) @@ -1851,7 +1905,7 @@ static void ride_ratings_calculate_junior_roller_coaster(Ride* ride) ride_ratings_apply_max_speed_penalty(&ratings, ride, 0x70000, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 1, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 20480, 23831, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -1860,8 +1914,7 @@ static void ride_ratings_calculate_junior_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_miniature_railway(Ride* ride) @@ -1892,12 +1945,11 @@ static void ride_ratings_calculate_miniature_railway(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - int32_t edx = get_num_of_sheltered_eighths(ride); - if (((edx >> 8) & 0xFF) >= 4) + auto shelteredEighths = get_num_of_sheltered_eighths(ride); + if (shelteredEighths.TrackShelteredEighths >= 4) ride->excitement /= 4; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = shelteredEighths.TotalShelteredEighths; } static void ride_ratings_calculate_monorail(Ride* ride) @@ -1928,12 +1980,11 @@ static void ride_ratings_calculate_monorail(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - int32_t edx = get_num_of_sheltered_eighths(ride); - if (((edx >> 8) & 0xFF) >= 4) + auto shelteredEighths = get_num_of_sheltered_eighths(ride); + if (shelteredEighths.TrackShelteredEighths >= 4) ride->excitement /= 4; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = shelteredEighths.TotalShelteredEighths; } static void ride_ratings_calculate_mini_suspended_coaster(Ride* ride) @@ -1963,7 +2014,7 @@ static void ride_ratings_calculate_mini_suspended_coaster(Ride* ride) ride_ratings_apply_max_lateral_g_penalty(&ratings, ride, FIXED_2DP(1, 30), 2, 2, 2); ride_ratings_apply_first_length_penalty(&ratings, ride, 0xC80000, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 35746, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -1972,8 +2023,7 @@ static void ride_ratings_calculate_mini_suspended_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_boat_hire(Ride* ride) @@ -1981,9 +2031,9 @@ static void ride_ratings_calculate_boat_hire(Ride* ride) ride->unreliability_factor = 7; set_unreliability_factor(ride); - // NOTE In the original game, the ratings were zeroed before calling set_unreliability_factor which is unusual as rest of - // the calculation functions do this before hand. This is because set_unreliability_factor alters the value of - // ebx (excitement). This is assumed to be a bug and therefore fixed. + // NOTE In the original game, the ratings were zeroed before calling set_unreliability_factor which is unusual as rest + // of the calculation functions do this before hand. This is because set_unreliability_factor alters the value of ebx + // (excitement). This is assumed to be a bug and therefore fixed. rating_tuple ratings; ride_ratings_set(&ratings, RIDE_RATING(1, 90), RIDE_RATING(0, 80), RIDE_RATING(0, 90)); @@ -2005,8 +2055,7 @@ static void ride_ratings_calculate_boat_hire(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + ride->sheltered_eighths = 0; } static void ride_ratings_calculate_wooden_wild_mouse(Ride* ride) @@ -2038,7 +2087,7 @@ static void ride_ratings_calculate_wooden_wild_mouse(Ride* ride) ride_ratings_apply_first_length_penalty(&ratings, ride, 0xAA0000, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 3, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 102400, 35746, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -2047,8 +2096,7 @@ static void ride_ratings_calculate_wooden_wild_mouse(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_steeplechase(Ride* ride) @@ -2079,7 +2127,7 @@ static void ride_ratings_calculate_steeplechase(Ride* ride) ride_ratings_apply_first_length_penalty(&ratings, ride, 0xF00000, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 20480, 20852, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -2088,8 +2136,7 @@ static void ride_ratings_calculate_steeplechase(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_car_ride(Ride* ride) @@ -2123,8 +2170,7 @@ static void ride_ratings_calculate_car_ride(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_launched_freefall(Ride* ride) @@ -2175,8 +2221,7 @@ static void ride_ratings_calculate_launched_freefall(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_bobsleigh_coaster(Ride* ride) @@ -2205,7 +2250,7 @@ static void ride_ratings_calculate_bobsleigh_coaster(Ride* ride) ride_ratings_apply_max_lateral_g_penalty(&ratings, ride, FIXED_2DP(1, 20), 2, 2, 2); ride_ratings_apply_first_length_penalty(&ratings, ride, 0x1720000, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 65536, 23831, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -2214,8 +2259,7 @@ static void ride_ratings_calculate_bobsleigh_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_observation_tower(Ride* ride) @@ -2241,11 +2285,10 @@ static void ride_ratings_calculate_observation_tower(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 7 << 5; + ride->sheltered_eighths = 7; - int32_t edx = get_num_of_sheltered_eighths(ride); - if (((edx >> 8) & 0xFF) >= 5) + auto shelteredEighths = get_num_of_sheltered_eighths(ride); + if (shelteredEighths.TrackShelteredEighths >= 5) ride->excitement /= 4; } @@ -2254,7 +2297,7 @@ static void ride_ratings_calculate_looping_roller_coaster(Ride* ride) if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) return; - ride->unreliability_factor = ride_is_powered_launched(ride) ? 20 : 15; + ride->unreliability_factor = ride->IsPoweredLaunched() ? 20 : 15; set_unreliability_factor(ride); rating_tuple ratings; @@ -2272,18 +2315,18 @@ static void ride_ratings_calculate_looping_roller_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 20130); ride_ratings_apply_scenery(&ratings, ride, 6693); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_highest_drop_height_penalty(&ratings, ride, 14, 2, 2, 2); ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) { ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 10), 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); } - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 35746, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -2292,8 +2335,7 @@ static void ride_ratings_calculate_looping_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_dinghy_slide(Ride* ride) @@ -2322,7 +2364,7 @@ static void ride_ratings_calculate_dinghy_slide(Ride* ride) ride_ratings_apply_max_speed_penalty(&ratings, ride, 0x70000, 2, 2, 2); ride_ratings_apply_first_length_penalty(&ratings, ride, 0x8C0000, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 65536, 29789, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -2331,8 +2373,7 @@ static void ride_ratings_calculate_dinghy_slide(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_mine_train_coaster(Ride* ride) @@ -2363,7 +2404,7 @@ static void ride_ratings_calculate_mine_train_coaster(Ride* ride) ride_ratings_apply_first_length_penalty(&ratings, ride, 0x1720000, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 40960, 35746, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -2372,8 +2413,7 @@ static void ride_ratings_calculate_mine_train_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_chairlift(Ride* ride) @@ -2411,12 +2451,11 @@ static void ride_ratings_calculate_chairlift(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - int32_t edx = get_num_of_sheltered_eighths(ride); - if (((edx >> 8) & 0xFF) >= 4) + auto shelteredEighths = get_num_of_sheltered_eighths(ride); + if (shelteredEighths.TrackShelteredEighths >= 4) ride->excitement /= 4; - ride->inversions &= 0x1F; - ride->inversions |= edx << 5; + ride->sheltered_eighths = shelteredEighths.TotalShelteredEighths; } static void ride_ratings_calculate_corkscrew_roller_coaster(Ride* ride) @@ -2442,18 +2481,18 @@ static void ride_ratings_calculate_corkscrew_roller_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 20130); ride_ratings_apply_scenery(&ratings, ride, 6693); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_highest_drop_height_penalty(&ratings, ride, 12, 2, 2, 2); ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) { ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 40), 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); } - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 35746, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -2462,8 +2501,7 @@ static void ride_ratings_calculate_corkscrew_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_maze(Ride* ride) @@ -2489,8 +2527,7 @@ static void ride_ratings_calculate_maze(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + ride->sheltered_eighths = 0; } static void ride_ratings_calculate_spiral_slide(Ride* ride) @@ -2519,8 +2556,7 @@ static void ride_ratings_calculate_spiral_slide(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 2 << 5; + ride->sheltered_eighths = 2; } static void ride_ratings_calculate_go_karts(Ride* ride) @@ -2557,12 +2593,10 @@ static void ride_ratings_calculate_go_karts(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - int32_t edx = get_num_of_sheltered_eighths(ride); + auto shelteredEighths = get_num_of_sheltered_eighths(ride); + ride->sheltered_eighths = shelteredEighths.TotalShelteredEighths; - ride->inversions &= 0x1F; - ride->inversions |= edx << 5; - - if (((edx >> 8) & 0xFF) >= 6) + if (shelteredEighths.TrackShelteredEighths >= 6) ride->excitement /= 2; } @@ -2595,8 +2629,7 @@ static void ride_ratings_calculate_log_flume(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_river_rapids(Ride* ride) @@ -2629,8 +2662,7 @@ static void ride_ratings_calculate_river_rapids(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_dodgems(Ride* ride) @@ -2665,8 +2697,7 @@ static void ride_ratings_calculate_dodgems(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 7 << 5; + ride->sheltered_eighths = 7; } static void ride_ratings_calculate_pirate_ship(Ride* ride) @@ -2691,8 +2722,7 @@ static void ride_ratings_calculate_pirate_ship(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + ride->sheltered_eighths = 0; } static void ride_ratings_calculate_inverter_ship(Ride* ride) @@ -2717,8 +2747,7 @@ static void ride_ratings_calculate_inverter_ship(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + ride->sheltered_eighths = 0; } static void ride_ratings_calculate_food_stall(Ride* ride) @@ -2758,8 +2787,7 @@ static void ride_ratings_calculate_merry_go_round(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 7 << 5; + ride->sheltered_eighths = 7; } static void ride_ratings_calculate_information_kiosk(Ride* ride) @@ -2793,8 +2821,7 @@ static void ride_ratings_calculate_ferris_wheel(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + ride->sheltered_eighths = 0; } static void ride_ratings_calculate_motion_simulator(Ride* ride) @@ -2827,8 +2854,7 @@ static void ride_ratings_calculate_motion_simulator(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 7 << 5; + ride->sheltered_eighths = 7; } static void ride_ratings_calculate_3d_cinema(Ride* ride) @@ -2868,8 +2894,7 @@ static void ride_ratings_calculate_3d_cinema(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 7 << 5; + ride->sheltered_eighths |= 7; } static void ride_ratings_calculate_top_spin(Ride* ride) @@ -2911,8 +2936,7 @@ static void ride_ratings_calculate_top_spin(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + ride->sheltered_eighths = 0; } static void ride_ratings_calculate_space_rings(Ride* ride) @@ -2934,8 +2958,7 @@ static void ride_ratings_calculate_space_rings(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + ride->sheltered_eighths = 0; } static void ride_ratings_calculate_reverse_freefall_coaster(Ride* ride) @@ -2965,8 +2988,7 @@ static void ride_ratings_calculate_reverse_freefall_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_lift(Ride* ride) @@ -2996,10 +3018,9 @@ static void ride_ratings_calculate_lift(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 7 << 5; + ride->sheltered_eighths = 7; - if ((get_num_of_sheltered_eighths(ride) >> 8) >= 5) + if ((get_num_of_sheltered_eighths(ride).TrackShelteredEighths) >= 5) ride->excitement /= 4; } @@ -3029,7 +3050,7 @@ static void ride_ratings_calculate_vertical_drop_roller_coaster(Ride* ride) ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 10), 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 1, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 40960, 35746, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3038,8 +3059,7 @@ static void ride_ratings_calculate_vertical_drop_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_cash_machine(Ride* ride) @@ -3067,8 +3087,7 @@ static void ride_ratings_calculate_twist(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + ride->sheltered_eighths = 0; } static void ride_ratings_calculate_haunted_house(Ride* ride) @@ -3092,8 +3111,7 @@ static void ride_ratings_calculate_haunted_house(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0xE0; + ride->sheltered_eighths = 7; } static void ride_ratings_calculate_flying_roller_coaster(Ride* ride) @@ -3119,17 +3137,17 @@ static void ride_ratings_calculate_flying_roller_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 20130); ride_ratings_apply_scenery(&ratings, ride, 6693); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ratings.excitement /= 2; ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 1, 1); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 40), 2, 1, 1); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 1, 1); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 38130, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3138,8 +3156,7 @@ static void ride_ratings_calculate_flying_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_virginia_reel(Ride* ride) @@ -3167,7 +3184,7 @@ static void ride_ratings_calculate_virginia_reel(Ride* ride) ride_ratings_apply_first_length_penalty(&ratings, ride, 0xD20000, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 110592, 29789, 59578); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3176,8 +3193,7 @@ static void ride_ratings_calculate_virginia_reel(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_splash_boats(Ride* ride) @@ -3209,8 +3225,7 @@ static void ride_ratings_calculate_splash_boats(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_mini_helicopters(Ride* ride) @@ -3244,8 +3259,7 @@ static void ride_ratings_calculate_mini_helicopters(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 6 << 5; + ride->sheltered_eighths = 6; } static void ride_ratings_calculate_lay_down_roller_coaster(Ride* ride) @@ -3271,7 +3285,7 @@ static void ride_ratings_calculate_lay_down_roller_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 20130); ride_ratings_apply_scenery(&ratings, ride, 6693); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) { ratings.excitement /= 4; ratings.intensity /= 2; @@ -3280,13 +3294,13 @@ static void ride_ratings_calculate_lay_down_roller_coaster(Ride* ride) ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) { ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 40), 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); } - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 38130, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3295,8 +3309,7 @@ static void ride_ratings_calculate_lay_down_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_suspended_monorail(Ride* ride) @@ -3327,12 +3340,11 @@ static void ride_ratings_calculate_suspended_monorail(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - int32_t edx = get_num_of_sheltered_eighths(ride); - if (((edx >> 8) & 0xFF) >= 4) + auto shelteredEighths = get_num_of_sheltered_eighths(ride); + if (shelteredEighths.TrackShelteredEighths >= 4) ride->excitement /= 4; - ride->inversions &= 0x1F; - ride->inversions |= edx << 5; + ride->sheltered_eighths = shelteredEighths.TotalShelteredEighths; } static void ride_ratings_calculate_reverser_roller_coaster(Ride* ride) @@ -3371,7 +3383,7 @@ static void ride_ratings_calculate_reverser_roller_coaster(Ride* ride) ride_ratings_apply_first_length_penalty(&ratings, ride, 0xC80000, 2, 1, 1); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 1, 1); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 28672, 23831, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3380,8 +3392,7 @@ static void ride_ratings_calculate_reverser_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_heartline_twister_coaster(Ride* ride) @@ -3407,12 +3418,12 @@ static void ride_ratings_calculate_heartline_twister_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 9841); ride_ratings_apply_scenery(&ratings, ride, 3904); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ratings.excitement /= 4; ride_ratings_apply_num_drops_penalty(&ratings, ride, 1, 4, 1, 1); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 44683, 89367); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3421,8 +3432,7 @@ static void ride_ratings_calculate_heartline_twister_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_mini_golf(Ride* ride) @@ -3442,10 +3452,10 @@ static void ride_ratings_calculate_mini_golf(Ride* ride) ride_ratings_apply_scenery(&ratings, ride, 27887); // Apply golf holes factor - ride_ratings_add(&ratings, (ride->holes & 0x1F) * 5, 0, 0); + ride_ratings_add(&ratings, (ride->holes) * 5, 0, 0); // Apply no golf holes penalty - if ((ride->inversions & 0x1F) == 0) + if (ride->holes == 0) { ratings.excitement /= 8; ratings.intensity /= 2; @@ -3460,8 +3470,7 @@ static void ride_ratings_calculate_mini_golf(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_first_aid(Ride* ride) @@ -3491,8 +3500,7 @@ static void ride_ratings_calculate_circus_show(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 7 << 5; + ride->sheltered_eighths = 7; } static void ride_ratings_calculate_ghost_train(Ride* ride) @@ -3525,8 +3533,7 @@ static void ride_ratings_calculate_ghost_train(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_twister_roller_coaster(Ride* ride) @@ -3552,18 +3559,18 @@ static void ride_ratings_calculate_twister_roller_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 20130); ride_ratings_apply_scenery(&ratings, ride, 6693); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_highest_drop_height_penalty(&ratings, ride, 12, 2, 2, 2); ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) { ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 40), 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); } - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 32768, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3572,8 +3579,7 @@ static void ride_ratings_calculate_twister_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_wooden_roller_coaster(Ride* ride) @@ -3604,7 +3610,7 @@ static void ride_ratings_calculate_wooden_roller_coaster(Ride* ride) ride_ratings_apply_first_length_penalty(&ratings, ride, 0x1720000, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 40960, 34555, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3613,8 +3619,7 @@ static void ride_ratings_calculate_wooden_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_side_friction_roller_coaster(Ride* ride) @@ -3644,7 +3649,7 @@ static void ride_ratings_calculate_side_friction_roller_coaster(Ride* ride) ride_ratings_apply_first_length_penalty(&ratings, ride, 0xFA0000, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 28672, 35746, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3653,8 +3658,7 @@ static void ride_ratings_calculate_side_friction_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_wild_mouse(Ride* ride) @@ -3685,7 +3689,7 @@ static void ride_ratings_calculate_wild_mouse(Ride* ride) ride_ratings_apply_first_length_penalty(&ratings, ride, 0xAA0000, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 102400, 35746, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3694,8 +3698,7 @@ static void ride_ratings_calculate_wild_mouse(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_multi_dimension_roller_coaster(Ride* ride) @@ -3721,17 +3724,17 @@ static void ride_ratings_calculate_multi_dimension_roller_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 20130); ride_ratings_apply_scenery(&ratings, ride, 6693); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ratings.excitement /= 4; ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 1, 1); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 40), 2, 1, 1); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 1, 1); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 38130, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3740,8 +3743,7 @@ static void ride_ratings_calculate_multi_dimension_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_giga_coaster(Ride* ride) @@ -3767,18 +3769,18 @@ static void ride_ratings_calculate_giga_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 20130); ride_ratings_apply_scenery(&ratings, ride, 6693); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_highest_drop_height_penalty(&ratings, ride, 16, 2, 2, 2); ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) { ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 40), 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); } - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 36864, 30384, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3787,8 +3789,7 @@ static void ride_ratings_calculate_giga_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_roto_drop(Ride* ride) @@ -3816,8 +3817,7 @@ static void ride_ratings_calculate_roto_drop(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_flying_saucers(Ride* ride) @@ -3855,8 +3855,7 @@ static void ride_ratings_calculate_flying_saucers(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + ride->sheltered_eighths = 0; } static void ride_ratings_calculate_crooked_house(Ride* ride) @@ -3880,8 +3879,7 @@ static void ride_ratings_calculate_crooked_house(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0xE0; + ride->sheltered_eighths = 7; } static void ride_ratings_calculate_monorail_cycles(Ride* ride) @@ -3915,8 +3913,7 @@ static void ride_ratings_calculate_monorail_cycles(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_compact_inverted_coaster(Ride* ride) @@ -3942,15 +3939,15 @@ static void ride_ratings_calculate_compact_inverted_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 15657); ride_ratings_apply_scenery(&ratings, ride, 8366); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_highest_drop_height_penalty(&ratings, ride, 12, 2, 2, 2); ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 30), 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 30980, 55606); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -3959,8 +3956,7 @@ static void ride_ratings_calculate_compact_inverted_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_water_coaster(Ride* ride) @@ -3989,7 +3985,7 @@ static void ride_ratings_calculate_water_coaster(Ride* ride) ride_ratings_apply_max_speed_penalty(&ratings, ride, 0x70000, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 1, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 20480, 23831, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -4001,8 +3997,7 @@ static void ride_ratings_calculate_water_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_air_powered_vertical_coaster(Ride* ride) @@ -4024,7 +4019,7 @@ static void ride_ratings_calculate_air_powered_vertical_coaster(Ride* ride) ride_ratings_apply_scenery(&ratings, ride, 11155); ride_ratings_apply_highest_drop_height_penalty(&ratings, ride, 34, 2, 1, 1); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 35746, 59578); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -4033,8 +4028,7 @@ static void ride_ratings_calculate_air_powered_vertical_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_inverted_hairpin_coaster(Ride* ride) @@ -4066,7 +4060,7 @@ static void ride_ratings_calculate_inverted_hairpin_coaster(Ride* ride) ride_ratings_apply_first_length_penalty(&ratings, ride, 0xAA0000, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 3, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 102400, 35746, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -4075,8 +4069,7 @@ static void ride_ratings_calculate_inverted_hairpin_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_magic_carpet(Ride* ride) @@ -4101,8 +4094,7 @@ static void ride_ratings_calculate_magic_carpet(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + ride->sheltered_eighths = 0; } static void ride_ratings_calculate_submarine_ride(Ride* ride) @@ -4126,8 +4118,8 @@ static void ride_ratings_calculate_submarine_ride(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 0 << 5; + // Originally, this was always to zero, even though the default vehicle is completely enclosed. + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_river_rafts(Ride* ride) @@ -4157,8 +4149,7 @@ static void ride_ratings_calculate_river_rafts(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_enterprise(Ride* ride) @@ -4187,8 +4178,7 @@ static void ride_ratings_calculate_enterprise(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= 3 << 5; + ride->sheltered_eighths = 3; } static void ride_ratings_calculate_inverted_impulse_coaster(Ride* ride) @@ -4216,7 +4206,7 @@ static void ride_ratings_calculate_inverted_impulse_coaster(Ride* ride) ride_ratings_apply_highest_drop_height_penalty(&ratings, ride, 20, 2, 2, 2); ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 29789, 55606); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -4225,8 +4215,7 @@ static void ride_ratings_calculate_inverted_impulse_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_mini_roller_coaster(Ride* ride) @@ -4256,7 +4245,7 @@ static void ride_ratings_calculate_mini_roller_coaster(Ride* ride) ride_ratings_apply_max_negative_g_penalty(&ratings, ride, FIXED_2DP(0, 50), 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 20480, 23831, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -4265,8 +4254,7 @@ static void ride_ratings_calculate_mini_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_mine_ride(Ride* ride) @@ -4293,7 +4281,7 @@ static void ride_ratings_calculate_mine_ride(Ride* ride) ride_ratings_apply_scenery(&ratings, ride, 16732); ride_ratings_apply_first_length_penalty(&ratings, ride, 0x10E0000, 2, 2, 2); - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 40960, 29789, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -4302,8 +4290,7 @@ static void ride_ratings_calculate_mine_ride(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } static void ride_ratings_calculate_lim_launched_roller_coaster(Ride* ride) @@ -4329,18 +4316,18 @@ static void ride_ratings_calculate_lim_launched_roller_coaster(Ride* ride) ride_ratings_apply_proximity(&ratings, 20130); ride_ratings_apply_scenery(&ratings, ride, 6693); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) ride_ratings_apply_highest_drop_height_penalty(&ratings, ride, 10, 2, 2, 2); ride_ratings_apply_max_speed_penalty(&ratings, ride, 0xA0000, 2, 2, 2); - if ((ride->inversions & 0x1F) == 0) + if (ride->inversions == 0) { ride_ratings_apply_max_negative_g_penalty(&ratings, ride, 10, 2, 2, 2); ride_ratings_apply_num_drops_penalty(&ratings, ride, 2, 2, 2, 2); } - ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride); + ride_ratings_apply_excessive_lateral_g_penalty(&ratings, ride, 24576, 35746, 49648); ride_ratings_apply_intensity_penalty(&ratings); ride_ratings_apply_adjustments(ride, &ratings); @@ -4349,8 +4336,7 @@ static void ride_ratings_calculate_lim_launched_roller_coaster(Ride* ride) ride->upkeep_cost = ride_compute_upkeep(ride); ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME; - ride->inversions &= 0x1F; - ride->inversions |= get_num_of_sheltered_eighths(ride) << 5; + ride->sheltered_eighths = get_num_of_sheltered_eighths(ride).TotalShelteredEighths; } #pragma endregion @@ -4358,7 +4344,7 @@ static void ride_ratings_calculate_lim_launched_roller_coaster(Ride* ride) #pragma region Ride rating calculation function table // rct2: 0x0097E050 -static const ride_ratings_calculation ride_ratings_calculate_func_table[RIDE_TYPE_COUNT] = { +static const ride_ratings_calculation RideRatingsCalculateFuncTable[RIDE_TYPE_COUNT] = { ride_ratings_calculate_spiral_roller_coaster, // SPIRAL_ROLLER_COASTER ride_ratings_calculate_stand_up_roller_coaster, // STAND_UP_ROLLER_COASTER ride_ratings_calculate_suspended_swinging_coaster, // SUSPENDED_SWINGING_COASTER @@ -4454,7 +4440,7 @@ static const ride_ratings_calculation ride_ratings_calculate_func_table[RIDE_TYP static ride_ratings_calculation ride_ratings_get_calculate_func(uint8_t rideType) { - return ride_ratings_calculate_func_table[rideType]; + return RideRatingsCalculateFuncTable[rideType]; } #pragma endregion diff --git a/src/openrct2/ride/RideRatings.h b/src/openrct2/ride/RideRatings.h index c46a69d721..475c9dc5a1 100644 --- a/src/openrct2/ride/RideRatings.h +++ b/src/openrct2/ride/RideRatings.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -37,7 +37,7 @@ enum RIDE_RATING_STATION_FLAG_NO_ENTRANCE = 1 << 0 }; -struct rct_ride_rating_calc_data +struct RideRatingCalculationData { uint16_t proximity_x; uint16_t proximity_y; @@ -56,7 +56,7 @@ struct rct_ride_rating_calc_data uint16_t station_flags; }; -extern rct_ride_rating_calc_data gRideRatingsCalcData; +extern RideRatingCalculationData gRideRatingsCalcData; -void ride_ratings_update_ride(ride_id_t rideIndex); +void ride_ratings_update_ride(const Ride& ride); void ride_ratings_update_all(); diff --git a/src/openrct2/ride/RideTypes.h b/src/openrct2/ride/RideTypes.h index d52b7753b5..99c2b87e2a 100644 --- a/src/openrct2/ride/RideTypes.h +++ b/src/openrct2/ride/RideTypes.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,3 +12,5 @@ #include typedef uint8_t ride_id_t; +typedef uint32_t ride_idnew_t; // Temporary, old one can be removed after switching save format. +struct Ride; diff --git a/src/openrct2/ride/ShopItem.cpp b/src/openrct2/ride/ShopItem.cpp index b317066dca..4c898b0e0d 100644 --- a/src/openrct2/ride/ShopItem.cpp +++ b/src/openrct2/ride/ShopItem.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,290 +13,91 @@ #include "../localisation/StringIds.h" #include "../sprites.h" -uint32_t gSamePriceThroughoutParkA; -uint32_t gSamePriceThroughoutParkB; - -/** rct2: 0x00982164 */ -static const rct_shop_item_stats ShopItemStats[SHOP_ITEM_COUNT] = { - { 3, 14, 14, 14 }, // SHOP_ITEM_BALLOON - { 15, 30, 30, 30 }, // SHOP_ITEM_TOY - { 1, 7, 7, 8 }, // SHOP_ITEM_MAP - { 2, 30, 30, 30 }, // SHOP_ITEM_PHOTO - { 20, 35, 25, 50 }, // SHOP_ITEM_UMBRELLA - { 3, 12, 20, 10 }, // SHOP_ITEM_DRINK - { 5, 19, 19, 22 }, // SHOP_ITEM_BURGER - { 4, 16, 16, 18 }, // SHOP_ITEM_CHIPS - { 4, 10, 15, 6 }, // SHOP_ITEM_ICE_CREAM - { 3, 9, 9, 6 }, // SHOP_ITEM_CANDYFLOSS - { 0, 0, 0, 0 }, // SHOP_ITEM_EMPTY_CAN - { 0, 0, 0, 0 }, // SHOP_ITEM_RUBBISH - { 0, 0, 0, 0 }, // SHOP_ITEM_EMPTY_BURGER_BOX - { 6, 21, 21, 25 }, // SHOP_ITEM_PIZZA - { 0, 0, 0, 0 }, // SHOP_ITEM_VOUCHER - { 5, 13, 13, 11 }, // SHOP_ITEM_POPCORN - { 5, 17, 17, 20 }, // SHOP_ITEM_HOT_DOG - { 11, 22, 20, 18 }, // SHOP_ITEM_TENTACLE - { 9, 27, 32, 24 }, // SHOP_ITEM_HAT - { 4, 10, 10, 10 }, // SHOP_ITEM_TOFFEE_APPLE - { 20, 37, 37, 37 }, // SHOP_ITEM_TSHIRT - { 4, 8, 7, 10 }, // SHOP_ITEM_DOUGHNUT - { 3, 11, 15, 20 }, // SHOP_ITEM_COFFEE - { 0, 0, 0, 0 }, // SHOP_ITEM_EMPTY_CUP - { 5, 19, 19, 22 }, // SHOP_ITEM_CHICKEN - { 4, 11, 21, 10 }, // SHOP_ITEM_LEMONADE - { 0, 0, 0, 0 }, // SHOP_ITEM_EMPTY_BOX - { 0, 0, 0, 0 }, // SHOP_ITEM_EMPTY_BOTTLE - { 0, 0, 0, 0 }, // 28 - { 0, 0, 0, 0 }, // 29 - { 0, 0, 0, 0 }, // 30 - { 0, 0, 0, 0 }, // SHOP_ITEM_ADMISSION - { 2, 30, 30, 30 }, // SHOP_ITEM_PHOTO2 - { 2, 30, 30, 30 }, // SHOP_ITEM_PHOTO3 - { 2, 30, 30, 30 }, // SHOP_ITEM_PHOTO4 - { 5, 11, 11, 11 }, // SHOP_ITEM_PRETZEL - { 4, 13, 13, 20 }, // SHOP_ITEM_CHOCOLATE - { 3, 10, 20, 10 }, // SHOP_ITEM_ICED_TEA - { 5, 13, 11, 14 }, // SHOP_ITEM_FUNNEL_CAKE - { 8, 15, 20, 12 }, // SHOP_ITEM_SUNGLASSES - { 7, 17, 17, 20 }, // SHOP_ITEM_BEEF_NOODLES - { 6, 17, 17, 20 }, // SHOP_ITEM_FRIED_RICE_NOODLES - { 4, 13, 13, 15 }, // SHOP_ITEM_WONTON_SOUP - { 5, 14, 14, 16 }, // SHOP_ITEM_MEATBALL_SOUP - { 4, 11, 19, 11 }, // SHOP_ITEM_FRUIT_JUICE - { 4, 10, 14, 10 }, // SHOP_ITEM_SOYBEAN_MILK - { 3, 11, 14, 11 }, // SHOP_ITEM_SUJEONGGWA - { 5, 19, 19, 17 }, // SHOP_ITEM_SUB_SANDWICH - { 4, 8, 8, 8 }, // SHOP_ITEM_COOKIE - { 0, 0, 0, 0 }, // SHOP_ITEM_EMPTY_BOWL_RED - { 0, 0, 0, 0 }, // SHOP_ITEM_EMPTY_DRINK_CARTON - { 0, 0, 0, 0 }, // SHOP_ITEM_EMPTY_JUICE_CUP - { 5, 16, 16, 20 }, // SHOP_ITEM_ROAST_SAUSAGE - { 0, 0, 0, 0 }, // SHOP_ITEM_EMPTY_BOWL_BLUE -}; - -// rct2: 0x00982358 -const money8 DefaultShopItemPrice[SHOP_ITEM_COUNT] = { - MONEY(0, 90), // SHOP_ITEM_BALLOON - MONEY(2, 50), // SHOP_ITEM_TOY - MONEY(0, 60), // SHOP_ITEM_MAP - MONEY(0, 00), // SHOP_ITEM_PHOTO - MONEY(2, 50), // SHOP_ITEM_UMBRELLA - MONEY(1, 20), // SHOP_ITEM_DRINK - MONEY(1, 50), // SHOP_ITEM_BURGER - MONEY(1, 50), // SHOP_ITEM_CHIPS - MONEY(0, 90), // SHOP_ITEM_ICE_CREAM - MONEY(0, 80), // SHOP_ITEM_CANDYFLOSS - MONEY(0, 00), // SHOP_ITEM_EMPTY_CAN - MONEY(0, 00), // SHOP_ITEM_RUBBISH - MONEY(0, 00), // SHOP_ITEM_EMPTY_BURGER_BOX - MONEY(1, 60), // SHOP_ITEM_PIZZA - MONEY(0, 00), // SHOP_ITEM_VOUCHER - MONEY(1, 20), // SHOP_ITEM_POPCORN - MONEY(1, 00), // SHOP_ITEM_HOT_DOG - MONEY(1, 50), // SHOP_ITEM_TENTACLE - MONEY(1, 50), // SHOP_ITEM_HAT - MONEY(0, 70), // SHOP_ITEM_TOFFEE_APPLE - MONEY(3, 00), // SHOP_ITEM_TSHIRT - MONEY(0, 70), // SHOP_ITEM_DOUGHNUT - MONEY(1, 20), // SHOP_ITEM_COFFEE - MONEY(0, 00), // SHOP_ITEM_EMPTY_CUP - MONEY(1, 50), // SHOP_ITEM_CHICKEN - MONEY(1, 20), // SHOP_ITEM_LEMONADE - MONEY(0, 00), // SHOP_ITEM_EMPTY_BOX - MONEY(0, 00), // SHOP_ITEM_EMPTY_BOTTLE - MONEY(0, 00), // 28 - MONEY(0, 00), // 29 - MONEY(0, 00), // 30 - MONEY(0, 00), // 31 - MONEY(0, 00), // SHOP_ITEM_PHOTO2 - MONEY(0, 00), // SHOP_ITEM_PHOTO3 - MONEY(0, 00), // SHOP_ITEM_PHOTO4 - MONEY(1, 10), // SHOP_ITEM_PRETZEL - MONEY(1, 20), // SHOP_ITEM_CHOCOLATE - MONEY(1, 10), // SHOP_ITEM_ICED_TEA - MONEY(1, 20), // SHOP_ITEM_FUNNEL_CAKE - MONEY(1, 50), // SHOP_ITEM_SUNGLASSES - MONEY(1, 50), // SHOP_ITEM_BEEF_NOODLES - MONEY(1, 50), // SHOP_ITEM_FRIED_RICE_NOODLES - MONEY(1, 50), // SHOP_ITEM_WONTON_SOUP - MONEY(1, 50), // SHOP_ITEM_MEATBALL_SOUP - MONEY(1, 20), // SHOP_ITEM_FRUIT_JUICE - MONEY(1, 20), // SHOP_ITEM_SOYBEAN_MILK - MONEY(1, 20), // SHOP_ITEM_SUJEONGGWA - MONEY(1, 50), // SHOP_ITEM_SUB_SANDWICH - MONEY(0, 70), // SHOP_ITEM_COOKIE - MONEY(0, 00), // SHOP_ITEM_EMPTY_BOWL_RED - MONEY(0, 00), // SHOP_ITEM_EMPTY_DRINK_CARTON - MONEY(0, 00), // SHOP_ITEM_EMPTY_JUICE_CUP - MONEY(1, 50), // SHOP_ITEM_ROAST_SAUSAGE - MONEY(0, 00), // SHOP_ITEM_EMPTY_BOWL_BLUE - MONEY(0, 00), // 54 - MONEY(0, 00), // 55 -}; +uint64_t gSamePriceThroughoutPark; // clang-format off -const rct_shop_item_string_types ShopItemStringIds[SHOP_ITEM_COUNT] = -{ - { STR_SHOP_ITEM_PRICE_LABEL_BALLOON, STR_SHOP_ITEM_SINGULAR_BALLOON, STR_SHOP_ITEM_PLURAL_BALLOON, STR_SHOP_ITEM_INDEFINITE_BALLOON, STR_SHOP_ITEM_DISPLAY_BALLOON }, - { STR_SHOP_ITEM_PRICE_LABEL_CUDDLY_TOY, STR_SHOP_ITEM_SINGULAR_CUDDLY_TOY, STR_SHOP_ITEM_PLURAL_CUDDLY_TOY, STR_SHOP_ITEM_INDEFINITE_CUDDLY_TOY, STR_SHOP_ITEM_DISPLAY_CUDDLY_TOY }, - { STR_SHOP_ITEM_PRICE_LABEL_PARK_MAP, STR_SHOP_ITEM_SINGULAR_PARK_MAP, STR_SHOP_ITEM_PLURAL_PARK_MAP, STR_SHOP_ITEM_INDEFINITE_PARK_MAP, STR_SHOP_ITEM_DISPLAY_PARK_MAP }, - { STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO, STR_SHOP_ITEM_SINGULAR_ON_RIDE_PHOTO, STR_SHOP_ITEM_PLURAL_ON_RIDE_PHOTO, STR_SHOP_ITEM_INDEFINITE_ON_RIDE_PHOTO, STR_SHOP_ITEM_DISPLAY_ON_RIDE_PHOTO }, - { STR_SHOP_ITEM_PRICE_LABEL_UMBRELLA, STR_SHOP_ITEM_SINGULAR_UMBRELLA, STR_SHOP_ITEM_PLURAL_UMBRELLA, STR_SHOP_ITEM_INDEFINITE_UMBRELLA, STR_SHOP_ITEM_DISPLAY_UMBRELLA }, - { STR_SHOP_ITEM_PRICE_LABEL_DRINK, STR_SHOP_ITEM_SINGULAR_DRINK, STR_SHOP_ITEM_PLURAL_DRINK, STR_SHOP_ITEM_INDEFINITE_DRINK, STR_SHOP_ITEM_DISPLAY_DRINK }, - { STR_SHOP_ITEM_PRICE_LABEL_BURGER, STR_SHOP_ITEM_SINGULAR_BURGER, STR_SHOP_ITEM_PLURAL_BURGER, STR_SHOP_ITEM_INDEFINITE_BURGER, STR_SHOP_ITEM_DISPLAY_BURGER }, - { STR_SHOP_ITEM_PRICE_LABEL_CHIPS, STR_SHOP_ITEM_SINGULAR_CHIPS, STR_SHOP_ITEM_PLURAL_CHIPS, STR_SHOP_ITEM_INDEFINITE_CHIPS, STR_SHOP_ITEM_DISPLAY_CHIPS }, - { STR_SHOP_ITEM_PRICE_LABEL_ICE_CREAM, STR_SHOP_ITEM_SINGULAR_ICE_CREAM, STR_SHOP_ITEM_PLURAL_ICE_CREAM, STR_SHOP_ITEM_INDEFINITE_ICE_CREAM, STR_SHOP_ITEM_DISPLAY_ICE_CREAM }, - { STR_SHOP_ITEM_PRICE_LABEL_CANDYFLOSS, STR_SHOP_ITEM_SINGULAR_CANDYFLOSS, STR_SHOP_ITEM_PLURAL_CANDYFLOSS, STR_SHOP_ITEM_INDEFINITE_CANDYFLOSS, STR_SHOP_ITEM_DISPLAY_CANDYFLOSS }, - { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_CAN, STR_SHOP_ITEM_SINGULAR_EMPTY_CAN, STR_SHOP_ITEM_PLURAL_EMPTY_CAN, STR_SHOP_ITEM_INDEFINITE_EMPTY_CAN, STR_SHOP_ITEM_DISPLAY_EMPTY_CAN }, - { STR_SHOP_ITEM_PRICE_LABEL_RUBBISH, STR_SHOP_ITEM_SINGULAR_RUBBISH, STR_SHOP_ITEM_PLURAL_RUBBISH, STR_SHOP_ITEM_INDEFINITE_RUBBISH, STR_SHOP_ITEM_DISPLAY_RUBBISH }, - { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_BURGER_BOX, STR_SHOP_ITEM_SINGULAR_EMPTY_BURGER_BOX, STR_SHOP_ITEM_PLURAL_EMPTY_BURGER_BOX, STR_SHOP_ITEM_INDEFINITE_EMPTY_BURGER_BOX, STR_SHOP_ITEM_DISPLAY_EMPTY_BURGER_BOX }, - { STR_SHOP_ITEM_PRICE_LABEL_PIZZA, STR_SHOP_ITEM_SINGULAR_PIZZA, STR_SHOP_ITEM_PLURAL_PIZZA, STR_SHOP_ITEM_INDEFINITE_PIZZA, STR_SHOP_ITEM_DISPLAY_PIZZA }, - { STR_SHOP_ITEM_PRICE_LABEL_VOUCHER, STR_SHOP_ITEM_SINGULAR_VOUCHER, STR_SHOP_ITEM_PLURAL_VOUCHER, STR_SHOP_ITEM_INDEFINITE_VOUCHER, STR_SHOP_ITEM_DISPLAY_VOUCHER }, - { STR_SHOP_ITEM_PRICE_LABEL_POPCORN, STR_SHOP_ITEM_SINGULAR_POPCORN, STR_SHOP_ITEM_PLURAL_POPCORN, STR_SHOP_ITEM_INDEFINITE_POPCORN, STR_SHOP_ITEM_DISPLAY_POPCORN }, - { STR_SHOP_ITEM_PRICE_LABEL_HOT_DOG, STR_SHOP_ITEM_SINGULAR_HOT_DOG, STR_SHOP_ITEM_PLURAL_HOT_DOG, STR_SHOP_ITEM_INDEFINITE_HOT_DOG, STR_SHOP_ITEM_DISPLAY_HOT_DOG }, - { STR_SHOP_ITEM_PRICE_LABEL_TENTACLE, STR_SHOP_ITEM_SINGULAR_TENTACLE, STR_SHOP_ITEM_PLURAL_TENTACLE, STR_SHOP_ITEM_INDEFINITE_TENTACLE, STR_SHOP_ITEM_DISPLAY_TENTACLE }, - { STR_SHOP_ITEM_PRICE_LABEL_HAT, STR_SHOP_ITEM_SINGULAR_HAT, STR_SHOP_ITEM_PLURAL_HAT, STR_SHOP_ITEM_INDEFINITE_HAT, STR_SHOP_ITEM_DISPLAY_HAT }, - { STR_SHOP_ITEM_PRICE_LABEL_TOFFEE_APPLE, STR_SHOP_ITEM_SINGULAR_TOFFEE_APPLE, STR_SHOP_ITEM_PLURAL_TOFFEE_APPLE, STR_SHOP_ITEM_INDEFINITE_TOFFEE_APPLE, STR_SHOP_ITEM_DISPLAY_TOFFEE_APPLE }, - { STR_SHOP_ITEM_PRICE_LABEL_T_SHIRT, STR_SHOP_ITEM_SINGULAR_T_SHIRT, STR_SHOP_ITEM_PLURAL_T_SHIRT, STR_SHOP_ITEM_INDEFINITE_T_SHIRT, STR_SHOP_ITEM_DISPLAY_T_SHIRT }, - { STR_SHOP_ITEM_PRICE_LABEL_DOUGHNUT, STR_SHOP_ITEM_SINGULAR_DOUGHNUT, STR_SHOP_ITEM_PLURAL_DOUGHNUT, STR_SHOP_ITEM_INDEFINITE_DOUGHNUT, STR_SHOP_ITEM_DISPLAY_DOUGHNUT }, - { STR_SHOP_ITEM_PRICE_LABEL_COFFEE, STR_SHOP_ITEM_SINGULAR_COFFEE, STR_SHOP_ITEM_PLURAL_COFFEE, STR_SHOP_ITEM_INDEFINITE_COFFEE, STR_SHOP_ITEM_DISPLAY_COFFEE }, - { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_CUP, STR_SHOP_ITEM_SINGULAR_EMPTY_CUP, STR_SHOP_ITEM_PLURAL_EMPTY_CUP, STR_SHOP_ITEM_INDEFINITE_EMPTY_CUP, STR_SHOP_ITEM_DISPLAY_EMPTY_CUP }, - { STR_SHOP_ITEM_PRICE_LABEL_FRIED_CHICKEN, STR_SHOP_ITEM_SINGULAR_FRIED_CHICKEN, STR_SHOP_ITEM_PLURAL_FRIED_CHICKEN, STR_SHOP_ITEM_INDEFINITE_FRIED_CHICKEN, STR_SHOP_ITEM_DISPLAY_FRIED_CHICKEN }, - { STR_SHOP_ITEM_PRICE_LABEL_LEMONADE, STR_SHOP_ITEM_SINGULAR_LEMONADE, STR_SHOP_ITEM_PLURAL_LEMONADE, STR_SHOP_ITEM_INDEFINITE_LEMONADE, STR_SHOP_ITEM_DISPLAY_LEMONADE }, - { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_BOX, STR_SHOP_ITEM_SINGULAR_EMPTY_BOX, STR_SHOP_ITEM_PLURAL_EMPTY_BOX, STR_SHOP_ITEM_INDEFINITE_EMPTY_BOX, STR_SHOP_ITEM_DISPLAY_EMPTY_BOX }, - { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_BOTTLE, STR_SHOP_ITEM_SINGULAR_EMPTY_BOTTLE, STR_SHOP_ITEM_PLURAL_EMPTY_BOTTLE, STR_SHOP_ITEM_INDEFINITE_EMPTY_BOTTLE, STR_SHOP_ITEM_DISPLAY_EMPTY_BOTTLE }, - { STR_NONE, STR_NONE, STR_NONE, STR_NONE, STR_NONE }, - { STR_NONE, STR_NONE, STR_NONE, STR_NONE, STR_NONE }, - { STR_NONE, STR_NONE, STR_NONE, STR_NONE, STR_NONE }, - { STR_NONE, STR_NONE, STR_NONE, STR_NONE, STR_NONE }, - { STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO, STR_SHOP_ITEM_SINGULAR_ON_RIDE_PHOTO, STR_SHOP_ITEM_PLURAL_ON_RIDE_PHOTO, STR_SHOP_ITEM_INDEFINITE_ON_RIDE_PHOTO, STR_SHOP_ITEM_DISPLAY_ON_RIDE_PHOTO }, - { STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO, STR_SHOP_ITEM_SINGULAR_ON_RIDE_PHOTO, STR_SHOP_ITEM_PLURAL_ON_RIDE_PHOTO, STR_SHOP_ITEM_INDEFINITE_ON_RIDE_PHOTO, STR_SHOP_ITEM_DISPLAY_ON_RIDE_PHOTO }, - { STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO, STR_SHOP_ITEM_SINGULAR_ON_RIDE_PHOTO, STR_SHOP_ITEM_PLURAL_ON_RIDE_PHOTO, STR_SHOP_ITEM_INDEFINITE_ON_RIDE_PHOTO, STR_SHOP_ITEM_DISPLAY_ON_RIDE_PHOTO }, - { STR_SHOP_ITEM_PRICE_LABEL_PRETZEL, STR_SHOP_ITEM_SINGULAR_PRETZEL, STR_SHOP_ITEM_PLURAL_PRETZEL, STR_SHOP_ITEM_INDEFINITE_PRETZEL, STR_SHOP_ITEM_DISPLAY_PRETZEL }, - { STR_SHOP_ITEM_PRICE_LABEL_HOT_CHOCOLATE, STR_SHOP_ITEM_SINGULAR_HOT_CHOCOLATE, STR_SHOP_ITEM_PLURAL_HOT_CHOCOLATE, STR_SHOP_ITEM_INDEFINITE_HOT_CHOCOLATE, STR_SHOP_ITEM_DISPLAY_HOT_CHOCOLATE }, - { STR_SHOP_ITEM_PRICE_LABEL_ICED_TEA, STR_SHOP_ITEM_SINGULAR_ICED_TEA, STR_SHOP_ITEM_PLURAL_ICED_TEA, STR_SHOP_ITEM_INDEFINITE_ICED_TEA, STR_SHOP_ITEM_DISPLAY_ICED_TEA }, - { STR_SHOP_ITEM_PRICE_LABEL_FUNNEL_CAKE, STR_SHOP_ITEM_SINGULAR_FUNNEL_CAKE, STR_SHOP_ITEM_PLURAL_FUNNEL_CAKE, STR_SHOP_ITEM_INDEFINITE_FUNNEL_CAKE, STR_SHOP_ITEM_DISPLAY_FUNNEL_CAKE }, - { STR_SHOP_ITEM_PRICE_LABEL_SUNGLASSES, STR_SHOP_ITEM_SINGULAR_SUNGLASSES, STR_SHOP_ITEM_PLURAL_SUNGLASSES, STR_SHOP_ITEM_INDEFINITE_SUNGLASSES, STR_SHOP_ITEM_DISPLAY_SUNGLASSES }, - { STR_SHOP_ITEM_PRICE_LABEL_BEEF_NOODLES, STR_SHOP_ITEM_SINGULAR_BEEF_NOODLES, STR_SHOP_ITEM_PLURAL_BEEF_NOODLES, STR_SHOP_ITEM_INDEFINITE_BEEF_NOODLES, STR_SHOP_ITEM_DISPLAY_BEEF_NOODLES }, - { STR_SHOP_ITEM_PRICE_LABEL_FRIED_RICE_NOODLES, STR_SHOP_ITEM_SINGULAR_FRIED_RICE_NOODLES, STR_SHOP_ITEM_PLURAL_FRIED_RICE_NOODLES, STR_SHOP_ITEM_INDEFINITE_FRIED_RICE_NOODLES, STR_SHOP_ITEM_DISPLAY_FRIED_RICE_NOODLES }, - { STR_SHOP_ITEM_PRICE_LABEL_WONTON_SOUP, STR_SHOP_ITEM_SINGULAR_WONTON_SOUP, STR_SHOP_ITEM_PLURAL_WONTON_SOUP, STR_SHOP_ITEM_INDEFINITE_WONTON_SOUP, STR_SHOP_ITEM_DISPLAY_WONTON_SOUP }, - { STR_SHOP_ITEM_PRICE_LABEL_MEATBALL_SOUP, STR_SHOP_ITEM_SINGULAR_MEATBALL_SOUP, STR_SHOP_ITEM_PLURAL_MEATBALL_SOUP, STR_SHOP_ITEM_INDEFINITE_MEATBALL_SOUP, STR_SHOP_ITEM_DISPLAY_MEATBALL_SOUP }, - { STR_SHOP_ITEM_PRICE_LABEL_FRUIT_JUICE, STR_SHOP_ITEM_SINGULAR_FRUIT_JUICE, STR_SHOP_ITEM_PLURAL_FRUIT_JUICE, STR_SHOP_ITEM_INDEFINITE_FRUIT_JUICE, STR_SHOP_ITEM_DISPLAY_FRUIT_JUICE }, - { STR_SHOP_ITEM_PRICE_LABEL_SOYBEAN_MILK, STR_SHOP_ITEM_SINGULAR_SOYBEAN_MILK, STR_SHOP_ITEM_PLURAL_SOYBEAN_MILK, STR_SHOP_ITEM_INDEFINITE_SOYBEAN_MILK, STR_SHOP_ITEM_DISPLAY_SOYBEAN_MILK }, - { STR_SHOP_ITEM_PRICE_LABEL_SUJONGKWA, STR_SHOP_ITEM_SINGULAR_SUJONGKWA, STR_SHOP_ITEM_PLURAL_SUJONGKWA, STR_SHOP_ITEM_INDEFINITE_SUJONGKWA, STR_SHOP_ITEM_DISPLAY_SUJONGKWA }, - { STR_SHOP_ITEM_PRICE_LABEL_SUB_SANDWICH, STR_SHOP_ITEM_SINGULAR_SUB_SANDWICH, STR_SHOP_ITEM_PLURAL_SUB_SANDWICH, STR_SHOP_ITEM_INDEFINITE_SUB_SANDWICH, STR_SHOP_ITEM_DISPLAY_SUB_SANDWICH }, - { STR_SHOP_ITEM_PRICE_LABEL_COOKIE, STR_SHOP_ITEM_SINGULAR_COOKIE, STR_SHOP_ITEM_PLURAL_COOKIE, STR_SHOP_ITEM_INDEFINITE_COOKIE, STR_SHOP_ITEM_DISPLAY_COOKIE }, - { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_BOWL_RED, STR_SHOP_ITEM_SINGULAR_EMPTY_BOWL_RED, STR_SHOP_ITEM_PLURAL_EMPTY_BOWL_RED, STR_SHOP_ITEM_INDEFINITE_EMPTY_BOWL_RED, STR_SHOP_ITEM_DISPLAY_EMPTY_BOWL_RED }, - { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_DRINK_CARTON, STR_SHOP_ITEM_SINGULAR_EMPTY_DRINK_CARTON, STR_SHOP_ITEM_PLURAL_EMPTY_DRINK_CARTON, STR_SHOP_ITEM_INDEFINITE_EMPTY_DRINK_CARTON, STR_SHOP_ITEM_DISPLAY_EMPTY_DRINK_CARTON }, - { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_JUICE_CUP, STR_SHOP_ITEM_SINGULAR_EMPTY_JUICE_CUP, STR_SHOP_ITEM_PLURAL_EMPTY_JUICE_CUP, STR_SHOP_ITEM_INDEFINITE_EMPTY_JUICE_CUP, STR_SHOP_ITEM_DISPLAY_EMPTY_JUICE_CUP }, - { STR_SHOP_ITEM_PRICE_LABEL_ROAST_SAUSAGE, STR_SHOP_ITEM_SINGULAR_ROAST_SAUSAGE, STR_SHOP_ITEM_PLURAL_ROAST_SAUSAGE, STR_SHOP_ITEM_INDEFINITE_ROAST_SAUSAGE, STR_SHOP_ITEM_DISPLAY_ROAST_SAUSAGE }, - { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_BOWL_BLUE, STR_SHOP_ITEM_SINGULAR_EMPTY_BOWL_BLUE, STR_SHOP_ITEM_PLURAL_EMPTY_BOWL_BLUE, STR_SHOP_ITEM_INDEFINITE_EMPTY_BOWL_BLUE, STR_SHOP_ITEM_DISPLAY_EMPTY_BOWL_BLUE }, +/** rct2: 0x00982164 (cost, base value, hot and cold value); 0x00982358 (default price) */ +const ShopItemDescriptor ShopItems[SHOP_ITEM_COUNT] = { + // Item, Cost, Base value, Hot value, Cold value, Default price, Image, Price label, Singular, Plural, Indefinite, Display (in guest inventory) + /* SHOP_ITEM_BALLOON */ { 3, 14, 14, 14, MONEY(0, 90), SPR_SHOP_ITEM_BALLOON, { STR_SHOP_ITEM_PRICE_LABEL_BALLOON, STR_SHOP_ITEM_SINGULAR_BALLOON, STR_SHOP_ITEM_PLURAL_BALLOON, STR_SHOP_ITEM_INDEFINITE_BALLOON, STR_SHOP_ITEM_DISPLAY_BALLOON } }, + /* SHOP_ITEM_TOY */ { 15, 30, 30, 30, MONEY(2, 50), SPR_SHOP_ITEM_TOY, { STR_SHOP_ITEM_PRICE_LABEL_CUDDLY_TOY, STR_SHOP_ITEM_SINGULAR_CUDDLY_TOY, STR_SHOP_ITEM_PLURAL_CUDDLY_TOY, STR_SHOP_ITEM_INDEFINITE_CUDDLY_TOY, STR_SHOP_ITEM_DISPLAY_CUDDLY_TOY } }, + /* SHOP_ITEM_MAP */ { 1, 7, 7, 8, MONEY(0, 60), SPR_SHOP_ITEM_MAP, { STR_SHOP_ITEM_PRICE_LABEL_PARK_MAP, STR_SHOP_ITEM_SINGULAR_PARK_MAP, STR_SHOP_ITEM_PLURAL_PARK_MAP, STR_SHOP_ITEM_INDEFINITE_PARK_MAP, STR_SHOP_ITEM_DISPLAY_PARK_MAP } }, + /* SHOP_ITEM_PHOTO */ { 2, 30, 30, 30, MONEY(0, 00), SPR_SHOP_ITEM_PHOTO, { STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO, STR_SHOP_ITEM_SINGULAR_ON_RIDE_PHOTO, STR_SHOP_ITEM_PLURAL_ON_RIDE_PHOTO, STR_SHOP_ITEM_INDEFINITE_ON_RIDE_PHOTO, STR_SHOP_ITEM_DISPLAY_ON_RIDE_PHOTO } }, + /* SHOP_ITEM_UMBRELLA */ { 20, 35, 25, 50, MONEY(2, 50), SPR_SHOP_ITEM_UMBRELLA, { STR_SHOP_ITEM_PRICE_LABEL_UMBRELLA, STR_SHOP_ITEM_SINGULAR_UMBRELLA, STR_SHOP_ITEM_PLURAL_UMBRELLA, STR_SHOP_ITEM_INDEFINITE_UMBRELLA, STR_SHOP_ITEM_DISPLAY_UMBRELLA } }, + /* SHOP_ITEM_DRINK */ { 3, 12, 20, 10, MONEY(1, 20), SPR_SHOP_ITEM_DRINK, { STR_SHOP_ITEM_PRICE_LABEL_DRINK, STR_SHOP_ITEM_SINGULAR_DRINK, STR_SHOP_ITEM_PLURAL_DRINK, STR_SHOP_ITEM_INDEFINITE_DRINK, STR_SHOP_ITEM_DISPLAY_DRINK } }, + /* SHOP_ITEM_BURGER */ { 5, 19, 19, 22, MONEY(1, 50), SPR_SHOP_ITEM_BURGER, { STR_SHOP_ITEM_PRICE_LABEL_BURGER, STR_SHOP_ITEM_SINGULAR_BURGER, STR_SHOP_ITEM_PLURAL_BURGER, STR_SHOP_ITEM_INDEFINITE_BURGER, STR_SHOP_ITEM_DISPLAY_BURGER } }, + /* SHOP_ITEM_CHIPS */ { 4, 16, 16, 18, MONEY(1, 50), SPR_SHOP_ITEM_CHIPS, { STR_SHOP_ITEM_PRICE_LABEL_CHIPS, STR_SHOP_ITEM_SINGULAR_CHIPS, STR_SHOP_ITEM_PLURAL_CHIPS, STR_SHOP_ITEM_INDEFINITE_CHIPS, STR_SHOP_ITEM_DISPLAY_CHIPS } }, + /* SHOP_ITEM_ICE_CREAM */ { 4, 10, 15, 6, MONEY(0, 90), SPR_SHOP_ITEM_ICE_CREAM, { STR_SHOP_ITEM_PRICE_LABEL_ICE_CREAM, STR_SHOP_ITEM_SINGULAR_ICE_CREAM, STR_SHOP_ITEM_PLURAL_ICE_CREAM, STR_SHOP_ITEM_INDEFINITE_ICE_CREAM, STR_SHOP_ITEM_DISPLAY_ICE_CREAM } }, + /* SHOP_ITEM_CANDYFLOSS */ { 3, 9, 9, 6, MONEY(0, 80), SPR_SHOP_ITEM_CANDYFLOSS, { STR_SHOP_ITEM_PRICE_LABEL_CANDYFLOSS, STR_SHOP_ITEM_SINGULAR_CANDYFLOSS, STR_SHOP_ITEM_PLURAL_CANDYFLOSS, STR_SHOP_ITEM_INDEFINITE_CANDYFLOSS, STR_SHOP_ITEM_DISPLAY_CANDYFLOSS } }, + /* SHOP_ITEM_EMPTY_CAN */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_EMPTY_CAN, { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_CAN, STR_SHOP_ITEM_SINGULAR_EMPTY_CAN, STR_SHOP_ITEM_PLURAL_EMPTY_CAN, STR_SHOP_ITEM_INDEFINITE_EMPTY_CAN, STR_SHOP_ITEM_DISPLAY_EMPTY_CAN } }, + /* SHOP_ITEM_RUBBISH */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_RUBBISH, { STR_SHOP_ITEM_PRICE_LABEL_RUBBISH, STR_SHOP_ITEM_SINGULAR_RUBBISH, STR_SHOP_ITEM_PLURAL_RUBBISH, STR_SHOP_ITEM_INDEFINITE_RUBBISH, STR_SHOP_ITEM_DISPLAY_RUBBISH } }, + /* SHOP_ITEM_EMPTY_BURGER_BOX */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_EMPTY_BURGER_BOX, { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_BURGER_BOX, STR_SHOP_ITEM_SINGULAR_EMPTY_BURGER_BOX, STR_SHOP_ITEM_PLURAL_EMPTY_BURGER_BOX, STR_SHOP_ITEM_INDEFINITE_EMPTY_BURGER_BOX, STR_SHOP_ITEM_DISPLAY_EMPTY_BURGER_BOX } }, + /* SHOP_ITEM_PIZZA */ { 6, 21, 21, 25, MONEY(1, 60), SPR_SHOP_ITEM_PIZZA, { STR_SHOP_ITEM_PRICE_LABEL_PIZZA, STR_SHOP_ITEM_SINGULAR_PIZZA, STR_SHOP_ITEM_PLURAL_PIZZA, STR_SHOP_ITEM_INDEFINITE_PIZZA, STR_SHOP_ITEM_DISPLAY_PIZZA } }, + /* SHOP_ITEM_VOUCHER */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_VOUCHER, { STR_SHOP_ITEM_PRICE_LABEL_VOUCHER, STR_SHOP_ITEM_SINGULAR_VOUCHER, STR_SHOP_ITEM_PLURAL_VOUCHER, STR_SHOP_ITEM_INDEFINITE_VOUCHER, STR_SHOP_ITEM_DISPLAY_VOUCHER } }, + /* SHOP_ITEM_POPCORN */ { 5, 13, 13, 11, MONEY(1, 20), SPR_SHOP_ITEM_POPCORN, { STR_SHOP_ITEM_PRICE_LABEL_POPCORN, STR_SHOP_ITEM_SINGULAR_POPCORN, STR_SHOP_ITEM_PLURAL_POPCORN, STR_SHOP_ITEM_INDEFINITE_POPCORN, STR_SHOP_ITEM_DISPLAY_POPCORN } }, + /* SHOP_ITEM_HOT_DOG */ { 5, 17, 17, 20, MONEY(1, 00), SPR_SHOP_ITEM_HOT_DOG, { STR_SHOP_ITEM_PRICE_LABEL_HOT_DOG, STR_SHOP_ITEM_SINGULAR_HOT_DOG, STR_SHOP_ITEM_PLURAL_HOT_DOG, STR_SHOP_ITEM_INDEFINITE_HOT_DOG, STR_SHOP_ITEM_DISPLAY_HOT_DOG } }, + /* SHOP_ITEM_TENTACLE */ { 11, 22, 20, 18, MONEY(1, 50), SPR_SHOP_ITEM_TENTACLE, { STR_SHOP_ITEM_PRICE_LABEL_TENTACLE, STR_SHOP_ITEM_SINGULAR_TENTACLE, STR_SHOP_ITEM_PLURAL_TENTACLE, STR_SHOP_ITEM_INDEFINITE_TENTACLE, STR_SHOP_ITEM_DISPLAY_TENTACLE } }, + /* SHOP_ITEM_HAT */ { 9, 27, 32, 24, MONEY(1, 50), SPR_SHOP_ITEM_HAT, { STR_SHOP_ITEM_PRICE_LABEL_HAT, STR_SHOP_ITEM_SINGULAR_HAT, STR_SHOP_ITEM_PLURAL_HAT, STR_SHOP_ITEM_INDEFINITE_HAT, STR_SHOP_ITEM_DISPLAY_HAT } }, + /* SHOP_ITEM_TOFFEE_APPLE */ { 4, 10, 10, 10, MONEY(0, 70), SPR_SHOP_ITEM_TOFFEE_APPLE, { STR_SHOP_ITEM_PRICE_LABEL_TOFFEE_APPLE, STR_SHOP_ITEM_SINGULAR_TOFFEE_APPLE, STR_SHOP_ITEM_PLURAL_TOFFEE_APPLE, STR_SHOP_ITEM_INDEFINITE_TOFFEE_APPLE, STR_SHOP_ITEM_DISPLAY_TOFFEE_APPLE } }, + /* SHOP_ITEM_TSHIRT */ { 20, 37, 37, 37, MONEY(3, 00), SPR_SHOP_ITEM_TSHIRT, { STR_SHOP_ITEM_PRICE_LABEL_T_SHIRT, STR_SHOP_ITEM_SINGULAR_T_SHIRT, STR_SHOP_ITEM_PLURAL_T_SHIRT, STR_SHOP_ITEM_INDEFINITE_T_SHIRT, STR_SHOP_ITEM_DISPLAY_T_SHIRT } }, + /* SHOP_ITEM_DOUGHNUT */ { 4, 8, 7, 10, MONEY(0, 70), SPR_SHOP_ITEM_DOUGHNUT, { STR_SHOP_ITEM_PRICE_LABEL_DOUGHNUT, STR_SHOP_ITEM_SINGULAR_DOUGHNUT, STR_SHOP_ITEM_PLURAL_DOUGHNUT, STR_SHOP_ITEM_INDEFINITE_DOUGHNUT, STR_SHOP_ITEM_DISPLAY_DOUGHNUT } }, + /* SHOP_ITEM_COFFEE */ { 3, 11, 15, 20, MONEY(1, 20), SPR_SHOP_ITEM_COFFEE, { STR_SHOP_ITEM_PRICE_LABEL_COFFEE, STR_SHOP_ITEM_SINGULAR_COFFEE, STR_SHOP_ITEM_PLURAL_COFFEE, STR_SHOP_ITEM_INDEFINITE_COFFEE, STR_SHOP_ITEM_DISPLAY_COFFEE } }, + /* SHOP_ITEM_EMPTY_CUP */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_EMPTY_CUP, { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_CUP, STR_SHOP_ITEM_SINGULAR_EMPTY_CUP, STR_SHOP_ITEM_PLURAL_EMPTY_CUP, STR_SHOP_ITEM_INDEFINITE_EMPTY_CUP, STR_SHOP_ITEM_DISPLAY_EMPTY_CUP } }, + /* SHOP_ITEM_CHICKEN */ { 5, 19, 19, 22, MONEY(1, 50), SPR_SHOP_ITEM_CHICKEN, { STR_SHOP_ITEM_PRICE_LABEL_FRIED_CHICKEN, STR_SHOP_ITEM_SINGULAR_FRIED_CHICKEN, STR_SHOP_ITEM_PLURAL_FRIED_CHICKEN, STR_SHOP_ITEM_INDEFINITE_FRIED_CHICKEN, STR_SHOP_ITEM_DISPLAY_FRIED_CHICKEN } }, + /* SHOP_ITEM_LEMONADE */ { 4, 11, 21, 10, MONEY(1, 20), SPR_SHOP_ITEM_LEMONADE, { STR_SHOP_ITEM_PRICE_LABEL_LEMONADE, STR_SHOP_ITEM_SINGULAR_LEMONADE, STR_SHOP_ITEM_PLURAL_LEMONADE, STR_SHOP_ITEM_INDEFINITE_LEMONADE, STR_SHOP_ITEM_DISPLAY_LEMONADE } }, + /* SHOP_ITEM_EMPTY_BOX */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_EMPTY_BOX, { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_BOX, STR_SHOP_ITEM_SINGULAR_EMPTY_BOX, STR_SHOP_ITEM_PLURAL_EMPTY_BOX, STR_SHOP_ITEM_INDEFINITE_EMPTY_BOX, STR_SHOP_ITEM_DISPLAY_EMPTY_BOX } }, + /* SHOP_ITEM_EMPTY_BOTTLE */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_EMPTY_BOTTLE, { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_BOTTLE, STR_SHOP_ITEM_SINGULAR_EMPTY_BOTTLE, STR_SHOP_ITEM_PLURAL_EMPTY_BOTTLE, STR_SHOP_ITEM_INDEFINITE_EMPTY_BOTTLE, STR_SHOP_ITEM_DISPLAY_EMPTY_BOTTLE } }, + /* 28 */ { 0, 0, 0, 0, MONEY(0, 00), 0, { STR_NONE, STR_NONE, STR_NONE, STR_NONE, STR_NONE } }, + /* 29 */ { 0, 0, 0, 0, MONEY(0, 00), 0, { STR_NONE, STR_NONE, STR_NONE, STR_NONE, STR_NONE } }, + /* 30 */ { 0, 0, 0, 0, MONEY(0, 00), 0, { STR_NONE, STR_NONE, STR_NONE, STR_NONE, STR_NONE } }, + /* SHOP_ITEM_ADMISSION */ { 0, 0, 0, 0, MONEY(0, 00), 0, { STR_NONE, STR_NONE, STR_NONE, STR_NONE, STR_NONE } }, + /* SHOP_ITEM_PHOTO2 */ { 2, 30, 30, 30, MONEY(0, 00), SPR_SHOP_ITEM_PHOTO2, { STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO, STR_SHOP_ITEM_SINGULAR_ON_RIDE_PHOTO, STR_SHOP_ITEM_PLURAL_ON_RIDE_PHOTO, STR_SHOP_ITEM_INDEFINITE_ON_RIDE_PHOTO, STR_SHOP_ITEM_DISPLAY_ON_RIDE_PHOTO } }, + /* SHOP_ITEM_PHOTO3 */ { 2, 30, 30, 30, MONEY(0, 00), SPR_SHOP_ITEM_PHOTO3, { STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO, STR_SHOP_ITEM_SINGULAR_ON_RIDE_PHOTO, STR_SHOP_ITEM_PLURAL_ON_RIDE_PHOTO, STR_SHOP_ITEM_INDEFINITE_ON_RIDE_PHOTO, STR_SHOP_ITEM_DISPLAY_ON_RIDE_PHOTO } }, + /* SHOP_ITEM_PHOTO4 */ { 2, 30, 30, 30, MONEY(0, 00), SPR_SHOP_ITEM_PHOTO4, { STR_SHOP_ITEM_PRICE_LABEL_ON_RIDE_PHOTO, STR_SHOP_ITEM_SINGULAR_ON_RIDE_PHOTO, STR_SHOP_ITEM_PLURAL_ON_RIDE_PHOTO, STR_SHOP_ITEM_INDEFINITE_ON_RIDE_PHOTO, STR_SHOP_ITEM_DISPLAY_ON_RIDE_PHOTO } }, + /* SHOP_ITEM_PRETZEL */ { 5, 11, 11, 11, MONEY(1, 10), SPR_SHOP_ITEM_PRETZEL, { STR_SHOP_ITEM_PRICE_LABEL_PRETZEL, STR_SHOP_ITEM_SINGULAR_PRETZEL, STR_SHOP_ITEM_PLURAL_PRETZEL, STR_SHOP_ITEM_INDEFINITE_PRETZEL, STR_SHOP_ITEM_DISPLAY_PRETZEL } }, + /* SHOP_ITEM_CHOCOLATE */ { 4, 13, 13, 20, MONEY(1, 20), SPR_SHOP_ITEM_CHOCOLATE, { STR_SHOP_ITEM_PRICE_LABEL_HOT_CHOCOLATE, STR_SHOP_ITEM_SINGULAR_HOT_CHOCOLATE, STR_SHOP_ITEM_PLURAL_HOT_CHOCOLATE, STR_SHOP_ITEM_INDEFINITE_HOT_CHOCOLATE, STR_SHOP_ITEM_DISPLAY_HOT_CHOCOLATE } }, + /* SHOP_ITEM_ICED_TEA */ { 3, 10, 20, 10, MONEY(1, 10), SPR_SHOP_ITEM_ICED_TEA, { STR_SHOP_ITEM_PRICE_LABEL_ICED_TEA, STR_SHOP_ITEM_SINGULAR_ICED_TEA, STR_SHOP_ITEM_PLURAL_ICED_TEA, STR_SHOP_ITEM_INDEFINITE_ICED_TEA, STR_SHOP_ITEM_DISPLAY_ICED_TEA } }, + /* SHOP_ITEM_FUNNEL_CAKE */ { 5, 13, 11, 14, MONEY(1, 20), SPR_SHOP_ITEM_FUNNEL_CAKE, { STR_SHOP_ITEM_PRICE_LABEL_FUNNEL_CAKE, STR_SHOP_ITEM_SINGULAR_FUNNEL_CAKE, STR_SHOP_ITEM_PLURAL_FUNNEL_CAKE, STR_SHOP_ITEM_INDEFINITE_FUNNEL_CAKE, STR_SHOP_ITEM_DISPLAY_FUNNEL_CAKE } }, + /* SHOP_ITEM_SUNGLASSES */ { 8, 15, 20, 12, MONEY(1, 50), SPR_SHOP_ITEM_SUNGLASSES, { STR_SHOP_ITEM_PRICE_LABEL_SUNGLASSES, STR_SHOP_ITEM_SINGULAR_SUNGLASSES, STR_SHOP_ITEM_PLURAL_SUNGLASSES, STR_SHOP_ITEM_INDEFINITE_SUNGLASSES, STR_SHOP_ITEM_DISPLAY_SUNGLASSES } }, + /* SHOP_ITEM_BEEF_NOODLES */ { 7, 17, 17, 20, MONEY(1, 50), SPR_SHOP_ITEM_BEEF_NOODLES, { STR_SHOP_ITEM_PRICE_LABEL_BEEF_NOODLES, STR_SHOP_ITEM_SINGULAR_BEEF_NOODLES, STR_SHOP_ITEM_PLURAL_BEEF_NOODLES, STR_SHOP_ITEM_INDEFINITE_BEEF_NOODLES, STR_SHOP_ITEM_DISPLAY_BEEF_NOODLES } }, + /* SHOP_ITEM_FRIED_RICE_NOODLES */ { 6, 17, 17, 20, MONEY(1, 50), SPR_SHOP_ITEM_FRIED_RICE_NOODLES, { STR_SHOP_ITEM_PRICE_LABEL_FRIED_RICE_NOODLES, STR_SHOP_ITEM_SINGULAR_FRIED_RICE_NOODLES, STR_SHOP_ITEM_PLURAL_FRIED_RICE_NOODLES, STR_SHOP_ITEM_INDEFINITE_FRIED_RICE_NOODLES, STR_SHOP_ITEM_DISPLAY_FRIED_RICE_NOODLES } }, + /* SHOP_ITEM_WONTON_SOUP */ { 4, 13, 13, 15, MONEY(1, 50), SPR_SHOP_ITEM_WONTON_SOUP, { STR_SHOP_ITEM_PRICE_LABEL_WONTON_SOUP, STR_SHOP_ITEM_SINGULAR_WONTON_SOUP, STR_SHOP_ITEM_PLURAL_WONTON_SOUP, STR_SHOP_ITEM_INDEFINITE_WONTON_SOUP, STR_SHOP_ITEM_DISPLAY_WONTON_SOUP } }, + /* SHOP_ITEM_MEATBALL_SOUP */ { 5, 14, 14, 16, MONEY(1, 50), SPR_SHOP_ITEM_MEATBALL_SOUP, { STR_SHOP_ITEM_PRICE_LABEL_MEATBALL_SOUP, STR_SHOP_ITEM_SINGULAR_MEATBALL_SOUP, STR_SHOP_ITEM_PLURAL_MEATBALL_SOUP, STR_SHOP_ITEM_INDEFINITE_MEATBALL_SOUP, STR_SHOP_ITEM_DISPLAY_MEATBALL_SOUP } }, + /* SHOP_ITEM_FRUIT_JUICE */ { 4, 11, 19, 11, MONEY(1, 20), SPR_SHOP_ITEM_FRUIT_JUICE, { STR_SHOP_ITEM_PRICE_LABEL_FRUIT_JUICE, STR_SHOP_ITEM_SINGULAR_FRUIT_JUICE, STR_SHOP_ITEM_PLURAL_FRUIT_JUICE, STR_SHOP_ITEM_INDEFINITE_FRUIT_JUICE, STR_SHOP_ITEM_DISPLAY_FRUIT_JUICE } }, + /* SHOP_ITEM_SOYBEAN_MILK */ { 4, 10, 14, 10, MONEY(1, 20), SPR_SHOP_ITEM_SOYBEAN_MILK, { STR_SHOP_ITEM_PRICE_LABEL_SOYBEAN_MILK, STR_SHOP_ITEM_SINGULAR_SOYBEAN_MILK, STR_SHOP_ITEM_PLURAL_SOYBEAN_MILK, STR_SHOP_ITEM_INDEFINITE_SOYBEAN_MILK, STR_SHOP_ITEM_DISPLAY_SOYBEAN_MILK } }, + /* SHOP_ITEM_SUJEONGGWA */ { 3, 11, 14, 11, MONEY(1, 20), SPR_SHOP_ITEM_SUJEONGGWA, { STR_SHOP_ITEM_PRICE_LABEL_SUJONGKWA, STR_SHOP_ITEM_SINGULAR_SUJONGKWA, STR_SHOP_ITEM_PLURAL_SUJONGKWA, STR_SHOP_ITEM_INDEFINITE_SUJONGKWA, STR_SHOP_ITEM_DISPLAY_SUJONGKWA } }, + /* SHOP_ITEM_SUB_SANDWICH */ { 5, 19, 19, 17, MONEY(1, 50), SPR_SHOP_ITEM_SUB_SANDWICH, { STR_SHOP_ITEM_PRICE_LABEL_SUB_SANDWICH, STR_SHOP_ITEM_SINGULAR_SUB_SANDWICH, STR_SHOP_ITEM_PLURAL_SUB_SANDWICH, STR_SHOP_ITEM_INDEFINITE_SUB_SANDWICH, STR_SHOP_ITEM_DISPLAY_SUB_SANDWICH } }, + /* SHOP_ITEM_COOKIE */ { 4, 8, 8, 8, MONEY(0, 70), SPR_SHOP_ITEM_COOKIE, { STR_SHOP_ITEM_PRICE_LABEL_COOKIE, STR_SHOP_ITEM_SINGULAR_COOKIE, STR_SHOP_ITEM_PLURAL_COOKIE, STR_SHOP_ITEM_INDEFINITE_COOKIE, STR_SHOP_ITEM_DISPLAY_COOKIE } }, + /* SHOP_ITEM_EMPTY_BOWL_RED */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_EMPTY_BOWL_RED, { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_BOWL_RED, STR_SHOP_ITEM_SINGULAR_EMPTY_BOWL_RED, STR_SHOP_ITEM_PLURAL_EMPTY_BOWL_RED, STR_SHOP_ITEM_INDEFINITE_EMPTY_BOWL_RED, STR_SHOP_ITEM_DISPLAY_EMPTY_BOWL_RED } }, + /* SHOP_ITEM_EMPTY_DRINK_CARTON */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_EMPTY_DRINK_CARTON, { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_DRINK_CARTON, STR_SHOP_ITEM_SINGULAR_EMPTY_DRINK_CARTON, STR_SHOP_ITEM_PLURAL_EMPTY_DRINK_CARTON, STR_SHOP_ITEM_INDEFINITE_EMPTY_DRINK_CARTON, STR_SHOP_ITEM_DISPLAY_EMPTY_DRINK_CARTON } }, + /* SHOP_ITEM_EMPTY_JUICE_CUP */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_EMPTY_JUICE_CUP, { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_JUICE_CUP, STR_SHOP_ITEM_SINGULAR_EMPTY_JUICE_CUP, STR_SHOP_ITEM_PLURAL_EMPTY_JUICE_CUP, STR_SHOP_ITEM_INDEFINITE_EMPTY_JUICE_CUP, STR_SHOP_ITEM_DISPLAY_EMPTY_JUICE_CUP } }, + /* SHOP_ITEM_ROAST_SAUSAGE */ { 5, 16, 16, 20, MONEY(1, 50), SPR_SHOP_ITEM_ROAST_SAUSAGE, { STR_SHOP_ITEM_PRICE_LABEL_ROAST_SAUSAGE, STR_SHOP_ITEM_SINGULAR_ROAST_SAUSAGE, STR_SHOP_ITEM_PLURAL_ROAST_SAUSAGE, STR_SHOP_ITEM_INDEFINITE_ROAST_SAUSAGE, STR_SHOP_ITEM_DISPLAY_ROAST_SAUSAGE } }, + /* SHOP_ITEM_EMPTY_BOWL_BLUE */ { 0, 0, 0, 0, MONEY(0, 00), SPR_SHOP_ITEM_EMPTY_BOWL_BLUE, { STR_SHOP_ITEM_PRICE_LABEL_EMPTY_BOWL_BLUE, STR_SHOP_ITEM_SINGULAR_EMPTY_BOWL_BLUE, STR_SHOP_ITEM_PLURAL_EMPTY_BOWL_BLUE, STR_SHOP_ITEM_INDEFINITE_EMPTY_BOWL_BLUE, STR_SHOP_ITEM_DISPLAY_EMPTY_BOWL_BLUE } }, }; // clang-format on -const uint32_t ShopItemImage[SHOP_ITEM_COUNT] = { - SPR_SHOP_ITEM_BALLOON, - SPR_SHOP_ITEM_TOY, - SPR_SHOP_ITEM_MAP, - SPR_SHOP_ITEM_PHOTO, - SPR_SHOP_ITEM_UMBRELLA, - SPR_SHOP_ITEM_DRINK, - SPR_SHOP_ITEM_BURGER, - SPR_SHOP_ITEM_CHIPS, - SPR_SHOP_ITEM_ICE_CREAM, - SPR_SHOP_ITEM_CANDYFLOSS, - SPR_SHOP_ITEM_EMPTY_CAN, - SPR_SHOP_ITEM_RUBBISH, - SPR_SHOP_ITEM_EMPTY_BURGER_BOX, - SPR_SHOP_ITEM_PIZZA, - SPR_SHOP_ITEM_VOUCHER, - SPR_SHOP_ITEM_POPCORN, - SPR_SHOP_ITEM_HOT_DOG, - SPR_SHOP_ITEM_TENTACLE, - SPR_SHOP_ITEM_HAT, - SPR_SHOP_ITEM_TOFFEE_APPLE, - SPR_SHOP_ITEM_TSHIRT, - SPR_SHOP_ITEM_DOUGHNUT, - SPR_SHOP_ITEM_COFFEE, - SPR_SHOP_ITEM_EMPTY_CUP, - SPR_SHOP_ITEM_CHICKEN, - SPR_SHOP_ITEM_LEMONADE, - SPR_SHOP_ITEM_EMPTY_BOX, - SPR_SHOP_ITEM_EMPTY_BOTTLE, - 0, // 28 - 0, // 29 - 0, // 30 - 0, // 31 - SPR_SHOP_ITEM_PHOTO2, - SPR_SHOP_ITEM_PHOTO3, - SPR_SHOP_ITEM_PHOTO4, - SPR_SHOP_ITEM_PRETZEL, - SPR_SHOP_ITEM_CHOCOLATE, - SPR_SHOP_ITEM_ICED_TEA, - SPR_SHOP_ITEM_FUNNEL_CAKE, - SPR_SHOP_ITEM_SUNGLASSES, - SPR_SHOP_ITEM_BEEF_NOODLES, - SPR_SHOP_ITEM_FRIED_RICE_NOODLES, - SPR_SHOP_ITEM_WONTON_SOUP, - SPR_SHOP_ITEM_MEATBALL_SOUP, - SPR_SHOP_ITEM_FRUIT_JUICE, - SPR_SHOP_ITEM_SOYBEAN_MILK, - SPR_SHOP_ITEM_SUJEONGGWA, - SPR_SHOP_ITEM_SUB_SANDWICH, - SPR_SHOP_ITEM_COOKIE, - SPR_SHOP_ITEM_EMPTY_BOWL_RED, - SPR_SHOP_ITEM_EMPTY_DRINK_CARTON, - SPR_SHOP_ITEM_EMPTY_JUICE_CUP, - SPR_SHOP_ITEM_ROAST_SAUSAGE, - SPR_SHOP_ITEM_EMPTY_BOWL_BLUE, -}; - -money32 get_shop_item_cost(int32_t shopItem) -{ - return ShopItemStats[shopItem].cost; -} - -money16 get_shop_base_value(int32_t shopItem) -{ - return ShopItemStats[shopItem].base_value; -} - -money16 get_shop_cold_value(int32_t shopItem) -{ - return ShopItemStats[shopItem].cold_value; -} - -money16 get_shop_hot_value(int32_t shopItem) -{ - return ShopItemStats[shopItem].hot_value; -} - money32 shop_item_get_common_price(Ride* forRide, int32_t shopItem) { - rct_ride_entry* rideEntry; - Ride* ride; - int32_t i; - - FOR_ALL_RIDES (i, ride) + for (const auto& ride : GetRideManager()) { - if (ride != forRide) + if (&ride != forRide) { - rideEntry = get_ride_entry(ride->subtype); + auto rideEntry = ride.GetRideEntry(); if (rideEntry == nullptr) { continue; } if (rideEntry->shop_item == shopItem) { - return ride->price; + return ride.price; } if (rideEntry->shop_item_secondary == shopItem) { - return ride->price_secondary; + return ride.price_secondary; } - if (shop_item_is_photo(shopItem) && (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO)) + if (shop_item_is_photo(shopItem) && (ride.lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO)) { - return ride->price_secondary; + return ride.price_secondary; } } } @@ -313,14 +114,7 @@ bool shop_item_is_photo(int32_t shopItem) bool shop_item_has_common_price(int32_t shopItem) { - if (shopItem < 32) - { - return (gSamePriceThroughoutParkA & (1u << shopItem)) != 0; - } - else - { - return (gSamePriceThroughoutParkB & (1u << (shopItem - 32))) != 0; - } + return (gSamePriceThroughoutPark & (1ULL << shopItem)) != 0; } bool shop_item_is_food_or_drink(int32_t shopItem) diff --git a/src/openrct2/ride/ShopItem.h b/src/openrct2/ride/ShopItem.h index 159ce77526..5d22872562 100644 --- a/src/openrct2/ride/ShopItem.h +++ b/src/openrct2/ride/ShopItem.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -70,34 +70,29 @@ enum SHOP_ITEM_NONE = 255 }; -struct rct_shop_item_stats +struct ShopItemStrings { - uint16_t cost; - uint16_t base_value; - uint16_t hot_value; - uint16_t cold_value; + rct_string_id PriceLabel; // Balloon price: + rct_string_id Singular; // Balloon + rct_string_id Plural; // Balloons + rct_string_id Indefinite; // a Balloon + rct_string_id Display; // "Diamond Heights" Balloon }; -struct rct_shop_item_string_types +struct ShopItemDescriptor { - rct_string_id price_label; // Balloon price: - rct_string_id singular; // Balloon - rct_string_id plural; // Balloons - rct_string_id indefinite; // a Balloon - rct_string_id display; // "Diamond Heights" Balloon + uint16_t Cost; + uint16_t BaseValue; + uint16_t HotValue; + uint16_t ColdValue; + money8 DefaultPrice; + uint32_t Image; + ShopItemStrings Naming; }; -extern uint32_t gSamePriceThroughoutParkA; -extern uint32_t gSamePriceThroughoutParkB; +extern const ShopItemDescriptor ShopItems[SHOP_ITEM_COUNT]; +extern uint64_t gSamePriceThroughoutPark; -extern const money8 DefaultShopItemPrice[SHOP_ITEM_COUNT]; -extern const rct_shop_item_string_types ShopItemStringIds[SHOP_ITEM_COUNT]; -extern const uint32_t ShopItemImage[SHOP_ITEM_COUNT]; - -money32 get_shop_item_cost(int32_t shopItem); -money16 get_shop_base_value(int32_t shopItem); -money16 get_shop_hot_value(int32_t shopItem); -money16 get_shop_cold_value(int32_t shopItem); money32 shop_item_get_common_price(Ride* forRide, int32_t shopItem); bool shop_item_is_photo(int32_t shopItem); bool shop_item_has_common_price(int32_t shopItem); diff --git a/src/openrct2/ride/Station.cpp b/src/openrct2/ride/Station.cpp index 0953e28283..891ddd94d9 100644 --- a/src/openrct2/ride/Station.cpp +++ b/src/openrct2/ride/Station.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -284,23 +284,29 @@ static void ride_race_init_vehicle_speeds(Ride* ride) { Peep* peep = &get_sprite(vehicle->peep[0])->peep; - switch (peep_get_easteregg_name_id(peep)) + // Easter egg names should only work on guests + Guest* guest = peep->AsGuest(); + + if (guest != nullptr) { - case EASTEREGG_PEEP_NAME_MICHAEL_SCHUMACHER: - vehicle->speed += 35; - break; - case EASTEREGG_PEEP_NAME_JACQUES_VILLENEUVE: - vehicle->speed += 25; - break; - case EASTEREGG_PEEP_NAME_DAMON_HILL: - vehicle->speed += 55; - break; - case EASTEREGG_PEEP_NAME_CHRIS_SAWYER: - vehicle->speed += 14; - break; - case EASTEREGG_PEEP_NAME_MR_BEAN: - vehicle->speed = 9; - break; + switch (guest->GetEasterEggNameId()) + { + case EASTEREGG_PEEP_NAME_MICHAEL_SCHUMACHER: + vehicle->speed += 35; + break; + case EASTEREGG_PEEP_NAME_JACQUES_VILLENEUVE: + vehicle->speed += 25; + break; + case EASTEREGG_PEEP_NAME_DAMON_HILL: + vehicle->speed += 55; + break; + case EASTEREGG_PEEP_NAME_CHRIS_SAWYER: + vehicle->speed += 14; + break; + case EASTEREGG_PEEP_NAME_MR_BEAN: + vehicle->speed = 9; + break; + } } } } @@ -334,6 +340,8 @@ TileElement* ride_get_station_start_track_element(Ride* ride, int32_t stationInd // Find the station track element TileElement* tileElement = map_get_first_element_at(x, y); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK && z == tileElement->base_height) @@ -348,8 +356,12 @@ TileElement* ride_get_station_exit_element(int32_t x, int32_t y, int32_t z) { // Find the station track element TileElement* tileElement = map_get_first_element_at(x, y); + if (tileElement == nullptr) + return nullptr; do { + if (tileElement == nullptr) + break; if (tileElement->GetType() == TILE_ELEMENT_TYPE_ENTRANCE && z == tileElement->base_height) return tileElement; } while (!(tileElement++)->IsLastForTile()); diff --git a/src/openrct2/ride/Station.h b/src/openrct2/ride/Station.h index e37f6db9af..9b9c754938 100644 --- a/src/openrct2/ride/Station.h +++ b/src/openrct2/ride/Station.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/Track.cpp b/src/openrct2/ride/Track.cpp index b5421f73e5..95c047ad6c 100644 --- a/src/openrct2/ride/Track.cpp +++ b/src/openrct2/ride/Track.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -35,8 +35,6 @@ #include "TrackData.h" #include "TrackDesign.h" -uint8_t gTrackGroundFlags; - /** rct2: 0x00997C9D */ // clang-format off const rct_trackdefinition TrackDefinitions[256] = @@ -601,6 +599,8 @@ const rct_preview_track* get_track_def_from_ride_index(ride_id_t rideIndex, int3 static TileElement* find_station_element(int32_t x, int32_t y, int32_t z, int32_t direction, ride_id_t rideIndex) { TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return nullptr; do { if (z != tileElement->base_height) @@ -638,13 +638,16 @@ static void ride_remove_station(Ride* ride, int32_t x, int32_t y, int32_t z) */ bool track_add_station_element(int32_t x, int32_t y, int32_t z, int32_t direction, ride_id_t rideIndex, int32_t flags) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return false; + int32_t stationX0 = x; int32_t stationY0 = y; int32_t stationX1 = x; int32_t stationY1 = y; int32_t stationLength = 1; - Ride* ride = get_ride(rideIndex); if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_3)) { if (ride->num_stations >= MAX_STATIONS) @@ -789,6 +792,10 @@ bool track_add_station_element(int32_t x, int32_t y, int32_t z, int32_t directio */ bool track_remove_station_element(int32_t x, int32_t y, int32_t z, int32_t direction, ride_id_t rideIndex, int32_t flags) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return false; + int32_t removeX = x; int32_t removeY = y; int32_t stationX0 = x; @@ -798,7 +805,6 @@ bool track_remove_station_element(int32_t x, int32_t y, int32_t z, int32_t direc int32_t stationLength = 0; int32_t byte_F441D1 = -1; - Ride* ride = get_ride(rideIndex); if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_3)) { TileElement* tileElement = map_get_track_element_at_with_direction_from_ride(x, y, z, direction, rideIndex); @@ -946,373 +952,6 @@ bool track_remove_station_element(int32_t x, int32_t y, int32_t z, int32_t direc return true; } -static money32 track_remove( - uint8_t type, uint8_t sequence, int16_t originX, int16_t originY, int16_t originZ, uint8_t rotation, uint8_t flags) -{ - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; - gCommandPosition.x = originX + 16; - gCommandPosition.y = originY + 16; - gCommandPosition.z = originZ; - int16_t trackpieceZ = originZ; - - switch (type) - { - case TRACK_ELEM_BEGIN_STATION: - case TRACK_ELEM_MIDDLE_STATION: - type = TRACK_ELEM_END_STATION; - break; - } - - if (!(flags & (1 << 3)) && game_is_paused() && !gCheatsBuildInPauseMode) - { - gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - return MONEY32_UNDEFINED; - } - - bool found = false; - bool isGhost = flags & GAME_COMMAND_FLAG_GHOST; - TileElement* tileElement = map_get_first_element_at(originX / 32, originY / 32); - if (tileElement == nullptr) - { - log_warning("Invalid coordinates for track removal. x = %d, y = %d", originX, originY); - return MONEY32_UNDEFINED; - } - do - { - if (tileElement->base_height * 8 != originZ) - continue; - - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - - if ((tileElement->GetDirection()) != rotation) - continue; - - if (tileElement->AsTrack()->GetSequenceIndex() != sequence) - continue; - - if (tileElement->IsGhost() != isGhost) - continue; - - uint8_t track_type = tileElement->AsTrack()->GetTrackType(); - switch (track_type) - { - case TRACK_ELEM_BEGIN_STATION: - case TRACK_ELEM_MIDDLE_STATION: - track_type = TRACK_ELEM_END_STATION; - break; - } - - if (track_type != type) - continue; - - found = true; - break; - } while (!(tileElement++)->IsLastForTile()); - - if (!found) - { - return MONEY32_UNDEFINED; - } - - if (tileElement->AsTrack()->IsIndestructible()) - { - gGameCommandErrorText = STR_YOU_ARE_NOT_ALLOWED_TO_REMOVE_THIS_SECTION; - return MONEY32_UNDEFINED; - } - - ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - type = tileElement->AsTrack()->GetTrackType(); - bool isLiftHill = tileElement->AsTrack()->HasChain(); - - Ride* ride = get_ride(rideIndex); - const rct_preview_track* trackBlock = get_track_def_from_ride(ride, type); - trackBlock += tileElement->AsTrack()->GetSequenceIndex(); - - uint8_t originDirection = tileElement->GetDirection(); - switch (originDirection) - { - case 0: - originX -= trackBlock->x; - originY -= trackBlock->y; - break; - case 1: - originX -= trackBlock->y; - originY += trackBlock->x; - break; - case 2: - originX += trackBlock->x; - originY += trackBlock->y; - break; - case 3: - originX += trackBlock->y; - originY -= trackBlock->x; - break; - } - - originZ -= trackBlock->z; - - money32 cost = 0; - - trackBlock = get_track_def_from_ride(ride, type); - for (; trackBlock->index != 255; trackBlock++) - { - int16_t x = originX, y = originY, z = originZ; - - switch (originDirection) - { - case 0: - x += trackBlock->x; - y += trackBlock->y; - break; - case 1: - x += trackBlock->y; - y -= trackBlock->x; - break; - case 2: - x -= trackBlock->x; - y -= trackBlock->y; - break; - case 3: - x -= trackBlock->y; - y += trackBlock->x; - break; - } - - z += trackBlock->z; - - map_invalidate_tile_full(x, y); - - trackpieceZ = z; - - found = false; - tileElement = map_get_first_element_at(x / 32, y / 32); - do - { - if (tileElement == nullptr) - break; - - if (tileElement->base_height != z / 8) - continue; - - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - - if (tileElement->GetDirection() != rotation) - continue; - - if (tileElement->AsTrack()->GetSequenceIndex() != trackBlock->index) - continue; - - if (tileElement->AsTrack()->GetTrackType() != type) - continue; - - if (tileElement->IsGhost() != isGhost) - continue; - - found = true; - break; - } while (!(tileElement++)->IsLastForTile()); - - if (!found) - { - log_error("Track map element part not found!"); - return MONEY32_UNDEFINED; - } - - int32_t entranceDirections; - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) - { - entranceDirections = FlatRideTrackSequenceProperties[type][0]; - } - else - { - entranceDirections = TrackSequenceProperties[type][0]; - } - - if (entranceDirections & TRACK_SEQUENCE_FLAG_ORIGIN && (tileElement->AsTrack()->GetSequenceIndex() == 0)) - { - if (!track_remove_station_element(x, y, z / 8, rotation, rideIndex, 0)) - { - return MONEY32_UNDEFINED; - } - } - - TileElement* surfaceElement = map_get_surface_element_at({ x, y }); - if (surfaceElement == nullptr) - { - return MONEY32_UNDEFINED; - } - - int8_t _support_height = tileElement->base_height - surfaceElement->base_height; - if (_support_height < 0) - { - _support_height = 10; - } - - cost += (_support_height / 2) * RideTrackCosts[ride->type].support_price; - - if (!(flags & GAME_COMMAND_FLAG_APPLY)) - continue; - - if (entranceDirections & (1 << 4) && (tileElement->AsTrack()->GetSequenceIndex() == 0)) - { - if (!track_remove_station_element(x, y, z / 8, rotation, rideIndex, GAME_COMMAND_FLAG_APPLY)) - { - return MONEY32_UNDEFINED; - } - } - - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_TRACK_MUST_BE_ON_WATER)) - { - surfaceElement->AsSurface()->SetHasTrackThatNeedsWater(false); - } - - invalidate_test_results(ride); - footpath_queue_chain_reset(); - if (!gCheatsDisableClearanceChecks || !(tileElement->IsGhost())) - { - footpath_remove_edges_at(x, y, tileElement); - } - tile_element_remove(tileElement); - sub_6CB945(ride); - if (!(flags & GAME_COMMAND_FLAG_GHOST)) - { - ride_update_max_vehicles(ride); - } - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - switch (type) - { - case TRACK_ELEM_ON_RIDE_PHOTO: - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_ON_RIDE_PHOTO; - break; - case TRACK_ELEM_CABLE_LIFT_HILL: - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_CABLE_LIFT_HILL_COMPONENT_USED; - break; - case TRACK_ELEM_BLOCK_BRAKES: - ride->num_block_brakes--; - if (ride->num_block_brakes == 0) - { - ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_OPERATING; - ride->mode = RIDE_MODE_CONTINUOUS_CIRCUIT; - if (ride->type == RIDE_TYPE_LIM_LAUNCHED_ROLLER_COASTER) - { - ride->mode = RIDE_MODE_POWERED_LAUNCH; - } - } - break; - } - - switch (type) - { - case TRACK_ELEM_25_DEG_UP_TO_FLAT: - case TRACK_ELEM_60_DEG_UP_TO_FLAT: - case TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT: - case TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT: - if (!isLiftHill) - break; - [[fallthrough]]; - case TRACK_ELEM_CABLE_LIFT_HILL: - ride->num_block_brakes--; - break; - } - } - - money32 price = RideTrackCosts[ride->type].track_price; - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) - { - price *= FlatRideTrackPricing[type]; - } - else - { - price *= TrackPricing[type]; - } - price >>= 16; - price = (price + cost) / 2; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED) - price *= -7; - else - price *= -10; - - if (gGameCommandNestLevel == 1) - { - LocationXYZ16 coord; - coord.x = originX + 16; - coord.y = originY + 16; - coord.z = trackpieceZ; - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - - if (gParkFlags & PARK_FLAGS_NO_MONEY) - return 0; - else - return price; -} - -/** - * - * rct2: 0x006C5B69 - */ -void game_command_remove_track( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, - [[maybe_unused]] int32_t* ebp) -{ - *ebx = track_remove( - *edx & 0xFF, (*edx >> 8) & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edi & 0xFFFF, (*ebx >> 8) & 0xFF, *ebx & 0xFF); -} - -/** - * - * rct2: 0x006C5AE9 - */ -void game_command_set_brakes_speed( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, - [[maybe_unused]] int32_t* ebp) -{ - int32_t x = (*eax & 0xFFFF); - int32_t y = (*ecx & 0xFFFF); - int32_t z = (*edi & 0xFFFF); - int32_t trackType = (*edx & 0xFF); - int32_t brakesSpeed = ((*ebx >> 8) & 0xFF); - - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = z; - - if (*ebx & GAME_COMMAND_FLAG_APPLY) - { - *ebx = 0; - return; - } - - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); - if (tileElement == nullptr) - { - log_warning("Invalid game command for setting brakes speed. x = %d, y = %d", x, y); - *ebx = MONEY32_UNDEFINED; - return; - } - do - { - if (tileElement->base_height * 8 != z) - continue; - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - if (tileElement->AsTrack()->GetTrackType() != trackType) - continue; - - tileElement->AsTrack()->SetBrakeBoosterSpeed(brakesSpeed); - - break; - } while (!(tileElement++)->IsLastForTile()); - - *ebx = 0; -} - void track_circuit_iterator_begin(track_circuit_iterator* it, CoordsXYE first) { it->last = first; @@ -1437,18 +1076,18 @@ void track_get_front(CoordsXYE* input, CoordsXYE* output) bool TrackElement::HasChain() const { - return type & TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; + return Flags2 & TRACK_ELEMENT_FLAGS2_CHAIN_LIFT; } void TrackElement::SetHasChain(bool on) { if (on) { - type |= TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; + Flags2 |= TRACK_ELEMENT_FLAGS2_CHAIN_LIFT; } else { - type &= ~TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; + Flags2 &= ~TRACK_ELEMENT_FLAGS2_CHAIN_LIFT; } } @@ -1480,9 +1119,13 @@ bool track_element_is_block_start(TileElement* trackElement) int32_t track_get_actual_bank(TileElement* tileElement, int32_t bank) { - Ride* ride = get_ride(tileElement->AsTrack()->GetRideIndex()); - bool isInverted = tileElement->AsTrack()->IsInverted(); - return track_get_actual_bank_2(ride->type, isInverted, bank); + auto ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + if (ride != nullptr) + { + bool isInverted = tileElement->AsTrack()->IsInverted(); + return track_get_actual_bank_2(ride->type, isInverted, bank); + } + return bank; } int32_t track_get_actual_bank_2(int32_t rideType, bool isInverted, int32_t bank) @@ -1506,12 +1149,15 @@ int32_t track_get_actual_bank_2(int32_t rideType, bool isInverted, int32_t bank) int32_t track_get_actual_bank_3(rct_vehicle* vehicle, TileElement* tileElement) { + auto trackType = tileElement->AsTrack()->GetTrackType(); + auto bankStart = TrackDefinitions[trackType].bank_start; + auto ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + if (ride == nullptr) + return bankStart; + bool isInverted = ((vehicle->update_flags & VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES) > 0) ^ tileElement->AsTrack()->IsInverted(); - int32_t trackType = tileElement->AsTrack()->GetTrackType(); - int32_t rideType = get_ride(tileElement->AsTrack()->GetRideIndex())->type; - int32_t bankStart = TrackDefinitions[trackType].bank_start; - return track_get_actual_bank_2(rideType, isInverted, bankStart); + return track_get_actual_bank_2(ride->type, isInverted, bankStart); } bool track_element_is_station(TileElement* trackElement) @@ -1571,150 +1217,159 @@ bool track_element_has_speed_setting(uint8_t trackType) uint8_t TrackElement::GetSeatRotation() const { - return colour >> 4; + return ColourScheme >> 4; } void TrackElement::SetSeatRotation(uint8_t newSeatRotation) { - colour &= 0x0F; - colour |= (newSeatRotation << 4); + ColourScheme &= ~TRACK_ELEMENT_COLOUR_SEAT_ROTATION_MASK; + ColourScheme |= (newSeatRotation << 4); } bool TrackElement::IsTakingPhoto() const { - return (sequence & MAP_ELEM_TRACK_SEQUENCE_TAKING_PHOTO_MASK) != 0; + return OnridePhotoBits != 0; } void TrackElement::SetPhotoTimeout() { - sequence &= MAP_ELEM_TRACK_SEQUENCE_SEQUENCE_MASK; - sequence |= (3 << 4); + OnridePhotoBits = 3; } void TrackElement::SetPhotoTimeout(uint8_t value) { - sequence &= MAP_ELEM_TRACK_SEQUENCE_SEQUENCE_MASK; - sequence |= (value << 4); + OnridePhotoBits = value; +} + +uint8_t TrackElement::GetPhotoTimeout() const +{ + return OnridePhotoBits; } void TrackElement::DecrementPhotoTimeout() { - // We should only touch the upper 4 bits, avoid underflow into the lower 4. - if (sequence & MAP_ELEM_TRACK_SEQUENCE_TAKING_PHOTO_MASK) - { - sequence -= (1 << 4); - } + OnridePhotoBits = std::max(0, OnridePhotoBits - 1); } uint16_t TrackElement::GetMazeEntry() const { - return mazeEntry; + return MazeEntry; } void TrackElement::SetMazeEntry(uint16_t newMazeEntry) { - mazeEntry = newMazeEntry; + MazeEntry = newMazeEntry; } void TrackElement::MazeEntryAdd(uint16_t addVal) { - mazeEntry |= addVal; + MazeEntry |= addVal; } void TrackElement::MazeEntrySubtract(uint16_t subVal) { - mazeEntry &= ~subVal; + MazeEntry &= ~subVal; } -uint8_t TrackElement::GetTrackType() const +uint16_t TrackElement::GetTrackType() const { - return trackType; + return TrackType; } -void TrackElement::SetTrackType(uint8_t newType) +void TrackElement::SetTrackType(uint16_t newType) { - trackType = newType; + TrackType = newType; } uint8_t TrackElement::GetSequenceIndex() const { - return sequence & MAP_ELEM_TRACK_SEQUENCE_SEQUENCE_MASK; + return Sequence; } void TrackElement::SetSequenceIndex(uint8_t newSequenceIndex) { - sequence &= ~MAP_ELEM_TRACK_SEQUENCE_SEQUENCE_MASK; - sequence |= (newSequenceIndex & MAP_ELEM_TRACK_SEQUENCE_SEQUENCE_MASK); + Sequence = newSequenceIndex; } uint8_t TrackElement::GetStationIndex() const { - return (sequence & MAP_ELEM_TRACK_SEQUENCE_STATION_INDEX_MASK) >> 4; + return StationIndex; } void TrackElement::SetStationIndex(uint8_t newStationIndex) { - sequence &= ~MAP_ELEM_TRACK_SEQUENCE_STATION_INDEX_MASK; - sequence |= (newStationIndex << 4); + StationIndex = newStationIndex; } uint8_t TrackElement::GetDoorAState() const { - return (colour & TRACK_ELEMENT_DOOR_A_MASK) >> 2; + return (ColourScheme & TRACK_ELEMENT_COLOUR_DOOR_A_MASK) >> 2; } uint8_t TrackElement::GetDoorBState() const { - return (colour & TRACK_ELEMENT_DOOR_B_MASK) >> 5; + return (ColourScheme & TRACK_ELEMENT_COLOUR_DOOR_B_MASK) >> 5; } -ride_id_t TrackElement::GetRideIndex() const +void TrackElement::SetDoorAState(uint8_t newState) { - return rideIndex; + ColourScheme &= ~TRACK_ELEMENT_COLOUR_DOOR_A_MASK; + ColourScheme |= ((newState << 2) & TRACK_ELEMENT_COLOUR_DOOR_A_MASK); } -void TrackElement::SetRideIndex(ride_id_t newRideIndex) +void TrackElement::SetDoorBState(uint8_t newState) { - rideIndex = newRideIndex; + ColourScheme &= ~TRACK_ELEMENT_COLOUR_DOOR_B_MASK; + ColourScheme |= ((newState << 5) & TRACK_ELEMENT_COLOUR_DOOR_B_MASK); +} + +ride_idnew_t TrackElement::GetRideIndex() const +{ + return RideIndex; +} + +void TrackElement::SetRideIndex(ride_idnew_t newRideIndex) +{ + RideIndex = newRideIndex; } uint8_t TrackElement::GetColourScheme() const { - return colour & 0x3; + return ColourScheme & TRACK_ELEMENT_COLOUR_SCHEME_MASK; } void TrackElement::SetColourScheme(uint8_t newColourScheme) { - colour &= ~0x3; - colour |= (newColourScheme & 0x3); + ColourScheme &= ~TRACK_ELEMENT_COLOUR_SCHEME_MASK; + ColourScheme |= (newColourScheme & TRACK_ELEMENT_COLOUR_SCHEME_MASK); } bool TrackElement::HasCableLift() const { - return colour & TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; + return Flags2 & TRACK_ELEMENT_FLAGS2_CABLE_LIFT; } void TrackElement::SetHasCableLift(bool on) { - colour &= ~TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; + Flags2 &= ~TRACK_ELEMENT_FLAGS2_CABLE_LIFT; if (on) - colour |= TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; + Flags2 |= TRACK_ELEMENT_FLAGS2_CABLE_LIFT; } bool TrackElement::IsInverted() const { - return colour & TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + return Flags2 & TRACK_ELEMENT_FLAGS2_INVERTED; } void TrackElement::SetInverted(bool inverted) { if (inverted) { - colour |= TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + Flags2 |= TRACK_ELEMENT_FLAGS2_INVERTED; } else { - colour &= ~TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + Flags2 &= ~TRACK_ELEMENT_FLAGS2_INVERTED; } } @@ -1754,37 +1409,36 @@ void TrackElement::SetIsIndestructible(bool isIndestructible) uint8_t TrackElement::GetBrakeBoosterSpeed() const { - return (sequence >> 4) << 1; + return BrakeBoosterSpeed << 1; } void TrackElement::SetBrakeBoosterSpeed(uint8_t speed) { - sequence &= ~0b11110000; - sequence |= ((speed >> 1) << 4); + BrakeBoosterSpeed = (speed >> 1); } -uint8_t TrackElement::HasGreenLight() const +bool TrackElement::HasGreenLight() const { - return (sequence & MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT) != 0; + return (Flags2 & TRACK_ELEMENT_FLAGS2_HAS_GREEN_LIGHT) != 0; } -void TrackElement::SetHasGreenLight(uint8_t greenLight) +void TrackElement::SetHasGreenLight(bool on) { - sequence &= ~MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT; - if (greenLight) + Flags2 &= ~TRACK_ELEMENT_FLAGS2_HAS_GREEN_LIGHT; + if (on) { - sequence |= MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT; + Flags2 |= TRACK_ELEMENT_FLAGS2_HAS_GREEN_LIGHT; } } bool TrackElement::IsHighlighted() const { - return (type & TILE_ELEMENT_TYPE_FLAG_HIGHLIGHT); + return (Flags2 & TRACK_ELEMENT_FLAGS2_HIGHLIGHT); } void TrackElement::SetHighlight(bool on) { - type &= ~TILE_ELEMENT_TYPE_FLAG_HIGHLIGHT; + Flags2 &= ~TRACK_ELEMENT_FLAGS2_HIGHLIGHT; if (on) - type |= TILE_ELEMENT_TYPE_FLAG_HIGHLIGHT; + Flags2 |= TRACK_ELEMENT_FLAGS2_HIGHLIGHT; } diff --git a/src/openrct2/ride/Track.h b/src/openrct2/ride/Track.h index ef6bb84501..aee8b206c9 100644 --- a/src/openrct2/ride/Track.h +++ b/src/openrct2/ride/Track.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,8 @@ #include "../object/Object.h" #include "Ride.h" +typedef uint16_t track_type_t; + #pragma pack(push, 1) struct rct_trackdefinition { @@ -53,32 +55,27 @@ struct rct_track_coordinates enum { TRACK_ELEMENT_FLAG_TERMINAL_STATION = 1 << 3, - TRACK_ELEMENT_FLAG_INVERTED = 1 << 6, + TD6_TRACK_ELEMENT_FLAG_INVERTED = 1 << 6, }; enum { - TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT = 1 << 7, -}; - -enum -{ - // Not anything to do with colour but uses - // that field in the map element - - // Used for multi-dimension coaster - TRACK_ELEMENT_COLOUR_FLAG_INVERTED = (1 << 2), - + TRACK_ELEMENT_FLAGS2_CHAIN_LIFT = 1 << 0, + TRACK_ELEMENT_FLAGS2_INVERTED = 1 << 1, // Used for giga coaster - TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT = (1 << 3), - - TRACK_ELEMENT_DOOR_A_MASK = 0b00011100, - TRACK_ELEMENT_DOOR_B_MASK = 0b11100000, + TRACK_ELEMENT_FLAGS2_CABLE_LIFT = 1 << 2, + TRACK_ELEMENT_FLAGS2_HIGHLIGHT = 1 << 3, + TRACK_ELEMENT_FLAGS2_HAS_GREEN_LIGHT = 1 << 4, }; -#define TRACK_ELEMENT_FLAG_MAGNITUDE_MASK 0x0F -#define TRACK_ELEMENT_FLAG_COLOUR_MASK 0x30 -#define TRACK_ELEMENT_FLAG_STATION_NO_MASK 0x02 +enum +{ + TRACK_ELEMENT_COLOUR_SCHEME_MASK = 0b00000011, + // Not colour related, but shares the field. + TRACK_ELEMENT_COLOUR_DOOR_A_MASK = 0b00011100, + TRACK_ELEMENT_COLOUR_DOOR_B_MASK = 0b11100000, + TRACK_ELEMENT_COLOUR_SEAT_ROTATION_MASK = 0b11110000, +}; #define MAX_STATION_PLATFORM_LENGTH 32 @@ -197,6 +194,7 @@ enum TRACK_ELEM_FLAG_DOWN = (1 << 5), TRACK_ELEM_FLAG_UP = (1 << 6), TRACK_ELEM_FLAG_NORMAL_TO_INVERSION = (1 << 7), + TRACK_ELEM_FLAG_IS_GOLF_HOLE = (1 << 7), TRACK_ELEM_FLAG_STARTS_AT_HALF_HEIGHT = (1 << 8), TRACK_ELEM_FLAG_ONLY_ABOVE_GROUND = (1 << 9), TRACK_ELEM_FLAG_IS_STEEP_UP = (1 << 10), // Used to allow steep backwards lifts on roller coasters that do not allow steep @@ -312,9 +310,9 @@ enum TRACK_ELEM_BRAKES, TRACK_ELEM_ROTATION_CONTROL_TOGGLE = 100, TRACK_ELEM_BOOSTER = 100, - TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP = 101, TRACK_ELEM_MAZE = 101, - TRACK_ELEM_255_ALIAS = 101, // Used by the multi-dimension coaster, as TD6 cannot handle index 255. + TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP_ALIAS = 101, // Used by the multi-dimension coaster, as TD6 cannot handle + // index 255. TRACK_ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_UP, TRACK_ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_UP, TRACK_ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_DOWN, @@ -468,7 +466,7 @@ enum TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_90_DEG_DOWN, TRACK_ELEM_MULTIDIM_90_DEG_UP_TO_INVERTED_FLAT_QUARTER_LOOP, TRACK_ELEM_MULTIDIM_FLAT_TO_90_DEG_DOWN_QUARTER_LOOP, - TRACK_ELEM_255, + TRACK_ELEM_MULTIDIM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP, }; enum @@ -521,8 +519,6 @@ struct track_circuit_iterator extern const rct_trackdefinition FlatRideTrackDefinitions[256]; extern const rct_trackdefinition TrackDefinitions[256]; -extern uint8_t gTrackGroundFlags; - int32_t track_is_connected_by_shape(TileElement* a, TileElement* b); const rct_preview_track* get_track_def_from_ride(Ride* ride, int32_t trackType); @@ -548,16 +544,9 @@ int32_t track_get_actual_bank_3(rct_vehicle* vehicle, TileElement* tileElement); bool track_add_station_element(int32_t x, int32_t y, int32_t z, int32_t direction, ride_id_t rideIndex, int32_t flags); bool track_remove_station_element(int32_t x, int32_t y, int32_t z, int32_t direction, ride_id_t rideIndex, int32_t flags); -void game_command_remove_track( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); - -void game_command_set_maze_track( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); money32 maze_set_track( uint16_t x, uint16_t y, uint16_t z, uint8_t flags, bool initialPlacement, uint8_t direction, ride_id_t rideIndex, uint8_t mode); -void game_command_set_brakes_speed( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); bool track_element_is_booster(uint8_t rideType, uint8_t trackType); bool track_element_has_speed_setting(uint8_t trackType); diff --git a/src/openrct2/ride/TrackData.cpp b/src/openrct2/ride/TrackData.cpp index c0ac0fa317..7d58547690 100644 --- a/src/openrct2/ride/TrackData.cpp +++ b/src/openrct2/ride/TrackData.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -30855,7 +30855,7 @@ const uint8_t TrackElementMirrorMap[] = { TRACK_ELEM_LEFT_QUARTER_TURN_1_TILE_60_DEG_DOWN, // TRACK_ELEM_RIGHT_QUARTER_TURN_1_TILE_60_DEG_DOWN TRACK_ELEM_BRAKES, TRACK_ELEM_ROTATION_CONTROL_TOGGLE, - TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP, + TRACK_ELEM_MAZE, TRACK_ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_UP, // TRACK_ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_UP TRACK_ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_UP, // TRACK_ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_UP TRACK_ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_DOWN, // TRACK_ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_DOWN @@ -32250,11 +32250,11 @@ const uint16_t TrackFlags[] = { /* TRACK_ELEM_HEARTLINE_TRANSFER_DOWN */ 0, /* TRACK_ELEM_LEFT_HEARTLINE_ROLL */ TRACK_ELEM_FLAG_NORMAL_TO_INVERSION | TRACK_ELEM_FLAG_INVERSION_TO_NORMAL, /* TRACK_ELEM_RIGHT_HEARTLINE_ROLL */ TRACK_ELEM_FLAG_NORMAL_TO_INVERSION | TRACK_ELEM_FLAG_INVERSION_TO_NORMAL, - /* TRACK_ELEM_MINI_GOLF_HOLE_A */ TRACK_ELEM_FLAG_NORMAL_TO_INVERSION, - /* TRACK_ELEM_MINI_GOLF_HOLE_B */ TRACK_ELEM_FLAG_NORMAL_TO_INVERSION, - /* TRACK_ELEM_MINI_GOLF_HOLE_C */ TRACK_ELEM_FLAG_NORMAL_TO_INVERSION, - /* TRACK_ELEM_MINI_GOLF_HOLE_D */ TRACK_ELEM_FLAG_NORMAL_TO_INVERSION, - /* TRACK_ELEM_MINI_GOLF_HOLE_E */ TRACK_ELEM_FLAG_NORMAL_TO_INVERSION, + /* TRACK_ELEM_MINI_GOLF_HOLE_A */ TRACK_ELEM_FLAG_IS_GOLF_HOLE, + /* TRACK_ELEM_MINI_GOLF_HOLE_B */ TRACK_ELEM_FLAG_IS_GOLF_HOLE, + /* TRACK_ELEM_MINI_GOLF_HOLE_C */ TRACK_ELEM_FLAG_IS_GOLF_HOLE, + /* TRACK_ELEM_MINI_GOLF_HOLE_D */ TRACK_ELEM_FLAG_IS_GOLF_HOLE, + /* TRACK_ELEM_MINI_GOLF_HOLE_E */ TRACK_ELEM_FLAG_IS_GOLF_HOLE, /* TRACK_ELEM_MULTIDIM_INVERTED_FLAT_TO_90_DEG_QUARTER_LOOP_DOWN */ TRACK_ELEM_FLAG_DOWN | TRACK_ELEM_FLAG_INVERSION_TO_NORMAL, /* TRACK_ELEM_90_DEG_TO_INVERTED_FLAT_QUARTER_LOOP_UP */ TRACK_ELEM_FLAG_UP | TRACK_ELEM_FLAG_NORMAL_TO_INVERSION | TRACK_ELEM_FLAG_INVERSION_TO_NORMAL, /* TRACK_ELEM_INVERTED_FLAT_TO_90_DEG_QUARTER_LOOP_DOWN */ TRACK_ELEM_FLAG_DOWN | TRACK_ELEM_FLAG_INVERSION_TO_NORMAL, diff --git a/src/openrct2/ride/TrackData.h b/src/openrct2/ride/TrackData.h index 7f0f243151..a8ea771e5c 100644 --- a/src/openrct2/ride/TrackData.h +++ b/src/openrct2/ride/TrackData.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/TrackDataOld.cpp b/src/openrct2/ride/TrackDataOld.cpp index dee1aa0396..6e5fbfc098 100644 --- a/src/openrct2/ride/TrackDataOld.cpp +++ b/src/openrct2/ride/TrackDataOld.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index 049c03e316..7a6c22ea33 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,6 +12,10 @@ #include "../Cheats.h" #include "../Game.h" #include "../OpenRCT2.h" +#include "../TrackImporter.h" +#include "../actions/FootpathPlaceFromTrackAction.hpp" +#include "../actions/FootpathRemoveAction.hpp" +#include "../actions/LargeSceneryPlaceAction.hpp" #include "../actions/LargeSceneryRemoveAction.hpp" #include "../actions/RideEntranceExitPlaceAction.hpp" #include "../actions/RideSetSetting.hpp" @@ -20,10 +24,12 @@ #include "../actions/SmallSceneryRemoveAction.hpp" #include "../actions/TrackPlaceAction.hpp" #include "../actions/TrackRemoveAction.hpp" +#include "../actions/WallPlaceAction.hpp" #include "../actions/WallRemoveAction.hpp" #include "../audio/audio.h" #include "../core/File.h" #include "../core/String.hpp" +#include "../drawing/X8DrawingEngine.h" #include "../localisation/Localisation.h" #include "../localisation/StringIds.h" #include "../management/Finance.h" @@ -50,6 +56,9 @@ #include #include +using namespace OpenRCT2; +using namespace OpenRCT2::Drawing; + struct map_backup { TileElement tile_elements[MAX_TILE_ELEMENTS]; @@ -61,7 +70,7 @@ struct map_backup uint8_t current_rotation; }; -rct_track_td6* gActiveTrackDesign; +TrackDesign* gActiveTrackDesign; bool gTrackDesignSceneryToggle; LocationXYZ16 gTrackPreviewMin; LocationXYZ16 gTrackPreviewMax; @@ -80,392 +89,498 @@ static bool _trackDesignPlaceStateSceneryUnavailable = false; static bool _trackDesignPlaceStateHasScenery = false; static bool _trackDesignPlaceStatePlaceScenery = true; -static rct_track_td6* track_design_open_from_buffer(uint8_t* src, size_t srcLength); - static map_backup* track_design_preview_backup_map(); static void track_design_preview_restore_map(map_backup* backup); static void track_design_preview_clear_map(); -static void td6_reset_trailing_elements(rct_track_td6* td6); - -static void td6_set_element_helper_pointers(rct_track_td6* td6, bool clearScenery); - -rct_track_td6* track_design_open(const utf8* path) +rct_string_id TrackDesign::CreateTrackDesign(const Ride& ride) { - log_verbose("track_design_open(\"%s\")", path); + type = ride.type; + auto object = object_entry_get_entry(OBJECT_TYPE_RIDE, ride.subtype); - try + // Note we are only copying rct_object_entry in size and + // not the extended as we don't need the chunk size. + std::memcpy(&vehicle_object, object, sizeof(rct_object_entry)); + + ride_mode = ride.mode; + colour_scheme = ride.colour_scheme_type & 3; + + for (int32_t i = 0; i < RCT12_MAX_VEHICLES_PER_RIDE; i++) { - auto buffer = File::ReadAllBytes(path); - if (!sawyercoding_validate_track_checksum(buffer.data(), buffer.size())) + vehicle_colours[i].body_colour = ride.vehicle_colours[i].Body; + vehicle_colours[i].trim_colour = ride.vehicle_colours[i].Trim; + vehicle_additional_colour[i] = ride.vehicle_colours[i].Ternary; + } + + for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) + { + track_spine_colour[i] = ride.track_colour[i].main; + track_rail_colour[i] = ride.track_colour[i].additional; + track_support_colour[i] = ride.track_colour[i].supports; + } + + depart_flags = ride.depart_flags; + number_of_trains = ride.num_vehicles; + number_of_cars_per_train = ride.num_cars_per_train; + min_waiting_time = ride.min_waiting_time; + max_waiting_time = ride.max_waiting_time; + operation_setting = ride.operation_option; + lift_hill_speed = ride.lift_hill_speed; + num_circuits = ride.num_circuits; + + entrance_style = ride.entrance_style; + max_speed = (int8_t)(ride.max_speed / 65536); + average_speed = (int8_t)(ride.average_speed / 65536); + ride_length = ride_get_total_length(&ride) / 65536; + max_positive_vertical_g = ride.max_positive_vertical_g / 32; + max_negative_vertical_g = ride.max_negative_vertical_g / 32; + max_lateral_g = ride.max_lateral_g / 32; + inversions = ride.holes & 0x1F; + inversions = ride.inversions & 0x1F; + inversions |= (ride.sheltered_eighths << 5); + drops = ride.drops; + highest_drop_height = ride.highest_drop_height; + + uint16_t totalAirTime = (ride.total_air_time * 123) / 1024; + if (totalAirTime > 255) + { + totalAirTime = 0; + } + total_air_time = (uint8_t)totalAirTime; + + excitement = ride.ratings.excitement / 10; + intensity = ride.ratings.intensity / 10; + nausea = ride.ratings.nausea / 10; + + upkeep_cost = ride.upkeep_cost; + flags = 0; + flags2 = 0; + + if (type == RIDE_TYPE_MAZE) + { + return CreateTrackDesignMaze(ride); + } + else + { + return CreateTrackDesignTrack(ride); + } +} + +rct_string_id TrackDesign::CreateTrackDesignTrack(const Ride& ride) +{ + CoordsXYE trackElement; + if (!ride_try_get_origin_element(&ride, &trackElement)) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + ride_get_start_of_track(&trackElement); + + int32_t z = trackElement.element->base_height * 8; + uint8_t trackType = trackElement.element->AsTrack()->GetTrackType(); + uint8_t direction = trackElement.element->GetDirection(); + _saveDirection = direction; + + if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, trackType, 0, &trackElement.element, 0)) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + const rct_track_coordinates* trackCoordinates = &TrackCoordinates[trackElement.element->AsTrack()->GetTrackType()]; + // Used in the following loop to know when we have + // completed all of the elements and are back at the + // start. + TileElement* initialMap = trackElement.element; + + int16_t start_x = trackElement.x; + int16_t start_y = trackElement.y; + int16_t start_z = z + trackCoordinates->z_begin; + gTrackPreviewOrigin = { start_x, start_y, start_z }; + + do + { + TrackDesignTrackElement track{}; + track.type = trackElement.element->AsTrack()->GetTrackType(); + // TODO move to RCT2 limit + if (track.type == TRACK_ELEM_MULTIDIM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP) { - log_error("Track checksum failed. %s", path); - return nullptr; + track.type = TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP_ALIAS; } - // Decode the track data - uint8_t* decoded = (uint8_t*)malloc(0x10000); - size_t decodedLength = sawyercoding_decode_td6(buffer.data(), decoded, buffer.size()); - decoded = (uint8_t*)realloc(decoded, decodedLength); - if (decoded == nullptr) + uint8_t trackFlags; + if (track_element_has_speed_setting(track.type)) { - log_error("failed to realloc"); + trackFlags = trackElement.element->AsTrack()->GetBrakeBoosterSpeed() >> 1; } else { - rct_track_td6* td6 = track_design_open_from_buffer(decoded, decodedLength); - free(decoded); + trackFlags = trackElement.element->AsTrack()->GetSeatRotation(); + } - if (td6 != nullptr) + if (trackElement.element->AsTrack()->HasChain()) + trackFlags |= RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; + trackFlags |= trackElement.element->AsTrack()->GetColourScheme() << 4; + if (RideData4[ride.type].flags & RIDE_TYPE_FLAG4_HAS_ALTERNATIVE_TRACK_TYPE + && trackElement.element->AsTrack()->IsInverted()) + { + trackFlags |= TD6_TRACK_ELEMENT_FLAG_INVERTED; + } + + track.flags = trackFlags; + track_elements.push_back(track); + + if (!track_block_get_next(&trackElement, &trackElement, nullptr, nullptr)) + { + break; + } + + z = trackElement.element->base_height * 8; + direction = trackElement.element->GetDirection(); + trackType = trackElement.element->AsTrack()->GetTrackType(); + + if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, trackType, 0, &trackElement.element, 0)) + { + break; + } + + // TODO move to RCT2 limit + constexpr auto TD6MaxTrackElements = 8192; + + if (track_elements.size() > TD6MaxTrackElements) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + } while (trackElement.element != initialMap); + + // First entrances, second exits + for (int32_t i = 0; i < 2; i++) + { + for (int32_t station_index = 0; station_index < RCT12_MAX_STATIONS_PER_RIDE; station_index++) + { + z = ride.stations[station_index].Height; + + TileCoordsXYZD location; + if (i == 0) { - td6->name = String::Duplicate(GetNameFromTrackPath(path).c_str()); - return td6; + location = ride_get_entrance_location(&ride, station_index); + } + else + { + location = ride_get_exit_location(&ride, station_index); + } + + if (location.isNull()) + { + continue; + } + + int16_t x = location.x * 32; + int16_t y = location.y * 32; + + TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + continue; + + do + { + if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) + continue; + if (tileElement->base_height == z) + break; + } while (!(tileElement++)->IsLastForTile()); + + // Add something that stops this from walking off the end + + Direction entranceDirection = tileElement->GetDirection(); + entranceDirection -= _saveDirection; + entranceDirection &= TILE_ELEMENT_DIRECTION_MASK; + + TrackDesignEntranceElement entrance{}; + entrance.direction = entranceDirection; + + x -= gTrackPreviewOrigin.x; + y -= gTrackPreviewOrigin.y; + + // Rotate entrance coordinates backwards to the correct direction + rotate_map_coordinates(&x, &y, (0 - _saveDirection) & 3); + entrance.x = x; + entrance.y = y; + + z *= 8; + z -= gTrackPreviewOrigin.z; + z /= 8; + + if (z > 127 || z < -126) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + entrance.z = z; + + // If this is the exit version + if (i == 1) + { + entrance.isExit = true; + } + entrance_elements.push_back(entrance); + } + } + + place_virtual_track(this, PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), 4096, 4096, 0); + + // Resave global vars for scenery reasons. + gTrackPreviewOrigin = { start_x, start_y, start_z }; + + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; + + space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; + space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; + return STR_NONE; +} + +rct_string_id TrackDesign::CreateTrackDesignMaze(const Ride& ride) +{ + auto startLoc = MazeGetFirstElement(ride); + + if (startLoc.element == nullptr) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + gTrackPreviewOrigin = { static_cast(startLoc.x), static_cast(startLoc.y), + (int16_t)(startLoc.element->base_height * 8) }; + + // x is defined here as we can start the search + // on tile start_x, start_y but then the next row + // must restart on 0 + for (int16_t y = startLoc.y, x = startLoc.y; y < 8192; y += 32) + { + for (; x < 8192; x += 32) + { + auto tileElement = map_get_first_element_at(x / 32, y / 32); + do + { + if (tileElement == nullptr) + break; + if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) + continue; + if (tileElement->AsTrack()->GetRideIndex() != ride.id) + continue; + + TrackDesignMazeElement maze{}; + + maze.maze_entry = tileElement->AsTrack()->GetMazeEntry(); + maze.x = (x - startLoc.x) / 32; + maze.y = (y - startLoc.y) / 32; + _saveDirection = tileElement->GetDirection(); + maze_elements.push_back(maze); + + if (maze_elements.size() >= 2000) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + } while (!(tileElement++)->IsLastForTile()); + } + x = 0; + } + + auto location = ride_get_entrance_location(&ride, 0); + if (location.isNull()) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + CoordsXY entranceLoc = { location.x * 32, location.y * 32 }; + auto tileElement = map_get_first_element_at(location.x, location.y); + do + { + if (tileElement == nullptr) + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) + continue; + if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE) + continue; + if (tileElement->AsEntrance()->GetRideIndex() == ride.id) + break; + } while (!(tileElement++)->IsLastForTile()); + // Add something that stops this from walking off the end + + uint8_t entranceDirection = tileElement->GetDirection(); + TrackDesignMazeElement mazeEntrance{}; + mazeEntrance.direction = entranceDirection; + mazeEntrance.type = 8; + mazeEntrance.x = (int8_t)((entranceLoc.x - startLoc.x) / 32); + mazeEntrance.y = (int8_t)((entranceLoc.y - startLoc.y) / 32); + maze_elements.push_back(mazeEntrance); + + location = ride_get_exit_location(&ride, 0); + if (location.isNull()) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + CoordsXY exitLoc = { location.x * 32, location.y * 32 }; + tileElement = map_get_first_element_at(location.x, location.y); + if (tileElement == nullptr) + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + do + { + if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) + continue; + if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT) + continue; + if (tileElement->AsEntrance()->GetRideIndex() == ride.id) + break; + } while (!(tileElement++)->IsLastForTile()); + // Add something that stops this from walking off the end + + uint8_t exit_direction = tileElement->GetDirection(); + TrackDesignMazeElement mazeExit{}; + mazeExit.direction = exit_direction; + mazeExit.type = 0x80; + mazeExit.x = (int8_t)((exitLoc.x - startLoc.x) / 32); + mazeExit.y = (int8_t)((exitLoc.y - startLoc.y) / 32); + maze_elements.push_back(mazeExit); + + // Save global vars as they are still used by scenery???? + int16_t startZ = gTrackPreviewOrigin.z; + place_virtual_track(this, PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), 4096, 4096, 0); + gTrackPreviewOrigin = { static_cast(startLoc.x), static_cast(startLoc.y), startZ }; + + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; + + space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; + space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; + return STR_NONE; +} + +CoordsXYE TrackDesign::MazeGetFirstElement(const Ride& ride) +{ + CoordsXYE tile{}; + for (tile.y = 0; tile.y < 8192; tile.y += 32) + { + for (tile.x = 0; tile.x < 8192; tile.x += 32) + { + tile.element = map_get_first_element_at(tile.x / 32, tile.y / 32); + do + { + if (tile.element == nullptr) + break; + + if (tile.element->GetType() != TILE_ELEMENT_TYPE_TRACK) + continue; + if (tile.element->AsTrack()->GetRideIndex() == ride.id) + { + return tile; + } + } while (!(tile.element++)->IsLastForTile()); + } + } + tile.element = nullptr; + return tile; +} + +rct_string_id TrackDesign::CreateTrackDesignScenery() +{ + scenery_elements = _trackSavedTileElementsDesc; + // Run an element loop + for (auto& scenery : scenery_elements) + { + switch (object_entry_get_type(&scenery.scenery_object)) + { + case OBJECT_TYPE_PATHS: + { + uint8_t slope = (scenery.flags & 0x60) >> 5; + slope -= _saveDirection; + + scenery.flags &= 0x9F; + scenery.flags |= ((slope & 3) << 5); + + // Direction of connection on path + uint8_t direction = scenery.flags & 0xF; + // Rotate the direction by the track direction + direction = ((direction << 4) >> _saveDirection); + + scenery.flags &= 0xF0; + scenery.flags |= (direction & 0xF) | (direction >> 4); + break; + } + case OBJECT_TYPE_WALLS: + { + uint8_t direction = scenery.flags & 3; + direction -= _saveDirection; + + scenery.flags &= 0xFC; + scenery.flags |= (direction & 3); + break; + } + default: + { + uint8_t direction = scenery.flags & 3; + uint8_t quadrant = (scenery.flags & 0x0C) >> 2; + + direction -= _saveDirection; + quadrant -= _saveDirection; + + scenery.flags &= 0xF0; + scenery.flags |= (direction & 3) | ((quadrant & 3) << 2); + break; } } + + int16_t x = ((uint8_t)scenery.x) * 32 - gTrackPreviewOrigin.x; + int16_t y = ((uint8_t)scenery.y) * 32 - gTrackPreviewOrigin.y; + rotate_map_coordinates(&x, &y, (0 - _saveDirection) & 3); + x /= 32; + y /= 32; + + if (x > 127 || y > 127 || x < -126 || y < -126) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + scenery.x = (int8_t)x; + scenery.y = (int8_t)y; + + int32_t z = scenery.z * 8 - gTrackPreviewOrigin.z; + z /= 8; + if (z > 127 || z < -126) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + scenery.z = z; + } + + return STR_NONE; +} + +std::unique_ptr track_design_open(const utf8* path) +{ + try + { + auto trackImporter = TrackImporter::Create(path); + trackImporter->Load(path); + return trackImporter->Import(); } catch (const std::exception& e) { log_error("Unable to load track design: %s", e.what()); } + log_verbose("track_design_open(\"%s\")", path); return nullptr; } -static rct_track_td6* track_design_open_from_td4(uint8_t* src, size_t srcLength) -{ - rct_track_td4* td4 = (rct_track_td4*)calloc(1, sizeof(rct_track_td4)); - if (td4 == nullptr) - { - log_error("Unable to allocate memory for TD4 data."); - SafeFree(td4); - return nullptr; - } - - uint8_t version = (src[7] >> 2) & 3; - if (version == 0) - { - std::memcpy(td4, src, 0x38); - td4->elementsSize = srcLength - 0x38; - td4->elements = malloc(td4->elementsSize); - if (td4->elements == nullptr) - { - log_error("Unable to allocate memory for TD4 element data."); - SafeFree(td4); - return nullptr; - } - std::memcpy(td4->elements, src + 0x38, td4->elementsSize); - } - else if (version == 1) - { - std::memcpy(td4, src, 0xC4); - td4->elementsSize = srcLength - 0xC4; - td4->elements = malloc(td4->elementsSize); - if (td4->elements == nullptr) - { - log_error("Unable to allocate memory for TD4 element data."); - SafeFree(td4); - return nullptr; - } - std::memcpy(td4->elements, src + 0xC4, td4->elementsSize); - } - else - { - log_error("Unsupported track design."); - SafeFree(td4); - return nullptr; - } - - rct_track_td6* td6 = (rct_track_td6*)calloc(1, sizeof(rct_track_td6)); - if (td6 == nullptr) - { - log_error("Unable to allocate memory for TD6 data."); - SafeFree(td4); - return nullptr; - } - - td6->type = RCT1::GetRideType(td4->type); - - // All TD4s that use powered launch use the type that doesn't pass the station. - td6->ride_mode = td4->mode; - if (td4->mode == RCT1_RIDE_MODE_POWERED_LAUNCH) - { - td6->ride_mode = RIDE_MODE_POWERED_LAUNCH; - } - - // Convert RCT1 vehicle type to RCT2 vehicle type. Intialise with an string consisting of 8 spaces. - rct_object_entry vehicleObject = { 0x80, " " }; - if (td4->type == RIDE_TYPE_MAZE) - { - const char* name = RCT1::GetRideTypeObject(td4->type); - assert(name != nullptr); - std::memcpy(vehicleObject.name, name, std::min(String::SizeOf(name), (size_t)8)); - } - else - { - const char* name = RCT1::GetVehicleObject(td4->vehicle_type); - assert(name != nullptr); - std::memcpy(vehicleObject.name, name, std::min(String::SizeOf(name), (size_t)8)); - } - std::memcpy(&td6->vehicle_object, &vehicleObject, sizeof(rct_object_entry)); - td6->vehicle_type = td4->vehicle_type; - - td6->flags = td4->flags; - td6->version_and_colour_scheme = td4->version_and_colour_scheme; - - // Vehicle colours - for (int32_t i = 0; i < RCT1_MAX_TRAINS_PER_RIDE; i++) - { - // RCT1 had no third colour - RCT1::RCT1VehicleColourSchemeCopyDescriptor colourSchemeCopyDescriptor = RCT1::GetColourSchemeCopyDescriptor( - td4->vehicle_type); - if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_1) - { - td6->vehicle_colours[i].body_colour = RCT1::GetColour(td4->vehicle_colours[i].body_colour); - } - else if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_2) - { - td6->vehicle_colours[i].body_colour = RCT1::GetColour(td4->vehicle_colours[i].trim_colour); - } - else - { - td6->vehicle_colours[i].body_colour = colourSchemeCopyDescriptor.colour1; - } - - if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_1) - { - td6->vehicle_colours[i].trim_colour = RCT1::GetColour(td4->vehicle_colours[i].body_colour); - } - else if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_2) - { - td6->vehicle_colours[i].trim_colour = RCT1::GetColour(td4->vehicle_colours[i].trim_colour); - } - else - { - td6->vehicle_colours[i].trim_colour = colourSchemeCopyDescriptor.colour2; - } - - if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_1) - { - td6->vehicle_additional_colour[i] = RCT1::GetColour(td4->vehicle_colours[i].body_colour); - } - else if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_2) - { - td6->vehicle_additional_colour[i] = RCT1::GetColour(td4->vehicle_colours[i].trim_colour); - } - else - { - td6->vehicle_additional_colour[i] = colourSchemeCopyDescriptor.colour3; - } - } - // Set remaining vehicles to same colour as first vehicle - for (int32_t i = RCT1_MAX_TRAINS_PER_RIDE; i < MAX_VEHICLES_PER_RIDE; i++) - { - td6->vehicle_colours[i] = td6->vehicle_colours[0]; - td6->vehicle_additional_colour[i] = td6->vehicle_additional_colour[0]; - } - - // Track colours - if (version == 0) - { - for (int32_t i = 0; i < NUM_COLOUR_SCHEMES; i++) - { - td6->track_spine_colour[i] = RCT1::GetColour(td4->track_spine_colour_v0); - td6->track_rail_colour[i] = RCT1::GetColour(td4->track_rail_colour_v0); - td6->track_support_colour[i] = RCT1::GetColour(td4->track_support_colour_v0); - - // Mazes were only hedges - switch (td4->type) - { - case RCT1_RIDE_TYPE_HEDGE_MAZE: - td6->track_support_colour[i] = MAZE_WALL_TYPE_HEDGE; - break; - case RCT1_RIDE_TYPE_RIVER_RAPIDS: - td6->track_spine_colour[i] = COLOUR_WHITE; - td6->track_rail_colour[i] = COLOUR_WHITE; - break; - } - } - } - else - { - for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) - { - td6->track_spine_colour[i] = RCT1::GetColour(td4->track_spine_colour[i]); - td6->track_rail_colour[i] = RCT1::GetColour(td4->track_rail_colour[i]); - td6->track_support_colour[i] = RCT1::GetColour(td4->track_support_colour[i]); - } - } - - td6->depart_flags = td4->depart_flags; - td6->number_of_trains = td4->number_of_trains; - td6->number_of_cars_per_train = td4->number_of_cars_per_train; - td6->min_waiting_time = td4->min_waiting_time; - td6->max_waiting_time = td4->max_waiting_time; - td6->operation_setting = std::min(td4->operation_setting, RideProperties[td6->type].max_value); - td6->max_speed = td4->max_speed; - td6->average_speed = td4->average_speed; - td6->ride_length = td4->ride_length; - td6->max_positive_vertical_g = td4->max_positive_vertical_g; - td6->max_negative_vertical_g = td4->max_negative_vertical_g; - td6->max_lateral_g = td4->max_lateral_g; - td6->inversions = td4->num_inversions; - td6->drops = td4->num_drops; - td6->highest_drop_height = td4->highest_drop_height / 2; - td6->excitement = td4->excitement; - td6->intensity = td4->intensity; - td6->nausea = td4->nausea; - td6->upkeep_cost = td4->upkeep_cost; - if (version == 1) - { - td6->flags2 = td4->flags2; - } - - td6->space_required_x = 255; - td6->space_required_y = 255; - td6->lift_hill_speed_num_circuits = 5; - - // Move elements across - td6->elements = td4->elements; - td6->elementsSize = td4->elementsSize; - - td6_reset_trailing_elements(td6); - td6_set_element_helper_pointers(td6, true); - - SafeFree(td4); - return td6; -} - -static rct_track_td6* track_design_open_from_buffer(uint8_t* src, size_t srcLength) -{ - uint8_t version = (src[7] >> 2) & 3; - if (version == 0 || version == 1) - { - return track_design_open_from_td4(src, srcLength); - } - else if (version != 2) - { - log_error("Unsupported track design."); - return nullptr; - } - - rct_track_td6* td6 = (rct_track_td6*)calloc(1, sizeof(rct_track_td6)); - if (td6 == nullptr) - { - log_error("Unable to allocate memory for TD6 data."); - return nullptr; - } - std::memcpy(td6, src, 0xA3); - td6->elementsSize = srcLength - 0xA3; - td6->elements = malloc(td6->elementsSize); - if (td6->elements == nullptr) - { - free(td6); - log_error("Unable to allocate memory for TD6 element data."); - return nullptr; - } - std::memcpy(td6->elements, src + 0xA3, td6->elementsSize); - - // Cap operation setting - td6->operation_setting = std::min(td6->operation_setting, RideProperties[td6->type].max_value); - - td6_set_element_helper_pointers(td6, false); - return td6; -} - -static void td6_reset_trailing_elements(rct_track_td6* td6) -{ - void* lastElement; - if (td6->type == RIDE_TYPE_MAZE) - { - rct_td6_maze_element* mazeElement = (rct_td6_maze_element*)td6->elements; - while (mazeElement->all != 0) - { - mazeElement++; - } - lastElement = (void*)((uintptr_t)mazeElement + 1); - - size_t trailingSize = td6->elementsSize - (size_t)((uintptr_t)lastElement - (uintptr_t)td6->elements); - std::memset(lastElement, 0, trailingSize); - } - else - { - rct_td6_track_element* trackElement = (rct_td6_track_element*)td6->elements; - while (trackElement->type != 0xFF) - { - trackElement++; - } - lastElement = (void*)((uintptr_t)trackElement + 1); - - size_t trailingSize = td6->elementsSize - (size_t)((uintptr_t)lastElement - (uintptr_t)td6->elements); - std::memset(lastElement, 0xFF, trailingSize); - } -} - -/** - * - * @param clearScenery Set when importing TD4 designs, to avoid corrupted data being interpreted as scenery. - */ -static void td6_set_element_helper_pointers(rct_track_td6* td6, bool clearScenery) -{ - uintptr_t sceneryElementsStart; - - if (td6->type == RIDE_TYPE_MAZE) - { - td6->track_elements = nullptr; - td6->maze_elements = (rct_td6_maze_element*)td6->elements; - - rct_td6_maze_element* maze = td6->maze_elements; - for (; maze->all != 0; maze++) - { - } - sceneryElementsStart = (uintptr_t)(++maze); - } - else - { - td6->maze_elements = nullptr; - td6->track_elements = (rct_td6_track_element*)td6->elements; - - rct_td6_track_element* track = td6->track_elements; - for (; track->type != 0xFF; track++) - { - } - uintptr_t entranceElementsStart = (uintptr_t)track + 1; - - rct_td6_entrance_element* entranceElement = (rct_td6_entrance_element*)entranceElementsStart; - td6->entrance_elements = entranceElement; - for (; entranceElement->z != -1; entranceElement++) - { - } - sceneryElementsStart = (uintptr_t)entranceElement + 1; - } - - if (clearScenery) - { - td6->scenery_elements = nullptr; - } - else - { - rct_td6_scenery_element* sceneryElement = (rct_td6_scenery_element*)sceneryElementsStart; - td6->scenery_elements = sceneryElement; - } -} - -void track_design_dispose(rct_track_td6* td6) -{ - if (td6 != nullptr) - { - free(td6->elements); - free(td6->name); - free(td6); - } -} - /** * * rct2: 0x006ABDB0 */ -static void track_design_load_scenery_objects(rct_track_td6* td6) +static void track_design_load_scenery_objects(TrackDesign* td6) { object_manager_unload_all_objects(); @@ -474,10 +589,9 @@ static void track_design_load_scenery_objects(rct_track_td6* td6) object_manager_load_object(rideEntry); // Load scenery objects - rct_td6_scenery_element* scenery = td6->scenery_elements; - for (; scenery != nullptr && scenery->scenery_object.end_flag != 0xFF; scenery++) + for (const auto& scenery : td6->scenery_elements) { - rct_object_entry* sceneryEntry = &scenery->scenery_object; + const rct_object_entry* sceneryEntry = &scenery.scenery_object; object_manager_load_object(sceneryEntry); } } @@ -486,19 +600,20 @@ static void track_design_load_scenery_objects(rct_track_td6* td6) * * rct2: 0x006D247A */ -static void track_design_mirror_scenery(rct_track_td6* td6) +static void track_design_mirror_scenery(TrackDesign* td6) { - rct_td6_scenery_element* scenery = td6->scenery_elements; - for (; scenery != nullptr && scenery->scenery_object.end_flag != 0xFF; scenery++) + for (auto& scenery : td6->scenery_elements) { - uint8_t entry_type, entry_index; - if (!find_object_in_entry_group(&scenery->scenery_object, &entry_type, &entry_index)) + uint8_t entry_type{ 0 }, entry_index{ 0 }; + if (!find_object_in_entry_group(&scenery.scenery_object, &entry_type, &entry_index)) { - entry_type = object_entry_get_type(&scenery->scenery_object); + entry_type = object_entry_get_type(&scenery.scenery_object); if (entry_type != OBJECT_TYPE_PATHS) { continue; } + + entry_index = 0; } rct_scenery_entry* scenery_entry = (rct_scenery_entry*)object_entry_get_chunk(entry_type, entry_index); @@ -521,73 +636,73 @@ static void track_design_mirror_scenery(rct_track_td6* td6) { y1 = tile->y_offset; } - if (y2 > tile->y_offset) + if (y2 < tile->y_offset) { y2 = tile->y_offset; } } - switch (scenery->flags & 3) + switch (scenery.flags & 3) { case 0: - scenery->y = (-(scenery->y * 32 + y1) - y2) / 32; + scenery.y = (-(scenery.y * 32 + y1) - y2) / 32; break; case 1: - scenery->x = (scenery->x * 32 + y2 + y1) / 32; - scenery->y = (-(scenery->y * 32)) / 32; - scenery->flags ^= (1 << 1); + scenery.x = (scenery.x * 32 + y2 + y1) / 32; + scenery.y = (-(scenery.y * 32)) / 32; + scenery.flags ^= (1 << 1); break; case 2: - scenery->y = (-(scenery->y * 32 - y2) + y1) / 32; + scenery.y = (-(scenery.y * 32 - y2) + y1) / 32; break; case 3: - scenery->x = (scenery->x * 32 - y2 - y1) / 32; - scenery->y = (-(scenery->y * 32)) / 32; - scenery->flags ^= (1 << 1); + scenery.x = (scenery.x * 32 - y2 - y1) / 32; + scenery.y = (-(scenery.y * 32)) / 32; + scenery.flags ^= (1 << 1); break; } break; } case OBJECT_TYPE_SMALL_SCENERY: - scenery->y = -scenery->y; + scenery.y = -scenery.y; if (scenery_small_entry_has_flag(scenery_entry, SMALL_SCENERY_FLAG_DIAGONAL)) { - scenery->flags ^= (1 << 0); + scenery.flags ^= (1 << 0); if (!scenery_small_entry_has_flag(scenery_entry, SMALL_SCENERY_FLAG_FULL_TILE)) { - scenery->flags ^= (1 << 2); + scenery.flags ^= (1 << 2); } break; } - if (scenery->flags & (1 << 0)) + if (scenery.flags & (1 << 0)) { - scenery->flags ^= (1 << 1); + scenery.flags ^= (1 << 1); } - scenery->flags ^= (1 << 2); + scenery.flags ^= (1 << 2); break; case OBJECT_TYPE_WALLS: - scenery->y = -scenery->y; - if (scenery->flags & (1 << 0)) + scenery.y = -scenery.y; + if (scenery.flags & (1 << 0)) { - scenery->flags ^= (1 << 1); + scenery.flags ^= (1 << 1); } break; case OBJECT_TYPE_PATHS: - scenery->y = -scenery->y; + scenery.y = -scenery.y; - if (scenery->flags & (1 << 5)) + if (scenery.flags & (1 << 5)) { - scenery->flags ^= (1 << 6); + scenery.flags ^= (1 << 6); } - uint8_t flags = scenery->flags; + uint8_t flags = scenery.flags; flags = ((flags & (1 << 3)) >> 2) | ((flags & (1 << 1)) << 2); - scenery->flags &= 0xF5; - scenery->flags |= flags; + scenery.flags &= 0xF5; + scenery.flags |= flags; } } } @@ -596,21 +711,19 @@ static void track_design_mirror_scenery(rct_track_td6* td6) * * rct2: 0x006D2443 */ -static void track_design_mirror_ride(rct_track_td6* td6) +static void track_design_mirror_ride(TrackDesign* td6) { - rct_td6_track_element* track = td6->track_elements; - for (; track->type != 0xFF; track++) + for (auto& track : td6->track_elements) { - track->type = TrackElementMirrorMap[track->type]; + track.type = TrackElementMirrorMap[track.type]; } - rct_td6_entrance_element* entrance = td6->entrance_elements; - for (; entrance->z != -1; entrance++) + for (auto& entrance : td6->entrance_elements) { - entrance->y = -entrance->y; - if (entrance->direction & 1) + entrance.y = -entrance.y; + if (entrance.direction & 1) { - entrance->direction = direction_reverse(entrance->direction); + entrance.direction = direction_reverse(entrance.direction); } } } @@ -624,30 +737,29 @@ static constexpr const uint8_t maze_segment_mirror_map[] = { * * rct2: 0x006D25FA */ -static void track_design_mirror_maze(rct_track_td6* td6) +static void track_design_mirror_maze(TrackDesign* td6) { - rct_td6_maze_element* maze = td6->maze_elements; - for (; maze->all != 0; maze++) + for (auto& maze : td6->maze_elements) { - maze->y = -maze->y; + maze.y = -maze.y; - if (maze->type == 0x8 || maze->type == 0x80) + if (maze.type == 0x8 || maze.type == 0x80) { - if (maze->direction & 1) + if (maze.direction & 1) { - maze->direction = direction_reverse(maze->direction); + maze.direction = direction_reverse(maze.direction); } continue; } - uint16_t maze_entry = maze->maze_entry; + uint16_t maze_entry = maze.maze_entry; uint16_t new_entry = 0; for (uint8_t position = bitscanforward(maze_entry); position != 0xFF; position = bitscanforward(maze_entry)) { maze_entry &= ~(1 << position); new_entry |= (1 << maze_segment_mirror_map[position]); } - maze->maze_entry = new_entry; + maze.maze_entry = new_entry; } } @@ -655,7 +767,7 @@ static void track_design_mirror_maze(rct_track_td6* td6) * * rct2: 0x006D2436 */ -void track_design_mirror(rct_track_td6* td6) +void track_design_mirror(TrackDesign* td6) { if (td6->type == RIDE_TYPE_MAZE) { @@ -670,24 +782,14 @@ void track_design_mirror(rct_track_td6* td6) static void track_design_add_selection_tile(int16_t x, int16_t y) { - LocationXY16* selectionTile = gMapSelectionTiles; - // Subtract 2 because the tile gets incremented later on - for (; (selectionTile < gMapSelectionTiles + std::size(gMapSelectionTiles) - 2) && (selectionTile->x != -1); - selectionTile++) + for (const auto& tile : gMapSelectionTiles) { - if (selectionTile->x == x && selectionTile->y == y) - { - return; - } - if (selectionTile + 1 >= &gMapSelectionTiles[300]) + if (tile.x == x && tile.y == y) { return; } } - selectionTile->x = x; - selectionTile->y = y; - selectionTile++; - selectionTile->x = -1; + gMapSelectionTiles.push_back(CoordsXY{ x, y }); } static void track_design_update_max_min_coordinates(int16_t x, int16_t y, int16_t z) @@ -700,16 +802,396 @@ static void track_design_update_max_min_coordinates(int16_t x, int16_t y, int16_ gTrackPreviewMax.z = std::max(gTrackPreviewMax.z, z); } +static bool TrackDesignPlaceSceneryElementGetEntry( + uint8_t& entry_type, uint8_t& entry_index, const TrackDesignSceneryElement& scenery) +{ + if (!find_object_in_entry_group(&scenery.scenery_object, &entry_type, &entry_index)) + { + entry_type = object_entry_get_type(&scenery.scenery_object); + if (entry_type != OBJECT_TYPE_PATHS) + { + _trackDesignPlaceStateSceneryUnavailable = true; + return true; + } + + if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) + { + _trackDesignPlaceStateSceneryUnavailable = true; + return true; + } + + entry_index = 0; + for (PathSurfaceEntry* path = get_path_surface_entry(0); entry_index < object_entry_group_counts[OBJECT_TYPE_PATHS]; + path = get_path_surface_entry(entry_index), entry_index++) + { + if (path == nullptr) + { + return true; + } + if (path->flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) + { + return true; + } + } + + if (entry_index == object_entry_group_counts[OBJECT_TYPE_PATHS]) + { + _trackDesignPlaceStateSceneryUnavailable = true; + return true; + } + } + return false; +} + +static bool TrackDesignPlaceSceneryElementRemoveGhost( + CoordsXY mapCoord, const TrackDesignSceneryElement& scenery, uint8_t rotation, int32_t originZ) +{ + uint8_t entry_type, entry_index; + if (TrackDesignPlaceSceneryElementGetEntry(entry_type, entry_index, scenery)) + { + return true; + } + + if (_trackDesignPlaceStateSceneryUnavailable) + { + return true; + } + + int32_t z = (scenery.z * 8 + originZ) / 8; + uint8_t sceneryRotation = (rotation + scenery.flags) & TILE_ELEMENT_DIRECTION_MASK; + const uint32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND + | GAME_COMMAND_FLAG_GHOST; + std::unique_ptr ga; + switch (entry_type) + { + case OBJECT_TYPE_SMALL_SCENERY: + { + uint8_t quadrant = (scenery.flags >> 2) + _currentTrackPieceDirection; + quadrant &= 3; + + rct_scenery_entry* small_scenery = get_small_scenery_entry(entry_index); + if (!(!scenery_small_entry_has_flag(small_scenery, SMALL_SCENERY_FLAG_FULL_TILE) + && scenery_small_entry_has_flag(small_scenery, SMALL_SCENERY_FLAG_DIAGONAL)) + && scenery_small_entry_has_flag( + small_scenery, + SMALL_SCENERY_FLAG_DIAGONAL | SMALL_SCENERY_FLAG_HALF_SPACE | SMALL_SCENERY_FLAG_THREE_QUARTERS)) + { + quadrant = 0; + } + + ga = std::make_unique(CoordsXYZ{ mapCoord.x, mapCoord.y, z * 8 }, quadrant, entry_index); + break; + } + case OBJECT_TYPE_LARGE_SCENERY: + ga = std::make_unique(CoordsXYZD{ mapCoord.x, mapCoord.y, z * 8, sceneryRotation }, 0); + break; + case OBJECT_TYPE_WALLS: + ga = std::make_unique(CoordsXYZD{ mapCoord.x, mapCoord.y, z * 8, sceneryRotation }); + break; + case OBJECT_TYPE_PATHS: + ga = std::make_unique(CoordsXYZ{ mapCoord.x, mapCoord.y, z * 8 }); + break; + default: + return true; + } + ga->SetFlags(flags); + GameActions::ExecuteNested(ga.get()); + return true; +} + +static bool TrackDesignPlaceSceneryElementGetPlaceZ(const TrackDesignSceneryElement& scenery) +{ + int32_t z = scenery.z * 8 + _trackDesignPlaceZ; + if (z < _trackDesignPlaceSceneryZ) + { + _trackDesignPlaceSceneryZ = z; + } + + uint8_t entry_type, entry_index; + TrackDesignPlaceSceneryElementGetEntry(entry_type, entry_index, scenery); + + return true; +} + +static bool TrackDesignPlaceSceneryElement( + CoordsXY mapCoord, uint8_t mode, const TrackDesignSceneryElement& scenery, uint8_t rotation, int32_t originZ) +{ + if (_trackDesignPlaceOperation == PTD_OPERATION_DRAW_OUTLINES && mode == 0) + { + track_design_add_selection_tile(mapCoord.x, mapCoord.y); + return true; + } + + if (_trackDesignPlaceOperation == PTD_OPERATION_REMOVE_GHOST && mode == 0) + { + return TrackDesignPlaceSceneryElementRemoveGhost(mapCoord, scenery, rotation, originZ); + } + + if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) + { + return TrackDesignPlaceSceneryElementGetPlaceZ(scenery); + } + + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY || _trackDesignPlaceOperation == PTD_OPERATION_PLACE + || _trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST + || _trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) + { + uint8_t entry_type, entry_index; + if (TrackDesignPlaceSceneryElementGetEntry(entry_type, entry_index, scenery)) + { + return true; + } + + money32 cost; + int16_t z; + uint8_t flags; + uint8_t quadrant; + + switch (entry_type) + { + case OBJECT_TYPE_SMALL_SCENERY: + { + if (mode != 0) + { + return true; + } + if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) + { + return true; + } + + rotation += scenery.flags; + rotation &= 3; + z = scenery.z * 8 + originZ; + quadrant = ((scenery.flags >> 2) + _currentTrackPieceDirection) & 3; + + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY; + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) + { + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED + | GAME_COMMAND_FLAG_NO_SPEND; + } + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST) + { + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED + | GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_NO_SPEND; + } + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY) + { + flags = GAME_COMMAND_FLAG_PATH_SCENERY; + } + + gGameCommandErrorTitle = STR_CANT_POSITION_THIS_HERE; + + auto smallSceneryPlace = SmallSceneryPlaceAction( + { mapCoord.x, mapCoord.y, z, rotation }, quadrant, entry_index, scenery.primary_colour, + scenery.secondary_colour); + + smallSceneryPlace.SetFlags(flags); + auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&smallSceneryPlace) + : GameActions::QueryNested(&smallSceneryPlace); + + cost = res->Error == GA_ERROR::OK ? res->Cost : 0; + break; + } + case OBJECT_TYPE_LARGE_SCENERY: + { + if (mode != 0) + { + return true; + } + if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) + { + return true; + } + + rotation += scenery.flags; + rotation &= 3; + + z = scenery.z * 8 + originZ; + + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY; + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) + { + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED + | GAME_COMMAND_FLAG_NO_SPEND; + } + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST) + { + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED + | GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_NO_SPEND; + } + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY) + { + flags = GAME_COMMAND_FLAG_PATH_SCENERY; + } + + auto sceneryPlaceAction = LargeSceneryPlaceAction( + { mapCoord.x, mapCoord.y, z, rotation }, entry_index, scenery.primary_colour, scenery.secondary_colour); + sceneryPlaceAction.SetFlags(flags); + auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::Execute(&sceneryPlaceAction) + : GameActions::Query(&sceneryPlaceAction); + + cost = res->Cost; + break; + } + case OBJECT_TYPE_WALLS: + { + if (mode != 0) + { + return true; + } + if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) + { + return true; + } + + z = scenery.z * 8 + originZ; + rotation += scenery.flags; + rotation &= 3; + + flags = GAME_COMMAND_FLAG_APPLY; + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) + { + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED + | GAME_COMMAND_FLAG_NO_SPEND; + } + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST) + { + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND + | GAME_COMMAND_FLAG_GHOST; + } + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY) + { + flags = 0; + } + + auto wallPlaceAction = WallPlaceAction( + entry_index, { mapCoord.x, mapCoord.y, z }, rotation, scenery.primary_colour, scenery.secondary_colour, + (scenery.flags & 0xFC) >> 2); + wallPlaceAction.SetFlags(flags); + auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&wallPlaceAction) + : GameActions::QueryNested(&wallPlaceAction); + + cost = res->Cost; + break; + } + case OBJECT_TYPE_PATHS: + if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) + { + return true; + } + + z = (scenery.z * 8 + originZ) / 8; + if (mode == 0) + { + if (scenery.flags & (1 << 7)) + { + // dh + entry_index |= (1 << 7); + } + + uint8_t bh = ((scenery.flags & 0xF) << rotation); + flags = bh >> 4; + bh = (bh | flags) & 0xF; + flags = (((scenery.flags >> 5) + rotation) & 3) << 5; + bh |= flags; + + bh |= scenery.flags & 0x90; + + flags = GAME_COMMAND_FLAG_APPLY; + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) + { + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND; + } + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST) + { + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND + | GAME_COMMAND_FLAG_GHOST; + } + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY) + { + flags = 0; + } + + uint8_t slope = ((bh >> 5) & 0x3) | ((bh >> 2) & 0x4); + uint8_t edges = bh & 0xF; + auto footpathPlaceAction = FootpathPlaceFromTrackAction( + { mapCoord.x, mapCoord.y, z * 8 }, slope, entry_index, edges); + footpathPlaceAction.SetFlags(flags); + auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&footpathPlaceAction) + : GameActions::QueryNested(&footpathPlaceAction); + // Ignore failures + cost = res->Error == GA_ERROR::OK ? res->Cost : 0; + } + else + { + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY) + { + return true; + } + + auto* pathElement = map_get_path_element_at({ mapCoord.x / 32, mapCoord.y / 32, z }); + + if (pathElement == nullptr) + { + return true; + } + + footpath_queue_chain_reset(); + footpath_remove_edges_at(mapCoord.x, mapCoord.y, reinterpret_cast(pathElement)); + + flags = GAME_COMMAND_FLAG_APPLY; + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) + { + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND; + } + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST) + { + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND + | GAME_COMMAND_FLAG_GHOST; + } + + footpath_connect_edges(mapCoord.x, mapCoord.y, reinterpret_cast(pathElement), flags); + footpath_update_queue_chains(); + return true; + } + break; + default: + _trackDesignPlaceStateSceneryUnavailable = true; + return true; + } + _trackDesignPlaceCost = add_clamp_money32(_trackDesignPlaceCost, cost); + if (_trackDesignPlaceOperation != PTD_OPERATION_PLACE) + { + if (cost == MONEY32_UNDEFINED) + { + _trackDesignPlaceCost = MONEY32_UNDEFINED; + } + } + if (_trackDesignPlaceCost != MONEY32_UNDEFINED) + { + return true; + } + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE) + { + return true; + } + return false; + } + return true; +} + /** * * rct2: 0x006D0964 */ -static int32_t track_design_place_scenery( - rct_td6_scenery_element* scenery_start, int32_t originX, int32_t originY, int32_t originZ) +static int32_t track_design_place_all_scenery( + const std::vector& sceneryList, int32_t originX, int32_t originY, int32_t originZ) { for (uint8_t mode = 0; mode <= 1; mode++) { - if (scenery_start->scenery_object.end_flag != 0xFF) + if (!sceneryList.empty()) { _trackDesignPlaceStateHasScenery = true; } @@ -719,448 +1201,18 @@ static int32_t track_design_place_scenery( continue; } - for (rct_td6_scenery_element* scenery = scenery_start; scenery->scenery_object.end_flag != 0xFF; scenery++) + for (const auto& scenery : sceneryList) { uint8_t rotation = _currentTrackPieceDirection; - LocationXY8 tile = { (uint8_t)(originX / 32), (uint8_t)(originY / 32) }; - switch (rotation & 3) - { - case TILE_ELEMENT_DIRECTION_WEST: - tile.x += scenery->x; - tile.y += scenery->y; - break; - case TILE_ELEMENT_DIRECTION_NORTH: - tile.x += scenery->y; - tile.y -= scenery->x; - break; - case TILE_ELEMENT_DIRECTION_EAST: - tile.x -= scenery->x; - tile.y -= scenery->y; - break; - case TILE_ELEMENT_DIRECTION_SOUTH: - tile.x -= scenery->y; - tile.y += scenery->x; - break; - } + TileCoordsXY tileCoords = { originX / 32, originY / 32 }; + TileCoordsXY offsets = { scenery.x, scenery.y }; + tileCoords += offsets.Rotate(rotation); - LocationXY16 mapCoord = { (int16_t)(tile.x * 32), (int16_t)(tile.y * 32) }; + CoordsXY mapCoord = { tileCoords.x * 32, tileCoords.y * 32 }; track_design_update_max_min_coordinates(mapCoord.x, mapCoord.y, originZ); - if (_trackDesignPlaceOperation == PTD_OPERATION_DRAW_OUTLINES && mode == 0) + if (!TrackDesignPlaceSceneryElement(mapCoord, mode, scenery, rotation, originZ)) { - uint8_t new_tile = 1; - LocationXY16* selectionTile = gMapSelectionTiles; - for (; selectionTile->x != -1; selectionTile++) - { - if (selectionTile->x == tile.x && selectionTile->y == tile.y) - { - new_tile = 0; - break; - } - // Need to subtract one because selectionTile in following block is incremented - if (selectionTile + 1 >= &gMapSelectionTiles[std::size(gMapSelectionTiles) - 1]) - { - new_tile = 0; - break; - } - } - if (new_tile) - { - selectionTile->x = tile.x; - selectionTile->y = tile.y; - selectionTile++; - selectionTile->x = -1; - } - } - - if (_trackDesignPlaceOperation == PTD_OPERATION_CLEAR_OUTLINES && mode == 0) - { - uint8_t entry_type, entry_index; - if (!find_object_in_entry_group(&scenery->scenery_object, &entry_type, &entry_index)) - { - entry_type = object_entry_get_type(&scenery->scenery_object); - if (entry_type != OBJECT_TYPE_PATHS) - { - entry_type = 0xFF; - } - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - { - entry_type = 0xFF; - } - - entry_index = 0; - for (PathSurfaceEntry* path = get_path_surface_entry(0); - entry_index < object_entry_group_counts[OBJECT_TYPE_PATHS]; - path = get_path_surface_entry(entry_index), entry_index++) - { - if (path == nullptr) - { - continue; - } - if (path->flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) - { - continue; - } - } - - if (entry_index == object_entry_group_counts[OBJECT_TYPE_PATHS]) - { - entry_type = 0xFF; - } - } - int32_t z; - const uint32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 - | GAME_COMMAND_FLAG_GHOST; - - switch (entry_type) - { - case OBJECT_TYPE_SMALL_SCENERY: - { - // bl - rotation += scenery->flags; - rotation &= 3; - - // bh - uint8_t quadrant = (scenery->flags >> 2) + _currentTrackPieceDirection; - quadrant &= 3; - - rct_scenery_entry* small_scenery = get_small_scenery_entry(entry_index); - if (!(!scenery_small_entry_has_flag(small_scenery, SMALL_SCENERY_FLAG_FULL_TILE) - && scenery_small_entry_has_flag(small_scenery, SMALL_SCENERY_FLAG_DIAGONAL)) - && scenery_small_entry_has_flag( - small_scenery, - SMALL_SCENERY_FLAG_DIAGONAL | SMALL_SCENERY_FLAG_HALF_SPACE - | SMALL_SCENERY_FLAG_THREE_QUARTERS)) - { - quadrant = 0; - } - - z = (scenery->z * 8 + originZ) / 8; - - auto removeSceneryAction = SmallSceneryRemoveAction(mapCoord.x, mapCoord.y, z, quadrant, entry_index); - removeSceneryAction.SetFlags(flags); - removeSceneryAction.Execute(); - - break; - } - case OBJECT_TYPE_LARGE_SCENERY: - { - z = (scenery->z * 8 + originZ) / 8; - - auto removeSceneryAction = LargeSceneryRemoveAction( - mapCoord.x, mapCoord.y, z, (rotation + scenery->flags) & 0x3, 0); - removeSceneryAction.SetFlags(flags); - removeSceneryAction.Execute(); - - break; - } - case OBJECT_TYPE_WALLS: - { - z = (scenery->z * 8 + originZ) / 8; - - uint8_t direction = (rotation + scenery->flags) & TILE_ELEMENT_DIRECTION_MASK; - - TileCoordsXYZD wallLocation = { tile.x, tile.y, z, direction }; - auto wallRemoveAction = WallRemoveAction(wallLocation); - wallRemoveAction.SetFlags(flags); - - GameActions::Execute(&wallRemoveAction); - - break; - } - case OBJECT_TYPE_PATHS: - z = (scenery->z * 8 + originZ) / 8; - footpath_remove(mapCoord.x, mapCoord.y, z, flags); - break; - } - } - - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) - { - int32_t z = scenery->z * 8 + _trackDesignPlaceZ; - if (z < _trackDesignPlaceSceneryZ) - { - _trackDesignPlaceSceneryZ = z; - } - } - - if (_trackDesignPlaceOperation == PTD_OPERATION_1 || _trackDesignPlaceOperation == PTD_OPERATION_2 - || _trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z || _trackDesignPlaceOperation == PTD_OPERATION_4 - || _trackDesignPlaceOperation == PTD_OPERATION_GET_COST) - { - uint8_t entry_type, entry_index; - if (!find_object_in_entry_group(&scenery->scenery_object, &entry_type, &entry_index)) - { - entry_type = object_entry_get_type(&scenery->scenery_object); - if (entry_type != OBJECT_TYPE_PATHS) - { - _trackDesignPlaceStateSceneryUnavailable = true; - continue; - } - - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - { - _trackDesignPlaceStateSceneryUnavailable = true; - continue; - } - - entry_index = 0; - for (PathSurfaceEntry* path = get_path_surface_entry(0); - entry_index < object_entry_group_counts[OBJECT_TYPE_PATHS]; - path = get_path_surface_entry(entry_index), entry_index++) - { - if (path == nullptr) - { - continue; - } - if (path->flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) - { - continue; - } - } - - if (entry_index == object_entry_group_counts[OBJECT_TYPE_PATHS]) - { - _trackDesignPlaceStateSceneryUnavailable = true; - continue; - } - } - - money32 cost; - int16_t z; - uint8_t flags; - uint8_t quadrant; - - switch (entry_type) - { - case OBJECT_TYPE_SMALL_SCENERY: - { - if (mode != 0) - { - continue; - } - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) - { - continue; - } - - rotation += scenery->flags; - rotation &= 3; - z = scenery->z * 8 + originZ; - quadrant = ((scenery->flags >> 2) + _currentTrackPieceDirection) & 3; - - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY; - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST) - { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY - | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5; - } - else if (_trackDesignPlaceOperation == PTD_OPERATION_4) - { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY - | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_5; - } - else if (_trackDesignPlaceOperation == PTD_OPERATION_1) - { - flags = GAME_COMMAND_FLAG_PATH_SCENERY; - } - - gGameCommandErrorTitle = STR_CANT_POSITION_THIS_HERE; - - auto smallSceneryPlace = SmallSceneryPlaceAction( - { mapCoord.x, mapCoord.y, z, rotation }, quadrant, entry_index, scenery->primary_colour, - scenery->secondary_colour); - - smallSceneryPlace.SetFlags(flags); - auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&smallSceneryPlace) - : GameActions::QueryNested(&smallSceneryPlace); - - cost = res->Error == GA_ERROR::OK ? res->Cost : 0; - break; - } - case OBJECT_TYPE_LARGE_SCENERY: - if (mode != 0) - { - continue; - } - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) - { - continue; - } - - rotation += scenery->flags; - rotation &= 3; - - z = scenery->z * 8 + originZ; - - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY; - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST) - { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY - | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5; - } - else if (_trackDesignPlaceOperation == PTD_OPERATION_4) - { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY - | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_5; - } - else if (_trackDesignPlaceOperation == PTD_OPERATION_1) - { - flags = GAME_COMMAND_FLAG_PATH_SCENERY; - } - - cost = game_do_command( - mapCoord.x, flags | (rotation << 8), mapCoord.y, - scenery->primary_colour | (scenery->secondary_colour << 8), GAME_COMMAND_PLACE_LARGE_SCENERY, - entry_index, z); - - if (cost == MONEY32_UNDEFINED) - { - cost = 0; - } - break; - case OBJECT_TYPE_WALLS: - if (mode != 0) - { - continue; - } - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) - { - continue; - } - - z = scenery->z * 8 + originZ; - rotation += scenery->flags; - rotation &= 3; - - flags = GAME_COMMAND_FLAG_APPLY; - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST) - { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY - | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5; - } - else if (_trackDesignPlaceOperation == PTD_OPERATION_4) - { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 - | GAME_COMMAND_FLAG_GHOST; - } - else if (_trackDesignPlaceOperation == PTD_OPERATION_1) - { - flags = 0; - } - - gGameCommandErrorTitle = STR_CANT_BUILD_PARK_ENTRANCE_HERE; - - cost = game_do_command( - mapCoord.x, flags | (entry_index << 8), mapCoord.y, rotation | (scenery->primary_colour << 8), - GAME_COMMAND_PLACE_WALL, z, scenery->secondary_colour | ((scenery->flags & 0xFC) << 6)); - - if (cost == MONEY32_UNDEFINED) - { - cost = 0; - } - break; - case OBJECT_TYPE_PATHS: - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) - { - continue; - } - - z = (scenery->z * 8 + originZ) / 8; - if (mode == 0) - { - if (scenery->flags & (1 << 7)) - { - // dh - entry_index |= (1 << 7); - } - - uint8_t bh = ((scenery->flags & 0xF) << rotation); - flags = bh >> 4; - bh = (bh | flags) & 0xF; - flags = (((scenery->flags >> 5) + rotation) & 3) << 5; - bh |= flags; - - bh |= scenery->flags & 0x90; - - flags = GAME_COMMAND_FLAG_APPLY; - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST) - { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5; - } - if (_trackDesignPlaceOperation == PTD_OPERATION_4) - { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 - | GAME_COMMAND_FLAG_GHOST; - } - if (_trackDesignPlaceOperation == PTD_OPERATION_1) - { - flags = 0; - } - - gGameCommandErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE; - cost = game_do_command( - mapCoord.x, flags | (bh << 8), mapCoord.y, z | (entry_index << 8), - GAME_COMMAND_PLACE_PATH_FROM_TRACK, 0, 0); - - if (cost == MONEY32_UNDEFINED) - { - cost = 0; - } - } - else - { - if (_trackDesignPlaceOperation == PTD_OPERATION_1) - { - continue; - } - - TileElement* tile_element = map_get_path_element_at(mapCoord.x / 32, mapCoord.y / 32, z); - - if (tile_element == nullptr) - { - continue; - } - - footpath_queue_chain_reset(); - footpath_remove_edges_at(mapCoord.x, mapCoord.y, tile_element); - - flags = GAME_COMMAND_FLAG_APPLY; - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST) - { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5; - } - if (_trackDesignPlaceOperation == PTD_OPERATION_4) - { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 - | GAME_COMMAND_FLAG_GHOST; - } - - footpath_connect_edges(mapCoord.x, mapCoord.y, tile_element, flags); - footpath_update_queue_chains(); - continue; - } - break; - default: - _trackDesignPlaceStateSceneryUnavailable = true; - continue; - } - _trackDesignPlaceCost = add_clamp_money32(_trackDesignPlaceCost, cost); - if (_trackDesignPlaceOperation != PTD_OPERATION_2) - { - if (cost == MONEY32_UNDEFINED) - { - _trackDesignPlaceCost = MONEY32_UNDEFINED; - } - } - if (_trackDesignPlaceCost != MONEY32_UNDEFINED) - { - continue; - } - if (_trackDesignPlaceOperation == PTD_OPERATION_2) - { - continue; - } return 0; } } @@ -1168,26 +1220,25 @@ static int32_t track_design_place_scenery( return 1; } -static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, int16_t z, Ride* ride) +static int32_t track_design_place_maze(TrackDesign* td6, int16_t x, int16_t y, int16_t z, Ride* ride) { if (_trackDesignPlaceOperation == PTD_OPERATION_DRAW_OUTLINES) { - gMapSelectionTiles->x = -1; + gMapSelectionTiles.clear(); gMapSelectArrowPosition.x = x; gMapSelectArrowPosition.y = y; - gMapSelectArrowPosition.z = tile_element_height(x, y) & 0xFFFF; + gMapSelectArrowPosition.z = tile_element_height({ x, y }); gMapSelectArrowDirection = _currentTrackPieceDirection; } _trackDesignPlaceZ = 0; _trackDesignPlaceCost = 0; - rct_td6_maze_element* maze_element = td6->maze_elements; - for (; maze_element->all != 0; maze_element++) + for (const auto& maze_element : td6->maze_elements) { uint8_t rotation = _currentTrackPieceDirection & 3; - int16_t tmpX = maze_element->x * 32; - int16_t tmpY = maze_element->y * 32; + int16_t tmpX = maze_element.x * 32; + int16_t tmpY = maze_element.y * 32; rotate_map_coordinates(&tmpX, &tmpY, rotation); CoordsXY mapCoord = { tmpX, tmpY }; mapCoord.x += x; @@ -1200,36 +1251,38 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, track_design_add_selection_tile(mapCoord.x, mapCoord.y); } - if (_trackDesignPlaceOperation == PTD_OPERATION_1 || _trackDesignPlaceOperation == PTD_OPERATION_2 - || _trackDesignPlaceOperation == PTD_OPERATION_4 || _trackDesignPlaceOperation == PTD_OPERATION_GET_COST) + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY || _trackDesignPlaceOperation == PTD_OPERATION_PLACE + || _trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST + || _trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) { uint8_t flags; money32 cost = 0; uint16_t maze_entry; - switch (maze_element->type) + switch (maze_element.type) { case MAZE_ELEMENT_TYPE_ENTRANCE: // entrance - rotation += maze_element->direction; + rotation += maze_element.direction; rotation &= 3; flags = GAME_COMMAND_FLAG_APPLY; gGameCommandErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE; - if (_trackDesignPlaceOperation == PTD_OPERATION_1) + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY) { auto res = RideEntranceExitPlaceAction::TrackPlaceQuery({ mapCoord.x, mapCoord.y, z }, false); cost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; } else { - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST) + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5; + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED + | GAME_COMMAND_FLAG_NO_SPEND; } - else if (_trackDesignPlaceOperation == PTD_OPERATION_4) + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST) { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; } auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(mapCoord, rotation, ride->id, 0, false); @@ -1244,26 +1297,27 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, break; case MAZE_ELEMENT_TYPE_EXIT: // exit - rotation += maze_element->direction; + rotation += maze_element.direction; rotation &= 3; flags = GAME_COMMAND_FLAG_APPLY; gGameCommandErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE; - if (_trackDesignPlaceOperation == PTD_OPERATION_1) + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY) { auto res = RideEntranceExitPlaceAction::TrackPlaceQuery({ mapCoord.x, mapCoord.y, z }, true); cost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; } else { - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST) + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5; + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED + | GAME_COMMAND_FLAG_NO_SPEND; } - else if (_trackDesignPlaceOperation == PTD_OPERATION_4) + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST) { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; } auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(mapCoord, rotation, ride->id, 0, true); @@ -1277,18 +1331,18 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, } break; default: - maze_entry = rol16(maze_element->maze_entry, rotation * 4); + maze_entry = rol16(maze_element.maze_entry, rotation * 4); - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST) + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5; + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND; } - else if (_trackDesignPlaceOperation == PTD_OPERATION_4) + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST) { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; } - else if (_trackDesignPlaceOperation == PTD_OPERATION_1) + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY) { flags = 0; } @@ -1333,20 +1387,20 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, continue; } - TileElement* tile_element = map_get_surface_element_at(mapCoord); - int16_t map_height = tile_element->base_height * 8; - if (tile_element->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) + auto surfaceElement = map_get_surface_element_at(mapCoord); + int16_t map_height = surfaceElement->base_height * 8; + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) { map_height += 16; - if (tile_element->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) { map_height += 16; } } - if (tile_element->AsSurface()->GetWaterHeight() > 0) + if (surfaceElement->GetWaterHeight() > 0) { - int16_t water_height = tile_element->AsSurface()->GetWaterHeight(); + int16_t water_height = surfaceElement->GetWaterHeight(); water_height *= 16; if (water_height > map_height) { @@ -1362,11 +1416,12 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, } } - if (_trackDesignPlaceOperation == PTD_OPERATION_CLEAR_OUTLINES) + if (_trackDesignPlaceOperation == PTD_OPERATION_REMOVE_GHOST) { ride_action_modify( ride, RIDE_MODIFY_DEMOLISH, - GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_GHOST); + GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND + | GAME_COMMAND_FLAG_GHOST); } gTrackPreviewOrigin.x = x; @@ -1375,7 +1430,7 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, return 1; } -static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, int16_t z, Ride* ride) +static bool track_design_place_ride(TrackDesign* td6, int16_t x, int16_t y, int16_t z, Ride* ride) { const rct_preview_track** trackBlockArray = (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_TRACK)) ? TrackBlocks : FlatRideTrackBlocks; @@ -1385,10 +1440,10 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in gTrackPreviewOrigin.z = z; if (_trackDesignPlaceOperation == PTD_OPERATION_DRAW_OUTLINES) { - gMapSelectionTiles->x = -1; + gMapSelectionTiles.clear(); gMapSelectArrowPosition.x = x; gMapSelectArrowPosition.y = y; - gMapSelectArrowPosition.z = tile_element_height(x, y) & 0xFFFF; + gMapSelectArrowPosition.z = tile_element_height({ x, y }); gMapSelectArrowDirection = _currentTrackPieceDirection; } @@ -1397,13 +1452,12 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in uint8_t rotation = _currentTrackPieceDirection; // Track elements - rct_td6_track_element* track = td6->track_elements; - for (; track->type != 0xFF; track++) + for (const auto& track : td6->track_elements) { - uint8_t trackType = track->type; - if (trackType == TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP) + uint8_t trackType = track.type; + if (trackType == TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP_ALIAS) { - trackType = 0xFF; + trackType = TRACK_ELEM_MULTIDIM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP; } track_design_update_max_min_coordinates(x, y, z); @@ -1419,53 +1473,53 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in track_design_add_selection_tile(tile.x, tile.y); } break; - case PTD_OPERATION_CLEAR_OUTLINES: + case PTD_OPERATION_REMOVE_GHOST: { const rct_track_coordinates* trackCoordinates = &TrackCoordinates[trackType]; const rct_preview_track* trackBlock = trackBlockArray[trackType]; int32_t tempZ = z - trackCoordinates->z_begin + trackBlock->z; auto trackRemoveAction = TrackRemoveAction(trackType, 0, { x, y, tempZ, static_cast(rotation & 3) }); trackRemoveAction.SetFlags( - GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_GHOST); + GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); GameActions::ExecuteNested(&trackRemoveAction); break; } - case PTD_OPERATION_1: - case PTD_OPERATION_2: - case PTD_OPERATION_4: - case PTD_OPERATION_GET_COST: + case PTD_OPERATION_PLACE_QUERY: + case PTD_OPERATION_PLACE: + case PTD_OPERATION_PLACE_GHOST: + case PTD_OPERATION_PLACE_TRACK_PREVIEW: { const rct_track_coordinates* trackCoordinates = &TrackCoordinates[trackType]; // di int16_t tempZ = z - trackCoordinates->z_begin; - uint32_t trackColour = (track->flags >> 4) & 0x3; - uint32_t brakeSpeed = (track->flags & 0x0F) * 2; - uint32_t seatRotation = track->flags & 0x0F; + uint32_t trackColour = (track.flags >> 4) & 0x3; + uint32_t brakeSpeed = (track.flags & 0x0F) * 2; + uint32_t seatRotation = track.flags & 0x0F; int32_t liftHillAndAlternativeState = 0; - if (track->flags & TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT) + if (track.flags & RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT) { liftHillAndAlternativeState |= 1; } - if (track->flags & TRACK_ELEMENT_FLAG_INVERTED) + if (track.flags & TD6_TRACK_ELEMENT_FLAG_INVERTED) { liftHillAndAlternativeState |= 2; } uint8_t flags = GAME_COMMAND_FLAG_APPLY; - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST) + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) { flags |= GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED; - flags |= GAME_COMMAND_FLAG_5; + flags |= GAME_COMMAND_FLAG_NO_SPEND; } - else if (_trackDesignPlaceOperation == PTD_OPERATION_4) + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST) { flags |= GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED; - flags |= GAME_COMMAND_FLAG_5; + flags |= GAME_COMMAND_FLAG_NO_SPEND; flags |= GAME_COMMAND_FLAG_GHOST; } - else if (_trackDesignPlaceOperation == PTD_OPERATION_1) + else if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY) { flags = 0; } @@ -1502,23 +1556,23 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in continue; } - TileElement* tileElement = map_get_surface_element_at(tile); - if (tileElement == nullptr) + auto surfaceElement = map_get_surface_element_at(tile); + if (surfaceElement == nullptr) { return false; } - int32_t height = tileElement->base_height * 8; - if (tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) + int32_t height = surfaceElement->base_height * 8; + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) { height += 16; - if (tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) { height += 16; } } - uint8_t water_height = tileElement->AsSurface()->GetWaterHeight() * 16; + uint8_t water_height = surfaceElement->GetWaterHeight() * 16; if (water_height > 0 && water_height > height) { height = water_height; @@ -1551,12 +1605,11 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in } // Entrance elements - rct_td6_entrance_element* entrance = td6->entrance_elements; - for (; entrance->z != -1; entrance++) + for (const auto& entrance : td6->entrance_elements) { rotation = _currentTrackPieceDirection & 3; - x = entrance->x; - y = entrance->y; + x = entrance.x; + y = entrance.y; rotate_map_coordinates(&x, &y, rotation); x += gTrackPreviewOrigin.x; y += gTrackPreviewOrigin.y; @@ -1568,19 +1621,13 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in case PTD_OPERATION_DRAW_OUTLINES: track_design_add_selection_tile(x, y); break; - case PTD_OPERATION_1: - case PTD_OPERATION_2: - case PTD_OPERATION_4: - case PTD_OPERATION_GET_COST: + case PTD_OPERATION_PLACE_QUERY: + case PTD_OPERATION_PLACE: + case PTD_OPERATION_PLACE_GHOST: + case PTD_OPERATION_PLACE_TRACK_PREVIEW: { - rotation = (rotation + entrance->direction) & 3; - bool isExit = false; - if (entrance->direction & (1 << 7)) - { - isExit = true; - } - - if (_trackDesignPlaceOperation != PTD_OPERATION_1) + rotation = (rotation + entrance.direction) & 3; + if (_trackDesignPlaceOperation != PTD_OPERATION_PLACE_QUERY) { LocationXY16 tile = { (int16_t)(x + CoordsDirectionDelta[rotation].x), @@ -1588,7 +1635,12 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in }; TileElement* tile_element = map_get_first_element_at(tile.x >> 5, tile.y >> 5); z = gTrackPreviewOrigin.z / 8; - z += (entrance->z == (int8_t)(uint8_t)0x80) ? -1 : entrance->z; + z += entrance.z; + if (tile_element == nullptr) + { + _trackDesignPlaceCost = MONEY32_UNDEFINED; + return false; + } do { @@ -1603,23 +1655,24 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in int32_t stationIndex = tile_element->AsTrack()->GetStationIndex(); uint8_t flags = GAME_COMMAND_FLAG_APPLY; - if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST) + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5; + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED + | GAME_COMMAND_FLAG_NO_SPEND; } - if (_trackDesignPlaceOperation == PTD_OPERATION_4) + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST) { - flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 + flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; } - if (_trackDesignPlaceOperation == PTD_OPERATION_1) + if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_QUERY) { flags = 0; } gGameCommandErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE; auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction( - { x, y }, rotation, ride->id, stationIndex, isExit); + { x, y }, rotation, ride->id, stationIndex, entrance.isExit); rideEntranceExitPlaceAction.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&rideEntranceExitPlaceAction) : GameActions::QueryNested(&rideEntranceExitPlaceAction); @@ -1637,8 +1690,7 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in } else { - z = (entrance->z == (int8_t)(uint8_t)0x80) ? -1 : entrance->z; - z *= 8; + z = entrance.z * 8; z += gTrackPreviewOrigin.z; auto res = RideEntranceExitPlaceAction::TrackPlaceQuery({ x, y, z }, false); @@ -1658,10 +1710,10 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in } } - if (_trackDesignPlaceOperation == PTD_OPERATION_CLEAR_OUTLINES) + if (_trackDesignPlaceOperation == PTD_OPERATION_REMOVE_GHOST) { sub_6CB945(ride); - ride_delete(ride); + ride->Delete(); } return true; } @@ -1680,7 +1732,7 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in * rct2: 0x006D01B3 */ int32_t place_virtual_track( - rct_track_td6* td6, uint8_t ptdOperation, bool placeScenery, Ride* ride, int16_t x, int16_t y, int16_t z) + TrackDesign* td6, uint8_t ptdOperation, bool placeScenery, Ride* ride, int16_t x, int16_t y, int16_t z) { // Previously byte_F4414E was cleared here _trackDesignPlaceStatePlaceScenery = placeScenery; @@ -1714,10 +1766,10 @@ int32_t place_virtual_track( } // Scenery elements - rct_td6_scenery_element* scenery = td6->scenery_elements; - if (track_place_success && scenery != nullptr) + if (track_place_success) { - if (!track_design_place_scenery(scenery, gTrackPreviewOrigin.x, gTrackPreviewOrigin.y, gTrackPreviewOrigin.z)) + if (!track_design_place_all_scenery( + td6->scenery_elements, gTrackPreviewOrigin.x, gTrackPreviewOrigin.y, gTrackPreviewOrigin.z)) { return _trackDesignPlaceCost; } @@ -1747,7 +1799,7 @@ int32_t place_virtual_track( * ebx = ride_id * cost = edi */ -static bool track_design_place_preview(rct_track_td6* td6, money32* cost, Ride** outRide, uint8_t* flags) +static bool track_design_place_preview(TrackDesign* td6, money32* cost, Ride** outRide, uint8_t* flags) { *outRide = nullptr; *flags = 0; @@ -1760,21 +1812,17 @@ static bool track_design_place_preview(rct_track_td6* td6, money32* cost, Ride** ride_id_t rideIndex; uint8_t colour; - uint8_t rideCreateFlags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5; + uint8_t rideCreateFlags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND; if (ride_create_command(td6->type, entry_index, rideCreateFlags, &rideIndex, &colour) == MONEY32_UNDEFINED) { return false; } auto ride = get_ride(rideIndex); - rct_string_id new_ride_name = user_string_allocate(USER_STRING_HIGH_ID_NUMBER | USER_STRING_DUPLICATION_PERMITTED, ""); - if (new_ride_name != 0) - { - rct_string_id old_name = ride->name; - ride->name = new_ride_name; - user_string_free(old_name); - } + if (ride == nullptr) + return false; + ride->custom_name = {}; ride->entrance_style = td6->entrance_style; for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) @@ -1803,7 +1851,7 @@ static bool track_design_place_preview(rct_track_td6* td6, money32* cost, Ride** int32_t mapSize = gMapSize << 4; _currentTrackPieceDirection = 0; - int32_t z = place_virtual_track(td6, PTD_OPERATION_GET_PLACE_Z, true, get_ride(0), mapSize, mapSize, 16); + int32_t z = place_virtual_track(td6, PTD_OPERATION_GET_PLACE_Z, true, GetOrAllocateRide(0), mapSize, mapSize, 16); if (_trackDesignPlaceStateHasScenery) { @@ -1819,7 +1867,7 @@ static bool track_design_place_preview(rct_track_td6* td6, money32* cost, Ride** *flags |= TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE; } - money32 resultCost = place_virtual_track(td6, PTD_OPERATION_GET_COST, placeScenery, ride, mapSize, mapSize, z); + money32 resultCost = place_virtual_track(td6, PTD_OPERATION_PLACE_TRACK_PREVIEW, placeScenery, ride, mapSize, mapSize, z); gParkFlags = backup_park_flags; if (resultCost != MONEY32_UNDEFINED) @@ -1842,8 +1890,7 @@ static bool track_design_place_preview(rct_track_td6* td6, money32* cost, Ride** else { _currentTrackPieceDirection = backup_rotation; - user_string_free(ride->name); - ride->type = RIDE_TYPE_NULL; + ride->Delete(); byte_9D8150 = false; return false; } @@ -1866,7 +1913,7 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags } } - rct_track_td6* td6 = gActiveTrackDesign; + TrackDesign* td6 = gActiveTrackDesign; if (td6 == nullptr) { return MONEY32_UNDEFINED; @@ -1924,9 +1971,9 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags } auto ride = get_ride(rideIndex); - if (ride->type == RIDE_TYPE_NULL) + if (ride == nullptr) { - log_warning("Invalid game command for track placement, ride id = %d", ride->id); + log_warning("Invalid game command for track placement, ride id = %d", rideIndex); return MONEY32_UNDEFINED; } @@ -1934,11 +1981,11 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags if (!(flags & GAME_COMMAND_FLAG_APPLY)) { _trackDesignDontPlaceScenery = false; - cost = place_virtual_track(td6, PTD_OPERATION_1, true, ride, x, y, z); + cost = place_virtual_track(td6, PTD_OPERATION_PLACE_QUERY, true, ride, x, y, z); if (_trackDesignPlaceStateSceneryUnavailable) { _trackDesignDontPlaceScenery = true; - cost = place_virtual_track(td6, PTD_OPERATION_1, false, ride, x, y, z); + cost = place_virtual_track(td6, PTD_OPERATION_PLACE_QUERY, false, ride, x, y, z); } } else @@ -1946,11 +1993,11 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags uint8_t operation; if (flags & GAME_COMMAND_FLAG_GHOST) { - operation = PTD_OPERATION_4; + operation = PTD_OPERATION_PLACE_GHOST; } else { - operation = PTD_OPERATION_2; + operation = PTD_OPERATION_PLACE; } cost = place_virtual_track(td6, operation, !_trackDesignDontPlaceScenery, ride, x, y, z); @@ -1986,17 +2033,17 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags set_operating_setting_nested(ride->id, RideSetSetting::MinWaitingTime, td6->min_waiting_time, flags); set_operating_setting_nested(ride->id, RideSetSetting::MaxWaitingTime, td6->max_waiting_time, flags); set_operating_setting_nested(ride->id, RideSetSetting::Operation, td6->operation_setting, flags); - set_operating_setting_nested(ride->id, RideSetSetting::LiftHillSpeed, td6->lift_hill_speed_num_circuits & 0x1F, flags); + set_operating_setting_nested(ride->id, RideSetSetting::LiftHillSpeed, td6->lift_hill_speed & 0x1F, flags); - uint8_t num_circuits = td6->lift_hill_speed_num_circuits >> 5; + uint8_t num_circuits = td6->num_circuits; if (num_circuits == 0) { num_circuits = 1; } set_operating_setting_nested(ride->id, RideSetSetting::NumCircuits, num_circuits, flags); - ride_set_to_default_inspection_interval(ride); + ride->SetToDefaultInspectionInterval(); ride->lifecycle_flags |= RIDE_LIFECYCLE_NOT_CUSTOM_DESIGN; - ride->colour_scheme_type = td6->version_and_colour_scheme & 3; + ride->colour_scheme_type = td6->colour_scheme; ride->entrance_style = td6->entrance_style; @@ -2014,7 +2061,7 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags ride->vehicle_colours[i].Ternary = td6->vehicle_additional_colour[i]; } - ride_set_name(ride, td6->name, flags); + ride_set_name(ride, td6->name.c_str(), flags); gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; *outRideIndex = ride->id; @@ -2057,7 +2104,7 @@ static money32 place_maze_design(uint8_t flags, Ride* ride, uint16_t mazeEntry, if (!gCheatsSandboxMode) { - if (!map_is_location_owned(floor2(x, 32), floor2(y, 32), z)) + if (!map_is_location_owned({ x, y, z })) { return MONEY32_UNDEFINED; } @@ -2066,11 +2113,11 @@ static money32 place_maze_design(uint8_t flags, Ride* ride, uint16_t mazeEntry, // Check support height if (!gCheatsDisableSupportLimits) { - TileElement* tileElement = map_get_surface_element_at({ x, y }); + auto surfaceElement = map_get_surface_element_at({ x, y }); uint8_t supportZ = (z + 32) >> 3; - if (supportZ > tileElement->base_height) + if (supportZ > surfaceElement->base_height) { - uint8_t supportHeight = (supportZ - tileElement->base_height) / 2; + uint8_t supportHeight = (supportZ - surfaceElement->base_height) / 2; uint8_t maxSupportHeight = RideData5[RIDE_TYPE_MAZE].max_height; if (supportHeight > maxSupportHeight) { @@ -2120,20 +2167,11 @@ static money32 place_maze_design(uint8_t flags, Ride* ride, uint16_t mazeEntry, if (flags & GAME_COMMAND_FLAG_APPLY) { - if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST)) - { - LocationXYZ16 coord; - coord.x = x + 8; - coord.y = y + 8; - coord.z = z; - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - // Place track element int32_t fx = floor2(x, 32); int32_t fy = floor2(y, 32); int32_t fz = z >> 3; - TileElement* tileElement = tile_element_insert(fx >> 5, fy >> 5, fz, 15); + TileElement* tileElement = tile_element_insert({ fx >> 5, fy >> 5, fz }, 0b1111); tileElement->clearance_height = fz + 4; tileElement->SetType(TILE_ELEMENT_TYPE_TRACK); tileElement->AsTrack()->SetTrackType(TRACK_ELEM_MAZE); @@ -2195,7 +2233,7 @@ void game_command_place_maze_design( * * rct2: 0x006D1EF0 */ -void track_design_draw_preview(rct_track_td6* td6, uint8_t* pixels) +void track_design_draw_preview(TrackDesign* td6, uint8_t* pixels) { // Make a copy of the map map_backup* mapBackup = track_design_preview_backup_map(); @@ -2278,17 +2316,20 @@ void track_design_draw_preview(rct_track_td6* td6, uint8_t* pixels) dpi.pitch = 0; dpi.bits = pixels; + auto drawingEngine = std::make_unique(GetContext()->GetUiContext()); + dpi.DrawingEngine = drawingEngine.get(); + CoordsXY offset = { size_x / 2, size_y / 2 }; for (uint8_t i = 0; i < 4; i++) { gCurrentRotation = i; - CoordsXY pos2d = translate_3d_to_2d_with_z(i, centre); - pos2d.x -= offset.x; - pos2d.y -= offset.y; + auto screenCoords = translate_3d_to_2d_with_z(i, centre); + screenCoords.x -= offset.x; + screenCoords.y -= offset.y; - int32_t left = pos2d.x; - int32_t top = pos2d.y; + int32_t left = screenCoords.x; + int32_t top = screenCoords.y; int32_t right = left + size_x; int32_t bottom = top + size_y; @@ -2299,7 +2340,7 @@ void track_design_draw_preview(rct_track_td6* td6, uint8_t* pixels) dpi.bits += TRACK_PREVIEW_IMAGE_SIZE; } - ride_delete(ride); + ride->Delete(); track_design_preview_restore_map(mapBackup); } @@ -2357,7 +2398,7 @@ static void track_design_preview_clear_map() { TileElement* tile_element = &gTileElements[i]; tile_element->ClearAs(TILE_ELEMENT_TYPE_SURFACE); - tile_element->flags = TILE_ELEMENT_FLAG_LAST_TILE; + tile_element->SetLastForTile(true); tile_element->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT); tile_element->AsSurface()->SetWaterHeight(0); tile_element->AsSurface()->SetSurfaceStyle(TERRAIN_GRASS); diff --git a/src/openrct2/ride/TrackDesign.h b/src/openrct2/ride/TrackDesign.h index 15f7634177..bb204abaf9 100644 --- a/src/openrct2/ride/TrackDesign.h +++ b/src/openrct2/ride/TrackDesign.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -21,9 +21,41 @@ struct Ride; #define TRACK_PREVIEW_IMAGE_SIZE (370 * 217) -#pragma pack(push, 1) +/* Track Entrance entry */ +struct TrackDesignEntranceElement +{ + int8_t z; + uint8_t direction; + int16_t x; + int16_t y; + bool isExit; +}; + +/* Track Scenery entry size: 0x16 */ +struct TrackDesignSceneryElement +{ + rct_object_entry scenery_object; // 0x00 + int8_t x; // 0x10 + int8_t y; // 0x11 + int8_t z; // 0x12 + uint8_t flags; // 0x13 direction quadrant tertiary colour + uint8_t primary_colour; // 0x14 + uint8_t secondary_colour; // 0x15 +}; + +/** + * Track design structure. + */ + +/* Track Element entry size: 0x02 */ +struct TrackDesignTrackElement +{ + uint8_t type; // 0x00 + uint8_t flags; // 0x01 +}; + /* Maze Element entry size: 0x04 */ -struct rct_td6_maze_element +struct TrackDesignMazeElement { union { @@ -44,125 +76,67 @@ struct rct_td6_maze_element }; }; }; -assert_struct_size(rct_td6_maze_element, 0x04); -/* Track Element entry size: 0x02 */ -struct rct_td6_track_element +struct TrackDesign { - uint8_t type; // 0x00 - uint8_t flags; // 0x01 -}; -assert_struct_size(rct_td6_track_element, 0x02); - -/* Track Entrance entry size: 0x06 */ -struct rct_td6_entrance_element -{ - int8_t z; // 0x00 - uint8_t direction; // 0x01 - int16_t x; // 0x02 - int16_t y; // 0x04 -}; -assert_struct_size(rct_td6_entrance_element, 0x06); - -/* Track Scenery entry size: 0x16 */ -struct rct_td6_scenery_element -{ - rct_object_entry scenery_object; // 0x00 - int8_t x; // 0x10 - int8_t y; // 0x11 - int8_t z; // 0x12 - uint8_t flags; // 0x13 direction quadrant tertiary colour - uint8_t primary_colour; // 0x14 - uint8_t secondary_colour; // 0x15 -}; -assert_struct_size(rct_td6_scenery_element, 0x16); - -/** - * Track design structure. - * size: 0x4E72B - */ -struct rct_track_td6 -{ - uint8_t type; // 0x00 + uint8_t type; uint8_t vehicle_type; - union - { - // After loading the track this is converted to - // a cost but before its a flags register - money32 cost; // 0x02 - uint32_t flags; // 0x02 - }; - union - { - // After loading the track this is converted to - // a flags register - uint8_t ride_mode; // 0x06 - uint8_t track_flags; // 0x06 - }; - uint8_t version_and_colour_scheme; // 0x07 0b0000_VVCC - rct_vehicle_colour vehicle_colours[RCT2_MAX_CARS_PER_TRAIN]; // 0x08 - union - { - uint8_t pad_48; - uint8_t track_spine_colour_rct1; // 0x48 - }; - union - { - uint8_t entrance_style; // 0x49 - uint8_t track_rail_colour_rct1; // 0x49 - }; - union - { - uint8_t total_air_time; // 0x4A - uint8_t track_support_colour_rct1; // 0x4A - }; - uint8_t depart_flags; // 0x4B - uint8_t number_of_trains; // 0x4C - uint8_t number_of_cars_per_train; // 0x4D - uint8_t min_waiting_time; // 0x4E - uint8_t max_waiting_time; // 0x4F + money32 cost; + uint32_t flags; + uint8_t ride_mode; + uint8_t track_flags; + uint8_t colour_scheme; + rct_vehicle_colour vehicle_colours[RCT2_MAX_CARS_PER_TRAIN]; + uint8_t entrance_style; + uint8_t total_air_time; + uint8_t depart_flags; + uint8_t number_of_trains; + uint8_t number_of_cars_per_train; + uint8_t min_waiting_time; + uint8_t max_waiting_time; uint8_t operation_setting; - int8_t max_speed; // 0x51 - int8_t average_speed; // 0x52 - uint16_t ride_length; // 0x53 - uint8_t max_positive_vertical_g; // 0x55 - int8_t max_negative_vertical_g; // 0x56 - uint8_t max_lateral_g; // 0x57 - union - { - uint8_t inversions; // 0x58 - uint8_t holes; // 0x58 - }; - uint8_t drops; // 0x59 - uint8_t highest_drop_height; // 0x5A - uint8_t excitement; // 0x5B - uint8_t intensity; // 0x5C - uint8_t nausea; // 0x5D - money16 upkeep_cost; // 0x5E - uint8_t track_spine_colour[RCT12_NUM_COLOUR_SCHEMES]; // 0x60 - uint8_t track_rail_colour[RCT12_NUM_COLOUR_SCHEMES]; // 0x64 - uint8_t track_support_colour[RCT12_NUM_COLOUR_SCHEMES]; // 0x68 - uint32_t flags2; // 0x6C - rct_object_entry vehicle_object; // 0x70 - uint8_t space_required_x; // 0x80 - uint8_t space_required_y; // 0x81 - uint8_t vehicle_additional_colour[RCT2_MAX_CARS_PER_TRAIN]; // 0x82 - uint8_t lift_hill_speed_num_circuits; // 0xA2 0bCCCL_LLLL - void* elements; // 0xA3 (data starts here in file) - size_t elementsSize; + int8_t max_speed; + int8_t average_speed; + uint16_t ride_length; + uint8_t max_positive_vertical_g; + int8_t max_negative_vertical_g; + uint8_t max_lateral_g; + uint8_t inversions; + uint8_t holes; + uint8_t drops; + uint8_t highest_drop_height; + uint8_t excitement; + uint8_t intensity; + uint8_t nausea; + money16 upkeep_cost; + uint8_t track_spine_colour[RCT12_NUM_COLOUR_SCHEMES]; + uint8_t track_rail_colour[RCT12_NUM_COLOUR_SCHEMES]; + uint8_t track_support_colour[RCT12_NUM_COLOUR_SCHEMES]; + uint32_t flags2; + rct_object_entry vehicle_object; + uint8_t space_required_x; + uint8_t space_required_y; + uint8_t vehicle_additional_colour[RCT2_MAX_CARS_PER_TRAIN]; + uint8_t lift_hill_speed; + uint8_t num_circuits; - rct_td6_maze_element* maze_elements; - rct_td6_track_element* track_elements; - rct_td6_entrance_element* entrance_elements; - rct_td6_scenery_element* scenery_elements; + std::vector maze_elements; + std::vector track_elements; + std::vector entrance_elements; + std::vector scenery_elements; - utf8* name; + std::string name; + +public: + rct_string_id CreateTrackDesign(const Ride& ride); + rct_string_id CreateTrackDesignScenery(); + +private: + uint8_t _saveDirection; + rct_string_id CreateTrackDesignTrack(const Ride& ride); + rct_string_id CreateTrackDesignMaze(const Ride& ride); + CoordsXYE MazeGetFirstElement(const Ride& ride); }; -// Warning: improper struct size in comment -#ifdef PLATFORM_32BIT -assert_struct_size(rct_track_td6, 0xbf); -#endif -#pragma pack(pop) // Only written to in RCT2, not used in OpenRCT2. All of these are elements that had to be invented in RCT1. enum : uint32_t @@ -198,12 +172,12 @@ enum enum { PTD_OPERATION_DRAW_OUTLINES, - PTD_OPERATION_1, - PTD_OPERATION_2, + PTD_OPERATION_PLACE_QUERY, + PTD_OPERATION_PLACE, PTD_OPERATION_GET_PLACE_Z, - PTD_OPERATION_4, - PTD_OPERATION_GET_COST, - PTD_OPERATION_CLEAR_OUTLINES, + PTD_OPERATION_PLACE_GHOST, + PTD_OPERATION_PLACE_TRACK_PREVIEW, + PTD_OPERATION_REMOVE_GHOST, }; enum @@ -213,7 +187,7 @@ enum MAZE_ELEMENT_TYPE_EXIT = (1 << 7) }; -extern rct_track_td6* gActiveTrackDesign; +extern TrackDesign* gActiveTrackDesign; extern bool gTrackDesignSceneryToggle; extern LocationXYZ16 gTrackPreviewMin; extern LocationXYZ16 gTrackPreviewMax; @@ -224,13 +198,12 @@ extern bool byte_9D8150; extern bool gTrackDesignSaveMode; extern ride_id_t gTrackDesignSaveRideIndex; -rct_track_td6* track_design_open(const utf8* path); -void track_design_dispose(rct_track_td6* td6); +std::unique_ptr track_design_open(const utf8* path); -void track_design_mirror(rct_track_td6* td6); +void track_design_mirror(TrackDesign* td6); int32_t place_virtual_track( - rct_track_td6* td6, uint8_t ptdOperation, bool placeScenery, Ride* ride, int16_t x, int16_t y, int16_t z); + TrackDesign* td6, uint8_t ptdOperation, bool placeScenery, Ride* ride, int16_t x, int16_t y, int16_t z); void game_command_place_track_design( int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); @@ -240,7 +213,7 @@ void game_command_place_maze_design( /////////////////////////////////////////////////////////////////////////////// // Track design preview /////////////////////////////////////////////////////////////////////////////// -void track_design_draw_preview(rct_track_td6* td6, uint8_t* pixels); +void track_design_draw_preview(TrackDesign* td6, uint8_t* pixels); /////////////////////////////////////////////////////////////////////////////// // Track design saving @@ -249,11 +222,11 @@ void track_design_save_init(); void track_design_save_reset_scenery(); bool track_design_save_contains_tile_element(const TileElement* tileElement); void track_design_save_select_nearby_scenery(ride_id_t rideIndex); -void track_design_save_select_tile_element( - int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement, bool collect); -bool track_design_save(ride_id_t rideIndex); -bool track_design_save_to_file(const utf8* path); +void track_design_save_select_tile_element(int32_t interactionType, CoordsXY loc, TileElement* tileElement, bool collect); bool track_design_are_entrance_and_exit_placed(); +extern std::vector _trackSavedTileElementsDesc; +extern std::vector _trackSavedTileElements; + #endif diff --git a/src/openrct2/ride/TrackDesignRepository.cpp b/src/openrct2/ride/TrackDesignRepository.cpp index 39573aceba..9f27ef5875 100644 --- a/src/openrct2/ride/TrackDesignRepository.cpp +++ b/src/openrct2/ride/TrackDesignRepository.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -88,7 +88,6 @@ public: { item.Flags |= TRIF_READ_ONLY; } - track_design_dispose(td6); return std::make_tuple(true, item); } else diff --git a/src/openrct2/ride/TrackDesignRepository.h b/src/openrct2/ride/TrackDesignRepository.h index 930ee25d77..51d4a841d8 100644 --- a/src/openrct2/ride/TrackDesignRepository.h +++ b/src/openrct2/ride/TrackDesignRepository.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/TrackDesignSave.cpp b/src/openrct2/ride/TrackDesignSave.cpp index a6e690d7a2..05bfcbddf1 100644 --- a/src/openrct2/ride/TrackDesignSave.cpp +++ b/src/openrct2/ride/TrackDesignSave.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -39,52 +39,38 @@ bool gTrackDesignSaveMode = false; ride_id_t gTrackDesignSaveRideIndex = RIDE_ID_NULL; -static size_t _trackSavedTileElementsCount; -static TileElement* _trackSavedTileElements[TRACK_MAX_SAVED_TILE_ELEMENTS]; - -static size_t _trackSavedTileElementsDescCount; -static rct_td6_scenery_element _trackSavedTileElementsDesc[TRACK_MAX_SAVED_TILE_ELEMENTS]; - -static rct_track_td6* _trackDesign; -static uint8_t _trackSaveDirection; +std::vector _trackSavedTileElements; +std::vector _trackSavedTileElementsDesc; static bool track_design_save_should_select_scenery_around(ride_id_t rideIndex, TileElement* tileElement); static void track_design_save_select_nearby_scenery_for_tile(ride_id_t rideIndex, int32_t cx, int32_t cy); -static bool track_design_save_add_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement); -static void track_design_save_remove_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement); -static bool track_design_save_copy_scenery_to_td6(rct_track_td6* td6); -static rct_track_td6* track_design_save_to_td6(ride_id_t rideIndex); -static bool track_design_save_to_td6_for_maze(const Ride* ride, rct_track_td6* td6); -static bool track_design_save_to_td6_for_tracked_ride(const Ride* ride, rct_track_td6* td6); +static bool track_design_save_add_tile_element(int32_t interactionType, CoordsXY loc, TileElement* tileElement); +static void track_design_save_remove_tile_element(int32_t interactionType, CoordsXY loc, TileElement* tileElement); void track_design_save_init() { - _trackSavedTileElementsCount = 0; - _trackSavedTileElementsDescCount = 0; - - std::memset(_trackSavedTileElements, 0, sizeof(_trackSavedTileElements)); - std::memset(_trackSavedTileElementsDesc, 0, sizeof(_trackSavedTileElementsDesc)); + _trackSavedTileElements.clear(); + _trackSavedTileElementsDesc.clear(); } /** * * rct2: 0x006D2B07 */ -void track_design_save_select_tile_element( - int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement, bool collect) +void track_design_save_select_tile_element(int32_t interactionType, CoordsXY loc, TileElement* tileElement, bool collect) { if (track_design_save_contains_tile_element(tileElement)) { if (!collect) { - track_design_save_remove_tile_element(interactionType, x, y, tileElement); + track_design_save_remove_tile_element(interactionType, loc, tileElement); } } else { if (collect) { - if (!track_design_save_add_tile_element(interactionType, x, y, tileElement)) + if (!track_design_save_add_tile_element(interactionType, loc, tileElement)) { context_show_error( STR_SAVE_TRACK_SCENERY_UNABLE_TO_SELECT_ADDITIONAL_ITEM_OF_SCENERY, @@ -100,23 +86,16 @@ void track_design_save_select_tile_element( */ void track_design_save_select_nearby_scenery(ride_id_t rideIndex) { - TileElement* tileElement; - - for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) + tile_element_iterator it; + tile_element_iterator_begin(&it); + do { - for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) + if (track_design_save_should_select_scenery_around(rideIndex, it.element)) { - tileElement = map_get_first_element_at(x, y); - do - { - if (track_design_save_should_select_scenery_around(rideIndex, tileElement)) - { - track_design_save_select_nearby_scenery_for_tile(rideIndex, x, y); - break; - } - } while (!(tileElement++)->IsLastForTile()); + track_design_save_select_nearby_scenery_for_tile(rideIndex, it.x, it.y); } - } + } while (tile_element_iterator_next(&it)); + gfx_invalidate_screen(); } @@ -130,75 +109,11 @@ void track_design_save_reset_scenery() gfx_invalidate_screen(); } -static void track_design_save_callback(int32_t result, [[maybe_unused]] const utf8* path) -{ - free(_trackDesign->track_elements); - free(_trackDesign->entrance_elements); - free(_trackDesign->scenery_elements); - free(_trackDesign); - - if (result == MODAL_RESULT_OK) - { - track_repository_scan(); - } - gfx_invalidate_screen(); -} - -/** - * - * rct2: 0x006D2804, 0x006D264D - */ -bool track_design_save(ride_id_t rideIndex) -{ - Ride* ride = get_ride(rideIndex); - - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); - return false; - } - - if (!ride_has_ratings(ride)) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); - return false; - } - - _trackDesign = track_design_save_to_td6(rideIndex); - if (_trackDesign == nullptr) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); - return false; - } - - if (gTrackDesignSaveMode) - { - if (!track_design_save_copy_scenery_to_td6(_trackDesign)) - { - free(_trackDesign->track_elements); - free(_trackDesign->entrance_elements); - free(_trackDesign); - return false; - } - } - - utf8 track_name[256]; - format_string(track_name, sizeof(track_name), ride->name, &ride->name_arguments); - - auto intent = Intent(WC_LOADSAVE); - intent.putExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK); - intent.putExtra(INTENT_EXTRA_PATH, std::string{ track_name }); - intent.putExtra(INTENT_EXTRA_CALLBACK, (void*)track_design_save_callback); - context_open_intent(&intent); - - return true; -} - bool track_design_save_contains_tile_element(const TileElement* tileElement) { - for (size_t i = 0; i < _trackSavedTileElementsCount; i++) + for (auto& tile : _trackSavedTileElements) { - if (_trackSavedTileElements[i] == tileElement) + if (tile == tileElement) { return true; } @@ -248,7 +163,7 @@ static bool track_design_save_can_add_tile_element(TileElement* tileElement) } // Get number of spare elements left - size_t spareSavedElements = TRACK_MAX_SAVED_TILE_ELEMENTS - _trackSavedTileElementsCount; + size_t spareSavedElements = TRACK_MAX_SAVED_TILE_ELEMENTS - _trackSavedTileElements.size(); if (newElementCount > spareSavedElements) { // No more spare saved elements left @@ -262,12 +177,12 @@ static bool track_design_save_can_add_tile_element(TileElement* tileElement) * * rct2: 0x006D2F4C */ -static void track_design_save_push_tile_element(int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_push_tile_element(CoordsXY loc, TileElement* tileElement) { - if (_trackSavedTileElementsCount < TRACK_MAX_SAVED_TILE_ELEMENTS) + if (_trackSavedTileElements.size() < TRACK_MAX_SAVED_TILE_ELEMENTS) { - _trackSavedTileElements[_trackSavedTileElementsCount++] = tileElement; - map_invalidate_tile_full(x, y); + _trackSavedTileElements.push_back(tileElement); + map_invalidate_tile_full(loc.x, loc.y); } } @@ -276,51 +191,52 @@ static void track_design_save_push_tile_element(int32_t x, int32_t y, TileElemen * rct2: 0x006D2FA7 */ static void track_design_save_push_tile_element_desc( - const rct_object_entry* entry, int32_t x, int32_t y, int32_t z, uint8_t flags, uint8_t primaryColour, - uint8_t secondaryColour) + const rct_object_entry* entry, CoordsXYZ loc, uint8_t flags, uint8_t primaryColour, uint8_t secondaryColour) { - rct_td6_scenery_element* item = &_trackSavedTileElementsDesc[_trackSavedTileElementsDescCount++]; - item->scenery_object = *entry; - item->x = x / 32; - item->y = y / 32; - item->z = z; - item->flags = flags; - item->primary_colour = primaryColour; - item->secondary_colour = secondaryColour; + TrackDesignSceneryElement item{}; + item.scenery_object = *entry; + item.x = loc.x / 32; + item.y = loc.y / 32; + item.z = loc.z / 8; + item.flags = flags; + item.primary_colour = primaryColour; + item.secondary_colour = secondaryColour; + + _trackSavedTileElementsDesc.push_back(item); } -static void track_design_save_add_scenery(int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_add_scenery(CoordsXY loc, SmallSceneryElement* sceneryElement) { - SmallSceneryElement* sceneryElement = tileElement->AsSmallScenery(); int32_t entryType = sceneryElement->GetEntryIndex(); auto entry = object_entry_get_entry(OBJECT_TYPE_SMALL_SCENERY, entryType); uint8_t flags = 0; - flags |= tileElement->GetDirection(); - flags |= tileElement->AsSmallScenery()->GetSceneryQuadrant() << 2; + flags |= sceneryElement->GetDirection(); + flags |= sceneryElement->GetSceneryQuadrant() << 2; uint8_t primaryColour = sceneryElement->GetPrimaryColour(); uint8_t secondaryColour = sceneryElement->GetSecondaryColour(); - track_design_save_push_tile_element(x, y, tileElement); - track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, primaryColour, secondaryColour); + track_design_save_push_tile_element(loc, reinterpret_cast(sceneryElement)); + track_design_save_push_tile_element_desc( + entry, { loc.x, loc.y, sceneryElement->base_height * 8 }, flags, primaryColour, secondaryColour); } -static void track_design_save_add_large_scenery(int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_add_large_scenery(CoordsXY loc, LargeSceneryElement* tileElement) { rct_large_scenery_tile *sceneryTiles, *tile; int32_t x0, y0, z0, z; int32_t direction, sequence; - int32_t entryType = tileElement->AsLargeScenery()->GetEntryIndex(); + int32_t entryType = tileElement->GetEntryIndex(); auto entry = object_entry_get_entry(OBJECT_TYPE_LARGE_SCENERY, entryType); sceneryTiles = get_large_scenery_entry(entryType)->large_scenery.tiles; z = tileElement->base_height; direction = tileElement->GetDirection(); - sequence = tileElement->AsLargeScenery()->GetSequenceIndex(); + sequence = tileElement->GetSequenceIndex(); - if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, nullptr)) + if (!map_large_scenery_get_origin(loc.x, loc.y, z, direction, sequence, &x0, &y0, &z0, nullptr)) { return; } @@ -333,63 +249,62 @@ static void track_design_save_add_large_scenery(int32_t x, int32_t y, TileElemen int16_t offsetY = tile->y_offset; rotate_map_coordinates(&offsetX, &offsetY, direction); - x = x0 + offsetX; - y = y0 + offsetY; - z = (z0 + tile->z_offset) / 8; - tileElement = map_get_large_scenery_segment(x, y, z, direction, sequence); - if (tileElement != nullptr) + CoordsXYZ tileLoc = { x0 + offsetX, y0 + offsetY, (z0 + tile->z_offset) }; + auto largeElement = map_get_large_scenery_segment(tileLoc.x, tileLoc.y, tileLoc.z / 8, direction, sequence); + if (largeElement != nullptr) { if (sequence == 0) { - uint8_t flags = tileElement->GetDirection(); - uint8_t primaryColour = tileElement->AsLargeScenery()->GetPrimaryColour(); - uint8_t secondaryColour = tileElement->AsLargeScenery()->GetSecondaryColour(); + uint8_t flags = largeElement->GetDirection(); + uint8_t primaryColour = largeElement->GetPrimaryColour(); + uint8_t secondaryColour = largeElement->GetSecondaryColour(); - track_design_save_push_tile_element_desc(entry, x, y, z, flags, primaryColour, secondaryColour); + track_design_save_push_tile_element_desc(entry, tileLoc, flags, primaryColour, secondaryColour); } - track_design_save_push_tile_element(x, y, tileElement); + track_design_save_push_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast(largeElement)); } } } -static void track_design_save_add_wall(int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_add_wall(CoordsXY loc, WallElement* wallElement) { - int32_t entryType = tileElement->AsWall()->GetEntryIndex(); + int32_t entryType = wallElement->GetEntryIndex(); auto entry = object_entry_get_entry(OBJECT_TYPE_WALLS, entryType); uint8_t flags = 0; - flags |= tileElement->GetDirection(); - flags |= tileElement->AsWall()->GetTertiaryColour() << 2; + flags |= wallElement->GetDirection(); + flags |= wallElement->GetTertiaryColour() << 2; - uint8_t secondaryColour = tileElement->AsWall()->GetSecondaryColour(); - uint8_t primaryColour = tileElement->AsWall()->GetPrimaryColour(); + uint8_t secondaryColour = wallElement->GetSecondaryColour(); + uint8_t primaryColour = wallElement->GetPrimaryColour(); - track_design_save_push_tile_element(x, y, tileElement); - track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, primaryColour, secondaryColour); + track_design_save_push_tile_element(loc, reinterpret_cast(wallElement)); + track_design_save_push_tile_element_desc( + entry, { loc.x, loc.y, wallElement->base_height * 8 }, flags, primaryColour, secondaryColour); } -static void track_design_save_add_footpath(int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_add_footpath(CoordsXY loc, PathElement* pathElement) { - int32_t entryType = tileElement->AsPath()->GetPathEntryIndex(); + int32_t entryType = pathElement->GetPathEntryIndex(); auto entry = object_entry_get_entry(OBJECT_TYPE_PATHS, entryType); uint8_t flags = 0; - flags |= tileElement->AsPath()->GetEdges(); - flags |= (tileElement->AsPath()->GetSlopeDirection()) << 5; - if (tileElement->AsPath()->IsSloped()) + flags |= pathElement->GetEdges(); + flags |= (pathElement->GetSlopeDirection()) << 5; + if (pathElement->IsSloped()) flags |= 0b00010000; - if (tileElement->AsPath()->IsQueue()) + if (pathElement->IsQueue()) flags |= 1 << 7; - track_design_save_push_tile_element(x, y, tileElement); - track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, 0, 0); + track_design_save_push_tile_element(loc, reinterpret_cast(pathElement)); + track_design_save_push_tile_element_desc(entry, { loc.x, loc.y, pathElement->base_height * 8 }, flags, 0, 0); } /** * * rct2: 0x006D2B3C */ -static bool track_design_save_add_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement) +static bool track_design_save_add_tile_element(int32_t interactionType, CoordsXY loc, TileElement* tileElement) { if (!track_design_save_can_add_tile_element(tileElement)) { @@ -399,16 +314,16 @@ static bool track_design_save_add_tile_element(int32_t interactionType, int32_t switch (interactionType) { case VIEWPORT_INTERACTION_ITEM_SCENERY: - track_design_save_add_scenery(x, y, tileElement); + track_design_save_add_scenery(loc, tileElement->AsSmallScenery()); return true; case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: - track_design_save_add_large_scenery(x, y, tileElement); + track_design_save_add_large_scenery(loc, tileElement->AsLargeScenery()); return true; case VIEWPORT_INTERACTION_ITEM_WALL: - track_design_save_add_wall(x, y, tileElement); + track_design_save_add_wall(loc, tileElement->AsWall()); return true; case VIEWPORT_INTERACTION_ITEM_FOOTPATH: - track_design_save_add_footpath(x, y, tileElement); + track_design_save_add_footpath(loc, tileElement->AsPath()); return true; default: return false; @@ -419,13 +334,13 @@ static bool track_design_save_add_tile_element(int32_t interactionType, int32_t * * rct2: 0x006D2F78 */ -static void track_design_save_pop_tile_element(int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_pop_tile_element(CoordsXY loc, TileElement* tileElement) { - map_invalidate_tile_full(x, y); + map_invalidate_tile_full(loc.x, loc.y); // Find index of map element to remove size_t removeIndex = SIZE_MAX; - for (size_t i = 0; i < _trackSavedTileElementsCount; i++) + for (size_t i = 0; i < _trackSavedTileElements.size(); i++) { if (_trackSavedTileElements[i] == tileElement) { @@ -433,18 +348,9 @@ static void track_design_save_pop_tile_element(int32_t x, int32_t y, TileElement } } - // Remove map element from list if (removeIndex != SIZE_MAX) { - size_t remainingNumItems = _trackSavedTileElementsCount - removeIndex - 1; - if (remainingNumItems > 0) - { - memmove( - &_trackSavedTileElements[removeIndex], &_trackSavedTileElements[removeIndex + 1], - remainingNumItems * sizeof(TileElement*)); - } - _trackSavedTileElementsCount--; - _trackSavedTileElements[_trackSavedTileElementsCount] = nullptr; + _trackSavedTileElements.erase(_trackSavedTileElements.begin() + removeIndex); } } @@ -452,18 +358,17 @@ static void track_design_save_pop_tile_element(int32_t x, int32_t y, TileElement * * rct2: 0x006D2FDD */ -static void track_design_save_pop_tile_element_desc( - const rct_object_entry* entry, int32_t x, int32_t y, int32_t z, uint8_t flags) +static void track_design_save_pop_tile_element_desc(const rct_object_entry* entry, CoordsXYZ loc, uint8_t flags) { size_t removeIndex = SIZE_MAX; - for (size_t i = 0; i < _trackSavedTileElementsDescCount; i++) + for (size_t i = 0; i < _trackSavedTileElementsDesc.size(); i++) { - rct_td6_scenery_element* item = &_trackSavedTileElementsDesc[i]; - if (item->x != x / 32) + TrackDesignSceneryElement* item = &_trackSavedTileElementsDesc[i]; + if (item->x != loc.x / 32) continue; - if (item->y != y / 32) + if (item->y != loc.y / 32) continue; - if (item->z != z) + if (item->z != loc.z / 8) continue; if (item->flags != flags) continue; @@ -475,45 +380,38 @@ static void track_design_save_pop_tile_element_desc( if (removeIndex != SIZE_MAX) { - size_t remainingNumItems = _trackSavedTileElementsDescCount - removeIndex - 1; - if (remainingNumItems > 0) - { - memmove( - &_trackSavedTileElementsDesc[removeIndex], &_trackSavedTileElementsDesc[removeIndex + 1], - remainingNumItems * sizeof(rct_td6_scenery_element)); - } - _trackSavedTileElementsDescCount--; + _trackSavedTileElementsDesc.erase(_trackSavedTileElementsDesc.begin() + removeIndex); } } -static void track_design_save_remove_scenery(int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_remove_scenery(CoordsXY loc, SmallSceneryElement* sceneryElement) { - int32_t entryType = tileElement->AsSmallScenery()->GetEntryIndex(); + int32_t entryType = sceneryElement->GetEntryIndex(); auto entry = object_entry_get_entry(OBJECT_TYPE_SMALL_SCENERY, entryType); uint8_t flags = 0; - flags |= tileElement->GetDirection(); - flags |= tileElement->AsSmallScenery()->GetSceneryQuadrant() << 2; + flags |= sceneryElement->GetDirection(); + flags |= sceneryElement->GetSceneryQuadrant() << 2; - track_design_save_pop_tile_element(x, y, tileElement); - track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags); + track_design_save_pop_tile_element(loc, reinterpret_cast(sceneryElement)); + track_design_save_pop_tile_element_desc(entry, { loc.x, loc.y, sceneryElement->base_height * 8 }, flags); } -static void track_design_save_remove_large_scenery(int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_remove_large_scenery(CoordsXY loc, LargeSceneryElement* tileElement) { rct_large_scenery_tile *sceneryTiles, *tile; int32_t x0, y0, z0, z; int32_t direction, sequence; - int32_t entryType = tileElement->AsLargeScenery()->GetEntryIndex(); + int32_t entryType = tileElement->GetEntryIndex(); auto entry = object_entry_get_entry(OBJECT_TYPE_LARGE_SCENERY, entryType); sceneryTiles = get_large_scenery_entry(entryType)->large_scenery.tiles; z = tileElement->base_height; direction = tileElement->GetDirection(); - sequence = tileElement->AsLargeScenery()->GetSequenceIndex(); + sequence = tileElement->GetSequenceIndex(); - if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, nullptr)) + if (!map_large_scenery_get_origin(loc.x, loc.y, z, direction, sequence, &x0, &y0, &z0, nullptr)) { return; } @@ -526,71 +424,69 @@ static void track_design_save_remove_large_scenery(int32_t x, int32_t y, TileEle int16_t offsetY = tile->y_offset; rotate_map_coordinates(&offsetX, &offsetY, direction); - x = x0 + offsetX; - y = y0 + offsetY; - z = (z0 + tile->z_offset) / 8; - tileElement = map_get_large_scenery_segment(x, y, z, direction, sequence); - if (tileElement != nullptr) + CoordsXYZ tileLoc = { x0 + offsetX, y0 + offsetY, z0 + tile->z_offset }; + auto largeElement = map_get_large_scenery_segment(tileLoc.x, tileLoc.y, tileLoc.z / 8, direction, sequence); + if (largeElement != nullptr) { if (sequence == 0) { - uint8_t flags = tileElement->GetDirection(); - track_design_save_pop_tile_element_desc(entry, x, y, z, flags); + uint8_t flags = largeElement->GetDirection(); + track_design_save_pop_tile_element_desc(entry, tileLoc, flags); } - track_design_save_pop_tile_element(x, y, tileElement); + track_design_save_pop_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast(largeElement)); } } } -static void track_design_save_remove_wall(int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_remove_wall(CoordsXY loc, WallElement* wallElement) { - int32_t entryType = tileElement->AsWall()->GetEntryIndex(); + int32_t entryType = wallElement->GetEntryIndex(); auto entry = object_entry_get_entry(OBJECT_TYPE_WALLS, entryType); uint8_t flags = 0; - flags |= tileElement->GetDirection(); - flags |= tileElement->AsWall()->GetTertiaryColour() << 2; + flags |= wallElement->GetDirection(); + flags |= wallElement->GetTertiaryColour() << 2; - track_design_save_pop_tile_element(x, y, tileElement); - track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags); + track_design_save_pop_tile_element(loc, reinterpret_cast(wallElement)); + track_design_save_pop_tile_element_desc(entry, { loc.x, loc.y, wallElement->base_height * 8 }, flags); } -static void track_design_save_remove_footpath(int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_remove_footpath(CoordsXY loc, PathElement* pathElement) { - int32_t entryType = tileElement->AsPath()->GetPathEntryIndex(); + int32_t entryType = pathElement->GetPathEntryIndex(); auto entry = object_entry_get_entry(OBJECT_TYPE_PATHS, entryType); uint8_t flags = 0; - flags |= tileElement->AsPath()->GetEdges(); - if (tileElement->AsPath()->IsSloped()) + flags |= pathElement->GetEdges(); + if (pathElement->IsSloped()) flags |= (1 << 4); - flags |= (tileElement->AsPath()->GetSlopeDirection()) << 5; - if (tileElement->AsPath()->IsQueue()) + flags |= (pathElement->GetSlopeDirection()) << 5; + if (pathElement->IsQueue()) flags |= (1 << 7); - track_design_save_pop_tile_element(x, y, tileElement); - track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags); + track_design_save_pop_tile_element(loc, reinterpret_cast(pathElement)); + track_design_save_pop_tile_element_desc(entry, { loc.x, loc.y, pathElement->base_height * 8 }, flags); } /** * * rct2: 0x006D2B3C */ -static void track_design_save_remove_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement) +static void track_design_save_remove_tile_element(int32_t interactionType, CoordsXY loc, TileElement* tileElement) { switch (interactionType) { case VIEWPORT_INTERACTION_ITEM_SCENERY: - track_design_save_remove_scenery(x, y, tileElement); + track_design_save_remove_scenery(loc, tileElement->AsSmallScenery()); break; case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: - track_design_save_remove_large_scenery(x, y, tileElement); + track_design_save_remove_large_scenery(loc, tileElement->AsLargeScenery()); break; case VIEWPORT_INTERACTION_ITEM_WALL: - track_design_save_remove_wall(x, y, tileElement); + track_design_save_remove_wall(loc, tileElement->AsWall()); break; case VIEWPORT_INTERACTION_ITEM_FOOTPATH: - track_design_save_remove_footpath(x, y, tileElement); + track_design_save_remove_footpath(loc, tileElement->AsPath()); break; } } @@ -629,6 +525,8 @@ static void track_design_save_select_nearby_scenery_for_tile(ride_id_t rideIndex for (int32_t x = cx - TRACK_NEARBY_SCENERY_DISTANCE; x <= cx + TRACK_NEARBY_SCENERY_DISTANCE; x++) { tileElement = map_get_first_element_at(x, y); + if (tileElement == nullptr) + continue; do { int32_t interactionType = VIEWPORT_INTERACTION_ITEM_NONE; @@ -655,669 +553,10 @@ static void track_design_save_select_nearby_scenery_for_tile(ride_id_t rideIndex { if (!track_design_save_contains_tile_element(tileElement)) { - track_design_save_add_tile_element(interactionType, x * 32, y * 32, tileElement); + track_design_save_add_tile_element(interactionType, { x * 32, y * 32 }, tileElement); } } } while (!(tileElement++)->IsLastForTile()); } } } - -/* Based on rct2: 0x006D2897 */ -static bool track_design_save_copy_scenery_to_td6(rct_track_td6* td6) -{ - // Copy TD6 scenery elements to new memory and add end marker - size_t totalSceneryElementsSize = _trackSavedTileElementsDescCount * sizeof(rct_td6_scenery_element); - td6->scenery_elements = (rct_td6_scenery_element*)malloc(totalSceneryElementsSize + 1); - std::memcpy(td6->scenery_elements, _trackSavedTileElementsDesc, totalSceneryElementsSize); - *((uint8_t*)&td6->scenery_elements[_trackSavedTileElementsDescCount]) = 0xFF; - - // Run an element loop - for (size_t i = 0; i < _trackSavedTileElementsDescCount; i++) - { - rct_td6_scenery_element* scenery = &td6->scenery_elements[i]; - - switch (object_entry_get_type(&scenery->scenery_object)) - { - case OBJECT_TYPE_PATHS: - { - uint8_t slope = (scenery->flags & 0x60) >> 5; - slope -= _trackSaveDirection; - - scenery->flags &= 0x9F; - scenery->flags |= ((slope & 3) << 5); - - // Direction of connection on path - uint8_t direction = scenery->flags & 0xF; - // Rotate the direction by the track direction - direction = ((direction << 4) >> _trackSaveDirection); - - scenery->flags &= 0xF0; - scenery->flags |= (direction & 0xF) | (direction >> 4); - break; - } - case OBJECT_TYPE_WALLS: - { - uint8_t direction = scenery->flags & 3; - direction -= _trackSaveDirection; - - scenery->flags &= 0xFC; - scenery->flags |= (direction & 3); - break; - } - default: - { - uint8_t direction = scenery->flags & 3; - uint8_t quadrant = (scenery->flags & 0x0C) >> 2; - - direction -= _trackSaveDirection; - quadrant -= _trackSaveDirection; - - scenery->flags &= 0xF0; - scenery->flags |= (direction & 3) | ((quadrant & 3) << 2); - break; - } - } - - int16_t x = ((uint8_t)scenery->x) * 32 - gTrackPreviewOrigin.x; - int16_t y = ((uint8_t)scenery->y) * 32 - gTrackPreviewOrigin.y; - rotate_map_coordinates(&x, &y, (0 - _trackSaveDirection) & 3); - x /= 32; - y /= 32; - - if (x > 127 || y > 127 || x < -126 || y < -126) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY); - SafeFree(td6->scenery_elements); - return false; - } - - scenery->x = (int8_t)x; - scenery->y = (int8_t)y; - - int32_t z = scenery->z * 8 - gTrackPreviewOrigin.z; - z /= 8; - if (z > 127 || z < -126) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY); - SafeFree(td6->scenery_elements); - return false; - } - scenery->z = z; - } - - return true; -} - -/** - * - * rct2: 0x006CE44F - */ -static rct_track_td6* track_design_save_to_td6(ride_id_t rideIndex) -{ - rct_track_td6* td6 = (rct_track_td6*)calloc(1, sizeof(rct_track_td6)); - Ride* ride = get_ride(rideIndex); - td6->type = ride->type; - auto object = object_entry_get_entry(OBJECT_TYPE_RIDE, ride->subtype); - - // Note we are only copying rct_object_entry in size and - // not the extended as we don't need the chunk size. - std::memcpy(&td6->vehicle_object, object, sizeof(rct_object_entry)); - - td6->ride_mode = ride->mode; - - td6->version_and_colour_scheme = (ride->colour_scheme_type & 3) | (1 << 3); // Version .TD6 - - for (int32_t i = 0; i < RCT12_MAX_VEHICLES_PER_RIDE; i++) - { - td6->vehicle_colours[i].body_colour = ride->vehicle_colours[i].Body; - td6->vehicle_colours[i].trim_colour = ride->vehicle_colours[i].Trim; - td6->vehicle_additional_colour[i] = ride->vehicle_colours[i].Ternary; - } - - for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) - { - td6->track_spine_colour[i] = ride->track_colour[i].main; - td6->track_rail_colour[i] = ride->track_colour[i].additional; - td6->track_support_colour[i] = ride->track_colour[i].supports; - } - - td6->depart_flags = ride->depart_flags; - td6->number_of_trains = ride->num_vehicles; - td6->number_of_cars_per_train = ride->num_cars_per_train; - td6->min_waiting_time = ride->min_waiting_time; - td6->max_waiting_time = ride->max_waiting_time; - td6->operation_setting = ride->operation_option; - td6->lift_hill_speed_num_circuits = ride->lift_hill_speed | (ride->num_circuits << 5); - - td6->entrance_style = ride->entrance_style; - td6->max_speed = (int8_t)(ride->max_speed / 65536); - td6->average_speed = (int8_t)(ride->average_speed / 65536); - td6->ride_length = ride_get_total_length(ride) / 65536; - td6->max_positive_vertical_g = ride->max_positive_vertical_g / 32; - td6->max_negative_vertical_g = ride->max_negative_vertical_g / 32; - td6->max_lateral_g = ride->max_lateral_g / 32; - td6->inversions = ride->inversions; - td6->drops = ride->drops; - td6->highest_drop_height = ride->highest_drop_height; - - uint16_t total_air_time = (ride->total_air_time * 123) / 1024; - if (total_air_time > 255) - { - total_air_time = 0; - } - td6->total_air_time = (uint8_t)total_air_time; - - td6->excitement = ride->ratings.excitement / 10; - td6->intensity = ride->ratings.intensity / 10; - td6->nausea = ride->ratings.nausea / 10; - - td6->upkeep_cost = ride->upkeep_cost; - td6->flags = 0; - td6->flags2 = 0; - - bool result; - if (td6->type == RIDE_TYPE_MAZE) - { - result = track_design_save_to_td6_for_maze(ride, td6); - } - else - { - result = track_design_save_to_td6_for_tracked_ride(ride, td6); - } - - if (!result) - { - track_design_dispose(td6); - td6 = nullptr; - } - return td6; -} - -/** - * - * rct2: 0x006CEAAE - */ -static bool track_design_save_to_td6_for_maze(const Ride* ride, rct_track_td6* td6) -{ - TileElement* tileElement = nullptr; - bool mapFound = false; - int16_t startX = 0; - int16_t startY = 0; - for (startY = 0; startY < 8192; startY += 32) - { - for (startX = 0; startX < 8192; startX += 32) - { - tileElement = map_get_first_element_at(startX >> 5, startY >> 5); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - if (tileElement->AsTrack()->GetRideIndex() == ride->id) - { - mapFound = true; - break; - } - } while (!(tileElement++)->IsLastForTile()); - if (mapFound) - { - break; - } - } - if (mapFound) - { - break; - } - } - - if (mapFound == 0) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return false; - } - - gTrackPreviewOrigin = { startX, startY, (int16_t)(tileElement->base_height * 8) }; - - size_t numMazeElements = 0; - td6->maze_elements = (rct_td6_maze_element*)calloc(8192, sizeof(rct_td6_maze_element)); - rct_td6_maze_element* maze = td6->maze_elements; - - // x is defined here as we can start the search - // on tile start_x, start_y but then the next row - // must restart on 0 - for (int16_t y = startY, x = startX; y < 8192; y += 32) - { - for (; x < 8192; x += 32) - { - tileElement = map_get_first_element_at(x / 32, y / 32); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - if (tileElement->AsTrack()->GetRideIndex() != ride->id) - continue; - - maze->maze_entry = tileElement->AsTrack()->GetMazeEntry(); - maze->x = (x - startX) / 32; - maze->y = (y - startY) / 32; - maze++; - numMazeElements++; - - if (numMazeElements >= 2000) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - SafeFree(td6->maze_elements); - return false; - } - } while (!(tileElement++)->IsLastForTile()); - } - x = 0; - } - - auto location = ride_get_entrance_location(ride, 0); - if (location.isNull()) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - SafeFree(td6->maze_elements); - return false; - } - - int16_t x = location.x * 32; - int16_t y = location.y * 32; - - tileElement = map_get_first_element_at(location.x, location.y); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) - continue; - if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE) - continue; - if (tileElement->AsEntrance()->GetRideIndex() == ride->id) - break; - } while (!(tileElement++)->IsLastForTile()); - // Add something that stops this from walking off the end - - uint8_t entrance_direction = tileElement->GetDirection(); - maze->direction = entrance_direction; - maze->type = 8; - maze->x = (int8_t)((x - startX) / 32); - maze->y = (int8_t)((y - startY) / 32); - maze++; - numMazeElements++; - - location = ride_get_exit_location(ride, 0); - if (location.isNull()) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - SafeFree(td6->maze_elements); - return false; - } - - x = location.x * 32; - y = location.y * 32; - tileElement = map_get_first_element_at(location.x, location.y); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) - continue; - if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT) - continue; - if (tileElement->AsEntrance()->GetRideIndex() == ride->id) - break; - } while (!(tileElement++)->IsLastForTile()); - // Add something that stops this from walking off the end - - uint8_t exit_direction = tileElement->GetDirection(); - maze->direction = exit_direction; - maze->type = 0x80; - maze->x = (int8_t)((x - startX) / 32); - maze->y = (int8_t)((y - startY) / 32); - maze++; - maze->all = 0; - maze++; - numMazeElements++; - - // Write end marker - maze->all = 0; - maze++; - numMazeElements++; - - // Trim memory - td6->maze_elements = (rct_td6_maze_element*)realloc(td6->maze_elements, numMazeElements * sizeof(rct_td6_maze_element)); - - // Save global vars as they are still used by scenery - int16_t startZ = gTrackPreviewOrigin.z; - place_virtual_track(td6, PTD_OPERATION_DRAW_OUTLINES, true, get_ride(0), 4096, 4096, 0); - gTrackPreviewOrigin = { startX, startY, startZ }; - - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; - - td6->space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; - td6->space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; - return true; -} - -/** - * - * rct2: 0x006CE68D - */ -static bool track_design_save_to_td6_for_tracked_ride(const Ride* ride, rct_track_td6* td6) -{ - CoordsXYE trackElement; - if (!ride_try_get_origin_element(ride, &trackElement)) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return false; - } - - ride_get_start_of_track(&trackElement); - - int32_t z = trackElement.element->base_height * 8; - uint8_t track_type = trackElement.element->AsTrack()->GetTrackType(); - uint8_t direction = trackElement.element->GetDirection(); - _trackSaveDirection = direction; - - if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - - const rct_track_coordinates* trackCoordinates = &TrackCoordinates[trackElement.element->AsTrack()->GetTrackType()]; - // Used in the following loop to know when we have - // completed all of the elements and are back at the - // start. - TileElement* initialMap = trackElement.element; - - int16_t start_x = trackElement.x; - int16_t start_y = trackElement.y; - int16_t start_z = z + trackCoordinates->z_begin; - gTrackPreviewOrigin = { start_x, start_y, start_z }; - - size_t numTrackElements = 0; - td6->track_elements = (rct_td6_track_element*)calloc(TRACK_TD6_MAX_ELEMENTS, sizeof(rct_td6_track_element)); - rct_td6_track_element* track = td6->track_elements; - do - { - track->type = trackElement.element->AsTrack()->GetTrackType(); - if (track->type == TRACK_ELEM_255) - { - track->type = TRACK_ELEM_255_ALIAS; - } - - uint8_t flags; - if (track_element_has_speed_setting(track->type)) - { - flags = trackElement.element->AsTrack()->GetBrakeBoosterSpeed() >> 1; - } - else - { - flags = trackElement.element->AsTrack()->GetSeatRotation(); - } - - if (trackElement.element->AsTrack()->HasChain()) - flags |= (1 << 7); - flags |= trackElement.element->AsTrack()->GetColourScheme() << 4; - if (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_HAS_ALTERNATIVE_TRACK_TYPE - && trackElement.element->AsTrack()->IsInverted()) - { - flags |= TRACK_ELEMENT_FLAG_INVERTED; - } - - track->flags = flags; - track++; - numTrackElements++; - - if (!track_block_get_next(&trackElement, &trackElement, nullptr, nullptr)) - { - break; - } - - z = trackElement.element->base_height * 8; - direction = trackElement.element->GetDirection(); - track_type = trackElement.element->AsTrack()->GetTrackType(); - - if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) - { - break; - } - - if (TRACK_TD6_MAX_ELEMENTS == numTrackElements) - { - free(td6->track_elements); - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return false; - } - } while (trackElement.element != initialMap); - - td6->track_elements = (rct_td6_track_element*)realloc( - td6->track_elements, numTrackElements * sizeof(rct_td6_track_element) + 1); - *((uint8_t*)&td6->track_elements[numTrackElements]) = 0xFF; - - size_t numEntranceElements = 0; - td6->entrance_elements = (rct_td6_entrance_element*)calloc(32, sizeof(rct_td6_entrance_element)); - rct_td6_entrance_element* entrance = td6->entrance_elements; - - // First entrances, second exits - for (int32_t i = 0; i < 2; i++) - { - for (int32_t station_index = 0; station_index < RCT12_MAX_STATIONS_PER_RIDE; station_index++) - { - z = ride->stations[station_index].Height; - - TileCoordsXYZD location; - if (i == 0) - { - location = ride_get_entrance_location(ride, station_index); - } - else - { - location = ride_get_exit_location(ride, station_index); - } - - if (location.isNull()) - { - continue; - } - - int16_t x = location.x * 32; - int16_t y = location.y * 32; - - TileElement* tile_element = map_get_first_element_at(x >> 5, y >> 5); - do - { - if (tile_element->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) - continue; - if (tile_element->base_height == z) - break; - } while (!(tile_element++)->IsLastForTile()); - // Add something that stops this from walking off the end - - uint8_t entrance_direction = tile_element->GetDirection(); - entrance_direction -= _trackSaveDirection; - entrance_direction &= TILE_ELEMENT_DIRECTION_MASK; - entrance->direction = entrance_direction; - - x -= gTrackPreviewOrigin.x; - y -= gTrackPreviewOrigin.y; - - // Rotate entrance coordinates backwards to the correct direction - rotate_map_coordinates(&x, &y, (0 - _trackSaveDirection) & 3); - entrance->x = x; - entrance->y = y; - - z *= 8; - z -= gTrackPreviewOrigin.z; - z /= 8; - - if (z > 127 || z < -126) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - - if (z == 0xFF) - { - z = 0x80; - } - - entrance->z = z; - - // If this is the exit version - if (i == 1) - { - entrance->direction |= (1 << 7); - } - entrance++; - numEntranceElements++; - } - } - td6->entrance_elements = (rct_td6_entrance_element*)realloc( - td6->entrance_elements, numEntranceElements * sizeof(rct_td6_entrance_element) + 1); - *((uint8_t*)&td6->entrance_elements[numEntranceElements]) = 0xFF; - - place_virtual_track(td6, PTD_OPERATION_DRAW_OUTLINES, true, get_ride(0), 4096, 4096, 0); - - // Resave global vars for scenery reasons. - gTrackPreviewOrigin = { start_x, start_y, start_z }; - - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; - - td6->space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; - td6->space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; - return true; -} - -static size_t track_design_get_maze_elements_count(rct_track_td6* td6) -{ - size_t count = 0; - rct_td6_maze_element* mazeElement = td6->maze_elements; - while (mazeElement->all != 0) - { - count++; - mazeElement++; - } - return count; -} - -static size_t track_design_get_track_elements_count(rct_track_td6* td6) -{ - size_t count = 0; - rct_td6_track_element* trackElement = td6->track_elements; - while (trackElement->type != 0xFF) - { - count++; - trackElement++; - } - return count; -} - -static size_t track_design_get_entrance_elements_count(rct_track_td6* td6) -{ - size_t count = 0; - rct_td6_entrance_element* entranceElement = td6->entrance_elements; - while (entranceElement->z != -1) - { - count++; - entranceElement++; - } - return count; -} - -static size_t track_design_get_scenery_elements_count(rct_track_td6* td6) -{ - size_t count = 0; - rct_td6_scenery_element* sceneryElement = td6->scenery_elements; - if (sceneryElement != nullptr) - { - while (sceneryElement->scenery_object.end_flag != 0xFF) - { - count++; - sceneryElement++; - } - } - return count; -} - -struct auto_buffer -{ - void* ptr; - size_t length; - size_t capacity; -}; - -static void auto_buffer_write(auto_buffer* buffer, const void* src, size_t len) -{ - size_t remainingSpace = buffer->capacity - buffer->length; - if (remainingSpace < len) - { - do - { - buffer->capacity = std::max(8, buffer->capacity * 2); - remainingSpace = buffer->capacity - buffer->length; - } while (remainingSpace < len); - - buffer->ptr = realloc(buffer->ptr, buffer->capacity); - } - std::memcpy((void*)((uintptr_t)buffer->ptr + buffer->length), src, len); - buffer->length += len; -} - -/** - * - * rct2: 0x006771DC but not really its branched from that - * quite far. - */ -bool track_design_save_to_file(const utf8* path) -{ - rct_track_td6* td6 = _trackDesign; - const rct_td6_maze_element EndMarkerForMaze = {}; - const uint8_t EndMarker = 0xFF; - - window_close_construction_windows(); - - // Create TD6 data buffer - auto_buffer td6Buffer = {}; - auto_buffer_write(&td6Buffer, td6, 0xA3); - if (td6->type == RIDE_TYPE_MAZE) - { - auto_buffer_write( - &td6Buffer, td6->maze_elements, track_design_get_maze_elements_count(td6) * sizeof(rct_td6_maze_element)); - auto_buffer_write(&td6Buffer, &EndMarkerForMaze, sizeof(EndMarkerForMaze)); - } - else - { - auto_buffer_write( - &td6Buffer, td6->track_elements, track_design_get_track_elements_count(td6) * sizeof(rct_td6_track_element)); - auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker)); - auto_buffer_write( - &td6Buffer, td6->entrance_elements, - track_design_get_entrance_elements_count(td6) * sizeof(rct_td6_entrance_element)); - auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker)); - } - auto_buffer_write( - &td6Buffer, td6->scenery_elements, track_design_get_scenery_elements_count(td6) * sizeof(rct_td6_scenery_element)); - auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker)); - - // Encode TD6 data - uint8_t* encodedData = (uint8_t*)malloc(0x8000); - assert(td6Buffer.ptr != nullptr); - size_t encodedDataLength = sawyercoding_encode_td6((uint8_t*)td6Buffer.ptr, encodedData, td6Buffer.length); - - // Save encoded TD6 data to file - bool result; - log_verbose("saving track %s", path); - result = writeentirefile(path, encodedData, encodedDataLength); - if (!result) - { - log_error("Failed to save %s", path); - } - - free(encodedData); - free(td6Buffer.ptr); - return result; -} diff --git a/src/openrct2/ride/TrackPaint.cpp b/src/openrct2/ride/TrackPaint.cpp index 183190ce72..572ca1af48 100644 --- a/src/openrct2/ride/TrackPaint.cpp +++ b/src/openrct2/ride/TrackPaint.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -324,8 +324,11 @@ static void track_paint_util_draw_station_impl( paint_session* session, ride_id_t rideIndex, uint8_t direction, uint16_t height, uint16_t coverHeight, const TileElement* tileElement, int32_t fenceOffsetA, int32_t fenceOffsetB) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + LocationXY16 position = session->MapPosition; - Ride* ride = get_ride(rideIndex); auto stationObj = ride_get_station_object(ride); const bool hasGreenLight = tileElement->AsTrack()->HasGreenLight(); @@ -529,8 +532,11 @@ void track_paint_util_draw_station_inverted( paint_session* session, ride_id_t rideIndex, uint8_t direction, int32_t height, const TileElement* tileElement, uint8_t stationVariant) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + LocationXY16 position = session->MapPosition; - Ride* ride = get_ride(rideIndex); auto stationObj = ride_get_station_object(ride); const bool hasGreenLight = tileElement->AsTrack()->HasGreenLight(); @@ -2157,14 +2163,14 @@ void track_paint_util_left_corkscrew_up_supports(paint_session* session, uint8_t void track_paint(paint_session* session, uint8_t direction, int32_t height, const TileElement* tileElement) { ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - if (ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(rideIndex); + if (ride == nullptr) { log_error("Attempted to paint invalid ride: %d", rideIndex); return; } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if ((!gTrackDesignSaveMode || rideIndex == gTrackDesignSaveRideIndex) && !(session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES)) diff --git a/src/openrct2/ride/TrackPaint.h b/src/openrct2/ride/TrackPaint.h index 5ae3d689dc..4bfba44f2a 100644 --- a/src/openrct2/ride/TrackPaint.h +++ b/src/openrct2/ride/TrackPaint.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/Vehicle.cpp b/src/openrct2/ride/Vehicle.cpp index 97aeb55a96..31fbe9028a 100644 --- a/src/openrct2/ride/Vehicle.cpp +++ b/src/openrct2/ride/Vehicle.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -81,12 +81,11 @@ static bool vehicle_update_motion_collision_detection( rct_vehicle* vehicle, int16_t x, int16_t y, int16_t z, uint16_t* otherVehicleIndex); static int32_t vehicle_get_sound_priority_factor(rct_vehicle* vehicle); static void vehicle_update_sound(rct_vehicle* vehicle); -static int32_t vehicle_update_scream_sound(rct_vehicle* vehicle); +static SoundId vehicle_update_scream_sound(rct_vehicle* vehicle); static void vehicle_kill_all_passengers(rct_vehicle* vehicle); static bool vehicle_can_depart_synchronised(rct_vehicle* vehicle); -#define NO_SCREAM 254 #define VEHICLE_INVALID_ID (-1) #define VEHICLE_MAX_SPIN_SPEED 1536 @@ -109,77 +108,77 @@ rct_vehicle* _vehicleFrontVehicle; LocationXYZ16 unk_F64E20; // clang-format off -static constexpr const uint8_t byte_9A3A14[] = { SOUND_SCREAM_8, SOUND_SCREAM_1 }; -static constexpr const uint8_t byte_9A3A16[] = { SOUND_SCREAM_1, SOUND_SCREAM_6 }; -static constexpr const uint8_t byte_9A3A18[] = { - SOUND_SCREAM_3, SOUND_SCREAM_1, SOUND_SCREAM_5, SOUND_SCREAM_6, - SOUND_SCREAM_7, SOUND_SCREAM_2, SOUND_SCREAM_4 +static constexpr const SoundId byte_9A3A14[] = { SoundId::Scream8, SoundId::Scream1 }; +static constexpr const SoundId byte_9A3A16[] = { SoundId::Scream1, SoundId::Scream6 }; +static constexpr const SoundId byte_9A3A18[] = { + SoundId::Scream3, SoundId::Scream1, SoundId::Scream5, SoundId::Scream6, + SoundId::Scream7, SoundId::Scream2, SoundId::Scream4 }; -static constexpr const uint8_t _soundParams[SOUND_MAXID][2] = +static constexpr const uint8_t _soundParams[RCT2SoundCount][2] = { - { 1, 0 }, // SOUND_LIFT_1 - { 1, 0 }, // SOUND_TRACK_FRICTION_1 - { 1, 0 }, // SOUND_LIFT_2 - { 0, 1 }, // SOUND_SCREAM_1 - { 0, 0 }, // SOUND_CLICK_1 - { 0, 0 }, // SOUND_CLICK_2 - { 0, 0 }, // SOUND_PLACE_ITEM - { 0, 1 }, // SOUND_SCREAM_2 - { 0, 1 }, // SOUND_SCREAM_3 - { 0, 1 }, // SOUND_SCREAM_4 - { 0, 1 }, // SOUND_SCREAM_5 - { 0, 1 }, // SOUND_SCREAM_6 - { 1, 0 }, // SOUND_LIFT_3 - { 0, 0 }, // SOUND_PURCHASE - { 0, 0 }, // SOUND_CRASH - { 0, 0 }, // SOUND_LAYING_OUT_WATER - { 0, 0 }, // SOUND_WATER_1 - { 0, 0 }, // SOUND_WATER_2 - { 0, 1 }, // SOUND_TRAIN_WHISTLE - { 0, 1 }, // SOUND_TRAIN_CHUGGING - { 0, 0 }, // SOUND_WATER_SPLASH - { 1, 0 }, // SOUND_HAMMERING - { 0, 0 }, // SOUND_RIDE_LAUNCH_1 - { 0, 0 }, // SOUND_RIDE_LAUNCH_2 - { 0, 0 }, // SOUND_COUGH_1 - { 0, 0 }, // SOUND_COUGH_2 - { 0, 0 }, // SOUND_COUGH_3 - { 0, 0 }, // SOUND_COUGH_4 - { 1, 0 }, // SOUND_RAIN_1 - { 0, 0 }, // SOUND_THUNDER_1 - { 0, 0 }, // SOUND_THUNDER_2 - { 1, 0 }, // SOUND_RAIN_2 - { 1, 0 }, // SOUND_RAIN_3 - { 0, 0 }, // SOUND_BALLOON_POP - { 0, 0 }, // SOUND_MECHANIC_FIX - { 0, 1 }, // SOUND_SCREAM_7 - { 0, 0 }, // SOUND_TOILET_FLUSH - { 0, 0 }, // SOUND_CLICK_3 - { 0, 0 }, // SOUND_QUACK - { 0, 0 }, // SOUND_NEWS_ITEM - { 0, 0 }, // SOUND_WINDOW_OPEN - { 0, 0 }, // SOUND_LAUGH_1 - { 0, 0 }, // SOUND_LAUGH_2 - { 0, 0 }, // SOUND_LAUGH_3 - { 0, 0 }, // SOUND_APPLAUSE - { 0, 0 }, // SOUND_HAUNTED_HOUSE_SCARE - { 0, 0 }, // SOUND_HAUNTED_HOUSE_SCREAM_1 - { 0, 0 }, // SOUND_HAUNTED_HOUSE_SCREAM_2 - { 0, 0 }, // SOUND_48 - { 0, 0 }, // SOUND_49 - { 0, 0 }, // SOUND_ERROR - { 0, 0 }, // SOUND_51 - { 1, 0 }, // SOUND_LIFT_4 - { 1, 0 }, // SOUND_LIFT_5 - { 1, 0 }, // SOUND_TRACK_FRICTION_2 - { 1, 0 }, // SOUND_LIFT_6 - { 1, 0 }, // SOUND_LIFT_7 - { 1, 2 }, // SOUND_TRACK_FRICTION_3 - { 0, 1 }, // SOUND_SCREAM_8 - { 0, 1 }, // SOUND_TRAM - { 0, 0 }, // SOUND_DOOR_OPEN - { 0, 0 }, // SOUND_DOOR_CLOSE - { 0, 0 } // SOUND_62 + { 1, 0 }, // LiftClassic + { 1, 0 }, // TrackFrictionClassicWood + { 1, 0 }, // FrictionClassic + { 0, 1 }, // Scream1 + { 0, 0 }, // Click1 + { 0, 0 }, // Click2 + { 0, 0 }, // PlaceItem + { 0, 1 }, // Scream2 + { 0, 1 }, // Scream3 + { 0, 1 }, // Scream4 + { 0, 1 }, // Scream5 + { 0, 1 }, // Scream6 + { 1, 0 }, // LiftFrictionWheels + { 0, 0 }, // Purchase + { 0, 0 }, // Crash + { 0, 0 }, // LayingOutWater + { 0, 0 }, // Water1 + { 0, 0 }, // Water2 + { 0, 1 }, // TrainWhistle + { 0, 1 }, // TrainDeparting + { 0, 0 }, // WaterSplash + { 1, 0 }, // GoKartEngine + { 0, 0 }, // RideLaunch1 + { 0, 0 }, // RideLaunch2 + { 0, 0 }, // Cough1 + { 0, 0 }, // Cough2 + { 0, 0 }, // Cough3 + { 0, 0 }, // Cough4 + { 1, 0 }, // Rain + { 0, 0 }, // Thunder1 + { 0, 0 }, // Thunder2 + { 1, 0 }, // TrackFrictionTrain + { 1, 0 }, // TrackFrictionWater + { 0, 0 }, // BalloonPop + { 0, 0 }, // MechanicFix + { 0, 1 }, // Scream7 + { 0, 0 }, // ToiletFlush + { 0, 0 }, // Click3 + { 0, 0 }, // Quack + { 0, 0 }, // NewsItem + { 0, 0 }, // WindowOpen + { 0, 0 }, // Laugh1 + { 0, 0 }, // Laugh2 + { 0, 0 }, // Laugh3 + { 0, 0 }, // Applause + { 0, 0 }, // HauntedHouseScare + { 0, 0 }, // HauntedHouseScream1 + { 0, 0 }, // HauntedHouseScream2 + { 0, 0 }, // BlockBrakeClose + { 0, 0 }, // BlockBrakeRelease + { 0, 0 }, // Error + { 0, 0 }, // BrakeRelease + { 1, 0 }, // LiftArrow + { 1, 0 }, // LiftWood + { 1, 0 }, // TrackFrictionWood + { 1, 0 }, // LiftWildMouse + { 1, 0 }, // LiftBM + { 1, 2 }, // TrackFrictionBM + { 0, 1 }, // Scream8 + { 0, 1 }, // Tram + { 0, 0 }, // DoorOpen + { 0, 0 }, // DoorClose + { 0, 0 } // Portcullis }; static constexpr const uint8_t SpaceRingsTimeToSpriteMap[] = @@ -691,16 +690,16 @@ static constexpr const LocationXY16 AvoidCollisionMoveOffset[] = }; -static constexpr const uint8_t DoorOpenSoundIds[] = +static constexpr const SoundId DoorOpenSoundIds[] = { - SOUND_DOOR_OPEN, - SOUND_62 + SoundId::DoorOpen, + SoundId::Portcullis }; -static constexpr const uint8_t DoorCloseSoundIds[] = +static constexpr const SoundId DoorCloseSoundIds[] = { - SOUND_DOOR_CLOSE, - SOUND_62 + SoundId::DoorClose, + SoundId::Portcullis }; static const struct @@ -839,9 +838,9 @@ rct_vehicle* try_get_vehicle(uint16_t spriteIndex) return &sprite->vehicle; } -static void vehicle_invalidate(rct_vehicle* vehicle) +void rct_vehicle::Invalidate() { - invalidate_sprite_2((rct_sprite*)vehicle); + invalidate_sprite_2((rct_sprite*)this); } static int32_t get_train_mass(rct_vehicle* first_vehicle) @@ -873,7 +872,7 @@ static void vehicle_update_sound_params(rct_vehicle* vehicle) if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) && gS6Info.editor_step != EDITOR_STEP_ROLLERCOASTER_DESIGNER) return; - if (vehicle->sound1_id == RCT12_SOUND_ID_NULL && vehicle->sound2_id == RCT12_SOUND_ID_NULL) + if (vehicle->sound1_id == SoundId::Null && vehicle->sound2_id == SoundId::Null) return; if (vehicle->sprite_left == LOCATION_NULL) @@ -975,10 +974,10 @@ static void vehicle_update_sound_params(rct_vehicle* vehicle) if (vehicle->x != LOCATION_NULL) { - TileElement* tile_element = map_get_surface_element_at({ vehicle->x, vehicle->y }); + auto surfaceElement = map_get_surface_element_at({ vehicle->x, vehicle->y }); // vehicle underground - if (tile_element != nullptr && tile_element->base_height * 8 > vehicle->z) + if (surfaceElement != nullptr && surfaceElement->base_height * 8 > vehicle->z) { soundParam->volume = 0x30; } @@ -1101,8 +1100,8 @@ static rct_vehicle_sound* vehicle_sounds_update_get_vehicle_sound(rct_vehicle_so if (vehicleSound->id == SOUND_ID_NULL) { vehicleSound->id = sound_params->id; - vehicleSound->sound1_id = SOUND_ID_NULL; - vehicleSound->sound2_id = SOUND_ID_NULL; + vehicleSound->sound1_id = SoundId::Null; + vehicleSound->sound2_id = SoundId::Null; vehicleSound->volume = 0x30; return vehicleSound; } @@ -1120,33 +1119,33 @@ static void vehicle_sounds_update_sound_1( volume = volume / 8; volume = std::max(volume - 0x1FFF, -10000); - if (vehicle->sound1_id == RCT12_SOUND_ID_NULL) + if (vehicle->sound1_id == SoundId::Null) { - if (sound->sound1_id != SOUND_ID_NULL) + if (sound->sound1_id != SoundId::Null) { - sound->sound1_id = SOUND_ID_NULL; + sound->sound1_id = SoundId::Null; Mixer_Stop_Channel(sound->sound1_channel); } return; } - if (sound->sound1_id != SOUND_ID_NULL && vehicle->sound1_id != sound->sound1_id) + if (sound->sound1_id != SoundId::Null && vehicle->sound1_id != sound->sound1_id) { Mixer_Stop_Channel(sound->sound1_channel); } - if ((sound->sound1_id == SOUND_ID_NULL) || (vehicle->sound1_id != sound->sound1_id)) + if ((sound->sound1_id == SoundId::Null) || (vehicle->sound1_id != sound->sound1_id)) { sound->sound1_id = vehicle->sound1_id; sound->sound1_pan = sound_params->pan_x; sound->sound1_volume = volume; sound->sound1_freq = sound_params->frequency; uint16_t frequency = sound_params->frequency; - if (_soundParams[vehicle->sound1_id][1] & 2) + if (_soundParams[static_cast(vehicle->sound1_id)][1] & 2) { frequency = (frequency / 2) + 4000; } - uint8_t looping = _soundParams[vehicle->sound1_id][0]; + uint8_t looping = _soundParams[static_cast(vehicle->sound1_id)][0]; int32_t pan = sound_params->pan_x; sound->sound1_channel = Mixer_Play_Effect( vehicle->sound1_id, looping ? MIXER_LOOP_INFINITE : MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(pan), @@ -1167,7 +1166,7 @@ static void vehicle_sounds_update_sound_1( { sound->sound1_freq = sound_params->frequency; uint16_t frequency = sound_params->frequency; - if (_soundParams[vehicle->sound1_id][1] & 2) + if (_soundParams[static_cast(vehicle->sound1_id)][1] & 2) { frequency = (frequency / 2) + 4000; } @@ -1184,35 +1183,35 @@ static void vehicle_sounds_update_sound_2( volume = volume / 8; volume = std::max(volume - 0x1FFF, -10000); - if (vehicle->sound2_id == RCT12_SOUND_ID_NULL) + if (vehicle->sound2_id == SoundId::Null) { - if (sound->sound2_id != SOUND_ID_NULL) + if (sound->sound2_id != SoundId::Null) { - sound->sound2_id = SOUND_ID_NULL; + sound->sound2_id = SoundId::Null; Mixer_Stop_Channel(sound->sound2_channel); } return; } - if (sound->sound2_id != SOUND_ID_NULL && vehicle->sound2_id != sound->sound2_id) + if (sound->sound2_id != SoundId::Null && vehicle->sound2_id != sound->sound2_id) { Mixer_Stop_Channel(sound->sound2_channel); } - if ((sound->sound2_id == SOUND_ID_NULL) || (vehicle->sound2_id != sound->sound2_id)) + if ((sound->sound2_id == SoundId::Null) || (vehicle->sound2_id != sound->sound2_id)) { sound->sound2_id = vehicle->sound2_id; sound->sound2_pan = sound_params->pan_x; sound->sound2_volume = volume; sound->sound2_freq = sound_params->frequency; uint16_t frequency = sound_params->frequency; - if (_soundParams[vehicle->sound2_id][1] & 1) + if (_soundParams[static_cast(vehicle->sound2_id)][1] & 1) { frequency = 12649; } frequency = std::min((frequency * 2) - 3248, 25700); - uint8_t looping = _soundParams[vehicle->sound2_id][0]; + uint8_t looping = _soundParams[static_cast(vehicle->sound2_id)][0]; int32_t pan = sound_params->pan_x; sound->sound2_channel = Mixer_Play_Effect( vehicle->sound2_id, looping ? MIXER_LOOP_INFINITE : MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(pan), @@ -1232,7 +1231,7 @@ static void vehicle_sounds_update_sound_2( if (!(gCurrentTicks & 3) && sound_params->frequency != sound->sound2_freq) { sound->sound2_freq = sound_params->frequency; - if (!(_soundParams[vehicle->sound2_id][1] & 1)) + if (!(_soundParams[static_cast(vehicle->sound2_id)][1] & 1)) { uint16_t frequency = (sound_params->frequency * 2) - 3248; if (frequency > 25700) @@ -1256,7 +1255,7 @@ void vehicle_sounds_update() vehicle_sounds_update_window_setup(); gVehicleSoundParamsListEnd = &gVehicleSoundParamsList[0]; - for (uint16_t i = gSpriteListHead[SPRITE_LIST_TRAIN]; i != SPRITE_INDEX_NULL; i = get_sprite(i)->vehicle.next) + for (uint16_t i = gSpriteListHead[SPRITE_LIST_VEHICLE_HEAD]; i != SPRITE_INDEX_NULL; i = get_sprite(i)->vehicle.next) { vehicle_update_sound_params(&get_sprite(i)->vehicle); } @@ -1280,11 +1279,11 @@ void vehicle_sounds_update() if (keepPlaying) continue; - if (vehicle_sound.sound1_id != SOUND_ID_NULL) + if (vehicle_sound.sound1_id != SoundId::Null) { Mixer_Stop_Channel(vehicle_sound.sound1_channel); } - if (vehicle_sound.sound2_id != SOUND_ID_NULL) + if (vehicle_sound.sound2_id != SoundId::Null) { Mixer_Stop_Channel(vehicle_sound.sound2_channel); } @@ -1339,7 +1338,7 @@ void vehicle_update_all() if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) && gS6Info.editor_step != EDITOR_STEP_ROLLERCOASTER_DESIGNER) return; - sprite_index = gSpriteListHead[SPRITE_LIST_TRAIN]; + sprite_index = gSpriteListHead[SPRITE_LIST_VEHICLE_HEAD]; while (sprite_index != SPRITE_INDEX_NULL) { vehicle = GET_VEHICLE(sprite_index); @@ -1356,7 +1355,10 @@ void vehicle_update_all() */ static bool vehicle_close_restraints(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return true; + bool restraintsClosed = true; uint16_t vehicle_id = vehicle->sprite_index; @@ -1392,7 +1394,7 @@ static bool vehicle_close_restraints(rct_vehicle* vehicle) continue; } } - vehicle_invalidate(vehicle); + vehicle->Invalidate(); restraintsClosed = false; } while ((vehicle_id = vehicle->next_vehicle_on_train) != SPRITE_INDEX_NULL); @@ -1417,8 +1419,11 @@ static bool vehicle_open_restraints(rct_vehicle* vehicle) vehicle->var_4E = 0; vehicle->swing_sprite = 0; - Ride* ride = get_ride(vehicle->ride); - rct_ride_entry* rideEntry = get_ride_entry(vehicle->ride_subtype); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + continue; + + auto rideEntry = get_ride_entry(vehicle->ride_subtype); if (rideEntry == nullptr) { continue; @@ -1449,7 +1454,7 @@ static bool vehicle_open_restraints(rct_vehicle* vehicle) vehicle->spin_sprite += value; vehicle->spin_speed -= value; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); continue; } } @@ -1460,7 +1465,7 @@ static bool vehicle_open_restraints(rct_vehicle* vehicle) vehicle->var_C8 = vehicle->var_C8 + 0x3333 - 0xFFFF; vehicle->animation_frame++; vehicle->animation_frame &= 7; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } else { @@ -1500,7 +1505,7 @@ static bool vehicle_open_restraints(rct_vehicle* vehicle) } vehicle->restraints_position += 20; } - vehicle_invalidate(vehicle); + vehicle->Invalidate(); restraintsOpen = false; } while ((vehicle_id = vehicle->next_vehicle_on_train) != SPRITE_INDEX_NULL); @@ -1513,9 +1518,9 @@ static bool vehicle_open_restraints(rct_vehicle* vehicle) */ static void vehicle_update_measurements(rct_vehicle* vehicle) { - Ride* ride; - - ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; if (vehicle->status == VEHICLE_STATUS_TRAVELLING_BOAT) { @@ -1557,32 +1562,27 @@ static void vehicle_update_measurements(rct_vehicle* vehicle) if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) { - int32_t vertical_g, lateral_g; - vehicle_get_g_forces(vehicle, &vertical_g, &lateral_g); + auto gForces = vehicle_get_g_forces(vehicle); + gForces.VerticalG += ride->previous_vertical_g; + gForces.LateralG += ride->previous_lateral_g; + gForces.VerticalG /= 2; + gForces.LateralG /= 2; - vertical_g += ride->previous_vertical_g; - lateral_g += ride->previous_lateral_g; - vertical_g >>= 1; - lateral_g >>= 1; - - ride->previous_vertical_g = vertical_g; - ride->previous_lateral_g = lateral_g; - - if (vertical_g <= 0) + ride->previous_vertical_g = gForces.VerticalG; + ride->previous_lateral_g = gForces.LateralG; + if (gForces.VerticalG <= 0) { ride->total_air_time++; } - if (vertical_g > ride->max_positive_vertical_g) - ride->max_positive_vertical_g = vertical_g; + if (gForces.VerticalG > ride->max_positive_vertical_g) + ride->max_positive_vertical_g = gForces.VerticalG; - if (vertical_g < ride->max_negative_vertical_g) - ride->max_negative_vertical_g = vertical_g; + if (gForces.VerticalG < ride->max_negative_vertical_g) + ride->max_negative_vertical_g = gForces.VerticalG; - lateral_g = abs(lateral_g); - - if (lateral_g > ride->max_lateral_g) - ride->max_lateral_g = lateral_g; + gForces.LateralG = std::abs(gForces.LateralG); + ride->max_lateral_g = std::max(ride->max_lateral_g, (fixed16_2dp)gForces.LateralG); } } @@ -1781,20 +1781,27 @@ static void vehicle_update_measurements(rct_vehicle* vehicle) ride->start_drop_height = vehicle->z / 8; } - if (track_flags & TRACK_ELEM_FLAG_NORMAL_TO_INVERSION) + if (ride->type == RIDE_TYPE_MINI_GOLF) { - uint8_t inversions = ride->inversions & 0x1F; - if (inversions != 0x1F) - inversions++; - - ride->inversions &= ~0x1F; - ride->inversions |= inversions; + if (track_flags & TRACK_ELEM_FLAG_IS_GOLF_HOLE) + { + if (ride->holes < MAX_GOLF_HOLES) + ride->holes++; + } + } + else + { + if (track_flags & TRACK_ELEM_FLAG_NORMAL_TO_INVERSION) + { + if (ride->inversions < MAX_INVERSIONS) + ride->inversions++; + } } if (track_flags & TRACK_ELEM_FLAG_HELIX) { uint8_t helixes = ride_get_helix_sections(ride); - if (helixes != 0x1F) + if (helixes != MAX_HELICES) helixes++; ride->special_track_elements &= ~0x1F; @@ -1816,13 +1823,15 @@ static void vehicle_update_measurements(rct_vehicle* vehicle) return; } - TileElement* tile_element = map_get_surface_element_at({ x, y }); + auto surfaceElement = map_get_surface_element_at({ x, y }); // If vehicle above ground. - if (tile_element != nullptr && tile_element->base_height * 8 <= z) + if (surfaceElement != nullptr && surfaceElement->base_height * 8 <= z) { // Set tile_element to first element. Since elements aren't always ordered by base height, // we must start at the first element and iterate through each tile element. - tile_element = map_get_first_element_at(x / 32, y / 32); + auto tile_element = map_get_first_element_at(x / 32, y / 32); + if (tile_element == nullptr) + return; bool cover_found = false; do @@ -1855,7 +1864,7 @@ static void vehicle_update_measurements(rct_vehicle* vehicle) // Iterate through each tile_element. } while (!(tile_element++)->IsLastForTile()); - if (cover_found == false) + if (!cover_found) { ride->testing_flags &= ~RIDE_TESTING_SHELTERED; return; @@ -1890,20 +1899,26 @@ static void vehicle_update_measurements(rct_vehicle* vehicle) ride->sheltered_length = add_clamp_int32_t(ride->sheltered_length, distance); } -static uint16_t sub_6D7AC0(int32_t currentSoundId, int32_t currentVolume, int32_t targetSoundId, int32_t targetVolume) +struct SoundIdVolume { - if (currentSoundId != 255) + SoundId id; + uint8_t volume; +}; + +static SoundIdVolume sub_6D7AC0(SoundId currentSoundId, uint8_t currentVolume, SoundId targetSoundId, uint8_t targetVolume) +{ + if (currentSoundId != SoundId::Null) { if (currentSoundId == targetSoundId) { - currentVolume = std::min(currentVolume + 15, targetVolume); - return (currentVolume << 8) | currentSoundId; + currentVolume = std::min(currentVolume + 15, targetVolume); + return { currentSoundId, currentVolume }; } else { currentVolume -= 9; if (currentVolume >= 80) - return (currentVolume << 8) | currentSoundId; + return { currentSoundId, currentVolume }; } } @@ -1911,7 +1926,7 @@ static uint16_t sub_6D7AC0(int32_t currentSoundId, int32_t currentVolume, int32_ currentSoundId = targetSoundId; currentVolume = targetVolume == 255 ? 255 : targetVolume / 4; - return (currentVolume << 8) | currentSoundId; + return { currentSoundId, currentVolume }; } /** @@ -1920,9 +1935,6 @@ static uint16_t sub_6D7AC0(int32_t currentSoundId, int32_t currentVolume, int32_ */ static void vehicle_update(rct_vehicle* vehicle) { - Ride* ride; - rct_ride_entry* rideEntry; - // The cable lift uses the ride type of NULL if (vehicle->ride_subtype == RIDE_TYPE_NULL) { @@ -1930,11 +1942,14 @@ static void vehicle_update(rct_vehicle* vehicle) return; } - rideEntry = get_ride_entry(vehicle->ride_subtype); + auto rideEntry = get_ride_entry(vehicle->ride_subtype); + if (rideEntry == nullptr) + return; - rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; - ride = get_ride(vehicle->ride); if (vehicle->update_flags & VEHICLE_UPDATE_FLAG_TESTING) vehicle_update_measurements(vehicle); @@ -1942,6 +1957,7 @@ static void vehicle_update(rct_vehicle* vehicle) if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN)) { _vehicleBreakdown = ride->breakdown_reason_pending; + auto vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; if ((vehicleEntry->flags & VEHICLE_ENTRY_FLAG_POWERED) && ride->breakdown_reason_pending == BREAKDOWN_SAFETY_CUT_OUT) { if (!(vehicleEntry->flags & VEHICLE_ENTRY_FLAG_WATER_RIDE) @@ -2020,6 +2036,8 @@ static void vehicle_update(rct_vehicle* vehicle) break; case VEHICLE_STATUS_DOING_CIRCUS_SHOW: vehicle_update_doing_circus_show(vehicle); + default: + break; } vehicle_update_sound(vehicle); @@ -2031,7 +2049,10 @@ static void vehicle_update(rct_vehicle* vehicle) */ static void vehicle_update_moving_to_end_of_station(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + int32_t flags, station; switch (ride->mode) @@ -2073,9 +2094,7 @@ static void vehicle_update_moving_to_end_of_station(rct_vehicle* vehicle) vehicle->current_station = 0; vehicle->velocity = 0; vehicle->acceleration = 0; - vehicle->status = VEHICLE_STATUS_WAITING_FOR_PASSENGERS; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_WAITING_FOR_PASSENGERS); break; default: { @@ -2110,9 +2129,7 @@ static void vehicle_update_moving_to_end_of_station(rct_vehicle* vehicle) if (ride->mode == RIDE_MODE_RACE && vehicle->sub_state >= 40) { - vehicle->status = VEHICLE_STATUS_WAITING_FOR_PASSENGERS; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_WAITING_FOR_PASSENGERS); break; } } @@ -2130,9 +2147,7 @@ static void vehicle_update_moving_to_end_of_station(rct_vehicle* vehicle) vehicle->current_station = station; vehicle->velocity = 0; vehicle->acceleration = 0; - vehicle->status = VEHICLE_STATUS_WAITING_FOR_PASSENGERS; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_WAITING_FOR_PASSENGERS); break; } } @@ -2147,7 +2162,9 @@ static void train_ready_to_depart(rct_vehicle* vehicle, uint8_t num_peeps_on_tra if (num_peeps_on_train != num_used_seats) return; - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; if (ride->status == RIDE_STATUS_OPEN && !(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) && !(vehicle->update_flags & VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART)) @@ -2173,9 +2190,7 @@ static void train_ready_to_depart(rct_vehicle* vehicle, uint8_t num_peeps_on_tra if (vehicle->peep[peep] != SPRITE_INDEX_NULL) { ride->stations[vehicle->current_station].TrainAtStation = RideStation::NO_TRAIN; - vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_UNLOADING_PASSENGERS); return; } @@ -2191,9 +2206,7 @@ static void train_ready_to_depart(rct_vehicle* vehicle, uint8_t num_peeps_on_tra return; ride->stations[vehicle->current_station].TrainAtStation = RideStation::NO_TRAIN; - vehicle->status = VEHICLE_STATUS_WAITING_FOR_PASSENGERS; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_WAITING_FOR_PASSENGERS); } static int ride_get_train_index_from_vehicle(Ride* ride, uint16_t spriteIndex) @@ -2225,7 +2238,9 @@ static void vehicle_update_waiting_for_passengers(rct_vehicle* vehicle) { vehicle->velocity = 0; - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; if (vehicle->sub_state == 0) { @@ -2252,7 +2267,7 @@ static void vehicle_update_waiting_for_passengers(rct_vehicle* vehicle) vehicle->sub_state = 1; vehicle->time_waiting = 0; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); return; } else if (vehicle->sub_state == 1) @@ -2278,7 +2293,7 @@ static void vehicle_update_waiting_for_passengers(rct_vehicle* vehicle) num_seats_on_train &= 0x7F; - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE)) + if (ride->SupportsStatus(RIDE_STATUS_TESTING)) { if (vehicle->time_waiting < 20) { @@ -2350,61 +2365,24 @@ static void vehicle_update_waiting_for_passengers(rct_vehicle* vehicle) return; } + // any load: load=4 , full: load=3 , 3/4s: load=2 , half: load=1 , quarter: load=0 uint8_t load = ride->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK; - if (load == 3) - { - train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); - return; - } - uint8_t three_quater_seats = (3 * num_seats_on_train) / 4; - if (three_quater_seats != 0 && num_peeps_on_train >= three_quater_seats) - { + // We want to wait for ceiling((load+1)/4 * num_seats_on_train) peeps, the +3 below is used instead of + // ceil() to prevent issues on different cpus/platforms with floats. Note that vanilla RCT1/2 rounded + // down here; our change reflects the expected behaviour for waiting for a minimum load target (see #9987) + uint8_t peepTarget = ((load + 1) * num_seats_on_train + 3) / 4; + + if (load == 4) // take care of "any load" special case + peepTarget = 1; + + if (num_peeps_on_train >= peepTarget) vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; - train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); - return; - } - if (load == 2) - { - train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); - return; - } - - if (num_seats_on_train / 2 != 0 && num_peeps_on_train >= num_seats_on_train / 2) - { - vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; - train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); - return; - } - - if (load == 1) - { - train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); - return; - } - - if (num_seats_on_train / 4 != 0 && num_peeps_on_train >= num_seats_on_train / 4) - { - vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; - train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); - return; - } - - if (load == 0) - { - vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; - train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); - return; - } - - if (num_peeps_on_train != 0) - { - vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; - } train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); return; } + vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART; train_ready_to_depart(vehicle, num_peeps_on_train, num_used_seats_on_train); return; @@ -2414,8 +2392,6 @@ static void vehicle_update_waiting_for_passengers(rct_vehicle* vehicle) return; vehicle->velocity = 0; - vehicle->status = VEHICLE_STATUS_WAITING_TO_DEPART; - vehicle->sub_state = 0; vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT; if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS) @@ -2423,7 +2399,7 @@ static void vehicle_update_waiting_for_passengers(rct_vehicle* vehicle) vehicle->update_flags |= VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT; } - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_WAITING_TO_DEPART); } /** @@ -2432,7 +2408,10 @@ static void vehicle_update_waiting_for_passengers(rct_vehicle* vehicle) */ static void vehicle_update_dodgems_mode(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + rct_ride_entry* rideEntry = get_ride_entry(vehicle->ride_subtype); if (rideEntry == nullptr) { @@ -2444,7 +2423,7 @@ static void vehicle_update_dodgems_mode(rct_vehicle* vehicle) if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_DODGEM_INUSE_LIGHTS && vehicle->animation_frame != 1) { vehicle->animation_frame = 1; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } vehicle_update_motion_dodgems(vehicle); @@ -2460,12 +2439,10 @@ static void vehicle_update_dodgems_mode(rct_vehicle* vehicle) // Mark the dodgem as not in use. vehicle->animation_frame = 0; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); vehicle->velocity = 0; vehicle->acceleration = 0; - vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_UNLOADING_PASSENGERS); } /** @@ -2474,7 +2451,10 @@ static void vehicle_update_dodgems_mode(rct_vehicle* vehicle) */ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + bool shouldBreak = false; if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) { @@ -2492,7 +2472,7 @@ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) } bool skipCheck = false; - if (shouldBreak == true || ride->status != RIDE_STATUS_OPEN) + if (shouldBreak || ride->status != RIDE_STATUS_OPEN) { if (ride->mode == RIDE_MODE_FORWARD_ROTATION || ride->mode == RIDE_MODE_BACKWARD_ROTATION) { @@ -2508,9 +2488,7 @@ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) { if (!ride_get_exit_location(ride, vehicle->current_station).isNull()) { - vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_UNLOADING_PASSENGERS); return; } } @@ -2526,9 +2504,7 @@ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) { if (!ride_get_exit_location(ride, vehicle->current_station).isNull()) { - vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_UNLOADING_PASSENGERS); return; } break; @@ -2537,7 +2513,7 @@ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) } } - if (skipCheck == false) + if (!skipCheck) { if (!(ride->stations[vehicle->current_station].Depart & STATION_DEPART_FLAG)) return; @@ -2557,8 +2533,7 @@ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) } } - vehicle->status = VEHICLE_STATUS_DEPARTING; - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_DEPARTING); if (ride->lifecycle_flags & RIDE_LIFECYCLE_CABLE_LIFT) { @@ -2572,7 +2547,7 @@ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) { if (track.element->AsTrack()->HasCableLift()) { - vehicle->status = VEHICLE_STATUS_WAITING_FOR_CABLE_LIFT; + vehicle->SetState(VEHICLE_STATUS_WAITING_FOR_CABLE_LIFT, vehicle->sub_state); } } } @@ -2580,45 +2555,38 @@ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) switch (ride->mode) { case RIDE_MODE_BUMPERCAR: - vehicle->status = VEHICLE_STATUS_TRAVELLING_DODGEMS; - vehicle_invalidate_window(vehicle); // Bumper mode uses sub_state / var_CE to tell how long // the vehicle has been ridden. - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_TRAVELLING_DODGEMS); vehicle->var_CE = 0; vehicle_update_dodgems_mode(vehicle); break; case RIDE_MODE_SWING: - vehicle->status = VEHICLE_STATUS_SWINGING; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_SWINGING); vehicle->var_CE = 0; vehicle->current_time = -1; vehicle_update_swinging(vehicle); break; case RIDE_MODE_ROTATION: - vehicle->status = VEHICLE_STATUS_ROTATING; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_ROTATING); vehicle->var_CE = 0; vehicle->current_time = -1; vehicle_update_rotating(vehicle); break; case RIDE_MODE_FILM_AVENGING_AVIATORS: + vehicle->SetState(VEHICLE_STATUS_SIMULATOR_OPERATING); + vehicle->current_time = -1; + vehicle_update_simulator_operating(vehicle); + break; case RIDE_MODE_FILM_THRILL_RIDERS: - vehicle->status = VEHICLE_STATUS_SIMULATOR_OPERATING; - vehicle->sub_state = 0; - if (ride->mode == RIDE_MODE_FILM_THRILL_RIDERS) - vehicle->sub_state = 1; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_SIMULATOR_OPERATING, 1); vehicle->current_time = -1; vehicle_update_simulator_operating(vehicle); break; case RIDE_MODE_BEGINNERS: case RIDE_MODE_INTENSE: case RIDE_MODE_BERSERK: - vehicle->status = VEHICLE_STATUS_TOP_SPIN_OPERATING; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_TOP_SPIN_OPERATING, vehicle->sub_state); switch (ride->mode) { @@ -2639,9 +2607,7 @@ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) break; case RIDE_MODE_FORWARD_ROTATION: case RIDE_MODE_BACKWARD_ROTATION: - vehicle->status = VEHICLE_STATUS_FERRIS_WHEEL_ROTATING; - vehicle->sub_state = vehicle->vehicle_sprite_type; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_FERRIS_WHEEL_ROTATING, vehicle->vehicle_sprite_type); vehicle->var_CE = 0; vehicle->ferris_wheel_var_0 = 8; vehicle->ferris_wheel_var_1 = 8; @@ -2650,8 +2616,7 @@ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) case RIDE_MODE_3D_FILM_MOUSE_TAILS: case RIDE_MODE_3D_FILM_STORM_CHASERS: case RIDE_MODE_3D_FILM_SPACE_RAIDERS: - vehicle->status = VEHICLE_STATUS_SHOWING_FILM; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_SHOWING_FILM, vehicle->sub_state); switch (ride->mode) { case RIDE_MODE_3D_FILM_MOUSE_TAILS: @@ -2668,39 +2633,30 @@ static void vehicle_update_waiting_to_depart(rct_vehicle* vehicle) vehicle_update_showing_film(vehicle); break; case RIDE_MODE_CIRCUS_SHOW: - vehicle->status = VEHICLE_STATUS_DOING_CIRCUS_SHOW; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_DOING_CIRCUS_SHOW); vehicle->current_time = -1; vehicle_update_doing_circus_show(vehicle); break; case RIDE_MODE_SPACE_RINGS: - vehicle->status = VEHICLE_STATUS_SPACE_RINGS_OPERATING; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_SPACE_RINGS_OPERATING); vehicle->vehicle_sprite_type = 0; vehicle->current_time = -1; vehicle_update_space_rings_operating(vehicle); break; case RIDE_MODE_HAUNTED_HOUSE: - vehicle->status = VEHICLE_STATUS_HAUNTED_HOUSE_OPERATING; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_HAUNTED_HOUSE_OPERATING); vehicle->vehicle_sprite_type = 0; vehicle->current_time = -1; vehicle_update_haunted_house_operating(vehicle); break; case RIDE_MODE_CROOKED_HOUSE: - vehicle->status = VEHICLE_STATUS_CROOKED_HOUSE_OPERATING; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_CROOKED_HOUSE_OPERATING); vehicle->vehicle_sprite_type = 0; vehicle->current_time = -1; vehicle_update_crooked_house_operating(vehicle); break; default: - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(vehicle->status); vehicle->var_CE = 0; break; } @@ -2744,9 +2700,9 @@ static bool try_add_synchronised_station(int32_t x, int32_t y, int32_t z) return false; } - ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - if (!(ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS)) + auto rideIndex = tileElement->AsTrack()->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride == nullptr || !(ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS)) { /* Ride is not set to synchronise with adjacent stations. */ return false; @@ -2829,14 +2785,17 @@ static bool try_add_synchronised_station(int32_t x, int32_t y, int32_t z) */ static bool vehicle_can_depart_synchronised(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return false; + int32_t station = vehicle->current_station; LocationXY8 location = ride->stations[station].Start; int32_t x = location.x * 32; int32_t y = location.y * 32; int32_t z = ride->stations[station].Height; - TileElement* tileElement = map_get_track_element_at(x, y, z); + auto tileElement = map_get_track_element_at(x, y, z); if (tileElement == nullptr) { return false; @@ -2906,7 +2865,7 @@ static bool vehicle_can_depart_synchronised(rct_vehicle* vehicle) { if (sv_ride->status != RIDE_STATUS_CLOSED) { - if (ride_is_block_sectioned(sv_ride)) + if (sv_ride->IsBlockSectioned()) { if (!(sv_ride->stations[sv->station_id].Depart & STATION_DEPART_FLAG)) { @@ -2927,13 +2886,16 @@ static bool vehicle_can_depart_synchronised(rct_vehicle* vehicle) /* Here all the of sync-ed stations are from the same ride */ ride = get_ride(rideId); - for (int32_t i = 0; i < ride->num_vehicles; i++) + if (ride != nullptr) { - rct_vehicle* v = GET_VEHICLE(ride->vehicles[i]); - if (v->status != VEHICLE_STATUS_WAITING_TO_DEPART && v->velocity != 0) + for (int32_t i = 0; i < ride->num_vehicles; i++) { - // Here at least one vehicle on the ride is moving. - return false; + rct_vehicle* v = GET_VEHICLE(ride->vehicles[i]); + if (v->status != VEHICLE_STATUS_WAITING_TO_DEPART && v->velocity != 0) + { + // Here at least one vehicle on the ride is moving. + return false; + } } } @@ -3035,7 +2997,7 @@ void vehicle_peep_easteregg_here_we_are(const rct_vehicle* vehicle) Peep* peep = GET_PEEP(vehicle->peep[i]); if (peep->peep_flags & PEEP_FLAGS_HERE_WE_ARE) { - peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_HERE_WE_ARE, peep->current_ride); + peep->InsertNewThought(PEEP_THOUGHT_TYPE_HERE_WE_ARE, peep->current_ride); } } } while ((spriteId = vehicle->next_vehicle_on_train) != SPRITE_INDEX_NULL); @@ -3047,7 +3009,10 @@ void vehicle_peep_easteregg_here_we_are(const rct_vehicle* vehicle) */ void vehicle_update_test_finish(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_TEST_IN_PROGRESS; vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_TESTING; ride->lifecycle_flags |= RIDE_LIFECYCLE_TESTED; @@ -3086,7 +3051,10 @@ void vehicle_test_reset(rct_vehicle* vehicle) { vehicle->update_flags |= VEHICLE_UPDATE_FLAG_TESTING; - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + ride->lifecycle_flags |= RIDE_LIFECYCLE_TEST_IN_PROGRESS; ride->lifecycle_flags &= ~RIDE_LIFECYCLE_NO_RAW_STATS; ride->max_speed = 0; @@ -3105,6 +3073,8 @@ void vehicle_test_reset(rct_vehicle* vehicle) ride->turn_count_banked = 0; ride->turn_count_sloped = 0; ride->inversions = 0; + ride->holes = 0; + ride->sheltered_eighths = 0; ride->drops = 0; ride->sheltered_length = 0; ride->var_11C = 0; @@ -3160,9 +3130,7 @@ static void vehicle_update_travelling_boat_hire_setup(rct_vehicle* vehicle) vehicle->boat_location = location; vehicle->var_35 = 0; - vehicle->status = VEHICLE_STATUS_TRAVELLING_BOAT; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_TRAVELLING_BOAT); vehicle->remaining_distance += 27924; vehicle_update_travelling_boat(vehicle); @@ -3175,7 +3143,10 @@ static void vehicle_update_travelling_boat_hire_setup(rct_vehicle* vehicle) static void vehicle_update_departing_boat_hire(rct_vehicle* vehicle) { vehicle->lost_time_out = 0; - Ride* ride = get_ride(vehicle->ride); + + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; ride->stations[vehicle->current_station].Depart &= STATION_DEPART_FLAG; uint8_t waitingTime = std::max(ride->min_waiting_time, static_cast(3)); @@ -3190,12 +3161,13 @@ static void vehicle_update_departing_boat_hire(rct_vehicle* vehicle) */ static void vehicle_update_departing(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); - rct_ride_entry* rideEntry = get_ride_entry(vehicle->ride_subtype); - if (rideEntry == nullptr) - { + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + + auto rideEntry = get_ride_entry(vehicle->ride_subtype); + if (rideEntry == nullptr) return; - } if (vehicle->sub_state == 0) { @@ -3221,14 +3193,14 @@ static void vehicle_update_departing(rct_vehicle* vehicle) if (rideEntry->flags & RIDE_ENTRY_FLAG_PLAY_DEPART_SOUND) { - uint8_t soundId = (rideEntry->vehicles[0].sound_range == 4) ? SOUND_TRAM : SOUND_TRAIN_CHUGGING; + auto soundId = (rideEntry->vehicles[0].sound_range == 4) ? SoundId::Tram : SoundId::TrainDeparting; - audio_play_sound_at_location(soundId, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(soundId, { vehicle->x, vehicle->y, vehicle->z }); } if (ride->mode == RIDE_MODE_UPWARD_LAUNCH || (ride->mode == RIDE_MODE_DOWNWARD_LAUNCH && vehicle->var_CE > 1)) { - audio_play_sound_at_location(SOUND_RIDE_LAUNCH_2, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::RideLaunch2, { vehicle->x, vehicle->y, vehicle->z }); } if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) @@ -3245,7 +3217,7 @@ static void vehicle_update_departing(rct_vehicle* vehicle) vehicle_update_test_finish(vehicle); } } - else if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS)) + else if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS) && !vehicle->IsGhost()) { vehicle_test_reset(vehicle); } @@ -3405,10 +3377,10 @@ static void vehicle_update_departing(rct_vehicle* vehicle) } } - if (vehicle_current_tower_element_is_top(vehicle) == false) + if (!vehicle_current_tower_element_is_top(vehicle)) { if (ride->mode == RIDE_MODE_FREEFALL_DROP) - vehicle_invalidate(vehicle); + vehicle->Invalidate(); return; } @@ -3426,14 +3398,16 @@ static void vehicle_update_departing(rct_vehicle* vehicle) */ static void vehicle_finish_departing(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; if (ride->mode == RIDE_MODE_DOWNWARD_LAUNCH) { if (vehicle->var_CE >= 1 && (14 << 16) > vehicle->velocity) return; - audio_play_sound_at_location(SOUND_RIDE_LAUNCH_1, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::RideLaunch1, { vehicle->x, vehicle->y, vehicle->z }); } if (ride->mode == RIDE_MODE_UPWARD_LAUNCH) @@ -3441,7 +3415,7 @@ static void vehicle_finish_departing(rct_vehicle* vehicle) if ((ride->launch_speed << 16) > vehicle->velocity) return; - audio_play_sound_at_location(SOUND_RIDE_LAUNCH_1, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::RideLaunch1, { vehicle->x, vehicle->y, vehicle->z }); } if (ride->mode != RIDE_MODE_RACE && ride->mode != RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED @@ -3457,12 +3431,8 @@ static void vehicle_finish_departing(rct_vehicle* vehicle) ride->stations[vehicle->current_station].Depart |= waitingTime; } - - vehicle->status = VEHICLE_STATUS_TRAVELLING; - vehicle_invalidate_window(vehicle); vehicle->lost_time_out = 0; - - vehicle->sub_state = 1; + vehicle->SetState(VEHICLE_STATUS_TRAVELLING, 1); if (vehicle->velocity < 0) vehicle->sub_state = 0; } @@ -3473,7 +3443,9 @@ static void vehicle_finish_departing(rct_vehicle* vehicle) */ static void vehicle_check_if_missing(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)) return; @@ -3504,13 +3476,21 @@ static void vehicle_check_if_missing(rct_vehicle* vehicle) vehicleIndex++; set_format_arg(2, uint16_t, vehicleIndex); - set_format_arg(4, rct_string_id, ride->name); - set_format_arg(6, uint32_t, ride->name_arguments); - set_format_arg(10, rct_string_id, RideComponentNames[RideNameConvention[ride->type].station].singular); + auto nameArgLen = ride->FormatNameTo(gCommonFormatArgs + 4); + set_format_arg(4 + nameArgLen, rct_string_id, RideComponentNames[RideNameConvention[ride->type].station].singular); news_item_add_to_queue(NEWS_ITEM_RIDE, STR_NEWS_VEHICLE_HAS_STALLED, vehicle->ride); } +static void vehicle_simulate_crash(rct_vehicle* vehicle) +{ + auto ride = get_ride(vehicle->ride); + if (ride != nullptr) + { + ride->lifecycle_flags |= RIDE_LIFECYCLE_CRASHED; + } +} + /** * Setup function for a vehicle colliding with * another vehicle. @@ -3519,10 +3499,18 @@ static void vehicle_check_if_missing(rct_vehicle* vehicle) */ static void vehicle_update_collision_setup(rct_vehicle* vehicle) { - vehicle->status = VEHICLE_STATUS_CRASHED; - vehicle_invalidate_window(vehicle); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + + if (ride->status == RIDE_STATUS_SIMULATING) + { + vehicle_simulate_crash(vehicle); + return; + } + + vehicle->SetState(VEHICLE_STATUS_CRASHED, vehicle->sub_state); - Ride* ride = get_ride(vehicle->ride); if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { auto frontVehicle = vehicle->GetHead(); @@ -3532,7 +3520,7 @@ static void vehicle_update_collision_setup(rct_vehicle* vehicle) return; } - ride_crash(ride, trainIndex); + ride->Crash(trainIndex); if (ride->status != RIDE_STATUS_CLOSED) { @@ -3553,7 +3541,7 @@ static void vehicle_update_collision_setup(rct_vehicle* vehicle) train->sub_state = 2; - audio_play_sound_at_location(SOUND_CRASH, train->x, train->y, train->z); + audio_play_sound_at_location(SoundId::Crash, { train->x, train->y, train->z }); sprite_misc_explosion_cloud_create(train->x, train->y, train->z); @@ -3594,13 +3582,18 @@ static constexpr const LocationXY16 stru_9A3AC4[] = { */ static void vehicle_update_crash_setup(rct_vehicle* vehicle) { - vehicle->status = VEHICLE_STATUS_CRASHING; - vehicle_invalidate_window(vehicle); + auto ride = get_ride(vehicle->ride); + if (ride != nullptr && ride->status == RIDE_STATUS_SIMULATING) + { + vehicle_simulate_crash(vehicle); + return; + } + vehicle->SetState(VEHICLE_STATUS_CRASHING, vehicle->sub_state); int32_t num_peeps = vehicle_get_total_num_peeps(vehicle); if (num_peeps != 0) { - audio_play_sound_at_location(SOUND_HAUNTED_HOUSE_SCREAM_2, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::HauntedHouseScream2, { vehicle->x, vehicle->y, vehicle->z }); } int32_t edx = vehicle->velocity >> 10; @@ -3654,8 +3647,8 @@ static void vehicle_update_travelling(rct_vehicle* vehicle) { vehicle_check_if_missing(vehicle); - Ride* ride = get_ride(vehicle->ride); - if (_vehicleBreakdown == 0 && ride->mode == RIDE_MODE_ROTATING_LIFT) + auto ride = get_ride(vehicle->ride); + if (ride == nullptr || (_vehicleBreakdown == 0 && ride->mode == RIDE_MODE_ROTATING_LIFT)) return; if (vehicle->sub_state == 2) @@ -3672,7 +3665,7 @@ static void vehicle_update_travelling(rct_vehicle* vehicle) vehicle->animation_frame++; vehicle->velocity = 0; vehicle->acceleration = 0; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); return; } @@ -3707,9 +3700,7 @@ static void vehicle_update_travelling(rct_vehicle* vehicle) { if (vehicle->sub_state <= 1) { - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 1; + vehicle->SetState(VEHICLE_STATUS_ARRIVING, 1); vehicle->var_C0 = 0; return; } @@ -3747,7 +3738,7 @@ static void vehicle_update_travelling(rct_vehicle* vehicle) } else { - if (vehicle_current_tower_element_is_top(vehicle) == true) + if (vehicle_current_tower_element_is_top(vehicle)) { vehicle->velocity = 0; vehicle->sub_state = 2; @@ -3819,12 +3810,9 @@ static void vehicle_update_travelling(rct_vehicle* vehicle) if (ride->mode == RIDE_MODE_POWERED_LAUNCH_PASSTROUGH && vehicle->velocity < 0) return; - vehicle->status = VEHICLE_STATUS_ARRIVING; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->current_station = _vehicleStationIndex; - vehicle_invalidate_window(vehicle); vehicle->var_C0 = 0; - - vehicle->sub_state = 0; if (vehicle->velocity < 0) vehicle->sub_state = 1; } @@ -3835,9 +3823,11 @@ static void vehicle_update_travelling(rct_vehicle* vehicle) */ static void vehicle_update_arriving(rct_vehicle* vehicle) { - uint8_t unkF64E35 = 1; - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + uint8_t unkF64E35 = 1; switch (ride->mode) { case RIDE_MODE_SWING: @@ -3859,9 +3849,7 @@ static void vehicle_update_arriving(rct_vehicle* vehicle) vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_12; vehicle->velocity = 0; vehicle->acceleration = 0; - vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_UNLOADING_PASSENGERS); return; } @@ -3967,9 +3955,7 @@ loc_6D8E36: if (flags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION && unkF64E35 == 0) { - vehicle->status = VEHICLE_STATUS_DEPARTING; - vehicle->sub_state = 1; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_DEPARTING, 1); return; } @@ -3989,68 +3975,56 @@ loc_6D8E36: return; } - TileElement* tileElement = map_get_track_element_at(vehicle->track_x, vehicle->track_y, vehicle->track_z / 8); + auto trackElement = map_get_track_element_at(vehicle->track_x, vehicle->track_y, vehicle->track_z / 8); - if (tileElement == nullptr) + if (trackElement == nullptr) { return; } - vehicle->current_station = tileElement->AsTrack()->GetStationIndex(); + vehicle->current_station = trackElement->GetStationIndex(); vehicle->num_laps++; if (vehicle->sub_state != 0) { if (vehicle->num_laps < ride->num_circuits) { - vehicle->status = VEHICLE_STATUS_DEPARTING; - vehicle->sub_state = 1; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_DEPARTING, 1); return; } if (vehicle->num_laps == ride->num_circuits && vehicle->update_flags & VEHICLE_UPDATE_FLAG_12) { - vehicle->status = VEHICLE_STATUS_DEPARTING; - vehicle->sub_state = 1; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_DEPARTING, 1); return; } } if (ride->num_circuits != 1 && vehicle->num_laps < ride->num_circuits) { - vehicle->status = VEHICLE_STATUS_DEPARTING; - vehicle->sub_state = 1; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_DEPARTING, 1); return; } if ((ride->mode == RIDE_MODE_UPWARD_LAUNCH || ride->mode == RIDE_MODE_DOWNWARD_LAUNCH) && vehicle->var_CE < 2) { - audio_play_sound_at_location(SOUND_RIDE_LAUNCH_2, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::RideLaunch2, { vehicle->x, vehicle->y, vehicle->z }); vehicle->velocity = 0; vehicle->acceleration = 0; - vehicle->status = VEHICLE_STATUS_DEPARTING; - vehicle->sub_state = 1; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_DEPARTING, 1); return; } if (ride->mode == RIDE_MODE_RACE && ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) { - vehicle->status = VEHICLE_STATUS_DEPARTING; - vehicle->sub_state = 1; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_DEPARTING, 1); return; } vehicle->update_flags &= ~VEHICLE_UPDATE_FLAG_12; vehicle->velocity = 0; vehicle->acceleration = 0; - vehicle->status = VEHICLE_STATUS_UNLOADING_PASSENGERS; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_UNLOADING_PASSENGERS); } /** @@ -4067,7 +4041,10 @@ static void vehicle_update_unloading_passengers(rct_vehicle* vehicle) } } - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + if (ride->mode == RIDE_MODE_FORWARD_ROTATION || ride->mode == RIDE_MODE_BACKWARD_ROTATION) { uint8_t seat = ((-vehicle->vehicle_sprite_type) >> 3) & 0xF; @@ -4100,9 +4077,7 @@ static void vehicle_update_unloading_passengers(rct_vehicle* vehicle) { vehicle_update_test_finish(vehicle); } - vehicle->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_MOVING_TO_END_OF_STATION); return; } @@ -4142,9 +4117,7 @@ static void vehicle_update_unloading_passengers(rct_vehicle* vehicle) { vehicle_update_test_finish(vehicle); } - vehicle->status = VEHICLE_STATUS_MOVING_TO_END_OF_STATION; - vehicle->sub_state = 0; - vehicle_invalidate_window(vehicle); + vehicle->SetState(VEHICLE_STATUS_MOVING_TO_END_OF_STATION); } /** @@ -4153,14 +4126,16 @@ static void vehicle_update_unloading_passengers(rct_vehicle* vehicle) */ static void vehicle_update_waiting_for_cable_lift(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; rct_vehicle* cableLift = GET_VEHICLE(ride->cable_lift); if (cableLift->status != VEHICLE_STATUS_WAITING_FOR_PASSENGERS) return; - cableLift->status = VEHICLE_STATUS_WAITING_TO_DEPART; + cableLift->SetState(VEHICLE_STATUS_WAITING_TO_DEPART, vehicle->sub_state); cableLift->cable_lift_target = vehicle->sprite_index; } @@ -4170,7 +4145,9 @@ static void vehicle_update_waiting_for_cable_lift(rct_vehicle* vehicle) */ static void vehicle_update_travelling_cable_lift(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; if (vehicle->sub_state == 0) { @@ -4207,7 +4184,7 @@ static void vehicle_update_travelling_cable_lift(rct_vehicle* vehicle) vehicle_update_test_finish(vehicle); } } - else if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS)) + else if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS) && !vehicle->IsGhost()) { vehicle_test_reset(vehicle); } @@ -4222,9 +4199,7 @@ static void vehicle_update_travelling_cable_lift(rct_vehicle* vehicle) if (flags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_11) { - vehicle->status = VEHICLE_STATUS_TRAVELLING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 1; + vehicle->SetState(VEHICLE_STATUS_TRAVELLING, 1); vehicle->lost_time_out = 0; return; } @@ -4270,13 +4245,16 @@ static void loc_6DA9F9(rct_vehicle* vehicle, int32_t x, int32_t y, int32_t bx, i vehicle->track_x = bx; vehicle->track_y = dx; - TileElement* tileElement = map_get_track_element_at(vehicle->track_x, vehicle->track_y, vehicle->track_z >> 3); + auto trackElement = map_get_track_element_at(vehicle->track_x, vehicle->track_y, vehicle->track_z >> 3); - Ride* ride = get_ride(vehicle->ride); - vehicle->track_type = (tileElement->AsTrack()->GetTrackType() << 2) | (ride->boat_hire_return_direction & 3); + auto ride = get_ride(vehicle->ride); + if (ride != nullptr) + { + vehicle->track_type = (trackElement->GetTrackType() << 2) | (ride->boat_hire_return_direction & 3); + } vehicle->track_progress = 0; - vehicle->status = VEHICLE_STATUS_TRAVELLING; + vehicle->SetState(VEHICLE_STATUS_TRAVELLING, vehicle->sub_state); unk_F64E20.x = x; unk_F64E20.y = y; } @@ -4312,7 +4290,7 @@ static void vehicle_update_motion_boat_hire(rct_vehicle* vehicle) unk_F64E20.x = vehicle->x; unk_F64E20.y = vehicle->y; unk_F64E20.z = vehicle->z; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); for (;;) { @@ -4445,7 +4423,9 @@ static void vehicle_update_motion_boat_hire(rct_vehicle* vehicle) if (!vehicle_boat_is_location_accessible(TileCoordsXYZ(CoordsXYZ{ x, y, vehicle->track_z }))) { // loc_6DA939: - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; bool do_loc_6DAA97 = false; if (vehicle->sub_state != 1) @@ -4536,7 +4516,7 @@ static void vehicle_update_motion_boat_hire(rct_vehicle* vehicle) } sprite_move(unk_F64E20.x, unk_F64E20.y, unk_F64E20.z, (rct_sprite*)vehicle); - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } // loc_6DAAC9: @@ -4578,7 +4558,10 @@ static void vehicle_update_motion_boat_hire(rct_vehicle* vehicle) */ static void vehicle_update_boat_location(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + LocationXY8 returnPosition = ride->boat_hire_return_position; uint8_t returnDirection = ride->boat_hire_return_direction & 3; @@ -4654,9 +4637,11 @@ static void vehicle_update_boat_location(rct_vehicle* vehicle) static bool vehicle_boat_is_location_accessible(const TileCoordsXYZ& location) { TileElement* tileElement = map_get_first_element_at(location.x, location.y); + if (tileElement == nullptr) + return false; do { - if (tileElement->IsGhost() == true) + if (tileElement->IsGhost()) continue; if (tileElement->GetType() == TILE_ELEMENT_TYPE_SURFACE) @@ -4684,8 +4669,13 @@ static bool vehicle_boat_is_location_accessible(const TileCoordsXYZ& location) */ static void vehicle_update_swinging(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); - rct_ride_entry* rideEntry = get_ride_entry(vehicle->ride_subtype); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + + auto rideEntry = get_ride_entry(vehicle->ride_subtype); + if (rideEntry == nullptr) + return; // SubState for this ride means swinging state // 0 == first swing @@ -4710,7 +4700,7 @@ static void vehicle_update_swinging(rct_vehicle* vehicle) { // Used to know which sprite to draw vehicle->vehicle_sprite_type = (uint8_t)spriteType; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } return; } @@ -4738,9 +4728,7 @@ static void vehicle_update_swinging(rct_vehicle* vehicle) // swing has to be in slowing down phase if (vehicle->sub_state == 0) { - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; return; } @@ -4758,7 +4746,10 @@ static void vehicle_update_ferris_wheel_rotating(rct_vehicle* vehicle) if (_vehicleBreakdown == 0) return; - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + if ((vehicle->ferris_wheel_var_1 -= 1) != 0) return; @@ -4795,7 +4786,7 @@ static void vehicle_update_ferris_wheel_rotating(rct_vehicle* vehicle) if (rotation == vehicle->sub_state) vehicle->var_CE++; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); uint8_t subState = vehicle->sub_state; if (ride->mode == RIDE_MODE_FORWARD_ROTATION) @@ -4834,9 +4825,7 @@ static void vehicle_update_ferris_wheel_rotating(rct_vehicle* vehicle) if (subState != vehicle->vehicle_sprite_type) return; - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; } @@ -4858,13 +4847,11 @@ static void vehicle_update_simulator_operating(rct_vehicle* vehicle) if (al == vehicle->vehicle_sprite_type) return; vehicle->vehicle_sprite_type = al; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); return; } - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; } @@ -4877,8 +4864,11 @@ static void vehicle_update_rotating(rct_vehicle* vehicle) if (_vehicleBreakdown == 0) return; - Ride* ride = get_ride(vehicle->ride); - rct_ride_entry* rideEntry = get_ride_entry(vehicle->ride_subtype); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + + auto rideEntry = get_ride_entry(vehicle->ride_subtype); if (rideEntry == nullptr) { return; @@ -4912,7 +4902,7 @@ static void vehicle_update_rotating(rct_vehicle* vehicle) if (sprite == vehicle->vehicle_sprite_type) return; vehicle->vehicle_sprite_type = sprite; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); return; } @@ -4931,13 +4921,11 @@ static void vehicle_update_rotating(rct_vehicle* vehicle) shouldStop = false; } - if (shouldStop == true) + if (shouldStop) { if (vehicle->sub_state == 2) { - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; return; } @@ -4949,9 +4937,7 @@ static void vehicle_update_rotating(rct_vehicle* vehicle) if (ride->type == RIDE_TYPE_ENTERPRISE && vehicle->sub_state == 2) { - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; return; } @@ -4976,14 +4962,12 @@ static void vehicle_update_space_rings_operating(rct_vehicle* vehicle) if (spriteType != vehicle->vehicle_sprite_type) { vehicle->vehicle_sprite_type = spriteType; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } } else { - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; } } @@ -5002,7 +4986,7 @@ static void vehicle_update_haunted_house_operating(rct_vehicle* vehicle) if (gCurrentTicks & 1) { vehicle->vehicle_sprite_type++; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); if (vehicle->vehicle_sprite_type == 19) vehicle->vehicle_sprite_type = 0; @@ -5011,9 +4995,7 @@ static void vehicle_update_haunted_house_operating(rct_vehicle* vehicle) if (vehicle->current_time + 1 > 1500) { - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; return; } @@ -5022,24 +5004,24 @@ static void vehicle_update_haunted_house_operating(rct_vehicle* vehicle) switch (vehicle->current_time) { case 45: - audio_play_sound_at_location(SOUND_HAUNTED_HOUSE_SCARE, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::HauntedHouseScare, { vehicle->x, vehicle->y, vehicle->z }); break; case 75: vehicle->vehicle_sprite_type = 1; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); break; case 400: - audio_play_sound_at_location(SOUND_HAUNTED_HOUSE_SCREAM_1, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::HauntedHouseScream1, { vehicle->x, vehicle->y, vehicle->z }); break; case 745: - audio_play_sound_at_location(SOUND_HAUNTED_HOUSE_SCARE, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::HauntedHouseScare, { vehicle->x, vehicle->y, vehicle->z }); break; case 775: vehicle->vehicle_sprite_type = 1; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); break; case 1100: - audio_play_sound_at_location(SOUND_HAUNTED_HOUSE_SCREAM_2, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::HauntedHouseScream2, { vehicle->x, vehicle->y, vehicle->z }); break; } } @@ -5056,9 +5038,7 @@ static void vehicle_update_crooked_house_operating(rct_vehicle* vehicle) // Originally used an array of size 1 at 0x009A0AC4 and passed the sub state into it. if ((uint16_t)(vehicle->current_time + 1) > 600) { - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; return; } @@ -5083,20 +5063,18 @@ static void vehicle_update_top_spin_operating(rct_vehicle* vehicle) if (rotation != vehicle->vehicle_sprite_type) { vehicle->vehicle_sprite_type = rotation; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } rotation = sprite_map[vehicle->current_time].bank_rotation; if (rotation != vehicle->bank_rotation) { vehicle->bank_rotation = rotation; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } return; } - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; } @@ -5119,9 +5097,7 @@ static void vehicle_update_showing_film(rct_vehicle* vehicle) } else { - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; } } @@ -5142,9 +5118,7 @@ static void vehicle_update_doing_circus_show(rct_vehicle* vehicle) } else { - vehicle->status = VEHICLE_STATUS_ARRIVING; - vehicle_invalidate_window(vehicle); - vehicle->sub_state = 0; + vehicle->SetState(VEHICLE_STATUS_ARRIVING); vehicle->var_C0 = 0; } } @@ -5162,18 +5136,18 @@ static TileElement* vehicle_check_collision(int16_t x, int16_t y, int16_t z) return nullptr; } - uint8_t bl; + uint8_t quadrant; if ((x & 0x1F) >= 16) { - bl = 1; + quadrant = 1; if ((y & 0x1F) < 16) - bl = 2; + quadrant = 2; } else { - bl = 4; + quadrant = 4; if ((y & 0x1F) >= 16) - bl = 8; + quadrant = 8; } do @@ -5184,7 +5158,7 @@ static TileElement* vehicle_check_collision(int16_t x, int16_t y, int16_t z) if (z / 8 >= tileElement->clearance_height) continue; - if (tileElement->flags & bl) + if (tileElement->GetOccupiedQuadrants() & quadrant) return tileElement; } while (!(tileElement++)->IsLastForTile()); @@ -5197,8 +5171,11 @@ static TileElement* vehicle_check_collision(int16_t x, int16_t y, int16_t z) */ static void vehicle_kill_all_passengers(rct_vehicle* vehicle) { - uint16_t numFatalities = 0; + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + uint16_t numFatalities = 0; uint16_t spriteId = vehicle->sprite_index; for (rct_vehicle* curVehicle; spriteId != SPRITE_INDEX_NULL; spriteId = curVehicle->next_vehicle_on_train) { @@ -5206,7 +5183,6 @@ static void vehicle_kill_all_passengers(rct_vehicle* vehicle) numFatalities += curVehicle->num_peeps; } - Ride* ride = get_ride(vehicle->ride); set_format_arg(0, uint16_t, numFatalities); uint8_t crashType = numFatalities == 0 ? RIDE_CRASH_TYPE_NO_FATALITIES : RIDE_CRASH_TYPE_FATALITIES; @@ -5216,8 +5192,7 @@ static void vehicle_kill_all_passengers(rct_vehicle* vehicle) if (numFatalities != 0) { - set_format_arg(2, rct_string_id, ride->name); - set_format_arg(4, uint32_t, ride->name_arguments); + ride->FormatNameTo(gCommonFormatArgs + 2); news_item_add_to_queue(NEWS_ITEM_RIDE, STR_X_PEOPLE_DIED_ON_X, vehicle->ride); if (gParkRatingCasualtyPenalty < 500) @@ -5257,10 +5232,17 @@ static void vehicle_kill_all_passengers(rct_vehicle* vehicle) static void vehicle_crash_on_land(rct_vehicle* vehicle) { - vehicle->status = VEHICLE_STATUS_CRASHED; - vehicle_invalidate_window(vehicle); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + + if (ride->status == RIDE_STATUS_SIMULATING) + { + vehicle_simulate_crash(vehicle); + return; + } + vehicle->SetState(VEHICLE_STATUS_CRASHED, vehicle->sub_state); - Ride* ride = get_ride(vehicle->ride); if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { auto frontVehicle = vehicle->GetHead(); @@ -5270,7 +5252,7 @@ static void vehicle_crash_on_land(rct_vehicle* vehicle) return; } - ride_crash(ride, trainIndex); + ride->Crash(trainIndex); if (ride->status != RIDE_STATUS_CLOSED) { @@ -5286,7 +5268,7 @@ static void vehicle_crash_on_land(rct_vehicle* vehicle) } vehicle->sub_state = 2; - audio_play_sound_at_location(SOUND_CRASH, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::Crash, { vehicle->x, vehicle->y, vehicle->z }); sprite_misc_explosion_cloud_create(vehicle->x, vehicle->y, vehicle->z); sprite_misc_explosion_flare_create(vehicle->x, vehicle->y, vehicle->z); @@ -5304,17 +5286,24 @@ static void vehicle_crash_on_land(rct_vehicle* vehicle) vehicle->sprite_height_positive = 5; sprite_move(vehicle->x, vehicle->y, vehicle->z, (rct_sprite*)vehicle); - vehicle_invalidate(vehicle); + vehicle->Invalidate(); vehicle->crash_z = 0; } static void vehicle_crash_on_water(rct_vehicle* vehicle) { - vehicle->status = VEHICLE_STATUS_CRASHED; - vehicle_invalidate_window(vehicle); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + + if (ride->status == RIDE_STATUS_SIMULATING) + { + vehicle_simulate_crash(vehicle); + return; + } + vehicle->SetState(VEHICLE_STATUS_CRASHED, vehicle->sub_state); - Ride* ride = get_ride(vehicle->ride); if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)) { auto frontVehicle = vehicle->GetHead(); @@ -5324,7 +5313,7 @@ static void vehicle_crash_on_water(rct_vehicle* vehicle) return; } - ride_crash(ride, trainIndex); + ride->Crash(trainIndex); if (ride->status != RIDE_STATUS_CLOSED) { @@ -5340,7 +5329,7 @@ static void vehicle_crash_on_water(rct_vehicle* vehicle) } vehicle->sub_state = 2; - audio_play_sound_at_location(SOUND_WATER_1, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::Water1, { vehicle->x, vehicle->y, vehicle->z }); crash_splash_create(vehicle->x, vehicle->y, vehicle->z); crash_splash_create(vehicle->x - 8, vehicle->y - 9, vehicle->z); @@ -5359,7 +5348,7 @@ static void vehicle_crash_on_water(rct_vehicle* vehicle) vehicle->sprite_height_positive = 5; sprite_move(vehicle->x, vehicle->y, vehicle->z, (rct_sprite*)vehicle); - vehicle_invalidate(vehicle); + vehicle->Invalidate(); vehicle->crash_z = -1; } @@ -5382,9 +5371,9 @@ static void vehicle_update_crash(rct_vehicle* vehicle) curVehicle->crash_z++; if ((scenario_rand() & 0xFFFF) <= 0x1555) { - sprite_misc_explosion_cloud_create( - curVehicle->x + ((scenario_rand() & 2) - 1), curVehicle->y + ((scenario_rand() & 2) - 1), - curVehicle->z); + auto xOffset = (scenario_rand() & 2) - 1; + auto yOffset = (scenario_rand() & 2) - 1; + sprite_misc_explosion_cloud_create(curVehicle->x + xOffset, curVehicle->y + yOffset, curVehicle->z); } } if (curVehicle->var_C8 + 7281 > 0xFFFF) @@ -5409,9 +5398,8 @@ static void vehicle_update_crash(rct_vehicle* vehicle) continue; } - int32_t z = tile_element_height(curVehicle->x, curVehicle->y); - int16_t waterHeight = (z >> 16) & 0xFFFF; - z = (int16_t)(z & 0xFFFF); + int16_t z = tile_element_height({ curVehicle->x, curVehicle->y }); + int16_t waterHeight = tile_element_water_height({ curVehicle->x, curVehicle->y }); int16_t zDiff; if (waterHeight != 0) { @@ -5462,21 +5450,20 @@ static void vehicle_update_crash(rct_vehicle* vehicle) */ static void vehicle_update_sound(rct_vehicle* vehicle) { - Ride* ride; - rct_ride_entry* rideEntry; // frictionVolume (bl) should be set before hand - uint8_t frictionVolume = 255, frictionId = 255; + uint8_t frictionVolume = 255; + SoundId frictionId = SoundId::Null; // bh screamVolume should be set before hand - uint8_t screamId = 255, screamVolume = 255; - uint16_t soundIdVolume; + SoundId screamId = SoundId::Null; + uint8_t screamVolume = 255; - ride = get_ride(vehicle->ride); - rideEntry = get_ride_entry(vehicle->ride_subtype); - - if (rideEntry == nullptr) - { + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + + auto rideEntry = get_ride_entry(vehicle->ride_subtype); + if (rideEntry == nullptr) return; - } rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type]; @@ -5494,18 +5481,18 @@ static void vehicle_update_sound(rct_vehicle* vehicle) screamId = vehicle->scream_sound_id; if (!(gCurrentTicks & 0x7F)) { - if (vehicle->velocity < 0x40000 || vehicle->scream_sound_id != 255) + if (vehicle->velocity < 0x40000 || vehicle->scream_sound_id != SoundId::Null) goto loc_6D7A97; if ((scenario_rand() & 0xFFFF) <= 0x5555) { - vehicle->scream_sound_id = SOUND_TRAIN_WHISTLE; + vehicle->scream_sound_id = SoundId::TrainWhistle; screamVolume = 255; break; } } - if (screamId == NO_SCREAM) - screamId = 255; + if (screamId == SoundId::NoScream) + screamId = SoundId::Null; screamVolume = 255; break; @@ -5513,18 +5500,18 @@ static void vehicle_update_sound(rct_vehicle* vehicle) screamId = vehicle->scream_sound_id; if (!(gCurrentTicks & 0x7F)) { - if (vehicle->velocity < 0x40000 || vehicle->scream_sound_id != 255) + if (vehicle->velocity < 0x40000 || vehicle->scream_sound_id != SoundId::Null) goto loc_6D7A97; if ((scenario_rand() & 0xFFFF) <= 0x5555) { - vehicle->scream_sound_id = SOUND_TRAM; + vehicle->scream_sound_id = SoundId::Tram; screamVolume = 255; break; } } - if (screamId == NO_SCREAM) - screamId = 255; + if (screamId == SoundId::NoScream) + screamId = SoundId::Null; screamVolume = 255; break; @@ -5532,34 +5519,34 @@ static void vehicle_update_sound(rct_vehicle* vehicle) if ((vehicleEntry->flags & VEHICLE_ENTRY_FLAG_RIDERS_SCREAM)) { screamId = vehicle_update_scream_sound(vehicle); - if (screamId == NO_SCREAM) - screamId = 255; - if (screamId == 255) + if (screamId == SoundId::NoScream) + screamId = SoundId::Null; + if (screamId == SoundId::Null) goto loc_6D7A97; break; } loc_6D7A97: - vehicle->scream_sound_id = 255; + vehicle->scream_sound_id = SoundId::Null; if (ride->type < std::size(RideLiftData)) { // Get lift hill sound screamId = RideLiftData[ride->type].sound_id; screamVolume = 243; if (!(vehicle->sound2_flags & VEHICLE_SOUND2_FLAGS_LIFT_HILL)) - screamId = 255; + screamId = SoundId::Null; } } // Friction sound - soundIdVolume = sub_6D7AC0(vehicle->sound1_id, vehicle->sound1_volume, frictionId, frictionVolume); - vehicle->sound1_id = soundIdVolume & 0xFF; - vehicle->sound1_volume = (soundIdVolume >> 8) & 0xFF; + auto soundIdVolume = sub_6D7AC0(vehicle->sound1_id, vehicle->sound1_volume, frictionId, frictionVolume); + vehicle->sound1_id = soundIdVolume.id; + vehicle->sound1_volume = soundIdVolume.volume; // Scream sound soundIdVolume = sub_6D7AC0(vehicle->sound2_id, vehicle->sound2_volume, screamId, screamVolume); - vehicle->sound2_id = soundIdVolume & 0xFF; - vehicle->sound2_volume = (soundIdVolume >> 8) & 0xFF; + vehicle->sound2_id = soundIdVolume.id; + vehicle->sound2_volume = soundIdVolume.volume; // Calculate Sound Vector (used for sound frequency calcs) int32_t soundDirection = SpriteDirectionToSoundDirection[vehicle->sprite_direction]; @@ -5573,7 +5560,7 @@ static void vehicle_update_sound(rct_vehicle* vehicle) * * rct2: 0x006D796B */ -static int32_t vehicle_update_scream_sound(rct_vehicle* vehicle) +static SoundId vehicle_update_scream_sound(rct_vehicle* vehicle) { uint32_t r; uint16_t spriteIndex; @@ -5586,12 +5573,12 @@ static int32_t vehicle_update_scream_sound(rct_vehicle* vehicle) int32_t totalNumPeeps = vehicle_get_total_num_peeps(vehicle); if (totalNumPeeps == 0) - return 255; + return SoundId::Null; if (vehicle->velocity < 0) { if (vehicle->velocity > -0x2C000) - return 255; + return SoundId::Null; spriteIndex = vehicle->sprite_index; do @@ -5606,11 +5593,11 @@ static int32_t vehicle_update_scream_sound(rct_vehicle* vehicle) if (vehicle2->vehicle_sprite_type <= 15) goto produceScream; } while ((spriteIndex = vehicle2->next_vehicle_on_train) != SPRITE_INDEX_NULL); - return 255; + return SoundId::Null; } if (vehicle->velocity < 0x2C000) - return 255; + return SoundId::Null; spriteIndex = vehicle->sprite_index; do @@ -5625,10 +5612,10 @@ static int32_t vehicle_update_scream_sound(rct_vehicle* vehicle) if (vehicle2->vehicle_sprite_type <= 23) goto produceScream; } while ((spriteIndex = vehicle2->next_vehicle_on_train) != SPRITE_INDEX_NULL); - return 255; + return SoundId::Null; produceScream: - if (vehicle->scream_sound_id == 255) + if (vehicle->scream_sound_id == SoundId::Null) { r = scenario_rand(); if (totalNumPeeps >= (int32_t)(r % 16)) @@ -5645,13 +5632,13 @@ produceScream: vehicle->scream_sound_id = byte_9A3A16[r % 2]; break; default: - vehicle->scream_sound_id = NO_SCREAM; + vehicle->scream_sound_id = SoundId::NoScream; break; } } else { - vehicle->scream_sound_id = NO_SCREAM; + vehicle->scream_sound_id = SoundId::NoScream; } } return vehicle->scream_sound_id; @@ -5664,7 +5651,7 @@ produceScream: * dx: lateralG * esi: vehicle */ -void vehicle_get_g_forces(const rct_vehicle* vehicle, int32_t* verticalG, int32_t* lateralG) +GForces vehicle_get_g_forces(const rct_vehicle* vehicle) { int32_t gForceVert = (((int64_t)0x280000) * Unk9A37E4[vehicle->vehicle_sprite_type]) >> 32; gForceVert = (((int64_t)gForceVert) * Unk9A39C4[vehicle->bank_rotation]) >> 32; @@ -5697,7 +5684,7 @@ void vehicle_get_g_forces(const rct_vehicle* vehicle, int32_t* verticalG, int32_ case TRACK_ELEM_60_DEG_DOWN_COVERED: case TRACK_ELEM_BRAKES: case TRACK_ELEM_ROTATION_CONTROL_TOGGLE: - case TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP: + case TRACK_ELEM_MAZE: case TRACK_ELEM_25_DEG_UP_LEFT_BANKED: case TRACK_ELEM_25_DEG_UP_RIGHT_BANKED: case TRACK_ELEM_WATERFALL: @@ -6129,7 +6116,7 @@ void vehicle_get_g_forces(const rct_vehicle* vehicle, int32_t* verticalG, int32_ break; case TRACK_ELEM_90_DEG_TO_INVERTED_FLAT_QUARTER_LOOP_UP: case TRACK_ELEM_MULTIDIM_90_DEG_UP_TO_INVERTED_FLAT_QUARTER_LOOP: - case 255: + case TRACK_ELEM_MULTIDIM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP: vertFactor = (((uint16_t)(-(vehicle->track_progress - 137))) / 4) + 55; // 6D7614 break; @@ -6179,37 +6166,34 @@ void vehicle_get_g_forces(const rct_vehicle* vehicle, int32_t* verticalG, int32_ gForceLateral *= 10; gForceVert >>= 16; gForceLateral >>= 16; - - if (verticalG != nullptr) - *verticalG = (int16_t)(gForceVert & 0xFFFF); - if (lateralG != nullptr) - *lateralG = (int16_t)(gForceLateral & 0xFFFF); + return { (int16_t)(gForceVert & 0xFFFF), (int16_t)(gForceLateral & 0xFFFF) }; } void vehicle_set_map_toolbar(const rct_vehicle* vehicle) { - Ride* ride; - int32_t vehicleIndex; + auto ride = get_ride(vehicle->ride); + if (ride != nullptr) + { + vehicle = vehicle->GetHead(); - ride = get_ride(vehicle->ride); - vehicle = vehicle->GetHead(); - for (vehicleIndex = 0; vehicleIndex < 32; vehicleIndex++) - if (ride->vehicles[vehicleIndex] == vehicle->sprite_index) - break; + int32_t vehicleIndex; + for (vehicleIndex = 0; vehicleIndex < 32; vehicleIndex++) + if (ride->vehicles[vehicleIndex] == vehicle->sprite_index) + break; - set_map_tooltip_format_arg(0, rct_string_id, STR_RIDE_MAP_TIP); - set_map_tooltip_format_arg(2, rct_string_id, STR_MAP_TOOLTIP_STRINGID_STRINGID); - set_map_tooltip_format_arg(4, rct_string_id, ride->name); - set_map_tooltip_format_arg(6, uint32_t, ride->name_arguments); - set_map_tooltip_format_arg(10, rct_string_id, RideComponentNames[RideNameConvention[ride->type].vehicle].capitalised); - set_map_tooltip_format_arg(12, uint16_t, vehicleIndex + 1); - - rct_string_id formatSecondary; - int32_t arg1; - ride_get_status(ride, &formatSecondary, &arg1); - set_map_tooltip_format_arg(14, rct_string_id, formatSecondary); - // TODO: odd cast - set_map_tooltip_format_arg(16, uint32_t, (uint16_t)arg1); + size_t argPos = 0; + set_map_tooltip_format_arg(argPos, rct_string_id, STR_RIDE_MAP_TIP); + argPos += sizeof(rct_string_id); + set_map_tooltip_format_arg(argPos, rct_string_id, STR_MAP_TOOLTIP_STRINGID_STRINGID); + argPos += sizeof(rct_string_id); + argPos += ride->FormatNameTo(gMapTooltipFormatArgs + argPos); + set_map_tooltip_format_arg( + argPos, rct_string_id, RideComponentNames[RideNameConvention[ride->type].vehicle].capitalised); + argPos += sizeof(rct_string_id); + set_map_tooltip_format_arg(argPos, uint16_t, vehicleIndex + 1); + argPos += sizeof(uint16_t); + ride->FormatStatusTo(gMapTooltipFormatArgs + argPos); + } } rct_vehicle* vehicle_get_head(const rct_vehicle* vehicle) @@ -6252,7 +6236,10 @@ int32_t vehicle_is_used_in_pairs(const rct_vehicle* vehicle) static int32_t vehicle_update_motion_dodgems(rct_vehicle* vehicle) { _vehicleMotionTrackFlags = 0; - Ride* ride = get_ride(vehicle->ride); + + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return _vehicleMotionTrackFlags; int32_t nextVelocity = vehicle->velocity + vehicle->acceleration; if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN) @@ -6283,7 +6270,7 @@ static int32_t vehicle_update_motion_dodgems(rct_vehicle* vehicle) vehicle->sprite_direction -= 2; } vehicle->sprite_direction &= 0x1E; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } else if ((scenario_rand() & 0xFFFF) <= 2849) { @@ -6292,7 +6279,7 @@ static int32_t vehicle_update_motion_dodgems(rct_vehicle* vehicle) else vehicle->sprite_direction += 2; vehicle->sprite_direction &= 0x1E; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } } @@ -6312,9 +6299,9 @@ static int32_t vehicle_update_motion_dodgems(rct_vehicle* vehicle) if (!vehicle_update_dodgems_collision(vehicle, location.x, location.y, &collideSprite)) { - vehicle_invalidate(vehicle); + vehicle->Invalidate(); sprite_move(location.x, location.y, location.z, (rct_sprite*)vehicle); - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } } @@ -6327,7 +6314,7 @@ static int32_t vehicle_update_motion_dodgems(rct_vehicle* vehicle) unk_F64E20.y = vehicle->y; unk_F64E20.z = vehicle->z; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); while (true) { @@ -6382,7 +6369,7 @@ static int32_t vehicle_update_motion_dodgems(rct_vehicle* vehicle) } sprite_move(unk_F64E20.x, unk_F64E20.y, unk_F64E20.z, (rct_sprite*)vehicle); - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } int32_t eax = vehicle->velocity / 2; @@ -6489,9 +6476,7 @@ bool vehicle_update_dodgems_collision(rct_vehicle* vehicle, int16_t x, int16_t y */ static void vehicle_update_track_motion_up_stop_check(rct_vehicle* vehicle) { - rct_ride_entry_vehicle* vehicleEntry = vehicle_get_vehicle_entry(vehicle); - int32_t verticalG, lateralG; - + auto vehicleEntry = vehicle_get_vehicle_entry(vehicle); if (vehicleEntry == nullptr) { return; @@ -6503,18 +6488,18 @@ static void vehicle_update_track_motion_up_stop_check(rct_vehicle* vehicle) int32_t trackType = vehicle->track_type >> 2; if (!track_element_is_covered(trackType)) { - vehicle_get_g_forces(vehicle, &verticalG, &lateralG); - lateralG = abs(lateralG); - if (lateralG <= 150) + auto gForces = vehicle_get_g_forces(vehicle); + gForces.LateralG = std::abs(gForces.LateralG); + if (gForces.LateralG <= 150) { if (dword_9A2970[vehicle->vehicle_sprite_type] < 0) { - if (verticalG > -40) + if (gForces.VerticalG > -40) { return; } } - else if (verticalG > -80) + else if (gForces.VerticalG > -80) { return; } @@ -6532,18 +6517,18 @@ static void vehicle_update_track_motion_up_stop_check(rct_vehicle* vehicle) int32_t trackType = vehicle->track_type >> 2; if (!track_element_is_covered(trackType)) { - vehicle_get_g_forces(vehicle, &verticalG, &lateralG); + auto gForces = vehicle_get_g_forces(vehicle); if (dword_9A2970[vehicle->vehicle_sprite_type] < 0) { - if (verticalG > -45) + if (gForces.VerticalG > -45) { return; } } else { - if (verticalG > -80) + if (gForces.VerticalG > -80) { return; } @@ -6622,13 +6607,13 @@ static void apply_block_brakes(rct_vehicle* vehicle, bool is_block_brake_closed) */ static void check_and_apply_block_section_stop_site(rct_vehicle* vehicle) { - Ride* ride = get_ride(vehicle->ride); - rct_ride_entry_vehicle* vehicleEntry = vehicle_get_vehicle_entry(vehicle); - - if (vehicleEntry == nullptr) - { + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + + auto vehicleEntry = vehicle_get_vehicle_entry(vehicle); + if (vehicleEntry == nullptr) return; - } // Is chair lift type if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_CHAIRLIFT) @@ -6655,7 +6640,7 @@ static void check_and_apply_block_section_stop_site(rct_vehicle* vehicle) switch (trackType) { case TRACK_ELEM_BLOCK_BRAKES: - if (ride_is_block_sectioned(ride)) + if (ride->IsBlockSectioned()) apply_block_brakes(vehicle, trackElement->AsTrack()->BlockBrakeClosed()); else apply_non_stop_block_brake(vehicle, true); @@ -6671,7 +6656,7 @@ static void check_and_apply_block_section_stop_site(rct_vehicle* vehicle) case TRACK_ELEM_CABLE_LIFT_HILL: case TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT: case TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT: - if (ride_is_block_sectioned(ride)) + if (ride->IsBlockSectioned()) { if (trackType == TRACK_ELEM_CABLE_LIFT_HILL || trackElement->AsTrack()->HasChain()) { @@ -6751,7 +6736,8 @@ static void vehicle_update_block_brakes_open_previous_section(rct_vehicle* vehic slowY = slowTrackBeginEnd.end_y; slowTileElement = *(slowTrackBeginEnd.begin_element); if (slowX == x && slowY == y && slowTileElement.base_height == tileElement->base_height - && slowTileElement.type == tileElement->type) + && slowTileElement.GetType() == tileElement->GetType() + && slowTileElement.GetDirection() == tileElement->GetDirection()) { return; } @@ -6762,21 +6748,21 @@ static void vehicle_update_block_brakes_open_previous_section(rct_vehicle* vehic x = trackBeginEnd.begin_x; y = trackBeginEnd.begin_y; z = trackBeginEnd.begin_z; - tileElement = map_get_track_element_at(x, y, z >> 3); - if (tileElement == nullptr) + auto trackElement = map_get_track_element_at(x, y, z >> 3); + if (trackElement == nullptr) { return; } - tileElement->AsTrack()->SetBlockBrakeClosed(false); - map_invalidate_element(x, y, tileElement); + trackElement->SetBlockBrakeClosed(false); + map_invalidate_element(x, y, reinterpret_cast(trackElement)); - int32_t trackType = tileElement->AsTrack()->GetTrackType(); + int32_t trackType = trackElement->GetTrackType(); if (trackType == TRACK_ELEM_BLOCK_BRAKES || trackType == TRACK_ELEM_END_STATION) { - Ride* ride = get_ride(vehicle->ride); - if (ride_is_block_sectioned(ride)) + auto ride = get_ride(vehicle->ride); + if (ride != nullptr && ride->IsBlockSectioned()) { - audio_play_sound_at_location(SOUND_48, x, y, z); + audio_play_sound_at_location(SoundId::BlockBrakeClose, { x, y, z }); } } } @@ -7043,7 +7029,7 @@ static void vehicle_update_swinging_car(rct_vehicle* vehicle) if (swingSprite != vehicle->swing_sprite) { vehicle->swing_sprite = swingSprite; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } } @@ -7202,7 +7188,7 @@ static void vehicle_update_spinning_car(rct_vehicle* vehicle) vehicle->spin_sprite += spinSpeed >> 8; // Note this actually increases the spin speed if going right! vehicle->spin_speed -= spinSpeed >> vehicleEntry->spinning_friction; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } /** @@ -7211,10 +7197,10 @@ static void vehicle_update_spinning_car(rct_vehicle* vehicle) */ static void steam_particle_create(int16_t x, int16_t y, int16_t z) { - TileElement* tileElement = map_get_surface_element_at({ x, y }); - if (tileElement != nullptr && z > tileElement->base_height * 8) + auto surfaceElement = map_get_surface_element_at({ x, y }); + if (surfaceElement != nullptr && z > surfaceElement->base_height * 8) { - rct_steam_particle* steam = (rct_steam_particle*)create_sprite(2); + rct_steam_particle* steam = &create_sprite(SPRITE_IDENTIFIER_MISC)->steam_particle; if (steam == nullptr) return; @@ -7258,26 +7244,29 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) ah &= 0x02; if (al != ah) { - Ride* ride = get_ride(vehicle->ride); - if (!ride_has_station_shelter(ride) - || (vehicle->status != VEHICLE_STATUS_MOVING_TO_END_OF_STATION - && vehicle->status != VEHICLE_STATUS_ARRIVING)) + auto ride = get_ride(vehicle->ride); + if (ride != nullptr) { - int32_t index = vehicle->sprite_direction >> 1; - if (vehicle->vehicle_sprite_type == 2) + if (!ride_has_station_shelter(ride) + || (vehicle->status != VEHICLE_STATUS_MOVING_TO_END_OF_STATION + && vehicle->status != VEHICLE_STATUS_ARRIVING)) { - index += 16; + int32_t index = vehicle->sprite_direction >> 1; + if (vehicle->vehicle_sprite_type == 2) + { + index += 16; + } + if (vehicle->vehicle_sprite_type == 6) + { + index += 32; + } + steam_particle_create( + vehicle->x + SteamParticleOffsets[index].x, vehicle->y + SteamParticleOffsets[index].y, + vehicle->z + SteamParticleOffsets[index].z); } - if (vehicle->vehicle_sprite_type == 6) - { - index += 32; - } - steam_particle_create( - vehicle->x + SteamParticleOffsets[index].x, vehicle->y + SteamParticleOffsets[index].y, - vehicle->z + SteamParticleOffsets[index].z); } } - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } break; case VEHICLE_ENTRY_ANIMATION_SWAN: // loc_6D6424 @@ -7286,7 +7275,7 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) if (vehicle->animation_frame != al) { vehicle->animation_frame = al; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } break; case VEHICLE_ENTRY_ANIMATION_CANOES: // loc_6D6482 @@ -7296,7 +7285,7 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) if (vehicle->animation_frame != ah) { vehicle->animation_frame = ah; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } break; case VEHICLE_ENTRY_ANIMATION_ROW_BOATS: // loc_6D64F7 @@ -7306,7 +7295,7 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) if (vehicle->animation_frame != ah) { vehicle->animation_frame = ah; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } break; case VEHICLE_ENTRY_ANIMATION_WATER_TRICYCLES: // loc_6D6453 @@ -7315,7 +7304,7 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) if (vehicle->animation_frame != al) { vehicle->animation_frame = al; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } break; case VEHICLE_ENTRY_ANIMATION_OBSERVATION_TOWER: // loc_6D65C3 @@ -7328,7 +7317,7 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) vehicle->var_C8 += 0x3333; vehicle->animation_frame += 1; vehicle->animation_frame &= 7; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } break; case VEHICLE_ENTRY_ANIMATION_HELICARS: // loc_6D63F5 @@ -7337,7 +7326,7 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) if (vehicle->animation_frame != al) { vehicle->animation_frame = al; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } break; case VEHICLE_ENTRY_ANIMATION_MONORAIL_CYCLES: // loc_6D64B6 @@ -7349,7 +7338,7 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) if (vehicle->animation_frame != ah) { vehicle->animation_frame = ah; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } } break; @@ -7371,7 +7360,7 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) vehicle->seat_rotation++; vehicle->animation_frame = (vehicle->seat_rotation - 4) & 7; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } } break; @@ -7382,16 +7371,16 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) * * rct2: 0x006DEDB1 */ -static void vehicle_play_scenery_door_open_sound(rct_vehicle* vehicle, TileElement* tileElement) +static void vehicle_play_scenery_door_open_sound(rct_vehicle* vehicle, WallElement* tileElement) { - rct_scenery_entry* wallEntry = tileElement->AsWall()->GetEntry(); + rct_scenery_entry* wallEntry = tileElement->GetEntry(); int32_t doorSoundType = wall_entry_get_door_sound(wallEntry); if (doorSoundType != 0) { - int32_t soundId = DoorOpenSoundIds[doorSoundType - 1]; - if (soundId != 255) + auto soundId = DoorOpenSoundIds[doorSoundType - 1]; + if (soundId != SoundId::Null) { - audio_play_sound_at_location(soundId, vehicle->x, vehicle->track_y, vehicle->track_z); + audio_play_sound_at_location(soundId, { vehicle->track_x, vehicle->track_y, vehicle->track_z }); } } } @@ -7400,16 +7389,16 @@ static void vehicle_play_scenery_door_open_sound(rct_vehicle* vehicle, TileEleme * * rct2: 0x006DED7A */ -static void vehicle_play_scenery_door_close_sound(rct_vehicle* vehicle, TileElement* tileElement) +static void vehicle_play_scenery_door_close_sound(rct_vehicle* vehicle, WallElement* tileElement) { - rct_scenery_entry* wallEntry = tileElement->AsWall()->GetEntry(); + rct_scenery_entry* wallEntry = tileElement->GetEntry(); int32_t doorSoundType = wall_entry_get_door_sound(wallEntry); if (doorSoundType != 0) { - int32_t soundId = DoorCloseSoundIds[doorSoundType - 1]; - if (soundId != 255) + auto soundId = DoorCloseSoundIds[doorSoundType - 1]; + if (soundId != SoundId::Null) { - audio_play_sound_at_location(soundId, vehicle->x, vehicle->track_y, vehicle->track_z); + audio_play_sound_at_location(soundId, { vehicle->track_x, vehicle->track_y, vehicle->track_z }); } } } @@ -7432,23 +7421,24 @@ static void vehicle_update_scenery_door(rct_vehicle* vehicle) int32_t z = (vehicle->track_z - trackBlock->z + trackCoordinates->z_end) >> 3; int32_t direction = (vehicle->track_direction + trackCoordinates->rotation_end) & 3; - TileElement* tileElement = map_get_wall_element_at(x, y, z, direction); + auto tileElement = map_get_wall_element_at(x, y, z, direction); if (tileElement == nullptr) { return; } - if (vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) + if ((vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) && (tileElement->GetAnimationFrame() == 0)) { - tileElement->AsWall()->SetAnimationIsBackwards(false); - tileElement->AsWall()->SetAnimationFrame(1); + tileElement->SetAnimationIsBackwards(false); + tileElement->SetAnimationFrame(1); map_animation_create(MAP_ANIMATION_TYPE_WALL_DOOR, x, y, z); vehicle_play_scenery_door_open_sound(vehicle, tileElement); } - else + + if (vehicle->next_vehicle_on_train == SPRITE_INDEX_NULL) { - tileElement->AsWall()->SetAnimationIsBackwards(false); - tileElement->AsWall()->SetAnimationFrame(6); + tileElement->SetAnimationIsBackwards(false); + tileElement->SetAnimationFrame(6); vehicle_play_scenery_door_close_sound(vehicle, tileElement); } } @@ -7512,23 +7502,24 @@ static void vehicle_update_handle_scenery_door(rct_vehicle* vehicle) int32_t direction = (vehicle->track_direction + trackCoordinates->rotation_begin) & 3; direction = direction_reverse(direction); - TileElement* tileElement = map_get_wall_element_at(x, y, z, direction); + auto tileElement = map_get_wall_element_at(x, y, z, direction); if (tileElement == nullptr) { return; } - if (vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) + if ((vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) && (tileElement->GetAnimationFrame() == 0)) { - tileElement->AsWall()->SetAnimationIsBackwards(true); - tileElement->AsWall()->SetAnimationFrame(1); + tileElement->SetAnimationIsBackwards(true); + tileElement->SetAnimationFrame(1); map_animation_create(MAP_ANIMATION_TYPE_WALL_DOOR, x, y, z); vehicle_play_scenery_door_open_sound(vehicle, tileElement); } - else + + if (vehicle->next_vehicle_on_train == SPRITE_INDEX_NULL) { - tileElement->AsWall()->SetAnimationIsBackwards(true); - tileElement->AsWall()->SetAnimationFrame(6); + tileElement->SetAnimationIsBackwards(true); + tileElement->SetAnimationFrame(6); vehicle_play_scenery_door_close_sound(vehicle, tileElement); } } @@ -7540,7 +7531,7 @@ static void vehicle_update_play_water_splash_sound() return; } - audio_play_sound_at_location(SOUND_WATER_SPLASH, unk_F64E20.x, unk_F64E20.y, unk_F64E20.z); + audio_play_sound_at_location(SoundId::WaterSplash, { unk_F64E20.x, unk_F64E20.y, unk_F64E20.z }); } /** @@ -7661,10 +7652,7 @@ static bool vehicle_update_motion_collision_detection( return false; uint8_t direction = (vehicle->sprite_direction - collideVehicle->sprite_direction + 7) & 0x1F; - if (direction >= 0xF) - return false; - - return true; + return direction < 0xF; } LocationXY8 location = { static_cast(x / 32), static_cast(y / 32) }; @@ -7746,13 +7734,13 @@ static bool vehicle_update_motion_collision_detection( break; } } - if (mayCollide == true) + if (mayCollide) { break; } } - if (mayCollide == false) + if (!mayCollide) { vehicle->var_C4 = 0; return false; @@ -7958,7 +7946,8 @@ static bool vehicle_update_track_motion_forwards_get_new_track( { if (!(rideEntry->vehicles[0].flags & VEHICLE_ENTRY_FLAG_POWERED)) { - audio_play_sound_at_location(SOUND_49, vehicle->track_x, vehicle->track_y, vehicle->track_z); + audio_play_sound_at_location( + SoundId::BlockBrakeRelease, { vehicle->track_x, vehicle->track_y, vehicle->track_z }); } } map_invalidate_element(vehicle->track_x, vehicle->track_z, tileElement); @@ -8110,10 +8099,14 @@ loc_6DB41D: vehicle_trigger_on_ride_photo(vehicle, tileElement); } { - uint16_t rideType = get_ride(tileElement->AsTrack()->GetRideIndex())->type; - if (trackType == TRACK_ELEM_ROTATION_CONTROL_TOGGLE && rideType == RIDE_TYPE_STEEL_WILD_MOUSE) + ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + if (ride != nullptr) { - vehicle->update_flags ^= VEHICLE_UPDATE_FLAG_ROTATION_OFF_WILD_MOUSE; + uint16_t rideType = ride->type; + if (trackType == TRACK_ELEM_ROTATION_CONTROL_TOGGLE && rideType == RIDE_TYPE_STEEL_WILD_MOUSE) + { + vehicle->update_flags ^= VEHICLE_UPDATE_FLAG_ROTATION_OFF_WILD_MOUSE; + } } } // Change from original: this used to check if the vehicle allowed doors. @@ -8165,7 +8158,7 @@ loc_6DAEB9: if (_vehicleF64E2C == 0) { _vehicleF64E2C++; - audio_play_sound_at_location(SOUND_51, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::BrakeRelease, { vehicle->x, vehicle->y, vehicle->z }); } } } @@ -8398,7 +8391,7 @@ static bool vehicle_update_track_motion_backwards_get_new_track( break; } - if (nextTileBackwards == true) + if (nextTileBackwards) { // loc_6DBB7E:; track_begin_end trackBeginEnd; @@ -8688,7 +8681,10 @@ static int32_t vehicle_update_track_motion_mini_golf(rct_vehicle* vehicle, int32 { registers regs = {}; - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return 0; + rct_ride_entry* rideEntry = get_ride_entry(vehicle->ride_subtype); rct_ride_entry_vehicle* vehicleEntry = vehicle_get_vehicle_entry(vehicle); @@ -8722,7 +8718,7 @@ loc_6DC40E: unk_F64E20.x = vehicle->x; unk_F64E20.y = vehicle->y; unk_F64E20.z = vehicle->z; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); loc_6DC462: if (vehicle->var_D3 == 0) @@ -9066,7 +9062,7 @@ loc_6DCA7A: unk_F64E20.x = vehicle->x; unk_F64E20.y = vehicle->y; unk_F64E20.z = vehicle->z; - vehicle_invalidate(vehicle); + vehicle->Invalidate(); loc_6DCA9A: regs.ax = vehicle->track_progress - 1; @@ -9231,7 +9227,7 @@ loc_6DCD6B: loc_6DCDE4: sprite_move(unk_F64E20.x, unk_F64E20.y, unk_F64E20.z, (rct_sprite*)vehicle); - vehicle_invalidate(vehicle); + vehicle->Invalidate(); loc_6DCE02: vehicle->acceleration /= _vehicleUnkF64E10; @@ -9552,7 +9548,10 @@ int32_t vehicle_update_track_motion(rct_vehicle* vehicle, int32_t* outStation) { registers regs = {}; - Ride* ride = get_ride(vehicle->ride); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return 0; + rct_ride_entry* rideEntry = get_ride_entry(vehicle->ride_subtype); rct_ride_entry_vehicle* vehicleEntry = vehicle_get_vehicle_entry(vehicle); @@ -9758,17 +9757,14 @@ int32_t vehicle_update_track_motion(rct_vehicle* vehicle, int32_t* outStation) { vehicle_update_track_motion_powered_ride_acceleration(vehicle, vehicleEntry, totalMass, &acceleration); } - else + else if (acceleration <= 0 && acceleration >= -500) { - if (acceleration <= 0) + // Probably moving slowly on a flat track piece, low rolling resistance and drag. + + if (vehicle->velocity <= 0x8000 && vehicle->velocity >= 0) { - if (acceleration >= -500) - { - if (vehicle->velocity <= 0x8000) - { - acceleration += 400; - } - } + // Vehicle is creeping forwards very slowly (less than ~2km/h), boost speed a bit. + acceleration += 400; } } @@ -9891,17 +9887,19 @@ void vehicle_update_crossings(const rct_vehicle* vehicle) while (true) { - TileElement* tileElement = map_get_path_element_at( - xyElement.x / 32, xyElement.y / 32, xyElement.element->base_height); + auto* pathElement = map_get_path_element_at({ xyElement.x / 32, xyElement.y / 32, xyElement.element->base_height }); + auto ride = get_ride(vehicle->ride); - if (tileElement) + // Many New Element parks have invisible rides hacked into the path. + // Limit path blocking to Miniature Railway to prevent peeps getting stuck everywhere. + if (pathElement && ride != nullptr && ride->type == RIDE_TYPE_MINIATURE_RAILWAY) { - if (!playedClaxon && !tileElement->AsPath()->IsBlockedByVehicle()) + if (!playedClaxon && !pathElement->IsBlockedByVehicle()) { vehicle_claxon(vehicle); } crossingBonus = 4; - tileElement->AsPath()->SetIsBlockedByVehicle(true); + pathElement->SetIsBlockedByVehicle(true); } else { @@ -9964,11 +9962,10 @@ void vehicle_update_crossings(const rct_vehicle* vehicle) } } - TileElement* tileElement = map_get_path_element_at( - xyElement.x / 32, xyElement.y / 32, xyElement.element->base_height); - if (tileElement) + auto* pathElement = map_get_path_element_at({ xyElement.x / 32, xyElement.y / 32, xyElement.element->base_height }); + if (pathElement) { - tileElement->AsPath()->SetIsBlockedByVehicle(false); + pathElement->SetIsBlockedByVehicle(false); } } } @@ -9980,10 +9977,10 @@ void vehicle_claxon(const rct_vehicle* vehicle) switch (rideEntry->vehicles[vehicle->vehicle_type].sound_range) { case SOUND_RANGE_WHISTLE: - audio_play_sound_at_location(SOUND_TRAIN_WHISTLE, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::TrainWhistle, { vehicle->x, vehicle->y, vehicle->z }); break; case SOUND_RANGE_BELL: - audio_play_sound_at_location(SOUND_TRAM, vehicle->x, vehicle->y, vehicle->z); + audio_play_sound_at_location(SoundId::Tram, { vehicle->x, vehicle->y, vehicle->z }); break; } } @@ -10012,3 +10009,16 @@ const rct_vehicle* rct_vehicle::GetCar(size_t carIndex) const } return car; } + +void rct_vehicle::SetState(VEHICLE_STATUS vehicleStatus, uint8_t subState) +{ + status = vehicleStatus; + sub_state = subState; + vehicle_invalidate_window(this); +} + +bool rct_vehicle::IsGhost() const +{ + auto r = get_ride(ride); + return r != nullptr && r->status == RIDE_STATUS_SIMULATING; +} diff --git a/src/openrct2/ride/Vehicle.h b/src/openrct2/ride/Vehicle.h index f1b91d0f05..adc77a91c5 100644 --- a/src/openrct2/ride/Vehicle.h +++ b/src/openrct2/ride/Vehicle.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -19,6 +19,8 @@ #include #include +enum class SoundId : uint8_t; + struct rct_vehicle_colour { uint8_t body_colour; @@ -79,7 +81,7 @@ struct rct_ride_entry_vehicle uint8_t no_seating_rows; // 0x54 , 0x6E uint8_t spinning_inertia; // 0x55 , 0x6F uint8_t spinning_friction; // 0x56 , 0x70 - uint8_t friction_sound_id; // 0x57 , 0x71 + SoundId friction_sound_id; // 0x57 , 0x71 uint8_t log_flume_reverser_vehicle_type; // 0x58 , 0x72 uint8_t sound_range; // 0x59 , 0x73 uint8_t @@ -111,6 +113,41 @@ enum VEHICLE_TYPE : uint8_t VEHICLE_TYPE_TAIL = 1, }; +enum VEHICLE_STATUS +{ + VEHICLE_STATUS_MOVING_TO_END_OF_STATION, + VEHICLE_STATUS_WAITING_FOR_PASSENGERS, + VEHICLE_STATUS_WAITING_TO_DEPART, + VEHICLE_STATUS_DEPARTING, + VEHICLE_STATUS_TRAVELLING, + VEHICLE_STATUS_ARRIVING, + VEHICLE_STATUS_UNLOADING_PASSENGERS, + VEHICLE_STATUS_TRAVELLING_BOAT, + VEHICLE_STATUS_CRASHING, + VEHICLE_STATUS_CRASHED, + VEHICLE_STATUS_TRAVELLING_DODGEMS, + VEHICLE_STATUS_SWINGING, + VEHICLE_STATUS_ROTATING, + VEHICLE_STATUS_FERRIS_WHEEL_ROTATING, + VEHICLE_STATUS_SIMULATOR_OPERATING, + VEHICLE_STATUS_SHOWING_FILM, + VEHICLE_STATUS_SPACE_RINGS_OPERATING, + VEHICLE_STATUS_TOP_SPIN_OPERATING, + VEHICLE_STATUS_HAUNTED_HOUSE_OPERATING, + VEHICLE_STATUS_DOING_CIRCUS_SHOW, + VEHICLE_STATUS_CROOKED_HOUSE_OPERATING, + VEHICLE_STATUS_WAITING_FOR_CABLE_LIFT, + VEHICLE_STATUS_TRAVELLING_CABLE_LIFT, + VEHICLE_STATUS_STOPPING, + VEHICLE_STATUS_WAITING_FOR_PASSENGERS_17, + VEHICLE_STATUS_WAITING_TO_START, + VEHICLE_STATUS_STARTING, + VEHICLE_STATUS_OPERATING_1A, + VEHICLE_STATUS_STOPPING_1B, + VEHICLE_STATUS_UNLOADING_PASSENGERS_1C, + VEHICLE_STATUS_STOPPED_BY_BLOCK_BRAKES +}; + struct rct_vehicle : rct_sprite_common { uint8_t vehicle_sprite_type; // 0x1F @@ -167,7 +204,7 @@ struct rct_vehicle : rct_sprite_common int16_t var_4E; int16_t crash_z; // 0x4E }; - uint8_t status; // 0x50 + VEHICLE_STATUS status; // 0x50 uint8_t sub_state; // 0x51 uint16_t peep[32]; // 0x52 uint8_t peep_tshirt_colours[32]; // 0x92 @@ -182,9 +219,9 @@ struct rct_vehicle : rct_sprite_common }; uint16_t sound2_flags; // 0xB8 uint8_t spin_sprite; // 0xBA lowest 3 bits not used for sprite selection (divide by 8 to use) - uint8_t sound1_id; // 0xBB + SoundId sound1_id; // 0xBB uint8_t sound1_volume; // 0xBC - uint8_t sound2_id; // 0xBD + SoundId sound2_id; // 0xBD uint8_t sound2_volume; // 0xBE int8_t sound_vector_factor; union @@ -205,7 +242,7 @@ struct rct_vehicle : rct_sprite_common uint8_t pad_C6[0x2]; uint16_t var_C8; uint16_t var_CA; - uint8_t scream_sound_id; // 0xCC + SoundId scream_sound_id; // 0xCC uint8_t var_CD; union { @@ -234,6 +271,9 @@ struct rct_vehicle : rct_sprite_common rct_vehicle* GetHead(); const rct_vehicle* GetHead() const; const rct_vehicle* GetCar(size_t carIndex) const; + void Invalidate(); + void SetState(VEHICLE_STATUS vehicleStatus, uint8_t subState = 0); + bool IsGhost() const; }; struct train_ref @@ -313,41 +353,6 @@ enum VEHICLE_ENTRY_ANIMATION_MULTI_DIM_COASTER }; -enum -{ - VEHICLE_STATUS_MOVING_TO_END_OF_STATION, - VEHICLE_STATUS_WAITING_FOR_PASSENGERS, - VEHICLE_STATUS_WAITING_TO_DEPART, - VEHICLE_STATUS_DEPARTING, - VEHICLE_STATUS_TRAVELLING, - VEHICLE_STATUS_ARRIVING, - VEHICLE_STATUS_UNLOADING_PASSENGERS, - VEHICLE_STATUS_TRAVELLING_BOAT, - VEHICLE_STATUS_CRASHING, - VEHICLE_STATUS_CRASHED, - VEHICLE_STATUS_TRAVELLING_DODGEMS, - VEHICLE_STATUS_SWINGING, - VEHICLE_STATUS_ROTATING, - VEHICLE_STATUS_FERRIS_WHEEL_ROTATING, - VEHICLE_STATUS_SIMULATOR_OPERATING, - VEHICLE_STATUS_SHOWING_FILM, - VEHICLE_STATUS_SPACE_RINGS_OPERATING, - VEHICLE_STATUS_TOP_SPIN_OPERATING, - VEHICLE_STATUS_HAUNTED_HOUSE_OPERATING, - VEHICLE_STATUS_DOING_CIRCUS_SHOW, - VEHICLE_STATUS_CROOKED_HOUSE_OPERATING, - VEHICLE_STATUS_WAITING_FOR_CABLE_LIFT, - VEHICLE_STATUS_TRAVELLING_CABLE_LIFT, - VEHICLE_STATUS_STOPPING, - VEHICLE_STATUS_WAITING_FOR_PASSENGERS_17, - VEHICLE_STATUS_WAITING_TO_START, - VEHICLE_STATUS_STARTING, - VEHICLE_STATUS_OPERATING_1A, - VEHICLE_STATUS_STOPPING_1B, - VEHICLE_STATUS_UNLOADING_PASSENGERS_1C, - VEHICLE_STATUS_STOPPED_BY_BLOCK_BRAKES -}; - enum : uint32_t { VEHICLE_UPDATE_FLAG_ON_LIFT_HILL = (1 << 0), @@ -455,10 +460,16 @@ enum #define VEHICLE_SEAT_PAIR_FLAG 0x80 #define VEHICLE_SEAT_NUM_MASK 0x7F +struct GForces +{ + int32_t VerticalG{}; + int32_t LateralG{}; +}; + rct_vehicle* try_get_vehicle(uint16_t spriteIndex); void vehicle_update_all(); void vehicle_sounds_update(); -void vehicle_get_g_forces(const rct_vehicle* vehicle, int32_t* verticalG, int32_t* lateralG); +GForces vehicle_get_g_forces(const rct_vehicle* vehicle); void vehicle_set_map_toolbar(const rct_vehicle* vehicle); int32_t vehicle_is_used_in_pairs(const rct_vehicle* vehicle); int32_t vehicle_update_track_motion(rct_vehicle* vehicle, int32_t* outStation); diff --git a/src/openrct2/ride/VehicleData.cpp b/src/openrct2/ride/VehicleData.cpp index 9d4b5ad81d..1412370290 100644 --- a/src/openrct2/ride/VehicleData.cpp +++ b/src/openrct2/ride/VehicleData.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/VehicleData.h b/src/openrct2/ride/VehicleData.h index 3450a42537..9a4dd6f13c 100644 --- a/src/openrct2/ride/VehicleData.h +++ b/src/openrct2/ride/VehicleData.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/VehiclePaint.cpp b/src/openrct2/ride/VehiclePaint.cpp index e18f6af50c..c4f37d73f8 100644 --- a/src/openrct2/ride/VehiclePaint.cpp +++ b/src/openrct2/ride/VehiclePaint.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -909,13 +909,19 @@ static void vehicle_sprite_paint( } int32_t image_id = baseImage_id | (vehicle->colours.body_colour << 19) | (vehicle->colours.trim_colour << 24) | IMAGE_TYPE_REMAP_2_PLUS; + + if (vehicle->IsGhost()) + { + image_id &= 0x7FFFF; + image_id |= CONSTRUCTION_MARKER; + } paint_struct* ps = sub_98197C( session, image_id, 0, 0, bb.length_x, bb.length_y, bb.length_z, z, bb.offset_x, bb.offset_y, bb.offset_z + z); if (ps != nullptr) { ps->tertiary_colour = vehicle->colours_extended; } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level < 2 && vehicle->num_peeps > 0 && vehicleEntry->no_seating_rows > 0) { baseImage_id += vehicleEntry->no_vehicle_images; @@ -930,6 +936,13 @@ static void vehicle_sprite_paint( { image_id += (vehicleEntry->no_vehicle_images * vehicle->animation_frame); } + + if (vehicle->IsGhost()) + { + image_id &= 0x7FFFF; + image_id |= CONSTRUCTION_MARKER; + } + sub_98199C( session, image_id, 0, 0, bb.length_x, bb.length_y, bb.length_z, z, bb.offset_x, bb.offset_y, bb.offset_z + z); diff --git a/src/openrct2/ride/VehiclePaint.h b/src/openrct2/ride/VehiclePaint.h index 7047f4094d..af014e846d 100644 --- a/src/openrct2/ride/VehiclePaint.h +++ b/src/openrct2/ride/VehiclePaint.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/AirPoweredVerticalCoaster.cpp b/src/openrct2/ride/coaster/AirPoweredVerticalCoaster.cpp index 71b4f781ca..99047ba44a 100644 --- a/src/openrct2/ride/coaster/AirPoweredVerticalCoaster.cpp +++ b/src/openrct2/ride/coaster/AirPoweredVerticalCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -216,8 +216,9 @@ static void air_powered_vertical_rc_track_station( wooden_a_supports_paint_setup(session, direction & 1, 0, height, session->TrackColours[SCHEME_SUPPORTS], nullptr); - Ride* ride = get_ride(rideIndex); - track_paint_util_draw_station_platform(session, ride, direction, height, 5, tileElement); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + track_paint_util_draw_station_platform(session, ride, direction, height, 5, tileElement); paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_6); diff --git a/src/openrct2/ride/coaster/BobsleighCoaster.cpp b/src/openrct2/ride/coaster/BobsleighCoaster.cpp index 1e1a1f612d..77daced4b8 100644 --- a/src/openrct2/ride/coaster/BobsleighCoaster.cpp +++ b/src/openrct2/ride/coaster/BobsleighCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/BolligerMabillardTrack.cpp b/src/openrct2/ride/coaster/BolligerMabillardTrack.cpp index f77723c405..680a35b980 100644 --- a/src/openrct2/ride/coaster/BolligerMabillardTrack.cpp +++ b/src/openrct2/ride/coaster/BolligerMabillardTrack.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -105,8 +105,9 @@ void bolliger_mabillard_track_station( track_paint_util_draw_station_metal_supports_2( session, direction, height, session->TrackColours[SCHEME_SUPPORTS], supportType); - Ride* ride = get_ride(rideIndex); - track_paint_util_draw_station_platform(session, ride, direction, height, 9, tileElement); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + track_paint_util_draw_station_platform(session, ride, direction, height, 9, tileElement); paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_6); paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0); diff --git a/src/openrct2/ride/coaster/BolligerMabillardTrack.h b/src/openrct2/ride/coaster/BolligerMabillardTrack.h index 15de4bbc93..2b7e54eb8a 100644 --- a/src/openrct2/ride/coaster/BolligerMabillardTrack.h +++ b/src/openrct2/ride/coaster/BolligerMabillardTrack.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/CompactInvertedCoaster.cpp b/src/openrct2/ride/coaster/CompactInvertedCoaster.cpp index 8d69939df0..48387220e3 100644 --- a/src/openrct2/ride/coaster/CompactInvertedCoaster.cpp +++ b/src/openrct2/ride/coaster/CompactInvertedCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/CorkscrewRollerCoaster.cpp b/src/openrct2/ride/coaster/CorkscrewRollerCoaster.cpp index 8474f25652..20e829c116 100644 --- a/src/openrct2/ride/coaster/CorkscrewRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/CorkscrewRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/FlyingRollerCoaster.cpp b/src/openrct2/ride/coaster/FlyingRollerCoaster.cpp index 060637a0c1..1df9e34be4 100644 --- a/src/openrct2/ride/coaster/FlyingRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/FlyingRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/GigaCoaster.cpp b/src/openrct2/ride/coaster/GigaCoaster.cpp index da9f7910e4..13c5017238 100644 --- a/src/openrct2/ride/coaster/GigaCoaster.cpp +++ b/src/openrct2/ride/coaster/GigaCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/HeartlineTwisterCoaster.cpp b/src/openrct2/ride/coaster/HeartlineTwisterCoaster.cpp index 81874a9532..8da23bbb45 100644 --- a/src/openrct2/ride/coaster/HeartlineTwisterCoaster.cpp +++ b/src/openrct2/ride/coaster/HeartlineTwisterCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/InvertedHairpinCoaster.cpp b/src/openrct2/ride/coaster/InvertedHairpinCoaster.cpp index 58eb37a15b..2809460bac 100644 --- a/src/openrct2/ride/coaster/InvertedHairpinCoaster.cpp +++ b/src/openrct2/ride/coaster/InvertedHairpinCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/InvertedImpulseCoaster.cpp b/src/openrct2/ride/coaster/InvertedImpulseCoaster.cpp index 89c824165f..084311d291 100644 --- a/src/openrct2/ride/coaster/InvertedImpulseCoaster.cpp +++ b/src/openrct2/ride/coaster/InvertedImpulseCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/InvertedRollerCoaster.cpp b/src/openrct2/ride/coaster/InvertedRollerCoaster.cpp index 0691971476..9920c198ee 100644 --- a/src/openrct2/ride/coaster/InvertedRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/InvertedRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/JuniorRollerCoaster.cpp b/src/openrct2/ride/coaster/JuniorRollerCoaster.cpp index 5ed48af14a..60948f7c19 100644 --- a/src/openrct2/ride/coaster/JuniorRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/JuniorRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/JuniorRollerCoaster.h b/src/openrct2/ride/coaster/JuniorRollerCoaster.h index fea7b0b74f..3b7862034a 100644 --- a/src/openrct2/ride/coaster/JuniorRollerCoaster.h +++ b/src/openrct2/ride/coaster/JuniorRollerCoaster.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/LayDownRollerCoaster.cpp b/src/openrct2/ride/coaster/LayDownRollerCoaster.cpp index 6dd2782cd6..4add9c771a 100644 --- a/src/openrct2/ride/coaster/LayDownRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/LayDownRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/LimLaunchedRollerCoaster.cpp b/src/openrct2/ride/coaster/LimLaunchedRollerCoaster.cpp index 58657d786d..2398971859 100644 --- a/src/openrct2/ride/coaster/LimLaunchedRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/LimLaunchedRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/LoopingRollerCoaster.cpp b/src/openrct2/ride/coaster/LoopingRollerCoaster.cpp index dcb9daa0c3..367d3b55ec 100644 --- a/src/openrct2/ride/coaster/LoopingRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/LoopingRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/MineRide.cpp b/src/openrct2/ride/coaster/MineRide.cpp index e9db81fc9a..1cd5eb79bb 100644 --- a/src/openrct2/ride/coaster/MineRide.cpp +++ b/src/openrct2/ride/coaster/MineRide.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/MineTrainCoaster.cpp b/src/openrct2/ride/coaster/MineTrainCoaster.cpp index 096f52b0a4..663647dd99 100644 --- a/src/openrct2/ride/coaster/MineTrainCoaster.cpp +++ b/src/openrct2/ride/coaster/MineTrainCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/MiniRollerCoaster.cpp b/src/openrct2/ride/coaster/MiniRollerCoaster.cpp index e87ab28589..ffcb2229a7 100644 --- a/src/openrct2/ride/coaster/MiniRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/MiniRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/MiniSuspendedCoaster.cpp b/src/openrct2/ride/coaster/MiniSuspendedCoaster.cpp index f8e160374d..727c2e719d 100644 --- a/src/openrct2/ride/coaster/MiniSuspendedCoaster.cpp +++ b/src/openrct2/ride/coaster/MiniSuspendedCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/MultiDimensionRollerCoaster.cpp b/src/openrct2/ride/coaster/MultiDimensionRollerCoaster.cpp index ed19156cd7..7cfdc2939c 100644 --- a/src/openrct2/ride/coaster/MultiDimensionRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/MultiDimensionRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -131,29 +131,32 @@ static void multi_dimension_rc_track_station( } track_paint_util_draw_station_metal_supports_2(session, direction, height, session->TrackColours[SCHEME_SUPPORTS], 11); - Ride* ride = get_ride(rideIndex); - auto stationObj = ride_get_station_object(ride); - bool hasFence; - if (direction == 0 || direction == 2) + auto ride = get_ride(rideIndex); + if (ride != nullptr) { - hasFence = track_paint_util_has_fence(EDGE_NW, position, tileElement, ride, session->CurrentRotation); - track_paint_util_draw_station_covers(session, EDGE_NW, hasFence, stationObj, height); - } - else - { - hasFence = track_paint_util_has_fence(EDGE_NE, position, tileElement, ride, session->CurrentRotation); - track_paint_util_draw_station_covers(session, EDGE_NE, hasFence, stationObj, height); - } + auto stationObj = ride_get_station_object(ride); + bool hasFence; + if (direction == 0 || direction == 2) + { + hasFence = track_paint_util_has_fence(EDGE_NW, position, tileElement, ride, session->CurrentRotation); + track_paint_util_draw_station_covers(session, EDGE_NW, hasFence, stationObj, height); + } + else + { + hasFence = track_paint_util_has_fence(EDGE_NE, position, tileElement, ride, session->CurrentRotation); + track_paint_util_draw_station_covers(session, EDGE_NE, hasFence, stationObj, height); + } - if (direction == 0 || direction == 2) - { - hasFence = track_paint_util_has_fence(EDGE_SE, position, tileElement, ride, session->CurrentRotation); - track_paint_util_draw_station_covers(session, EDGE_SE, hasFence, stationObj, height); - } - else - { - hasFence = track_paint_util_has_fence(EDGE_SW, position, tileElement, ride, session->CurrentRotation); - track_paint_util_draw_station_covers(session, EDGE_SW, hasFence, stationObj, height); + if (direction == 0 || direction == 2) + { + hasFence = track_paint_util_has_fence(EDGE_SE, position, tileElement, ride, session->CurrentRotation); + track_paint_util_draw_station_covers(session, EDGE_SE, hasFence, stationObj, height); + } + else + { + hasFence = track_paint_util_has_fence(EDGE_SW, position, tileElement, ride, session->CurrentRotation); + track_paint_util_draw_station_covers(session, EDGE_SW, hasFence, stationObj, height); + } } paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_6); @@ -12935,7 +12938,7 @@ static void multi_dimension_rc_track_multidim_flat_to_90_deg_down_quarter_loop( } /** rct2: 0x00793388 */ -static void multi_dimension_rc_track_elem_255( +static void multi_dimension_rc_track_multidim_inverted_90_deg_up_to_flat_quarter_loop( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { @@ -13329,8 +13332,8 @@ TRACK_PAINT_FUNCTION get_track_paint_function_multi_dimension_rc(int32_t trackTy return multi_dimension_rc_track_multidim_90_deg_up_to_inverted_flat_quarter_loop; case TRACK_ELEM_MULTIDIM_FLAT_TO_90_DEG_DOWN_QUARTER_LOOP: return multi_dimension_rc_track_multidim_flat_to_90_deg_down_quarter_loop; - case TRACK_ELEM_255: - return multi_dimension_rc_track_elem_255; + case TRACK_ELEM_MULTIDIM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP: + return multi_dimension_rc_track_multidim_inverted_90_deg_up_to_flat_quarter_loop; } return nullptr; } diff --git a/src/openrct2/ride/coaster/ReverseFreefallCoaster.cpp b/src/openrct2/ride/coaster/ReverseFreefallCoaster.cpp index cec6c2b122..d9405be1d2 100644 --- a/src/openrct2/ride/coaster/ReverseFreefallCoaster.cpp +++ b/src/openrct2/ride/coaster/ReverseFreefallCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -219,7 +219,6 @@ static void paint_reverse_freefall_rc_station( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { - Ride* ride = get_ride(rideIndex); uint32_t imageId; if (direction == 0 || direction == 2) @@ -251,7 +250,9 @@ static void paint_reverse_freefall_rc_station( paint_util_push_tunnel_right(session, height, TUNNEL_6); } - track_paint_util_draw_station_platform(session, ride, direction, height, 5, tileElement); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + track_paint_util_draw_station_platform(session, ride, direction, height, 5, tileElement); paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0); paint_util_set_general_support_height(session, height + 32, 0x20); diff --git a/src/openrct2/ride/coaster/ReverserRollerCoaster.cpp b/src/openrct2/ride/coaster/ReverserRollerCoaster.cpp index 54c66923ce..4c0af567c0 100644 --- a/src/openrct2/ride/coaster/ReverserRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/ReverserRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/SideFrictionRollerCoaster.cpp b/src/openrct2/ride/coaster/SideFrictionRollerCoaster.cpp index 3ed6ac7a35..2b8524524f 100644 --- a/src/openrct2/ride/coaster/SideFrictionRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/SideFrictionRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/StandUpRollerCoaster.cpp b/src/openrct2/ride/coaster/StandUpRollerCoaster.cpp index 8ea706a8d5..91be57b06f 100644 --- a/src/openrct2/ride/coaster/StandUpRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/StandUpRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/Steeplechase.cpp b/src/openrct2/ride/coaster/Steeplechase.cpp index 1ca90a00b9..598ff2a7df 100644 --- a/src/openrct2/ride/coaster/Steeplechase.cpp +++ b/src/openrct2/ride/coaster/Steeplechase.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/SuspendedSwingingCoaster.cpp b/src/openrct2/ride/coaster/SuspendedSwingingCoaster.cpp index 845ae71012..9b5bbf824c 100644 --- a/src/openrct2/ride/coaster/SuspendedSwingingCoaster.cpp +++ b/src/openrct2/ride/coaster/SuspendedSwingingCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/TwisterRollerCoaster.cpp b/src/openrct2/ride/coaster/TwisterRollerCoaster.cpp index 7810708812..2fc5809516 100644 --- a/src/openrct2/ride/coaster/TwisterRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/TwisterRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/VerticalDropRollerCoaster.cpp b/src/openrct2/ride/coaster/VerticalDropRollerCoaster.cpp index 00feb366f7..b560c6c41e 100644 --- a/src/openrct2/ride/coaster/VerticalDropRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/VerticalDropRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/VirginiaReel.cpp b/src/openrct2/ride/coaster/VirginiaReel.cpp index b1eefd5025..336a887593 100644 --- a/src/openrct2/ride/coaster/VirginiaReel.cpp +++ b/src/openrct2/ride/coaster/VirginiaReel.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -196,10 +196,14 @@ void vehicle_visual_virginia_reel( const vehicle_boundbox* bb = &_virginiaReelBoundbox[j]; image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour); + if (vehicle->IsGhost()) + { + image_id = (image_id & 0x7FFFF) | CONSTRUCTION_MARKER; + } sub_98197C( session, image_id, 0, 0, bb->length_x, bb->length_y, bb->length_z, z, bb->offset_x, bb->offset_y, bb->offset_z + z); - if (session->DPI->zoom_level < 2 && vehicle->num_peeps > 0) + if (session->DPI.zoom_level < 2 && vehicle->num_peeps > 0 && !vehicle->IsGhost()) { uint8_t riding_peep_sprites[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; for (int32_t i = 0; i < vehicle->num_peeps; i++) diff --git a/src/openrct2/ride/coaster/WildMouse.cpp b/src/openrct2/ride/coaster/WildMouse.cpp index d8dc7bc9d0..2bcb08122d 100644 --- a/src/openrct2/ride/coaster/WildMouse.cpp +++ b/src/openrct2/ride/coaster/WildMouse.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/WoodenRollerCoaster.cpp b/src/openrct2/ride/coaster/WoodenRollerCoaster.cpp index 0a18fac384..bb436de158 100644 --- a/src/openrct2/ride/coaster/WoodenRollerCoaster.cpp +++ b/src/openrct2/ride/coaster/WoodenRollerCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/coaster/WoodenWildMouse.cpp b/src/openrct2/ride/coaster/WoodenWildMouse.cpp index 63fe473d4d..506090199e 100644 --- a/src/openrct2/ride/coaster/WoodenWildMouse.cpp +++ b/src/openrct2/ride/coaster/WoodenWildMouse.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/gentle/CarRide.cpp b/src/openrct2/ride/gentle/CarRide.cpp index 59d34c9c5a..e0629f4ab6 100644 --- a/src/openrct2/ride/gentle/CarRide.cpp +++ b/src/openrct2/ride/gentle/CarRide.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/gentle/CircusShow.cpp b/src/openrct2/ride/gentle/CircusShow.cpp index e20fbb5c56..a31575ac40 100644 --- a/src/openrct2/ride/gentle/CircusShow.cpp +++ b/src/openrct2/ride/gentle/CircusShow.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -22,8 +22,13 @@ static void paint_circus_show_tent( { const TileElement* savedTileElement = static_cast(session->CurrentlyDrawnItem); - Ride* ride = get_ride(rideIndex); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && ride->vehicles[0] != SPRITE_INDEX_NULL) { @@ -54,16 +59,19 @@ static void paint_circus_show( trackSequence = track_map_3x3[direction][trackSequence]; int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_MISC], nullptr); track_paint_util_paint_floor(session, edges, session->TrackColours[SCHEME_TRACK], height, floorSpritesCork); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_SUPPORTS], height, fenceSpritesRope, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_SUPPORTS], height, fenceSpritesRope, + session->CurrentRotation); + } switch (trackSequence) { diff --git a/src/openrct2/ride/gentle/CrookedHouse.cpp b/src/openrct2/ride/gentle/CrookedHouse.cpp index 190d9ed233..4d0e1f8c5b 100644 --- a/src/openrct2/ride/gentle/CrookedHouse.cpp +++ b/src/openrct2/ride/gentle/CrookedHouse.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -36,9 +36,13 @@ static void paint_crooked_house_structure( { const TileElement* original_tile_element = static_cast(session->CurrentlyDrawnItem); - Ride* ride = get_ride(original_tile_element->AsTrack()->GetRideIndex()); + auto ride = get_ride(original_tile_element->AsTrack()->GetRideIndex()); + if (ride == nullptr) + return; - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK) { @@ -65,16 +69,19 @@ static void paint_crooked_house( trackSequence = track_map_3x3[direction][trackSequence]; int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_MISC], nullptr); track_paint_util_paint_floor(session, edges, session->TrackColours[SCHEME_TRACK], height, floorSpritesCork); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_MISC], height, fenceSpritesRope, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_MISC], height, fenceSpritesRope, + session->CurrentRotation); + } switch (trackSequence) { diff --git a/src/openrct2/ride/gentle/Dodgems.cpp b/src/openrct2/ride/gentle/Dodgems.cpp index c5c2fcee97..9603a2cf00 100644 --- a/src/openrct2/ride/gentle/Dodgems.cpp +++ b/src/openrct2/ride/gentle/Dodgems.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -43,7 +43,6 @@ static void paint_dodgems( uint8_t relativeTrackSequence = track_map_4x4[direction][trackSequence]; int32_t edges = edges_4x4[relativeTrackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, direction & 1, 0, height, session->TrackColours[SCHEME_MISC], nullptr); @@ -51,9 +50,13 @@ static void paint_dodgems( uint32_t imageId = SPR_DODGEMS_FLOOR | session->TrackColours[SCHEME_SUPPORTS]; sub_98197C(session, imageId, 0, 0, 30, 30, 1, height, 1, 1, height); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_SUPPORTS], height, dodgems_fence_sprites, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_SUPPORTS], height, dodgems_fence_sprites, + session->CurrentRotation); + } switch (direction) { diff --git a/src/openrct2/ride/gentle/FerrisWheel.cpp b/src/openrct2/ride/gentle/FerrisWheel.cpp index 1bd1c6537b..33438a0e57 100644 --- a/src/openrct2/ride/gentle/FerrisWheel.cpp +++ b/src/openrct2/ride/gentle/FerrisWheel.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -54,15 +54,15 @@ static void paint_ferris_wheel_structure( const TileElement* savedTileElement = static_cast(session->CurrentlyDrawnItem); - Ride* ride = get_ride(rideIndex); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - rct_vehicle* vehicle = nullptr; - if (rideEntry == nullptr) - { - log_error("Error drawing Ferris Wheel, rideEntry is NULL."); + auto ride = get_ride(rideIndex); + if (ride == nullptr) return; - } + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + rct_vehicle* vehicle = nullptr; int8_t xOffset = !(direction & 1) ? axisOffset : 0; int8_t yOffset = (direction & 1) ? axisOffset : 0; @@ -155,7 +155,6 @@ static void paint_ferris_wheel( edges = edges_1x4_ne_sw[relativeTrackSequence]; } - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, direction & 1, 0, height, session->TrackColours[SCHEME_MISC], nullptr); @@ -165,26 +164,30 @@ static void paint_ferris_wheel( uint32_t imageId; uint8_t rotation = session->CurrentRotation; uint32_t colourFlags = session->TrackColours[SCHEME_MISC]; - if (edges & EDGE_NW && track_paint_util_has_fence(EDGE_NW, position, tileElement, ride, rotation)) + auto ride = get_ride(rideIndex); + if (ride != nullptr) { - imageId = SPR_FENCE_ROPE_NW | colourFlags; - sub_98199C(session, imageId, 0, 0, 32, 1, 7, height, 0, 2, height + 2); - } - if (edges & EDGE_NE && track_paint_util_has_fence(EDGE_NE, position, tileElement, ride, rotation)) - { - imageId = SPR_FENCE_ROPE_NE | colourFlags; - sub_98199C(session, imageId, 0, 0, 1, 32, 7, height, 2, 0, height + 2); - } - if (edges & EDGE_SE && track_paint_util_has_fence(EDGE_SE, position, tileElement, ride, rotation)) - { - // Bound box is slightly different from track_paint_util_paint_fences - imageId = SPR_FENCE_ROPE_SE | colourFlags; - sub_98197C(session, imageId, 0, 0, 28, 1, 7, height, 0, 29, height + 3); - } - if (edges & EDGE_SW && track_paint_util_has_fence(EDGE_SW, position, tileElement, ride, rotation)) - { - imageId = SPR_FENCE_ROPE_SW | colourFlags; - sub_98197C(session, imageId, 0, 0, 1, 32, 7, height, 30, 0, height + 2); + if (edges & EDGE_NW && track_paint_util_has_fence(EDGE_NW, position, tileElement, ride, rotation)) + { + imageId = SPR_FENCE_ROPE_NW | colourFlags; + sub_98199C(session, imageId, 0, 0, 32, 1, 7, height, 0, 2, height + 2); + } + if (edges & EDGE_NE && track_paint_util_has_fence(EDGE_NE, position, tileElement, ride, rotation)) + { + imageId = SPR_FENCE_ROPE_NE | colourFlags; + sub_98199C(session, imageId, 0, 0, 1, 32, 7, height, 2, 0, height + 2); + } + if (edges & EDGE_SE && track_paint_util_has_fence(EDGE_SE, position, tileElement, ride, rotation)) + { + // Bound box is slightly different from track_paint_util_paint_fences + imageId = SPR_FENCE_ROPE_SE | colourFlags; + sub_98197C(session, imageId, 0, 0, 28, 1, 7, height, 0, 29, height + 3); + } + if (edges & EDGE_SW && track_paint_util_has_fence(EDGE_SW, position, tileElement, ride, rotation)) + { + imageId = SPR_FENCE_ROPE_SW | colourFlags; + sub_98197C(session, imageId, 0, 0, 1, 32, 7, height, 30, 0, height + 2); + } } switch (relativeTrackSequence) diff --git a/src/openrct2/ride/gentle/FlyingSaucers.cpp b/src/openrct2/ride/gentle/FlyingSaucers.cpp index 57fbec08b9..54c8fca3ff 100644 --- a/src/openrct2/ride/gentle/FlyingSaucers.cpp +++ b/src/openrct2/ride/gentle/FlyingSaucers.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -39,7 +39,6 @@ static void paint_flying_saucers( uint8_t relativeTrackSequence = track_map_4x4[direction][trackSequence]; int32_t edges = edges_4x4[relativeTrackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, direction & 1, 0, height, session->TrackColours[SCHEME_MISC], nullptr); @@ -47,9 +46,13 @@ static void paint_flying_saucers( uint32_t imageId = SPR_FLYING_SAUCERS_FLOOR | session->TrackColours[SCHEME_TRACK]; sub_98197C(session, imageId, 0, 0, 30, 30, 1, height, 1, 1, height); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, flying_saucers_fence_sprites, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, + flying_saucers_fence_sprites, session->CurrentRotation); + } paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0); paint_util_set_general_support_height(session, height + 48, 0x20); diff --git a/src/openrct2/ride/gentle/GhostTrain.cpp b/src/openrct2/ride/gentle/GhostTrain.cpp index b2e061d3dd..023b0c7c05 100644 --- a/src/openrct2/ride/gentle/GhostTrain.cpp +++ b/src/openrct2/ride/gentle/GhostTrain.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/gentle/HauntedHouse.cpp b/src/openrct2/ride/gentle/HauntedHouse.cpp index d8e0a89844..a5b9d9a379 100644 --- a/src/openrct2/ride/gentle/HauntedHouse.cpp +++ b/src/openrct2/ride/gentle/HauntedHouse.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -38,14 +38,14 @@ static void paint_haunted_house_structure( uint8_t frameNum = 0; - Ride* ride = get_ride(rideIndex); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - - if (rideEntry == nullptr) - { - log_error("Error drawing haunted house, rideEntry is NULL."); + auto ride = get_ride(rideIndex); + if (ride == nullptr) return; - } + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + uint32_t baseImageId = rideEntry->vehicles[0].base_image_id; if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && ride->vehicles[0] != SPRITE_INDEX_NULL) @@ -62,7 +62,7 @@ static void paint_haunted_house_structure( session, imageId, xOffset, yOffset, boundBox.length_x, boundBox.length_y, 127, height, boundBox.offset_x, boundBox.offset_y, height); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level == 0 && frameNum != 0) { switch (direction) @@ -100,16 +100,19 @@ static void paint_haunted_house( trackSequence = track_map_3x3[direction][trackSequence]; int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_MISC], nullptr); track_paint_util_paint_floor(session, edges, session->TrackColours[SCHEME_TRACK], height, floorSpritesCork); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_MISC], height, fenceSpritesRope, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_MISC], height, fenceSpritesRope, + session->CurrentRotation); + } switch (trackSequence) { diff --git a/src/openrct2/ride/gentle/Maze.cpp b/src/openrct2/ride/gentle/Maze.cpp index 0d0d1eefef..c09f8eb248 100644 --- a/src/openrct2/ride/gentle/Maze.cpp +++ b/src/openrct2/ride/gentle/Maze.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/gentle/MerryGoRound.cpp b/src/openrct2/ride/gentle/MerryGoRound.cpp index c3fb64540b..e26c80d9f0 100644 --- a/src/openrct2/ride/gentle/MerryGoRound.cpp +++ b/src/openrct2/ride/gentle/MerryGoRound.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -29,15 +29,15 @@ static void paint_merry_go_round_structure( const TileElement* savedTileElement = static_cast(session->CurrentlyDrawnItem); height += 7; - Ride* ride = get_ride(rideIndex); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - rct_vehicle* vehicle = nullptr; - - if (rideEntry == nullptr) - { + auto ride = get_ride(rideIndex); + if (ride == nullptr) return; - } + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + rct_vehicle* vehicle = nullptr; uint32_t baseImageId = rideEntry->vehicles[0].base_image_id; if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && ride->vehicles[0] != SPRITE_INDEX_NULL) @@ -71,7 +71,7 @@ static void paint_merry_go_round_structure( uint32_t imageId = (baseImageId + imageOffset) | imageColourFlags; sub_98197C(session, imageId, xOffset, yOffset, 24, 24, 48, height, xOffset + 16, yOffset + 16, height); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level == 0 && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && vehicle != nullptr) { for (int32_t peep = 0; peep <= 14; peep += 2) @@ -110,16 +110,19 @@ static void paint_merry_go_round( trackSequence = track_map_3x3[direction][trackSequence]; int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_MISC], nullptr); track_paint_util_paint_floor(session, edges, session->TrackColours[SCHEME_TRACK], height, floorSpritesCork); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_MISC], height, fenceSpritesRope, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_MISC], height, fenceSpritesRope, + session->CurrentRotation); + } switch (trackSequence) { diff --git a/src/openrct2/ride/gentle/MiniGolf.cpp b/src/openrct2/ride/gentle/MiniGolf.cpp index a196bda25d..46f86753b5 100644 --- a/src/openrct2/ride/gentle/MiniGolf.cpp +++ b/src/openrct2/ride/gentle/MiniGolf.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -673,8 +673,11 @@ static void paint_mini_golf_station( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + LocationXY16 position = session->MapPosition; - Ride* ride = get_ride(rideIndex); auto stationObj = ride_get_station_object(ride); uint32_t imageId; bool hasFence; @@ -1193,7 +1196,7 @@ void vehicle_visual_mini_golf_player( return; } - rct_drawpixelinfo* edi = session->DPI; + rct_drawpixelinfo* edi = &session->DPI; if (edi->zoom_level >= 2) { return; @@ -1204,7 +1207,14 @@ void vehicle_visual_mini_golf_player( return; } - rct_ride_entry* rideEntry = get_ride_entry(get_ride(vehicle->ride)->subtype); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + rct_sprite* sprite = get_sprite(vehicle->peep[0]); uint8_t frame = mini_golf_peep_animation_frames[vehicle->mini_golf_current_animation][vehicle->animation_frame]; @@ -1226,7 +1236,7 @@ void vehicle_visual_mini_golf_ball( return; } - rct_drawpixelinfo* edi = session->DPI; + rct_drawpixelinfo* edi = &session->DPI; if (edi->zoom_level >= 1) { return; @@ -1237,8 +1247,13 @@ void vehicle_visual_mini_golf_ball( return; } - Ride* ride = get_ride(vehicle->ride); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); + auto ride = get_ride(vehicle->ride); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; uint32_t image_id = rideEntry->vehicles[0].base_image_id; sub_98197C(session, image_id, 0, 0, 1, 1, 0, z, 0, 0, z + 3); diff --git a/src/openrct2/ride/gentle/MiniHelicopters.cpp b/src/openrct2/ride/gentle/MiniHelicopters.cpp index 159129df59..845fe6aaeb 100644 --- a/src/openrct2/ride/gentle/MiniHelicopters.cpp +++ b/src/openrct2/ride/gentle/MiniHelicopters.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/gentle/MonorailCycles.cpp b/src/openrct2/ride/gentle/MonorailCycles.cpp index ef759e5992..4011499472 100644 --- a/src/openrct2/ride/gentle/MonorailCycles.cpp +++ b/src/openrct2/ride/gentle/MonorailCycles.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/gentle/ObservationTower.cpp b/src/openrct2/ride/gentle/ObservationTower.cpp index 433b768c00..c454ad1f47 100644 --- a/src/openrct2/ride/gentle/ObservationTower.cpp +++ b/src/openrct2/ride/gentle/ObservationTower.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -54,8 +54,11 @@ void vehicle_visual_observation_tower( baseImage_id = (vehicle->animation_frame * 2) + vehicleEntry->base_image_id + 8; } - image_id = baseImage_id | (vehicle->colours.body_colour << 19) | (vehicle->colours.trim_colour << 24) - | IMAGE_TYPE_REMAP_2_PLUS; + image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_3(vehicle->colours.body_colour, vehicle->colours.trim_colour); + if (vehicle->IsGhost()) + { + image_id = (image_id & 0x7FFFF) | CONSTRUCTION_MARKER; + } paint_struct* ps = sub_98197C(session, image_id, 0, 0, 2, 2, 41, z, -11, -11, z + 1); if (ps != nullptr) { @@ -81,7 +84,6 @@ static void paint_observation_tower_base( trackSequence = track_map_3x3[direction][trackSequence]; int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_MISC], nullptr); @@ -89,9 +91,13 @@ static void paint_observation_tower_base( uint32_t imageId = SPR_FLOOR_METAL_B | session->TrackColours[SCHEME_SUPPORTS]; sub_98197C(session, imageId, 0, 0, 32, 32, 1, height, 0, 0, height); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, fenceSpritesMetalB, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, fenceSpritesMetalB, + session->CurrentRotation); + } if (trackSequence == 0) { diff --git a/src/openrct2/ride/gentle/SpaceRings.cpp b/src/openrct2/ride/gentle/SpaceRings.cpp index 0aeb20390f..e248132ac5 100644 --- a/src/openrct2/ride/gentle/SpaceRings.cpp +++ b/src/openrct2/ride/gentle/SpaceRings.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -87,10 +87,13 @@ static void paint_space_rings( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + trackSequence = track_map_3x3[direction][trackSequence]; int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; uint32_t imageId; diff --git a/src/openrct2/ride/gentle/SpiralSlide.cpp b/src/openrct2/ride/gentle/SpiralSlide.cpp index 355c2f8857..fb5b8954d9 100644 --- a/src/openrct2/ride/gentle/SpiralSlide.cpp +++ b/src/openrct2/ride/gentle/SpiralSlide.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -87,7 +87,9 @@ static void spiral_slide_paint_tile_front( { uint32_t image_id = 0; - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; if (direction == 1) { @@ -121,7 +123,7 @@ static void spiral_slide_paint_tile_front( sub_98197C(session, image_id, 16, 16, 8, 16, 108, height, 8, 0, height + 3); } - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level == 0 && ride->slide_in_use != 0) { uint8_t slide_progress = ride->spiral_slide_progress; @@ -196,7 +198,6 @@ static void paint_spiral_slide( trackSequence = track_map_2x2[direction][trackSequence]; int32_t edges = edges_2x2[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, direction & 1, 0, height, session->TrackColours[SCHEME_MISC], nullptr); @@ -205,9 +206,13 @@ static void paint_spiral_slide( uint32_t imageId = ((direction & 1) ? SPIRAL_SLIDE_BASE_B : SPIRAL_SLIDE_BASE_A) | session->TrackColours[SCHEME_SUPPORTS]; sub_98197C(session, imageId, 0, 0, 32, 32, 1, height, 0, 0, height); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, spiral_slide_fence_sprites, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, + spiral_slide_fence_sprites, session->CurrentRotation); + } switch (trackSequence) { diff --git a/src/openrct2/ride/shops/Facility.cpp b/src/openrct2/ride/shops/Facility.cpp index 999a62145e..11428794e9 100644 --- a/src/openrct2/ride/shops/Facility.cpp +++ b/src/openrct2/ride/shops/Facility.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -29,21 +29,17 @@ static void facility_paint_setup( bool hasSupports = wooden_a_supports_paint_setup( session, direction & 1, 0, height, session->TrackColours[SCHEME_3], nullptr); - Ride* ride = get_ride(rideIndex); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + auto rideEntry = ride->GetRideEntry(); if (rideEntry == nullptr) - { - log_error("Error drawing facility, rideEntry is NULL."); return; - } - rct_ride_entry_vehicle* firstVehicleEntry = &rideEntry->vehicles[0]; + auto firstVehicleEntry = &rideEntry->vehicles[0]; if (firstVehicleEntry == nullptr) - { - log_error("Error drawing facility, firstVehicleEntry is NULL."); return; - } uint32_t imageId = session->TrackColours[SCHEME_TRACK]; imageId |= firstVehicleEntry->base_image_id; diff --git a/src/openrct2/ride/shops/Shop.cpp b/src/openrct2/ride/shops/Shop.cpp index 4c64cf7222..90952fcaae 100644 --- a/src/openrct2/ride/shops/Shop.cpp +++ b/src/openrct2/ride/shops/Shop.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -29,15 +29,17 @@ static void shop_paint_setup( bool hasSupports = wooden_a_supports_paint_setup( session, direction & 1, 0, height, session->TrackColours[SCHEME_3], nullptr); - Ride* ride = get_ride(rideIndex); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - rct_ride_entry_vehicle* firstVehicleEntry = &rideEntry->vehicles[0]; - - if (rideEntry == nullptr || firstVehicleEntry == nullptr) - { - log_error("Error drawing shop, rideEntry or firstVehicleEntry is NULL."); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; + + auto firstVehicleEntry = &rideEntry->vehicles[0]; + if (firstVehicleEntry == nullptr) return; - } uint32_t imageId = session->TrackColours[SCHEME_TRACK]; if (imageId & IMAGE_TYPE_REMAP_2_PLUS) diff --git a/src/openrct2/ride/thrill/3dCinema.cpp b/src/openrct2/ride/thrill/3dCinema.cpp index eea1874b22..940efb3a98 100644 --- a/src/openrct2/ride/thrill/3dCinema.cpp +++ b/src/openrct2/ride/thrill/3dCinema.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -22,13 +22,13 @@ static void paint_3d_cinema_structure( { const TileElement* savedTileElement = static_cast(session->CurrentlyDrawnItem); - Ride* ride = get_ride(rideIndex); - rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - - if (rideEntry == nullptr) - { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) return; - } if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && ride->vehicles[0] != SPRITE_INDEX_NULL) { @@ -59,16 +59,19 @@ static void paint_3d_cinema( trackSequence = track_map_3x3[direction][trackSequence]; int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_MISC], nullptr); track_paint_util_paint_floor(session, edges, session->TrackColours[SCHEME_TRACK], height, floorSpritesCork); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_MISC], height, fenceSpritesRope, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_MISC], height, fenceSpritesRope, + session->CurrentRotation); + } switch (trackSequence) { diff --git a/src/openrct2/ride/thrill/Enterprise.cpp b/src/openrct2/ride/thrill/Enterprise.cpp index 00d18d5b8d..48d60f6721 100644 --- a/src/openrct2/ride/thrill/Enterprise.cpp +++ b/src/openrct2/ride/thrill/Enterprise.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -54,7 +54,7 @@ static void paint_enterprise_structure( uint32_t imageId = (baseImageId + imageOffset) | imageColourFlags; sub_98197C(session, imageId, xOffset, yOffset, 24, 24, 48, height, 0, 0, height); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level == 0 && imageOffset < 12 && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && vehicle != nullptr) { @@ -81,10 +81,13 @@ static void paint_enterprise( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + trackSequence = track_map_4x4[direction][trackSequence]; int32_t edges = edges_4x4[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, direction & 1, 0, height, session->TrackColours[SCHEME_MISC], nullptr); diff --git a/src/openrct2/ride/thrill/GoKarts.cpp b/src/openrct2/ride/thrill/GoKarts.cpp index 73b268c438..9f3a5774a8 100644 --- a/src/openrct2/ride/thrill/GoKarts.cpp +++ b/src/openrct2/ride/thrill/GoKarts.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -347,8 +347,11 @@ static void paint_go_karts_station( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + LocationXY16 position = session->MapPosition; - Ride* ride = get_ride(rideIndex); auto stationObj = ride_get_station_object(ride); bool hasFence; diff --git a/src/openrct2/ride/thrill/LaunchedFreefall.cpp b/src/openrct2/ride/thrill/LaunchedFreefall.cpp index 5f126e87c3..eb18536da6 100644 --- a/src/openrct2/ride/thrill/LaunchedFreefall.cpp +++ b/src/openrct2/ride/thrill/LaunchedFreefall.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -35,48 +35,50 @@ void vehicle_visual_launched_freefall( paint_session* session, int32_t x, int32_t imageDirection, int32_t y, int32_t z, const rct_vehicle* vehicle, const rct_ride_entry_vehicle* vehicleEntry) { - int32_t image_id; - int32_t baseImage_id = vehicleEntry->base_image_id + ((vehicle->restraints_position / 64) * 2); + auto imageFlags = SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour); + if (vehicle->IsGhost()) + { + imageFlags = CONSTRUCTION_MARKER; + } // Draw back: - image_id = (baseImage_id + 2) | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour); + int32_t baseImage_id = vehicleEntry->base_image_id + ((vehicle->restraints_position / 64) * 2); + auto image_id = (baseImage_id + 2) | imageFlags; sub_98197C(session, image_id, 0, 0, 2, 2, 41, z, -11, -11, z + 1); // Draw front: - image_id = (baseImage_id + 1) | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour); + image_id = (baseImage_id + 1) | imageFlags; sub_98197C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); // Draw peeps: - if (session->DPI->zoom_level < 2) + if (session->DPI.zoom_level < 2 && vehicle->num_peeps > 0 && !vehicle->IsGhost()) { - if (vehicle->num_peeps > 0) + baseImage_id = vehicleEntry->base_image_id + 9; + if ((vehicle->restraints_position / 64) == 3) { - baseImage_id = vehicleEntry->base_image_id + 9; - if ((vehicle->restraints_position / 64) == 3) - { - baseImage_id += 2; // Draw peeps sitting without transparent area between them for restraints - } - image_id = (baseImage_id + ((((imageDirection / 8) + 0) & 3) * 3)) - | SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[0], vehicle->peep_tshirt_colours[1]); + baseImage_id += 2; // Draw peeps sitting without transparent area between them for restraints + } + auto directionOffset = imageDirection / 8; + image_id = (baseImage_id + (((directionOffset + 0) & 3) * 3)) + | SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[0], vehicle->peep_tshirt_colours[1]); + sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); + if (vehicle->num_peeps > 2) + { + image_id = (baseImage_id + (((directionOffset + 1) & 3) * 3)) + | SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[2], vehicle->peep_tshirt_colours[3]); + sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); + } + if (vehicle->num_peeps > 4) + { + image_id = (baseImage_id + (((directionOffset + 2) & 3) * 3)) + | SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[4], vehicle->peep_tshirt_colours[5]); + sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); + } + if (vehicle->num_peeps > 6) + { + image_id = (baseImage_id + (((directionOffset + 3) & 3) * 3)) + | SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[6], vehicle->peep_tshirt_colours[7]); sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); - if (vehicle->num_peeps > 2) - { - image_id = (baseImage_id + ((((imageDirection / 8) + 1) & 3) * 3)) - | SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[2], vehicle->peep_tshirt_colours[3]); - sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); - } - if (vehicle->num_peeps > 4) - { - image_id = (baseImage_id + ((((imageDirection / 8) + 2) & 3) * 3)) - | SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[4], vehicle->peep_tshirt_colours[5]); - sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); - } - if (vehicle->num_peeps > 6) - { - image_id = (baseImage_id + ((((imageDirection / 8) + 3) & 3) * 3)) - | SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[6], vehicle->peep_tshirt_colours[7]); - sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); - } } } @@ -91,7 +93,6 @@ static void paint_launched_freefall_base( trackSequence = track_map_3x3[direction][trackSequence]; int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_MISC], nullptr); @@ -99,9 +100,13 @@ static void paint_launched_freefall_base( uint32_t imageId = SPR_FLOOR_METAL | session->TrackColours[SCHEME_SUPPORTS]; sub_98197C(session, imageId, 0, 0, 32, 32, 1, height, 0, 0, height); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, - launched_freefall_fence_sprites, session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, + launched_freefall_fence_sprites, session->CurrentRotation); + } if (trackSequence == 0) { diff --git a/src/openrct2/ride/thrill/MagicCarpet.cpp b/src/openrct2/ride/thrill/MagicCarpet.cpp index 0eb94a2a83..5009460093 100644 --- a/src/openrct2/ride/thrill/MagicCarpet.cpp +++ b/src/openrct2/ride/thrill/MagicCarpet.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -122,7 +122,7 @@ static void paint_magic_carpet_vehicle( paint_session* session, Ride* ride, uint8_t direction, uint32_t swingImageId, LocationXYZ16 offset, LocationXYZ16 bbOffset, LocationXYZ16 bbSize) { - rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride); + rct_ride_entry* rideEntry = ride->GetRideEntry(); uint32_t vehicleImageId = rideEntry->vehicles[0].base_image_id + direction; // Vehicle @@ -155,7 +155,7 @@ static void paint_magic_carpet_vehicle( bbOffset.x, bbOffset.y, bbOffset.z); // Riders - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level <= 1 && (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK)) { rct_vehicle* vehicle = get_first_vehicle(ride); @@ -244,21 +244,24 @@ static void paint_magic_carpet( break; } - Ride* ride = get_ride(rideIndex); - switch (relativeTrackSequence) + auto ride = get_ride(rideIndex); + if (ride != nullptr) { - case 3: - paint_magic_carpet_structure(session, ride, direction, -48, height); - break; - case 0: - paint_magic_carpet_structure(session, ride, direction, -16, height); - break; - case 2: - paint_magic_carpet_structure(session, ride, direction, 16, height); - break; - case 1: - paint_magic_carpet_structure(session, ride, direction, 48, height); - break; + switch (relativeTrackSequence) + { + case 3: + paint_magic_carpet_structure(session, ride, direction, -48, height); + break; + case 0: + paint_magic_carpet_structure(session, ride, direction, -16, height); + break; + case 2: + paint_magic_carpet_structure(session, ride, direction, 16, height); + break; + case 1: + paint_magic_carpet_structure(session, ride, direction, 48, height); + break; + } } paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0); diff --git a/src/openrct2/ride/thrill/MotionSimulator.cpp b/src/openrct2/ride/thrill/MotionSimulator.cpp index 004be7e8f2..de79d4b833 100644 --- a/src/openrct2/ride/thrill/MotionSimulator.cpp +++ b/src/openrct2/ride/thrill/MotionSimulator.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -33,8 +33,13 @@ enum static void paint_motionsimulator_vehicle( paint_session* session, int8_t offsetX, int8_t offsetY, uint8_t direction, int32_t height, const TileElement* tileElement) { - Ride* ride = get_ride(tileElement->AsTrack()->GetRideIndex()); - rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride); + auto ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + if (ride == nullptr) + return; + + auto rideEntry = ride->GetRideEntry(); + if (rideEntry == nullptr) + return; const TileElement* savedTileElement = static_cast(session->CurrentlyDrawnItem); @@ -132,14 +137,18 @@ static void paint_motionsimulator( trackSequence = track_map_2x2[direction][trackSequence]; int32_t edges = edges_2x2[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = { session->MapPosition.x, session->MapPosition.y }; wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_MISC], nullptr); track_paint_util_paint_floor(session, edges, session->TrackColours[SCHEME_TRACK], height, floorSpritesCork); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_SUPPORTS], height, fenceSpritesRope, - session->CurrentRotation); + + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_SUPPORTS], height, fenceSpritesRope, + session->CurrentRotation); + } switch (trackSequence) { diff --git a/src/openrct2/ride/thrill/PirateShip.cpp b/src/openrct2/ride/thrill/PirateShip.cpp index ef08368231..7ba39aa09a 100644 --- a/src/openrct2/ride/thrill/PirateShip.cpp +++ b/src/openrct2/ride/thrill/PirateShip.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -115,7 +115,7 @@ static void paint_pirate_ship_structure( session, imageId, xOffset, yOffset, bounds.length_x, bounds.length_y, 80, height, bounds.offset_x, bounds.offset_y, height); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level <= 1 && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && vehicle != nullptr) { @@ -170,8 +170,11 @@ static void paint_pirate_ship( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + uint8_t relativeTrackSequence = track_map_1x5[direction][trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; uint32_t imageId; diff --git a/src/openrct2/ride/thrill/RotoDrop.cpp b/src/openrct2/ride/thrill/RotoDrop.cpp index ccbb55b6c2..32c773ae63 100644 --- a/src/openrct2/ride/thrill/RotoDrop.cpp +++ b/src/openrct2/ride/thrill/RotoDrop.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -35,6 +35,12 @@ void vehicle_visual_roto_drop( paint_session* session, int32_t x, int32_t imageDirection, int32_t y, int32_t z, const rct_vehicle* vehicle, const rct_ride_entry_vehicle* vehicleEntry) { + auto imageFlags = SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour); + if (vehicle->IsGhost()) + { + imageFlags = CONSTRUCTION_MARKER; + } + int32_t image_id; int32_t baseImage_id = (vehicleEntry->base_image_id + 4) + ((vehicle->animation_frame / 4) & 0x3); if (vehicle->restraints_position >= 64) @@ -44,41 +50,44 @@ void vehicle_visual_roto_drop( } // Draw back: - image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour); + image_id = baseImage_id | imageFlags; sub_98197C(session, image_id, 0, 0, 2, 2, 41, z, -11, -11, z + 1); // Draw front: - image_id = (baseImage_id + 4) | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour); + image_id = (baseImage_id + 4) | imageFlags; sub_98197C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); - uint8_t riding_peep_sprites[64]; - std::fill_n(riding_peep_sprites, sizeof(riding_peep_sprites), 0xFF); - for (int32_t i = 0; i < vehicle->num_peeps; i++) + if (vehicle->num_peeps > 0 && !vehicle->IsGhost()) { - uint8_t cl = (i & 3) * 16; - cl += (i & 0xFC); - cl += vehicle->animation_frame / 4; - cl += (imageDirection / 8) * 16; - cl &= 0x3F; - riding_peep_sprites[cl] = vehicle->peep_tshirt_colours[i]; - } - - // Draw riding peep sprites in back to front order: - for (int32_t j = 0; j <= 48; j++) - { - int32_t i = (j % 2) ? (48 - (j / 2)) : (j / 2); - if (riding_peep_sprites[i] != 0xFF) + uint8_t riding_peep_sprites[64]; + std::fill_n(riding_peep_sprites, sizeof(riding_peep_sprites), 0xFF); + for (int32_t i = 0; i < vehicle->num_peeps; i++) { - baseImage_id = vehicleEntry->base_image_id + 20 + i; - if (vehicle->restraints_position >= 64) - { - baseImage_id += 64; - baseImage_id += vehicle->restraints_position / 64; - } - image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_1(riding_peep_sprites[i]); - sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); + uint8_t cl = (i & 3) * 16; + cl += (i & 0xFC); + cl += vehicle->animation_frame / 4; + cl += (imageDirection / 8) * 16; + cl &= 0x3F; + riding_peep_sprites[cl] = vehicle->peep_tshirt_colours[i]; } - }; + + // Draw riding peep sprites in back to front order: + for (int32_t j = 0; j <= 48; j++) + { + int32_t i = (j % 2) ? (48 - (j / 2)) : (j / 2); + if (riding_peep_sprites[i] != 0xFF) + { + baseImage_id = vehicleEntry->base_image_id + 20 + i; + if (vehicle->restraints_position >= 64) + { + baseImage_id += 64; + baseImage_id += vehicle->restraints_position / 64; + } + image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_1(riding_peep_sprites[i]); + sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1); + } + } + } assert(vehicleEntry->effect_visual == 1); // Although called in original code, effect_visual (splash effects) are not used for many rides and does not make sense so @@ -93,7 +102,6 @@ static void paint_roto_drop_base( trackSequence = track_map_3x3[direction][trackSequence]; int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_MISC], nullptr); @@ -101,9 +109,13 @@ static void paint_roto_drop_base( uint32_t imageId = SPR_FLOOR_METAL_B | session->TrackColours[SCHEME_SUPPORTS]; sub_98197C(session, imageId, 0, 0, 32, 32, 1, height, 0, 0, height); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, fenceSpritesMetalB, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, fenceSpritesMetalB, + session->CurrentRotation); + } if (trackSequence == 0) { diff --git a/src/openrct2/ride/thrill/SwingingInverterShip.cpp b/src/openrct2/ride/thrill/SwingingInverterShip.cpp index f5ed22c227..e126165a35 100644 --- a/src/openrct2/ride/thrill/SwingingInverterShip.cpp +++ b/src/openrct2/ride/thrill/SwingingInverterShip.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -126,9 +126,6 @@ static void paint_swinging_inverter_ship( const TileElement* tileElement) { uint8_t relativeTrackSequence = track_map_1x4[direction][trackSequence]; - - Ride* ride = get_ride(rideIndex); - uint32_t imageId; if (relativeTrackSequence != 1 && relativeTrackSequence != 3) @@ -168,20 +165,24 @@ static void paint_swinging_inverter_ship( } } - switch (relativeTrackSequence) + auto ride = get_ride(rideIndex); + if (ride != nullptr) { - case 1: - paint_swinging_inverter_ship_structure(session, ride, direction, 48, height + 7); - break; - case 2: - paint_swinging_inverter_ship_structure(session, ride, direction, 16, height + 7); - break; - case 0: - paint_swinging_inverter_ship_structure(session, ride, direction, -16, height + 7); - break; - case 3: - paint_swinging_inverter_ship_structure(session, ride, direction, -48, height + 7); - break; + switch (relativeTrackSequence) + { + case 1: + paint_swinging_inverter_ship_structure(session, ride, direction, 48, height + 7); + break; + case 2: + paint_swinging_inverter_ship_structure(session, ride, direction, 16, height + 7); + break; + case 0: + paint_swinging_inverter_ship_structure(session, ride, direction, -16, height + 7); + break; + case 3: + paint_swinging_inverter_ship_structure(session, ride, direction, -48, height + 7); + break; + } } paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0); diff --git a/src/openrct2/ride/thrill/TopSpin.cpp b/src/openrct2/ride/thrill/TopSpin.cpp index 01b977eb34..4c0186ac35 100644 --- a/src/openrct2/ride/thrill/TopSpin.cpp +++ b/src/openrct2/ride/thrill/TopSpin.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -45,6 +45,10 @@ static void top_spin_paint_vehicle( paint_session* session, int8_t al, int8_t cl, ride_id_t rideIndex, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + uint16_t boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ; // As we will be drawing a vehicle we need to backup the tileElement that // is assigned to the drawings. @@ -52,7 +56,6 @@ static void top_spin_paint_vehicle( height += 3; - Ride* ride = get_ride(rideIndex); rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); rct_vehicle* vehicle = nullptr; @@ -167,7 +170,7 @@ static void top_spin_paint_vehicle( session, image_id, (int8_t)seatCoords.x, (int8_t)seatCoords.y, lengthX, lengthY, 90, seatCoords.z, boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level < 2 && vehicle != nullptr && vehicle->num_peeps != 0) { image_id = (seatImageId + (1 * 76)) @@ -249,16 +252,19 @@ static void paint_top_spin( trackSequence = track_map_3x3[direction][trackSequence]; int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; wooden_a_supports_paint_setup(session, direction & 1, 0, height, session->TrackColours[SCHEME_MISC], nullptr); track_paint_util_paint_floor(session, edges, session->TrackColours[SCHEME_TRACK], height, floorSpritesCork); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_MISC], height, fenceSpritesRope, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_MISC], height, fenceSpritesRope, + session->CurrentRotation); + } switch (trackSequence) { diff --git a/src/openrct2/ride/thrill/Twist.cpp b/src/openrct2/ride/thrill/Twist.cpp index 29d991ffd3..0e76bf5928 100644 --- a/src/openrct2/ride/thrill/Twist.cpp +++ b/src/openrct2/ride/thrill/Twist.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -58,7 +58,7 @@ static void paint_twist_structure( uint32_t imageId = (baseImageId + structureFrameNum) | imageColourFlags; sub_98197C(session, imageId, xOffset, yOffset, 24, 24, 48, height, xOffset + 16, yOffset + 16, height); - rct_drawpixelinfo* dpi = session->DPI; + rct_drawpixelinfo* dpi = &session->DPI; if (dpi->zoom_level < 1 && ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK && vehicle != nullptr) { @@ -81,10 +81,13 @@ static void paint_twist( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + trackSequence = track_map_3x3[direction][trackSequence]; const uint8_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; uint32_t imageId; diff --git a/src/openrct2/ride/transport/Chairlift.cpp b/src/openrct2/ride/transport/Chairlift.cpp index 11752fa1e8..0d09223a5b 100644 --- a/src/openrct2/ride/transport/Chairlift.cpp +++ b/src/openrct2/ride/transport/Chairlift.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -175,13 +175,15 @@ static void chairlift_paint_station_ne_sw( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + const LocationXY16 pos = session->MapPosition; uint8_t trackType = tileElement->AsTrack()->GetTrackType(); - Ride* ride = get_ride(rideIndex); uint32_t imageId; bool isStart = chairlift_paint_util_is_first_track(rideIndex, tileElement, pos, trackType); - ; bool isEnd = chairlift_paint_util_is_last_track(rideIndex, tileElement, pos, trackType); auto stationObj = ride_get_station_object(ride); @@ -266,13 +268,15 @@ static void chairlift_paint_station_se_nw( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + const LocationXY16 pos = session->MapPosition; uint8_t trackType = tileElement->AsTrack()->GetTrackType(); - Ride* ride = get_ride(rideIndex); uint32_t imageId; bool isStart = chairlift_paint_util_is_first_track(rideIndex, tileElement, pos, trackType); - ; bool isEnd = chairlift_paint_util_is_last_track(rideIndex, tileElement, pos, trackType); auto stationObj = ride_get_station_object(ride); diff --git a/src/openrct2/ride/transport/Lift.cpp b/src/openrct2/ride/transport/Lift.cpp index 48804a7aac..792731774b 100644 --- a/src/openrct2/ride/transport/Lift.cpp +++ b/src/openrct2/ride/transport/Lift.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -73,15 +73,18 @@ static void paint_lift_base( } int32_t edges = edges_3x3[trackSequence]; - Ride* ride = get_ride(rideIndex); LocationXY16 position = session->MapPosition; uint32_t imageId = SPR_FLOOR_METAL_B | session->TrackColours[SCHEME_SUPPORTS]; sub_98197C(session, imageId, 0, 0, 32, 32, 1, height, 0, 0, height); - track_paint_util_paint_fences( - session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, fenceSpritesMetalB, - session->CurrentRotation); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + { + track_paint_util_paint_fences( + session, edges, position, tileElement, ride, session->TrackColours[SCHEME_TRACK], height, fenceSpritesMetalB, + session->CurrentRotation); + } int32_t blockedSegments = 0; switch (trackSequence) diff --git a/src/openrct2/ride/transport/MiniatureRailway.cpp b/src/openrct2/ride/transport/MiniatureRailway.cpp index 6ab4d03eb3..1c355777bc 100644 --- a/src/openrct2/ride/transport/MiniatureRailway.cpp +++ b/src/openrct2/ride/transport/MiniatureRailway.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -927,7 +927,7 @@ static void paint_miniature_railway_track_right_quarter_turn_5_tiles( session, right_quarter_turn_5_supports_type[direction][trackSequence], 0, height, session->TrackColours[SCHEME_SUPPORTS], nullptr); - if (isSupported == false || (trackSequence == 3 && direction == 2)) + if (!isSupported || (trackSequence == 3 && direction == 2)) { track_paint_util_right_quarter_turn_5_tiles_paint( session, 2, height, direction, trackSequence, session->TrackColours[SCHEME_TRACK], @@ -1063,7 +1063,7 @@ static void paint_miniature_railway_track_s_bend_left( LocationXY16 offset = offsetList[trackSequence]; LocationXY16 bounds = boundsList[trackSequence]; - if (isSupported == false) + if (!isSupported) { sub_98197C_rotated( session, direction, imageId, (int8_t)offset.x, (int8_t)offset.y, bounds.x, bounds.y, 2, height, offset.x, offset.y, @@ -1163,7 +1163,7 @@ static void paint_miniature_railway_track_s_bend_right( | session->TrackColours[SCHEME_TRACK]; LocationXY16 offset = offsetList[trackSequence]; LocationXY16 bounds = boundsList[trackSequence]; - if (isSupported == false) + if (!isSupported) { sub_98197C_rotated( session, direction, imageId, (int8_t)offset.x, (int8_t)offset.y, bounds.x, bounds.y, 2, height, offset.x, offset.y, @@ -1274,7 +1274,7 @@ static void paint_miniature_railway_track_right_quarter_turn_3_tiles( isSupported = wooden_a_supports_paint_setup( session, supportType[direction], 0, height, session->TrackColours[SCHEME_SUPPORTS], nullptr); } - if (isSupported == false) + if (!isSupported) { track_paint_util_right_quarter_turn_3_tiles_paint( session, 3, height, direction, trackSequence, session->TrackColours[SCHEME_TRACK], @@ -1295,9 +1295,9 @@ static void paint_miniature_railway_track_right_quarter_turn_3_tiles( miniature_railway_right_quarter_turn_3_tile_track_floor, nullptr, defaultRightQuarterTurn3TilesBoundLengths, miniature_railway_right_quarter_turn_3_tile_bound_offsets); - static constexpr const int8_t right_quarter_turn_3_tiles_sprite_map[] = { 0, -1, 1, 2 }; + static constexpr const int8_t _right_quarter_turn_3_tiles_sprite_map[] = { 0, -1, 1, 2 }; - int32_t index = right_quarter_turn_3_tiles_sprite_map[trackSequence]; + int32_t index = _right_quarter_turn_3_tiles_sprite_map[trackSequence]; uint32_t imageId = miniature_railway_track_pieces_flat_quarter_turn_3_tiles[direction][index] | session->TrackColours[SCHEME_TRACK]; @@ -1429,7 +1429,7 @@ static void paint_miniature_railway_track_left_eighth_to_diag( session, supportType[direction][trackSequence], 0, height, session->TrackColours[SCHEME_SUPPORTS], nullptr); } uint32_t imageId; - if (isSupported == false) + if (!isSupported) { int8_t index = paint_miniature_railway_eighth_to_diag_index[trackSequence]; if (index >= 0) @@ -1566,7 +1566,7 @@ static void paint_miniature_railway_track_right_eighth_to_diag( } uint32_t imageId; - if (isSupported == false) + if (!isSupported) { int8_t index = paint_miniature_railway_eighth_to_diag_index[trackSequence]; if (index >= 0) diff --git a/src/openrct2/ride/transport/Monorail.cpp b/src/openrct2/ride/transport/Monorail.cpp index 4320743c49..daebf45e53 100644 --- a/src/openrct2/ride/transport/Monorail.cpp +++ b/src/openrct2/ride/transport/Monorail.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/transport/SuspendedMonorail.cpp b/src/openrct2/ride/transport/SuspendedMonorail.cpp index 365fd42f49..8069bdd190 100644 --- a/src/openrct2/ride/transport/SuspendedMonorail.cpp +++ b/src/openrct2/ride/transport/SuspendedMonorail.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/water/BoatHire.cpp b/src/openrct2/ride/water/BoatHire.cpp index 393761b2bd..6ff6a8bf42 100644 --- a/src/openrct2/ride/water/BoatHire.cpp +++ b/src/openrct2/ride/water/BoatHire.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -63,8 +63,11 @@ static void paint_boat_hire_station( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + LocationXY16 position = session->MapPosition; - Ride* ride = get_ride(rideIndex); auto stationObj = ride_get_station_object(ride); if (direction & 1) diff --git a/src/openrct2/ride/water/DingySlide.cpp b/src/openrct2/ride/water/DingySlide.cpp index 886e3e9d1c..63a7978fe3 100644 --- a/src/openrct2/ride/water/DingySlide.cpp +++ b/src/openrct2/ride/water/DingySlide.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/water/LogFlume.cpp b/src/openrct2/ride/water/LogFlume.cpp index 1d5093f4ab..d45c381d67 100644 --- a/src/openrct2/ride/water/LogFlume.cpp +++ b/src/openrct2/ride/water/LogFlume.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ride/water/RiverRapids.cpp b/src/openrct2/ride/water/RiverRapids.cpp index faa6eae9d5..cdaefc8f36 100644 --- a/src/openrct2/ride/water/RiverRapids.cpp +++ b/src/openrct2/ride/water/RiverRapids.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -223,10 +223,15 @@ void vehicle_visual_river_rapids( const vehicle_boundbox* bb = &_riverRapidsBoundbox[j]; image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour); + if (vehicle->IsGhost()) + { + image_id &= 0x7FFFF; + image_id |= CONSTRUCTION_MARKER; + } sub_98197C( session, image_id, 0, 0, bb->length_x, bb->length_y, bb->length_z, z, bb->offset_x, bb->offset_y, bb->offset_z + z); - if (session->DPI->zoom_level < 2 && vehicle->num_peeps > 0) + if (session->DPI.zoom_level < 2 && vehicle->num_peeps > 0 && !vehicle->IsGhost()) { // Draw peeps: (this particular vehicle doesn't sort them back to front like others so the back ones sometimes clip, but // that's how the original does it...) @@ -316,10 +321,10 @@ static void paint_river_rapids_station( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { - Ride* ride = get_ride(rideIndex); - paint_river_rapids_track_flat(session, rideIndex, trackSequence, direction, height, tileElement); - track_paint_util_draw_station_platform(session, ride, direction, height, 12, tileElement); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + track_paint_util_draw_station_platform(session, ride, direction, height, 12, tileElement); paint_util_set_general_support_height(session, height + 32, 0x20); } diff --git a/src/openrct2/ride/water/SplashBoats.cpp b/src/openrct2/ride/water/SplashBoats.cpp index ddbf89e0e9..ba238ccdc3 100644 --- a/src/openrct2/ride/water/SplashBoats.cpp +++ b/src/openrct2/ride/water/SplashBoats.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -783,8 +783,6 @@ static void paint_splash_boats_station( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { - Ride* ride = get_ride(rideIndex); - if (direction & 1) { uint32_t imageId = (direction == 1 ? SPR_SPLASH_BOATS_FLAT_TOP_NW_SE : SPR_SPLASH_BOATS_FLAT_TOP_SE_NW) @@ -806,7 +804,9 @@ static void paint_splash_boats_station( wooden_a_supports_paint_setup(session, (direction & 1), 0, height, session->TrackColours[SCHEME_SUPPORTS], nullptr); - track_paint_util_draw_station_platform(session, ride, direction, height, 7, tileElement); + auto ride = get_ride(rideIndex); + if (ride != nullptr) + track_paint_util_draw_station_platform(session, ride, direction, height, 7, tileElement); paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_6); diff --git a/src/openrct2/ride/water/SubmarineRide.cpp b/src/openrct2/ride/water/SubmarineRide.cpp index 42220fcb35..48c88fc791 100644 --- a/src/openrct2/ride/water/SubmarineRide.cpp +++ b/src/openrct2/ride/water/SubmarineRide.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -24,6 +24,12 @@ void vehicle_visual_submarine( paint_session* session, int32_t x, int32_t imageDirection, int32_t y, int32_t z, const rct_vehicle* vehicle, const rct_ride_entry_vehicle* vehicleEntry) { + auto imageFlags = SPRITE_ID_PALETTE_COLOUR_3(vehicle->colours.body_colour, vehicle->colours.trim_colour); + if (vehicle->IsGhost()) + { + imageFlags = CONSTRUCTION_MARKER; + } + int32_t baseImage_id = imageDirection; int32_t image_id; if (vehicle->restraints_position >= 64) @@ -53,8 +59,7 @@ void vehicle_visual_submarine( vehicle_boundbox bb = VehicleBoundboxes[vehicleEntry->draw_order][imageDirection / 2]; - image_id = baseImage_id | (vehicle->colours.body_colour << 19) | (vehicle->colours.trim_colour << 24) - | IMAGE_TYPE_REMAP_2_PLUS; + image_id = baseImage_id | imageFlags; paint_struct* ps = sub_98197C( session, image_id, 0, 0, bb.length_x, bb.length_y, bb.length_z, z, bb.offset_x, bb.offset_y, bb.offset_z + z); if (ps != nullptr) @@ -62,8 +67,7 @@ void vehicle_visual_submarine( ps->tertiary_colour = vehicle->colours_extended; } - image_id = (baseImage_id + 1) | (vehicle->colours.body_colour << 19) | (vehicle->colours.trim_colour << 24) - | IMAGE_TYPE_REMAP_2_PLUS; + image_id = (baseImage_id + 1) | imageFlags; ps = sub_98197C(session, image_id, 0, 0, bb.length_x, bb.length_y, 2, z, bb.offset_x, bb.offset_y, bb.offset_z + z - 10); if (ps != nullptr) { @@ -78,8 +82,11 @@ static void submarine_ride_paint_track_station( paint_session* session, ride_id_t rideIndex, uint8_t trackSequence, uint8_t direction, int32_t height, const TileElement* tileElement) { + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return; + LocationXY16 position = session->MapPosition; - Ride* ride = get_ride(rideIndex); auto stationObj = ride_get_station_object(ride); int32_t heightLower = height - 16; uint32_t imageId; diff --git a/src/openrct2/ride/water/WaterCoaster.cpp b/src/openrct2/ride/water/WaterCoaster.cpp index 5d16762a33..535cc05642 100644 --- a/src/openrct2/ride/water/WaterCoaster.cpp +++ b/src/openrct2/ride/water/WaterCoaster.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/scenario/Scenario.cpp b/src/openrct2/scenario/Scenario.cpp index 76761bc8c6..0ed0a77aa7 100644 --- a/src/openrct2/scenario/Scenario.cpp +++ b/src/openrct2/scenario/Scenario.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,6 +18,7 @@ #include "../ParkImporter.h" #include "../audio/audio.h" #include "../config/Config.h" +#include "../core/Guard.hpp" #include "../core/Random.hpp" #include "../interface/Viewport.h" #include "../localisation/Date.h" @@ -49,6 +50,7 @@ #include "ScenarioSources.h" #include +#include const rct_string_id ScenarioCategoryStringIds[SCENARIO_CATEGORY_COUNT] = { STR_BEGINNER_PARKS, STR_CHALLENGING_PARKS, STR_EXPERT_PARKS, STR_REAL_PARKS, STR_OTHER_PARKS, @@ -80,7 +82,6 @@ money32 gScenarioCompanyValueRecord; char gScenarioFileName[MAX_PATH]; -static int32_t scenario_create_ducks(); static void scenario_objective_check(); using namespace OpenRCT2; @@ -114,7 +115,7 @@ void scenario_begin() { utf8 normalisedName[64]; - scenario_normalise_name(normalisedName, sizeof(normalisedName), gS6Info.name); + ScenarioSources::NormaliseName(normalisedName, sizeof(normalisedName), gS6Info.name); rct_string_id localisedStringIds[3]; if (language_get_localised_scenario_strings(normalisedName, localisedStringIds)) @@ -125,7 +126,7 @@ void scenario_begin() } if (localisedStringIds[1] != STR_NONE) { - park_set_name(language_get_string(localisedStringIds[1])); + park.Name = language_get_string(localisedStringIds[1]); } if (localisedStringIds[2] != STR_NONE) { @@ -135,12 +136,9 @@ void scenario_begin() } // Set the last saved game path - char parkName[128]; - format_string(parkName, 128, gParkName, &gParkNameArgs); - char savePath[MAX_PATH]; platform_get_user_directory(savePath, "save", sizeof(savePath)); - safe_strcat_path(savePath, parkName, sizeof(savePath)); + safe_strcat_path(savePath, park.Name.c_str(), sizeof(savePath)); path_append_extension(savePath, ".sv6", sizeof(savePath)); gScenarioSavePath = savePath; @@ -238,7 +236,7 @@ static void scenario_entrance_fee_too_high_check() if ((gParkFlags & PARK_FLAGS_PARK_OPEN) && park_get_entrance_fee() > max_fee) { - if (gParkEntrances.size() > 0) + if (!gParkEntrances.empty()) { const auto& entrance = gParkEntrances[0]; auto x = entrance.x + 16; @@ -417,63 +415,74 @@ void scenario_update() } scenario_update_daynight_cycle(); } - /** * * rct2: 0x006744A9 */ -static int32_t scenario_create_ducks() +bool scenario_create_ducks() { - int32_t i, j, r, c, x, y, waterZ, centreWaterZ, x2, y2; + // Check NxN area around centre tile defined by SquareSize + constexpr int32_t SquareSize = 7; + constexpr int32_t SquareCentre = SquareSize / 2; + constexpr int32_t SquareRadiusSize = SquareCentre * 32; - r = scenario_rand(); - x = ((r >> 16) & 0xFFFF) & 0x7F; - y = (r & 0xFFFF) & 0x7F; - x = (x + 64) * 32; - y = (y + 64) * 32; + CoordsXY centrePos; + centrePos.x = SquareRadiusSize + (scenario_rand_max(MAXIMUM_MAP_SIZE_TECHNICAL - SquareCentre) * 32); + centrePos.y = SquareRadiusSize + (scenario_rand_max(MAXIMUM_MAP_SIZE_TECHNICAL - SquareCentre) * 32); - if (!map_is_location_in_park({ x, y })) - return 0; + Guard::Assert(map_is_location_valid(centrePos)); - centreWaterZ = (tile_element_height(x, y) >> 16) & 0xFFFF; + if (!map_is_location_in_park(centrePos)) + return false; + + int32_t centreWaterZ = (tile_element_water_height(centrePos)); if (centreWaterZ == 0) - return 0; + return false; - // Check 7x7 area around centre tile - x2 = x - (32 * 3); - y2 = y - (32 * 3); - c = 0; - for (i = 0; i < 7; i++) + CoordsXY innerPos{ centrePos.x - (32 * SquareCentre), centrePos.y - (32 * SquareCentre) }; + int32_t waterTiles = 0; + for (int32_t y = 0; y < SquareSize; y++) { - for (j = 0; j < 7; j++) + for (int32_t x = 0; x < SquareSize; x++) { - waterZ = (tile_element_height(x2, y2) >> 16) & 0xFFFF; - if (waterZ == centreWaterZ) - c++; + if (!map_is_location_valid(innerPos)) + continue; - x2 += 32; + if (!map_is_location_in_park(innerPos)) + continue; + + int32_t waterZ = (tile_element_water_height(innerPos)); + if (waterZ == centreWaterZ) + waterTiles++; + + innerPos.x += 32; } - x2 -= 224; - y2 += 32; + innerPos.x -= SquareSize * 32; + innerPos.y += 32; } // Must be at least 25 water tiles of the same height in 7x7 area - if (c < 25) - return 0; + if (waterTiles < 25) + return false; // Set x, y to the centre of the tile - x += 16; - y += 16; - c = (scenario_rand() & 3) + 2; - for (i = 0; i < c; i++) + centrePos.x += 16; + centrePos.y += 16; + + uint32_t duckCount = (scenario_rand() % 4) + 2; + for (uint32_t i = 0; i < duckCount; i++) { - r = scenario_rand(); - x2 = (r >> 16) & 0x7F; - y2 = (r & 0xFFFF) & 0x7F; - create_duck(x + x2 - 64, y + y2 - 64); + uint32_t r = scenario_rand(); + innerPos.x = (r >> 16) % SquareRadiusSize; + innerPos.y = (r & 0xFFFF) % SquareRadiusSize; + + CoordsXY targetPos{ centrePos.x + innerPos.x - SquareRadiusSize, centrePos.y + innerPos.y - SquareRadiusSize }; + + Guard::Assert(map_is_location_valid(targetPos)); + create_duck(targetPos); } - return 1; + return true; } const random_engine_t::state_type& scenario_rand_state() @@ -493,78 +502,11 @@ void scenario_rand_seed(random_engine_t::result_type s0, random_engine_t::result * * @return eax */ -#ifndef DEBUG_DESYNC random_engine_t::result_type scenario_rand() -#else -static FILE* fp = nullptr; -static const char* realm = "LC"; - -uint32_t dbg_scenario_rand(const char* file, const char* function, const uint32_t line, const void* data) -#endif { -#ifdef DEBUG_DESYNC - if (fp == nullptr) - { - if (network_get_mode() == NETWORK_MODE_SERVER) - { - fp = fopen("server_rand.txt", "wt"); - realm = "SV"; - } - else if (network_get_mode() == NETWORK_MODE_CLIENT) - { - fp = fopen("client_rand.txt", "wt"); - realm = "CL"; - } - else - { - if (fp) - fclose(fp); - fp = nullptr; - realm = "LC"; - } - } - if (fp) - { - fprintf(fp, "Tick: %d, Rand: %08X - REF: %s:%u %s (%p)\n", gCurrentTicks, gScenarioSrand1, file, line, function, data); - } - if (!gInUpdateCode && !gInMapInitCode) - { - log_warning("scenario_rand called from outside game update"); - assert(false); - } -#endif - return gScenarioRand(); } -#ifdef DEBUG_DESYNC -void dbg_report_desync(uint32_t tick, uint32_t srand0, uint32_t server_srand0, const char* clientHash, const char* serverHash) -{ - if (fp == nullptr) - { - if (network_get_mode() == NETWORK_MODE_SERVER) - { - fp = fopen("server_rand.txt", "wt"); - realm = "SV"; - } - else if (network_get_mode() == NETWORK_MODE_CLIENT) - { - fp = fopen("client_rand.txt", "wt"); - realm = "CL"; - } - } - if (fp) - { - const bool sprites_mismatch = serverHash[0] != '\0' && strcmp(clientHash, serverHash); - - fprintf( - fp, "[%s] !! DESYNC !! Tick: %d, Client Hash: %s, Server Hash: %s, Client Rand: %08X, Server Rand: %08X - %s\n", - realm, tick, clientHash, ((serverHash[0] != '\0') ? serverHash : ""), srand0, server_srand0, - (sprites_mismatch ? "Sprite hash mismatch" : "scenario rand mismatch")); - } -} -#endif - uint32_t scenario_rand_max(uint32_t max) { if (max < 2) @@ -586,24 +528,23 @@ uint32_t scenario_rand_max(uint32_t max) static bool scenario_prepare_rides_for_save() { int32_t isFiveCoasterObjective = gScenarioObjectiveType == OBJECTIVE_FINISH_5_ROLLERCOASTERS; - int32_t i; - Ride* ride; uint8_t rcs = 0; - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - const rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - - // If there are more than 5 roller coasters, only mark the first five. - if (isFiveCoasterObjective && rideEntry != nullptr - && (ride_entry_has_category(rideEntry, RIDE_CATEGORY_ROLLERCOASTER) && rcs < 5)) + const auto* rideEntry = ride.GetRideEntry(); + if (rideEntry != nullptr) { - ride->lifecycle_flags |= RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; - rcs++; - } - else - { - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; + // If there are more than 5 roller coasters, only mark the first five. + if (isFiveCoasterObjective && (ride_entry_has_category(rideEntry, RIDE_CATEGORY_ROLLERCOASTER) && rcs < 5)) + { + ride.lifecycle_flags |= RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; + rcs++; + } + else + { + ride.lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; + } } } @@ -624,7 +565,7 @@ static bool scenario_prepare_rides_for_save() if (isFiveCoasterObjective) { - ride = get_ride(it.element->AsTrack()->GetRideIndex()); + auto ride = get_ride(it.element->AsTrack()->GetRideIndex()); // In the previous step, this flag was set on the first five roller coasters. if (ride != nullptr && ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) @@ -646,10 +587,12 @@ static bool scenario_prepare_rides_for_save() */ bool scenario_prepare_for_save() { - gS6Info.entry.flags = 255; + auto& park = GetContext()->GetGameState()->GetPark(); + auto parkName = park.Name.c_str(); + gS6Info.entry.flags = 255; if (gS6Info.name[0] == 0) - format_string(gS6Info.name, 64, gParkName, &gParkNameArgs); + String::Set(gS6Info.name, sizeof(gS6Info.name), parkName); gS6Info.objective_type = gScenarioObjectiveType; gS6Info.objective_arg_1 = gScenarioObjectiveYear; @@ -673,11 +616,28 @@ bool scenario_prepare_for_save() /** * Modifies the given S6 data so that ghost elements, rides with no track elements or unused banners / user strings are saved. - * - * TODO: This employs some black casting magic that should go away once we export to our own format instead of SV6. */ void scenario_fix_ghosts(rct_s6_data* s6) { + // Build tile pointer cache (needed to get the first element at a certain location) + RCT12TileElement* tilePointers[MAX_TILE_TILE_ELEMENT_POINTERS]; + for (size_t i = 0; i < MAX_TILE_TILE_ELEMENT_POINTERS; i++) + { + tilePointers[i] = TILE_UNDEFINED_TILE_ELEMENT; + } + + RCT12TileElement* tileElement = s6->tile_elements; + RCT12TileElement** tile = tilePointers; + for (size_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) + { + for (size_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) + { + *tile++ = tileElement; + while (!(tileElement++)->IsLastForTile()) + ; + } + } + // Remove all ghost elements RCT12TileElement* destinationElement = s6->tile_elements; @@ -685,15 +645,16 @@ void scenario_fix_ghosts(rct_s6_data* s6) { for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) { - RCT12TileElement* originalElement = reinterpret_cast(map_get_first_element_at(x, y)); + // This is the equivalent of map_get_first_element_at(x, y), but on S6 data. + RCT12TileElement* originalElement = tilePointers[x + y * MAXIMUM_MAP_SIZE_TECHNICAL]; do { if (originalElement->IsGhost()) { - BannerIndex bannerIndex = tile_element_get_banner_index(reinterpret_cast(originalElement)); + BannerIndex bannerIndex = originalElement->GetBannerIndex(); if (bannerIndex != BANNER_INDEX_NULL) { - rct_banner* banner = &s6->banners[bannerIndex]; + auto banner = &s6->banners[bannerIndex]; if (banner->type != BANNER_NULL) { banner->type = BANNER_NULL; @@ -714,14 +675,28 @@ void scenario_fix_ghosts(rct_s6_data* s6) } } +static void ride_all_has_any_track_elements(std::array& rideIndexArray) +{ + tile_element_iterator it; + tile_element_iterator_begin(&it); + while (tile_element_iterator_next(&it)) + { + if (it.element->GetType() != TILE_ELEMENT_TYPE_TRACK) + continue; + if (it.element->IsGhost()) + continue; + + rideIndexArray[it.element->AsTrack()->GetRideIndex()] = true; + } +} + void scenario_remove_trackless_rides(rct_s6_data* s6) { - bool rideHasTrack[MAX_RIDES]; + std::array rideHasTrack{}; ride_all_has_any_track_elements(rideHasTrack); for (int32_t i = 0; i < RCT12_MAX_RIDES_IN_PARK; i++) { - rct2_ride* ride = &s6->rides[i]; - + auto ride = &s6->rides[i]; if (rideHasTrack[i] || ride->type == RIDE_TYPE_NULL) { continue; @@ -783,30 +758,27 @@ static void scenario_objective_check_park_value_by() **/ static void scenario_objective_check_10_rollercoasters() { - int32_t i, rcs = 0; - uint8_t type_already_counted[256] = {}; - Ride* ride; - - FOR_ALL_RIDES (i, ride) + auto rcs = 0; + std::bitset type_already_counted; + for (const auto& ride : GetRideManager()) { - uint8_t subtype_id = ride->subtype; - rct_ride_entry* rideEntry = get_ride_entry(subtype_id); - if (rideEntry == nullptr) + if (ride.status == RIDE_STATUS_OPEN && ride.excitement >= RIDE_RATING(6, 00) && ride.subtype != RIDE_ENTRY_INDEX_NULL) { - continue; - } - - if (rideEntry != nullptr && ride_entry_has_category(rideEntry, RIDE_CATEGORY_ROLLERCOASTER) - && ride->status == RIDE_STATUS_OPEN && ride->excitement >= RIDE_RATING(6, 00) - && type_already_counted[subtype_id] == 0) - { - type_already_counted[subtype_id]++; - rcs++; + auto rideEntry = ride.GetRideEntry(); + if (rideEntry != nullptr) + { + if (ride_entry_has_category(rideEntry, RIDE_CATEGORY_ROLLERCOASTER) && !type_already_counted[ride.subtype]) + { + type_already_counted[ride.subtype] = true; + rcs++; + } + } } } - if (rcs >= 10) + { scenario_success(); + } } /** @@ -880,59 +852,59 @@ static void scenario_objective_check_monthly_ride_income() */ static void scenario_objective_check_10_rollercoasters_length() { - int32_t i, rcs = 0; - uint8_t type_already_counted[256] = {}; - int16_t objective_length = gScenarioObjectiveNumGuests; - Ride* ride; - - FOR_ALL_RIDES (i, ride) + const auto objective_length = gScenarioObjectiveNumGuests; + std::bitset type_already_counted; + auto rcs = 0; + for (const auto& ride : GetRideManager()) { - uint8_t subtype_id = ride->subtype; - rct_ride_entry* rideEntry = get_ride_entry(subtype_id); - if (rideEntry == nullptr) + if (ride.status == RIDE_STATUS_OPEN && ride.excitement >= RIDE_RATING(7, 00) && ride.subtype != RIDE_ENTRY_INDEX_NULL) { - continue; - } - if (ride_entry_has_category(rideEntry, RIDE_CATEGORY_ROLLERCOASTER) && ride->status == RIDE_STATUS_OPEN - && ride->excitement >= RIDE_RATING(7, 00) && type_already_counted[subtype_id] == 0) - { - if ((ride_get_total_length(ride) >> 16) > objective_length) + auto rideEntry = ride.GetRideEntry(); + if (rideEntry != nullptr) { - type_already_counted[subtype_id]++; - rcs++; + if (ride_entry_has_category(rideEntry, RIDE_CATEGORY_ROLLERCOASTER) && !type_already_counted[ride.subtype]) + { + if ((ride_get_total_length(&ride) >> 16) > objective_length) + { + type_already_counted[ride.subtype] = true; + rcs++; + } + } } } } - if (rcs >= 10) + { scenario_success(); + } } static void scenario_objective_check_finish_5_rollercoasters() { - money32 objectiveRideExcitement = gScenarioObjectiveCurrency; + const auto objectiveRideExcitement = gScenarioObjectiveCurrency; // Originally, this did not check for null rides, neither did it check if // the rides are even rollercoasters, never mind the right rollercoasters to be finished. - int32_t i; - Ride* ride; - int32_t rcs = 0; - FOR_ALL_RIDES (i, ride) + auto rcs = 0; + for (const auto& ride : GetRideManager()) { - const rct_ride_entry* rideEntry = get_ride_entry(ride->subtype); - if (rideEntry == nullptr) + if (ride.status != RIDE_STATUS_CLOSED && ride.excitement >= objectiveRideExcitement) { - continue; + auto rideEntry = ride.GetRideEntry(); + if (rideEntry != nullptr) + { + if ((ride.lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) + && ride_entry_has_category(rideEntry, RIDE_CATEGORY_ROLLERCOASTER)) + { + rcs++; + } + } } - - if (ride->status != RIDE_STATUS_CLOSED && ride->excitement >= objectiveRideExcitement - && (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) && // Set on partially finished coasters - ride_entry_has_category(rideEntry, RIDE_CATEGORY_ROLLERCOASTER)) - rcs++; } - if (rcs >= 5) + { scenario_success(); + } } static void scenario_objective_check_replay_loan_and_park_value() diff --git a/src/openrct2/scenario/Scenario.h b/src/openrct2/scenario/Scenario.h index 39c4667a10..56e06876e0 100644 --- a/src/openrct2/scenario/Scenario.h +++ b/src/openrct2/scenario/Scenario.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -238,7 +238,7 @@ struct rct_s6_data uint8_t last_entrance_style; uint8_t rct1_water_colour; uint8_t pad_01358842[2]; - rct_research_item research_items[MAX_RESEARCH_ITEMS]; + RCT12ResearchItem research_items[MAX_RESEARCH_ITEMS]; uint16_t map_base_z; char scenario_name[64]; char scenario_description[256]; @@ -251,7 +251,7 @@ struct rct_s6_data uint8_t park_entrance_direction[RCT12_MAX_PARK_ENTRANCES]; char scenario_filename[256]; uint8_t saved_expansion_pack_names[3256]; - rct_banner banners[RCT2_MAX_BANNERS_IN_PARK]; + RCT12Banner banners[RCT2_MAX_BANNERS_IN_PARK]; char custom_strings[RCT12_MAX_USER_STRINGS][RCT12_USER_STRING_MAX_LENGTH]; uint32_t game_ticks_1; rct2_ride rides[RCT12_MAX_RIDES_IN_PARK]; @@ -260,12 +260,12 @@ struct rct_s6_data uint16_t saved_view_y; uint8_t saved_view_zoom; uint8_t saved_view_rotation; - rct_map_animation map_animations[RCT2_MAX_ANIMATED_OBJECTS]; + RCT12MapAnimation map_animations[RCT2_MAX_ANIMATED_OBJECTS]; uint16_t num_map_animations; uint8_t pad_0138B582[2]; - rct_ride_rating_calc_data ride_ratings_calc_data; + RCT2RideRatingCalculationData ride_ratings_calc_data; uint8_t pad_0138B5D0[60]; - rct_ride_measurement ride_measurements[8]; + RCT12RideMeasurement ride_measurements[8]; uint32_t next_guest_index; uint16_t grass_and_scenery_tilepos; uint32_t patrol_areas[(RCT2_MAX_STAFF + RCT12_STAFF_TYPE_COUNT) * RCT12_PATROL_AREA_SIZE]; @@ -395,6 +395,7 @@ extern char gScenarioFileName[260]; void load_from_sc6(const char* path); void scenario_begin(); void scenario_update(); +bool scenario_create_ducks(); const random_engine_t::state_type& scenario_rand_state(); void scenario_rand_seed(random_engine_t::result_type s0, random_engine_t::result_type s1); diff --git a/src/openrct2/scenario/ScenarioRepository.cpp b/src/openrct2/scenario/ScenarioRepository.cpp index 24c2c6e028..41fe072fc5 100644 --- a/src/openrct2/scenario/ScenarioRepository.cpp +++ b/src/openrct2/scenario/ScenarioRepository.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/scenario/ScenarioRepository.h b/src/openrct2/scenario/ScenarioRepository.h index 31146be112..c175e042d9 100644 --- a/src/openrct2/scenario/ScenarioRepository.h +++ b/src/openrct2/scenario/ScenarioRepository.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/scenario/ScenarioSources.cpp b/src/openrct2/scenario/ScenarioSources.cpp index 50de6c64e2..cb912f5895 100644 --- a/src/openrct2/scenario/ScenarioSources.cpp +++ b/src/openrct2/scenario/ScenarioSources.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -416,18 +416,3 @@ namespace ScenarioSources } } } // namespace ScenarioSources - -bool scenario_get_source_desc(const utf8* name, source_desc* outDesc) -{ - return ScenarioSources::TryGetByName(name, outDesc); -} - -bool scenario_get_source_desc_by_id(uint8_t id, source_desc* outDesc) -{ - return ScenarioSources::TryGetById(id, outDesc); -} - -void scenario_normalise_name(utf8* buffer, size_t bufferSize, utf8* name) -{ - ScenarioSources::NormaliseName(buffer, bufferSize, name); -} diff --git a/src/openrct2/scenario/ScenarioSources.h b/src/openrct2/scenario/ScenarioSources.h index 31677333ab..36e576f294 100644 --- a/src/openrct2/scenario/ScenarioSources.h +++ b/src/openrct2/scenario/ScenarioSources.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -27,10 +27,6 @@ namespace ScenarioSources void NormaliseName(utf8* buffer, size_t bufferSize, const utf8* name); } // namespace ScenarioSources -bool scenario_get_source_desc(const utf8* name, source_desc* outDesc); -bool scenario_get_source_desc_by_id(uint8_t id, source_desc* outDesc); -void scenario_normalise_name(utf8* buffer, size_t bufferSize, utf8* name); - // RCT1 scenario index map enum { diff --git a/src/openrct2/sprites.h b/src/openrct2/sprites.h index 5bad3cefca..25fa8f8fc8 100644 --- a/src/openrct2/sprites.h +++ b/src/openrct2/sprites.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,12 +14,13 @@ enum { - SPR_NONE = -1, + SPR_NONE = -1, // 0x7FFFF // Used for on-demand drawing of dynamic memory - SPR_TEMP = 0x70000, + SPR_TEMP = 0x7FFFE, SPR_SCROLLING_TEXT_START = 1542, + SPR_SCROLLING_TEXT_END = SPR_SCROLLING_TEXT_START + 32, SPR_SCROLLING_TEXT_DEFAULT = 1574, SPR_EDGE_ROCK_BASE = 1579, @@ -39,6 +40,8 @@ enum PEEP_SPAWN_ARROW_2 = 3113, PEEP_SPAWN_ARROW_3 = 3114, + SPR_BLANK_TILE = 3123, + // This is the start of every character there are // 224 characters per font (first 32 are control codes hence why it doesn't go to 255) // 4 fonts @@ -736,7 +739,7 @@ enum SPR_G1_END = 29294, SPR_RCTC_G1_END = 29357, // The number of elements in RCTC's g1.dat file - SPR_G2_BEGIN = 324288, + SPR_G2_BEGIN = 29357, SPR_G2_LOGO = SPR_G2_BEGIN + 0, SPR_G2_TITLE = SPR_G2_BEGIN + 1, SPR_G2_FASTFORWARD = SPR_G2_BEGIN + 2, @@ -829,7 +832,27 @@ enum SPR_G2_EYEDROPPER = SPR_G2_BEGIN + 112, SPR_G2_CHAT = SPR_G2_BEGIN + 113, - SPR_G2_CHAR_BEGIN = SPR_G2_BEGIN + 114, + SPR_G2_MAP_NORTH = SPR_G2_BEGIN + 114, + SPR_G2_MAP_NORTH_PRESSED = SPR_G2_BEGIN + 115, + SPR_G2_MAP_WEST = SPR_G2_BEGIN + 116, + SPR_G2_MAP_WEST_PRESSED = SPR_G2_BEGIN + 117, + SPR_G2_MAP_SOUTH = SPR_G2_BEGIN + 118, + SPR_G2_MAP_SOUTH_PRESSED = SPR_G2_BEGIN + 119, + SPR_G2_MAP_EAST = SPR_G2_BEGIN + 120, + SPR_G2_MAP_EAST_PRESSED = SPR_G2_BEGIN + 121, + + SPR_G2_TOOLBAR_MULTIPLAYER = SPR_G2_BEGIN + 122, + SPR_G2_TOOLBAR_MULTIPLAYER_PRESSED = SPR_G2_BEGIN + 123, + SPR_G2_MULTIPLAYER_SYNC = SPR_G2_BEGIN + 124, + SPR_G2_MULTIPLAYER_DESYNC = SPR_G2_BEGIN + 125, + + SPR_G2_SIMULATE = SPR_G2_BEGIN + 126, + SPR_G2_RCT1_SIMULATE_BUTTON_0 = SPR_G2_BEGIN + 127, + SPR_G2_RCT1_SIMULATE_BUTTON_1 = SPR_G2_BEGIN + 128, + SPR_G2_RCT1_SIMULATE_BUTTON_2 = SPR_G2_BEGIN + 129, + SPR_G2_RCT1_SIMULATE_BUTTON_3 = SPR_G2_BEGIN + 130, + + SPR_G2_CHAR_BEGIN = SPR_G2_BEGIN + 131, SPR_G2_AE_UPPER = SPR_G2_CHAR_BEGIN, SPR_G2_AE_LOWER = SPR_G2_CHAR_BEGIN + 1, @@ -917,21 +940,29 @@ enum SPR_G2_D_CARON_LOWER = SPR_G2_CHAR_BEGIN + 71, SPR_G2_E_CARON_UPPER = SPR_G2_CHAR_BEGIN + 72, SPR_G2_E_CARON_LOWER = SPR_G2_CHAR_BEGIN + 73, + SPR_G2_N_CARON_UPPER = SPR_G2_CHAR_BEGIN + 74, + SPR_G2_N_CARON_LOWER = SPR_G2_CHAR_BEGIN + 75, + SPR_G2_R_CARON_UPPER = SPR_G2_CHAR_BEGIN + 76, + SPR_G2_R_CARON_LOWER = SPR_G2_CHAR_BEGIN + 77, + SPR_G2_S_CARON_UPPER = SPR_G2_CHAR_BEGIN + 78, + SPR_G2_S_CARON_LOWER = SPR_G2_CHAR_BEGIN + 79, + SPR_G2_T_CARON_UPPER = SPR_G2_CHAR_BEGIN + 80, + SPR_G2_T_CARON_LOWER = SPR_G2_CHAR_BEGIN + 81, + SPR_G2_U_RING_UPPER = SPR_G2_CHAR_BEGIN + 82, + SPR_G2_U_RING_LOWER = SPR_G2_CHAR_BEGIN + 83, + SPR_G2_Z_CARON_UPPER = SPR_G2_CHAR_BEGIN + 84, + SPR_G2_Z_CARON_LOWER = SPR_G2_CHAR_BEGIN + 85, - SPR_G2_ROUBLE_SIGN = SPR_G2_CHAR_BEGIN + 74, - SPR_G2_CHAR_END = SPR_G2_ROUBLE_SIGN, - SPR_G2_GLYPH_COUNT = (SPR_G2_CHAR_END - SPR_G2_CHAR_BEGIN) + 1, + SPR_G2_ROUBLE_SIGN = SPR_G2_CHAR_BEGIN + 86, + SPR_G2_GLYPH_COUNT = (SPR_G2_ROUBLE_SIGN - SPR_G2_CHAR_BEGIN) + 1, + SPR_G2_CHAR_END = SPR_G2_CHAR_BEGIN + SPR_G2_GLYPH_COUNT * 3, + SPR_G2_END = SPR_G2_CHAR_END, - // 0x60000, chosen because it's a round hex number - // of the last possible range of image ID values that is large enough to fit all csg1 sprites. - SPR_CSG_BEGIN = 393216, + SPR_CSG_BEGIN = SPR_G2_END, + SPR_CSG_END = SPR_CSG_BEGIN + 69917, - SPR_CSG_ICE_CREAM_STALL_BEGIN = SPR_CSG_BEGIN + 60625, - SPR_CSG_TOILETS_BEGIN = SPR_CSG_BEGIN + 61289, - - SPR_CSG_RIDE_PREVIEWS_BEGIN = SPR_CSG_BEGIN + 64195, - SPR_CSG_RIDE_PREVIEW_ICE_CREAM_STALL = SPR_CSG_RIDE_PREVIEWS_BEGIN + RCT1_RIDE_TYPE_ICE_CREAM_STALL, - SPR_CSG_RIDE_PREVIEW_TOILETS = SPR_CSG_RIDE_PREVIEWS_BEGIN + RCT1_RIDE_TYPE_TOILETS, + SPR_IMAGE_LIST_BEGIN = SPR_CSG_END, + SPR_IMAGE_LIST_END = 0x7FFFE }; #endif diff --git a/src/openrct2/title/TitleScreen.cpp b/src/openrct2/title/TitleScreen.cpp index 06a0ed6226..8643d5b48d 100644 --- a/src/openrct2/title/TitleScreen.cpp +++ b/src/openrct2/title/TitleScreen.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/title/TitleScreen.h b/src/openrct2/title/TitleScreen.h index ef634a7afb..4ded4f646a 100644 --- a/src/openrct2/title/TitleScreen.h +++ b/src/openrct2/title/TitleScreen.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/title/TitleSequence.cpp b/src/openrct2/title/TitleSequence.cpp index d771514179..c04cc3e4ea 100644 --- a/src/openrct2/title/TitleSequence.cpp +++ b/src/openrct2/title/TitleSequence.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/title/TitleSequence.h b/src/openrct2/title/TitleSequence.h index d12a68a221..8bfbcd65b1 100644 --- a/src/openrct2/title/TitleSequence.h +++ b/src/openrct2/title/TitleSequence.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/title/TitleSequenceManager.cpp b/src/openrct2/title/TitleSequenceManager.cpp index f2b4b5421e..1c6913d1c1 100644 --- a/src/openrct2/title/TitleSequenceManager.cpp +++ b/src/openrct2/title/TitleSequenceManager.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/title/TitleSequenceManager.h b/src/openrct2/title/TitleSequenceManager.h index 3739c64840..dea64298ca 100644 --- a/src/openrct2/title/TitleSequenceManager.h +++ b/src/openrct2/title/TitleSequenceManager.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/title/TitleSequencePlayer.h b/src/openrct2/title/TitleSequencePlayer.h index 2af337b254..50889652d7 100644 --- a/src/openrct2/title/TitleSequencePlayer.h +++ b/src/openrct2/title/TitleSequencePlayer.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ui/DummyUiContext.cpp b/src/openrct2/ui/DummyUiContext.cpp index f8d42ce594..5d65b31af5 100644 --- a/src/openrct2/ui/DummyUiContext.cpp +++ b/src/openrct2/ui/DummyUiContext.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ui/DummyWindowManager.cpp b/src/openrct2/ui/DummyWindowManager.cpp index 42ae86ecdf..3550c864e8 100644 --- a/src/openrct2/ui/DummyWindowManager.cpp +++ b/src/openrct2/ui/DummyWindowManager.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ui/UiContext.h b/src/openrct2/ui/UiContext.h index c7080d2add..2b1a336564 100644 --- a/src/openrct2/ui/UiContext.h +++ b/src/openrct2/ui/UiContext.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/ui/WindowManager.h b/src/openrct2/ui/WindowManager.h index edb3865848..2556d9a8c5 100644 --- a/src/openrct2/ui/WindowManager.h +++ b/src/openrct2/ui/WindowManager.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/util/SawyerCoding.cpp b/src/openrct2/util/SawyerCoding.cpp index 5326d24eb4..c5c3a408d0 100644 --- a/src/openrct2/util/SawyerCoding.cpp +++ b/src/openrct2/util/SawyerCoding.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -44,7 +44,7 @@ size_t sawyercoding_write_chunk_buffer(uint8_t* dst_file, const uint8_t* buffer, { uint8_t *encode_buffer, *encode_buffer2; - if (gUseRLE == false) + if (!gUseRLE) { if (chunkHeader.encoding == CHUNK_ENCODING_RLE || chunkHeader.encoding == CHUNK_ENCODING_RLECOMPRESSED) { diff --git a/src/openrct2/util/SawyerCoding.h b/src/openrct2/util/SawyerCoding.h index 24c178928e..b29b1cdada 100644 --- a/src/openrct2/util/SawyerCoding.h +++ b/src/openrct2/util/SawyerCoding.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/util/Util.cpp b/src/openrct2/util/Util.cpp index 027fd25bbb..0cd47c5cfb 100644 --- a/src/openrct2/util/Util.cpp +++ b/src/openrct2/util/Util.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -21,6 +21,7 @@ #include #include #include +#include int32_t squaredmetres_to_squaredfeet(int32_t squaredMetres) { @@ -223,10 +224,10 @@ bool sse41_available() bool avx2_available() { #ifdef OPENRCT2_X86 -// For GCC and similar use the builtin function, as cpuid changed its semantics in -// https://github.com/gcc-mirror/gcc/commit/132fa33ce998df69a9f793d63785785f4b93e6f1 -// which causes it to ignore subleafs, but the new function is unavailable on Ubuntu's -// prehistoric toolchains + // For GCC and similar use the builtin function, as cpuid changed its semantics in + // https://github.com/gcc-mirror/gcc/commit/132fa33ce998df69a9f793d63785785f4b93e6f1 + // which causes it to ignore subleafs, but the new function is unavailable on + // Ubuntu 18.04's toolchains. # if defined(OpenRCT2_CPUID_GNUC_X86) && (!defined(__FreeBSD__) || (__FreeBSD__ > 10)) return __builtin_cpu_supports("avx2"); # else @@ -234,7 +235,15 @@ bool avx2_available() uint32_t regs[4] = { 0 }; if (cpuid_x86(regs, 7)) { - return (regs[1] & (1 << 5)); + bool avxCPUSupport = (regs[1] & (1 << 5)) != 0; + if (avxCPUSupport) + { + // Need to check if OS also supports the register of xmm/ymm + // This check has to be conditional, otherwise INVALID_INSTRUCTION exception. + uint64_t xcrFeatureMask = _xgetbv(_XCR_XFEATURE_ENABLED_MASK); + avxCPUSupport = (xcrFeatureMask & 0x6) || false; + } + return avxCPUSupport; } # endif #endif @@ -296,11 +305,6 @@ int32_t bitcount(uint32_t source) return bitcount_fn(source); } -bool strequals(const char* a, const char* b, int32_t length, bool caseInsensitive) -{ - return caseInsensitive ? _strnicmp(a, b, length) == 0 : strncmp(a, b, length) == 0; -} - /* case insensitive compare */ int32_t strcicmp(char const* a, char const* b) { @@ -474,15 +478,6 @@ char* safe_strcat_path(char* destination, const char* source, size_t size) return safe_strcat(destination, source, size); } -char* safe_strtrimleft(char* destination, const char* source, size_t size) -{ - while (*source == ' ') - { - source++; - } - return safe_strcpy(destination, source, size); -} - #if defined(_WIN32) char* strcasestr(const char* haystack, const char* needle) { @@ -532,15 +527,10 @@ bool str_is_null_or_empty(const char* str) return str == nullptr || str[0] == 0; } -void util_srand(int32_t source) -{ - srand(source); -} - -// Caveat: rand() might only return values up to 0x7FFF, which is the minimum specified in the C standard. uint32_t util_rand() { - return rand(); + thread_local std::mt19937 _prng(std::random_device{}()); + return _prng(); } #define CHUNK (128 * 1024) diff --git a/src/openrct2/util/Util.h b/src/openrct2/util/Util.h index d8c45030e2..ec046e8036 100644 --- a/src/openrct2/util/Util.h +++ b/src/openrct2/util/Util.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -37,14 +37,12 @@ bool avx2_available(); int32_t bitscanforward(int32_t source); void bitcount_init(); int32_t bitcount(uint32_t source); -bool strequals(const char* a, const char* b, int32_t length, bool caseInsensitive); int32_t strcicmp(char const* a, char const* b); int32_t strlogicalcmp(char const* a, char const* b); utf8* safe_strtrunc(utf8* text, size_t size); char* safe_strcpy(char* destination, const char* source, size_t num); char* safe_strcat(char* destination, const char* source, size_t size); char* safe_strcat_path(char* destination, const char* source, size_t size); -char* safe_strtrimleft(char* destination, const char* source, size_t size); #if defined(_WIN32) char* strcasestr(const char* haystack, const char* needle); #endif @@ -52,7 +50,6 @@ char* strcasestr(const char* haystack, const char* needle); bool utf8_is_bom(const char* str); bool str_is_null_or_empty(const char* str); -void util_srand(int32_t source); uint32_t util_rand(); uint8_t* util_zlib_deflate(const uint8_t* data, size_t data_in_size, size_t* data_out_size); diff --git a/src/openrct2/windows/Intent.cpp b/src/openrct2/windows/Intent.cpp index 37469b09a9..b472f740f3 100644 --- a/src/openrct2/windows/Intent.cpp +++ b/src/openrct2/windows/Intent.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/windows/Intent.h b/src/openrct2/windows/Intent.h index a0429f4091..9b062e07d2 100644 --- a/src/openrct2/windows/Intent.h +++ b/src/openrct2/windows/Intent.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/windows/_legacy.cpp b/src/openrct2/windows/_legacy.cpp index 207255dd2b..da42d4e16e 100644 --- a/src/openrct2/windows/_legacy.cpp +++ b/src/openrct2/windows/_legacy.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -29,77 +29,6 @@ bool gDisableErrorWindowSound = false; -void game_command_callback_pickup_guest( - int32_t eax, int32_t ebx, int32_t ecx, [[maybe_unused]] int32_t edx, [[maybe_unused]] int32_t esi, - [[maybe_unused]] int32_t edi, [[maybe_unused]] int32_t ebp) -{ - switch (ecx) - { - case 0: - { - int32_t peepnum = eax; - rct_window* w = window_find_by_number(WC_PEEP, peepnum); - if (w) - { - tool_set(w, WC_PEEP__WIDX_PICKUP, TOOL_PICKER); - } - } - break; - case 2: - if (ebx == 0) - { - tool_cancel(); - gPickupPeepImage = UINT32_MAX; - } - break; - } -} - -void game_command_callback_hire_new_staff_member( - [[maybe_unused]] int32_t eax, [[maybe_unused]] int32_t ebx, [[maybe_unused]] int32_t ecx, [[maybe_unused]] int32_t edx, - [[maybe_unused]] int32_t esi, int32_t edi, [[maybe_unused]] int32_t ebp) -{ - int32_t sprite_index = edi; - if (sprite_index == SPRITE_INDEX_NULL) - { - rct_window* window = window_find_by_class(WC_STAFF_LIST); - window_invalidate(window); - } - else - { - Peep* peep = &get_sprite(sprite_index)->peep; - auto intent = Intent(WC_PEEP); - intent.putExtra(INTENT_EXTRA_PEEP, peep); - context_open_intent(&intent); - } -} - -void game_command_callback_pickup_staff( - int32_t eax, int32_t ebx, int32_t ecx, [[maybe_unused]] int32_t edx, [[maybe_unused]] int32_t esi, - [[maybe_unused]] int32_t edi, [[maybe_unused]] int32_t ebp) -{ - switch (ecx) - { - case 0: - { - int32_t peepnum = eax; - rct_window* w = window_find_by_number(WC_PEEP, peepnum); - if (w) - { - tool_set(w, WC_STAFF__WIDX_PICKUP, TOOL_PICKER); - } - } - break; - case 2: - if (ebx == 0) - { - tool_cancel(); - gPickupPeepImage = UINT32_MAX; - } - break; - } -} - uint64_t _enabledRidePieces; uint8_t _rideConstructionState2; @@ -116,14 +45,15 @@ money32 place_provisional_track_piece( ride_id_t rideIndex, int32_t trackType, int32_t trackDirection, int32_t liftHillAndAlternativeState, int32_t x, int32_t y, int32_t z) { - Ride* ride; - money32 result; + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return MONEY32_UNDEFINED; + money32 result; ride_construction_remove_ghosts(); - ride = get_ride(rideIndex); if (ride->type == RIDE_TYPE_MAZE) { - int32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 + int32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; // 105 result = maze_set_track(x, y, z, flags, true, 0, rideIndex, GC_SET_MAZE_TRACK_BUILD); if (result == MONEY32_UNDEFINED) @@ -134,7 +64,7 @@ money32 place_provisional_track_piece( _unkF440C5.z = z; _unkF440C5.direction = trackDirection; _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_TRACK; - viewport_set_visibility((gTrackGroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) ? 1 : 3); + viewport_set_visibility(3); if (_currentTrackSlopeEnd != 0) viewport_set_visibility(2); @@ -153,7 +83,7 @@ money32 place_provisional_track_piece( { auto trackPlaceAction = TrackPlaceAction( rideIndex, trackType, { x, y, z, static_cast(trackDirection) }, 0, 0, 0, liftHillAndAlternativeState); - trackPlaceAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_GHOST); + trackPlaceAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); // This command must not be sent over the network auto res = GameActions::Execute(&trackPlaceAction); result = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; @@ -179,7 +109,8 @@ money32 place_provisional_track_piece( _unkF440C5.z = z; _unkF440C5.direction = trackDirection; _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_TRACK; - viewport_set_visibility((gTrackGroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) ? 1 : 3); + viewport_set_visibility( + (dynamic_cast(res.get())->GroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) ? 1 : 3); if (_currentTrackSlopeEnd != 0) viewport_set_visibility(2); @@ -340,7 +271,9 @@ bool window_ride_construction_update_state( liftHillAndAlternativeState |= RIDE_TYPE_ALTERNATIVE_TRACK_TYPE; } - Ride* ride = get_ride(rideIndex); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return true; if (_enabledRidePieces & (1ULL << TRACK_SLOPE_STEEP_LONG)) { @@ -404,28 +337,11 @@ bool window_ride_construction_update_state( trackDirection |= 0x04; } - switch (trackDirection & 0x03) - { - case 0: - x -= trackCoordinates->x; - y -= trackCoordinates->y; - break; - - case 1: - x -= trackCoordinates->y; - y += trackCoordinates->x; - break; - - case 2: - x += trackCoordinates->x; - y += trackCoordinates->y; - break; - - case 3: - x += trackCoordinates->y; - y -= trackCoordinates->x; - break; - } + CoordsXY offsets = { trackCoordinates->x, trackCoordinates->y }; + CoordsXY coords = { x, y }; + coords += offsets.Rotate(direction_reverse(trackDirection)); + x = (uint16_t)coords.x; + y = (uint16_t)coords.y; } else { @@ -489,9 +405,8 @@ bool window_ride_construction_update_state( void window_ride_construction_do_entrance_exit_check() { - rct_window* w = window_find_by_class(WC_RIDE_CONSTRUCTION); - Ride* ride = get_ride(_currentRideIndex); - + auto w = window_find_by_class(WC_RIDE_CONSTRUCTION); + auto ride = get_ride(_currentRideIndex); if (w == nullptr || ride == nullptr) { return; @@ -516,7 +431,7 @@ void window_ride_construction_do_entrance_exit_check() void window_ride_construction_do_station_check() { - Ride* ride = get_ride(_currentRideIndex); + auto ride = get_ride(_currentRideIndex); if (ride != nullptr) { _stationConstructed = ride->num_stations != 0; @@ -611,19 +526,6 @@ void window_ride_construction_update_active_elements() context_broadcast_intent(&intent); } -void game_command_callback_place_banner( - [[maybe_unused]] int32_t eax, int32_t ebx, [[maybe_unused]] int32_t ecx, [[maybe_unused]] int32_t edx, - [[maybe_unused]] int32_t esi, int32_t edi, [[maybe_unused]] int32_t ebp) -{ - if (ebx != MONEY32_UNDEFINED) - { - int32_t bannerId = edi; - - audio_play_sound_at_location(SOUND_PLACE_ITEM, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z); - context_open_detail_window(WD_BANNER, bannerId); - } -} - /** * * rct2: 0x0066DB3D diff --git a/src/openrct2/windows/tile_inspector.h b/src/openrct2/windows/tile_inspector.h index 3f2a4b62b4..2766549507 100644 --- a/src/openrct2/windows/tile_inspector.h +++ b/src/openrct2/windows/tile_inspector.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/world/Balloon.cpp b/src/openrct2/world/Balloon.cpp index ee0b40520b..aff99b9dd7 100644 --- a/src/openrct2/world/Balloon.cpp +++ b/src/openrct2/world/Balloon.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -81,30 +81,12 @@ void rct_balloon::Pop() { popped = 1; frame = 0; - audio_play_sound_at_location(SOUND_BALLOON_POP, x, y, z); -} - -static money32 game_command_balloon_press(uint16_t spriteIndex, uint8_t flags) -{ - rct_sprite* sprite = try_get_sprite(spriteIndex); - if (sprite == nullptr || !sprite->IsBalloon()) - { - log_error("Tried getting invalid sprite for balloon: %u", spriteIndex); - return MONEY32_UNDEFINED; - } - else - { - if (flags & GAME_COMMAND_FLAG_APPLY) - { - sprite->AsBalloon()->Press(); - } - return 0; - } + audio_play_sound_at_location(SoundId::BalloonPop, { x, y, z }); } void create_balloon(int32_t x, int32_t y, int32_t z, int32_t colour, bool isPopped) { - rct_sprite* sprite = create_sprite(2); + rct_sprite* sprite = create_sprite(SPRITE_IDENTIFIER_MISC); if (sprite != nullptr) { sprite->balloon.sprite_width = 13; @@ -124,10 +106,3 @@ void balloon_update(rct_balloon* balloon) { balloon->Update(); } - -void game_command_balloon_press( - int32_t* eax, int32_t* ebx, [[maybe_unused]] int32_t* ecx, [[maybe_unused]] int32_t* edx, [[maybe_unused]] int32_t* esi, - [[maybe_unused]] int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - *ebx = game_command_balloon_press(*eax & 0xFFFF, *ebx & 0xFF); -} diff --git a/src/openrct2/world/Banner.cpp b/src/openrct2/world/Banner.cpp index e97d51b537..4ca8242390 100644 --- a/src/openrct2/world/Banner.cpp +++ b/src/openrct2/world/Banner.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -30,7 +30,48 @@ #include #include -rct_banner gBanners[MAX_BANNERS]; +static Banner _banners[MAX_BANNERS]; + +std::string Banner::GetText() const +{ + uint8_t args[32]{}; + FormatTextTo(args); + return format_string(STR_STRINGID, args); +} + +size_t Banner::FormatTextTo(void* argsV) const +{ + auto args = (uint8_t*)argsV; + if (flags & BANNER_FLAG_NO_ENTRY) + { + set_format_arg_on(args, 0, rct_string_id, STR_NO_ENTRY); + return sizeof(rct_string_id); + } + else if (flags & BANNER_FLAG_LINKED_TO_RIDE) + { + auto ride = get_ride(ride_index); + if (ride != nullptr) + { + return ride->FormatNameTo(args); + } + else + { + set_format_arg_on(args, 0, rct_string_id, STR_DEFAULT_SIGN); + return sizeof(rct_string_id); + } + } + else if (text.empty()) + { + set_format_arg_on(args, 0, rct_string_id, STR_DEFAULT_SIGN); + return sizeof(rct_string_id); + } + else + { + set_format_arg_on(args, 0, rct_string_id, STR_STRING); + set_format_arg_on(args, 2, const char*, text.c_str()); + return sizeof(rct_string_id) + sizeof(const char*); + } +} /** * @@ -40,14 +81,16 @@ static uint8_t banner_get_ride_index_at(int32_t x, int32_t y, int32_t z) { TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); ride_id_t resultRideIndex = RIDE_ID_NULL; + if (tileElement == nullptr) + return resultRideIndex; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) continue; ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) + auto ride = get_ride(rideIndex); + if (ride == nullptr || ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) continue; if ((tileElement->clearance_height * 8) + 32 <= z) @@ -59,292 +102,11 @@ static uint8_t banner_get_ride_index_at(int32_t x, int32_t y, int32_t z) return resultRideIndex; } -static money32 BannerRemove(int16_t x, int16_t y, uint8_t baseHeight, uint8_t direction, uint8_t flags) -{ - int32_t z = baseHeight * 8; - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = z; - - if (!(flags & GAME_COMMAND_FLAG_GHOST) && game_is_paused() && !gCheatsBuildInPauseMode) - { - gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - return MONEY32_UNDEFINED; - } - - if (!map_can_build_at(x, y, z - 16)) - { - return MONEY32_UNDEFINED; - } - - // Slight modification to the code so that it now checks height as well - // This was causing a bug with banners on two paths stacked. - BannerElement* tileElement = map_get_banner_element_at(x / 32, y / 32, baseHeight, direction); - if (tileElement == nullptr) - { - return MONEY32_UNDEFINED; - } - - rct_banner* banner = &gBanners[tileElement->GetIndex()]; - rct_scenery_entry* bannerEntry = get_banner_entry(banner->type); - money32 refund = 0; - if (bannerEntry != nullptr) - { - refund = -((bannerEntry->banner.price * 3) / 4); - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST)) - { - LocationXYZ16 coord; - coord.x = x + 16; - coord.y = y + 16; - coord.z = tile_element_height(coord.x, coord.y); - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - - tile_element_remove_banner_entry((TileElement*)tileElement); - map_invalidate_tile_zoom1(x, y, z, z + 32); - tileElement->Remove(); - } - - if (gParkFlags & PARK_FLAGS_NO_MONEY) - { - refund = 0; - } - return refund; -} - -static money32 BannerSetColour(int16_t x, int16_t y, uint8_t baseHeight, uint8_t direction, uint8_t colour, uint8_t flags) -{ - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - int32_t z = (baseHeight * 8); - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = z; - - if (!map_can_build_at(x, y, z - 16)) - { - return MONEY32_UNDEFINED; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); - - bool found = false; - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER) - continue; - - if (tileElement->AsBanner()->GetPosition() != direction) - continue; - - found = true; - break; - } while (!(tileElement++)->IsLastForTile()); - - if (!found) - { - return MONEY32_UNDEFINED; - } - - auto intent = Intent(INTENT_ACTION_UPDATE_BANNER); - intent.putExtra(INTENT_EXTRA_BANNER_INDEX, tileElement->AsBanner()->GetIndex()); - context_broadcast_intent(&intent); - - gBanners[tileElement->AsBanner()->GetIndex()].colour = colour; - map_invalidate_tile_zoom1(x, y, z, z + 32); - } - - return 0; -} - -static money32 BannerPlace( - int16_t x, int16_t y, uint8_t pathBaseHeight, uint8_t direction, uint8_t colour, uint8_t type, BannerIndex* bannerIndex, - uint8_t flags) -{ - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = pathBaseHeight * 16; - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - if (game_is_paused() && !gCheatsBuildInPauseMode) - { - gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - return MONEY32_UNDEFINED; - } - - if (!map_check_free_elements_and_reorganise(1)) - { - return MONEY32_UNDEFINED; - } - - if (!map_is_location_valid({ x, y })) - { - return MONEY32_UNDEFINED; - } - - TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); - - bool pathFound = false; - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) - continue; - - if (tileElement->base_height != pathBaseHeight * 2 && tileElement->base_height != (pathBaseHeight - 1) * 2) - continue; - - if (!(tileElement->AsPath()->GetEdges() & (1 << direction))) - continue; - - pathFound = true; - break; - } while (!(tileElement++)->IsLastForTile()); - - if (!pathFound) - { - gGameCommandErrorText = STR_CAN_ONLY_BE_BUILT_ACROSS_PATHS; - return MONEY32_UNDEFINED; - } - - if (!map_can_build_at(x, y, pathBaseHeight * 16)) - { - return MONEY32_UNDEFINED; - } - - uint8_t baseHeight = (pathBaseHeight + 1) * 2; - BannerElement* bannerElement = map_get_banner_element_at(x / 32, y / 32, baseHeight, direction); - if (bannerElement != nullptr) - { - gGameCommandErrorText = STR_BANNER_SIGN_IN_THE_WAY; - return MONEY32_UNDEFINED; - } - - *bannerIndex = create_new_banner(flags); - if (*bannerIndex == BANNER_INDEX_NULL) - { - return MONEY32_UNDEFINED; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST)) - { - LocationXYZ16 coord; - coord.x = x + 16; - coord.y = y + 16; - coord.z = tile_element_height(coord.x, coord.y); - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - - TileElement* newTileElement = tile_element_insert(x / 32, y / 32, baseHeight, 0); - assert(newTileElement != nullptr); - gBanners[*bannerIndex].type = type; - gBanners[*bannerIndex].colour = colour; - gBanners[*bannerIndex].x = x / 32; - gBanners[*bannerIndex].y = y / 32; - newTileElement->SetType(TILE_ELEMENT_TYPE_BANNER); - newTileElement->clearance_height = newTileElement->base_height + 2; - newTileElement->AsBanner()->SetPosition(direction); - newTileElement->AsBanner()->ResetAllowedEdges(); - newTileElement->AsBanner()->SetIndex(*bannerIndex); - if (flags & GAME_COMMAND_FLAG_GHOST) - { - newTileElement->SetGhost(true); - } - map_invalidate_tile_full(x, y); - map_animation_create(MAP_ANIMATION_TYPE_BANNER, x, y, newTileElement->base_height); - } - - rct_scenery_entry* bannerEntry = get_banner_entry(type); - if (bannerEntry == nullptr) - { - return MONEY32_UNDEFINED; - } - - if (gParkFlags & PARK_FLAGS_NO_MONEY) - { - return 0; - } - return bannerEntry->banner.price; -} - -static money32 BannerSetStyle(BannerIndex bannerIndex, uint8_t colour, uint8_t textColour, uint8_t bannerFlags, uint8_t flags) -{ - if (bannerIndex >= MAX_BANNERS) - { - gGameCommandErrorText = STR_INVALID_SELECTION_OF_OBJECTS; - return MONEY32_UNDEFINED; - } - - rct_banner* banner = &gBanners[bannerIndex]; - - TileElement* tileElement = banner_get_tile_element(bannerIndex); - - if (tileElement == nullptr) - { - return MONEY32_UNDEFINED; - } - - if (!(flags & GAME_COMMAND_FLAG_APPLY)) - { - return 0; - } - - banner->colour = colour; - banner->text_colour = textColour; - banner->flags = bannerFlags; - - uint8_t allowedEdges = 0xF; - if (banner->flags & BANNER_FLAG_NO_ENTRY) - { - allowedEdges &= ~(1 << tileElement->AsBanner()->GetPosition()); - } - tileElement->AsBanner()->SetAllowedEdges(allowedEdges); - - int32_t colourCodepoint = FORMAT_COLOUR_CODE_START + banner->text_colour; - - utf8 buffer[256]; - format_string(buffer, 256, banner->string_idx, nullptr); - int32_t firstCodepoint = utf8_get_next(buffer, nullptr); - if (firstCodepoint >= FORMAT_COLOUR_CODE_START && firstCodepoint <= FORMAT_COLOUR_CODE_END) - { - utf8_write_codepoint(buffer, colourCodepoint); - } - else - { - utf8_insert_codepoint(buffer, colourCodepoint); - } - - rct_string_id stringId = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, buffer); - if (stringId != 0) - { - rct_string_id prevStringId = banner->string_idx; - banner->string_idx = stringId; - user_string_free(prevStringId); - - auto intent = Intent(INTENT_ACTION_UPDATE_BANNER); - intent.putExtra(INTENT_EXTRA_BANNER_INDEX, bannerIndex); - context_broadcast_intent(&intent); - } - else - { - gGameCommandErrorText = STR_ERR_CANT_SET_BANNER_TEXT; - return MONEY32_UNDEFINED; - } - - return 0; -} - static BannerIndex BannerGetNewIndex() { for (BannerIndex bannerIndex = 0; bannerIndex < MAX_BANNERS; bannerIndex++) { - if (gBanners[bannerIndex].type == BANNER_NULL) + if (_banners[bannerIndex].IsNull()) { return bannerIndex; } @@ -358,9 +120,9 @@ static BannerIndex BannerGetNewIndex() */ void banner_init() { - for (auto& banner : gBanners) + for (auto& banner : _banners) { - banner.type = BANNER_NULL; + banner = {}; } } @@ -384,11 +146,11 @@ BannerIndex create_new_banner(uint8_t flags) if (flags & GAME_COMMAND_FLAG_APPLY) { - rct_banner* banner = &gBanners[bannerIndex]; + auto banner = &_banners[bannerIndex]; banner->flags = 0; banner->type = 0; - banner->string_idx = STR_DEFAULT_SIGN; + banner->text = {}; banner->colour = 2; banner->text_colour = 2; } @@ -397,15 +159,49 @@ BannerIndex create_new_banner(uint8_t flags) TileElement* banner_get_tile_element(BannerIndex bannerIndex) { - rct_banner* banner = &gBanners[bannerIndex]; - TileElement* tileElement = map_get_first_element_at(banner->x, banner->y); + auto banner = GetBanner(bannerIndex); + if (banner != nullptr) + { + auto tileElement = map_get_first_element_at(banner->position.x, banner->position.y); + if (tileElement != nullptr) + { + do + { + if (tile_element_get_banner_index(tileElement) == bannerIndex) + { + return tileElement; + } + } while (!(tileElement++)->IsLastForTile()); + } + } + return nullptr; +} + +WallElement* banner_get_scrolling_wall_tile_element(BannerIndex bannerIndex) +{ + auto banner = GetBanner(bannerIndex); + if (banner == nullptr) + return nullptr; + + auto tileElement = map_get_first_element_at(banner->position.x, banner->position.y); + if (tileElement == nullptr) + return nullptr; + do { - if (tile_element_get_banner_index(tileElement) == bannerIndex) - { - return tileElement; - } + auto wallElement = tileElement->AsWall(); + + if (wallElement == nullptr) + continue; + + rct_scenery_entry* scenery_entry = wallElement->GetEntry(); + if (scenery_entry->wall.scrolling_mode == SCROLLING_MODE_NONE) + continue; + if (wallElement->GetBannerIndex() != bannerIndex) + continue; + return wallElement; } while (!(tileElement++)->IsLastForTile()); + return nullptr; } @@ -415,13 +211,11 @@ TileElement* banner_get_tile_element(BannerIndex bannerIndex) */ uint8_t banner_get_closest_ride_index(int32_t x, int32_t y, int32_t z) { - Ride* ride; - static constexpr const LocationXY16 NeighbourCheckOrder[] = { { 32, 0 }, { -32, 0 }, { 0, 32 }, { 0, -32 }, { -32, +32 }, { +32, -32 }, { +32, +32 }, { -32, +32 }, { 0, 0 } }; - for (size_t i = 0; i < (int32_t)std::size(NeighbourCheckOrder); i++) + for (size_t i = 0; i < std::size(NeighbourCheckOrder); i++) { ride_id_t rideIndex = banner_get_ride_index_at(x + NeighbourCheckOrder[i].x, y + NeighbourCheckOrder[i].y, z); if (rideIndex != RIDE_ID_NULL) @@ -430,15 +224,14 @@ uint8_t banner_get_closest_ride_index(int32_t x, int32_t y, int32_t z) } } - uint8_t index; - ride_id_t rideIndex = RIDE_ID_NULL; - int32_t resultDistance = std::numeric_limits::max(); - FOR_ALL_RIDES (index, ride) + auto rideIndex = RIDE_ID_NULL; + auto resultDistance = std::numeric_limits::max(); + for (auto& ride : GetRideManager()) { - if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) + if (ride_type_has_flag(ride.type, RIDE_TYPE_FLAG_IS_SHOP)) continue; - LocationXY8 location = ride->overall_view; + LocationXY8 location = ride.overall_view; if (location.xy == RCT_XY8_UNDEFINED) continue; @@ -448,10 +241,9 @@ uint8_t banner_get_closest_ride_index(int32_t x, int32_t y, int32_t z) if (distance < resultDistance) { resultDistance = distance; - rideIndex = index; + rideIndex = ride.id; } } - return rideIndex; } @@ -459,115 +251,77 @@ void banner_reset_broken_index() { for (BannerIndex bannerIndex = 0; bannerIndex < MAX_BANNERS; bannerIndex++) { - TileElement* tileElement = banner_get_tile_element(bannerIndex); + auto tileElement = banner_get_tile_element(bannerIndex); if (tileElement == nullptr) - gBanners[bannerIndex].type = BANNER_NULL; + { + _banners[bannerIndex].type = BANNER_NULL; + } } } void fix_duplicated_banners() { // For each banner in the map, check if the banner index is in use already, and if so, create a new entry for it - bool activeBanners[std::size(gBanners)]{}; - TileElement* tileElement; + bool activeBanners[std::size(_banners)]{}; for (int y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) { for (int x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) { - tileElement = map_get_first_element_at(x, y); - do + auto tileElement = map_get_first_element_at(x, y); + if (tileElement != nullptr) { - // TODO: Handle walls and large-scenery that use banner indices too. Large scenery can be tricky, as they occupy - // multiple tiles that should both refer to the same banner index. - if (tileElement->GetType() == TILE_ELEMENT_TYPE_BANNER) + do { - uint8_t bannerIndex = tileElement->AsBanner()->GetIndex(); - if (activeBanners[bannerIndex]) + // TODO: Handle walls and large-scenery that use banner indices too. Large scenery can be tricky, as they + // occupy multiple tiles that should both refer to the same banner index. + if (tileElement->GetType() == TILE_ELEMENT_TYPE_BANNER) { - log_info( - "Duplicated banner with index %d found at x = %d, y = %d and z = %d.", bannerIndex, x, y, - tileElement->base_height); - - // Banner index is already in use by another banner, so duplicate it - BannerIndex newBannerIndex = create_new_banner(GAME_COMMAND_FLAG_APPLY); - if (newBannerIndex == BANNER_INDEX_NULL) + uint8_t bannerIndex = tileElement->AsBanner()->GetIndex(); + if (activeBanners[bannerIndex]) { - log_error("Failed to create new banner."); - continue; - } - Guard::Assert(activeBanners[newBannerIndex] == false); + log_info( + "Duplicated banner with index %d found at x = %d, y = %d and z = %d.", bannerIndex, x, y, + tileElement->base_height); - // Copy over the original banner, but update the location - rct_banner& newBanner = gBanners[newBannerIndex]; - newBanner = gBanners[bannerIndex]; - newBanner.x = x; - newBanner.y = y; - - // Duplicate user string too - rct_string_id stringIdx = newBanner.string_idx; - if (is_user_string_id(stringIdx)) - { - utf8 buffer[USER_STRING_MAX_LENGTH]; - format_string(buffer, USER_STRING_MAX_LENGTH, stringIdx, nullptr); - rct_string_id newStringIdx = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, buffer); - if (newStringIdx == 0) + // Banner index is already in use by another banner, so duplicate it + BannerIndex newBannerIndex = create_new_banner(GAME_COMMAND_FLAG_APPLY); + if (newBannerIndex == BANNER_INDEX_NULL) { - log_error("Failed to allocate user string for banner"); + log_error("Failed to create new banner."); continue; } - newBanner.string_idx = newStringIdx; + Guard::Assert(!activeBanners[newBannerIndex]); + + // Copy over the original banner, but update the location + auto& newBanner = *GetBanner(newBannerIndex); + newBanner = *GetBanner(bannerIndex); + newBanner.position = { x, y }; + + tileElement->AsBanner()->SetIndex(newBannerIndex); } - tileElement->AsBanner()->SetIndex(newBannerIndex); + // Mark banner index as in-use + activeBanners[bannerIndex] = true; } - - // Mark banner index as in-use - activeBanners[bannerIndex] = true; - } - } while (!(tileElement++)->IsLastForTile()); + } while (!(tileElement++)->IsLastForTile()); + } } } } -/** - * - * rct2: 0x006BA058 - */ -void game_command_remove_banner( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, - [[maybe_unused]] int32_t* ebp) +Banner* BannerElement::GetBanner() const { - *ebx = BannerRemove(*eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*edx >> 8) & 0xFF, *ebx & 0xFF); + return ::GetBanner(GetIndex()); } -/** - * - * rct2: 0x006BA16A - */ -void game_command_set_banner_colour( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, - int32_t* ebp) +rct_scenery_entry* BannerElement::GetEntry() const { - *ebx = BannerSetColour(*eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*edx >> 8) & 0xFF, *ebp & 0xFF, *ebx & 0xFF); -} - -/** - * - * rct2: 0x006B9E6D - */ -void game_command_place_banner( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - *ebx = BannerPlace( - *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*edx >> 8) & 0xFF, *ebp & 0xFF, (*ebx >> 8) & 0xFF, (BannerIndex*)edi, - *ebx & 0xFF); -} - -void game_command_set_banner_style( - [[maybe_unused]] int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, - int32_t* ebp) -{ - *ebx = BannerSetStyle(*ecx & 0xFF, *edx & 0xFF, *edi & 0xFF, *ebp & 0xFF, *ebx & 0xFF); + auto banner = GetBanner(); + if (banner != nullptr) + { + return get_banner_entry(banner->type); + } + return nullptr; } BannerIndex BannerElement::GetIndex() const @@ -605,3 +359,12 @@ void BannerElement::ResetAllowedEdges() { flags |= 0b00001111; } + +Banner* GetBanner(BannerIndex id) +{ + if (id < std::size(_banners)) + { + return &_banners[id]; + } + return nullptr; +} diff --git a/src/openrct2/world/Banner.h b/src/openrct2/world/Banner.h index a4a9cb0c5a..0fe1d373d8 100644 --- a/src/openrct2/world/Banner.h +++ b/src/openrct2/world/Banner.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,23 +18,24 @@ constexpr BannerIndex BANNER_INDEX_NULL = (BannerIndex)-1; constexpr uint8_t SCROLLING_MODE_NONE = 255; -#pragma pack(push, 1) -struct rct_banner +struct Banner { - uint8_t type; - uint8_t flags; // 0x01 - rct_string_id string_idx; // 0x02 - union + uint8_t type = BANNER_NULL; + uint8_t flags{}; + std::string text; + uint8_t colour{}; + ride_id_t ride_index{}; + uint8_t text_colour{}; + TileCoordsXY position; + + bool IsNull() const { - uint8_t colour; // 0x04 - uint8_t ride_index; // 0x04 - }; - uint8_t text_colour; // 0x05 - uint8_t x; // 0x06 - uint8_t y; // 0x07 + return type == BANNER_NULL; + } + + std::string GetText() const; + size_t FormatTextTo(void* args) const; }; -assert_struct_size(rct_banner, 8); -#pragma pack(pop) enum BANNER_FLAGS { @@ -44,13 +45,11 @@ enum BANNER_FLAGS BANNER_FLAG_IS_WALL = (1 << 3) }; -extern rct_banner gBanners[MAX_BANNERS]; - void banner_init(); BannerIndex create_new_banner(uint8_t flags); TileElement* banner_get_tile_element(BannerIndex bannerIndex); +WallElement* banner_get_scrolling_wall_tile_element(BannerIndex bannerIndex); uint8_t banner_get_closest_ride_index(int32_t x, int32_t y, int32_t z); void banner_reset_broken_index(); void fix_duplicated_banners(); -void game_command_callback_place_banner( - int32_t eax, int32_t ebx, int32_t ecx, int32_t edx, int32_t esi, int32_t edi, int32_t ebp); +Banner* GetBanner(BannerIndex id); diff --git a/src/openrct2/world/Climate.cpp b/src/openrct2/world/Climate.cpp index 03d1016866..22f2b0075f 100644 --- a/src/openrct2/world/Climate.cpp +++ b/src/openrct2/world/Climate.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -59,7 +59,7 @@ static uint32_t _lightningTimer; static uint32_t _thunderTimer; static void* _thunderSoundChannels[MAX_THUNDER_INSTANCES]; static THUNDER_STATUS _thunderStatus[MAX_THUNDER_INSTANCES] = { THUNDER_STATUS::NONE, THUNDER_STATUS::NONE }; -static uint32_t _thunderSoundId; +static SoundId _thunderSoundId; static int32_t _thunderVolume; static int32_t _thunderStereoEcho = 0; @@ -69,7 +69,7 @@ static void climate_update_rain_sound(); static void climate_update_thunder_sound(); static void climate_update_lightning(); static void climate_update_thunder(); -static void climate_play_thunder(int32_t instanceIndex, int32_t soundId, int32_t volume, int32_t pan); +static void climate_play_thunder(int32_t instanceIndex, SoundId soundId, int32_t volume, int32_t pan); int32_t climate_celsius_to_fahrenheit(int32_t celsius) { @@ -282,7 +282,7 @@ static void climate_update_rain_sound() // Start playing the rain sound if (gRainSoundChannel == nullptr) { - gRainSoundChannel = Mixer_Play_Effect(SOUND_RAIN_1, MIXER_LOOP_INFINITE, DStoMixerVolume(-4000), 0.5f, 1, 0); + gRainSoundChannel = Mixer_Play_Effect(SoundId::Rain, MIXER_LOOP_INFINITE, DStoMixerVolume(-4000), 0.5f, 1, 0); } if (_rainVolume == 1) { @@ -371,7 +371,7 @@ static void climate_update_thunder() if (_thunderStatus[0] == THUNDER_STATUS::NONE && _thunderStatus[1] == THUNDER_STATUS::NONE) { // Play thunder on left side - _thunderSoundId = (randomNumber & 0x20000) ? SOUND_THUNDER_1 : SOUND_THUNDER_2; + _thunderSoundId = (randomNumber & 0x20000) ? SoundId::Thunder1 : SoundId::Thunder2; _thunderVolume = (-((int32_t)((randomNumber >> 18) & 0xFF))) * 8; climate_play_thunder(0, _thunderSoundId, _thunderVolume, -10000); @@ -383,7 +383,7 @@ static void climate_update_thunder() { if (_thunderStatus[0] == THUNDER_STATUS::NONE) { - _thunderSoundId = (randomNumber & 0x20000) ? SOUND_THUNDER_1 : SOUND_THUNDER_2; + _thunderSoundId = (randomNumber & 0x20000) ? SoundId::Thunder1 : SoundId::Thunder2; int32_t pan = (((randomNumber >> 18) & 0xFF) - 128) * 16; climate_play_thunder(0, _thunderSoundId, 0, pan); } @@ -391,7 +391,7 @@ static void climate_update_thunder() } } -static void climate_play_thunder(int32_t instanceIndex, int32_t soundId, int32_t volume, int32_t pan) +static void climate_play_thunder(int32_t instanceIndex, SoundId soundId, int32_t volume, int32_t pan) { _thunderSoundChannels[instanceIndex] = Mixer_Play_Effect( soundId, MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(pan), 1, 0); diff --git a/src/openrct2/world/Climate.h b/src/openrct2/world/Climate.h index b53a80a1d7..f0e7b126ea 100644 --- a/src/openrct2/world/Climate.h +++ b/src/openrct2/world/Climate.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/world/Duck.cpp b/src/openrct2/world/Duck.cpp index 68cf9c4cdb..ca9478ab87 100644 --- a/src/openrct2/world/Duck.cpp +++ b/src/openrct2/world/Duck.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -91,11 +91,12 @@ rct_duck* rct_sprite::AsDuck() void rct_duck::Invalidate() { - invalidate_sprite_0((rct_sprite*)this); + invalidate_sprite_1((rct_sprite*)this); } void rct_duck::Remove() { + Invalidate(); sprite_remove((rct_sprite*)this); } @@ -122,8 +123,8 @@ void rct_duck::UpdateFlyToWater() int32_t newY = y + DuckMoveOffset[direction].y; int32_t manhattanDistanceN = abs(target_x - newX) + abs(target_y - newY); - TileElement* tileElement = map_get_surface_element_at({ target_x, target_y }); - int32_t waterHeight = tileElement->AsSurface()->GetWaterHeight(); + auto surfaceElement = map_get_surface_element_at({ target_x, target_y }); + int32_t waterHeight = surfaceElement != nullptr ? surfaceElement->GetWaterHeight() : 0; if (waterHeight == 0) { state = DUCK_STATE::FLY_AWAY; @@ -201,9 +202,8 @@ void rct_duck::UpdateSwim() else { Invalidate(); - int32_t landZ = tile_element_height(x, y); - int32_t waterZ = (landZ >> 16) & 0xFFFF; - landZ &= 0xFFFF; + int16_t landZ = tile_element_height({ x, y }); + int16_t waterZ = tile_element_water_height({ x, y }); if (z < landZ || waterZ == 0) { @@ -223,9 +223,8 @@ void rct_duck::UpdateSwim() int32_t direction = sprite_direction >> 3; int32_t newX = x + DuckMoveOffset[direction].x; int32_t newY = y + DuckMoveOffset[direction].y; - landZ = tile_element_height(newX, newY); - waterZ = (landZ >> 16) & 0xFFFF; - landZ &= 0xFFFF; + landZ = tile_element_height({ newX, newY }); + waterZ = tile_element_water_height({ newX, newY }); if (z >= landZ && z == waterZ) { @@ -307,42 +306,45 @@ uint32_t rct_duck::GetFrameImage(int32_t direction) const return imageId; } -void create_duck(int32_t targetX, int32_t targetY) +void create_duck(const CoordsXY& pos) { - rct_sprite* sprite = create_sprite(2); - if (sprite != nullptr) + rct_sprite* sprite = create_sprite(SPRITE_IDENTIFIER_MISC); + if (sprite == nullptr) + return; + + CoordsXY targetPos = pos; + + int32_t offsetXY = scenario_rand() & 0x1E; + targetPos.x += offsetXY; + targetPos.y += offsetXY; + + sprite->duck.sprite_identifier = SPRITE_IDENTIFIER_MISC; + sprite->duck.type = SPRITE_MISC_DUCK; + sprite->duck.sprite_width = 9; + sprite->duck.sprite_height_negative = 12; + sprite->duck.sprite_height_positive = 9; + sprite->duck.target_x = targetPos.x; + sprite->duck.target_y = targetPos.y; + uint8_t direction = scenario_rand() & 3; + switch (direction) { - sprite->duck.sprite_identifier = SPRITE_IDENTIFIER_MISC; - sprite->duck.type = SPRITE_MISC_DUCK; - sprite->duck.sprite_width = 9; - sprite->duck.sprite_height_negative = 12; - sprite->duck.sprite_height_positive = 9; - int32_t offsetXY = scenario_rand() & 0x1E; - targetX += offsetXY; - targetY += offsetXY; - sprite->duck.target_x = targetX; - sprite->duck.target_y = targetY; - uint8_t direction = scenario_rand() & 3; - switch (direction) - { - case 0: - targetX = 8191 - (scenario_rand() & 0x3F); - break; - case 1: - targetY = scenario_rand() & 0x3F; - break; - case 2: - targetX = scenario_rand() & 0x3F; - break; - case 3: - targetY = 8191 - (scenario_rand() & 0x3F); - break; - } - sprite->duck.sprite_direction = direction << 3; - sprite_move(targetX, targetY, 496, sprite); - sprite->duck.state = DUCK_STATE::FLY_TO_WATER; - sprite->duck.frame = 0; + case 0: + targetPos.x = 8191 - (scenario_rand() & 0x3F); + break; + case 1: + targetPos.y = scenario_rand() & 0x3F; + break; + case 2: + targetPos.x = scenario_rand() & 0x3F; + break; + case 3: + targetPos.y = 8191 - (scenario_rand() & 0x3F); + break; } + sprite->duck.sprite_direction = direction << 3; + sprite_move(targetPos.x, targetPos.y, 496, sprite); + sprite->duck.state = DUCK_STATE::FLY_TO_WATER; + sprite->duck.frame = 0; } void duck_update(rct_duck* duck) @@ -369,7 +371,7 @@ void duck_update(rct_duck* duck) void duck_press(rct_duck* duck) { - audio_play_sound_at_location(SOUND_QUACK, duck->x, duck->y, duck->z); + audio_play_sound_at_location(SoundId::Quack, { duck->x, duck->y, duck->z }); } void duck_remove_all() @@ -382,6 +384,7 @@ void duck_remove_all() nextSpriteIndex = sprite->next; if (sprite->type == SPRITE_MISC_DUCK) { + invalidate_sprite_1((rct_sprite*)sprite); sprite_remove((rct_sprite*)sprite); } } diff --git a/src/openrct2/world/Entrance.cpp b/src/openrct2/world/Entrance.cpp index 489d68ee04..a25d06f0b3 100644 --- a/src/openrct2/world/Entrance.cpp +++ b/src/openrct2/world/Entrance.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,6 +12,7 @@ #include "../Cheats.h" #include "../Game.h" #include "../OpenRCT2.h" +#include "../actions/ParkEntranceRemoveAction.hpp" #include "../actions/RideEntranceExitPlaceAction.hpp" #include "../actions/RideEntranceExitRemoveAction.hpp" #include "../localisation/StringIds.h" @@ -35,59 +36,6 @@ std::vector gParkEntrances; CoordsXYZD gRideEntranceExitGhostPosition; uint8_t gRideEntranceExitGhostStationIndex; -static void ParkEntranceRemoveSegment(int32_t x, int32_t y, int32_t z) -{ - EntranceElement* tileElement; - - tileElement = map_get_park_entrance_element_at(x, y, z, true); - if (tileElement == nullptr) - { - return; - } - - map_invalidate_tile(x, y, tileElement->base_height * 8, tileElement->clearance_height * 8); - tileElement->Remove(); - update_park_fences({ x, y }); -} - -static money32 ParkEntranceRemove(int16_t x, int16_t y, uint8_t z, uint8_t flags) -{ - if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode) - { - return MONEY32_UNDEFINED; - } - - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; - gCommandPosition.x = x; - gCommandPosition.y = y; - gCommandPosition.z = z * 16; - - if (!(flags & GAME_COMMAND_FLAG_APPLY)) - { - return 0; - } - - auto entranceIndex = park_entrance_get_index(x, y, z * 16); - if (entranceIndex == -1) - { - return 0; - } - - auto direction = (gParkEntrances[entranceIndex].direction - 1) & 3; - - // Centre (sign) - ParkEntranceRemoveSegment(x, y, z * 2); - - // Left post - ParkEntranceRemoveSegment(x + CoordsDirectionDelta[direction].x, y + CoordsDirectionDelta[direction].y, z * 2); - - // Right post - ParkEntranceRemoveSegment(x - CoordsDirectionDelta[direction].x, y - CoordsDirectionDelta[direction].y, z * 2); - - gParkEntrances.erase(gParkEntrances.begin() + entranceIndex); - return 0; -} - static money32 RideEntranceExitPlaceGhost( ride_id_t rideIndex, int16_t x, int16_t y, uint8_t direction, uint8_t placeType, uint8_t stationNum) { @@ -99,17 +47,6 @@ static money32 RideEntranceExitPlaceGhost( return res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED; } -/** - * - * rct2: 0x00666A63 - */ -void game_command_remove_park_entrance( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, - [[maybe_unused]] int32_t* ebp) -{ - *ebx = ParkEntranceRemove(*eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, *ebx & 0xFF); -} - /** * * rct2: 0x00666F9E @@ -119,9 +56,10 @@ void park_entrance_remove_ghost() if (gParkEntranceGhostExists) { gParkEntranceGhostExists = false; - game_do_command( - gParkEntranceGhostPosition.x, GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_APPLY, - gParkEntranceGhostPosition.y, gParkEntranceGhostPosition.z, GAME_COMMAND_REMOVE_PARK_ENTRANCE, 0, 0); + auto parkEntranceRemoveAction = ParkEntranceRemoveAction( + { gParkEntranceGhostPosition.x, gParkEntranceGhostPosition.y, gParkEntranceGhostPosition.z * 16 }); + parkEntranceRemoveAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); + GameActions::Execute(&parkEntranceRemoveAction); } } @@ -201,6 +139,8 @@ void maze_entrance_hedge_replacement(int32_t x, int32_t y, TileElement* tileElem ride_id_t rideIndex = tileElement->AsEntrance()->GetRideIndex(); tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -237,6 +177,8 @@ void maze_entrance_hedge_removal(int32_t x, int32_t y, TileElement* tileElement) ride_id_t rideIndex = tileElement->AsEntrance()->GetRideIndex(); tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) diff --git a/src/openrct2/world/Entrance.h b/src/openrct2/world/Entrance.h index 740791b3f6..d1f797a795 100644 --- a/src/openrct2/world/Entrance.h +++ b/src/openrct2/world/Entrance.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -26,9 +26,6 @@ struct rct_entrance_type assert_struct_size(rct_entrance_type, 8); #pragma pack(pop) -void game_command_remove_park_entrance( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); - struct TileElement; extern bool gParkEntranceGhostExists; diff --git a/src/openrct2/world/Footpath.cpp b/src/openrct2/world/Footpath.cpp index 3795e43c5a..8b62b1b9ce 100644 --- a/src/openrct2/world/Footpath.cpp +++ b/src/openrct2/world/Footpath.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,6 +13,7 @@ #include "../OpenRCT2.h" #include "../actions/FootpathPlaceAction.hpp" #include "../actions/FootpathRemoveAction.hpp" +#include "../actions/LandSetRightsAction.hpp" #include "../core/Guard.hpp" #include "../localisation/Localisation.h" #include "../management/Finance.h" @@ -112,11 +113,11 @@ static bool entrance_has_direction(TileElement* tileElement, int32_t direction) TileElement* map_get_footpath_element(int32_t x, int32_t y, int32_t z) { - TileElement* tileElement; - - tileElement = map_get_first_element_at(x, y); + TileElement* tileElement = map_get_first_element_at(x, y); do { + if (tileElement == nullptr) + break; if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH && tileElement->base_height == z) return tileElement; } while (!(tileElement++)->IsLastForTile()); @@ -124,198 +125,9 @@ TileElement* map_get_footpath_element(int32_t x, int32_t y, int32_t z) return nullptr; } -/** rct2: 0x0098D7EC */ -static constexpr const QuarterTile SlopedFootpathQuarterTiles[] = { - { 0b1111, 0b1100 }, { 0b1111, 0b1001 }, { 0b1111, 0b0011 }, { 0b1111, 0b0110 } -}; - -/** - * - * rct2: 0x006BA23E - */ -void remove_banners_at_element(int32_t x, int32_t y, TileElement* tileElement) -{ - while (!(tileElement++)->IsLastForTile()) - { - if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH) - return; - else if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER) - continue; - - game_do_command( - x, 1, y, tileElement->base_height | tileElement->AsBanner()->GetPosition() << 8, GAME_COMMAND_REMOVE_BANNER, 0, 0); - tileElement--; - } -} - -static money32 footpath_place_from_track( - int32_t type, int32_t x, int32_t y, int32_t z, int32_t slope, int32_t edges, int32_t flags) -{ - TileElement* tileElement; - EntranceElement* entranceElement; - bool entrancePath = false, entranceIsSamePath = false; - - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = z * 8; - - if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) && game_is_paused() && !gCheatsBuildInPauseMode) - { - gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - return MONEY32_UNDEFINED; - } - - if ((flags & GAME_COMMAND_FLAG_APPLY) && !(flags & GAME_COMMAND_FLAG_GHOST)) - footpath_interrupt_peeps(x, y, z * 8); - - gFootpathPrice = 0; - gFootpathGroundFlags = 0; - - if (!map_is_location_owned(x, y, z * 8) && !gCheatsSandboxMode) - { - return MONEY32_UNDEFINED; - } - - if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(x, y, z * 8)) - return MONEY32_UNDEFINED; - - if (z < 2) - { - gGameCommandErrorText = STR_TOO_LOW; - return MONEY32_UNDEFINED; - } - - if (z > 248) - { - gGameCommandErrorText = STR_TOO_HIGH; - return MONEY32_UNDEFINED; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - if (!(flags & (GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST))) - { - footpath_remove_litter(x, y, z * 8); - } - } - - gFootpathPrice += 120; - QuarterTile quarterTile = { 0b1111, 0 }; - int32_t zHigh = z + 4; - if (slope & TILE_ELEMENT_SLOPE_S_CORNER_UP) - { - quarterTile = SlopedFootpathQuarterTiles[slope & TILE_ELEMENT_SLOPE_NE_SIDE_UP]; - zHigh += 2; - } - - entranceElement = map_get_park_entrance_element_at(x, y, z, false); - // Make sure the entrance part is the middle - if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0) - { - entrancePath = true; - // Make the price the same as replacing a path - if (entranceElement->GetPathType() == (type & 0xF)) - entranceIsSamePath = true; - else - gFootpathPrice -= MONEY(6, 00); - } - - // Do not attempt to build a crossing with a queue or a sloped. - uint8_t crossingMode = (type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (slope != TILE_ELEMENT_SLOPE_FLAT) - ? CREATE_CROSSING_MODE_NONE - : CREATE_CROSSING_MODE_PATH_OVER_TRACK; - if (!entrancePath - && !map_can_construct_with_clear_at( - x, y, z, zHigh, &map_place_non_scenery_clear_func, quarterTile, flags, &gFootpathPrice, crossingMode)) - return MONEY32_UNDEFINED; - - gFootpathGroundFlags = gMapGroundFlags; - if (!gCheatsDisableClearanceChecks && (gMapGroundFlags & ELEMENT_IS_UNDERWATER)) - { - gGameCommandErrorText = STR_CANT_BUILD_THIS_UNDERWATER; - return MONEY32_UNDEFINED; - } - - tileElement = map_get_surface_element_at({ x, y }); - - int32_t supportHeight = z - tileElement->base_height; - gFootpathPrice += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / 2) * MONEY(5, 00); - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST)) - { - LocationXYZ16 coord; - coord.x = x + 16; - coord.y = y + 16; - coord.z = tile_element_height(coord.x, coord.y); - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - - if (entrancePath) - { - if (!(flags & GAME_COMMAND_FLAG_GHOST) && !entranceIsSamePath) - { - // Set the path type but make sure it's not a queue as that will not show up - entranceElement->SetPathType(type & 0x7F); - map_invalidate_tile_full(x, y); - } - } - else - { - tileElement = tile_element_insert(x / 32, y / 32, z, 0x0F); - assert(tileElement != nullptr); - tileElement->SetType(TILE_ELEMENT_TYPE_PATH); - PathElement* pathElement = tileElement->AsPath(); - // This can NEVER happen, but GCC does not want to believe that... - if (pathElement == nullptr) - { - assert(false); - return MONEY32_UNDEFINED; - } - pathElement->clearance_height = z + 4 + ((slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED) ? 2 : 0); - pathElement->SetPathEntryIndex(type & 0xF); - pathElement->SetSlopeDirection(slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK); - if (slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED) - pathElement->SetSloped(true); - if (type & (1 << 7)) - pathElement->SetIsQueue(true); - pathElement->SetAddition(0); - pathElement->SetRideIndex(RIDE_ID_NULL); - pathElement->SetAdditionStatus(255); - pathElement->SetEdges(edges); - pathElement->SetCorners(0); - pathElement->SetIsBroken(false); - if (flags & (1 << 6)) - pathElement->SetGhost(true); - - map_invalidate_tile_full(x, y); - } - } - - if (entranceIsSamePath) - gFootpathPrice = 0; - - return gParkFlags & PARK_FLAGS_NO_MONEY ? 0 : gFootpathPrice; -} - -/** - * - * rct2: 0x006A68AE - */ -void game_command_place_footpath_from_track( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, - [[maybe_unused]] int32_t* ebp) -{ - *ebx = footpath_place_from_track( - (*edx >> 8) & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, ((*ebx >> 13) & 0x3) | ((*ebx >> 10) & 0x4), - (*ebx >> 8) & 0xF, *ebx & 0xFF); -} - money32 footpath_remove(int32_t x, int32_t y, int32_t z, int32_t flags) { - auto action = FootpathRemoveAction(x, y, z); + auto action = FootpathRemoveAction({ x, y, z * 8 }); action.SetFlags(flags); if (flags & GAME_COMMAND_FLAG_APPLY) @@ -399,7 +211,8 @@ void footpath_provisional_remove() footpath_remove( gFootpathProvisionalPosition.x, gFootpathProvisionalPosition.y, gFootpathProvisionalPosition.z, - GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_GHOST); + GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND + | GAME_COMMAND_FLAG_GHOST); } } @@ -479,7 +292,7 @@ void footpath_get_coordinates_from_pos( { if (interactionType != VIEWPORT_INTERACTION_ITEM_FOOTPATH) { - z = tile_element_height(position.x, position.y); + z = tile_element_height({ position.x, position.y }); } position = viewport_coord_to_map_coord(start_vp_pos.x, start_vp_pos.y, z); position.x = std::clamp(position.x, minPosition.x, maxPosition.x); @@ -594,7 +407,7 @@ void footpath_remove_litter(int32_t x, int32_t y, int32_t z) { rct_litter* sprite = &get_sprite(spriteIndex)->litter; uint16_t nextSpriteIndex = sprite->next_in_quadrant; - if (sprite->linked_list_type_offset == SPRITE_LIST_LITTER * 2) + if (sprite->linked_list_index == SPRITE_LIST_LITTER) { int32_t distanceZ = abs(sprite->z - z); if (distanceZ <= 32) @@ -618,7 +431,7 @@ void footpath_interrupt_peeps(int32_t x, int32_t y, int32_t z) { Peep* peep = &get_sprite(spriteIndex)->peep; uint16_t nextSpriteIndex = peep->next_in_quadrant; - if (peep->linked_list_type_offset == SPRITE_LIST_PEEP * 2) + if (peep->linked_list_index == SPRITE_LIST_PEEP) { if (peep->state == PEEP_STATE_SITTING || peep->state == PEEP_STATE_WATCHING) { @@ -637,6 +450,10 @@ void footpath_interrupt_peeps(int32_t x, int32_t y, int32_t z) } /** + * Returns true if the edge of tile x, y specified by direction is occupied by a fence + * between heights z0 and z1. + * + * Note that there may still be a fence on the opposing tile. * * rct2: 0x006E59DC */ @@ -673,6 +490,8 @@ static TileElement* footpath_connect_corners_get_neighbour(int32_t x, int32_t y, } TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -845,6 +664,8 @@ static TileElement* footpath_get_element(int32_t x, int32_t y, int32_t z0, int32 int32_t slope; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -875,16 +696,26 @@ static TileElement* footpath_get_element(int32_t x, int32_t y, int32_t z0, int32 return nullptr; } -static bool sub_footpath_disconnect_queue_from_path( - int32_t x, int32_t y, TileElement* tileElement, int32_t action, int32_t direction) +/** + * Attempt to connect a newly disconnected queue tile to the specified path tile + */ +static bool footpath_reconnect_queue_to_path(int32_t x, int32_t y, TileElement* tileElement, int32_t action, int32_t direction) { if (((tileElement->AsPath()->GetEdges() & (1 << direction)) == 0) ^ (action < 0)) return false; - if ((action < 0) && fence_in_the_way(x, y, tileElement->base_height, tileElement->clearance_height, direction)) - return false; int32_t x1 = x + CoordsDirectionDelta[direction].x; int32_t y1 = y + CoordsDirectionDelta[direction].y; + + if (action < 0) + { + if (fence_in_the_way(x, y, tileElement->base_height, tileElement->clearance_height, direction)) + return false; + + if (fence_in_the_way(x1, y1, tileElement->base_height, tileElement->clearance_height, direction_reverse(direction))) + return false; + } + int32_t z = tileElement->base_height; TileElement* otherTileElement = footpath_get_element(x1, y1, z - 2, z, direction); if (otherTileElement != nullptr && !otherTileElement->AsPath()->IsQueue()) @@ -924,15 +755,15 @@ static bool footpath_disconnect_queue_from_path(int32_t x, int32_t y, TileElemen if (action < 0) { uint8_t direction = tileElement->AsPath()->GetSlopeDirection(); - if (sub_footpath_disconnect_queue_from_path(x, y, tileElement, action, direction)) + if (footpath_reconnect_queue_to_path(x, y, tileElement, action, direction)) return true; } - for (int32_t direction = 0; direction < 4; direction++) + for (Direction direction : ALL_DIRECTIONS) { if ((action < 0) && (direction == tileElement->AsPath()->GetSlopeDirection())) continue; - if (sub_footpath_disconnect_queue_from_path(x, y, tileElement, action, direction)) + if (footpath_reconnect_queue_to_path(x, y, tileElement, action, direction)) return true; } @@ -959,6 +790,8 @@ static void loc_6A6D7E( else { TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { switch (tileElement->GetType()) @@ -988,8 +821,8 @@ static void loc_6A6D7E( case TILE_ELEMENT_TYPE_TRACK: if (z == tileElement->base_height) { - Ride* ride = get_ride(tileElement->AsTrack()->GetRideIndex()); - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) + auto ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + if (ride == nullptr || !ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) { continue; } @@ -1116,8 +949,8 @@ static void loc_6A6C85( if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK) { - Ride* ride = get_ride(tileElement->AsTrack()->GetRideIndex()); - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) + auto ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + if (ride == nullptr || !ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) { return; } @@ -1167,7 +1000,7 @@ void footpath_connect_edges(int32_t x, int32_t y, TileElement* tileElement, int3 neighbour_list_init(&neighbourList); footpath_update_queue_entrance_banner(x, y, tileElement); - for (int32_t direction = 0; direction < 4; direction++) + for (Direction direction : ALL_DIRECTIONS) { loc_6A6C85(x, y, direction, tileElement, flags, true, &neighbourList); } @@ -1363,8 +1196,8 @@ void footpath_update_queue_chains() for (uint8_t* queueChainPtr = _footpathQueueChain; queueChainPtr < _footpathQueueChainNext; queueChainPtr++) { ride_id_t rideIndex = *queueChainPtr; - Ride* ride = get_ride(rideIndex); - if (ride->type == RIDE_TYPE_NULL) + auto ride = get_ride(rideIndex); + if (ride == nullptr) continue; for (int32_t i = 0; i < MAX_STATIONS; i++) @@ -1385,7 +1218,7 @@ void footpath_update_queue_chains() if (tileElement->AsEntrance()->GetRideIndex() != rideIndex) continue; - uint8_t direction = tileElement->GetDirectionWithOffset(2); + Direction direction = direction_reverse(tileElement->GetDirection()); footpath_chain_ride_queue(rideIndex, i, location.x << 5, location.y << 5, tileElement, direction); } while (!(tileElement++)->IsLastForTile()); } @@ -1399,7 +1232,7 @@ void footpath_update_queue_chains() */ static void footpath_fix_ownership(int32_t x, int32_t y) { - const TileElement* surfaceElement = map_get_surface_element_at({ x, y }); + const auto* surfaceElement = map_get_surface_element_at({ x, y }); uint16_t ownership; // Unlikely to be NULL unless deliberate. @@ -1413,7 +1246,7 @@ static void footpath_fix_ownership(int32_t x, int32_t y) // If the tile is safe to own construction rights of, do not erase contruction rights. else { - ownership = surfaceElement->AsSurface()->GetOwnership(); + ownership = surfaceElement->GetOwnership(); // You can't own the entrance path. if (ownership == OWNERSHIP_OWNED || ownership == OWNERSHIP_AVAILABLE) { @@ -1426,7 +1259,9 @@ static void footpath_fix_ownership(int32_t x, int32_t y) ownership = OWNERSHIP_UNOWNED; } - map_buy_land_rights(x, y, x, y, BUY_LAND_RIGHTS_FLAG_SET_OWNERSHIP_WITH_CHECKS, (ownership << 4) | GAME_COMMAND_FLAG_APPLY); + auto landSetRightsAction = LandSetRightsAction({ x, y }, LandSetRightSetting::SetOwnershipWithChecks, ownership); + landSetRightsAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND); + GameActions::Execute(&landSetRightsAction); } static bool get_next_direction(int32_t edges, int32_t* direction) @@ -1465,6 +1300,8 @@ static int32_t footpath_is_connected_to_map_edge_recurse( return FOOTPATH_SEARCH_SUCCESS; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return level == 1 ? FOOTPATH_SEARCH_NOT_FOUND : FOOTPATH_SEARCH_INCOMPLETE; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -1596,15 +1433,15 @@ void PathElement::SetSloped(bool isSloped) entryIndex |= FOOTPATH_PROPERTIES_FLAG_IS_SLOPED; } -uint8_t PathElement::GetSlopeDirection() const +Direction PathElement::GetSlopeDirection() const { - return entryIndex & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK; + return static_cast(entryIndex & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK); } -void PathElement::SetSlopeDirection(uint8_t newSlope) +void PathElement::SetSlopeDirection(Direction newSlope) { entryIndex &= ~FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK; - entryIndex |= newSlope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK; + entryIndex |= static_cast(newSlope) & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK; } bool PathElement::IsQueue() const @@ -1638,7 +1475,7 @@ bool PathElement::IsBroken() const void PathElement::SetIsBroken(bool isBroken) { - if (isBroken == true) + if (isBroken) { flags |= TILE_ELEMENT_FLAG_BROKEN; } @@ -1655,7 +1492,7 @@ bool PathElement::IsBlockedByVehicle() const void PathElement::SetIsBlockedByVehicle(bool isBlocked) { - if (isBlocked == true) + if (isBlocked) { flags |= TILE_ELEMENT_FLAG_BLOCKED_BY_VEHICLE; } @@ -1738,7 +1575,10 @@ uint8_t PathElement::GetRailingEntryIndex() const PathSurfaceEntry* PathElement::GetPathEntry() const { - return get_path_surface_entry(GetPathEntryIndex()); + if (!IsQueue()) + return get_path_surface_entry(GetPathEntryIndex()); + else + return get_path_surface_entry(GetPathEntryIndex() + MAX_PATH_OBJECTS); } PathRailingsEntry* PathElement::GetRailingEntry() const @@ -1787,6 +1627,8 @@ void PathElement::SetShouldDrawPathOverSupports(bool on) static void footpath_clear_wide(int32_t x, int32_t y) { TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); + if (tileElement == nullptr) + return; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -1804,6 +1646,8 @@ static void footpath_clear_wide(int32_t x, int32_t y) static TileElement* footpath_can_be_wide(int32_t x, int32_t y, uint8_t height) { TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -1855,6 +1699,8 @@ void footpath_update_path_wide_flags(int32_t x, int32_t y) // y -= 0x20; TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); + if (tileElement == nullptr) + return; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -2028,8 +1874,8 @@ void footpath_update_path_wide_flags(int32_t x, int32_t y) bool footpath_is_blocked_by_vehicle(const TileCoordsXYZ& position) { - auto pathElement = map_get_path_element_at(position.x, position.y, position.z); - return pathElement != nullptr && pathElement->AsPath()->IsBlockedByVehicle(); + auto pathElement = map_get_path_element_at(position); + return pathElement != nullptr && pathElement->IsBlockedByVehicle(); } /** @@ -2059,7 +1905,7 @@ void footpath_update_queue_entrance_banner(int32_t x, int32_t y, TileElement* ti if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) { footpath_queue_chain_push(tileElement->AsEntrance()->GetRideIndex()); - footpath_chain_ride_queue(255, 0, x, y, tileElement, tileElement->GetDirectionWithOffset(2)); + footpath_chain_ride_queue(255, 0, x, y, tileElement, direction_reverse(tileElement->GetDirection())); } break; } @@ -2093,6 +1939,8 @@ static void footpath_remove_edges_towards_here( y += CoordsDirectionDelta[direction].y; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -2122,6 +1970,8 @@ static void footpath_remove_edges_towards(int32_t x, int32_t y, int32_t z0, int3 } TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -2159,6 +2009,8 @@ static void footpath_remove_edges_towards(int32_t x, int32_t y, int32_t z0, int3 bool tile_element_wants_path_connection_towards(TileCoordsXYZD coords, const TileElement* const elementToBeRemoved) { TileElement* tileElement = map_get_first_element_at(coords.x, coords.y); + if (tileElement == nullptr) + return false; do { // Don't check the element that gets removed @@ -2187,7 +2039,10 @@ bool tile_element_wants_path_connection_towards(TileCoordsXYZD coords, const Til case TILE_ELEMENT_TYPE_TRACK: if (tileElement->base_height == coords.z) { - Ride* ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + auto ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + if (ride == nullptr) + continue; + if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) break; @@ -2245,6 +2100,8 @@ static void footpath_fix_corners_around(int32_t x, int32_t y, TileElement* pathE continue; TileElement* tileElement = map_get_first_element_at(x + xOffset, y + yOffset); + if (tileElement == nullptr) + continue; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -2272,9 +2129,9 @@ void footpath_remove_edges_at(int32_t x, int32_t y, TileElement* tileElement) { if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK) { - ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) + auto rideIndex = tileElement->AsTrack()->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride == nullptr || !ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) return; } @@ -2329,10 +2186,14 @@ PathSurfaceEntry* get_path_surface_entry(int32_t entryIndex) { PathSurfaceEntry* result = nullptr; auto& objMgr = OpenRCT2::GetContext()->GetObjectManager(); - auto obj = objMgr.GetLoadedObject(OBJECT_TYPE_PATHS, entryIndex); + // TODO: Change when moving to the new save format. + auto obj = objMgr.GetLoadedObject(OBJECT_TYPE_PATHS, entryIndex % MAX_PATH_OBJECTS); if (obj != nullptr) { - result = ((FootpathObject*)obj)->GetPathSurfaceEntry(); + if (entryIndex < MAX_PATH_OBJECTS) + result = ((FootpathObject*)obj)->GetPathSurfaceEntry(); + else + result = ((FootpathObject*)obj)->GetQueueEntry(); } return result; } diff --git a/src/openrct2/world/Footpath.h b/src/openrct2/world/Footpath.h index 8364a44f72..0feee82a0b 100644 --- a/src/openrct2/world/Footpath.h +++ b/src/openrct2/world/Footpath.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -21,17 +21,27 @@ enum PROVISIONAL_PATH_FLAG_2 = (1 << 2), }; +constexpr auto FootpathMaxHeight = 248; +constexpr auto FootpathMinHeight = 2; + #define FOOTPATH_ELEMENT_INSERT_QUEUE 0x80 +enum class RailingEntrySupportType : uint8_t +{ + Box = 0, + Pole = 1, + Count +}; + #pragma pack(push, 1) struct rct_footpath_entry { - rct_string_id string_idx; // 0x00 - uint32_t image; // 0x02 - uint32_t bridge_image; // 0x06 - uint8_t support_type; // 0x0A - uint8_t flags; // 0x0B - uint8_t scrolling_mode; // 0x0C + rct_string_id string_idx; // 0x00 + uint32_t image; // 0x02 + uint32_t bridge_image; // 0x06 + RailingEntrySupportType support_type; // 0x0A + uint8_t flags; // 0x0B + uint8_t scrolling_mode; // 0x0C }; assert_struct_size(rct_footpath_entry, 13); #pragma pack(pop) @@ -40,7 +50,6 @@ struct PathSurfaceEntry { rct_string_id string_idx; uint32_t image; - uint32_t queue_image; uint32_t preview; uint8_t flags; }; @@ -51,7 +60,7 @@ struct PathRailingsEntry uint32_t preview; uint32_t bridge_image; uint32_t railings_image; - uint8_t support_type; + RailingEntrySupportType support_type; uint8_t flags; uint8_t scrolling_mode; }; @@ -89,16 +98,10 @@ enum FOOTPATH_PROPERTIES_ADDITIONS_FLAG_GHOST = (1 << 7), }; -enum -{ - FOOTPATH_ENTRY_SUPPORT_TYPE_BOX = 0, - FOOTPATH_ENTRY_SUPPORT_TYPE_POLE = 1, - FOOTPATH_ENTRY_SUPPORT_TYPE_COUNT -}; - enum { FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR = (1 << 2), + FOOTPATH_ENTRY_FLAG_IS_QUEUE = (1 << 3), }; enum @@ -173,15 +176,10 @@ TileElement* map_get_footpath_element(int32_t x, int32_t y, int32_t z); struct PathElement; PathElement* map_get_footpath_element_slope(int32_t x, int32_t y, int32_t z, int32_t slope); void footpath_interrupt_peeps(int32_t x, int32_t y, int32_t z); -void game_command_place_footpath_from_track( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_remove_footpath( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); money32 footpath_remove(int32_t x, int32_t y, int32_t z, int32_t flags); money32 footpath_provisional_set(int32_t type, int32_t x, int32_t y, int32_t z, int32_t slope); void footpath_provisional_remove(); void footpath_provisional_update(); -void remove_banners_at_element(int32_t x, int32_t y, TileElement* tileElement); void footpath_get_coordinates_from_pos( int32_t screenX, int32_t screenY, int32_t* x, int32_t* y, int32_t* direction, TileElement** tileElement); void footpath_bridge_get_info_from_pos( diff --git a/src/openrct2/world/Fountain.cpp b/src/openrct2/world/Fountain.cpp index d5dad509bd..e991f79fb3 100644 --- a/src/openrct2/world/Fountain.cpp +++ b/src/openrct2/world/Fountain.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -38,12 +38,14 @@ namespace FOUNTAIN_FLAG const uint32_t DIRECTION = 1 << 7; }; // namespace FOUNTAIN_FLAG -static constexpr const LocationXY16 _fountainDirectionsNegative[] = { +static constexpr const CoordsXY _fountainDirectionsNegative[] = { { -32, 0 }, { -32, -32 }, { 0, 0 }, { -32, 0 }, { 0, 0 }, { 0, -32 }, { 0, -32 }, { -32, -32 }, }; -static constexpr const LocationXY16 _fountainDirectionsPositive[] = { { 32, 0 }, { 0, 0 }, { 0, 32 }, { 32, 32 }, - { 32, 32 }, { 32, 0 }, { 0, 0 }, { 0, 32 } }; +static constexpr const CoordsXY _fountainDirectionsPositive[] = { { 32, 0 }, { 0, 0 }, { 0, 32 }, { 32, 32 }, + { 32, 32 }, { 32, 0 }, { 0, 0 }, { 0, 32 } }; +constexpr auto _FountainChanceOfStoppingEdgeMode = 0x3333; // 0.200 +constexpr auto _FountainChanceOfStoppingRandomMode = 0x2000; // 0.125 // rct2: 0x0097F040 const uint8_t _fountainDirections[] = { 0, 1, 2, 3, 0, 1, 2, 3 }; @@ -65,36 +67,21 @@ const uint8_t _fountainPatternFlags[] = { FOUNTAIN_FLAG::FAST // FAST_RANDOM_CHASERS }; -static int32_t jumping_fountain_get_type(const rct_jumping_fountain* jumpingFountain); -static void jumping_fountain_continue(rct_jumping_fountain* jumpingFountain); -static bool is_jumping_fountain(int32_t type, int32_t x, int32_t y, int32_t z); - -static void jumping_fountain_goto_edge( - const rct_jumping_fountain* jumpingFountain, int32_t x, int32_t y, int32_t z, int32_t availableDirections); -static void jumping_fountain_bounce( - rct_jumping_fountain* jumpingFountain, int32_t x, int32_t y, int32_t z, int32_t availableDirections); -static void jumping_fountain_split( - const rct_jumping_fountain* jumpingFountain, int32_t x, int32_t y, int32_t z, int32_t availableDirections); -static void jumping_fountain_random( - const rct_jumping_fountain* jumpingFountain, int32_t x, int32_t y, int32_t z, int32_t availableDirections); -static void jumping_fountain_create_next( - const rct_jumping_fountain* jumpingFountain, int32_t x, int32_t y, int32_t z, int32_t direction); - -void jumping_fountain_begin(int32_t type, int32_t x, int32_t y, const TileElement* tileElement) +void JumpingFountain::StartAnimation(const int32_t newType, const CoordsXY newLoc, const TileElement* tileElement) { int32_t randomIndex; - int32_t z = tileElement->base_height * 8; + auto newZ = tileElement->base_height * 8; // Change pattern approximately every 51 seconds uint32_t pattern = (gCurrentTicks >> 11) & 7; - switch ((PATTERN)pattern) + switch (static_cast(pattern)) { case PATTERN::CYCLIC_SQUARES: // 0, 1, 2, 3 for (int32_t i = 0; i < 4; i++) { - jumping_fountain_create( - type, x + _fountainDirectionsPositive[i].x, y + _fountainDirectionsPositive[i].y, z, _fountainDirections[i], + JumpingFountain::Create( + newType, { newLoc + _fountainDirectionsPositive[i], newZ }, _fountainDirections[i], _fountainDirectionFlags[i] | _fountainPatternFlags[pattern], 0); } break; @@ -103,112 +90,111 @@ void jumping_fountain_begin(int32_t type, int32_t x, int32_t y, const TileElemen randomIndex = scenario_rand() & 1; for (int32_t i = randomIndex; i < 4; i += 2) { - jumping_fountain_create( - type, x + _fountainDirectionsPositive[i].x, y + _fountainDirectionsPositive[i].y, z, _fountainDirections[i], + JumpingFountain::Create( + newType, { newLoc + _fountainDirectionsPositive[i], newZ }, _fountainDirections[i], _fountainDirectionFlags[i] | _fountainPatternFlags[pattern], 0); } break; case PATTERN::RACING_PAIRS: // random [0 - 3 and 4 - 7] randomIndex = scenario_rand() & 3; - jumping_fountain_create( - type, x + _fountainDirectionsPositive[randomIndex].x, y + _fountainDirectionsPositive[randomIndex].y, z, - _fountainDirections[randomIndex], _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0); + JumpingFountain::Create( + newType, { newLoc + _fountainDirectionsPositive[randomIndex], newZ }, _fountainDirections[randomIndex], + _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0); randomIndex += 4; - jumping_fountain_create( - type, x + _fountainDirectionsPositive[randomIndex].x, y + _fountainDirectionsPositive[randomIndex].y, z, - _fountainDirections[randomIndex], _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0); + JumpingFountain::Create( + newType, { newLoc + _fountainDirectionsPositive[randomIndex], newZ }, _fountainDirections[randomIndex], + _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0); break; default: // random [0 - 7] randomIndex = scenario_rand() & 7; - jumping_fountain_create( - type, x + _fountainDirectionsPositive[randomIndex].x, y + _fountainDirectionsPositive[randomIndex].y, z, - _fountainDirections[randomIndex], _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0); + JumpingFountain::Create( + newType, { newLoc + _fountainDirectionsPositive[randomIndex], newZ }, _fountainDirections[randomIndex], + _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0); break; } } -void jumping_fountain_create(int32_t type, int32_t x, int32_t y, int32_t z, int32_t direction, int32_t flags, int32_t iteration) +void JumpingFountain::Create( + const int32_t newType, const CoordsXYZ newLoc, const int32_t direction, const int32_t newFlags, const int32_t iteration) { - rct_jumping_fountain* jumpingFountain = (rct_jumping_fountain*)create_sprite(SPRITE_IDENTIFIER_MISC); + auto* jumpingFountain = reinterpret_cast(create_sprite(SPRITE_IDENTIFIER_MISC)); if (jumpingFountain != nullptr) { - jumpingFountain->iteration = iteration; - jumpingFountain->fountain_flags = flags; + jumpingFountain->Iteration = iteration; + jumpingFountain->FountainFlags = newFlags; jumpingFountain->sprite_direction = direction << 3; jumpingFountain->sprite_width = 33; jumpingFountain->sprite_height_negative = 36; jumpingFountain->sprite_height_positive = 12; jumpingFountain->sprite_identifier = SPRITE_IDENTIFIER_MISC; - sprite_move(x, y, z, (rct_sprite*)jumpingFountain); - jumpingFountain->type = type == JUMPING_FOUNTAIN_TYPE_SNOW ? SPRITE_MISC_JUMPING_FOUNTAIN_SNOW - : SPRITE_MISC_JUMPING_FOUNTAIN_WATER; - jumpingFountain->num_ticks_alive = 0; + sprite_move(newLoc.x, newLoc.y, newLoc.z, reinterpret_cast(jumpingFountain)); + jumpingFountain->type = newType == JUMPING_FOUNTAIN_TYPE_SNOW ? SPRITE_MISC_JUMPING_FOUNTAIN_SNOW + : SPRITE_MISC_JUMPING_FOUNTAIN_WATER; + jumpingFountain->NumTicksAlive = 0; jumpingFountain->frame = 0; } } -void jumping_fountain_update(rct_jumping_fountain* jumpingFountain) +void JumpingFountain::Update() { - jumpingFountain->num_ticks_alive++; - // Originaly this would not update the frame on the following + NumTicksAlive++; + // Originally this would not update the frame on the following // ticks: 1, 3, 6, 9, 11, 14, 17, 19, 22, 25 - // This change was to simplefy the code base. There is a small increase + // This change was to simplify the code base. There is a small increase // in speed of the fountain jump because of this change. - if ((jumpingFountain->num_ticks_alive % 3) == 0) + if (NumTicksAlive % 3 == 0) { return; } - invalidate_sprite_0((rct_sprite*)jumpingFountain); - jumpingFountain->frame++; + invalidate_sprite_0(reinterpret_cast(this)); + frame++; - switch (jumpingFountain->type) + switch (type) { case SPRITE_MISC_JUMPING_FOUNTAIN_WATER: - if (jumpingFountain->frame == 11 && (jumpingFountain->fountain_flags & FOUNTAIN_FLAG::FAST)) + if (frame == 11 && (FountainFlags & FOUNTAIN_FLAG::FAST)) { - jumping_fountain_continue(jumpingFountain); + AdvanceAnimation(); } - if (jumpingFountain->frame == 16 && !(jumpingFountain->fountain_flags & FOUNTAIN_FLAG::FAST)) + if (frame == 16 && !(FountainFlags & FOUNTAIN_FLAG::FAST)) { - jumping_fountain_continue(jumpingFountain); + AdvanceAnimation(); } break; case SPRITE_MISC_JUMPING_FOUNTAIN_SNOW: - if (jumpingFountain->frame == 16) + if (frame == 16) { - jumping_fountain_continue(jumpingFountain); + AdvanceAnimation(); } break; } - if (jumpingFountain->frame == 16) + if (frame == 16) { - sprite_remove((rct_sprite*)jumpingFountain); + sprite_remove(reinterpret_cast(this)); } } -static int32_t jumping_fountain_get_type(const rct_jumping_fountain* jumpingFountain) +int32_t JumpingFountain::GetType() const { - int32_t type = jumpingFountain->type == SPRITE_MISC_JUMPING_FOUNTAIN_SNOW ? JUMPING_FOUNTAIN_TYPE_SNOW - : JUMPING_FOUNTAIN_TYPE_WATER; - return type; + const int32_t fountainType = type == SPRITE_MISC_JUMPING_FOUNTAIN_SNOW ? JUMPING_FOUNTAIN_TYPE_SNOW + : JUMPING_FOUNTAIN_TYPE_WATER; + return fountainType; } -static void jumping_fountain_continue(rct_jumping_fountain* jumpingFountain) +void JumpingFountain::AdvanceAnimation() { - int32_t type = jumping_fountain_get_type(jumpingFountain); - int32_t direction = (jumpingFountain->sprite_direction >> 3) & 7; - int32_t x = jumpingFountain->x + CoordsDirectionDelta[direction].x; - int32_t y = jumpingFountain->y + CoordsDirectionDelta[direction].y; - int32_t z = jumpingFountain->z; + const int32_t newType = GetType(); + const int32_t direction = (sprite_direction >> 3) & 7; + const CoordsXY newLoc = CoordsXY{ x, y } + CoordsDirectionDelta[direction]; int32_t availableDirections = 0; for (int32_t i = 0; i < 8; i++) { - if (is_jumping_fountain(type, x + _fountainDirectionsNegative[i].x, y + _fountainDirectionsNegative[i].y, z)) + if (IsJumpingFountain(newType, { newLoc + _fountainDirectionsNegative[i], z })) { availableDirections |= 1 << i; } @@ -219,52 +205,52 @@ static void jumping_fountain_continue(rct_jumping_fountain* jumpingFountain) return; } - if (jumpingFountain->fountain_flags & FOUNTAIN_FLAG::TERMINATE) + if (FountainFlags & FOUNTAIN_FLAG::TERMINATE) { return; } - if (jumpingFountain->fountain_flags & FOUNTAIN_FLAG::GOTO_EDGE) + if (FountainFlags & FOUNTAIN_FLAG::GOTO_EDGE) { - jumping_fountain_goto_edge(jumpingFountain, x, y, z, availableDirections); + GoToEdge({ newLoc, z }, availableDirections); return; } - if (jumpingFountain->fountain_flags & FOUNTAIN_FLAG::BOUNCE) + if (FountainFlags & FOUNTAIN_FLAG::BOUNCE) { - jumping_fountain_bounce(jumpingFountain, x, y, z, availableDirections); + Bounce({ newLoc, z }, availableDirections); return; } - if (jumpingFountain->fountain_flags & FOUNTAIN_FLAG::SPLIT) + if (FountainFlags & FOUNTAIN_FLAG::SPLIT) { - jumping_fountain_split(jumpingFountain, x, y, z, availableDirections); + Split({ newLoc, z }, availableDirections); return; } - jumping_fountain_random(jumpingFountain, x, y, z, availableDirections); + Random({ newLoc, z }, availableDirections); } -static bool is_jumping_fountain(int32_t type, int32_t x, int32_t y, int32_t z) +bool JumpingFountain::IsJumpingFountain(const int32_t newType, const CoordsXYZ newLoc) { - z = z >> 3; + const int32_t pathBitFlagMask = newType == JUMPING_FOUNTAIN_TYPE_SNOW ? PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW + : PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER; - int32_t pathBitFlagMask = type == JUMPING_FOUNTAIN_TYPE_SNOW ? PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW - : PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER; - - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + TileElement* tileElement = map_get_first_element_at(newLoc.x / 32, newLoc.y / 32); + if (tileElement == nullptr) + return false; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) continue; - if (tileElement->base_height != z) + if (tileElement->base_height != newLoc.z / 8) continue; if (tileElement->AsPath()->AdditionIsGhost()) continue; if (!tileElement->AsPath()->HasAddition()) continue; - uint8_t additionIndex = tileElement->AsPath()->GetAdditionEntryIndex(); + const uint8_t additionIndex = tileElement->AsPath()->GetAdditionEntryIndex(); rct_scenery_entry* sceneryEntry = get_footpath_item_entry(additionIndex); if (sceneryEntry != nullptr && sceneryEntry->path_bit.flags & pathBitFlagMask) { @@ -275,32 +261,31 @@ static bool is_jumping_fountain(int32_t type, int32_t x, int32_t y, int32_t z) return false; } -static void jumping_fountain_goto_edge( - const rct_jumping_fountain* jumpingFountain, int32_t x, int32_t y, int32_t z, int32_t availableDirections) +void JumpingFountain::GoToEdge(const CoordsXYZ newLoc, const int32_t availableDirections) const { - int32_t direction = (jumpingFountain->sprite_direction >> 3) << 1; + int32_t direction = (sprite_direction >> 3) << 1; if (availableDirections & (1 << direction)) { - jumping_fountain_create_next(jumpingFountain, x, y, z, direction); + CreateNext(newLoc, direction); return; } direction++; if (availableDirections & (1 << direction)) { - jumping_fountain_create_next(jumpingFountain, x, y, z, direction); + CreateNext(newLoc, direction); return; } - uint32_t randomIndex = scenario_rand(); - if ((randomIndex & 0xFFFF) < 0x3333) + const uint32_t randomIndex = scenario_rand(); + if ((randomIndex & 0xFFFF) < _FountainChanceOfStoppingEdgeMode) { return; } - if (jumpingFountain->fountain_flags & FOUNTAIN_FLAG::SPLIT) + if (FountainFlags & FOUNTAIN_FLAG::SPLIT) { - jumping_fountain_split(jumpingFountain, x, y, z, availableDirections); + Split(newLoc, availableDirections); return; } @@ -310,38 +295,36 @@ static void jumping_fountain_goto_edge( direction = (direction + 1) & 7; } - jumping_fountain_create_next(jumpingFountain, x, y, z, direction); + CreateNext(newLoc, direction); } -static void jumping_fountain_bounce( - rct_jumping_fountain* jumpingFountain, int32_t x, int32_t y, int32_t z, int32_t availableDirections) +void JumpingFountain::Bounce(const CoordsXYZ newLoc, const int32_t availableDirections) { - jumpingFountain->iteration++; - if (jumpingFountain->iteration < 8) + Iteration++; + if (Iteration < 8) { - int32_t direction = ((jumpingFountain->sprite_direction >> 3) ^ 2) << 1; + int32_t direction = ((sprite_direction >> 3) ^ 2) << 1; if (availableDirections & (1 << direction)) { - jumping_fountain_create_next(jumpingFountain, x, y, z, direction); + CreateNext(newLoc, direction); } else { direction++; if (availableDirections & (1 << direction)) { - jumping_fountain_create_next(jumpingFountain, x, y, z, direction); + CreateNext(newLoc, direction); } } } } -static void jumping_fountain_split( - const rct_jumping_fountain* jumpingFountain, int32_t x, int32_t y, int32_t z, int32_t availableDirections) +void JumpingFountain::Split(const CoordsXYZ newLoc, int32_t availableDirections) const { - if (jumpingFountain->iteration < 3) + if (Iteration < 3) { - int32_t type = jumping_fountain_get_type(jumpingFountain); - int32_t direction = ((jumpingFountain->sprite_direction >> 3) ^ 2) << 1; + const int32_t newType = GetType(); + int32_t direction = ((sprite_direction >> 3) ^ 2) << 1; availableDirections &= ~(1 << direction); availableDirections &= ~(1 << (direction + 1)); @@ -349,44 +332,40 @@ static void jumping_fountain_split( { if (availableDirections & (1 << direction)) { - jumping_fountain_create( - type, x, y, z, direction >> 1, jumpingFountain->fountain_flags & ~FOUNTAIN_FLAG::DIRECTION, - jumpingFountain->iteration + 1); + JumpingFountain::Create( + newType, newLoc, direction >> 1, FountainFlags & ~FOUNTAIN_FLAG::DIRECTION, Iteration + 1); } direction++; if (availableDirections & (1 << direction)) { - jumping_fountain_create( - type, x, y, z, direction >> 1, jumpingFountain->fountain_flags | FOUNTAIN_FLAG::DIRECTION, - jumpingFountain->iteration + 1); + JumpingFountain::Create( + newType, newLoc, direction >> 1, FountainFlags | FOUNTAIN_FLAG::DIRECTION, Iteration + 1); } } } } -static void jumping_fountain_random( - const rct_jumping_fountain* jumpingFountain, int32_t x, int32_t y, int32_t z, int32_t availableDirections) +void JumpingFountain::Random(const CoordsXYZ newLoc, int32_t availableDirections) const { - uint32_t randomIndex = scenario_rand(); - if ((randomIndex & 0xFFFF) >= 0x2000) + const uint32_t randomIndex = scenario_rand(); + if ((randomIndex & 0xFFFF) >= _FountainChanceOfStoppingRandomMode) { int32_t direction = randomIndex & 7; while (!(availableDirections & (1 << direction))) { direction = (direction + 1) & 7; } - jumping_fountain_create_next(jumpingFountain, x, y, z, direction); + CreateNext(newLoc, direction); } } -static void jumping_fountain_create_next( - const rct_jumping_fountain* jumpingFountain, int32_t x, int32_t y, int32_t z, int32_t direction) +void JumpingFountain::CreateNext(const CoordsXYZ newLoc, int32_t direction) const { - int32_t type = jumping_fountain_get_type(jumpingFountain); - int32_t flags = jumpingFountain->fountain_flags & ~FOUNTAIN_FLAG::DIRECTION; + const int32_t newType = GetType(); + int32_t newFlags = FountainFlags & ~FOUNTAIN_FLAG::DIRECTION; if (direction & 1) { - flags |= FOUNTAIN_FLAG::DIRECTION; + newFlags |= FOUNTAIN_FLAG::DIRECTION; } - jumping_fountain_create(type, x, y, z, direction >> 1, flags, jumpingFountain->iteration); + JumpingFountain::Create(newType, newLoc, direction >> 1, newFlags, Iteration); } diff --git a/src/openrct2/world/Fountain.h b/src/openrct2/world/Fountain.h index c0d1a4abef..6b650ce034 100644 --- a/src/openrct2/world/Fountain.h +++ b/src/openrct2/world/Fountain.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,15 +11,33 @@ #include "../common.h" #include "Map.h" -#include "Sprite.h" +#include "SpriteBase.h" + +struct JumpingFountain : rct_sprite_generic +{ + uint8_t NumTicksAlive; + uint8_t FountainFlags; + int16_t TargetX; + int16_t TargetY; + uint16_t Iteration; + + void Update(); + static void StartAnimation(int32_t newType, const CoordsXY newLoc, const TileElement* tileElement); + +private: + int32_t GetType() const; + void AdvanceAnimation(); + void GoToEdge(CoordsXYZ newLoc, int32_t availableDirections) const; + void Bounce(CoordsXYZ newLoc, int32_t availableDirections); + void Split(CoordsXYZ newLoc, int32_t availableDirections) const; + void Random(CoordsXYZ newLoc, int32_t availableDirections) const; + void CreateNext(CoordsXYZ newLoc, int32_t direction) const; + static void Create(int32_t newType, CoordsXYZ newLoc, int32_t direction, int32_t newFlags, int32_t iteration); + static bool IsJumpingFountain(int32_t newType, CoordsXYZ newLoc); +}; enum { JUMPING_FOUNTAIN_TYPE_WATER, JUMPING_FOUNTAIN_TYPE_SNOW }; - -void jumping_fountain_begin(int32_t type, int32_t x, int32_t y, const TileElement* tileElement); -void jumping_fountain_create( - int32_t type, int32_t x, int32_t y, int32_t z, int32_t direction, int32_t flags, int32_t iteration); -void jumping_fountain_update(rct_jumping_fountain* jumpingFountain); diff --git a/src/openrct2/world/LargeScenery.cpp b/src/openrct2/world/LargeScenery.cpp index cc3114c5f2..ea7d23c33b 100644 --- a/src/openrct2/world/LargeScenery.cpp +++ b/src/openrct2/world/LargeScenery.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,42 +12,44 @@ #include "../Context.h" #include "../common.h" #include "../object/ObjectManager.h" +#include "../world/Banner.h" #include "TileElement.h" colour_t LargeSceneryElement::GetPrimaryColour() const { - return colour[0] & TILE_ELEMENT_COLOUR_MASK; + return Colour[0]; } colour_t LargeSceneryElement::GetSecondaryColour() const { - return colour[1] & TILE_ELEMENT_COLOUR_MASK; + return Colour[1]; } void LargeSceneryElement::SetPrimaryColour(colour_t newColour) { assert(newColour <= 31); - colour[0] &= ~TILE_ELEMENT_COLOUR_MASK; - colour[0] |= newColour; + Colour[0] = newColour; } void LargeSceneryElement::SetSecondaryColour(colour_t newColour) { assert(newColour <= 31); - colour[1] &= ~TILE_ELEMENT_COLOUR_MASK; - colour[1] |= newColour; + Colour[1] = newColour; +} + +Banner* LargeSceneryElement::GetBanner() const +{ + return ::GetBanner(GetBannerIndex()); } BannerIndex LargeSceneryElement::GetBannerIndex() const { - return (type & 0xC0) | (((colour[0]) & ~TILE_ELEMENT_COLOUR_MASK) >> 2) | (((colour[1]) & ~TILE_ELEMENT_COLOUR_MASK) >> 5); + return BannerIndex; } -void LargeSceneryElement::SetBannerIndex(BannerIndex newIndex) +void LargeSceneryElement::SetBannerIndex(::BannerIndex newIndex) { - type |= newIndex & 0xC0; - colour[0] |= (newIndex & 0x38) << 2; - colour[1] |= (newIndex & 7) << 5; + this->BannerIndex = newIndex; } bool LargeSceneryElement::IsAccounted() const @@ -69,7 +71,7 @@ void LargeSceneryElement::SetIsAccounted(bool isAccounted) uint32_t LargeSceneryElement::GetEntryIndex() const { - return entryIndex & TILE_ELEMENT_LARGE_TYPE_MASK; + return EntryIndex; } rct_scenery_entry* LargeSceneryElement::GetEntry() const @@ -77,21 +79,19 @@ rct_scenery_entry* LargeSceneryElement::GetEntry() const return get_large_scenery_entry(GetEntryIndex()); } -uint16_t LargeSceneryElement::GetSequenceIndex() const +uint8_t LargeSceneryElement::GetSequenceIndex() const { - return (entryIndex >> 10); + return SequenceIndex; } void LargeSceneryElement::SetEntryIndex(uint32_t newIndex) { - entryIndex &= ~TILE_ELEMENT_LARGE_TYPE_MASK; - entryIndex |= (newIndex & TILE_ELEMENT_LARGE_TYPE_MASK); + EntryIndex = newIndex; } -void LargeSceneryElement::SetSequenceIndex(uint16_t sequence) +void LargeSceneryElement::SetSequenceIndex(uint8_t sequence) { - entryIndex &= TILE_ELEMENT_LARGE_TYPE_MASK; - entryIndex |= (sequence << 10); + SequenceIndex = sequence; } rct_scenery_entry* get_large_scenery_entry(int32_t entryIndex) diff --git a/src/openrct2/world/LargeScenery.h b/src/openrct2/world/LargeScenery.h index df52e6df7a..070a68e968 100644 --- a/src/openrct2/world/LargeScenery.h +++ b/src/openrct2/world/LargeScenery.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/world/Location.hpp b/src/openrct2/world/Location.hpp index ae9df02b6a..412d8e4bf4 100644 --- a/src/openrct2/world/Location.hpp +++ b/src/openrct2/world/Location.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -55,6 +55,24 @@ assert_struct_size(LocationXYZ16, 6); constexpr int32_t COORDS_NULL = -1; +struct ScreenCoordsXY +{ + int32_t x = 0; + int32_t y = 0; + + ScreenCoordsXY() = default; + constexpr ScreenCoordsXY(int32_t _x, int32_t _y) + : x(_x) + , y(_y) + { + } + + const ScreenCoordsXY operator-(const ScreenCoordsXY& rhs) const + { + return { x - rhs.x, y - rhs.y }; + } +}; + /** * Tile coordinates use 1 x/y increment per tile and 1 z increment per step. * Regular ('big', 'sprite') coordinates use 32 x/y increments per tile and 8 z increments per step. @@ -70,6 +88,57 @@ struct CoordsXY , y(_y) { } + + CoordsXY& operator+=(const CoordsXY rhs) + { + x += rhs.x; + y += rhs.y; + return *this; + } + + CoordsXY& operator-=(const CoordsXY rhs) + { + x -= rhs.x; + y -= rhs.y; + return *this; + } + + const CoordsXY operator+(const CoordsXY& rhs) const + { + return { x + rhs.x, y + rhs.y }; + } + + const CoordsXY operator-(const CoordsXY& rhs) const + { + return { x - rhs.x, y - rhs.y }; + } + + CoordsXY Rotate(int32_t direction) const + { + CoordsXY rotatedCoords; + switch (direction & 3) + { + default: + case 0: + rotatedCoords.x = x; + rotatedCoords.y = y; + break; + case 1: + rotatedCoords.x = y; + rotatedCoords.y = -x; + break; + case 2: + rotatedCoords.x = -x; + rotatedCoords.y = -y; + break; + case 3: + rotatedCoords.x = -y; + rotatedCoords.y = x; + break; + } + + return rotatedCoords; + } }; struct TileCoordsXY @@ -91,19 +160,56 @@ struct TileCoordsXY y += rhs.y; return *this; } + TileCoordsXY& operator-=(const TileCoordsXY rhs) + { + x -= rhs.x; + y -= rhs.y; + return *this; + } + + TileCoordsXY Rotate(int32_t direction) + { + TileCoordsXY rotatedCoords; + switch (direction & 3) + { + default: + case 0: + rotatedCoords.x = x; + rotatedCoords.y = y; + break; + case 1: + rotatedCoords.x = y; + rotatedCoords.y = -x; + break; + case 2: + rotatedCoords.x = -x; + rotatedCoords.y = -y; + break; + case 3: + rotatedCoords.x = -y; + rotatedCoords.y = x; + break; + } + + return rotatedCoords; + } + int32_t x = 0, y = 0; }; -struct CoordsXYZ +struct CoordsXYZ : public CoordsXY { - int32_t x = 0; - int32_t y = 0; int32_t z = 0; CoordsXYZ() = default; constexpr CoordsXYZ(int32_t _x, int32_t _y, int32_t _z) - : x(_x) - , y(_y) + : CoordsXY(_x, _y) + , z(_z) + { + } + + constexpr CoordsXYZ(CoordsXY c, int32_t _z) + : CoordsXY(c) , z(_z) { } @@ -137,6 +243,12 @@ struct TileCoordsXYZ return *this; } + TileCoordsXYZ& operator-=(const TileCoordsXY rhs) + { + x -= rhs.x; + y -= rhs.y; + return *this; + } bool operator==(const TileCoordsXYZ& other) const { return x == other.x && y == other.y && z == other.z; @@ -160,6 +272,14 @@ struct TileCoordsXYZ */ typedef uint8_t Direction; +const Direction INVALID_DIRECTION = 0xFF; + +/** + * Array of all valid cardinal directions, to make it easy to write range-based for loops like: + * for (Direction d : ALL_DIRECTIONS) + */ +constexpr Direction ALL_DIRECTIONS[] = { 0, 1, 2, 3 }; + /** * Given a direction, return the direction that points the other way, * on the same axis. @@ -174,10 +294,34 @@ typedef uint8_t Direction; return dir < 4; } -struct CoordsXYZD +/** + * Given a direction, return the next cardinal direction, wrapping around if necessary. + * (TODO: Figure out if this is CW or CCW) + */ +[[maybe_unused]] static constexpr Direction direction_next(Direction dir) { - int32_t x, y, z; - Direction direction; + return (dir + 1) & 0x03; +} + +/** + * Given a direction, return the previous cardinal direction, wrapping around if necessary. + * (TODO: Figure out if this is CW or CCW) + */ +[[maybe_unused]] static constexpr Direction direction_prev(Direction dir) +{ + return (dir - 1) & 0x03; +} + +struct CoordsXYZD : public CoordsXYZ +{ + Direction direction = 0; + + CoordsXYZD() = default; + constexpr CoordsXYZD(int32_t _x, int32_t _y, int32_t _z, Direction _d) + : CoordsXYZ(_x, _y, _z) + , direction(_d) + { + } bool isNull() const { diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index 2fd1c22770..a61d3ac784 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,9 +14,14 @@ #include "../Game.h" #include "../Input.h" #include "../OpenRCT2.h" +#include "../actions/BannerRemoveAction.hpp" #include "../actions/FootpathRemoveAction.hpp" +#include "../actions/LandLowerAction.hpp" +#include "../actions/LandRaiseAction.hpp" #include "../actions/LandSetHeightAction.hpp" +#include "../actions/LandSetRightsAction.hpp" #include "../actions/LargeSceneryRemoveAction.hpp" +#include "../actions/ParkEntranceRemoveAction.hpp" #include "../actions/SmallSceneryRemoveAction.hpp" #include "../actions/WallRemoveAction.hpp" #include "../actions/WaterSetHeightAction.hpp" @@ -85,7 +90,7 @@ int16_t gMapBaseZ; TileElement gTileElements[MAX_TILE_TILE_ELEMENT_POINTERS * 3]; TileElement* gTileElementTilePointers[MAX_TILE_TILE_ELEMENT_POINTERS]; -LocationXY16 gMapSelectionTiles[300]; +std::vector gMapSelectionTiles; std::vector gPeepSpawns; TileElement* gNextFreeTileElement; @@ -104,8 +109,8 @@ LocationXYZ16 gCommandPosition; bool gMapLandRightsUpdateSuccess; -static void clear_elements_at(int32_t x, int32_t y); -static void translate_3d_to_2d(int32_t rotation, int32_t* x, int32_t* y); +static void clear_elements_at(const CoordsXY& loc); +static ScreenCoordsXY translate_3d_to_2d(int32_t rotation, const CoordsXY& pos); void rotate_map_coordinates(int16_t* x, int16_t* y, int32_t rotation) { @@ -132,33 +137,6 @@ void rotate_map_coordinates(int16_t* x, int16_t* y, int32_t rotation) } } -LocationXY16 coordinate_3d_to_2d(const LocationXYZ16* coordinate_3d, int32_t rotation) -{ - LocationXY16 coordinate_2d; - - switch (rotation) - { - default: - case 0: - coordinate_2d.x = coordinate_3d->y - coordinate_3d->x; - coordinate_2d.y = ((coordinate_3d->y + coordinate_3d->x) >> 1) - coordinate_3d->z; - break; - case 1: - coordinate_2d.x = -coordinate_3d->y - coordinate_3d->x; - coordinate_2d.y = ((coordinate_3d->y - coordinate_3d->x) >> 1) - coordinate_3d->z; - break; - case 2: - coordinate_2d.x = -coordinate_3d->y + coordinate_3d->x; - coordinate_2d.y = ((-coordinate_3d->y - coordinate_3d->x) >> 1) - coordinate_3d->z; - break; - case 3: - coordinate_2d.x = coordinate_3d->y + coordinate_3d->x; - coordinate_2d.y = ((-coordinate_3d->y + coordinate_3d->x) >> 1) - coordinate_3d->z; - break; - } - return coordinate_2d; -} - void tile_element_iterator_begin(tile_element_iterator* it) { it->x = 0; @@ -250,7 +228,7 @@ void map_set_tile_elements(int32_t x, int32_t y, TileElement* elements) gTileElementTilePointers[x + y * MAXIMUM_MAP_SIZE_TECHNICAL] = elements; } -TileElement* map_get_surface_element_at(int32_t x, int32_t y) +SurfaceElement* map_get_surface_element_at(int32_t x, int32_t y) { TileElement* tileElement = map_get_first_element_at(x, y); @@ -266,17 +244,17 @@ TileElement* map_get_surface_element_at(int32_t x, int32_t y) tileElement++; } - return tileElement; + return tileElement->AsSurface(); } -TileElement* map_get_surface_element_at(const CoordsXY coords) +SurfaceElement* map_get_surface_element_at(const CoordsXY& coords) { return map_get_surface_element_at(coords.x / 32, coords.y / 32); } -TileElement* map_get_path_element_at(int32_t x, int32_t y, int32_t z) +PathElement* map_get_path_element_at(const TileCoordsXYZ& loc) { - TileElement* tileElement = map_get_first_element_at(x, y); + TileElement* tileElement = map_get_first_element_at(loc.x, loc.y); if (tileElement == nullptr) return nullptr; @@ -288,10 +266,10 @@ TileElement* map_get_path_element_at(int32_t x, int32_t y, int32_t z) continue; if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) continue; - if (tileElement->base_height != z) + if (tileElement->base_height != loc.z) continue; - return tileElement; + return tileElement->AsPath(); } while (!(tileElement++)->IsLastForTile()); return nullptr; @@ -326,14 +304,13 @@ BannerElement* map_get_banner_element_at(int32_t x, int32_t y, int32_t z, uint8_ */ void map_init(int32_t size) { - gNumMapAnimations = 0; gNextFreeTileElementPointerIndex = 0; for (int32_t i = 0; i < MAX_TILE_TILE_ELEMENT_POINTERS; i++) { TileElement* tile_element = &gTileElements[i]; tile_element->ClearAs(TILE_ELEMENT_TYPE_SURFACE); - tile_element->flags = TILE_ELEMENT_FLAG_LAST_TILE; + tile_element->SetLastForTile(true); tile_element->base_height = 14; tile_element->clearance_height = 14; tile_element->AsSurface()->SetWaterHeight(0); @@ -355,6 +332,7 @@ void map_init(int32_t size) gMapBaseZ = 7; map_update_tile_pointers(); map_remove_out_of_range_elements(); + AutoCreateMapAnimations(); auto intent = Intent(INTENT_ACTION_MAP); context_broadcast_intent(&intent); @@ -374,14 +352,14 @@ void map_count_remaining_land_rights() { for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) { - TileElement* element = map_get_surface_element_at(x, y); + auto* surfaceElement = map_get_surface_element_at(x, y); // Surface elements are sometimes hacked out to save some space for other map elements - if (element == nullptr) + if (surfaceElement == nullptr) { continue; } - uint8_t flags = element->AsSurface()->GetOwnership(); + uint8_t flags = surfaceElement->GetOwnership(); // Do not combine this condition with (flags & OWNERSHIP_AVAILABLE) // As some RCT1 parks have owned tiles with the 'construction rights available' flag also set @@ -454,29 +432,23 @@ void map_update_tile_pointers() * dx: return remember to & with 0xFFFF if you don't want water affecting results * rct2: 0x00662783 */ -int32_t tile_element_height(int32_t x, int32_t y) +int16_t tile_element_height(const CoordsXY& loc) { - TileElement* tileElement; - // Off the map - if ((unsigned)x >= 8192 || (unsigned)y >= 8192) + if ((unsigned)loc.x >= 8192 || (unsigned)loc.y >= 8192) return 16; - // Truncate subtile coordinates - int32_t x_tile = x & 0xFFFFFFE0; - int32_t y_tile = y & 0xFFFFFFE0; - // Get the surface element for the tile - tileElement = map_get_surface_element_at({ x_tile, y_tile }); + auto surfaceElement = map_get_surface_element_at(loc); - if (tileElement == nullptr) + if (surfaceElement == nullptr) { return 16; } - uint32_t height = (tileElement->AsSurface()->GetWaterHeight() << 20) | (tileElement->base_height << 3); + uint16_t height = (surfaceElement->base_height << 3); - uint32_t slope = tileElement->AsSurface()->GetSlope(); + uint32_t slope = surfaceElement->GetSlope(); uint8_t extra_height = (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) >> 4; // 0x10 is the 5th bit - sets slope to double height // Remove the extra height bit slope &= TILE_ELEMENT_SLOPE_ALL_CORNERS_UP; @@ -488,8 +460,8 @@ int32_t tile_element_height(int32_t x, int32_t y) uint8_t TILE_SIZE = 31; - xl = x & 0x1f; - yl = y & 0x1f; + xl = loc.x & 0x1f; + yl = loc.y & 0x1f; // Slope logic: // Each of the four bits in slope represents that corner being raised @@ -606,13 +578,35 @@ int32_t tile_element_height(int32_t x, int32_t y) return height; } +int16_t tile_element_water_height(const CoordsXY& loc) +{ + // Off the map + if ((unsigned)loc.x >= 8192 || (unsigned)loc.y >= 8192) + return 0; + + // Get the surface element for the tile + auto surfaceElement = map_get_surface_element_at(loc); + + if (surfaceElement == nullptr) + { + return 0; + } + + uint16_t height = (surfaceElement->GetWaterHeight() << 4); + + return height; +} + /** * Checks if the tile at coordinate at height counts as connected. * @return 1 if connected, 0 otherwise */ -bool map_coord_is_connected(int32_t x, int32_t y, int32_t z, uint8_t faceDirection) +bool map_coord_is_connected(const TileCoordsXYZ& loc, uint8_t faceDirection) { - TileElement* tileElement = map_get_first_element_at(x, y); + TileElement* tileElement = map_get_first_element_at(loc.x, loc.y); + + if (tileElement == nullptr) + return false; do { @@ -625,17 +619,17 @@ bool map_coord_is_connected(int32_t x, int32_t y, int32_t z, uint8_t faceDirecti { if (slopeDirection == faceDirection) { - if (z == tileElement->base_height + 2) + if (loc.z == tileElement->base_height + 2) return true; } - else if (direction_reverse(slopeDirection) == faceDirection && z == tileElement->base_height) + else if (direction_reverse(slopeDirection) == faceDirection && loc.z == tileElement->base_height) { return true; } } else { - if (z == tileElement->base_height) + if (loc.z == tileElement->base_height) return true; } } while (!(tileElement++)->IsLastForTile()); @@ -683,7 +677,7 @@ void map_update_path_wide_flags() * * rct2: 0x006A7B84 */ -int32_t map_height_from_slope(const CoordsXY coords, int32_t slope, bool isSloped) +int32_t map_height_from_slope(const CoordsXY& coords, int32_t slope, bool isSloped) { if (!isSloped) return 0; @@ -702,28 +696,25 @@ int32_t map_height_from_slope(const CoordsXY coords, int32_t slope, bool isSlope return 0; } -bool map_is_location_valid(const CoordsXY coords) +bool map_is_location_valid(const CoordsXY& coords) { - if (coords.x < (MAXIMUM_MAP_SIZE_TECHNICAL * 32) && coords.x >= 0 && coords.y < (MAXIMUM_MAP_SIZE_TECHNICAL * 32) - && coords.y >= 0) - { - return true; - } - return false; + const bool is_x_valid = coords.x < (MAXIMUM_MAP_SIZE_TECHNICAL * 32) && coords.x >= 0; + const bool is_y_valid = coords.y < (MAXIMUM_MAP_SIZE_TECHNICAL * 32) && coords.y >= 0; + return is_x_valid && is_y_valid; } -bool map_is_edge(const CoordsXY coords) +bool map_is_edge(const CoordsXY& coords) { return (coords.x < 32 || coords.y < 32 || coords.x >= gMapSizeUnits || coords.y >= gMapSizeUnits); } -bool map_can_build_at(int32_t x, int32_t y, int32_t z) +bool map_can_build_at(const CoordsXYZ& loc) { if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) return true; if (gCheatsSandboxMode) return true; - if (map_is_location_owned(x, y, z)) + if (map_is_location_owned(loc)) return true; return false; } @@ -732,21 +723,20 @@ bool map_can_build_at(int32_t x, int32_t y, int32_t z) * * rct2: 0x00664F72 */ -bool map_is_location_owned(int32_t x, int32_t y, int32_t z) +bool map_is_location_owned(const CoordsXYZ& loc) { // This check is to avoid throwing lots of messages in logs. - if (map_is_location_valid({ x, y })) + if (map_is_location_valid(loc)) { - TileElement* tileElement = map_get_surface_element_at({ x, y }); - if (tileElement != nullptr) + auto* surfaceElement = map_get_surface_element_at(loc); + if (surfaceElement != nullptr) { - if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED) + if (surfaceElement->GetOwnership() & OWNERSHIP_OWNED) return true; - if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED) + if (surfaceElement->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED) { - z /= 8; - if (z < tileElement->base_height || z - 2 > tileElement->base_height) + if (loc.z / 8 < surfaceElement->base_height || loc.z / 8 - 2 > surfaceElement->base_height) return true; } } @@ -760,14 +750,14 @@ bool map_is_location_owned(int32_t x, int32_t y, int32_t z) * * rct2: 0x00664F2C */ -bool map_is_location_in_park(const CoordsXY coords) +bool map_is_location_in_park(const CoordsXY& coords) { if (map_is_location_valid(coords)) { - TileElement* tileElement = map_get_surface_element_at(coords); - if (tileElement == nullptr) + auto surfaceElement = map_get_surface_element_at(coords); + if (surfaceElement == nullptr) return false; - if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED) + if (surfaceElement->GetOwnership() & OWNERSHIP_OWNED) return true; } @@ -775,251 +765,28 @@ bool map_is_location_in_park(const CoordsXY coords) return false; } -bool map_is_location_owned_or_has_rights(int32_t x, int32_t y) +bool map_is_location_owned_or_has_rights(const CoordsXY& loc) { - if (map_is_location_valid({ x, y })) + if (map_is_location_valid(loc)) { - TileElement* tileElement = map_get_surface_element_at({ x, y }); - if (tileElement == nullptr) + auto surfaceElement = map_get_surface_element_at(loc); + if (surfaceElement == nullptr) { return false; } - if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED) + if (surfaceElement->GetOwnership() & OWNERSHIP_OWNED) return true; - if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED) + if (surfaceElement->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED) return true; } return false; } -/** - * - * rct2: 0x006B909A - */ -void game_command_set_large_scenery_colour( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, - int32_t* ebp) -{ - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - int32_t x = *eax; - int32_t y = *ecx; - uint8_t tile_element_direction = *ebx >> 8; - uint8_t flags = *ebx & 0xFF; - uint8_t base_height = *edx; - uint8_t tileIndex = *edx >> 8; - uint8_t colour1 = *ebp; - uint8_t colour2 = *ebp >> 8; - int32_t z = tile_element_height(x, y); - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = z; - - TileElement* tile_element = map_get_large_scenery_segment(x, y, base_height, tile_element_direction, tileIndex); - - if (tile_element == nullptr) - { - *ebx = 0; - return; - } - - if ((flags & GAME_COMMAND_FLAG_GHOST) && !(tile_element->IsGhost())) - { - *ebx = 0; - return; - } - - rct_scenery_entry* scenery_entry = tile_element->AsLargeScenery()->GetEntry(); - - // Work out the base tile coordinates (Tile with index 0) - LocationXYZ16 baseTile = { - scenery_entry->large_scenery.tiles[tileIndex].x_offset, scenery_entry->large_scenery.tiles[tileIndex].y_offset, - static_cast((base_height * 8) - scenery_entry->large_scenery.tiles[tileIndex].z_offset) - }; - rotate_map_coordinates(&baseTile.x, &baseTile.y, tile_element_direction); - baseTile.x = x - baseTile.x; - baseTile.y = y - baseTile.y; - - for (int32_t i = 0; scenery_entry->large_scenery.tiles[i].x_offset != -1; ++i) - { - assert(i < MAXIMUM_MAP_SIZE_TECHNICAL); - - // Work out the current tile coordinates - LocationXYZ16 currentTile = { scenery_entry->large_scenery.tiles[i].x_offset, - scenery_entry->large_scenery.tiles[i].y_offset, - scenery_entry->large_scenery.tiles[i].z_offset }; - rotate_map_coordinates(¤tTile.x, ¤tTile.y, tile_element_direction); - currentTile.x += baseTile.x; - currentTile.y += baseTile.y; - currentTile.z += baseTile.z; - - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) - { - if (!map_is_location_owned(currentTile.x, currentTile.y, currentTile.z)) - { - *ebx = MONEY32_UNDEFINED; - return; - } - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - TileElement* tileElement = map_get_large_scenery_segment( - currentTile.x, currentTile.y, base_height, tile_element_direction, i); - - tileElement->AsLargeScenery()->SetPrimaryColour(colour1); - tileElement->AsLargeScenery()->SetSecondaryColour(colour2); - - map_invalidate_tile_full(currentTile.x, currentTile.y); - } - } - *ebx = 0; -} - -/** - * - * rct2: 0x00663CCD - */ -static money32 map_change_surface_style( - int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint8_t surfaceStyle, uint8_t edgeStyle, uint8_t flags) -{ - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - - x0 = std::max(x0, 32); - y0 = std::max(y0, 32); - x1 = std::min(x1, (int32_t)gMapSizeMaxXY); - y1 = std::min(y1, (int32_t)gMapSizeMaxXY); - - int32_t xMid, yMid; - - xMid = (x0 + x1) / 2 + 16; - yMid = (y0 + y1) / 2 + 16; - - int32_t heightMid = tile_element_height(xMid, yMid); - - gCommandPosition.x = xMid; - gCommandPosition.y = yMid; - gCommandPosition.z = heightMid; - - // Do nothing during pause - if (game_is_paused() && !gCheatsBuildInPauseMode) - { - return 0; - } - - // Do nothing if not in editor, sandbox mode or landscaping is forbidden - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode - && (gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES)) - { - return 0; - } - - money32 surfaceCost = 0; - money32 edgeCost = 0; - for (int32_t x = x0; x <= x1; x += 32) - { - for (int32_t y = y0; y <= y1; y += 32) - { - if (x > 0x1FFF) - continue; - if (y > 0x1FFF) - continue; - - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) - { - if (!map_is_location_in_park({ x, y })) - continue; - } - - auto surfaceElement = map_get_surface_element_at({ x, y })->AsSurface(); - if (surfaceElement == nullptr) - { - continue; - } - - if (surfaceStyle != 0xFF) - { - uint8_t cur_terrain = surfaceElement->GetSurfaceStyle(); - - if (surfaceStyle != cur_terrain) - { - // Prevent network-originated value of surfaceStyle from causing - // invalid access. - uint8_t style = surfaceStyle & 0x1F; - auto& objManager = GetContext()->GetObjectManager(); - const auto surfaceObj = static_cast( - objManager.GetLoadedObject(OBJECT_TYPE_TERRAIN_SURFACE, style)); - if (surfaceObj != nullptr) - { - surfaceCost += surfaceObj->Price; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - surfaceElement->SetSurfaceStyle(surfaceStyle); - - map_invalidate_tile_full(x, y); - footpath_remove_litter(x, y, tile_element_height(x, y)); - } - } - } - - if (edgeStyle != 0xFF) - { - uint8_t currentEdge = surfaceElement->GetEdgeStyle(); - - if (edgeStyle != currentEdge) - { - edgeCost += 100; - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - surfaceElement->SetEdgeStyle(edgeStyle); - map_invalidate_tile_full(x, y); - } - } - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - if (surfaceElement->CanGrassGrow() && (surfaceElement->GetGrassLength() & 7) != GRASS_LENGTH_CLEAR_0) - { - surfaceElement->SetGrassLength(GRASS_LENGTH_CLEAR_0); - map_invalidate_tile_full(x, y); - } - } - } - } - - if (flags & GAME_COMMAND_FLAG_APPLY && gGameCommandNestLevel == 1) - { - LocationXYZ16 coord; - coord.x = ((x0 + x1) / 2) + 16; - coord.y = ((y0 + y1) / 2) + 16; - coord.z = tile_element_height(coord.x, coord.y); - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - - return (gParkFlags & PARK_FLAGS_NO_MONEY) ? 0 : surfaceCost + edgeCost; -} - -/** - * - * rct2: 0x00663CCD - */ -void game_command_change_surface_style( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - *ebx = map_change_surface_style( - (int16_t)(*eax & 0xFFFF), (int16_t)(*ecx & 0xFFFF), (int16_t)(*edi & 0xFFFF), (int16_t)(*ebp & 0xFFFF), *edx & 0xFF, - (*edx & 0xFF00) >> 8, *ebx & 0xFF); -} - // 0x00981A1E -#define SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT 0x20 // Table of pre-calculated surface slopes (32) when raising the land tile for a given selection (5) // 0x1F = new slope // 0x20 = base height increases -static constexpr const uint8_t tile_element_raise_styles[9][32] = { +const uint8_t tile_element_raise_styles[9][32] = { { 0x01, 0x1B, 0x03, 0x1B, 0x05, 0x21, 0x07, 0x21, 0x09, 0x1B, 0x0B, 0x1B, 0x0D, 0x21, 0x20, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x23, 0x18, 0x19, 0x1A, 0x3B, 0x1C, 0x29, 0x24, 0x1F }, // MAP_SELECT_TYPE_CORNER_0 // (absolute rotation) @@ -1045,7 +812,7 @@ static constexpr const uint8_t tile_element_raise_styles[9][32] = { // Basically the inverse of the table above. // 0x1F = new slope // 0x20 = base height increases -static constexpr const uint8_t tile_element_lower_styles[9][32] = { +const uint8_t tile_element_lower_styles[9][32] = { { 0x2E, 0x00, 0x2E, 0x02, 0x3E, 0x04, 0x3E, 0x06, 0x2E, 0x08, 0x2E, 0x0A, 0x3E, 0x0C, 0x3E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x06, 0x18, 0x19, 0x1A, 0x0B, 0x1C, 0x0C, 0x3E, 0x1F }, // MAP_SELECT_TYPE_CORNER_0 { 0x2D, 0x2D, 0x00, 0x01, 0x2D, 0x2D, 0x04, 0x05, 0x3D, 0x3D, 0x08, 0x09, 0x3D, 0x3D, 0x0C, 0x0F, @@ -1114,100 +881,52 @@ int32_t map_get_corner_height(int32_t z, int32_t slope, int32_t direction) return z; } -int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t direction) +int32_t tile_element_get_corner_height(const SurfaceElement* surfaceElement, int32_t direction) { - int32_t z = tileElement->base_height; - int32_t slope = tileElement->AsSurface()->GetSlope(); + int32_t z = surfaceElement->base_height; + int32_t slope = surfaceElement->GetSlope(); return map_get_corner_height(z, slope, direction); } -static money32 map_set_land_ownership(uint8_t flags, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t newOwnership) +uint8_t map_get_lowest_land_height(const MapRange& range) { - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; - - if (!(flags & GAME_COMMAND_FLAG_APPLY)) - return 0; - - // Clamp to maximum addressable element to prevent long loop spamming the log - x1 = std::clamp(x1, 32, gMapSizeUnits - 32); - y1 = std::clamp(y1, 32, gMapSizeUnits - 32); - x2 = std::clamp(x2, 32, gMapSizeUnits - 32); - y2 = std::clamp(y2, 32, gMapSizeUnits - 32); - gMapLandRightsUpdateSuccess = false; - map_buy_land_rights(x1, y1, x2, y2, BUY_LAND_RIGHTS_FLAG_SET_OWNERSHIP_WITH_CHECKS, flags | (newOwnership << 8)); - - if (!gMapLandRightsUpdateSuccess) - return 0; - - int16_t x = std::clamp(x1, 32, gMapSizeUnits - 32); - int16_t y = std::clamp(y1, 32, gMapSizeUnits - 32); - - x += 16; - y += 16; - - int16_t z = tile_element_height(x, y) & 0xFFFF; - audio_play_sound_at_location(SOUND_PLACE_ITEM, x, y, z); - return 0; -} - -/** - * - * rct2: 0x006648E3 - */ -void game_command_set_land_ownership( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - int32_t flags = *ebx & 0xFF; - - *ebx = map_set_land_ownership(flags, *eax & 0xFFFF, *ecx & 0xFFFF, *edi & 0xFFFF, *ebp & 0xFFFF, *edx & 0xFF); - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - map_count_remaining_land_rights(); - } -} - -static uint8_t map_get_lowest_land_height(int32_t xMin, int32_t xMax, int32_t yMin, int32_t yMax) -{ - xMin = std::max(xMin, 32); - yMin = std::max(yMin, 32); - xMax = std::min(xMax, (int32_t)gMapSizeMaxXY); - yMax = std::min(yMax, (int32_t)gMapSizeMaxXY); + MapRange validRange = { std::max(range.GetLeft(), 32), std::max(range.GetTop(), 32), + std::min(range.GetRight(), (int32_t)gMapSizeMaxXY), + std::min(range.GetBottom(), (int32_t)gMapSizeMaxXY) }; uint8_t min_height = 0xFF; - for (int32_t yi = yMin; yi <= yMax; yi += 32) + for (int32_t yi = validRange.GetTop(); yi <= validRange.GetBottom(); yi += 32) { - for (int32_t xi = xMin; xi <= xMax; xi += 32) + for (int32_t xi = validRange.GetLeft(); xi <= validRange.GetRight(); xi += 32) { - TileElement* tile_element = map_get_surface_element_at({ xi, yi }); - if (tile_element != nullptr && min_height > tile_element->base_height) + auto* surfaceElement = map_get_surface_element_at({ xi, yi }); + if (surfaceElement != nullptr && min_height > surfaceElement->base_height) { - min_height = tile_element->base_height; + min_height = surfaceElement->base_height; } } } return min_height; } -static uint8_t map_get_highest_land_height(int32_t xMin, int32_t xMax, int32_t yMin, int32_t yMax) +uint8_t map_get_highest_land_height(const MapRange& range) { - xMin = std::max(xMin, 32); - yMin = std::max(yMin, 32); - xMax = std::min(xMax, (int32_t)gMapSizeMaxXY); - yMax = std::min(yMax, (int32_t)gMapSizeMaxXY); + MapRange validRange = { std::max(range.GetLeft(), 32), std::max(range.GetTop(), 32), + std::min(range.GetRight(), (int32_t)gMapSizeMaxXY), + std::min(range.GetBottom(), (int32_t)gMapSizeMaxXY) }; uint8_t max_height = 0; - for (int32_t yi = yMin; yi <= yMax; yi += 32) + for (int32_t yi = validRange.GetTop(); yi <= validRange.GetBottom(); yi += 32) { - for (int32_t xi = xMin; xi <= xMax; xi += 32) + for (int32_t xi = validRange.GetLeft(); xi <= validRange.GetRight(); xi += 32) { - TileElement* tile_element = map_get_surface_element_at({ xi, yi }); - if (tile_element != nullptr) + auto* surfaceElement = map_get_surface_element_at({ xi, yi }); + if (surfaceElement != nullptr) { - uint8_t base_height = tile_element->base_height; - if (tile_element->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) + uint8_t base_height = surfaceElement->base_height; + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) base_height += 2; - if (tile_element->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) base_height += 2; if (max_height < base_height) max_height = base_height; @@ -1217,1183 +936,10 @@ static uint8_t map_get_highest_land_height(int32_t xMin, int32_t xMax, int32_t y return max_height; } -static money32 raise_land( - int32_t flags, int32_t x, int32_t y, int32_t z, int32_t point_a_x, int32_t point_a_y, int32_t point_b_x, int32_t point_b_y, - int32_t selectionType) +bool map_is_location_at_edge(const CoordsXY& loc) { - money32 cost = 0; - size_t tableRow = selectionType; - - // The selections between MAP_SELECT_TYPE_FULL and MAP_SELECT_TYPE_EDGE_0 are not included in the tables - if (selectionType >= MAP_SELECT_TYPE_EDGE_0 && selectionType <= MAP_SELECT_TYPE_EDGE_3) - tableRow -= MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1; - - if ((flags & GAME_COMMAND_FLAG_APPLY) && gGameCommandNestLevel == 1) - { - audio_play_sound_at_location(SOUND_PLACE_ITEM, x, y, z); - } - - // Keep big coordinates within map boundaries - point_a_x = std::max(32, point_a_x); - point_b_x = std::min(gMapSizeMaxXY, point_b_x); - point_a_y = std::max(32, point_a_y); - point_b_y = std::min(gMapSizeMaxXY, point_b_y); - - uint8_t min_height = map_get_lowest_land_height(point_a_x, point_b_x, point_a_y, point_b_y); - - for (int32_t y_coord = point_a_y; y_coord <= point_b_y; y_coord += 32) - { - for (int32_t x_coord = point_a_x; x_coord <= point_b_x; x_coord += 32) - { - TileElement* tile_element = map_get_surface_element_at(x_coord / 32, y_coord / 32); - if (tile_element != nullptr) - { - uint8_t height = tile_element->base_height; - if (height <= min_height) - { - uint8_t raisedCorners = tile_element->AsSurface()->GetSlope(); - uint8_t slope = tile_element_raise_styles[tableRow][raisedCorners]; - - if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) - height += 2; - - slope &= TILE_ELEMENT_SURFACE_SLOPE_MASK; - auto landSetHeightAction = LandSetHeightAction({ x_coord, y_coord }, height, slope); - auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? landSetHeightAction.Execute() : landSetHeightAction.Query(); - - if (res->Error != GA_ERROR::OK) - { - return MONEY32_UNDEFINED; - } - cost += res->Cost; - } - } - } - } - - // Force ride construction to recheck area - _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; - - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - gCommandPosition.x = x; - gCommandPosition.y = y; - gCommandPosition.z = z; - return cost; -} - -static money32 lower_land( - int32_t flags, int32_t x, int32_t y, int32_t z, int32_t point_a_x, int32_t point_a_y, int32_t point_b_x, int32_t point_b_y, - int32_t selectionType) -{ - money32 cost = 0; - size_t tableRow = selectionType; - - // The selections between MAP_SELECT_TYPE_FULL and MAP_SELECT_TYPE_EDGE_0 are not included in the tables - if (selectionType >= MAP_SELECT_TYPE_EDGE_0 && selectionType <= MAP_SELECT_TYPE_EDGE_3) - tableRow -= MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1; - - // Keep big coordinates within map boundaries - point_a_x = std::max(32, point_a_x); - point_b_x = std::min(gMapSizeMaxXY, point_b_x); - point_a_y = std::max(32, point_a_y); - point_b_y = std::min(gMapSizeMaxXY, point_b_y); - - if ((flags & GAME_COMMAND_FLAG_APPLY) && gGameCommandNestLevel == 1) - { - audio_play_sound_at_location(SOUND_PLACE_ITEM, x, y, z); - } - - uint8_t max_height = map_get_highest_land_height(point_a_x, point_b_x, point_a_y, point_b_y); - - for (int32_t y_coord = point_a_y; y_coord <= point_b_y; y_coord += 32) - { - for (int32_t x_coord = point_a_x; x_coord <= point_b_x; x_coord += 32) - { - TileElement* tile_element = map_get_surface_element_at(x_coord / 32, y_coord / 32); - if (tile_element != nullptr) - { - uint8_t height = tile_element->base_height; - if (tile_element->AsSurface()->GetSlope() & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK) - height += 2; - if (tile_element->AsSurface()->GetSlope() & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG) - height += 2; - - if (height >= max_height) - { - height = tile_element->base_height; - uint8_t currentSlope = tile_element->AsSurface()->GetSlope(); - uint8_t newSlope = tile_element_lower_styles[tableRow][currentSlope]; - if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) - height -= 2; - - newSlope &= TILE_ELEMENT_SURFACE_SLOPE_MASK; - - auto landSetHeightAction = LandSetHeightAction({ x_coord, y_coord }, height, newSlope); - landSetHeightAction.SetFlags(flags); - - auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::ExecuteNested(&landSetHeightAction) - : GameActions::QueryNested(&landSetHeightAction); - if (res->Error != GA_ERROR::OK) - { - return MONEY32_UNDEFINED; - } - cost += res->Cost; - } - } - } - } - - // Force ride construction to recheck area - _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; - - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - gCommandPosition.x = x; - gCommandPosition.y = y; - gCommandPosition.z = z; - return cost; -} - -money32 raise_water(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t flags) -{ - money32 cost = 0; - bool waterHeightChanged = false; - - uint8_t max_height = 0xFF; - - x0 = std::max(x0, (int16_t)32); - y0 = std::max(y0, (int16_t)32); - x1 = std::min(x1, gMapSizeMaxXY); - y1 = std::min(y1, gMapSizeMaxXY); - - for (int32_t yi = y0; yi <= y1; yi += 32) - { - for (int32_t xi = x0; xi <= x1; xi += 32) - { - TileElement* tile_element = map_get_surface_element_at({ xi, yi }); - if (tile_element != nullptr) - { - uint8_t height = tile_element->base_height; - if (tile_element->AsSurface()->GetWaterHeight() > 0) - height = tile_element->AsSurface()->GetWaterHeight() * 2; - if (max_height > height) - max_height = height; - } - } - } - - for (int32_t yi = y0; yi <= y1; yi += 32) - { - for (int32_t xi = x0; xi <= x1; xi += 32) - { - TileElement* tile_element = map_get_surface_element_at({ xi, yi }); - if (tile_element != nullptr) - { - if (tile_element->base_height <= max_height) - { - uint8_t height = tile_element->AsSurface()->GetWaterHeight(); - if (height != 0) - { - height *= 2; - if (height > max_height) - continue; - height += 2; - } - else - { - height = tile_element->base_height + 2; - } - - auto waterSetHeightAction = WaterSetHeightAction({ xi, yi }, height); - waterSetHeightAction.SetFlags(flags); - auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&waterSetHeightAction) - : GameActions::QueryNested(&waterSetHeightAction); - if (res->Error != GA_ERROR::OK) - { - gGameCommandErrorText = res->ErrorMessage; - // set gCommonFormatArguments to res->ErrorArgs - return MONEY32_UNDEFINED; - } - - cost += res->Cost; - waterHeightChanged = true; - } - } - } - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - int32_t x = ((x0 + x1) / 2) + 16; - int32_t y = ((y0 + y1) / 2) + 16; - int32_t z = tile_element_height(x, y); - int16_t water_height_z = z >> 16; - int16_t base_height_z = z; - z = water_height_z; - if (z == 0) - z = base_height_z; - - LocationXYZ16 coord; - coord.x = x; - coord.y = y; - coord.z = z; - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - - gCommandPosition.x = x; - gCommandPosition.y = y; - gCommandPosition.z = z; - if (waterHeightChanged) - { - audio_play_sound_at_location(SOUND_LAYING_OUT_WATER, x, y, z); - } - } - - // Force ride construction to recheck area - _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; - - return cost; -} - -money32 lower_water(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t flags) -{ - money32 cost = 0; - bool waterHeightChanged = false; - - uint8_t min_height = 0; - - x0 = std::max(x0, (int16_t)32); - y0 = std::max(y0, (int16_t)32); - x1 = std::min(x1, gMapSizeMaxXY); - y1 = std::min(y1, gMapSizeMaxXY); - - for (int32_t yi = y0; yi <= y1; yi += 32) - { - for (int32_t xi = x0; xi <= x1; xi += 32) - { - TileElement* tile_element = map_get_surface_element_at({ xi, yi }); - if (tile_element != nullptr) - { - uint8_t height = tile_element->AsSurface()->GetWaterHeight(); - if (height != 0) - { - height *= 2; - if (height > min_height) - min_height = height; - } - } - } - } - - for (int32_t yi = y0; yi <= y1; yi += 32) - { - for (int32_t xi = x0; xi <= x1; xi += 32) - { - TileElement* tile_element = map_get_surface_element_at({ xi, yi }); - if (tile_element != nullptr) - { - uint8_t height = tile_element->AsSurface()->GetWaterHeight(); - if (height != 0) - { - height *= 2; - if (height < min_height) - continue; - height -= 2; - auto waterSetHeightAction = WaterSetHeightAction({ xi, yi }, height); - waterSetHeightAction.SetFlags(flags); - auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&waterSetHeightAction) - : GameActions::QueryNested(&waterSetHeightAction); - if (res->Error != GA_ERROR::OK) - { - gGameCommandErrorText = res->ErrorMessage; - // set gCommonFormatArguments to res->ErrorArgs - return MONEY32_UNDEFINED; - } - - cost += res->Cost; - waterHeightChanged = true; - } - } - } - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - int32_t x = ((x0 + x1) / 2) + 16; - int32_t y = ((y0 + y1) / 2) + 16; - int32_t z = tile_element_height(x, y); - int16_t water_height_z = z >> 16; - int16_t base_height_z = z; - z = water_height_z; - if (z == 0) - z = base_height_z; - - LocationXYZ16 coord; - coord.x = x; - coord.y = y; - coord.z = z; - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - - gCommandPosition.x = x; - gCommandPosition.y = y; - gCommandPosition.z = z; - if (waterHeightChanged) - { - audio_play_sound_at_location(SOUND_LAYING_OUT_WATER, x, y, z); - } - } - - // Force ride construction to recheck area - _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; - - return cost; -} - -/** - * - * rct2: 0x0068C542 - */ -void game_command_raise_land( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - *ebx = raise_land( - *ebx, *eax, *ecx, tile_element_height(*eax, *ecx), (int16_t)(*edx & 0xFFFF), (int16_t)(*ebp & 0xFFFF), *edx >> 16, - *ebp >> 16, *edi & 0xFFFF); -} - -/** - * - * rct2: 0x0068C6D1 - */ -void game_command_lower_land( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - *ebx = lower_land( - *ebx, *eax, *ecx, tile_element_height(*eax, *ecx), (int16_t)(*edx & 0xFFFF), (int16_t)(*ebp & 0xFFFF), *edx >> 16, - *ebp >> 16, *edi & 0xFFFF); -} - -static money32 smooth_land_tile( - int32_t direction, uint8_t flags, int32_t x, int32_t y, TileElement* tileElement, bool raiseLand) -{ - int32_t targetBaseZ = tileElement->base_height; - int32_t slope = tileElement->AsSurface()->GetSlope(); - if (raiseLand) - { - slope = tile_element_raise_styles[direction][slope]; - if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) - { - targetBaseZ += 2; - slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; - } - } - else - { - slope = tile_element_lower_styles[direction][slope]; - if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) - { - targetBaseZ -= 2; - slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; - } - } - - auto landSetHeightAction = LandSetHeightAction({ x, y }, targetBaseZ, slope); - landSetHeightAction.SetFlags(flags); - auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::ExecuteNested(&landSetHeightAction) - : GameActions::QueryNested(&landSetHeightAction); - - if (res->Error == GA_ERROR::OK) - { - return res->Cost; - } - else - { - return MONEY32_UNDEFINED; - } -} - -static money32 smooth_land_row_by_edge( - int32_t flags, int32_t x, int32_t y, int32_t expectedLandHeight1, int32_t expectedLandHeight2, int32_t stepX, int32_t stepY, - int32_t direction1, int32_t direction2, int32_t checkDirection1, int32_t checkDirection2, bool raiseLand) -{ - uint8_t shouldContinue = 0xF; - int32_t landChangePerTile = raiseLand ? -2 : 2; - TileElement *tileElement, *nextTileElement; - money32 totalCost = 0; - - // check if we need to start at all - if (!map_is_location_valid({ x, y }) || !map_is_location_valid({ x + stepX, y + stepY })) - { - return 0; - } - tileElement = map_get_surface_element_at({ x, y }); - nextTileElement = map_get_surface_element_at({ x + stepX, y + stepY }); - if (tileElement == nullptr || nextTileElement == nullptr) - { - return 0; - } - if (tile_element_get_corner_height(tileElement, checkDirection1) != expectedLandHeight1 + (raiseLand ? -2 : 2)) - { - shouldContinue &= ~0x1; - } - if (tile_element_get_corner_height(tileElement, checkDirection2) != expectedLandHeight2 + (raiseLand ? -2 : 2)) - { - shouldContinue &= ~0x2; - } - if (tile_element_get_corner_height(tileElement, checkDirection1) - != tile_element_get_corner_height(nextTileElement, direction1)) - { - shouldContinue &= ~0x1; - } - if (tile_element_get_corner_height(tileElement, checkDirection2) - != tile_element_get_corner_height(nextTileElement, direction2)) - { - shouldContinue &= ~0x2; - } - while ((shouldContinue & 0x3) != 0) - { - shouldContinue = ((shouldContinue << 2) | 0x3) & shouldContinue; - x += stepX; - y += stepY; - // check if we need to continue after raising the current tile - // this needs to be checked before the tile is changed - if (!map_is_location_valid({ x + stepX, y + stepY })) - { - shouldContinue &= ~0x3; - } - else - { - tileElement = nextTileElement; - nextTileElement = map_get_surface_element_at({ x + stepX, y + stepY }); - if (nextTileElement == nullptr) - { - shouldContinue &= ~0x3; - } - if (tile_element_get_corner_height(tileElement, direction1) + landChangePerTile - != tile_element_get_corner_height(tileElement, checkDirection1)) - { - shouldContinue &= ~0x1; - } - if (tile_element_get_corner_height(tileElement, direction2) + landChangePerTile - != tile_element_get_corner_height(tileElement, checkDirection2)) - { - shouldContinue &= ~0x2; - } - if ((shouldContinue & 0x1) - && tile_element_get_corner_height(tileElement, checkDirection1) - != tile_element_get_corner_height(nextTileElement, direction1)) - { - shouldContinue &= ~0x1; - } - if ((shouldContinue & 0x2) - && tile_element_get_corner_height(tileElement, checkDirection2) - != tile_element_get_corner_height(nextTileElement, direction2)) - { - shouldContinue &= ~0x2; - } - } - expectedLandHeight1 += landChangePerTile; - - // change land of current tile - int32_t targetBaseZ = tileElement->base_height; - int32_t slope = tileElement->AsSurface()->GetSlope(); - int32_t oldSlope = slope; - if (raiseLand) - { - if (shouldContinue & 0x4) - { - slope = tile_element_raise_styles[direction1][slope]; - if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) - { - targetBaseZ += 2; - slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; - } - } - if ((shouldContinue & 0x8) - && map_get_corner_height(tileElement->base_height, oldSlope, direction2) - == map_get_corner_height(targetBaseZ, slope, direction2)) - { - slope = tile_element_raise_styles[direction2][slope]; - if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) - { - targetBaseZ += 2; - slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; - } - } - } - else - { - if (shouldContinue & 0x4) - { - slope = tile_element_lower_styles[direction1][slope]; - if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) - { - targetBaseZ -= 2; - slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; - } - } - if ((shouldContinue & 0x8) - && map_get_corner_height(tileElement->base_height, oldSlope, direction2) - == map_get_corner_height(targetBaseZ, slope, direction2)) - { - slope = tile_element_lower_styles[direction2][slope]; - if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) - { - targetBaseZ -= 2; - slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; - } - } - } - auto landSetHeightAction = LandSetHeightAction({ x, y }, targetBaseZ, slope); - landSetHeightAction.SetFlags(flags); - auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::ExecuteNested(&landSetHeightAction) - : GameActions::QueryNested(&landSetHeightAction); - if (res->Error == GA_ERROR::OK) - { - totalCost += res->Cost; - } - } - return totalCost; -} - -static money32 smooth_land_row_by_corner( - int32_t flags, int32_t x, int32_t y, int32_t expectedLandHeight, int32_t stepX, int32_t stepY, int32_t direction, - int32_t checkDirection, bool raiseLand) -{ - bool shouldContinue = true; - TileElement *tileElement, *nextTileElement; - money32 totalCost = 0; - money32 result; - int32_t landChangePerTile; - if (stepX == 0 || stepY == 0) - { - landChangePerTile = raiseLand ? -2 : 2; - } - else - { - landChangePerTile = raiseLand ? -4 : 4; - } - - // check if we need to start at all - if (!map_is_location_valid({ x, y }) || !map_is_location_valid({ x + stepX, y + stepY })) - { - return 0; - } - tileElement = map_get_surface_element_at({ x, y }); - nextTileElement = map_get_surface_element_at((x + stepX) >> 5, (y + stepY) >> 5); - if (tileElement == nullptr || nextTileElement == nullptr) - { - return 0; - } - if (tile_element_get_corner_height(tileElement, checkDirection) != expectedLandHeight + (raiseLand ? -2 : 2)) - { - return 0; - } - if (tile_element_get_corner_height(tileElement, checkDirection) - != tile_element_get_corner_height(nextTileElement, direction)) - { - return 0; - } - while (shouldContinue) - { - x += stepX; - y += stepY; - // check if we need to continue after raising the current tile - // this needs to be checked before the tile is changed - if (!map_is_location_valid({ x + stepX, y + stepY })) - { - shouldContinue = false; - } - else - { - tileElement = nextTileElement; - nextTileElement = map_get_surface_element_at((x + stepX) >> 5, (y + stepY) >> 5); - if (nextTileElement == nullptr) - { - shouldContinue = false; - } - if (tile_element_get_corner_height(tileElement, direction) + landChangePerTile - != tile_element_get_corner_height(tileElement, checkDirection)) - { - shouldContinue = false; - } - if (shouldContinue - && tile_element_get_corner_height(tileElement, checkDirection) - != tile_element_get_corner_height(nextTileElement, direction)) - { - shouldContinue = false; - } - } - if (stepX * stepY != 0) - { - totalCost += smooth_land_row_by_corner( - flags, x, y, expectedLandHeight + (landChangePerTile / 2), 0, stepY, direction, checkDirection ^ 3, raiseLand); - totalCost += smooth_land_row_by_corner( - flags, x, y, expectedLandHeight + (landChangePerTile / 2), stepX, 0, direction, checkDirection ^ 1, raiseLand); - } - expectedLandHeight += landChangePerTile; - // change land of current tile - result = smooth_land_tile(direction, flags, x, y, tileElement, raiseLand); - if (result != MONEY32_UNDEFINED) - { - totalCost += result; - } - } - return totalCost; -} - -static money32 smooth_land( - int32_t flags, int32_t centreX, int32_t centreY, int32_t mapLeft, int32_t mapTop, int32_t mapRight, int32_t mapBottom, - int32_t command) -{ - // break up information in command - const bool raiseLand = command < 0x7FFF; - const int32_t selectionType = command & 0x7FFF; - const int32_t heightOffset = raiseLand ? 2 : -2; - - // Cap bounds to map - mapLeft = std::max(mapLeft, 32); - mapTop = std::max(mapTop, 32); - mapRight = std::clamp(mapRight, 0, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); - mapBottom = std::clamp(mapBottom, 0, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); - - // Play sound (only once) - int32_t centreZ = tile_element_height(centreX, centreY); - if ((flags & GAME_COMMAND_FLAG_APPLY) && gGameCommandNestLevel == 1) - { - audio_play_sound_at_location(SOUND_PLACE_ITEM, centreX, centreY, centreZ); - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - LocationXYZ16 coord; - coord.x = centreX + 16; - coord.y = centreY + 16; - coord.z = tile_element_height(coord.x, coord.y); - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - - // Do the smoothing - money32 totalCost = 0; - switch (selectionType) - { - case MAP_SELECT_TYPE_FULL: - { - uint8_t minHeight = heightOffset + map_get_lowest_land_height(mapLeft, mapRight, mapTop, mapBottom); - uint8_t maxHeight = heightOffset + map_get_highest_land_height(mapLeft, mapRight, mapTop, mapBottom); - - // Smooth the 4 corners - { // top-left - TileElement* tileElement = map_get_surface_element_at({ mapLeft, mapTop }); - int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 2), minHeight, maxHeight); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand); - } - { // bottom-left - TileElement* tileElement = map_get_surface_element_at(mapLeft >> 5, mapBottom >> 5); - int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 3), minHeight, maxHeight); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapBottom, z, -32, 32, 1, 3, raiseLand); - } - { // bottom-right - TileElement* tileElement = map_get_surface_element_at(mapRight >> 5, mapBottom >> 5); - int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 0), minHeight, maxHeight); - totalCost += smooth_land_row_by_corner(flags, mapRight, mapBottom, z, 32, 32, 2, 0, raiseLand); - } - { // top-right - TileElement* tileElement = map_get_surface_element_at(mapRight >> 5, mapTop >> 5); - int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 1), minHeight, maxHeight); - totalCost += smooth_land_row_by_corner(flags, mapRight, mapTop, z, 32, -32, 3, 1, raiseLand); - } - - // Smooth the edges - TileElement* tileElement = nullptr; - int32_t z1, z2; - for (int32_t y = mapTop; y <= mapBottom; y += 32) - { - tileElement = map_get_surface_element_at({ mapLeft, y }); - z1 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 3), minHeight, maxHeight); - z2 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 2), minHeight, maxHeight); - totalCost += smooth_land_row_by_edge(flags, mapLeft, y, z1, z2, -32, 0, 0, 1, 3, 2, raiseLand); - - tileElement = map_get_surface_element_at({ mapRight, y }); - z1 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 1), minHeight, maxHeight); - z2 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 0), minHeight, maxHeight); - totalCost += smooth_land_row_by_edge(flags, mapRight, y, z1, z2, 32, 0, 2, 3, 1, 0, raiseLand); - } - - for (int32_t x = mapLeft; x <= mapRight; x += 32) - { - tileElement = map_get_surface_element_at({ x, mapTop }); - z1 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 1), minHeight, maxHeight); - z2 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 2), minHeight, maxHeight); - totalCost += smooth_land_row_by_edge(flags, x, mapTop, z1, z2, 0, -32, 0, 3, 1, 2, raiseLand); - - tileElement = map_get_surface_element_at({ x, mapBottom }); - z1 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 0), minHeight, maxHeight); - z2 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 3), minHeight, maxHeight); - totalCost += smooth_land_row_by_edge(flags, x, mapBottom, z1, z2, 0, 32, 1, 2, 0, 3, raiseLand); - } - break; - } - case MAP_SELECT_TYPE_CORNER_0: - case MAP_SELECT_TYPE_CORNER_1: - case MAP_SELECT_TYPE_CORNER_2: - case MAP_SELECT_TYPE_CORNER_3: - { - TileElement* tileElement = map_get_surface_element_at({ mapLeft, mapTop }); - uint8_t newBaseZ = tileElement->base_height; - uint8_t newSlope = tileElement->AsSurface()->GetSlope(); - - if (raiseLand) - { - newSlope = tile_element_raise_styles[selectionType][newSlope]; - } - else - { - newSlope = tile_element_lower_styles[selectionType][newSlope]; - } - - if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) - { - newBaseZ += heightOffset; - newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; - } - - // Smooth the corners - int32_t z = map_get_corner_height(newBaseZ, newSlope, 2); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 0); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 32, 2, 0, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 3); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 32, 1, 3, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 1); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, -32, 3, 1, raiseLand); - - // Smooth the edges - switch (selectionType) - { - case MAP_SELECT_TYPE_CORNER_0: - z = map_get_corner_height(newBaseZ, newSlope, 0); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 3, 0, raiseLand); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 1, 0, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 3); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 0, 3, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 1); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 0, 1, raiseLand); - break; - case MAP_SELECT_TYPE_CORNER_1: - z = map_get_corner_height(newBaseZ, newSlope, 1); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 2, 1, raiseLand); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 0, 1, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 2); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 1, 2, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 0); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 1, 0, raiseLand); - break; - case MAP_SELECT_TYPE_CORNER_2: - z = map_get_corner_height(newBaseZ, newSlope, 2); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 1, 2, raiseLand); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 3, 2, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 1); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 2, 1, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 3); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 2, 3, raiseLand); - break; - case MAP_SELECT_TYPE_CORNER_3: - z = map_get_corner_height(newBaseZ, newSlope, 3); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 0, 3, raiseLand); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 2, 3, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 0); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 3, 0, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 2); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 3, 2, raiseLand); - break; - } - break; - } - case MAP_SELECT_TYPE_EDGE_0: - case MAP_SELECT_TYPE_EDGE_1: - case MAP_SELECT_TYPE_EDGE_2: - case MAP_SELECT_TYPE_EDGE_3: - { - // TODO: Handle smoothing by edge - // Get the two corners to raise - TileElement* surfaceElement = map_get_surface_element_at({ mapLeft, mapTop }); - uint8_t newBaseZ = surfaceElement->base_height; - uint8_t oldSlope = surfaceElement->AsSurface()->GetSlope(); - uint8_t newSlope = oldSlope; - int32_t rowIndex = selectionType - (MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1); - - if (raiseLand) - { - newSlope = tile_element_raise_styles[rowIndex][oldSlope]; - } - else - { - newSlope = tile_element_lower_styles[rowIndex][oldSlope]; - } - - const bool changeBaseHeight = newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; - if (changeBaseHeight) - { - newBaseZ += heightOffset; - newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; - } - - const uint8_t edge = selectionType - MAP_SELECT_TYPE_EDGE_0; - - // Table with corners for each edge selection. The first two are the selected corners, the latter two are the - // opposites - static constexpr uint8_t cornerIndices[][4] = { - { 2, 3, 1, 0 }, // MAP_SELECT_TYPE_EDGE_0 - { 3, 0, 2, 1 }, // MAP_SELECT_TYPE_EDGE_1 - { 0, 1, 3, 2 }, // MAP_SELECT_TYPE_EDGE_2 - { 1, 2, 0, 3 }, // MAP_SELECT_TYPE_EDGE_3 - }; - // Big coordinate offsets for the neigbouring tile for the given edge selection - static constexpr sLocationXY8 stepOffsets[] = { - { -32, 0 }, - { 0, 32 }, - { 32, 0 }, - { 0, -32 }, - }; - - // Smooth higher and lower edges - uint8_t c1 = cornerIndices[edge][0]; - uint8_t c2 = cornerIndices[edge][1]; - uint8_t c3 = cornerIndices[edge][2]; - uint8_t c4 = cornerIndices[edge][3]; - uint8_t z1 = map_get_corner_height(newBaseZ, newSlope, c1); - uint8_t z2 = map_get_corner_height(newBaseZ, newSlope, c2); - uint8_t z3 = map_get_corner_height(newBaseZ, newSlope, c3); - uint8_t z4 = map_get_corner_height(newBaseZ, newSlope, c4); - // Smooth the edge at the top of the new slope - totalCost += smooth_land_row_by_edge( - flags, mapLeft, mapTop, z1, z2, stepOffsets[edge].x, stepOffsets[edge].y, c3, c4, c1, c2, raiseLand); - // Smooth the edge at the bottom of the new slope - totalCost += smooth_land_row_by_edge( - flags, mapLeft, mapTop, z3, z4, -stepOffsets[edge].x, -stepOffsets[edge].y, c1, c2, c3, c4, raiseLand); - - // Smooth corners - totalCost += smooth_land_row_by_corner( - flags, mapLeft, mapTop, z1, -stepOffsets[edge].y, stepOffsets[edge].x, c2, c1, raiseLand); - totalCost += smooth_land_row_by_corner( - flags, mapLeft, mapTop, z2, stepOffsets[edge].y, -stepOffsets[edge].x, c1, c2, raiseLand); - int32_t z = map_get_corner_height(newBaseZ, newSlope, 2); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 0); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 32, 2, 0, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 3); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 32, 1, 3, raiseLand); - z = map_get_corner_height(newBaseZ, newSlope, 1); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, -32, 3, 1, raiseLand); - break; - } - } // switch selectionType - - // Raise / lower the land tool selection area - int32_t commandType = raiseLand ? GAME_COMMAND_RAISE_LAND : GAME_COMMAND_LOWER_LAND; - int32_t mapLeftRight = mapLeft | (mapRight << 16); - int32_t mapTopBottom = mapTop | (mapBottom << 16); - money32 cost = game_do_command(centreX, flags, centreY, mapLeftRight, commandType, command & 0x7FFF, mapTopBottom); - if (cost == MONEY32_UNDEFINED) - { - return MONEY32_UNDEFINED; - } - - totalCost += cost; - - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - gCommandPosition.x = centreX; - gCommandPosition.y = centreY; - gCommandPosition.z = centreZ; - return totalCost; -} - -/** - * - * rct2: 0x0068BC01 - */ -void game_command_smooth_land( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - int32_t flags = *ebx & 0xFF; - int32_t centreX = *eax & 0xFFFF; - int32_t centreY = *ecx & 0xFFFF; - int32_t mapLeft = (int16_t)(*edx & 0xFFFF); - int32_t mapTop = (int16_t)(*ebp & 0xFFFF); - int32_t mapRight = (int16_t)(*edx >> 16); - int32_t mapBottom = (int16_t)(*ebp >> 16); - int32_t command = *edi; - *ebx = smooth_land(flags, centreX, centreY, mapLeft, mapTop, mapRight, mapBottom, command); -} - -/** - * - * rct2: 0x006E66A0 - */ -void game_command_raise_water( - int32_t* eax, int32_t* ebx, int32_t* ecx, [[maybe_unused]] int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, - int32_t* ebp) -{ - *ebx = raise_water( - (int16_t)(*eax & 0xFFFF), (int16_t)(*ecx & 0xFFFF), (int16_t)(*edi & 0xFFFF), (int16_t)(*ebp & 0xFFFF), (uint8_t)*ebx); -} - -/** - * - * rct2: 0x006E6878 - */ -void game_command_lower_water( - int32_t* eax, int32_t* ebx, int32_t* ecx, [[maybe_unused]] int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, - int32_t* ebp) -{ - *ebx = lower_water( - (int16_t)(*eax & 0xFFFF), (int16_t)(*ecx & 0xFFFF), (int16_t)(*edi & 0xFFFF), (int16_t)(*ebp & 0xFFFF), (uint8_t)*ebx); -} - -bool map_is_location_at_edge(int32_t x, int32_t y) -{ - return x < 32 || y < 32 || x >= ((MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32) || y >= ((MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); -} - -/** - * - * rct2: 0x006B893C - */ -void game_command_place_large_scenery( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - int32_t x = (int16_t)*eax; - int32_t y = (int16_t)*ecx; - int32_t z = (int16_t)*ebp; - colour_t colour1 = *edx & TILE_ELEMENT_COLOUR_MASK; - colour_t colour2 = (*edx >> 8) & TILE_ELEMENT_COLOUR_MASK; - uint8_t flags = *ebx; - uint8_t rotation = *ebx >> 8; - uint8_t entry_index = *edi; - int32_t base_height = tile_element_height(x, y); - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = base_height; - gSceneryGroundFlags = 0; - BannerIndex banner_id = BANNER_INDEX_NULL; - money32 supportsCost = 0; - - if (game_is_paused() && !gCheatsBuildInPauseMode) - { - gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - *ebx = MONEY32_UNDEFINED; - return; - } - - if (entry_index >= 128) - { - log_warning("Invalid game command for scenery placement, entry_index = %u", entry_index); - *ebx = MONEY32_UNDEFINED; - return; - } - - rct_scenery_entry* scenery_entry = get_large_scenery_entry(entry_index); - if (scenery_entry == nullptr) - { - log_warning("Invalid game command for scenery placement, entry_index = %u", entry_index); - *ebx = MONEY32_UNDEFINED; - return; - } - - if (scenery_entry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE) - { - banner_id = create_new_banner(flags); - - if (banner_id == BANNER_INDEX_NULL) - { - *ebx = MONEY32_UNDEFINED; - return; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - rct_banner* banner = &gBanners[banner_id]; - banner->flags |= BANNER_FLAG_IS_LARGE_SCENERY; - banner->type = 0; - banner->x = x / 32; - banner->y = y / 32; - - ride_id_t rideIndex = banner_get_closest_ride_index(x, y, z); - if (rideIndex != RIDE_ID_NULL) - { - banner->ride_index = rideIndex; - banner->flags |= BANNER_FLAG_LINKED_TO_RIDE; - } - } - } - - uint32_t num_elements = 0; - int16_t maxHeight = -1; - for (rct_large_scenery_tile* tile = scenery_entry->large_scenery.tiles; tile->x_offset != -1; tile++) - { - num_elements++; - - LocationXY16 curTile = { tile->x_offset, tile->y_offset }; - - rotate_map_coordinates(&curTile.x, &curTile.y, rotation); - - curTile.x += x; - curTile.y += y; - - if (curTile.x >= 0x1FFF || curTile.y >= 0x1FFF || curTile.x < 0 || curTile.y < 0) - { - continue; - } - - TileElement* tile_element = map_get_surface_element_at({ curTile.x, curTile.y }); - if (tile_element != nullptr) - { - int32_t height = tile_element->base_height * 8; - int32_t slope = tile_element->AsSurface()->GetSlope(); - - if (slope & 0xF) - { - height += 16; - if (slope & 0x10) - { - height += 16; - } - } - - if (height > maxHeight) - { - maxHeight = height; - } - } - } - - if (z != 0) - { - maxHeight = z; - } - - if (!map_check_free_elements_and_reorganise(num_elements)) - { - *ebx = MONEY32_UNDEFINED; - return; - } - - gCommandPosition.z = maxHeight; - uint8_t tile_num = 0; - for (rct_large_scenery_tile* tile = scenery_entry->large_scenery.tiles; tile->x_offset != -1; tile++, tile_num++) - { - LocationXY16 curTile = { tile->x_offset, tile->y_offset }; - - rotate_map_coordinates(&curTile.x, &curTile.y, rotation); - - curTile.x += x; - curTile.y += y; - - int32_t zLow = (tile->z_offset + maxHeight) / 8; - int32_t zHigh = (tile->z_clearance / 8) + zLow; - - QuarterTile quarterTile = QuarterTile{ static_cast(tile->flags >> 12), 0 }.Rotate(rotation); - if (!map_can_construct_with_clear_at( - curTile.x, curTile.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, flags, &supportsCost, - CREATE_CROSSING_MODE_NONE)) - { - *ebx = MONEY32_UNDEFINED; - return; - } - - if ((gMapGroundFlags & ELEMENT_IS_UNDERWATER) || (gMapGroundFlags & ELEMENT_IS_UNDERGROUND)) - { - *ebx = MONEY32_UNDEFINED; - return; - } - - int32_t b = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND); - if (!gCheatsDisableClearanceChecks) - { - if (gSceneryGroundFlags && !(gSceneryGroundFlags & b)) - { - gGameCommandErrorText = STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND; - *ebx = MONEY32_UNDEFINED; - return; - } - } - gSceneryGroundFlags = b; - - if (curTile.x >= gMapSizeUnits || curTile.y >= gMapSizeUnits) - { - gGameCommandErrorText = STR_OFF_EDGE_OF_MAP; - *ebx = MONEY32_UNDEFINED; - return; - } - - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_owned(curTile.x, curTile.y, zLow * 8) - && !gCheatsSandboxMode) - { - *ebx = MONEY32_UNDEFINED; - return; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - if (!(flags & GAME_COMMAND_FLAG_GHOST)) - { - footpath_remove_litter(curTile.x, curTile.y, zLow * 8); - if (!gCheatsDisableClearanceChecks) - { - wall_remove_at(curTile.x, curTile.y, zLow * 8, zHigh * 8); - } - } - if (gGameCommandNestLevel == 1 && !(*ebx & GAME_COMMAND_FLAG_GHOST)) - { - LocationXYZ16 coord; - coord.x = x + 16; - coord.y = y + 16; - coord.z = tile_element_height(coord.x, coord.y); - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - - TileElement* new_tile_element = tile_element_insert( - curTile.x / 32, curTile.y / 32, zLow, quarterTile.GetBaseQuarterOccupied()); - assert(new_tile_element != nullptr); - map_animation_create(MAP_ANIMATION_TYPE_LARGE_SCENERY, curTile.x, curTile.y, zLow); - - new_tile_element->clearance_height = zHigh; - new_tile_element->SetType(TILE_ELEMENT_TYPE_LARGE_SCENERY); - new_tile_element->SetDirection(rotation); - - auto newSceneryElement = new_tile_element->AsLargeScenery(); - newSceneryElement->SetEntryIndex(entry_index); - newSceneryElement->SetSequenceIndex(tile_num); - - newSceneryElement->SetPrimaryColour(colour1); - newSceneryElement->SetSecondaryColour(colour2); - - if (banner_id != BANNER_INDEX_NULL) - { - newSceneryElement->SetBannerIndex(banner_id); - } - - if (flags & GAME_COMMAND_FLAG_GHOST) - { - new_tile_element->SetGhost(true); - } - - if (tile_num == 0) - { - gSceneryTileElement = new_tile_element; - } - map_invalidate_tile_full(curTile.x, curTile.y); - } - } - - // Force ride construction to recheck area - _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; - - *ebx = (scenery_entry->large_scenery.price * 10) + supportsCost; - if (gParkFlags & PARK_FLAGS_NO_MONEY) - { - *ebx = 0; - } + return loc.x < 32 || loc.y < 32 || loc.x >= ((MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32) + || loc.y >= ((MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); } /** @@ -2414,7 +960,7 @@ void tile_element_remove(TileElement* tileElement) } // Mark the latest element with the last element flag. - (tileElement - 1)->flags |= TILE_ELEMENT_FLAG_LAST_TILE; + (tileElement - 1)->SetLastForTile(true); tileElement->base_height = 0xFF; if ((tileElement + 1) == gNextFreeTileElement) @@ -2463,60 +1009,38 @@ void map_remove_all_rides() */ void map_invalidate_map_selection_tiles() { - LocationXY16* position; - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE_CONSTRUCT)) return; - for (position = gMapSelectionTiles; position->x != -1; position++) - map_invalidate_tile_full(position->x, position->y); + for (const auto& position : gMapSelectionTiles) + map_invalidate_tile_full(position.x, position.y); } -void map_get_bounding_box( - int32_t ax, int32_t ay, int32_t bx, int32_t by, int32_t* left, int32_t* top, int32_t* right, int32_t* bottom) +static void map_get_bounding_box(const MapRange& _range, int32_t* left, int32_t* top, int32_t* right, int32_t* bottom) { - int32_t x, y; - x = ax; - y = ay; uint32_t rotation = get_current_rotation(); - translate_3d_to_2d(rotation, &x, &y); - *left = x; - *right = x; - *top = y; - *bottom = y; - x = bx; - y = ay; - translate_3d_to_2d(rotation, &x, &y); - if (x < *left) - *left = x; - if (x > *right) - *right = x; - if (y > *bottom) - *bottom = y; - if (y < *top) - *top = y; - x = bx; - y = by; - translate_3d_to_2d(rotation, &x, &y); - if (x < *left) - *left = x; - if (x > *right) - *right = x; - if (y > *bottom) - *bottom = y; - if (y < *top) - *top = y; - x = ax; - y = by; - translate_3d_to_2d(rotation, &x, &y); - if (x < *left) - *left = x; - if (x > *right) - *right = x; - if (y > *bottom) - *bottom = y; - if (y < *top) - *top = y; + std::array corners{ CoordsXY{ _range.GetLeft(), _range.GetTop() }, + CoordsXY{ _range.GetRight(), _range.GetTop() }, + CoordsXY{ _range.GetRight(), _range.GetBottom() }, + CoordsXY{ _range.GetLeft(), _range.GetBottom() } }; + + *left = std::numeric_limits::max(); + *top = std::numeric_limits::max(); + *right = std::numeric_limits::min(); + *bottom = std::numeric_limits::min(); + + for (const auto& corner : corners) + { + auto screenCoord = translate_3d_to_2d(rotation, corner); + if (screenCoord.x < *left) + *left = screenCoord.x; + if (screenCoord.x > *right) + *right = screenCoord.x; + if (screenCoord.y > *bottom) + *bottom = screenCoord.y; + if (screenCoord.y < *top) + *top = screenCoord.y; + } } /** @@ -2534,7 +1058,7 @@ void map_invalidate_selection_rect() y0 = gMapSelectPositionA.y + 16; x1 = gMapSelectPositionB.x + 16; y1 = gMapSelectPositionB.y + 16; - map_get_bounding_box(x0, y0, x1, y1, &left, &top, &right, &bottom); + map_get_bounding_box({ x0, y0, x1, y1 }, &left, &top, &right, &bottom); left -= 32; right += 32; bottom += 32; @@ -2575,6 +1099,8 @@ void map_reorganise_elements() for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) { TileElement* startElement = map_get_first_element_at(x, y); + if (startElement == nullptr) + continue; TileElement* endElement = startElement; while (!(endElement++)->IsLastForTile()) ; @@ -2632,9 +1158,10 @@ bool map_check_free_elements_and_reorganise(int32_t numElements) * * rct2: 0x0068B1F6 */ -TileElement* tile_element_insert(int32_t x, int32_t y, int32_t z, int32_t flags) +TileElement* tile_element_insert(const TileCoordsXYZ& loc, int32_t occupiedQuadrants) { TileElement *originalTileElement, *newTileElement, *insertedElement; + bool isLastForTile = false; if (!map_check_free_elements_and_reorganise(1)) { @@ -2643,13 +1170,13 @@ TileElement* tile_element_insert(int32_t x, int32_t y, int32_t z, int32_t flags) } newTileElement = gNextFreeTileElement; - originalTileElement = gTileElementTilePointers[y * MAXIMUM_MAP_SIZE_TECHNICAL + x]; + originalTileElement = gTileElementTilePointers[loc.y * MAXIMUM_MAP_SIZE_TECHNICAL + loc.x]; // Set tile index pointer to point to new element block - gTileElementTilePointers[y * MAXIMUM_MAP_SIZE_TECHNICAL + x] = newTileElement; + gTileElementTilePointers[loc.y * MAXIMUM_MAP_SIZE_TECHNICAL + loc.x] = newTileElement; // Copy all elements that are below the insert height - while (z >= originalTileElement->base_height) + while (loc.z >= originalTileElement->base_height) { // Copy over map element *newTileElement = *originalTileElement; @@ -2657,11 +1184,11 @@ TileElement* tile_element_insert(int32_t x, int32_t y, int32_t z, int32_t flags) originalTileElement++; newTileElement++; - if ((newTileElement - 1)->flags & TILE_ELEMENT_FLAG_LAST_TILE) + if ((newTileElement - 1)->IsLastForTile()) { // No more elements above the insert element - (newTileElement - 1)->flags &= ~TILE_ELEMENT_FLAG_LAST_TILE; - flags |= TILE_ELEMENT_FLAG_LAST_TILE; + (newTileElement - 1)->SetLastForTile(false); + isLastForTile = true; break; } } @@ -2669,14 +1196,17 @@ TileElement* tile_element_insert(int32_t x, int32_t y, int32_t z, int32_t flags) // Insert new map element insertedElement = newTileElement; newTileElement->type = 0; - newTileElement->base_height = z; - newTileElement->flags = flags; - newTileElement->clearance_height = z; + newTileElement->base_height = loc.z; + newTileElement->flags = 0; + newTileElement->SetLastForTile(isLastForTile); + newTileElement->SetOccupiedQuadrants(occupiedQuadrants); + newTileElement->clearance_height = loc.z; std::memset(&newTileElement->pad_04, 0, sizeof(newTileElement->pad_04)); + std::memset(&newTileElement->pad_08, 0, sizeof(newTileElement->pad_08)); newTileElement++; // Insert rest of map elements above insert height - if (!(flags & TILE_ELEMENT_FLAG_LAST_TILE)) + if (!isLastForTile) { do { @@ -2713,9 +1243,11 @@ void map_obstruction_set_error_text(TileElement* tileElement) break; case TILE_ELEMENT_TYPE_TRACK: ride = get_ride(tileElement->AsTrack()->GetRideIndex()); - errorStringId = STR_X_IN_THE_WAY; - set_format_arg(0, rct_string_id, ride->name); - set_format_arg(2, uint32_t, ride->name_arguments); + if (ride != nullptr) + { + errorStringId = STR_X_IN_THE_WAY; + ride->FormatNameTo(gCommonFormatArgs); + } break; case TILE_ELEMENT_TYPE_SMALL_SCENERY: sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); @@ -2762,8 +1294,8 @@ void map_obstruction_set_error_text(TileElement* tileElement) * bl = bl */ bool map_can_construct_with_clear_at( - int32_t x, int32_t y, int32_t zLow, int32_t zHigh, CLEAR_FUNC clearFunc, QuarterTile bl, uint8_t flags, money32* price, - uint8_t crossingMode) + int32_t x, int32_t y, int32_t zLow, int32_t zHigh, CLEAR_FUNC clearFunc, QuarterTile quarterTile, uint8_t flags, + money32* price, uint8_t crossingMode) { int32_t al, ah, bh, cl, ch, water_height; al = ah = bh = cl = ch = water_height = 0; @@ -2783,13 +1315,15 @@ bool map_can_construct_with_clear_at( } TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); + if (tileElement == nullptr) + return false; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_SURFACE) { if (zLow < tileElement->clearance_height && zHigh > tileElement->base_height && !(tileElement->IsGhost())) { - if (tileElement->flags & (bl.GetBaseQuarterOccupied())) + if (tileElement->GetOccupiedQuadrants() & (quarterTile.GetBaseQuarterOccupied())) { goto loc_68BABC; } @@ -2826,7 +1360,7 @@ bool map_can_construct_with_clear_at( canBuildCrossing = true; } - if (bl.GetZQuarterOccupied() != 0b1111) + if (quarterTile.GetZQuarterOccupied() != 0b1111) { if (tileElement->base_height >= zHigh) { @@ -2867,8 +1401,8 @@ bool map_can_construct_with_clear_at( } bh = zLow + 4; { - auto baseQuarter = bl.GetBaseQuarterOccupied(); - auto zQuarter = bl.GetZQuarterOccupied(); + auto baseQuarter = quarterTile.GetBaseQuarterOccupied(); + auto zQuarter = quarterTile.GetZQuarterOccupied(); if ((!(baseQuarter & 0b0001) || ((zQuarter & 0b0001 || zLow >= al) && bh >= al)) && (!(baseQuarter & 0b0010) || ((zQuarter & 0b0010 || zLow >= ah) && bh >= ah)) && (!(baseQuarter & 0b0100) || ((zQuarter & 0b0100 || zLow >= cl) && bh >= cl)) @@ -2898,8 +1432,8 @@ bool map_can_construct_with_clear_at( crossingMode == 2 && canBuildCrossing && tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK && tileElement->base_height == zLow && tileElement->AsTrack()->GetTrackType() == TRACK_ELEM_FLAT) { - Ride* ride = get_ride(tileElement->AsTrack()->GetRideIndex()); - if (ride->type == RIDE_TYPE_MINIATURE_RAILWAY) + auto ride = get_ride(tileElement->AsTrack()->GetRideIndex()); + if (ride != nullptr && ride->type == RIDE_TYPE_MINIATURE_RAILWAY) { continue; } @@ -2965,10 +1499,10 @@ void map_update_tiles() interleaved_xy >>= 1; } - TileElement* tileElement = map_get_surface_element_at(x, y); - if (tileElement != nullptr) + auto* surfaceElement = map_get_surface_element_at(x, y); + if (surfaceElement != nullptr) { - tileElement->AsSurface()->UpdateGrassLength({ x * 32, y * 32 }); + surfaceElement->UpdateGrassLength({ x * 32, y * 32 }); scenery_update_tile(x * 32, y * 32); } @@ -3015,17 +1549,71 @@ void map_remove_out_of_range_elements() { int32_t mapMaxXY = gMapSizeMaxXY; + // Ensure that we can remove elements + // + // NOTE: This is only a workaround for non-networked games. + // Map resize has to become its own Game Action to properly solve this issue. + // + bool buildState = gCheatsBuildInPauseMode; + gCheatsBuildInPauseMode = true; + for (int32_t y = 0; y < (MAXIMUM_MAP_SIZE_TECHNICAL * 32); y += 32) { for (int32_t x = 0; x < (MAXIMUM_MAP_SIZE_TECHNICAL * 32); x += 32) { if (x == 0 || y == 0 || x >= mapMaxXY || y >= mapMaxXY) { - map_buy_land_rights(x, y, x, y, BUY_LAND_RIGHTS_FLAG_UNOWN_TILE, GAME_COMMAND_FLAG_APPLY); - clear_elements_at(x, y); + // Note this purposely does not use LandSetRightsAction as X Y coordinates are outside of normal range. + auto surfaceElement = map_get_surface_element_at({ x, y }); + if (surfaceElement != nullptr) + { + surfaceElement->SetOwnership(OWNERSHIP_UNOWNED); + update_park_fences_around_tile({ x, y }); + } + clear_elements_at({ x, y }); } } } + + // Reset cheat state + gCheatsBuildInPauseMode = buildState; +} + +static void map_extend_boundary_surface_extend_tile(const SurfaceElement& sourceTile, SurfaceElement& destTile) +{ + destTile.SetSurfaceStyle(sourceTile.GetSurfaceStyle()); + destTile.SetEdgeStyle(sourceTile.GetEdgeStyle()); + destTile.SetGrassLength(sourceTile.GetGrassLength()); + destTile.SetOwnership(OWNERSHIP_UNOWNED); + destTile.SetWaterHeight(sourceTile.GetWaterHeight()); + + auto z = sourceTile.base_height; + auto slope = sourceTile.GetSlope() & TILE_ELEMENT_SLOPE_NW_SIDE_UP; + if (slope == TILE_ELEMENT_SLOPE_NW_SIDE_UP) + { + z += 2; + slope = TILE_ELEMENT_SLOPE_FLAT; + if (sourceTile.GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + { + slope = TILE_ELEMENT_SLOPE_N_CORNER_UP; + if (sourceTile.GetSlope() & TILE_ELEMENT_SLOPE_S_CORNER_UP) + { + slope = TILE_ELEMENT_SLOPE_W_CORNER_UP; + if (sourceTile.GetSlope() & TILE_ELEMENT_SLOPE_E_CORNER_UP) + { + slope = TILE_ELEMENT_SLOPE_FLAT; + } + } + } + } + if (slope & TILE_ELEMENT_SLOPE_N_CORNER_UP) + slope |= TILE_ELEMENT_SLOPE_E_CORNER_UP; + if (slope & TILE_ELEMENT_SLOPE_W_CORNER_UP) + slope |= TILE_ELEMENT_SLOPE_S_CORNER_UP; + + destTile.SetSlope(slope); + destTile.base_height = z; + destTile.clearance_height = z; } /** @@ -3035,46 +1623,18 @@ void map_remove_out_of_range_elements() void map_extend_boundary_surface() { SurfaceElement *existingTileElement, *newTileElement; - int32_t x, y, z, slope; + int32_t x, y; y = gMapSize - 2; for (x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) { - existingTileElement = map_get_surface_element_at(x, y - 1)->AsSurface(); - newTileElement = map_get_surface_element_at(x, y)->AsSurface(); - newTileElement->SetSurfaceStyle(existingTileElement->GetSurfaceStyle()); - newTileElement->SetEdgeStyle(existingTileElement->GetEdgeStyle()); - newTileElement->SetGrassLength(existingTileElement->GetGrassLength()); - newTileElement->SetOwnership(OWNERSHIP_UNOWNED); - newTileElement->SetWaterHeight(existingTileElement->GetWaterHeight()); + existingTileElement = map_get_surface_element_at(x, y - 1); + newTileElement = map_get_surface_element_at(x, y); - z = existingTileElement->base_height; - slope = existingTileElement->GetSlope() & TILE_ELEMENT_SLOPE_NW_SIDE_UP; - if (slope == TILE_ELEMENT_SLOPE_NW_SIDE_UP) + if (existingTileElement && newTileElement) { - z += 2; - slope = TILE_ELEMENT_SLOPE_FLAT; - if (existingTileElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) - { - slope = TILE_ELEMENT_SLOPE_N_CORNER_UP; - if (existingTileElement->GetSlope() & TILE_ELEMENT_SLOPE_S_CORNER_UP) - { - slope = TILE_ELEMENT_SLOPE_W_CORNER_UP; - if (existingTileElement->GetSlope() & TILE_ELEMENT_SLOPE_E_CORNER_UP) - { - slope = TILE_ELEMENT_SLOPE_FLAT; - } - } - } + map_extend_boundary_surface_extend_tile(*existingTileElement, *newTileElement); } - if (slope & TILE_ELEMENT_SLOPE_N_CORNER_UP) - slope |= TILE_ELEMENT_SLOPE_E_CORNER_UP; - if (slope & TILE_ELEMENT_SLOPE_W_CORNER_UP) - slope |= TILE_ELEMENT_SLOPE_S_CORNER_UP; - - newTileElement->SetSlope(slope); - newTileElement->base_height = z; - newTileElement->clearance_height = z; update_park_fences({ x << 5, y << 5 }); } @@ -3082,42 +1642,13 @@ void map_extend_boundary_surface() x = gMapSize - 2; for (y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) { - existingTileElement = map_get_surface_element_at(x - 1, y)->AsSurface(); - newTileElement = map_get_surface_element_at(x, y)->AsSurface(); + existingTileElement = map_get_surface_element_at(x - 1, y); + newTileElement = map_get_surface_element_at(x, y); - newTileElement->SetSurfaceStyle(existingTileElement->GetSurfaceStyle()); - newTileElement->SetEdgeStyle(existingTileElement->GetEdgeStyle()); - newTileElement->SetGrassLength(existingTileElement->GetGrassLength()); - newTileElement->SetOwnership(OWNERSHIP_UNOWNED); - newTileElement->SetWaterHeight(existingTileElement->GetWaterHeight()); - - z = existingTileElement->base_height; - slope = existingTileElement->GetSlope() & TILE_ELEMENT_SLOPE_NE_SIDE_UP; - if (slope == TILE_ELEMENT_SLOPE_NE_SIDE_UP) + if (existingTileElement && newTileElement) { - z += 2; - slope = TILE_ELEMENT_SLOPE_FLAT; - if (existingTileElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) - { - slope = TILE_ELEMENT_SLOPE_N_CORNER_UP; - if (existingTileElement->GetSlope() & TILE_ELEMENT_SLOPE_S_CORNER_UP) - { - slope = TILE_ELEMENT_SLOPE_E_CORNER_UP; - if (existingTileElement->GetSlope() & TILE_ELEMENT_SLOPE_W_CORNER_UP) - { - slope = TILE_ELEMENT_SLOPE_FLAT; - } - } - } + map_extend_boundary_surface_extend_tile(*existingTileElement, *newTileElement); } - if (slope & TILE_ELEMENT_SLOPE_N_CORNER_UP) - slope |= TILE_ELEMENT_SLOPE_W_CORNER_UP; - if (slope & TILE_ELEMENT_SLOPE_E_CORNER_UP) - slope |= TILE_ELEMENT_SLOPE_S_CORNER_UP; - - newTileElement->SetSlope(slope); - newTileElement->base_height = z; - newTileElement->clearance_height = z; update_park_fences({ x << 5, y << 5 }); } @@ -3127,7 +1658,7 @@ void map_extend_boundary_surface() * Clears the provided element properly from a certain tile, and updates * the pointer (when needed) passed to this function to point to the next element. */ -static void clear_element_at(int32_t x, int32_t y, TileElement** elementPtr) +static void clear_element_at(const CoordsXY& loc, TileElement** elementPtr) { TileElement* element = *elementPtr; switch (element->GetType()) @@ -3149,24 +1680,23 @@ static void clear_element_at(int32_t x, int32_t y, TileElement** elementPtr) case TILE_ELEMENT_TYPE_ENTRANCE: { int32_t rotation = element->GetDirectionWithOffset(1); + auto seqLoc = loc; switch (element->AsEntrance()->GetSequenceIndex()) { case 1: - x += CoordsDirectionDelta[rotation].x; - y += CoordsDirectionDelta[rotation].y; + seqLoc += CoordsDirectionDelta[rotation]; break; case 2: - x -= CoordsDirectionDelta[rotation].x; - y -= CoordsDirectionDelta[rotation].y; + seqLoc -= CoordsDirectionDelta[rotation]; break; } - gGameCommandErrorTitle = STR_CANT_REMOVE_THIS; - game_do_command(x, GAME_COMMAND_FLAG_APPLY, y, element->base_height / 2, GAME_COMMAND_REMOVE_PARK_ENTRANCE, 0, 0); + auto parkEntranceRemoveAction = ParkEntranceRemoveAction(CoordsXYZ{ seqLoc, element->base_height * 8 }); + GameActions::Execute(&parkEntranceRemoveAction); break; } case TILE_ELEMENT_TYPE_WALL: { - TileCoordsXYZD wallLocation = { x >> 5, y >> 5, element->base_height, element->GetDirection() }; + CoordsXYZD wallLocation = { loc.x, loc.y, element->base_height * 8, element->GetDirection() }; auto wallRemoveAction = WallRemoveAction(wallLocation); GameActions::Execute(&wallRemoveAction); } @@ -3174,16 +1704,18 @@ static void clear_element_at(int32_t x, int32_t y, TileElement** elementPtr) case TILE_ELEMENT_TYPE_LARGE_SCENERY: { auto removeSceneryAction = LargeSceneryRemoveAction( - x, y, element->base_height, element->GetDirection(), element->AsLargeScenery()->GetSequenceIndex()); + { loc.x, loc.y, element->base_height * 8, element->GetDirection() }, + element->AsLargeScenery()->GetSequenceIndex()); GameActions::Execute(&removeSceneryAction); } break; case TILE_ELEMENT_TYPE_BANNER: - gGameCommandErrorTitle = STR_CANT_REMOVE_THIS; - game_do_command( - x, GAME_COMMAND_FLAG_APPLY, y, (element->base_height) | ((element->AsBanner()->GetPosition() & 3) << 8), - GAME_COMMAND_REMOVE_BANNER, 0, 0); + { + auto bannerRemoveAction = BannerRemoveAction( + { loc.x, loc.y, element->base_height * 8, element->AsBanner()->GetPosition() }); + GameActions::Execute(&bannerRemoveAction); break; + } default: tile_element_remove(element); break; @@ -3194,49 +1726,50 @@ static void clear_element_at(int32_t x, int32_t y, TileElement** elementPtr) * Clears all elements properly from a certain tile. * rct2: 0x0068AE2A */ -static void clear_elements_at(int32_t x, int32_t y) +static void clear_elements_at(const CoordsXY& loc) { // Remove the spawn point (if there is one in the current tile) gPeepSpawns.erase( std::remove_if( gPeepSpawns.begin(), gPeepSpawns.end(), - [x, y](const auto& spawn) { return floor2(spawn.x, 32) == x && floor2(spawn.y, 32) == y; }), + [x = loc.x, y = loc.y](const auto& spawn) { return floor2(spawn.x, 32) == x && floor2(spawn.y, 32) == y; }), gPeepSpawns.end()); - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + TileElement* tileElement = map_get_first_element_at(loc.x / 32, loc.y / 32); + if (tileElement == nullptr) + return; // Remove all elements except the last one while (!tileElement->IsLastForTile()) - clear_element_at(x, y, &tileElement); + clear_element_at(loc, &tileElement); // Remove the last element - clear_element_at(x, y, &tileElement); + clear_element_at(loc, &tileElement); } -int32_t map_get_highest_z(int32_t tileX, int32_t tileY) +int32_t map_get_highest_z(const CoordsXY& loc) { - TileElement* tileElement; uint32_t z; - tileElement = map_get_surface_element_at(tileX, tileY); - if (tileElement == nullptr) + auto surfaceElement = map_get_surface_element_at(loc); + if (surfaceElement == nullptr) return -1; - z = tileElement->base_height * 8; + z = surfaceElement->base_height * 8; // Raise z so that is above highest point of land and water on tile - if ((tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != TILE_ELEMENT_SLOPE_FLAT) + if ((surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != TILE_ELEMENT_SLOPE_FLAT) z += 16; - if ((tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) != 0) + if ((surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) != 0) z += 16; - z = std::max(z, tileElement->AsSurface()->GetWaterHeight() * 16); + z = std::max(z, surfaceElement->GetWaterHeight() * 16); return z; } -TileElement* map_get_large_scenery_segment(int32_t x, int32_t y, int32_t z, int32_t direction, int32_t sequence) +LargeSceneryElement* map_get_large_scenery_segment(int32_t x, int32_t y, int32_t z, int32_t direction, int32_t sequence) { - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); if (tileElement == nullptr) { return nullptr; @@ -3252,14 +1785,14 @@ TileElement* map_get_large_scenery_segment(int32_t x, int32_t y, int32_t z, int3 if ((tileElement->GetDirection()) != direction) continue; - return tileElement; + return tileElement->AsLargeScenery(); } while (!(tileElement++)->IsLastForTile()); return nullptr; } EntranceElement* map_get_park_entrance_element_at(int32_t x, int32_t y, int32_t z, bool ghost) { - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); if (tileElement != nullptr) { do @@ -3273,7 +1806,7 @@ EntranceElement* map_get_park_entrance_element_at(int32_t x, int32_t y, int32_t if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE) continue; - if ((ghost == false) && (tileElement->IsGhost())) + if (!ghost && tileElement->IsGhost()) continue; return tileElement->AsEntrance(); @@ -3284,7 +1817,7 @@ EntranceElement* map_get_park_entrance_element_at(int32_t x, int32_t y, int32_t EntranceElement* map_get_ride_entrance_element_at(int32_t x, int32_t y, int32_t z, bool ghost) { - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); if (tileElement != nullptr) { do @@ -3298,7 +1831,7 @@ EntranceElement* map_get_ride_entrance_element_at(int32_t x, int32_t y, int32_t if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE) continue; - if ((ghost == false) && (tileElement->IsGhost())) + if (!ghost && tileElement->IsGhost()) continue; return tileElement->AsEntrance(); @@ -3309,7 +1842,7 @@ EntranceElement* map_get_ride_entrance_element_at(int32_t x, int32_t y, int32_t EntranceElement* map_get_ride_exit_element_at(int32_t x, int32_t y, int32_t z, bool ghost) { - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); if (tileElement != nullptr) { do @@ -3323,7 +1856,7 @@ EntranceElement* map_get_ride_exit_element_at(int32_t x, int32_t y, int32_t z, b if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT) continue; - if ((ghost == false) && (tileElement->IsGhost())) + if (!ghost && tileElement->IsGhost()) continue; return tileElement->AsEntrance(); @@ -3332,9 +1865,9 @@ EntranceElement* map_get_ride_exit_element_at(int32_t x, int32_t y, int32_t z, b return nullptr; } -TileElement* map_get_small_scenery_element_at(int32_t x, int32_t y, int32_t z, int32_t type, uint8_t quadrant) +SmallSceneryElement* map_get_small_scenery_element_at(int32_t x, int32_t y, int32_t z, int32_t type, uint8_t quadrant) { - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); if (tileElement != nullptr) { do @@ -3348,7 +1881,7 @@ TileElement* map_get_small_scenery_element_at(int32_t x, int32_t y, int32_t z, i if (tileElement->AsSmallScenery()->GetEntryIndex() != type) continue; - return tileElement; + return tileElement->AsSmallScenery(); } while (!(tileElement++)->IsLastForTile()); } return nullptr; @@ -3356,18 +1889,17 @@ TileElement* map_get_small_scenery_element_at(int32_t x, int32_t y, int32_t z, i bool map_large_scenery_get_origin( int32_t x, int32_t y, int32_t z, int32_t direction, int32_t sequence, int32_t* outX, int32_t* outY, int32_t* outZ, - TileElement** outElement) + LargeSceneryElement** outElement) { - TileElement* tileElement; rct_scenery_entry* sceneryEntry; rct_large_scenery_tile* tile; int16_t offsetX, offsetY; - tileElement = map_get_large_scenery_segment(x, y, z, direction, sequence); + auto tileElement = map_get_large_scenery_segment(x, y, z, direction, sequence); if (tileElement == nullptr) return false; - sceneryEntry = tileElement->AsLargeScenery()->GetEntry(); + sceneryEntry = tileElement->GetEntry(); tile = &sceneryEntry->large_scenery.tiles[sequence]; offsetX = tile->x_offset; @@ -3389,7 +1921,7 @@ bool map_large_scenery_get_origin( bool sign_set_colour( int32_t x, int32_t y, int32_t z, int32_t direction, int32_t sequence, uint8_t mainColour, uint8_t textColour) { - TileElement* tileElement; + LargeSceneryElement* tileElement; rct_scenery_entry* sceneryEntry; rct_large_scenery_tile *sceneryTiles, *tile; int16_t offsetX, offsetY; @@ -3400,7 +1932,7 @@ bool sign_set_colour( return false; } - sceneryEntry = tileElement->AsLargeScenery()->GetEntry(); + sceneryEntry = tileElement->GetEntry(); sceneryTiles = sceneryEntry->large_scenery.tiles; // Iterate through each tile of the large scenery element @@ -3417,8 +1949,8 @@ bool sign_set_colour( tileElement = map_get_large_scenery_segment(x, y, z, direction, sequence); if (tileElement != nullptr) { - tileElement->AsLargeScenery()->SetPrimaryColour(mainColour); - tileElement->AsLargeScenery()->SetSecondaryColour(textColour); + tileElement->SetPrimaryColour(mainColour); + tileElement->SetSecondaryColour(textColour); map_invalidate_tile(x, y, tileElement->base_height * 8, tileElement->clearance_height * 8); } @@ -3427,60 +1959,16 @@ bool sign_set_colour( return true; } -static void translate_3d_to_2d(int32_t rotation, int32_t* x, int32_t* y) +static ScreenCoordsXY translate_3d_to_2d(int32_t rotation, const CoordsXY& pos) { - int32_t rx, ry; - - switch (rotation & 3) - { - default: - case 0: - rx = (*y) - (*x); - ry = (*x) + (*y); - break; - case 1: - rx = -(*x) - (*y); - ry = (*y) - (*x); - break; - case 2: - rx = (*x) - (*y); - ry = -(*x) - (*y); - break; - case 3: - rx = (*x) + (*y); - ry = (*x) - (*y); - break; - } - ry /= 2; - - *x = rx; - *y = ry; + return translate_3d_to_2d_with_z(rotation, CoordsXYZ{ pos, 0 }); } -CoordsXY translate_3d_to_2d_with_z(int32_t rotation, CoordsXYZ pos) +ScreenCoordsXY translate_3d_to_2d_with_z(int32_t rotation, const CoordsXYZ& pos) { - CoordsXY result = {}; - switch (rotation & 3) - { - default: - case 0: - result.x = pos.y - pos.x; - result.y = (pos.x + pos.y) / 2 - pos.z; - break; - case 1: - result.x = -pos.x - pos.y; - result.y = (pos.y - pos.x) / 2 - pos.z; - break; - case 2: - result.x = pos.x - pos.y; - result.y = (-pos.x - pos.y) / 2 - pos.z; - break; - case 3: - result.x = pos.x + pos.y; - result.y = (pos.x - pos.y) / 2 - pos.z; - break; - } - return result; + auto rotated = pos.Rotate(rotation); + // Use right shift to avoid issues like #9301 + return ScreenCoordsXY{ rotated.y - rotated.x, ((rotated.x + rotated.y) >> 1) - pos.z }; } static void map_invalidate_tile_under_zoom(int32_t x, int32_t y, int32_t z0, int32_t z1, int32_t maxZoom) @@ -3492,12 +1980,12 @@ static void map_invalidate_tile_under_zoom(int32_t x, int32_t y, int32_t z0, int x += 16; y += 16; - translate_3d_to_2d(get_current_rotation(), &x, &y); + auto screenCoord = translate_3d_to_2d(get_current_rotation(), { x, y }); - x1 = x - 32; - y1 = y - 32 - z1; - x2 = x + 32; - y2 = y + 32 - z0; + x1 = screenCoord.x - 32; + y1 = screenCoord.y - 32 - z1; + x2 = screenCoord.x + 32; + y2 = screenCoord.y + 32 - z0; for (int32_t i = 0; i < MAX_VIEWPORT_COUNT; i++) { @@ -3560,7 +2048,7 @@ void map_invalidate_region(const LocationXY16& mins, const LocationXY16& maxs) x1 = maxs.x + 16; y1 = maxs.y + 16; - map_get_bounding_box(x0, y0, x1, y1, &left, &top, &right, &bottom); + map_get_bounding_box({ x0, y0, x1, y1 }, &left, &top, &right, &bottom); left -= 32; right += 32; @@ -3597,27 +2085,27 @@ int32_t map_get_tile_quadrant(int32_t mapX, int32_t mapY) */ bool map_surface_is_blocked(int16_t x, int16_t y) { - TileElement* tileElement; if (x >= 8192 || y >= 8192) return true; - tileElement = map_get_surface_element_at({ x, y }); + auto surfaceElement = map_get_surface_element_at({ x, y }); - if (tileElement == nullptr) + if (surfaceElement == nullptr) { return true; } - int16_t water_height = tileElement->AsSurface()->GetWaterHeight(); + int16_t water_height = surfaceElement->GetWaterHeight(); water_height *= 2; - if (water_height > tileElement->base_height) + if (water_height > surfaceElement->base_height) return true; - int16_t base_z = tileElement->base_height; - int16_t clear_z = tileElement->base_height + 2; - if (tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + int16_t base_z = surfaceElement->base_height; + int16_t clear_z = surfaceElement->base_height + 2; + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) clear_z += 2; + auto tileElement = reinterpret_cast(surfaceElement); while (!(tileElement++)->IsLastForTile()) { if (clear_z >= tileElement->clearance_height) @@ -3650,179 +2138,22 @@ void map_clear_all_elements() { for (int32_t x = 0; x < (MAXIMUM_MAP_SIZE_TECHNICAL * 32); x += 32) { - clear_elements_at(x, y); + clear_elements_at({ x, y }); } } } -void game_command_modify_tile( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - const int32_t flags = *ebx; - const int32_t x = *ecx & 0xFF; - const int32_t y = (*ecx >> 8) & 0xFF; - const TILE_INSPECTOR_INSTRUCTION_TYPE instruction = static_cast(*eax); - - switch (instruction) - { - case TILE_INSPECTOR_ANY_REMOVE: - { - const int16_t elementIndex = *edx; - *ebx = tile_inspector_remove_element_at(x, y, elementIndex, flags); - break; - } - case TILE_INSPECTOR_ANY_SWAP: - { - const int32_t firstIndex = *edx; - const int32_t secondIndex = *edi; - *ebx = tile_inspector_swap_elements_at(x, y, firstIndex, secondIndex, flags); - break; - } - case TILE_INSPECTOR_ANY_INSERT_CORRUPT: - { - const int16_t elementIndex = *edx; - *ebx = tile_inspector_insert_corrupt_at(x, y, elementIndex, flags); - break; - } - case TILE_INSPECTOR_ANY_ROTATE: - { - const int16_t elementIndex = *edx; - *ebx = tile_inspector_rotate_element_at(x, y, elementIndex, flags); - break; - } - case TILE_INSPECTOR_ANY_PASTE: - { - TileElement elementToPaste; - const int32_t data[] = { *edx, *edi }; - assert_struct_size(data, sizeof(elementToPaste)); - std::memcpy(&elementToPaste, data, 8); - *ebx = tile_inspector_paste_element_at(x, y, elementToPaste, flags); - break; - } - case TILE_INSPECTOR_ANY_SORT: - { - *ebx = tile_inspector_sort_elements_at(x, y, flags); - break; - } - case TILE_INSPECTOR_ANY_BASE_HEIGHT_OFFSET: - { - const int16_t elementIndex = *edx; - const int8_t heightOffset = *edi; - *ebx = tile_inspector_any_base_height_offset(x, y, elementIndex, heightOffset, flags); - break; - } - case TILE_INSPECTOR_SURFACE_SHOW_PARK_FENCES: - { - const bool showFences = *edx; - *ebx = tile_inspector_surface_show_park_fences(x, y, showFences, flags); - break; - } - case TILE_INSPECTOR_SURFACE_TOGGLE_CORNER: - { - const int32_t cornerIndex = *edx; - *ebx = tile_inspector_surface_toggle_corner(x, y, cornerIndex, flags); - break; - } - case TILE_INSPECTOR_SURFACE_TOGGLE_DIAGONAL: - { - *ebx = tile_inspector_surface_toggle_diagonal(x, y, flags); - break; - } - case TILE_INSPECTOR_PATH_SET_SLOPE: - { - const int32_t elementIndex = *edx; - const bool sloped = *edi; - *ebx = tile_inspector_path_set_sloped(x, y, elementIndex, sloped, flags); - break; - } - case TILE_INSPECTOR_PATH_TOGGLE_EDGE: - { - const int32_t elementIndex = *edx; - const int32_t edgeIndex = *edi; - *ebx = tile_inspector_path_toggle_edge(x, y, elementIndex, edgeIndex, flags); - break; - } - case TILE_INSPECTOR_ENTRANCE_MAKE_USABLE: - { - const int32_t elementIndex = *edx; - *ebx = tile_inspector_entrance_make_usable(x, y, elementIndex, flags); - break; - } - case TILE_INSPECTOR_WALL_SET_SLOPE: - { - const int32_t elementIndex = *edx; - const int32_t slopeValue = *edi; - *ebx = tile_inspector_wall_set_slope(x, y, elementIndex, slopeValue, flags); - break; - } - case TILE_INSPECTOR_TRACK_BASE_HEIGHT_OFFSET: - { - const int32_t elementIndex = *edx; - const int8_t heightOffset = *edi; - *ebx = tile_inspector_track_base_height_offset(x, y, elementIndex, heightOffset, flags); - break; - } - case TILE_INSPECTOR_TRACK_SET_CHAIN: - { - const int32_t elementIndex = *edx; - const bool entireTrackBlock = *edi; - const bool setChain = *ebp; - *ebx = tile_inspector_track_set_chain(x, y, elementIndex, entireTrackBlock, setChain, flags); - break; - } - case TILE_INSPECTOR_SCENERY_SET_QUARTER_LOCATION: - { - const int32_t elementIndex = *edx; - const int32_t quarterIndex = *edi; - *ebx = tile_inspector_scenery_set_quarter_location(x, y, elementIndex, quarterIndex, flags); - break; - } - case TILE_INSPECTOR_SCENERY_SET_QUARTER_COLLISION: - { - const int32_t elementIndex = *edx; - const int32_t quarterIndex = *edi; - *ebx = tile_inspector_scenery_set_quarter_collision(x, y, elementIndex, quarterIndex, flags); - break; - } - case TILE_INSPECTOR_BANNER_TOGGLE_BLOCKING_EDGE: - { - const int32_t elementIndex = *edx; - const int32_t edgeIndex = *edi; - *ebx = tile_inspector_banner_toggle_blocking_edge(x, y, elementIndex, edgeIndex, flags); - break; - } - case TILE_INSPECTOR_CORRUPT_CLAMP: - { - const int32_t elementIndex = *edx; - *ebx = tile_inspector_corrupt_clamp(x, y, elementIndex, flags); - break; - } - default: - log_error("invalid instruction"); - *ebx = MONEY32_UNDEFINED; - break; - } - - if (flags & GAME_COMMAND_FLAG_APPLY && gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST) - && *ebx != MONEY32_UNDEFINED) - { - LocationXYZ16 coord; - coord.x = (x << 5) + 16; - coord.y = (y << 5) + 16; - coord.z = tile_element_height(coord.x, coord.y); - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } -} - /** * Gets the track element at x, y, z. * @param x x units, not tiles. * @param y y units, not tiles. * @param z Base height. */ -TileElement* map_get_track_element_at(int32_t x, int32_t y, int32_t z) +TrackElement* map_get_track_element_at(int32_t x, int32_t y, int32_t z) { TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -3830,7 +2161,7 @@ TileElement* map_get_track_element_at(int32_t x, int32_t y, int32_t z) if (tileElement->base_height != z) continue; - return tileElement; + return tileElement->AsTrack(); } while (!(tileElement++)->IsLastForTile()); return nullptr; @@ -3845,6 +2176,8 @@ TileElement* map_get_track_element_at(int32_t x, int32_t y, int32_t z) TileElement* map_get_track_element_at_of_type(int32_t x, int32_t y, int32_t z, int32_t trackType) { TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -3888,6 +2221,54 @@ TileElement* map_get_track_element_at_of_type_seq(int32_t x, int32_t y, int32_t return nullptr; } +TrackElement* map_get_track_element_at_of_type(CoordsXYZD location, int32_t trackType) +{ + auto tileElement = map_get_first_element_at(location.x / 32, location.y / 32); + if (tileElement != nullptr) + { + do + { + auto trackElement = tileElement->AsTrack(); + if (trackElement != nullptr) + { + if (trackElement->base_height != location.z / 8) + continue; + if (trackElement->GetDirection() != location.direction) + continue; + if (trackElement->GetTrackType() != trackType) + continue; + return trackElement; + } + } while (!(tileElement++)->IsLastForTile()); + } + return nullptr; +} + +TrackElement* map_get_track_element_at_of_type_seq(CoordsXYZD location, int32_t trackType, int32_t sequence) +{ + auto tileElement = map_get_first_element_at(location.x / 32, location.y / 32); + if (tileElement != nullptr) + { + do + { + auto trackElement = tileElement->AsTrack(); + if (trackElement != nullptr) + { + if (trackElement->base_height != location.z / 8) + continue; + if (trackElement->GetDirection() != location.direction) + continue; + if (trackElement->GetTrackType() != trackType) + continue; + if (trackElement->GetSequenceIndex() != sequence) + continue; + return trackElement; + } + } while (!(tileElement++)->IsLastForTile()); + } + return nullptr; +} + /** * Gets the track element at x, y, z that is the given track type and sequence. * @param x x units, not tiles. @@ -3897,6 +2278,8 @@ TileElement* map_get_track_element_at_of_type_seq(int32_t x, int32_t y, int32_t TileElement* map_get_track_element_at_of_type_from_ride(int32_t x, int32_t y, int32_t z, int32_t trackType, ride_id_t rideIndex) { TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -3923,6 +2306,8 @@ TileElement* map_get_track_element_at_of_type_from_ride(int32_t x, int32_t y, in TileElement* map_get_track_element_at_from_ride(int32_t x, int32_t y, int32_t z, ride_id_t rideIndex) { TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -3949,6 +2334,8 @@ TileElement* map_get_track_element_at_with_direction_from_ride( int32_t x, int32_t y, int32_t z, int32_t direction, ride_id_t rideIndex) { TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -3968,30 +2355,19 @@ TileElement* map_get_track_element_at_with_direction_from_ride( void map_offset_with_rotation(int16_t* x, int16_t* y, int16_t offsetX, int16_t offsetY, uint8_t rotation) { - switch (rotation & 3) - { - case TILE_ELEMENT_DIRECTION_WEST: - *x += offsetX; - *y += offsetY; - break; - case TILE_ELEMENT_DIRECTION_NORTH: - *x += offsetY; - *y -= offsetX; - break; - case TILE_ELEMENT_DIRECTION_EAST: - *x -= offsetX; - *y -= offsetY; - break; - case TILE_ELEMENT_DIRECTION_SOUTH: - *x -= offsetY; - *y += offsetX; - break; - } + TileCoordsXY offsets = { offsetX, offsetY }; + TileCoordsXY newCoords = { *x, *y }; + newCoords += offsets.Rotate(rotation); + + *x = (int16_t)newCoords.x; + *y = (int16_t)newCoords.y; } -TileElement* map_get_wall_element_at(int32_t x, int32_t y, int32_t z, int32_t direction) +WallElement* map_get_wall_element_at(int32_t x, int32_t y, int32_t z, int32_t direction) { TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return nullptr; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_WALL) @@ -4001,7 +2377,7 @@ TileElement* map_get_wall_element_at(int32_t x, int32_t y, int32_t z, int32_t di if (tileElement->GetDirection() != direction) continue; - return tileElement; + return tileElement->AsWall(); } while (!(tileElement++)->IsLastForTile()); return nullptr; } @@ -4044,11 +2420,10 @@ void FixLandOwnershipTiles(std::initializer_list tiles) void FixLandOwnershipTilesWithOwnership(std::initializer_list tiles, uint8_t ownership) { - TileElement* currentElement; for (const TileCoordsXY* tile = tiles.begin(); tile != tiles.end(); ++tile) { - currentElement = map_get_surface_element_at((*tile).x, (*tile).y); - currentElement->AsSurface()->SetOwnership(ownership); + auto surfaceElement = map_get_surface_element_at((*tile).x, (*tile).y); + surfaceElement->SetOwnership(ownership); update_park_fences_around_tile({ (*tile).x * 32, (*tile).y * 32 }); } } diff --git a/src/openrct2/world/Map.h b/src/openrct2/world/Map.h index 5bf2570356..f4fdf3bf82 100644 --- a/src/openrct2/world/Map.h +++ b/src/openrct2/world/Map.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -107,7 +107,7 @@ extern uint8_t gMapGroundFlags; extern TileElement gTileElements[MAX_TILE_TILE_ELEMENT_POINTERS * 3]; extern TileElement* gTileElementTilePointers[MAX_TILE_TILE_ELEMENT_POINTERS]; -extern LocationXY16 gMapSelectionTiles[300]; +extern std::vector gMapSelectionTiles; extern std::vector gPeepSpawns; extern TileElement* gNextFreeTileElement; @@ -129,93 +129,60 @@ extern LocationXYZ16 gCommandPosition; extern bool gMapLandRightsUpdateSuccess; +constexpr auto SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT = 0x20; +extern const uint8_t tile_element_lower_styles[9][32]; +extern const uint8_t tile_element_raise_styles[9][32]; + void map_init(int32_t size); + void map_count_remaining_land_rights(); void map_strip_ghost_flag_from_elements(); void map_update_tile_pointers(); TileElement* map_get_first_element_at(int32_t x, int32_t y); TileElement* map_get_nth_element_at(int32_t x, int32_t y, int32_t n); void map_set_tile_elements(int32_t x, int32_t y, TileElement* elements); -int32_t map_height_from_slope(CoordsXY coords, int32_t slope, bool isSloped); +int32_t map_height_from_slope(const CoordsXY& coords, int32_t slope, bool isSloped); BannerElement* map_get_banner_element_at(int32_t x, int32_t y, int32_t z, uint8_t direction); -TileElement* map_get_surface_element_at(int32_t x, int32_t y); -TileElement* map_get_surface_element_at(CoordsXY coords); -TileElement* map_get_path_element_at(int32_t x, int32_t y, int32_t z); -TileElement* map_get_wall_element_at(int32_t x, int32_t y, int32_t z, int32_t direction); -TileElement* map_get_small_scenery_element_at(int32_t x, int32_t y, int32_t z, int32_t type, uint8_t quadrant); +SurfaceElement* map_get_surface_element_at(int32_t x, int32_t y); +SurfaceElement* map_get_surface_element_at(const CoordsXY& coords); +PathElement* map_get_path_element_at(const TileCoordsXYZ& loc); +WallElement* map_get_wall_element_at(int32_t x, int32_t y, int32_t z, int32_t direction); +SmallSceneryElement* map_get_small_scenery_element_at(int32_t x, int32_t y, int32_t z, int32_t type, uint8_t quadrant); EntranceElement* map_get_park_entrance_element_at(int32_t x, int32_t y, int32_t z, bool ghost); EntranceElement* map_get_ride_entrance_element_at(int32_t x, int32_t y, int32_t z, bool ghost); EntranceElement* map_get_ride_exit_element_at(int32_t x, int32_t y, int32_t z, bool ghost); -int32_t tile_element_height(int32_t x, int32_t y); -bool map_coord_is_connected(int32_t x, int32_t y, int32_t z, uint8_t faceDirection); +int16_t tile_element_height(const CoordsXY& loc); +int16_t tile_element_water_height(const CoordsXY& loc); +uint8_t map_get_highest_land_height(const MapRange& range); +uint8_t map_get_lowest_land_height(const MapRange& range); +bool map_coord_is_connected(const TileCoordsXYZ& loc, uint8_t faceDirection); void map_remove_provisional_elements(); void map_restore_provisional_elements(); void map_update_path_wide_flags(); -bool map_is_location_valid(CoordsXY coords); -bool map_is_edge(CoordsXY coords); -bool map_can_build_at(int32_t x, int32_t y, int32_t z); -bool map_is_location_owned(int32_t x, int32_t y, int32_t z); -bool map_is_location_in_park(CoordsXY coords); -bool map_is_location_owned_or_has_rights(int32_t x, int32_t y); +bool map_is_location_valid(const CoordsXY& coords); +bool map_is_edge(const CoordsXY& coords); +bool map_can_build_at(const CoordsXYZ& loc); +bool map_is_location_owned(const CoordsXYZ& loc); +bool map_is_location_in_park(const CoordsXY& coords); +bool map_is_location_owned_or_has_rights(const CoordsXY& loc); bool map_surface_is_blocked(int16_t x, int16_t y); void tile_element_remove(TileElement* tileElement); void map_remove_all_rides(); void map_invalidate_map_selection_tiles(); -void map_get_bounding_box( - int32_t ax, int32_t ay, int32_t bx, int32_t by, int32_t* left, int32_t* top, int32_t* right, int32_t* bottom); void map_invalidate_selection_rect(); void map_reorganise_elements(); bool map_check_free_elements_and_reorganise(int32_t num_elements); -TileElement* tile_element_insert(int32_t x, int32_t y, int32_t z, int32_t flags); +TileElement* tile_element_insert(const TileCoordsXYZ& loc, int32_t occupiedQuadrants); using CLEAR_FUNC = int32_t (*)(TileElement** tile_element, int32_t x, int32_t y, uint8_t flags, money32* price); int32_t map_place_non_scenery_clear_func(TileElement** tile_element, int32_t x, int32_t y, uint8_t flags, money32* price); int32_t map_place_scenery_clear_func(TileElement** tile_element, int32_t x, int32_t y, uint8_t flags, money32* price); bool map_can_construct_with_clear_at( - int32_t x, int32_t y, int32_t zLow, int32_t zHigh, CLEAR_FUNC clearFunc, QuarterTile bl, uint8_t flags, money32* price, - uint8_t crossingMode); + int32_t x, int32_t y, int32_t zLow, int32_t zHigh, CLEAR_FUNC clearFunc, QuarterTile quarterTile, uint8_t flags, + money32* price, uint8_t crossingMode); int32_t map_can_construct_at(int32_t x, int32_t y, int32_t zLow, int32_t zHigh, QuarterTile bl); void rotate_map_coordinates(int16_t* x, int16_t* y, int32_t rotation); -LocationXY16 coordinate_3d_to_2d(const LocationXYZ16* coordinate_3d, int32_t rotation); -money32 map_clear_scenery(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t clear, int32_t flags); -money32 lower_water(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t flags); -money32 raise_water(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t flags); -money32 wall_place( - int32_t type, int32_t x, int32_t y, int32_t z, int32_t edge, int32_t primaryColour, int32_t secondaryColour, - int32_t tertiaryColour, int32_t flags); - -void game_command_set_land_ownership( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_remove_banner( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_set_scenery_colour( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_set_wall_colour( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_set_large_scenery_colour( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_set_banner_colour( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_change_surface_style( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_raise_land(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_lower_land(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_smooth_land(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_raise_water(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_lower_water(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_place_banner( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_place_wall(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_place_large_scenery( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_place_park_entrance( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_set_banner_name( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_set_banner_style( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_modify_tile(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); struct tile_element_iterator { @@ -233,7 +200,7 @@ void tile_element_iterator_restart_for_tile(tile_element_iterator* it); void wall_remove_intersecting_walls(int32_t x, int32_t y, int32_t z0, int32_t z1, int32_t direction); void map_update_tiles(); -int32_t map_get_highest_z(int32_t tileX, int32_t tileY); +int32_t map_get_highest_z(const CoordsXY& loc); bool tile_element_wants_path_connection_towards(TileCoordsXYZD coords, const TileElement* const elementToBeRemoved); @@ -255,28 +222,30 @@ void map_invalidate_region(const LocationXY16& mins, const LocationXY16& maxs); int32_t map_get_tile_side(int32_t mapX, int32_t mapY); int32_t map_get_tile_quadrant(int32_t mapX, int32_t mapY); int32_t map_get_corner_height(int32_t z, int32_t slope, int32_t direction); -int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t direction); +int32_t tile_element_get_corner_height(const SurfaceElement* surfaceElement, int32_t direction); void map_clear_all_elements(); -TileElement* map_get_large_scenery_segment(int32_t x, int32_t y, int32_t z, int32_t direction, int32_t sequence); +LargeSceneryElement* map_get_large_scenery_segment(int32_t x, int32_t y, int32_t z, int32_t direction, int32_t sequence); bool map_large_scenery_get_origin( int32_t x, int32_t y, int32_t z, int32_t direction, int32_t sequence, int32_t* outX, int32_t* outY, int32_t* outZ, - TileElement** outElement); + LargeSceneryElement** outElement); void map_offset_with_rotation(int16_t* x, int16_t* y, int16_t offsetX, int16_t offsetY, uint8_t rotation); -CoordsXY translate_3d_to_2d_with_z(int32_t rotation, CoordsXYZ pos); +ScreenCoordsXY translate_3d_to_2d_with_z(int32_t rotation, const CoordsXYZ& pos); -TileElement* map_get_track_element_at(int32_t x, int32_t y, int32_t z); +TrackElement* map_get_track_element_at(int32_t x, int32_t y, int32_t z); TileElement* map_get_track_element_at_of_type(int32_t x, int32_t y, int32_t z, int32_t trackType); TileElement* map_get_track_element_at_of_type_seq(int32_t x, int32_t y, int32_t z, int32_t trackType, int32_t sequence); +TrackElement* map_get_track_element_at_of_type(CoordsXYZD location, int32_t trackType); +TrackElement* map_get_track_element_at_of_type_seq(CoordsXYZD location, int32_t trackType, int32_t sequence); TileElement* map_get_track_element_at_of_type_from_ride( int32_t x, int32_t y, int32_t z, int32_t trackType, ride_id_t rideIndex); TileElement* map_get_track_element_at_from_ride(int32_t x, int32_t y, int32_t z, ride_id_t rideIndex); TileElement* map_get_track_element_at_with_direction_from_ride( int32_t x, int32_t y, int32_t z, int32_t direction, ride_id_t rideIndex); -bool map_is_location_at_edge(int32_t x, int32_t y); +bool map_is_location_at_edge(const CoordsXY& loc); void map_obstruction_set_error_text(TileElement* tileElement); uint16_t check_max_allowable_land_rights_for_tile(uint8_t x, uint8_t y, uint8_t base_z); diff --git a/src/openrct2/world/MapAnimation.cpp b/src/openrct2/world/MapAnimation.cpp index 84ee86c4a2..5f3b1f7176 100644 --- a/src/openrct2/world/MapAnimation.cpp +++ b/src/openrct2/world/MapAnimation.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -25,53 +25,42 @@ #include "SmallScenery.h" #include "Sprite.h" -#include - using map_animation_invalidate_event_handler = bool (*)(int32_t x, int32_t y, int32_t baseZ); -static bool map_animation_invalidate(rct_map_animation* obj); +static std::vector _mapAnimations; -uint16_t gNumMapAnimations; -rct_map_animation gAnimatedObjects[MAX_ANIMATED_OBJECTS]; +constexpr size_t MAX_ANIMATED_OBJECTS = 2000; + +static bool InvalidateMapAnimation(const MapAnimation& obj); + +static bool DoesAnimationExist(int32_t type, const CoordsXYZ& location) +{ + for (const auto& a : _mapAnimations) + { + if (a.type == type && a.location.x == location.x && a.location.y == location.y && a.location.z == location.z) + { + // Animation already exists + return true; + } + } + return false; +} -/** - * - * rct2: 0x0068AF67 - * - * @param type (dh) - * @param x (ax) - * @param y (cx) - * @param z (dl) - */ void map_animation_create(int32_t type, int32_t x, int32_t y, int32_t z) { - rct_map_animation* aobj = &gAnimatedObjects[0]; - int32_t numAnimatedObjects = gNumMapAnimations; - if (numAnimatedObjects >= MAX_ANIMATED_OBJECTS) + auto location = CoordsXYZ{ x, y, z }; + if (!DoesAnimationExist(type, location)) { - log_error("Exceeded the maximum number of animations"); - return; + if (_mapAnimations.size() < MAX_ANIMATED_OBJECTS) + { + // Create new animation + _mapAnimations.push_back({ (uint8_t)type, location }); + } + else + { + log_error("Exceeded the maximum number of animations"); + } } - for (int32_t i = 0; i < numAnimatedObjects; i++, aobj++) - { - if (aobj->x != x) - continue; - if (aobj->y != y) - continue; - if (aobj->baseZ != z) - continue; - if (aobj->type != type) - continue; - // Animation already exists - return; - } - - // Create new animation - gNumMapAnimations++; - aobj->type = type; - aobj->x = x; - aobj->y = y; - aobj->baseZ = z; } /** @@ -80,22 +69,17 @@ void map_animation_create(int32_t type, int32_t x, int32_t y, int32_t z) */ void map_animation_invalidate_all() { - rct_map_animation* aobj = &gAnimatedObjects[0]; - int32_t numAnimatedObjects = gNumMapAnimations; - while (numAnimatedObjects > 0) + auto it = _mapAnimations.begin(); + while (it != _mapAnimations.end()) { - if (map_animation_invalidate(aobj)) + if (InvalidateMapAnimation(*it)) { - // Remove animated object - gNumMapAnimations--; - numAnimatedObjects--; - if (numAnimatedObjects > 0) - memmove(aobj, aobj + 1, numAnimatedObjects * sizeof(rct_map_animation)); + // Map animation has finished, remove it + it = _mapAnimations.erase(it); } else { - numAnimatedObjects--; - aobj++; + it++; } } } @@ -107,6 +91,8 @@ void map_animation_invalidate_all() static bool map_animation_invalidate_ride_entrance(int32_t x, int32_t y, int32_t baseZ) { auto tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) @@ -141,13 +127,15 @@ static bool map_animation_invalidate_queue_banner(int32_t x, int32_t y, int32_t TileElement* tileElement; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) continue; if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) continue; - if (!(tileElement->flags & 1)) + if (!(tileElement->AsPath()->IsQueue())) continue; if (!tileElement->AsPath()->HasQueueBanner()) continue; @@ -176,6 +164,8 @@ static bool map_animation_invalidate_small_scenery(int32_t x, int32_t y, int32_t Peep* peep; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) @@ -211,7 +201,7 @@ static bool map_animation_invalidate_small_scenery(int32_t x, int32_t y, int32_t for (; spriteIdx != SPRITE_INDEX_NULL; spriteIdx = sprite->generic.next_in_quadrant) { sprite = get_sprite(spriteIdx); - if (sprite->generic.linked_list_type_offset != SPRITE_LIST_PEEP * 2) + if (sprite->generic.linked_list_index != SPRITE_LIST_PEEP) continue; peep = &sprite->peep; @@ -247,6 +237,8 @@ static bool map_animation_invalidate_park_entrance(int32_t x, int32_t y, int32_t TileElement* tileElement; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) @@ -275,6 +267,8 @@ static bool map_animation_invalidate_track_waterfall(int32_t x, int32_t y, int32 TileElement* tileElement; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) @@ -302,6 +296,8 @@ static bool map_animation_invalidate_track_rapids(int32_t x, int32_t y, int32_t TileElement* tileElement; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) @@ -329,10 +325,10 @@ static bool map_animation_invalidate_track_onridephoto(int32_t x, int32_t y, int TileElement* tileElement; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { - if (tileElement == nullptr) - break; if (tileElement->base_height != baseZ) continue; if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) @@ -369,6 +365,8 @@ static bool map_animation_invalidate_track_whirlpool(int32_t x, int32_t y, int32 TileElement* tileElement; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) @@ -396,6 +394,8 @@ static bool map_animation_invalidate_track_spinningtunnel(int32_t x, int32_t y, TileElement* tileElement; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) @@ -433,6 +433,8 @@ static bool map_animation_invalidate_banner(int32_t x, int32_t y, int32_t baseZ) TileElement* tileElement; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) @@ -459,6 +461,8 @@ static bool map_animation_invalidate_large_scenery(int32_t x, int32_t y, int32_t bool wasInvalidated = false; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) @@ -492,6 +496,8 @@ static bool map_animation_invalidate_wall_door(int32_t x, int32_t y, int32_t bas bool removeAnimation = true; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return removeAnimation; do { if (tileElement->base_height != baseZ) @@ -552,6 +558,8 @@ static bool map_animation_invalidate_wall(int32_t x, int32_t y, int32_t baseZ) bool wasInvalidated = false; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return true; do { if (tileElement->base_height != baseZ) @@ -598,9 +606,117 @@ static constexpr const map_animation_invalidate_event_handler _animatedObjectEve /** * @returns true if the animation should be removed. */ -static bool map_animation_invalidate(rct_map_animation* obj) +static bool InvalidateMapAnimation(const MapAnimation& a) { - assert(obj->type < MAP_ANIMATION_TYPE_COUNT); - - return _animatedObjectEventHandlers[obj->type](obj->x, obj->y, obj->baseZ); + if (a.type < std::size(_animatedObjectEventHandlers)) + { + return _animatedObjectEventHandlers[a.type](a.location.x, a.location.y, a.location.z); + } + return true; +} + +const std::vector& GetMapAnimations() +{ + return _mapAnimations; +} + +static void ClearMapAnimations() +{ + _mapAnimations.clear(); +} + +void AutoCreateMapAnimations() +{ + ClearMapAnimations(); + + tile_element_iterator it; + tile_element_iterator_begin(&it); + while (tile_element_iterator_next(&it)) + { + auto el = it.element; + auto loc = CoordsXYZ{ it.x * 32, it.y * 32, el->base_height }; + switch (el->GetType()) + { + case TILE_ELEMENT_TYPE_BANNER: + map_animation_create(MAP_ANIMATION_TYPE_BANNER, loc.x, loc.y, loc.z); + break; + case TILE_ELEMENT_TYPE_WALL: + { + auto wallEl = el->AsWall(); + auto entry = wallEl->GetEntry(); + if (entry != nullptr + && ((entry->wall.flags2 & WALL_SCENERY_2_ANIMATED) || entry->wall.scrolling_mode != SCROLLING_MODE_NONE)) + { + map_animation_create(MAP_ANIMATION_TYPE_WALL, loc.x, loc.y, loc.z); + } + break; + } + case TILE_ELEMENT_TYPE_SMALL_SCENERY: + { + auto sceneryEl = el->AsSmallScenery(); + auto entry = sceneryEl->GetEntry(); + if (entry != nullptr && scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_ANIMATED)) + { + map_animation_create(MAP_ANIMATION_TYPE_SMALL_SCENERY, loc.x, loc.y, loc.z); + } + break; + } + case TILE_ELEMENT_TYPE_LARGE_SCENERY: + { + auto sceneryEl = el->AsLargeScenery(); + auto entry = sceneryEl->GetEntry(); + if (entry != nullptr && (entry->large_scenery.flags & LARGE_SCENERY_FLAG_ANIMATED)) + { + map_animation_create(MAP_ANIMATION_TYPE_LARGE_SCENERY, loc.x, loc.y, loc.z); + } + break; + } + case TILE_ELEMENT_TYPE_PATH: + { + auto path = el->AsPath(); + if (path->HasQueueBanner()) + { + map_animation_create(MAP_ANIMATION_TYPE_QUEUE_BANNER, loc.x, loc.y, loc.z); + } + break; + } + case TILE_ELEMENT_TYPE_ENTRANCE: + { + auto entrance = el->AsEntrance(); + switch (entrance->GetEntranceType()) + { + case ENTRANCE_TYPE_PARK_ENTRANCE: + if (entrance->GetSequenceIndex() == 0) + { + map_animation_create(MAP_ANIMATION_TYPE_PARK_ENTRANCE, loc.x, loc.y, loc.z); + } + break; + case ENTRANCE_TYPE_RIDE_ENTRANCE: + map_animation_create(MAP_ANIMATION_TYPE_RIDE_ENTRANCE, loc.x, loc.y, loc.z); + break; + } + break; + } + case TILE_ELEMENT_TYPE_TRACK: + { + auto track = el->AsTrack(); + switch (track->GetTrackType()) + { + case TRACK_ELEM_WATERFALL: + map_animation_create(MAP_ANIMATION_TYPE_TRACK_WATERFALL, loc.x, loc.y, loc.z); + break; + case TRACK_ELEM_RAPIDS: + map_animation_create(MAP_ANIMATION_TYPE_TRACK_RAPIDS, loc.x, loc.y, loc.z); + break; + case TRACK_ELEM_WHIRLPOOL: + map_animation_create(MAP_ANIMATION_TYPE_TRACK_WHIRLPOOL, loc.x, loc.y, loc.z); + break; + case TRACK_ELEM_SPINNING_TUNNEL: + map_animation_create(MAP_ANIMATION_TYPE_TRACK_SPINNINGTUNNEL, loc.x, loc.y, loc.z); + break; + } + break; + } + } + } } diff --git a/src/openrct2/world/MapAnimation.h b/src/openrct2/world/MapAnimation.h index 9bf6efd7b5..6f8c9d64dc 100644 --- a/src/openrct2/world/MapAnimation.h +++ b/src/openrct2/world/MapAnimation.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -7,25 +7,18 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ -#ifndef _MAP_ANIMATION_H_ -#define _MAP_ANIMATION_H_ +#pragma once -#include "../common.h" +#include "Location.hpp" -#pragma pack(push, 1) -/** - * Animated object - * size: 0x06 - */ -struct rct_map_animation +#include +#include + +struct MapAnimation { - uint8_t baseZ; - uint8_t type; - uint16_t x; - uint16_t y; + uint8_t type{}; + CoordsXYZ location{}; }; -assert_struct_size(rct_map_animation, 6); -#pragma pack(pop) enum { @@ -46,12 +39,7 @@ enum MAP_ANIMATION_TYPE_COUNT }; -#define MAX_ANIMATED_OBJECTS 2000 - -extern uint16_t gNumMapAnimations; -extern rct_map_animation gAnimatedObjects[MAX_ANIMATED_OBJECTS]; - void map_animation_create(int32_t type, int32_t x, int32_t y, int32_t z); void map_animation_invalidate_all(); - -#endif +const std::vector& GetMapAnimations(); +void AutoCreateMapAnimations(); diff --git a/src/openrct2/world/MapGen.cpp b/src/openrct2/world/MapGen.cpp index 06f74aa5d8..8b37ff45b9 100644 --- a/src/openrct2/world/MapGen.cpp +++ b/src/openrct2/world/MapGen.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -111,8 +111,6 @@ static void set_height(int32_t x, int32_t y, int32_t height) void mapgen_generate_blank(mapgen_settings* settings) { int32_t x, y; - TileElement* tileElement; - map_clear_all_elements(); map_init(settings->mapSize); @@ -120,11 +118,11 @@ void mapgen_generate_blank(mapgen_settings* settings) { for (x = 1; x < settings->mapSize - 1; x++) { - tileElement = map_get_surface_element_at(x, y); - tileElement->AsSurface()->SetSurfaceStyle(settings->floor); - tileElement->AsSurface()->SetEdgeStyle(settings->wall); - tileElement->base_height = settings->height; - tileElement->clearance_height = settings->height; + auto surfaceElement = map_get_surface_element_at(x, y); + surfaceElement->SetSurfaceStyle(settings->floor); + surfaceElement->SetEdgeStyle(settings->wall); + surfaceElement->base_height = settings->height; + surfaceElement->clearance_height = settings->height; } } @@ -134,9 +132,6 @@ void mapgen_generate_blank(mapgen_settings* settings) void mapgen_generate(mapgen_settings* settings) { int32_t x, y, mapSize, floorTexture, wallTexture, waterLevel; - TileElement* tileElement; - - util_srand((int32_t)platform_get_ticks()); mapSize = settings->mapSize; floorTexture = settings->floor; @@ -171,11 +166,11 @@ void mapgen_generate(mapgen_settings* settings) { for (x = 1; x < mapSize - 1; x++) { - tileElement = map_get_surface_element_at(x, y); - tileElement->AsSurface()->SetSurfaceStyle(floorTexture); - tileElement->AsSurface()->SetEdgeStyle(wallTexture); - tileElement->base_height = settings->height; - tileElement->clearance_height = settings->height; + auto surfaceElement = map_get_surface_element_at(x, y); + surfaceElement->SetSurfaceStyle(floorTexture); + surfaceElement->SetEdgeStyle(wallTexture); + surfaceElement->base_height = settings->height; + surfaceElement->clearance_height = settings->height; } } @@ -217,10 +212,10 @@ void mapgen_generate(mapgen_settings* settings) { for (x = 1; x < mapSize - 1; x++) { - tileElement = map_get_surface_element_at(x, y); + auto surfaceElement = map_get_surface_element_at(x, y); - if (tileElement->base_height < waterLevel + 6) - tileElement->AsSurface()->SetSurfaceStyle(beachTexture); + if (surfaceElement->base_height < waterLevel + 6) + surfaceElement->SetSurfaceStyle(beachTexture); } } @@ -241,8 +236,8 @@ static void mapgen_place_tree(int32_t type, int32_t x, int32_t y) return; } - surfaceZ = tile_element_height(x * 32 + 16, y * 32 + 16) / 8; - tileElement = tile_element_insert(x, y, surfaceZ, (1 | 2 | 4 | 8)); + surfaceZ = tile_element_height({ x * 32 + 16, y * 32 + 16 }) / 8; + tileElement = tile_element_insert({ x, y, surfaceZ }, 0b1111); assert(tileElement != nullptr); tileElement->clearance_height = surfaceZ + (sceneryEntry->small_scenery.height >> 3); tileElement->SetType(TILE_ELEMENT_TYPE_SMALL_SCENERY); @@ -314,10 +309,10 @@ static void mapgen_place_trees() { for (int32_t x = 1; x < gMapSize - 1; x++) { - TileElement* tileElement = map_get_surface_element_at(x, y); + auto* surfaceElement = map_get_surface_element_at(x, y); // Exclude water tiles - if (tileElement->AsSurface()->GetWaterHeight() > 0) + if (surfaceElement->GetWaterHeight() > 0) continue; pos.x = x; @@ -348,13 +343,13 @@ static void mapgen_place_trees() pos = availablePositions[i]; int32_t type = -1; - TileElement* tileElement = map_get_surface_element_at(pos.x, pos.y); - switch (tileElement->AsSurface()->GetSurfaceStyle()) + auto* surfaceElement = map_get_surface_element_at(pos.x, pos.y); + switch (surfaceElement->GetSurfaceStyle()) { case TERRAIN_GRASS: case TERRAIN_DIRT: case TERRAIN_GRASS_CLUMPS: - if (grassTreeIds.size() == 0) + if (grassTreeIds.empty()) break; type = grassTreeIds[util_rand() % grassTreeIds.size()]; @@ -363,7 +358,7 @@ static void mapgen_place_trees() case TERRAIN_SAND: case TERRAIN_SAND_DARK: case TERRAIN_SAND_LIGHT: - if (desertTreeIds.size() == 0) + if (desertTreeIds.empty()) break; if (util_rand() % 4 == 0) @@ -371,7 +366,7 @@ static void mapgen_place_trees() break; case TERRAIN_ICE: - if (snowTreeIds.size() == 0) + if (snowTreeIds.empty()) break; type = snowTreeIds[util_rand() % snowTreeIds.size()]; @@ -389,7 +384,6 @@ static void mapgen_place_trees() static void mapgen_set_water_level(int32_t waterLevel) { int32_t x, y, mapSize; - TileElement* tileElement; mapSize = gMapSize; @@ -397,9 +391,9 @@ static void mapgen_set_water_level(int32_t waterLevel) { for (x = 1; x < mapSize - 1; x++) { - tileElement = map_get_surface_element_at(x, y); - if (tileElement->base_height < waterLevel) - tileElement->AsSurface()->SetWaterHeight(waterLevel / 2); + auto surfaceElement = map_get_surface_element_at(x, y); + if (surfaceElement->base_height < waterLevel) + surfaceElement->SetWaterHeight(waterLevel / 2); } } } @@ -443,7 +437,6 @@ static void mapgen_smooth_height(int32_t iterations) static void mapgen_set_height() { int32_t x, y, heightX, heightY, mapSize; - TileElement* tileElement; mapSize = _heightSize / 2; for (y = 1; y < mapSize - 1; y++) @@ -460,11 +453,11 @@ static void mapgen_set_height() uint8_t baseHeight = (q00 + q01 + q10 + q11) / 4; - tileElement = map_get_surface_element_at(x, y); - tileElement->base_height = std::max(2, baseHeight * 2); - tileElement->clearance_height = tileElement->base_height; + auto surfaceElement = map_get_surface_element_at(x, y); + surfaceElement->base_height = std::max(2, baseHeight * 2); + surfaceElement->clearance_height = surfaceElement->base_height; - uint8_t currentSlope = tileElement->AsSurface()->GetSlope(); + uint8_t currentSlope = surfaceElement->GetSlope(); if (q00 > baseHeight) currentSlope |= TILE_ELEMENT_SLOPE_S_CORNER_UP; @@ -475,7 +468,7 @@ static void mapgen_set_height() if (q11 > baseHeight) currentSlope |= TILE_ELEMENT_SLOPE_N_CORNER_UP; - tileElement->AsSurface()->SetSlope(currentSlope); + surfaceElement->SetSlope(currentSlope); } } } @@ -819,7 +812,7 @@ void mapgen_generate_from_heightmap(mapgen_settings* settings) for (uint32_t x = 0; x < _heightMapData.width; x++) { // The x and y axis are flipped in the world, so this uses y for x and x for y. - TileElement* const surfaceElement = map_get_surface_element_at(y + 1, x + 1); + auto* const surfaceElement = map_get_surface_element_at(y + 1, x + 1); // Read value from bitmap, and convert its range uint8_t value = dest[x + y * _heightMapData.width]; @@ -834,7 +827,7 @@ void mapgen_generate_from_heightmap(mapgen_settings* settings) // Set water level if (surfaceElement->base_height < settings->water_level) { - surfaceElement->AsSurface()->SetWaterHeight(settings->water_level / 2); + surfaceElement->SetWaterHeight(settings->water_level / 2); } } } diff --git a/src/openrct2/world/MapGen.h b/src/openrct2/world/MapGen.h index b2dcf79244..88c136cadb 100644 --- a/src/openrct2/world/MapGen.h +++ b/src/openrct2/world/MapGen.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/world/MapHelpers.cpp b/src/openrct2/world/MapHelpers.cpp index dd49bb6fe9..1babc02910 100644 --- a/src/openrct2/world/MapHelpers.cpp +++ b/src/openrct2/world/MapHelpers.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -21,24 +21,23 @@ int32_t map_smooth(int32_t l, int32_t t, int32_t r, int32_t b) { int32_t i, x, y, count, doubleCorner, raisedLand = 0; uint8_t highest, cornerHeights[4]; - TileElement *tileElement, *tileElement2; for (y = t; y < b; y++) { for (x = l; x < r; x++) { - tileElement = map_get_surface_element_at(x, y); - tileElement->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT); + auto surfaceElement = map_get_surface_element_at(x, y); + surfaceElement->SetSlope(TILE_ELEMENT_SLOPE_FLAT); // Raise to edge height - 2 - highest = tileElement->base_height; + highest = surfaceElement->base_height; highest = std::max(highest, map_get_surface_element_at(x - 1, y + 0)->base_height); highest = std::max(highest, map_get_surface_element_at(x + 1, y + 0)->base_height); highest = std::max(highest, map_get_surface_element_at(x + 0, y - 1)->base_height); highest = std::max(highest, map_get_surface_element_at(x + 0, y + 1)->base_height); - if (tileElement->base_height < highest - 2) + if (surfaceElement->base_height < highest - 2) { raisedLand = 1; - tileElement->base_height = tileElement->clearance_height = highest - 2; + surfaceElement->base_height = surfaceElement->clearance_height = highest - 2; } // Check corners @@ -47,11 +46,11 @@ int32_t map_smooth(int32_t l, int32_t t, int32_t r, int32_t b) cornerHeights[1] = map_get_surface_element_at(x + 1, y - 1)->base_height; cornerHeights[2] = map_get_surface_element_at(x + 1, y + 1)->base_height; cornerHeights[3] = map_get_surface_element_at(x - 1, y + 1)->base_height; - highest = tileElement->base_height; + highest = surfaceElement->base_height; for (i = 0; i < 4; i++) highest = std::max(highest, cornerHeights[i]); - if (highest >= tileElement->base_height + 4) + if (highest >= surfaceElement->base_height + 4) { count = 0; int32_t canCompensate = 1; @@ -89,9 +88,9 @@ int32_t map_smooth(int32_t l, int32_t t, int32_t r, int32_t b) break; } - if (highestOnLowestSide > tileElement->base_height) + if (highestOnLowestSide > surfaceElement->base_height) { - tileElement->base_height = tileElement->clearance_height = highestOnLowestSide; + surfaceElement->base_height = surfaceElement->clearance_height = highestOnLowestSide; raisedLand = 1; canCompensate = 0; } @@ -99,9 +98,9 @@ int32_t map_smooth(int32_t l, int32_t t, int32_t r, int32_t b) if (count == 1 && canCompensate) { - if (tileElement->base_height < highest - 4) + if (surfaceElement->base_height < highest - 4) { - tileElement->base_height = tileElement->clearance_height = highest - 4; + surfaceElement->base_height = surfaceElement->clearance_height = highest - 4; raisedLand = 1; } if (cornerHeights[0] == highest && cornerHeights[2] <= cornerHeights[0] - 4) @@ -115,9 +114,9 @@ int32_t map_smooth(int32_t l, int32_t t, int32_t r, int32_t b) } else { - if (tileElement->base_height < highest - 2) + if (surfaceElement->base_height < highest - 2) { - tileElement->base_height = tileElement->clearance_height = highest - 2; + surfaceElement->base_height = surfaceElement->clearance_height = highest - 2; raisedLand = 1; } } @@ -125,7 +124,7 @@ int32_t map_smooth(int32_t l, int32_t t, int32_t r, int32_t b) if (doubleCorner != -1) { - uint8_t slope = tileElement->AsSurface()->GetSlope() | TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT; + uint8_t slope = surfaceElement->GetSlope() | TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT; switch (doubleCorner) { case 0: @@ -141,52 +140,52 @@ int32_t map_smooth(int32_t l, int32_t t, int32_t r, int32_t b) slope |= TILE_ELEMENT_SLOPE_E_CORNER_DN; break; } - tileElement->AsSurface()->SetSlope(slope); + surfaceElement->SetSlope(slope); } else { - uint8_t slope = tileElement->AsSurface()->GetSlope(); + uint8_t slope = surfaceElement->GetSlope(); // Corners - tileElement2 = map_get_surface_element_at(x + 1, y + 1); - if (tileElement2->base_height > tileElement->base_height) + auto surfaceElement2 = map_get_surface_element_at(x + 1, y + 1); + if (surfaceElement2->base_height > surfaceElement->base_height) slope |= TILE_ELEMENT_SLOPE_N_CORNER_UP; - tileElement2 = map_get_surface_element_at(x - 1, y + 1); - if (tileElement2->base_height > tileElement->base_height) + surfaceElement2 = map_get_surface_element_at(x - 1, y + 1); + if (surfaceElement2->base_height > surfaceElement->base_height) slope |= TILE_ELEMENT_SLOPE_W_CORNER_UP; - tileElement2 = map_get_surface_element_at(x + 1, y - 1); - if (tileElement2->base_height > tileElement->base_height) + surfaceElement2 = map_get_surface_element_at(x + 1, y - 1); + if (surfaceElement2->base_height > surfaceElement->base_height) slope |= TILE_ELEMENT_SLOPE_E_CORNER_UP; - tileElement2 = map_get_surface_element_at(x - 1, y - 1); - if (tileElement2->base_height > tileElement->base_height) + surfaceElement2 = map_get_surface_element_at(x - 1, y - 1); + if (surfaceElement2->base_height > surfaceElement->base_height) slope |= TILE_ELEMENT_SLOPE_S_CORNER_UP; // Sides - tileElement2 = map_get_surface_element_at(x + 1, y + 0); - if (tileElement2->base_height > tileElement->base_height) + surfaceElement2 = map_get_surface_element_at(x + 1, y + 0); + if (surfaceElement2->base_height > surfaceElement->base_height) slope |= TILE_ELEMENT_SLOPE_NE_SIDE_UP; - tileElement2 = map_get_surface_element_at(x - 1, y + 0); - if (tileElement2->base_height > tileElement->base_height) + surfaceElement2 = map_get_surface_element_at(x - 1, y + 0); + if (surfaceElement2->base_height > surfaceElement->base_height) slope |= TILE_ELEMENT_SLOPE_SW_SIDE_UP; - tileElement2 = map_get_surface_element_at(x + 0, y - 1); - if (tileElement2->base_height > tileElement->base_height) + surfaceElement2 = map_get_surface_element_at(x + 0, y - 1); + if (surfaceElement2->base_height > surfaceElement->base_height) slope |= TILE_ELEMENT_SLOPE_SE_SIDE_UP; - tileElement2 = map_get_surface_element_at(x + 0, y + 1); - if (tileElement2->base_height > tileElement->base_height) + surfaceElement2 = map_get_surface_element_at(x + 0, y + 1); + if (surfaceElement2->base_height > surfaceElement->base_height) slope |= TILE_ELEMENT_SLOPE_NW_SIDE_UP; // Raise if (slope == TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) { slope = TILE_ELEMENT_SLOPE_FLAT; - tileElement->base_height = tileElement->clearance_height += 2; + surfaceElement->base_height = surfaceElement->clearance_height += 2; } - tileElement->AsSurface()->SetSlope(slope); + surfaceElement->SetSlope(slope); } } } @@ -201,7 +200,7 @@ int32_t map_smooth(int32_t l, int32_t t, int32_t r, int32_t b) */ int32_t tile_smooth(int32_t x, int32_t y) { - TileElement* const surfaceElement = map_get_surface_element_at(x, y); + auto* const surfaceElement = map_get_surface_element_at(x, y); // +-----+-----+-----+ // | W | NW | N | @@ -240,9 +239,9 @@ int32_t tile_smooth(int32_t x, int32_t y) continue; // Get neighbour height. If the element is not valid (outside of map) assume the same height - TileElement* neighbour_element = map_get_surface_element_at(x + x_offset, y + y_offset); - neighbourHeightOffset.baseheight[index] = neighbour_element ? neighbour_element->base_height - : surfaceElement->base_height; + auto* neighbourSurfaceElement = map_get_surface_element_at(x + x_offset, y + y_offset); + neighbourHeightOffset.baseheight[index] = neighbourSurfaceElement ? neighbourSurfaceElement->base_height + : surfaceElement->base_height; // Make the height relative to the current surface element neighbourHeightOffset.baseheight[index] -= surfaceElement->base_height; @@ -277,7 +276,7 @@ int32_t tile_smooth(int32_t x, int32_t y) } // Check if the calculated slope is the same already - uint8_t currentSlope = surfaceElement->AsSurface()->GetSlope(); + uint8_t currentSlope = surfaceElement->GetSlope(); if (currentSlope == slope) { return 0; @@ -286,18 +285,18 @@ int32_t tile_smooth(int32_t x, int32_t y) if ((slope & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) == TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) { // All corners are raised, raise the entire tile instead. - surfaceElement->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT); + surfaceElement->SetSlope(TILE_ELEMENT_SLOPE_FLAT); surfaceElement->base_height = (surfaceElement->clearance_height += 2); - uint8_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight() * 2; + uint8_t waterHeight = surfaceElement->GetWaterHeight() * 2; if (waterHeight <= surfaceElement->base_height) { - surfaceElement->AsSurface()->SetWaterHeight(0); + surfaceElement->SetWaterHeight(0); } } else { // Apply the slope to this tile - surfaceElement->AsSurface()->SetSlope(slope); + surfaceElement->SetSlope(slope); // Set correct clearance height if (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) diff --git a/src/openrct2/world/MapHelpers.h b/src/openrct2/world/MapHelpers.h index 440fec50b3..641d73c3d2 100644 --- a/src/openrct2/world/MapHelpers.h +++ b/src/openrct2/world/MapHelpers.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/world/MoneyEffect.cpp b/src/openrct2/world/MoneyEffect.cpp index 2af28d036c..2e686a66ed 100644 --- a/src/openrct2/world/MoneyEffect.cpp +++ b/src/openrct2/world/MoneyEffect.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,21 +12,38 @@ #include "../interface/Viewport.h" #include "../interface/Window.h" #include "../localisation/Localisation.h" +#include "../network/network.h" #include "Map.h" #include "Sprite.h" static constexpr const LocationXY16 _moneyEffectMoveOffset[] = { { 1, -1 }, { 1, 1 }, { -1, 1 }, { -1, -1 } }; +bool rct_sprite::IsMoneyEffect() +{ + return this->money_effect.sprite_identifier == SPRITE_IDENTIFIER_MISC + && this->money_effect.type == SPRITE_MISC_MONEY_EFFECT; +} + +rct_money_effect* rct_sprite::AsMoneyEffect() +{ + rct_money_effect* result = nullptr; + if (IsMoneyEffect()) + { + result = (rct_money_effect*)this; + } + return result; +} + /** * * rct2: 0x0067351F */ -void money_effect_create_at(money32 value, int32_t x, int32_t y, int32_t z, bool vertical) +void rct_money_effect::CreateAt(money32 value, int32_t x, int32_t y, int32_t z, bool vertical) { if (value == MONEY(0, 00)) return; - rct_money_effect* moneyEffect = (rct_money_effect*)create_sprite(2); + rct_money_effect* moneyEffect = &create_sprite(SPRITE_IDENTIFIER_MISC)->money_effect; if (moneyEffect == nullptr) return; @@ -44,10 +61,9 @@ void money_effect_create_at(money32 value, int32_t x, int32_t y, int32_t z, bool int16_t offsetX = 0; if (!gOpenRCT2NoGraphics) { - // Construct string to display - rct_string_id stringId = money_effect_get_string_id(moneyEffect, &value); + auto [stringId, newValue] = moneyEffect->GetStringId(); char buffer[128]; - format_string(buffer, 128, stringId, &value); + format_string(buffer, 128, stringId, &newValue); gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM; offsetX = -(gfx_get_string_width(buffer) / 2); } @@ -59,12 +75,20 @@ void money_effect_create_at(money32 value, int32_t x, int32_t y, int32_t z, bool * * rct2: 0x0069C5D0 */ -void money_effect_create(money32 value) +void rct_money_effect::Create(money32 value) { LocationXYZ16 mapPosition = { gCommandPosition.x, gCommandPosition.y, gCommandPosition.z }; if (mapPosition.x == LOCATION_NULL) { + // If game actions return no valid location of the action we can not use the screen + // coordinates as every client will have different ones. + if (network_get_mode() != NETWORK_MODE_NONE) + { + log_warning("Attempted to create money effect without a valid location in multiplayer"); + return; + } + rct_window* mainWindow = window_get_main(); if (mainWindow == nullptr) return; @@ -76,69 +100,65 @@ void money_effect_create(money32 value) if (mapPosition.x == LOCATION_NULL) return; - mapPosition.z = tile_element_height(mapPosition.x, mapPosition.y) & 0xFFFF; + mapPosition.z = tile_element_height({ mapPosition.x, mapPosition.y }); } mapPosition.z += 10; - money_effect_create_at(-value, mapPosition.x, mapPosition.y, mapPosition.z, false); + CreateAt(-value, mapPosition.x, mapPosition.y, mapPosition.z, false); } /** * * rct2: 0x00673232 */ -void money_effect_update(rct_money_effect* moneyEffect) +void rct_money_effect::Update() { - invalidate_sprite_2((rct_sprite*)moneyEffect); - moneyEffect->wiggle++; - if (moneyEffect->wiggle >= 22) + invalidate_sprite_2((rct_sprite*)this); + wiggle++; + if (wiggle >= 22) { - moneyEffect->wiggle = 0; + wiggle = 0; } - moneyEffect->move_delay++; - if (moneyEffect->move_delay < 2) + move_delay++; + if (move_delay < 2) { return; } - int32_t x = moneyEffect->x; - int32_t y = moneyEffect->y; - int32_t z = moneyEffect->z; - moneyEffect->move_delay = 0; + int32_t newX = x; + int32_t newY = y; + int32_t newZ = z; + move_delay = 0; - if (moneyEffect->vertical) + if (vertical) { - z += 1; + newZ += 1; } - y += _moneyEffectMoveOffset[get_current_rotation()].y; - x += _moneyEffectMoveOffset[get_current_rotation()].x; + newY += _moneyEffectMoveOffset[get_current_rotation()].y; + newX += _moneyEffectMoveOffset[get_current_rotation()].x; - sprite_move(x, y, z, (rct_sprite*)moneyEffect); + sprite_move(newX, newY, newZ, (rct_sprite*)this); - moneyEffect->num_movements++; - if (moneyEffect->num_movements < 55) + num_movements++; + if (num_movements < 55) { return; } - sprite_remove((rct_sprite*)moneyEffect); + sprite_remove((rct_sprite*)this); } -rct_string_id money_effect_get_string_id(const rct_money_effect* sprite, money32* outValue) +std::pair rct_money_effect::GetStringId() const { - bool vertical = (sprite->vertical != 0); rct_string_id spentStringId = vertical ? STR_MONEY_EFFECT_SPEND_HIGHP : STR_MONEY_EFFECT_SPEND; rct_string_id receiveStringId = vertical ? STR_MONEY_EFFECT_RECEIVE_HIGHP : STR_MONEY_EFFECT_RECEIVE; rct_string_id stringId = receiveStringId; - money32 value = sprite->value; + money32 outValue = value; if (value < 0) { - value *= -1; + outValue *= -1; stringId = spentStringId; } - if (outValue != nullptr) - { - *outValue = value; - } - return stringId; + + return std::make_pair(stringId, outValue); } diff --git a/src/openrct2/world/Park.cpp b/src/openrct2/world/Park.cpp index ea5c163742..598f26a169 100644 --- a/src/openrct2/world/Park.cpp +++ b/src/openrct2/world/Park.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -15,6 +15,7 @@ #include "../Game.h" #include "../GameState.h" #include "../OpenRCT2.h" +#include "../actions/ParkSetParameterAction.hpp" #include "../config/Config.h" #include "../core/Memory.hpp" #include "../interface/Colour.h" @@ -43,8 +44,6 @@ using namespace OpenRCT2; -rct_string_id gParkName; -uint32_t gParkNameArgs; uint32_t gParkFlags; uint16_t gParkRating; money16 gParkEntranceFee; @@ -78,23 +77,12 @@ int32_t _suggestedGuestMaximum; */ int32_t _guestGenerationProbability; -/** - * - * rct2: 0x00667104 - */ -void reset_park_entry() -{ - gParkName = 0; - reset_park_entrance(); - gPeepSpawns.clear(); -} - /** * Choose a random peep spawn and iterates through until defined spawn is found. */ static PeepSpawn* get_random_peep_spawn() { - if (gPeepSpawns.size() > 0) + if (!gPeepSpawns.empty()) { return &gPeepSpawns[scenario_rand() % gPeepSpawns.size()]; } @@ -104,55 +92,10 @@ static PeepSpawn* get_random_peep_spawn() } } -void park_set_open(int32_t open) +void park_set_open(bool open) { - game_do_command(0, GAME_COMMAND_FLAG_APPLY, 0, open << 8, GAME_COMMAND_SET_PARK_OPEN, 0, 0); -} - -/** - * - * rct2: 0x00669D4A - */ -void game_command_set_park_open( - [[maybe_unused]] int32_t* eax, int32_t* ebx, [[maybe_unused]] int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, - int32_t* edi, [[maybe_unused]] int32_t* ebp) -{ - if (!(*ebx & GAME_COMMAND_FLAG_APPLY)) - { - *ebx = 0; - return; - } - - int32_t dh = (*edx >> 8) & 0xFF; - - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_PARK_ENTRANCE_TICKETS; - switch (dh) - { - case 0: - if (gParkFlags & PARK_FLAGS_PARK_OPEN) - { - gParkFlags &= ~PARK_FLAGS_PARK_OPEN; - window_invalidate_by_class(WC_PARK_INFORMATION); - } - break; - case 1: - if (!(gParkFlags & PARK_FLAGS_PARK_OPEN)) - { - gParkFlags |= PARK_FLAGS_PARK_OPEN; - window_invalidate_by_class(WC_PARK_INFORMATION); - } - break; - case 2: - gSamePriceThroughoutParkA = *edi; - window_invalidate_by_class(WC_RIDE); - break; - case 3: - gSamePriceThroughoutParkB = *edi; - window_invalidate_by_class(WC_RIDE); - break; - } - - *ebx = 0; + auto parkSetParameter = ParkSetParameterAction(open ? ParkParameter::Open : ParkParameter::Close); + GameActions::Execute(&parkSetParameter); } /** @@ -164,16 +107,18 @@ void update_park_fences(const CoordsXY coords) if (map_is_edge(coords)) return; - TileElement* surfaceElement = map_get_surface_element_at(coords); + auto surfaceElement = map_get_surface_element_at(coords); if (surfaceElement == nullptr) return; uint8_t newFences = 0; - if ((surfaceElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED) == 0) + if ((surfaceElement->GetOwnership() & OWNERSHIP_OWNED) == 0) { bool fenceRequired = true; TileElement* tileElement = map_get_first_element_at(coords.x / 32, coords.y / 32); + if (tileElement == nullptr) + return; // If an entrance element do not place flags around surface do { @@ -219,12 +164,12 @@ void update_park_fences(const CoordsXY coords) } } - if (surfaceElement->AsSurface()->GetParkFences() != newFences) + if (surfaceElement->GetParkFences() != newFences) { int32_t z0 = surfaceElement->base_height * 8; int32_t z1 = z0 + 16; map_invalidate_tile(coords.x, coords.y, z0, z1); - surfaceElement->AsSurface()->SetParkFences(newFences); + surfaceElement->SetParkFences(newFences); } } @@ -237,223 +182,6 @@ void update_park_fences_around_tile(const CoordsXY coords) update_park_fences({ coords.x, coords.y - 32 }); } -void park_set_name(const char* name) -{ - auto nameId = user_string_allocate(USER_STRING_HIGH_ID_NUMBER, name); - if (nameId != 0) - { - user_string_free(gParkName); - gParkName = nameId; - } -} - -static money32 map_buy_land_rights_for_tile(int32_t x, int32_t y, int32_t setting, int32_t flags) -{ - SurfaceElement* surfaceElement = map_get_surface_element_at({ x, y })->AsSurface(); - if (surfaceElement == nullptr) - return MONEY32_UNDEFINED; - - switch (setting) - { - case BUY_LAND_RIGHTS_FLAG_BUY_LAND: // 0 - if ((surfaceElement->GetOwnership() & OWNERSHIP_OWNED) != 0) - { // If the land is already owned - return 0; - } - - if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0 - || (surfaceElement->GetOwnership() & OWNERSHIP_AVAILABLE) == 0) - { - gGameCommandErrorText = STR_LAND_NOT_FOR_SALE; - return MONEY32_UNDEFINED; - } - if (flags & GAME_COMMAND_FLAG_APPLY) - { - surfaceElement->SetOwnership(OWNERSHIP_OWNED); - update_park_fences_around_tile({ x, y }); - } - return gLandPrice; - case BUY_LAND_RIGHTS_FLAG_UNOWN_TILE: // 1 - if (flags & GAME_COMMAND_FLAG_APPLY) - { - surfaceElement->SetOwnership( - surfaceElement->GetOwnership() & ~(OWNERSHIP_OWNED | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED)); - update_park_fences_around_tile({ x, y }); - } - return 0; - case BUY_LAND_RIGHTS_FLAG_BUY_CONSTRUCTION_RIGHTS: // 2 - if ((surfaceElement->GetOwnership() & (OWNERSHIP_OWNED | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED)) != 0) - { // If the land or construction rights are already owned - return 0; - } - - if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0 - || (surfaceElement->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) == 0) - { - gGameCommandErrorText = STR_CONSTRUCTION_RIGHTS_NOT_FOR_SALE; - return MONEY32_UNDEFINED; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED); - uint16_t baseHeight = surfaceElement->base_height * 8; - map_invalidate_tile(x, y, baseHeight, baseHeight + 16); - } - return gConstructionRightsPrice; - case BUY_LAND_RIGHTS_FLAG_UNOWN_CONSTRUCTION_RIGHTS: // 3 - if (flags & GAME_COMMAND_FLAG_APPLY) - { - surfaceElement->SetOwnership(surfaceElement->GetOwnership() & ~OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED); - uint16_t baseHeight = surfaceElement->base_height * 8; - map_invalidate_tile(x, y, baseHeight, baseHeight + 16); - } - return 0; - case BUY_LAND_RIGHTS_FLAG_SET_FOR_SALE: // 4 - if (flags & GAME_COMMAND_FLAG_APPLY) - { - surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_AVAILABLE); - uint16_t baseHeight = surfaceElement->base_height * 8; - map_invalidate_tile(x, y, baseHeight, baseHeight + 16); - } - return 0; - case BUY_LAND_RIGHTS_FLAG_SET_CONSTRUCTION_RIGHTS_FOR_SALE: // 5 - if (flags & GAME_COMMAND_FLAG_APPLY) - { - surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE); - uint16_t baseHeight = surfaceElement->base_height * 8; - map_invalidate_tile(x, y, baseHeight, baseHeight + 16); - } - return 0; - case BUY_LAND_RIGHTS_FLAG_SET_OWNERSHIP_WITH_CHECKS: - { - if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode) - { - return MONEY32_UNDEFINED; - } - - if (x <= 0 || y <= 0) - { - gGameCommandErrorText = STR_TOO_CLOSE_TO_EDGE_OF_MAP; - return MONEY32_UNDEFINED; - } - - if (x >= gMapSizeUnits || y >= gMapSizeUnits) - { - gGameCommandErrorText = STR_TOO_CLOSE_TO_EDGE_OF_MAP; - return MONEY32_UNDEFINED; - } - - uint8_t newOwnership = (flags & 0xFF00) >> 4; - if (newOwnership == surfaceElement->GetOwnership()) - { - return 0; - } - - TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); - do - { - if (tileElement->GetType() == TILE_ELEMENT_TYPE_ENTRANCE - && tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) - { - // Do not allow ownership of park entrance. - if (newOwnership == OWNERSHIP_OWNED || newOwnership == OWNERSHIP_AVAILABLE) - return 0; - // Allow construction rights available / for sale on park entrances on surface. - // There is no need to check the height if newOwnership is 0 (unowned and no rights available). - if ((newOwnership == OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED - || newOwnership == OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) - && (tileElement->base_height - 3 > surfaceElement->base_height - || tileElement->base_height < surfaceElement->base_height)) - return 0; - } - } while (!(tileElement++)->IsLastForTile()); - - if (!(flags & GAME_COMMAND_FLAG_APPLY)) - { - return gLandPrice; - } - - if ((newOwnership & 0xF0) != 0) - { - gPeepSpawns.erase( - std::remove_if( - gPeepSpawns.begin(), gPeepSpawns.end(), - [x, y](const auto& spawn) { return floor2(spawn.x, 32) == x && floor2(spawn.y, 32) == y; }), - gPeepSpawns.end()); - } - surfaceElement->SetOwnership(newOwnership); - update_park_fences_around_tile({ x, y }); - gMapLandRightsUpdateSuccess = true; - return 0; - } - default: - log_warning("Tried calling map_buy_land_rights_for_tile() with an incorrect setting!"); - assert(false); - return MONEY32_UNDEFINED; - } -} - -int32_t map_buy_land_rights(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t setting, int32_t flags) -{ - int32_t x, y, z; - money32 totalCost, cost; - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; - - if (x1 == 0 && y1 == 0) - { - x1 = x0; - y1 = y0; - } - - x = (x0 + x1) / 2 + 16; - y = (y0 + y1) / 2 + 16; - z = tile_element_height(x, y); - gCommandPosition.x = x; - gCommandPosition.y = y; - gCommandPosition.z = z; - - // Game command modified to accept selection size - totalCost = 0; - gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0 || game_is_not_paused() || gCheatsBuildInPauseMode) - { - for (y = y0; y <= y1; y += 32) - { - for (x = x0; x <= x1; x += 32) - { - cost = map_buy_land_rights_for_tile(x, y, setting, flags); - if (cost != MONEY32_UNDEFINED) - { - totalCost += cost; - } - } - } - } - - return totalCost; -} - -/** - * - * rct2: 0x006649BD - */ -void game_command_buy_land_rights( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - int32_t flags = *ebx & 0xFFFF; - - *ebx = map_buy_land_rights((*eax & 0xFFFF), (*ecx & 0xFFFF), (*edi & 0xFFFF), (*ebp & 0xFFFF), (*edx & 0x00FF), flags); - - // Too expensive to always call in map_buy_land_rights. - // It's already counted when the park is loaded, after - // that it should only be called for user actions. - if (flags & GAME_COMMAND_FLAG_APPLY) - { - map_count_remaining_land_rights(); - } -} - void set_forced_park_rating(int32_t rating) { _forcedParkRating = rating; @@ -529,8 +257,7 @@ money32 Park::GetCompanyValue() const void Park::Initialise() { - gUnk13CA740 = 0; - gParkName = STR_UNNAMED_PARK; + Name = format_string(STR_UNNAMED_PARK, nullptr); gStaffHandymanColour = COLOUR_BRIGHT_RED; gStaffMechanicColour = COLOUR_LIGHT_BLUE; gStaffSecurityColour = COLOUR_YELLOW; @@ -553,10 +280,8 @@ void Park::Initialise() gParkEntranceFee = MONEY(10, 00); - for (auto& peepSpawn : gPeepSpawns) - { - peepSpawn.x = PEEP_SPAWN_UNDEFINED; - } + gPeepSpawns.clear(); + reset_park_entrance(); gResearchPriorities = (1 << RESEARCH_CATEGORY_TRANSPORT) | (1 << RESEARCH_CATEGORY_GENTLE) | (1 << RESEARCH_CATEGORY_ROLLERCOASTER) | (1 << RESEARCH_CATEGORY_THRILL) | (1 << RESEARCH_CATEGORY_WATER) @@ -697,16 +422,13 @@ int32_t Park::CalculateParkRating() const int32_t totalRideUptime = 0; int32_t totalRideIntensity = 0; int32_t totalRideExcitement = 0; - - int32_t i; - Ride* ride; - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - totalRideUptime += 100 - ride->downtime; - if (ride->excitement != RIDE_RATING_UNDEFINED) + totalRideUptime += 100 - ride.downtime; + if (ride_has_ratings(&ride)) { - totalRideExcitement += ride->excitement / 8; - totalRideIntensity += ride->intensity / 8; + totalRideExcitement += ride.excitement / 8; + totalRideIntensity += ride.intensity / 8; excitingRideCount++; } rideCount++; @@ -769,13 +491,11 @@ int32_t Park::CalculateParkRating() const money32 Park::CalculateParkValue() const { - money32 result = 0; - // Sum ride values - for (int32_t i = 0; i < MAX_RIDES; i++) + money32 result = 0; + for (const auto& ride : GetRideManager()) { - auto ride = get_ride(i); - result += CalculateRideValue(ride); + result += CalculateRideValue(&ride); } // +7.00 per guest @@ -787,7 +507,7 @@ money32 Park::CalculateParkValue() const money32 Park::CalculateRideValue(const Ride* ride) const { money32 result = 0; - if (ride->type != RIDE_TYPE_NULL && ride->value != RIDE_VALUE_UNDEFINED) + if (ride != nullptr && ride->value != RIDE_VALUE_UNDEFINED) { result = (ride->value * 10) * (ride_customers_in_last_5_minutes(ride) + rideBonusValue[ride->type] * 4); } @@ -802,21 +522,19 @@ money32 Park::CalculateCompanyValue() const money16 Park::CalculateTotalRideValueForMoney() const { money16 totalRideValue = 0; - int32_t i; - Ride* ride; - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (ride->status != RIDE_STATUS_OPEN) + if (ride.status != RIDE_STATUS_OPEN) continue; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + if (ride.lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) continue; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) + if (ride.lifecycle_flags & RIDE_LIFECYCLE_CRASHED) continue; // Add ride value - if (ride->value != RIDE_VALUE_UNDEFINED) + if (ride.value != RIDE_VALUE_UNDEFINED) { - money16 rideValue = (money16)(ride->value - ride->price); + money16 rideValue = (money16)(ride.value - ride.price); if (rideValue > 0) { totalRideValue += rideValue * 2; @@ -831,44 +549,42 @@ uint32_t Park::CalculateSuggestedMaxGuests() const uint32_t suggestedMaxGuests = 0; // TODO combine the two ride loops - int32_t i; - Ride* ride; - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (ride->status != RIDE_STATUS_OPEN) + if (ride.status != RIDE_STATUS_OPEN) continue; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + if (ride.lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) continue; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) + if (ride.lifecycle_flags & RIDE_LIFECYCLE_CRASHED) continue; // Add guest score for ride type - suggestedMaxGuests += rideBonusValue[ride->type]; + suggestedMaxGuests += rideBonusValue[ride.type]; } // If difficult guest generation, extra guests are available for good rides if (gParkFlags & PARK_FLAGS_DIFFICULT_GUEST_GENERATION) { suggestedMaxGuests = std::min(suggestedMaxGuests, 1000); - FOR_ALL_RIDES (i, ride) + for (auto& ride : GetRideManager()) { - if (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) + if (ride.lifecycle_flags & RIDE_LIFECYCLE_CRASHED) continue; - if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) + if (ride.lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) continue; - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) + if (!(ride.lifecycle_flags & RIDE_LIFECYCLE_TESTED)) continue; - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_TRACK)) + if (!ride_type_has_flag(ride.type, RIDE_TYPE_FLAG_HAS_TRACK)) continue; - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_DATA_LOGGING)) + if (!ride_type_has_flag(ride.type, RIDE_TYPE_FLAG_HAS_DATA_LOGGING)) continue; - if (ride->stations[0].SegmentLength < (600 << 16)) + if (ride.stations[0].SegmentLength < (600 << 16)) continue; - if (ride->excitement < RIDE_RATING(6, 00)) + if (ride.excitement < RIDE_RATING(6, 00)) continue; // Bonus guests for good ride - suggestedMaxGuests += rideBonusValue[ride->type] * 2; + suggestedMaxGuests += rideBonusValue[ride.type] * 2; } } @@ -993,7 +709,7 @@ Peep* Park::GenerateGuest() if (spawn != nullptr) { auto direction = direction_reverse(spawn->direction); - peep = peep_generate(spawn->x, spawn->y, spawn->z); + peep = Peep::Generate({ spawn->x, spawn->y, spawn->z }); if (peep != nullptr) { peep->sprite_direction = direction << 3; diff --git a/src/openrct2/world/Park.h b/src/openrct2/world/Park.h index cd54738bc9..72d1ca9c8a 100644 --- a/src/openrct2/world/Park.h +++ b/src/openrct2/world/Park.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -53,6 +53,8 @@ namespace OpenRCT2 class Park final { public: + std::string Name; + Park() = default; Park(const Park&) = delete; @@ -87,19 +89,6 @@ namespace OpenRCT2 }; } // namespace OpenRCT2 -enum -{ - BUY_LAND_RIGHTS_FLAG_BUY_LAND, - BUY_LAND_RIGHTS_FLAG_UNOWN_TILE, - BUY_LAND_RIGHTS_FLAG_BUY_CONSTRUCTION_RIGHTS, - BUY_LAND_RIGHTS_FLAG_UNOWN_CONSTRUCTION_RIGHTS, - BUY_LAND_RIGHTS_FLAG_SET_FOR_SALE, - BUY_LAND_RIGHTS_FLAG_SET_CONSTRUCTION_RIGHTS_FOR_SALE, - BUY_LAND_RIGHTS_FLAG_SET_OWNERSHIP_WITH_CHECKS, // Used in scenario editor -}; - -extern rct_string_id gParkName; -extern uint32_t gParkNameArgs; extern uint32_t gParkFlags; extern uint16_t gParkRating; extern money16 gParkEntranceFee; @@ -125,27 +114,14 @@ int32_t get_forced_park_rating(); int32_t park_is_open(); int32_t park_calculate_size(); -void reset_park_entry(); - void update_park_fences(CoordsXY coords); void update_park_fences_around_tile(CoordsXY coords); uint8_t calculate_guest_initial_happiness(uint8_t percentage); -void park_set_open(int32_t open); +void park_set_open(bool open); int32_t park_entrance_get_index(int32_t x, int32_t y, int32_t z); -void park_set_name(const char* name); void park_set_entrance_fee(money32 value); - -int32_t map_buy_land_rights(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t setting, int32_t flags); - -void game_command_set_park_entrance_fee( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_set_park_open( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); -void game_command_buy_land_rights( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); - money16 park_get_entrance_fee(); bool park_ride_prices_unlocked(); diff --git a/src/openrct2/world/Particle.cpp b/src/openrct2/world/Particle.cpp index b07414ac31..0c94f33d2e 100644 --- a/src/openrct2/world/Particle.cpp +++ b/src/openrct2/world/Particle.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -20,7 +20,7 @@ */ void crashed_vehicle_particle_create(rct_vehicle_colour colours, int32_t x, int32_t y, int32_t z) { - rct_crashed_vehicle_particle* sprite = (rct_crashed_vehicle_particle*)create_sprite(2); + rct_crashed_vehicle_particle* sprite = &create_sprite(SPRITE_IDENTIFIER_MISC)->crashed_vehicle_particle; if (sprite != nullptr) { sprite->colour[0] = colours.body_colour; @@ -80,14 +80,13 @@ void crashed_vehicle_particle_update(rct_crashed_vehicle_particle* particle) particle->velocity_z = vz & 0xFFFF; // Check collision with land / water - uint32_t waterLand = tile_element_height(x, y); - int16_t landZ = (waterLand & 0xFFFF); - int16_t waterZ = (waterLand >> 16); + int16_t landZ = tile_element_height({ x, y }); + int16_t waterZ = tile_element_water_height({ x, y }); if (waterZ != 0 && particle->z >= waterZ && z <= waterZ) { // Splash - audio_play_sound_at_location(SOUND_WATER_2, particle->x, particle->y, waterZ); + audio_play_sound_at_location(SoundId::Water2, { particle->x, particle->y, waterZ }); crash_splash_create(particle->x, particle->y, waterZ); sprite_remove((rct_sprite*)particle); return; @@ -115,7 +114,7 @@ void crashed_vehicle_particle_update(rct_crashed_vehicle_particle* particle) */ void crash_splash_create(int32_t x, int32_t y, int32_t z) { - rct_sprite_generic* sprite = (rct_sprite_generic*)create_sprite(2); + rct_sprite_generic* sprite = &create_sprite(SPRITE_IDENTIFIER_MISC)->generic; if (sprite != nullptr) { sprite->sprite_width = 33; diff --git a/src/openrct2/world/Scenery.cpp b/src/openrct2/world/Scenery.cpp index 212232ccc2..f5df790a29 100644 --- a/src/openrct2/world/Scenery.cpp +++ b/src/openrct2/world/Scenery.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -12,6 +12,7 @@ #include "../Cheats.h" #include "../Context.h" #include "../Game.h" +#include "../actions/BannerRemoveAction.hpp" #include "../actions/FootpathSceneryRemoveAction.hpp" #include "../actions/LargeSceneryRemoveAction.hpp" #include "../actions/SmallSceneryRemoveAction.hpp" @@ -40,7 +41,6 @@ colour_t gWindowScenerySecondaryColour; colour_t gWindowSceneryTertiaryColour; bool gWindowSceneryEyedropperEnabled; -TileElement* gSceneryTileElement; uint8_t gSceneryQuadrant; money32 gSceneryPlaceCost; @@ -63,8 +63,6 @@ int16_t gSceneryShiftPressZOffset; int16_t gSceneryCtrlPressed; int16_t gSceneryCtrlPressZ; -uint8_t gSceneryGroundFlags; - money32 gClearSceneryCost; // rct2: 0x009A3E74 @@ -75,6 +73,8 @@ void scenery_update_tile(int32_t x, int32_t y) TileElement* tileElement; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -98,11 +98,11 @@ void scenery_update_tile(int32_t x, int32_t y) { if (sceneryEntry->path_bit.flags & PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER) { - jumping_fountain_begin(JUMPING_FOUNTAIN_TYPE_WATER, x, y, tileElement); + JumpingFountain::StartAnimation(JUMPING_FOUNTAIN_TYPE_WATER, { x, y }, tileElement); } else if (sceneryEntry->path_bit.flags & PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW) { - jumping_fountain_begin(JUMPING_FOUNTAIN_TYPE_SNOW, x, y, tileElement); + JumpingFountain::StartAnimation(JUMPING_FOUNTAIN_TYPE_SNOW, { x, y }, tileElement); } } } @@ -139,7 +139,8 @@ void scenery_update_age(int32_t x, int32_t y, TileElement* tileElement) // Check map elements above, presumably to see if map element is blocked from rain tileElementAbove = tileElement; - while (!(tileElementAbove->flags & 7)) + // Change from original: RCT2 only checked for the first three quadrants, which was very likely to be a bug. + while (!(tileElementAbove->GetOccupiedQuadrants())) { tileElementAbove++; @@ -188,8 +189,9 @@ void scenery_remove_ghost_tool_placement() { gSceneryGhostType &= ~SCENERY_GHOST_FLAG_0; - auto removeSceneryAction = SmallSceneryRemoveAction(x, y, z, gSceneryQuadrant, gSceneryPlaceObject); - removeSceneryAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_GHOST); + auto removeSceneryAction = SmallSceneryRemoveAction({ x, y, z * 8 }, gSceneryQuadrant, gSceneryPlaceObject); + removeSceneryAction.SetFlags( + GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); removeSceneryAction.Execute(); } @@ -200,6 +202,9 @@ void scenery_remove_ghost_tool_placement() do { + if (tileElement == nullptr) + break; + if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) continue; @@ -217,7 +222,7 @@ void scenery_remove_ghost_tool_placement() { gSceneryGhostType &= ~SCENERY_GHOST_FLAG_2; - TileCoordsXYZD wallLocation = { x >> 5, y >> 5, z, gSceneryGhostWallRotation }; + CoordsXYZD wallLocation = { x, y, z * 8, gSceneryGhostWallRotation }; auto wallRemoveAction = WallRemoveAction(wallLocation); wallRemoveAction.SetFlags(GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_PATH_SCENERY); wallRemoveAction.Execute(); @@ -227,18 +232,21 @@ void scenery_remove_ghost_tool_placement() { gSceneryGhostType &= ~SCENERY_GHOST_FLAG_3; - auto removeSceneryAction = LargeSceneryRemoveAction(x, y, z, gSceneryPlaceRotation, 0); + auto removeSceneryAction = LargeSceneryRemoveAction({ x, y, z * 8, gSceneryPlaceRotation }, 0); removeSceneryAction.SetFlags( - GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5); + GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED + | GAME_COMMAND_FLAG_NO_SPEND); removeSceneryAction.Execute(); } if (gSceneryGhostType & SCENERY_GHOST_FLAG_4) { gSceneryGhostType &= ~SCENERY_GHOST_FLAG_4; - constexpr uint32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED - | GAME_COMMAND_FLAG_5; - game_do_command(x, flags, y, z | (gSceneryPlaceRotation << 8), GAME_COMMAND_REMOVE_BANNER, 0, 0); + + auto removeSceneryAction = BannerRemoveAction({ x, y, z * 8, gSceneryPlaceRotation }); + removeSceneryAction.SetFlags( + GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); + GameActions::Execute(&removeSceneryAction); } } diff --git a/src/openrct2/world/Scenery.h b/src/openrct2/world/Scenery.h index beb7ef241f..19b993d94a 100644 --- a/src/openrct2/world/Scenery.h +++ b/src/openrct2/world/Scenery.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -256,7 +256,6 @@ extern colour_t gWindowScenerySecondaryColour; extern colour_t gWindowSceneryTertiaryColour; extern bool gWindowSceneryEyedropperEnabled; -extern TileElement* gSceneryTileElement; extern uint8_t gSceneryQuadrant; extern money32 gSceneryPlaceCost; @@ -279,8 +278,6 @@ extern int16_t gSceneryShiftPressZOffset; extern int16_t gSceneryCtrlPressed; extern int16_t gSceneryCtrlPressZ; -extern uint8_t gSceneryGroundFlags; - extern const LocationXY8 ScenerySubTileOffsets[]; extern money32 gClearSceneryCost; diff --git a/src/openrct2/world/SmallScenery.cpp b/src/openrct2/world/SmallScenery.cpp index a62fe4673b..310346900c 100644 --- a/src/openrct2/world/SmallScenery.cpp +++ b/src/openrct2/world/SmallScenery.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -24,95 +24,47 @@ #include "Scenery.h" #include "Surface.h" -static money32 SmallScenerySetColour( - int16_t x, int16_t y, uint8_t baseHeight, uint8_t quadrant, uint8_t sceneryType, uint8_t primaryColour, - uint8_t secondaryColour, uint8_t flags) +static int32_t map_place_clear_func( + TileElement** tile_element, int32_t x, int32_t y, uint8_t flags, money32* price, bool is_scenery) { - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - int32_t z = baseHeight * 8; - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = z; + if ((*tile_element)->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) + return 1; - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode) + if (is_scenery && !(flags & GAME_COMMAND_FLAG_PATH_SCENERY)) + return 1; + + rct_scenery_entry* scenery = (*tile_element)->AsSmallScenery()->GetEntry(); + + if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) { - if (!map_is_location_owned(x, y, z)) - { - return MONEY32_UNDEFINED; - } + if (scenery_small_entry_has_flag(scenery, SMALL_SCENERY_FLAG_IS_TREE)) + return 1; } - TileElement* tileElement = map_get_small_scenery_element_at(x, y, baseHeight, sceneryType, quadrant); + if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) + *price += scenery->small_scenery.removal_price * 10; - if (tileElement == nullptr) - { + if (flags & GAME_COMMAND_FLAG_GHOST) return 0; - } - if ((flags & GAME_COMMAND_FLAG_GHOST) && !(tileElement->IsGhost())) - { + if (!(flags & GAME_COMMAND_FLAG_APPLY)) return 0; - } - if (flags & GAME_COMMAND_FLAG_APPLY) - { - tileElement->AsSmallScenery()->SetPrimaryColour(primaryColour); - tileElement->AsSmallScenery()->SetSecondaryColour(secondaryColour); + map_invalidate_tile(x, y, (*tile_element)->base_height * 8, (*tile_element)->clearance_height * 8); - map_invalidate_tile_full(x, y); - } + tile_element_remove(*tile_element); + (*tile_element)--; return 0; } -/** - * - * rct2: 0x006E0F26 - */ -void game_command_set_scenery_colour( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, - int32_t* ebp) -{ - *ebx = SmallScenerySetColour( - *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, ((*ebx >> 8) & 0xFF), (*edx >> 8) & 0xFF, *ebp & 0xFF, (*ebp >> 8) & 0xFF, - *ebx & 0xFF); -} - /** * * rct2: 0x006E0D6E, 0x006B8D88 */ int32_t map_place_scenery_clear_func(TileElement** tile_element, int32_t x, int32_t y, uint8_t flags, money32* price) { - if ((*tile_element)->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) - return 1; - - if (!(flags & GAME_COMMAND_FLAG_PATH_SCENERY)) - return 1; - - rct_scenery_entry* scenery = (*tile_element)->AsSmallScenery()->GetEntry(); - - if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) - { - if (scenery->small_scenery.height > 64) - return 1; - } - - if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) - *price += scenery->small_scenery.removal_price * 10; - - if (flags & GAME_COMMAND_FLAG_GHOST) - return 0; - - if (!(flags & GAME_COMMAND_FLAG_APPLY)) - return 0; - - map_invalidate_tile(x, y, (*tile_element)->base_height * 8, (*tile_element)->clearance_height * 8); - - tile_element_remove(*tile_element); - - (*tile_element)--; - return 0; + return map_place_clear_func(tile_element, x, y, flags, price, /*is_scenery=*/true); } /** @@ -121,32 +73,7 @@ int32_t map_place_scenery_clear_func(TileElement** tile_element, int32_t x, int3 */ int32_t map_place_non_scenery_clear_func(TileElement** tile_element, int32_t x, int32_t y, uint8_t flags, money32* price) { - if ((*tile_element)->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) - return 1; - - rct_scenery_entry* scenery = (*tile_element)->AsSmallScenery()->GetEntry(); - - if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) - { - if (scenery->small_scenery.height > 64) - return 1; - } - - if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) - *price += scenery->small_scenery.removal_price * 10; - - if (flags & GAME_COMMAND_FLAG_GHOST) - return 0; - - if (!(flags & GAME_COMMAND_FLAG_APPLY)) - return 0; - - map_invalidate_tile(x, y, (*tile_element)->base_height * 8, (*tile_element)->clearance_height * 8); - - tile_element_remove(*tile_element); - - (*tile_element)--; - return 0; + return map_place_clear_func(tile_element, x, y, flags, price, /*is_scenery=*/false); } bool scenery_small_entry_has_flag(const rct_scenery_entry* sceneryEntry, uint32_t flags) diff --git a/src/openrct2/world/SmallScenery.h b/src/openrct2/world/SmallScenery.h index 08e705dfbd..870269d67d 100644 --- a/src/openrct2/world/SmallScenery.h +++ b/src/openrct2/world/SmallScenery.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -44,6 +44,8 @@ enum SMALL_SCENERY_FLAGS SMALL_SCENERY_FLAG_THREE_QUARTERS = (1 << 25), // 0x2000000 SMALL_SCENERY_FLAG_PAINT_SUPPORTS = (1 << 26), // 0x4000000; used for scenery items which are support structures SMALL_SCENERY_FLAG27 = (1 << 27), // 0x8000000 + + SMALL_SCENERY_FLAG_IS_TREE = (1 << 28), // Added by OpenRCT2 }; enum diff --git a/src/openrct2/world/Sprite.cpp b/src/openrct2/world/Sprite.cpp index faf327f4e1..7d43e2e235 100644 --- a/src/openrct2/world/Sprite.cpp +++ b/src/openrct2/world/Sprite.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -25,8 +25,8 @@ #include #include -uint16_t gSpriteListHead[6]; -uint16_t gSpriteListCount[6]; +uint16_t gSpriteListHead[SPRITE_LIST_COUNT]; +uint16_t gSpriteListCount[SPRITE_LIST_COUNT]; static rct_sprite _spriteList[MAX_SPRITES]; static bool _spriteFlashingList[MAX_SPRITES]; @@ -85,6 +85,10 @@ rct_sprite* get_sprite(size_t sprite_idx) return nullptr; } openrct2_assert(sprite_idx < MAX_SPRITES, "Tried getting sprite %u", sprite_idx); + if (sprite_idx >= MAX_SPRITES) + { + return nullptr; + } return &_spriteList[sprite_idx]; } @@ -149,7 +153,7 @@ void reset_sprite_list() gSavedAge = 0; std::memset(_spriteList, 0, sizeof(_spriteList)); - for (int32_t i = 0; i < NUM_SPRITE_LISTS; i++) + for (int32_t i = 0; i < SPRITE_LIST_COUNT; i++) { gSpriteListHead[i] = SPRITE_INDEX_NULL; gSpriteListCount[i] = 0; @@ -164,7 +168,7 @@ void reset_sprite_list() spr->generic.sprite_identifier = SPRITE_IDENTIFIER_NULL; spr->generic.sprite_index = i; spr->generic.next = SPRITE_INDEX_NULL; - spr->generic.linked_list_type_offset = 0; + spr->generic.linked_list_index = 0; if (previous_spr != (rct_sprite*)SPRITE_INDEX_NULL) { @@ -174,13 +178,13 @@ void reset_sprite_list() else { spr->generic.previous = SPRITE_INDEX_NULL; - gSpriteListHead[SPRITE_LIST_NULL] = i; + gSpriteListHead[SPRITE_LIST_FREE] = i; } _spriteFlashingList[i] = false; previous_spr = spr; } - gSpriteListCount[SPRITE_LIST_NULL] = MAX_SPRITES; + gSpriteListCount[SPRITE_LIST_FREE] = MAX_SPRITES; reset_sprite_spatial_index(); } @@ -251,10 +255,16 @@ rct_sprite_checksum sprite_checksum() && sprite->generic.sprite_identifier != SPRITE_IDENTIFIER_MISC) { auto copy = *sprite; + + // Only required for rendering/invalidation, has no meaning to the game state. copy.generic.sprite_left = copy.generic.sprite_right = copy.generic.sprite_top = copy.generic.sprite_bottom = 0; + copy.generic.sprite_width = copy.generic.sprite_height_negative = copy.generic.sprite_height_positive = 0; if (copy.generic.sprite_identifier == SPRITE_IDENTIFIER_PEEP) { + // Name is pointer and will not be the same across clients + copy.peep.name = {}; + // We set this to 0 because as soon the client selects a guest the window will remove the // invalidation flags causing the sprite checksum to be different than on server, the flag does not affect // game state. @@ -287,7 +297,7 @@ rct_sprite_checksum sprite_checksum() static void sprite_reset(rct_sprite_generic* sprite) { // Need to retain how the sprite is linked in lists - uint8_t llto = sprite->linked_list_type_offset; + uint8_t llto = sprite->linked_list_index; uint16_t next = sprite->next; uint16_t next_in_quadrant = sprite->next_in_quadrant; uint16_t prev = sprite->previous; @@ -296,7 +306,7 @@ static void sprite_reset(rct_sprite_generic* sprite) std::memset(sprite, 0, sizeof(rct_sprite)); - sprite->linked_list_type_offset = llto; + sprite->linked_list_index = llto; sprite->next = next; sprite->next_in_quadrant = next_in_quadrant; sprite->previous = prev; @@ -313,13 +323,13 @@ void sprite_clear_all_unused() rct_sprite_generic* sprite; uint16_t spriteIndex, nextSpriteIndex; - spriteIndex = gSpriteListHead[SPRITE_LIST_NULL]; + spriteIndex = gSpriteListHead[SPRITE_LIST_FREE]; while (spriteIndex != SPRITE_INDEX_NULL) { sprite = &get_sprite(spriteIndex)->generic; nextSpriteIndex = sprite->next; sprite_reset(sprite); - sprite->linked_list_type_offset = SPRITE_LIST_NULL * 2; + sprite->linked_list_index = SPRITE_LIST_FREE; // This shouldn't be necessary, as sprite_reset() preserves the index // but it has been left in as a safety net in case the index isn't set correctly @@ -337,31 +347,51 @@ void sprite_clear_all_unused() } } -/* - * rct2: 0x0069EC6B - * bl: if bl & 2 > 0, the sprite ends up in the MISC linked list. - */ -rct_sprite* create_sprite(uint8_t bl) +static constexpr uint16_t MAX_MISC_SPRITES = 300; + +rct_sprite* create_sprite(SPRITE_IDENTIFIER spriteIdentifier) { - size_t linkedListTypeOffset = SPRITE_LIST_UNKNOWN * 2; - if ((bl & 2) != 0) - { - // 69EC96; - uint16_t cx = 0x12C - gSpriteListCount[SPRITE_LIST_MISC]; - if (cx >= gSpriteListCount[SPRITE_LIST_NULL]) - { - return nullptr; - } - linkedListTypeOffset = SPRITE_LIST_MISC * 2; - } - else if (gSpriteListCount[SPRITE_LIST_NULL] == 0) + if (gSpriteListCount[SPRITE_LIST_FREE] == 0) { + // No free sprites. return nullptr; } - rct_sprite_generic* sprite = &(get_sprite(gSpriteListHead[SPRITE_LIST_NULL]))->generic; + SPRITE_LIST linkedListIndex; + switch (spriteIdentifier) + { + case SPRITE_IDENTIFIER_VEHICLE: + linkedListIndex = SPRITE_LIST_VEHICLE; + break; + case SPRITE_IDENTIFIER_PEEP: + linkedListIndex = SPRITE_LIST_PEEP; + break; + case SPRITE_IDENTIFIER_MISC: + linkedListIndex = SPRITE_LIST_MISC; + break; + case SPRITE_IDENTIFIER_LITTER: + linkedListIndex = SPRITE_LIST_LITTER; + break; + default: + Guard::Assert(false, "Invalid sprite identifier: 0x%02X", spriteIdentifier); + return nullptr; + } - move_sprite_to_list((rct_sprite*)sprite, (uint8_t)linkedListTypeOffset); + if (linkedListIndex == SPRITE_LIST_MISC) + { + // Misc sprites are commonly used for effects, if there are less than MAX_MISC_SPRITES + // free it will fail to keep slots for more relevant sprites. + // Also there can't be more than MAX_MISC_SPRITES sprites in this list. + uint16_t miscSlotsRemaining = MAX_MISC_SPRITES - gSpriteListCount[SPRITE_LIST_MISC]; + if (miscSlotsRemaining >= gSpriteListCount[SPRITE_LIST_FREE]) + { + return nullptr; + } + } + + rct_sprite_generic* sprite = &(get_sprite(gSpriteListHead[SPRITE_LIST_FREE]))->generic; + + move_sprite_to_list((rct_sprite*)sprite, linkedListIndex); // Need to reset all sprite data, as the uninitialised values // may contain garbage and cause a desync later on. @@ -385,19 +415,15 @@ rct_sprite* create_sprite(uint8_t bl) /* * rct2: 0x0069ED0B * This function moves a sprite to the specified sprite linked list. - * There are 5/6 of those, and cl specifies a pointer offset - * of the desired linked list in a uint16_t array. Known valid values are - * 2, 4, 6, 8 or 10 (SPRITE_LIST_... * 2) + * The game uses this list to categorise sprites by type. */ -void move_sprite_to_list(rct_sprite* sprite, uint8_t newListOffset) +void move_sprite_to_list(rct_sprite* sprite, SPRITE_LIST newListIndex) { rct_sprite_generic* unkSprite = &sprite->generic; - uint8_t oldListOffset = unkSprite->linked_list_type_offset; - int32_t oldList = oldListOffset >> 1; - int32_t newList = newListOffset >> 1; + int32_t oldListIndex = unkSprite->linked_list_index; // No need to move if the sprite is already in the desired list - if (oldListOffset == newListOffset) + if (oldListIndex == newListIndex) { return; } @@ -406,7 +432,7 @@ void move_sprite_to_list(rct_sprite* sprite, uint8_t newListOffset) // sprite following this one becomes the new head of the list. if (unkSprite->previous == SPRITE_INDEX_NULL) { - gSpriteListHead[oldList] = unkSprite->next; + gSpriteListHead[oldListIndex] = unkSprite->next; } else { @@ -421,10 +447,10 @@ void move_sprite_to_list(rct_sprite* sprite, uint8_t newListOffset) } unkSprite->previous = SPRITE_INDEX_NULL; // We become the new head of the target list, so there's no previous sprite - unkSprite->linked_list_type_offset = newListOffset; + unkSprite->linked_list_index = newListIndex; - unkSprite->next = gSpriteListHead[newList]; // This sprite's next sprite is the old head, since we're the new head - gSpriteListHead[newList] = unkSprite->sprite_index; // Store this sprite's index as head of its new list + unkSprite->next = gSpriteListHead[newListIndex]; // This sprite's next sprite is the old head, since we're the new head + gSpriteListHead[newListIndex] = unkSprite->sprite_index; // Store this sprite's index as head of its new list if (unkSprite->next != SPRITE_INDEX_NULL) { @@ -434,8 +460,8 @@ void move_sprite_to_list(rct_sprite* sprite, uint8_t newListOffset) // These globals are probably counters for each sprite list? // Decrement old list counter, increment new list counter. - gSpriteListCount[oldList]--; - gSpriteListCount[newList]++; + gSpriteListCount[oldListIndex]--; + gSpriteListCount[newListIndex]++; } /** @@ -466,7 +492,7 @@ static void sprite_steam_particle_update(rct_steam_particle* steam) */ void sprite_misc_explosion_cloud_create(int32_t x, int32_t y, int32_t z) { - rct_sprite_generic* sprite = (rct_sprite_generic*)create_sprite(2); + rct_sprite_generic* sprite = &create_sprite(SPRITE_IDENTIFIER_MISC)->generic; if (sprite != nullptr) { sprite->sprite_width = 44; @@ -499,7 +525,7 @@ static void sprite_misc_explosion_cloud_update(rct_sprite* sprite) */ void sprite_misc_explosion_flare_create(int32_t x, int32_t y, int32_t z) { - rct_sprite_generic* sprite = (rct_sprite_generic*)create_sprite(2); + rct_sprite_generic* sprite = &create_sprite(SPRITE_IDENTIFIER_MISC)->generic; if (sprite != nullptr) { sprite->sprite_width = 25; @@ -538,7 +564,7 @@ static void sprite_misc_update(rct_sprite* sprite) sprite_steam_particle_update((rct_steam_particle*)sprite); break; case SPRITE_MISC_MONEY_EFFECT: - money_effect_update(&sprite->money_effect); + sprite->money_effect.Update(); break; case SPRITE_MISC_CRASHED_VEHICLE_PARTICLE: crashed_vehicle_particle_update((rct_crashed_vehicle_particle*)sprite); @@ -554,7 +580,7 @@ static void sprite_misc_update(rct_sprite* sprite) break; case SPRITE_MISC_JUMPING_FOUNTAIN_WATER: case SPRITE_MISC_JUMPING_FOUNTAIN_SNOW: - jumping_fountain_update(&sprite->jumping_fountain); + sprite->jumping_fountain.Update(); break; case SPRITE_MISC_BALLOON: balloon_update(&sprite->balloon); @@ -639,31 +665,13 @@ void sprite_move(int16_t x, int16_t y, int16_t z, rct_sprite* sprite) void sprite_set_coordinates(int16_t x, int16_t y, int16_t z, rct_sprite* sprite) { - int16_t new_x = x, new_y = y, start_x = x; - switch (get_current_rotation()) - { - case 0: - new_x = new_y - new_x; - new_y = (new_y + start_x) / 2 - z; - break; - case 1: - new_x = -new_y - new_x; - new_y = (new_y - start_x) / 2 - z; - break; - case 2: - new_x = -new_y + new_x; - new_y = (-new_y - start_x) / 2 - z; - break; - case 3: - new_x = new_y + new_x; - new_y = (-new_y + start_x) / 2 - z; - break; - } + CoordsXYZ coords3d = { x, y, z }; + auto screenCoords = translate_3d_to_2d_with_z(get_current_rotation(), coords3d); - sprite->generic.sprite_left = new_x - sprite->generic.sprite_width; - sprite->generic.sprite_right = new_x + sprite->generic.sprite_width; - sprite->generic.sprite_top = new_y - sprite->generic.sprite_height_negative; - sprite->generic.sprite_bottom = new_y + sprite->generic.sprite_height_positive; + sprite->generic.sprite_left = screenCoords.x - sprite->generic.sprite_width; + sprite->generic.sprite_right = screenCoords.x + sprite->generic.sprite_width; + sprite->generic.sprite_top = screenCoords.y - sprite->generic.sprite_height_negative; + sprite->generic.sprite_bottom = screenCoords.y + sprite->generic.sprite_height_positive; sprite->generic.x = x; sprite->generic.y = y; sprite->generic.z = z; @@ -678,10 +686,10 @@ void sprite_remove(rct_sprite* sprite) auto peep = sprite->AsPeep(); if (peep != nullptr) { - user_string_free(peep->name_string_idx); + peep->SetName({}); } - move_sprite_to_list(sprite, SPRITE_LIST_NULL * 2); + move_sprite_to_list(sprite, SPRITE_LIST_FREE); sprite->generic.sprite_identifier = SPRITE_IDENTIFIER_NULL; _spriteFlashingList[sprite->generic.sprite_index] = false; @@ -699,10 +707,12 @@ static bool litter_can_be_at(int32_t x, int32_t y, int32_t z) { TileElement* tileElement; - if (!map_is_location_owned(x & 0xFFE0, y & 0xFFE0, z)) + if (!map_is_location_owned({ x, y, z })) return false; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return false; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH) @@ -712,10 +722,7 @@ static bool litter_can_be_at(int32_t x, int32_t y, int32_t z) if (pathZ < z || pathZ >= z + 32) continue; - if (tile_element_is_underground(tileElement)) - return false; - - return true; + return !tile_element_is_underground(tileElement); } while (!(tileElement++)->IsLastForTile()); return false; } @@ -758,11 +765,10 @@ void litter_create(int32_t x, int32_t y, int32_t z, int32_t direction, int32_t t } } - rct_litter* litter = (rct_litter*)create_sprite(1); + rct_litter* litter = (rct_litter*)create_sprite(SPRITE_IDENTIFIER_LITTER); if (litter == nullptr) return; - move_sprite_to_list((rct_sprite*)litter, SPRITE_LIST_LITTER * 2); litter->sprite_direction = direction; litter->sprite_width = 6; litter->sprite_height_negative = 6; @@ -785,7 +791,7 @@ void litter_remove_at(int32_t x, int32_t y, int32_t z) { rct_sprite* sprite = get_sprite(spriteIndex); uint16_t nextSpriteIndex = sprite->generic.next_in_quadrant; - if (sprite->generic.linked_list_type_offset == SPRITE_LIST_LITTER * 2) + if (sprite->generic.linked_list_index == SPRITE_LIST_LITTER) { rct_litter* litter = &sprite->litter; @@ -807,11 +813,11 @@ void litter_remove_at(int32_t x, int32_t y, int32_t z) */ static bool sprite_should_tween(rct_sprite* sprite) { - switch (sprite->generic.linked_list_type_offset >> 1) + switch (sprite->generic.linked_list_index) { - case SPRITE_LIST_TRAIN: case SPRITE_LIST_PEEP: - case SPRITE_LIST_UNKNOWN: + case SPRITE_LIST_VEHICLE_HEAD: + case SPRITE_LIST_VEHICLE: return true; } return false; @@ -992,7 +998,7 @@ static bool index_is_in_list(uint16_t index, enum SPRITE_LIST sl) int32_t check_for_sprite_list_cycles(bool fix) { - for (int32_t i = 0; i < NUM_SPRITE_LISTS; i++) + for (int32_t i = 0; i < SPRITE_LIST_COUNT; i++) { rct_sprite* cycle_start = find_sprite_list_cycle(gSpriteListHead[i]); if (cycle_start != nullptr) @@ -1029,7 +1035,7 @@ int32_t check_for_sprite_list_cycles(bool fix) } /** - * Finds and fixes null sprites that are not reachable via SPRITE_LIST_NULL list. + * Finds and fixes null sprites that are not reachable via SPRITE_LIST_FREE list. * * @return count of disjoint sprites found */ @@ -1037,7 +1043,7 @@ int32_t fix_disjoint_sprites() { // Find reachable sprites bool reachable[MAX_SPRITES] = { false }; - uint16_t sprite_idx = gSpriteListHead[SPRITE_LIST_NULL]; + uint16_t sprite_idx = gSpriteListHead[SPRITE_LIST_FREE]; rct_sprite* null_list_tail = nullptr; while (sprite_idx != SPRITE_INDEX_NULL) { diff --git a/src/openrct2/world/Sprite.h b/src/openrct2/world/Sprite.h index 6becdade86..b0f76e0c42 100644 --- a/src/openrct2/world/Sprite.h +++ b/src/openrct2/world/Sprite.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -13,11 +13,11 @@ #include "../common.h" #include "../peep/Peep.h" #include "../ride/Vehicle.h" +#include "Fountain.h" #include "SpriteBase.h" #define SPRITE_INDEX_NULL 0xFFFF #define MAX_SPRITES 10000 -#define NUM_SPRITE_LISTS 6 enum SPRITE_IDENTIFIER { @@ -30,17 +30,13 @@ enum SPRITE_IDENTIFIER enum SPRITE_LIST { - SPRITE_LIST_NULL, - SPRITE_LIST_TRAIN, + SPRITE_LIST_FREE, + SPRITE_LIST_VEHICLE_HEAD, SPRITE_LIST_PEEP, SPRITE_LIST_MISC, SPRITE_LIST_LITTER, - SPRITE_LIST_UNKNOWN, -}; - -struct rct_sprite_generic : rct_sprite_common -{ - uint16_t frame; + SPRITE_LIST_VEHICLE, + SPRITE_LIST_COUNT, }; struct rct_litter : rct_sprite_common @@ -76,15 +72,6 @@ struct rct_duck : rct_sprite_generic void MoveTo(int16_t x, int16_t y, int16_t z); }; -struct rct_jumping_fountain : rct_sprite_generic -{ - uint8_t num_ticks_alive; - uint8_t fountain_flags; - int16_t target_x; - int16_t target_y; - uint16_t iteration; -}; - struct rct_money_effect : rct_sprite_common { uint16_t move_delay; @@ -93,6 +80,11 @@ struct rct_money_effect : rct_sprite_common money32 value; int16_t offset_x; uint16_t wiggle; + + static void CreateAt(money32 value, int32_t x, int32_t y, int32_t z, bool vertical); + static void Create(money32 value); + void Update(); + std::pair GetStringId() const; }; struct rct_crashed_vehicle_particle : rct_sprite_generic @@ -131,7 +123,7 @@ union rct_sprite rct_vehicle vehicle; rct_balloon balloon; rct_duck duck; - rct_jumping_fountain jumping_fountain; + JumpingFountain jumping_fountain; rct_money_effect money_effect; rct_crashed_vehicle_particle crashed_vehicle_particle; rct_crash_splash crash_splash; @@ -139,9 +131,11 @@ union rct_sprite bool IsBalloon(); bool IsDuck(); + bool IsMoneyEffect(); bool IsPeep(); rct_balloon* AsBalloon(); rct_duck* AsDuck(); + rct_money_effect* AsMoneyEffect(); Peep* AsPeep(); }; assert_struct_size(rct_sprite, 0x100); @@ -201,11 +195,11 @@ extern uint16_t gSpriteSpatialIndex[0x10001]; extern const rct_string_id litterNames[12]; -rct_sprite* create_sprite(uint8_t bl); +rct_sprite* create_sprite(SPRITE_IDENTIFIER spriteIdentifier); void reset_sprite_list(); void reset_sprite_spatial_index(); void sprite_clear_all_unused(); -void move_sprite_to_list(rct_sprite* sprite, uint8_t cl); +void move_sprite_to_list(rct_sprite* sprite, SPRITE_LIST newList); void sprite_misc_update_all(); void sprite_move(int16_t x, int16_t y, int16_t z, rct_sprite* sprite); void sprite_set_coordinates(int16_t x, int16_t y, int16_t z, rct_sprite* sprite); @@ -229,26 +223,16 @@ void sprite_position_tween_reset(); /////////////////////////////////////////////////////////////// void create_balloon(int32_t x, int32_t y, int32_t z, int32_t colour, bool isPopped); void balloon_update(rct_balloon* balloon); -void game_command_balloon_press( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); /////////////////////////////////////////////////////////////// // Duck /////////////////////////////////////////////////////////////// -void create_duck(int32_t targetX, int32_t targetY); +void create_duck(const CoordsXY& pos); void duck_update(rct_duck* duck); void duck_press(rct_duck* duck); void duck_remove_all(); uint32_t duck_get_frame_image(const rct_duck* duck, int32_t direction); -/////////////////////////////////////////////////////////////// -// Money effect -/////////////////////////////////////////////////////////////// -void money_effect_create(money32 value); -void money_effect_create_at(money32 value, int32_t x, int32_t y, int32_t z, bool vertical); -void money_effect_update(rct_money_effect* moneyEffect); -rct_string_id money_effect_get_string_id(const rct_money_effect* sprite, money32* outValue); - /////////////////////////////////////////////////////////////// // Crash particles /////////////////////////////////////////////////////////////// diff --git a/src/openrct2/world/SpriteBase.h b/src/openrct2/world/SpriteBase.h index d6ec5f0079..44f9737c26 100644 --- a/src/openrct2/world/SpriteBase.h +++ b/src/openrct2/world/SpriteBase.h @@ -10,7 +10,7 @@ struct rct_sprite_common uint16_t next; uint16_t previous; // Valid values are SPRITE_LINKEDLIST_OFFSET_... - uint8_t linked_list_type_offset; + uint8_t linked_list_index; // Height from centre of sprite to bottom uint8_t sprite_height_negative; uint16_t sprite_index; @@ -22,9 +22,16 @@ struct rct_sprite_common uint8_t sprite_width; // Height from centre of sprite to top uint8_t sprite_height_positive; + // Screen Coordinates of sprite int16_t sprite_left; int16_t sprite_top; int16_t sprite_right; int16_t sprite_bottom; + uint8_t sprite_direction; }; + +struct rct_sprite_generic : rct_sprite_common +{ + uint16_t frame; +}; diff --git a/src/openrct2/world/Surface.cpp b/src/openrct2/world/Surface.cpp index 4fa99a6137..4d1289d59d 100644 --- a/src/openrct2/world/Surface.cpp +++ b/src/openrct2/world/Surface.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -18,56 +18,32 @@ uint32_t SurfaceElement::GetSurfaceStyle() const { - uint32_t retVal = (terrain >> 5) & 7; - if (type & 1) - retVal |= (1 << 3); - return retVal; + return SurfaceStyle; } uint32_t SurfaceElement::GetEdgeStyle() const { - uint32_t terrain_edge = (slope >> 5) & 7; - if (type & 128) - terrain_edge |= (1 << 3); - return terrain_edge; + return EdgeStyle; } void SurfaceElement::SetSurfaceStyle(uint32_t newStyle) { - // Bit 3 for terrain is stored in element.type bit 0 - if (newStyle & 8) - type |= 1; - else - type &= ~1; - - // Bits 0, 1, 2 for terrain are stored in element.terrain bit 5, 6, 7 - terrain &= ~0xE0; - terrain |= (newStyle & 7) << 5; + SurfaceStyle = newStyle; } void SurfaceElement::SetEdgeStyle(uint32_t newStyle) { - // Bit 3 for terrain is stored in element.type bit 7 - if (newStyle & 8) - type |= 128; - else - type &= ~128; - - // Bits 0, 1, 2 for terrain are stored in element.slope bit 5, 6, 7 - slope &= ~TILE_ELEMENT_SURFACE_EDGE_STYLE_MASK; - slope |= (newStyle & 7) << 5; + EdgeStyle = newStyle; } uint32_t SurfaceElement::GetWaterHeight() const { - return terrain & TILE_ELEMENT_SURFACE_WATER_HEIGHT_MASK; + return WaterHeight; } void SurfaceElement::SetWaterHeight(uint32_t newWaterHeight) { - newWaterHeight &= 0x1F; - terrain &= ~TILE_ELEMENT_SURFACE_WATER_HEIGHT_MASK; - terrain |= newWaterHeight; + WaterHeight = newWaterHeight; } bool SurfaceElement::CanGrassGrow() const @@ -88,20 +64,20 @@ bool SurfaceElement::CanGrassGrow() const uint8_t SurfaceElement::GetGrassLength() const { - return grass_length; + return GrassLength; } void SurfaceElement::SetGrassLength(uint8_t newLength) { - grass_length = newLength; + GrassLength = newLength; } void SurfaceElement::SetGrassLengthAndInvalidate(uint8_t length, CoordsXY coords) { - uint8_t oldLength = grass_length & 0x7; + uint8_t oldLength = GrassLength & 0x7; uint8_t newLength = length & 0x7; - grass_length = length; + GrassLength = length; if (newLength == oldLength) { @@ -130,7 +106,7 @@ void SurfaceElement::UpdateGrassLength(CoordsXY coords) if (!CanGrassGrow()) return; - uint8_t grassLengthTmp = grass_length & 7; + uint8_t grassLengthTmp = GrassLength & 7; // Check if grass is underwater or outside park uint32_t waterHeight = GetWaterHeight() * 2; @@ -147,7 +123,7 @@ void SurfaceElement::UpdateGrassLength(CoordsXY coords) int32_t z0 = base_height; int32_t z1 = base_height + 2; - if (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + if (Slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) z1 += 2; // Check objects above grass @@ -162,17 +138,17 @@ void SurfaceElement::UpdateGrassLength(CoordsXY coords) uint8_t lengthNibble = (GetGrassLength() & 0xF0) >> 4; if (lengthNibble < 0xF) { - grass_length += 0x10; + GrassLength += 0x10; } else { // Zeros the length nibble - grass_length += 0x10; - grass_length ^= 8; - if (grass_length & 8) + GrassLength += 0x10; + GrassLength ^= 8; + if (GrassLength & 8) { // Random growth rate (length nibble) - grass_length |= scenario_rand() & 0x70; + GrassLength |= scenario_rand() & 0x70; } else { @@ -204,35 +180,34 @@ void SurfaceElement::UpdateGrassLength(CoordsXY coords) uint8_t SurfaceElement::GetOwnership() const { - return (ownership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK); + return (Ownership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK); } void SurfaceElement::SetOwnership(uint8_t newOwnership) { - ownership &= ~TILE_ELEMENT_SURFACE_OWNERSHIP_MASK; - ownership |= (newOwnership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK); + Ownership &= ~TILE_ELEMENT_SURFACE_OWNERSHIP_MASK; + Ownership |= (newOwnership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK); } uint8_t SurfaceElement::GetParkFences() const { - return (ownership & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK); + return (Ownership & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK); } void SurfaceElement::SetParkFences(uint8_t newParkFences) { - ownership &= ~TILE_ELEMENT_SURFACE_PARK_FENCE_MASK; - ownership |= (newParkFences & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK); + Ownership &= ~TILE_ELEMENT_SURFACE_PARK_FENCE_MASK; + Ownership |= (newParkFences & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK); } uint8_t SurfaceElement::GetSlope() const { - return (slope & TILE_ELEMENT_SURFACE_SLOPE_MASK); + return Slope; } void SurfaceElement::SetSlope(uint8_t newSlope) { - slope &= ~TILE_ELEMENT_SURFACE_SLOPE_MASK; - slope |= (newSlope & TILE_ELEMENT_SURFACE_SLOPE_MASK); + Slope = newSlope; } bool SurfaceElement::HasTrackThatNeedsWater() const diff --git a/src/openrct2/world/Surface.h b/src/openrct2/world/Surface.h index 21b601f9bd..dce541ecd2 100644 --- a/src/openrct2/world/Surface.h +++ b/src/openrct2/world/Surface.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/world/TileElement.cpp b/src/openrct2/world/TileElement.cpp index 827cd7be24..a514a9cbb9 100644 --- a/src/openrct2/world/TileElement.cpp +++ b/src/openrct2/world/TileElement.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -28,18 +28,18 @@ void TileElementBase::SetType(uint8_t newType) this->type |= (newType & TILE_ELEMENT_TYPE_MASK); } -uint8_t TileElementBase::GetDirection() const +Direction TileElementBase::GetDirection() const { return this->type & TILE_ELEMENT_DIRECTION_MASK; } -void TileElementBase::SetDirection(uint8_t direction) +void TileElementBase::SetDirection(Direction direction) { this->type &= ~TILE_ELEMENT_DIRECTION_MASK; this->type |= (direction & TILE_ELEMENT_DIRECTION_MASK); } -uint8_t TileElementBase::GetDirectionWithOffset(uint8_t offset) const +Direction TileElementBase::GetDirectionWithOffset(uint8_t offset) const { return ((this->type & TILE_ELEMENT_DIRECTION_MASK) + offset) & TILE_ELEMENT_DIRECTION_MASK; } @@ -49,6 +49,14 @@ bool TileElementBase::IsLastForTile() const return (this->flags & TILE_ELEMENT_FLAG_LAST_TILE) != 0; } +void TileElementBase::SetLastForTile(bool on) +{ + if (on) + flags |= TILE_ELEMENT_FLAG_LAST_TILE; + else + flags &= ~TILE_ELEMENT_FLAG_LAST_TILE; +} + bool TileElementBase::IsGhost() const { return (this->flags & TILE_ELEMENT_FLAG_GHOST) != 0; @@ -56,7 +64,7 @@ bool TileElementBase::IsGhost() const void TileElementBase::SetGhost(bool isGhost) { - if (isGhost == true) + if (isGhost) { this->flags |= TILE_ELEMENT_FLAG_GHOST; } @@ -123,17 +131,12 @@ void tile_element_set_banner_index(TileElement* tileElement, BannerIndex bannerI void tile_element_remove_banner_entry(TileElement* tileElement) { - BannerIndex bannerIndex = tile_element_get_banner_index(tileElement); - if (bannerIndex == BANNER_INDEX_NULL) - return; - - rct_banner* banner = &gBanners[bannerIndex]; - if (banner->type != BANNER_NULL) + auto bannerIndex = tile_element_get_banner_index(tileElement); + auto banner = GetBanner(bannerIndex); + if (banner != nullptr) { - rct_windownumber windowNumber = bannerIndex; - window_close_by_number(WC_BANNER, windowNumber); - banner->type = BANNER_NULL; - user_string_free(banner->string_idx); + window_close_by_number(WC_BANNER, bannerIndex); + *banner = {}; } } @@ -159,6 +162,7 @@ void TileElement::ClearAs(uint8_t newType) base_height = 2; clearance_height = 2; std::fill_n(pad_04, sizeof(pad_04), 0x00); + std::fill_n(pad_08, sizeof(pad_08), 0x00); } void TileElementBase::Remove() @@ -209,3 +213,14 @@ const QuarterTile QuarterTile::Rotate(uint8_t amount) const return QuarterTile{ 0 }; } } + +uint8_t TileElementBase::GetOccupiedQuadrants() const +{ + return flags & TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK; +} + +void TileElementBase::SetOccupiedQuadrants(uint8_t quadrants) +{ + flags &= ~TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK; + flags |= (quadrants & TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK); +} diff --git a/src/openrct2/world/TileElement.h b/src/openrct2/world/TileElement.h index 2eb2c7747a..f4d1a47f6d 100644 --- a/src/openrct2/world/TileElement.h +++ b/src/openrct2/world/TileElement.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -14,8 +14,10 @@ #include "Footpath.h" #include "Location.hpp" +struct Banner; struct rct_scenery_entry; struct rct_footpath_entry; +typedef uint16_t track_type_t; #pragma pack(push, 1) @@ -60,28 +62,32 @@ struct CorruptElement; struct TileElementBase { uint8_t type; // 0 - uint8_t flags; // 1 + uint8_t flags; // 1. Upper nibble: flags. Lower nibble: occupied quadrants (one bit per quadrant). uint8_t base_height; // 2 uint8_t clearance_height; // 3 uint8_t GetType() const; void SetType(uint8_t newType); - uint8_t GetDirection() const; - void SetDirection(uint8_t direction); - uint8_t GetDirectionWithOffset(uint8_t offset) const; + Direction GetDirection() const; + void SetDirection(Direction direction); + Direction GetDirectionWithOffset(uint8_t offset) const; bool IsLastForTile() const; + void SetLastForTile(bool on); bool IsGhost() const; void SetGhost(bool isGhost); void Remove(); + uint8_t GetOccupiedQuadrants() const; + void SetOccupiedQuadrants(uint8_t quadrants); }; /** * Map element structure - * size: 0x08 + * size: 0x10 */ struct TileElement : public TileElementBase { uint8_t pad_04[4]; + uint8_t pad_08[8]; template TType* as() const { @@ -121,22 +127,25 @@ public: { return as(); } - CorruptElement* AsCorrupt() const - { - return as(); - } void ClearAs(uint8_t newType); }; -assert_struct_size(TileElement, 8); +assert_struct_size(TileElement, 16); struct SurfaceElement : TileElementBase { private: - uint8_t slope; // 4 0xE0 Edge Style, 0x1F Slope - uint8_t terrain; // 5 0xE0 Terrain Style, 0x1F Water height - uint8_t grass_length; // 6 - uint8_t ownership; // 7 + uint8_t Slope; + uint8_t WaterHeight; + uint8_t GrassLength; + uint8_t Ownership; + uint8_t SurfaceStyle; + uint8_t EdgeStyle; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" + uint8_t pad_08[6]; +#pragma clang diagnostic pop + public: uint8_t GetSlope() const; void SetSlope(uint8_t newSlope); @@ -164,7 +173,7 @@ public: bool HasTrackThatNeedsWater() const; void SetHasTrackThatNeedsWater(bool on); }; -assert_struct_size(SurfaceElement, 8); +assert_struct_size(SurfaceElement, 16); struct PathElement : TileElementBase { @@ -177,6 +186,10 @@ private: uint8_t additionStatus; // 7 ride_id_t rideIndex; }; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" + uint8_t pad_08[8]; +#pragma clang diagnostic pop public: uint8_t GetPathEntryIndex() const; @@ -193,8 +206,8 @@ public: bool IsSloped() const; void SetSloped(bool isSloped); - uint8_t GetSlopeDirection() const; - void SetSlopeDirection(uint8_t newSlope); + Direction GetSlopeDirection() const; + void SetSlopeDirection(Direction newSlope); ride_id_t GetRideIndex() const; void SetRideIndex(ride_id_t newRideIndex); @@ -235,47 +248,53 @@ public: uint8_t GetAdditionStatus() const; void SetAdditionStatus(uint8_t newStatus); - uint8_t GetRCT1PathType() const; - bool ShouldDrawPathOverSupports(); void SetShouldDrawPathOverSupports(bool on); }; -assert_struct_size(PathElement, 8); +assert_struct_size(PathElement, 16); struct TrackElement : TileElementBase { - uint8_t trackType; // 4 +private: + track_type_t TrackType; union { struct { - // The lower 4 bits are the track sequence. - // The upper 4 bits are either station bits or on-ride photo bits. - // - // Station bits: - // - Bit 8 marks green light - // - Bit 5-7 are station index. - // - // On-ride photo bits: - // - Bits 7 and 8 are never set - // - Bits 5 and 6 are set when a vehicle triggers the on-ride photo and act like a countdown from 3. - // - If any of the bits 5-8 are set, the game counts it as a photo being taken. - uint8_t sequence; // 5. - uint8_t colour; // 6 + uint8_t Sequence; + uint8_t ColourScheme; + union + { + // - Bits 3 and 4 are never set + // - Bits 1 and 2 are set when a vehicle triggers the on-ride photo and act like a countdown from 3. + // - If any of the bits 1-4 are set, the game counts it as a photo being taken. + uint8_t OnridePhotoBits; + // Contains the brake/booster speed, divided by 2. + uint8_t BrakeBoosterSpeed; + }; + uint8_t StationIndex; + }; + struct + { + uint16_t MazeEntry; // 5 }; - uint16_t mazeEntry; // 5 }; - ride_id_t rideIndex; // 7 + uint8_t Flags2; + ride_idnew_t RideIndex; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" + uint8_t pad[1]; +#pragma clang diagnostic pop public: - uint8_t GetTrackType() const; - void SetTrackType(uint8_t newEntryIndex); + track_type_t GetTrackType() const; + void SetTrackType(track_type_t newEntryIndex); uint8_t GetSequenceIndex() const; void SetSequenceIndex(uint8_t newSequenceIndex); - ride_id_t GetRideIndex() const; - void SetRideIndex(ride_id_t newRideIndex); + uint32_t GetRideIndex() const; + void SetRideIndex(uint32_t newRideIndex); uint8_t GetColourScheme() const; void SetColourScheme(uint8_t newColourScheme); @@ -301,8 +320,8 @@ public: uint8_t GetBrakeBoosterSpeed() const; void SetBrakeBoosterSpeed(uint8_t speed); - uint8_t HasGreenLight() const; - void SetHasGreenLight(uint8_t greenLight); + bool HasGreenLight() const; + void SetHasGreenLight(bool on); uint8_t GetSeatRotation() const; void SetSeatRotation(uint8_t newSeatRotation); @@ -316,16 +335,19 @@ public: void SetPhotoTimeout(); void SetPhotoTimeout(uint8_t newValue); void DecrementPhotoTimeout(); + uint8_t GetPhotoTimeout() const; bool IsHighlighted() const; void SetHighlight(bool on); - // Used in RCT1, will be reintroduced at some point. + // Used by ghost train, RCT1 feature, will be reintroduced at some point. // (See https://github.com/OpenRCT2/OpenRCT2/issues/7059) uint8_t GetDoorAState() const; uint8_t GetDoorBState() const; + void SetDoorAState(uint8_t newState); + void SetDoorBState(uint8_t newState); }; -assert_struct_size(TrackElement, 8); +assert_struct_size(TrackElement, 16); struct SmallSceneryElement : TileElementBase { @@ -334,6 +356,11 @@ private: uint8_t age; // 5 uint8_t colour_1; // 6 uint8_t colour_2; // 7 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" + uint8_t pad_08[8]; +#pragma clang diagnostic pop + public: uint8_t GetEntryIndex() const; void SetEntryIndex(uint8_t newIndex); @@ -350,33 +377,37 @@ public: bool NeedsSupports() const; void SetNeedsSupports(); }; -assert_struct_size(SmallSceneryElement, 8); +assert_struct_size(SmallSceneryElement, 16); struct LargeSceneryElement : TileElementBase { private: - uint16_t entryIndex; // 4 - uint8_t colour[2]; // 6 + uint32_t EntryIndex; + uint32_t BannerIndex; + uint8_t SequenceIndex; + uint8_t Colour[3]; + public: uint32_t GetEntryIndex() const; void SetEntryIndex(uint32_t newIndex); rct_scenery_entry* GetEntry() const; - uint16_t GetSequenceIndex() const; - void SetSequenceIndex(uint16_t newIndex); + uint8_t GetSequenceIndex() const; + void SetSequenceIndex(uint8_t newIndex); colour_t GetPrimaryColour() const; void SetPrimaryColour(colour_t colour); colour_t GetSecondaryColour() const; void SetSecondaryColour(colour_t colour); - BannerIndex GetBannerIndex() const; - void SetBannerIndex(BannerIndex newIndex); + Banner* GetBanner() const; + ::BannerIndex GetBannerIndex() const; + void SetBannerIndex(::BannerIndex newIndex); bool IsAccounted() const; void SetIsAccounted(bool isAccounted); }; -assert_struct_size(LargeSceneryElement, 8); +assert_struct_size(LargeSceneryElement, 16); struct WallElement : TileElementBase { @@ -389,6 +420,10 @@ private: }; uint8_t colour_1; // 6 0b_2221_1111 2 = colour_2 (uses flags for rest of colour2), 1 = colour_1 uint8_t animation; // 7 0b_dfff_ft00 d = direction, f = frame num, t = across track flag (not used) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" + uint8_t pad_08[8]; +#pragma clang diagnostic pop public: uint8_t GetEntryIndex() const; @@ -408,6 +443,7 @@ public: uint8_t GetAnimationFrame() const; void SetAnimationFrame(uint8_t frameNum); + Banner* GetBanner() const; BannerIndex GetBannerIndex() const; void SetBannerIndex(BannerIndex newIndex); @@ -420,7 +456,7 @@ public: int32_t GetRCT1WallType(int32_t edge) const; colour_t GetRCT1WallColour() const; }; -assert_struct_size(WallElement, 8); +assert_struct_size(WallElement, 16); struct EntranceElement : TileElementBase { @@ -429,6 +465,10 @@ private: uint8_t index; // 5. 0bUSSS????, S = station index. uint8_t pathType; // 6 ride_id_t rideIndex; // 7 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" + uint8_t pad_08[8]; +#pragma clang diagnostic pop public: uint8_t GetEntranceType() const; @@ -446,7 +486,7 @@ public: uint8_t GetPathType() const; void SetPathType(uint8_t newPathType); }; -assert_struct_size(EntranceElement, 8); +assert_struct_size(EntranceElement, 16); struct BannerElement : TileElementBase { @@ -457,8 +497,12 @@ private: #pragma clang diagnostic ignored "-Wunused-private-field" uint8_t flags; // 6 uint8_t unused; // 7 + uint8_t pad_08[8]; #pragma clang diagnostic pop public: + Banner* GetBanner() const; + rct_scenery_entry* GetEntry() const; + BannerIndex GetIndex() const; void SetIndex(BannerIndex newIndex); @@ -469,13 +513,14 @@ public: void SetAllowedEdges(uint8_t newEdges); void ResetAllowedEdges(); }; -assert_struct_size(BannerElement, 8); +assert_struct_size(BannerElement, 16); struct CorruptElement : TileElementBase { uint8_t pad[4]; + uint8_t pad_08[8]; }; -assert_struct_size(CorruptElement, 8); +assert_struct_size(CorruptElement, 16); #pragma pack(pop) class QuarterTile @@ -518,7 +563,6 @@ enum enum { - TILE_ELEMENT_TYPE_FLAG_HIGHLIGHT = (1 << 6), SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER = (1 << 6), }; @@ -563,6 +607,7 @@ enum #define TILE_ELEMENT_QUADRANT_MASK 0b11000000 #define TILE_ELEMENT_TYPE_MASK 0b00111100 #define TILE_ELEMENT_DIRECTION_MASK 0b00000011 +#define TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK 0b00001111 #define TILE_ELEMENT_COLOUR_MASK 0b00011111 diff --git a/src/openrct2/world/TileInspector.cpp b/src/openrct2/world/TileInspector.cpp index 0e9aaaa54c..508f32c8b6 100644 --- a/src/openrct2/world/TileInspector.cpp +++ b/src/openrct2/world/TileInspector.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -11,9 +11,11 @@ #include "../Context.h" #include "../Game.h" +#include "../actions/GameAction.h" #include "../common.h" #include "../core/Guard.hpp" #include "../interface/Window.h" +#include "../interface/Window_internal.h" #include "../localisation/Localisation.h" #include "../ride/Station.h" #include "../ride/Track.h" @@ -34,10 +36,10 @@ uint32_t windowTileInspectorTileY; int32_t windowTileInspectorElementCount = 0; int32_t windowTileInspectorSelectedIndex; -static bool map_swap_elements_at(int32_t x, int32_t y, int16_t first, int16_t second) +static bool map_swap_elements_at(CoordsXY loc, int16_t first, int16_t second) { - TileElement* const firstElement = map_get_nth_element_at(x, y, first); - TileElement* const secondElement = map_get_nth_element_at(x, y, second); + TileElement* const firstElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, first); + TileElement* const secondElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, second); if (firstElement == nullptr) { @@ -63,8 +65,8 @@ static bool map_swap_elements_at(int32_t x, int32_t y, int16_t first, int16_t se // Swap the 'last map element for tile' flag if either one of them was last if ((firstElement)->IsLastForTile() || (secondElement)->IsLastForTile()) { - firstElement->flags ^= TILE_ELEMENT_FLAG_LAST_TILE; - secondElement->flags ^= TILE_ELEMENT_FLAG_LAST_TILE; + firstElement->SetLastForTile(!firstElement->IsLastForTile()); + secondElement->SetLastForTile(!secondElement->IsLastForTile()); } return true; @@ -77,28 +79,29 @@ static bool map_swap_elements_at(int32_t x, int32_t y, int16_t first, int16_t se * @param elementIndex The nth element on this tile * Returns 0 on success, MONEY_UNDEFINED otherwise. */ -int32_t tile_inspector_insert_corrupt_at(int32_t x, int32_t y, int16_t elementIndex, int32_t flags) +GameActionResult::Ptr tile_inspector_insert_corrupt_at(CoordsXY loc, int16_t elementIndex, bool isExecuting) { // Make sure there is enough space for the new element if (!map_check_free_elements_and_reorganise(1)) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { // Create new corrupt element - TileElement* corruptElement = tile_element_insert(x, y, -1, 0); // Ugly hack: -1 guarantees this to be placed first + TileElement* corruptElement = tile_element_insert( + { loc.x / 32, loc.y / 32, -1 }, 0b0000); // Ugly hack: -1 guarantees this to be placed first if (corruptElement == nullptr) { log_warning("Failed to insert corrupt element."); - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } corruptElement->SetType(TILE_ELEMENT_TYPE_CORRUPT); // Set the base height to be the same as the selected element - TileElement* const selectedElement = map_get_nth_element_at(x, y, elementIndex + 1); - if (!selectedElement) + TileElement* const selectedElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex + 1); + if (selectedElement == nullptr) { - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } corruptElement->base_height = corruptElement->clearance_height = selectedElement->base_height; @@ -106,7 +109,7 @@ int32_t tile_inspector_insert_corrupt_at(int32_t x, int32_t y, int16_t elementIn // this way it's placed under the selected element, even when there are multiple elements with the same base height for (int16_t i = 0; i < elementIndex; i++) { - if (!map_swap_elements_at(x, y, i, i + 1)) + if (!map_swap_elements_at(loc, i, i + 1)) { // don't return error here, we've already inserted an element // and moved it as far as we could, the only sensible thing left @@ -115,12 +118,12 @@ int32_t tile_inspector_insert_corrupt_at(int32_t x, int32_t y, int16_t elementIn } } - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); // Update the tile inspector's list for everyone who has the tile selected rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { windowTileInspectorElementCount++; @@ -130,12 +133,12 @@ int32_t tile_inspector_insert_corrupt_at(int32_t x, int32_t y, int16_t elementIn windowTileInspectorSelectedIndex++; } - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } // Nothing went wrong - return 0; + return std::make_unique(); } /** @@ -144,23 +147,23 @@ int32_t tile_inspector_insert_corrupt_at(int32_t x, int32_t y, int16_t elementIn * @param y The y coordinate of the tile * @param elementIndex The nth element on this tile */ -int32_t tile_inspector_remove_element_at(int32_t x, int32_t y, int16_t elementIndex, int32_t flags) +GameActionResult::Ptr tile_inspector_remove_element_at(CoordsXY loc, int16_t elementIndex, bool isExecuting) { - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { // Forcefully remove the element - TileElement* const tileElement = map_get_nth_element_at(x, y, elementIndex); - if (!tileElement) + TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); + if (tileElement == nullptr) { - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } tile_element_remove(tileElement); - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); // Update the window rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { windowTileInspectorElementCount--; @@ -173,27 +176,27 @@ int32_t tile_inspector_remove_element_at(int32_t x, int32_t y, int16_t elementIn windowTileInspectorSelectedIndex = -1; } - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_swap_elements_at(int32_t x, int32_t y, int16_t first, int16_t second, int32_t flags) +GameActionResult::Ptr tile_inspector_swap_elements_at(CoordsXY loc, int16_t first, int16_t second, bool isExecuting) { - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { - if (!map_swap_elements_at(x, y, first, second)) + if (!map_swap_elements_at(loc, first, second)) { - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); // Update the window rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { // If one of them was selected, update selected list item if (windowTileInspectorSelectedIndex == first) @@ -201,23 +204,23 @@ int32_t tile_inspector_swap_elements_at(int32_t x, int32_t y, int16_t first, int else if (windowTileInspectorSelectedIndex == second) windowTileInspectorSelectedIndex = first; - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_rotate_element_at(int32_t x, int32_t y, int32_t elementIndex, int32_t flags) +GameActionResult::Ptr tile_inspector_rotate_element_at(CoordsXY loc, int32_t elementIndex, bool isExecuting) { - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { uint8_t newRotation, pathEdges, pathCorners; - TileElement* const tileElement = map_get_nth_element_at(x, y, elementIndex); - if (!tileElement) + TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); + if (tileElement == nullptr) { - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } switch (tileElement->GetType()) { @@ -239,21 +242,26 @@ int32_t tile_inspector_rotate_element_at(int32_t x, int32_t y, int32_t elementIn tileElement->SetDirection(newRotation); // Update ride's known entrance/exit rotation - Ride* ride = get_ride(tileElement->AsEntrance()->GetRideIndex()); - uint8_t stationIndex = tileElement->AsEntrance()->GetStationIndex(); - auto entrance = ride_get_entrance_location(ride, stationIndex); - auto exit = ride_get_exit_location(ride, stationIndex); - uint8_t entranceType = tileElement->AsEntrance()->GetEntranceType(); - uint8_t z = tileElement->base_height; + auto ride = get_ride(tileElement->AsEntrance()->GetRideIndex()); + if (ride != nullptr) + { + auto stationIndex = tileElement->AsEntrance()->GetStationIndex(); + auto entrance = ride_get_entrance_location(ride, stationIndex); + auto exit = ride_get_exit_location(ride, stationIndex); + uint8_t entranceType = tileElement->AsEntrance()->GetEntranceType(); + uint8_t z = tileElement->base_height; - // Make sure this is the correct entrance or exit - if (entranceType == ENTRANCE_TYPE_RIDE_ENTRANCE && entrance.x == x && entrance.y == y && entrance.z == z) - { - ride_set_entrance_location(ride, stationIndex, { entrance.x, entrance.y, entrance.z, newRotation }); - } - else if (entranceType == ENTRANCE_TYPE_RIDE_EXIT && exit.x == x && exit.y == y && exit.z == z) - { - ride_set_exit_location(ride, stationIndex, { exit.x, exit.y, exit.z, newRotation }); + // Make sure this is the correct entrance or exit + if (entranceType == ENTRANCE_TYPE_RIDE_ENTRANCE && entrance.x == loc.x / 32 && entrance.y == loc.y / 32 + && entrance.z == z) + { + ride_set_entrance_location(ride, stationIndex, { entrance.x, entrance.y, entrance.z, newRotation }); + } + else if ( + entranceType == ENTRANCE_TYPE_RIDE_EXIT && exit.x == loc.x / 32 && exit.y == loc.y / 32 && exit.z == z) + { + ride_set_exit_location(ride, stationIndex, { exit.x, exit.y, exit.z, newRotation }); + } } break; } @@ -273,97 +281,82 @@ int32_t tile_inspector_rotate_element_at(int32_t x, int32_t y, int32_t elementIn } } - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); - if ((uint32_t)x == windowTileInspectorTileX && (uint32_t)y == windowTileInspectorTileY) + if ((uint32_t)(loc.x / 32) == windowTileInspectorTileX && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { window_invalidate_by_class(WC_TILE_INSPECTOR); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_paste_element_at(int32_t x, int32_t y, TileElement element, int32_t flags) +GameActionResult::Ptr tile_inspector_paste_element_at(CoordsXY loc, TileElement element, bool isExecuting) { // Make sure there is enough space for the new element if (!map_check_free_elements_and_reorganise(1)) { - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS, STR_NONE); } - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { // Check if the element to be pasted refers to a banner index BannerIndex bannerIndex = tile_element_get_banner_index(&element); if (bannerIndex != BANNER_INDEX_NULL) { // The element to be pasted refers to a banner index - make a copy of it - BannerIndex newBannerIndex = create_new_banner(flags); + BannerIndex newBannerIndex = create_new_banner(GAME_COMMAND_FLAG_APPLY); if (newBannerIndex == BANNER_INDEX_NULL) { - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } - rct_banner& newBanner = gBanners[newBannerIndex]; - newBanner = gBanners[bannerIndex]; - newBanner.x = x; - newBanner.y = y; + auto& newBanner = *GetBanner(newBannerIndex); + newBanner = *GetBanner(bannerIndex); + newBanner.position = TileCoordsXY(loc); // Use the new banner index tile_element_set_banner_index(&element, newBannerIndex); - - // Duplicate user string if needed - rct_string_id stringIdx = newBanner.string_idx; - if (is_user_string_id(stringIdx)) - { - utf8 buffer[USER_STRING_MAX_LENGTH]; - format_string(buffer, USER_STRING_MAX_LENGTH, stringIdx, nullptr); - rct_string_id newStringIdx = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, buffer); - if (newStringIdx == 0) - { - return MONEY32_UNDEFINED; - } - gBanners[newBannerIndex].string_idx = newStringIdx; - } } - TileElement* const pastedElement = tile_element_insert(x, y, element.base_height, 0); + // The occupiedQuadrants will be automatically set when the element is copied over, so it's not necessary to set them + // correctly _here_. + TileElement* const pastedElement = tile_element_insert({ loc.x / 32, loc.y / 32, element.base_height }, 0b0000); bool lastForTile = pastedElement->IsLastForTile(); *pastedElement = element; - pastedElement->flags &= ~TILE_ELEMENT_FLAG_LAST_TILE; - if (lastForTile) - { - pastedElement->flags |= TILE_ELEMENT_FLAG_LAST_TILE; - } + pastedElement->SetLastForTile(lastForTile); - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { windowTileInspectorElementCount++; // Select new element if there was none selected already - int16_t newIndex = (int16_t)(pastedElement - map_get_first_element_at(x, y)); + int16_t newIndex = (int16_t)(pastedElement - map_get_first_element_at(loc.x / 32, loc.y / 32)); if (windowTileInspectorSelectedIndex == -1) windowTileInspectorSelectedIndex = newIndex; else if (windowTileInspectorSelectedIndex >= newIndex) windowTileInspectorSelectedIndex++; - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_sort_elements_at(int32_t x, int32_t y, int32_t flags) +GameActionResult::Ptr tile_inspector_sort_elements_at(CoordsXY loc, bool isExecuting) { - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { - const TileElement* const firstElement = map_get_first_element_at(x, y); + const TileElement* const firstElement = map_get_first_element_at(loc.x / 32, loc.y / 32); + if (firstElement == nullptr) + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); // Count elements on tile int32_t numElement = 0; @@ -387,7 +380,7 @@ int32_t tile_inspector_sort_elements_at(int32_t x, int32_t y, int32_t flags) || (otherElement->base_height == currentElement->base_height && otherElement->clearance_height > currentElement->clearance_height))) { - if (!map_swap_elements_at(x, y, currentId - 1, currentId)) + if (!map_swap_elements_at(loc, currentId - 1, currentId)) { // don't return error here, we've already ran some actions // and moved things as far as we could, the only sensible @@ -401,35 +394,36 @@ int32_t tile_inspector_sort_elements_at(int32_t x, int32_t y, int32_t flags) } } - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); // Deselect tile for clients who had it selected rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { windowTileInspectorSelectedIndex = -1; - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_any_base_height_offset(int32_t x, int32_t y, int16_t elementIndex, int8_t heightOffset, int32_t flags) +GameActionResult::Ptr tile_inspector_any_base_height_offset( + CoordsXY loc, int16_t elementIndex, int8_t heightOffset, bool isExecuting) { - TileElement* const tileElement = map_get_nth_element_at(x, y, elementIndex); + TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (tileElement == nullptr) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); int16_t newBaseHeight = (int16_t)tileElement->base_height + heightOffset; int16_t newClearanceHeight = (int16_t)tileElement->clearance_height + heightOffset; if (newBaseHeight < 0 || newBaseHeight > 0xff || newClearanceHeight < 0 || newClearanceHeight > 0xff) { - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { if (tileElement->GetType() == TILE_ELEMENT_TYPE_ENTRANCE) { @@ -437,81 +431,86 @@ int32_t tile_inspector_any_base_height_offset(int32_t x, int32_t y, int16_t elem if (entranceType != ENTRANCE_TYPE_PARK_ENTRANCE) { // Update the ride's known entrance or exit height - Ride* ride = get_ride(tileElement->AsEntrance()->GetRideIndex()); - uint8_t entranceIndex = tileElement->AsEntrance()->GetStationIndex(); - auto entrance = ride_get_entrance_location(ride, entranceIndex); - auto exit = ride_get_exit_location(ride, entranceIndex); - uint8_t z = tileElement->base_height; + auto ride = get_ride(tileElement->AsEntrance()->GetRideIndex()); + if (ride != nullptr) + { + auto entranceIndex = tileElement->AsEntrance()->GetStationIndex(); + auto entrance = ride_get_entrance_location(ride, entranceIndex); + auto exit = ride_get_exit_location(ride, entranceIndex); + uint8_t z = tileElement->base_height; - // Make sure this is the correct entrance or exit - if (entranceType == ENTRANCE_TYPE_RIDE_ENTRANCE && entrance.x == x && entrance.y == y && entrance.z == z) - ride_set_entrance_location( - ride, entranceIndex, { entrance.x, entrance.y, z + heightOffset, entrance.direction }); - else if (entranceType == ENTRANCE_TYPE_RIDE_EXIT && exit.x == x && exit.y == y && exit.z == z) - ride_set_exit_location(ride, entranceIndex, { exit.x, exit.y, z + heightOffset, exit.direction }); + // Make sure this is the correct entrance or exit + if (entranceType == ENTRANCE_TYPE_RIDE_ENTRANCE && entrance.x == loc.x / 32 && entrance.y == loc.y / 32 + && entrance.z == z) + ride_set_entrance_location( + ride, entranceIndex, { entrance.x, entrance.y, z + heightOffset, entrance.direction }); + else if ( + entranceType == ENTRANCE_TYPE_RIDE_EXIT && exit.x == loc.x / 32 && exit.y == loc.y / 32 && exit.z == z) + ride_set_exit_location(ride, entranceIndex, { exit.x, exit.y, z + heightOffset, exit.direction }); + } } } tileElement->base_height += heightOffset; tileElement->clearance_height += heightOffset; - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_surface_show_park_fences(int32_t x, int32_t y, bool showFences, int32_t flags) +GameActionResult::Ptr tile_inspector_surface_show_park_fences(CoordsXY loc, bool showFences, bool isExecuting) { - TileElement* const surfaceelement = map_get_surface_element_at(x, y); + auto* const surfaceelement = map_get_surface_element_at(loc); // No surface element on tile if (surfaceelement == nullptr) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { if (!showFences) - surfaceelement->AsSurface()->SetParkFences(0); + surfaceelement->SetParkFences(0); else - update_park_fences({ x << 5, y << 5 }); + update_park_fences(loc); - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_surface_toggle_corner(int32_t x, int32_t y, int32_t cornerIndex, int32_t flags) +GameActionResult::Ptr tile_inspector_surface_toggle_corner(CoordsXY loc, int32_t cornerIndex, bool isExecuting) { - TileElement* const surfaceElement = map_get_surface_element_at(x, y); + auto* const surfaceElement = map_get_surface_element_at(loc); // No surface element on tile if (surfaceElement == nullptr) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { - const uint8_t originalSlope = surfaceElement->AsSurface()->GetSlope(); + const uint8_t originalSlope = surfaceElement->GetSlope(); const bool diagonal = (originalSlope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) >> 4; - uint8_t newSlope = surfaceElement->AsSurface()->GetSlope() ^ (1 << cornerIndex); - surfaceElement->AsSurface()->SetSlope(newSlope); - if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) + uint8_t newSlope = surfaceElement->GetSlope() ^ (1 << cornerIndex); + surfaceElement->SetSlope(newSlope); + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) { surfaceElement->clearance_height = surfaceElement->base_height + 2; } @@ -521,7 +520,7 @@ int32_t tile_inspector_surface_toggle_corner(int32_t x, int32_t y, int32_t corne } // All corners are raised - if ((surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) == TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) + if ((surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) == TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) { uint8_t slope = TILE_ELEMENT_SLOPE_FLAT; @@ -543,43 +542,43 @@ int32_t tile_inspector_surface_toggle_corner(int32_t x, int32_t y, int32_t corne break; } } - surfaceElement->AsSurface()->SetSlope(slope); + surfaceElement->SetSlope(slope); // Update base and clearance heights surfaceElement->base_height += 2; surfaceElement->clearance_height = surfaceElement->base_height + (diagonal ? 2 : 0); } - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_surface_toggle_diagonal(int32_t x, int32_t y, int32_t flags) +GameActionResult::Ptr tile_inspector_surface_toggle_diagonal(CoordsXY loc, bool isExecuting) { - TileElement* const surfaceElement = map_get_surface_element_at(x, y); + auto* const surfaceElement = map_get_surface_element_at(loc); // No surface element on tile if (surfaceElement == nullptr) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { - uint8_t newSlope = surfaceElement->AsSurface()->GetSlope() ^ TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT; - surfaceElement->AsSurface()->SetSlope(newSlope); - if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + uint8_t newSlope = surfaceElement->GetSlope() ^ TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT; + surfaceElement->SetSlope(newSlope); + if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) { surfaceElement->clearance_height = surfaceElement->base_height + 4; } - else if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) + else if (surfaceElement->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) { surfaceElement->clearance_height = surfaceElement->base_height + 2; } @@ -588,81 +587,104 @@ int32_t tile_inspector_surface_toggle_diagonal(int32_t x, int32_t y, int32_t fla surfaceElement->clearance_height = surfaceElement->base_height; } - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_path_set_sloped(int32_t x, int32_t y, int32_t elementIndex, bool sloped, int32_t flags) +GameActionResult::Ptr tile_inspector_path_set_sloped(CoordsXY loc, int32_t elementIndex, bool sloped, bool isExecuting) { - TileElement* const pathElement = map_get_nth_element_at(x, y, elementIndex); + TileElement* const pathElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (pathElement == nullptr || pathElement->GetType() != TILE_ELEMENT_TYPE_PATH) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { pathElement->AsPath()->SetSloped(sloped); - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_path_toggle_edge(int32_t x, int32_t y, int32_t elementIndex, int32_t edgeIndex, int32_t flags) +GameActionResult::Ptr tile_inspector_path_set_broken(CoordsXY loc, int32_t elementIndex, bool broken, bool isExecuting) { - TileElement* const pathElement = map_get_nth_element_at(x, y, elementIndex); + TileElement* const pathElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (pathElement == nullptr || pathElement->GetType() != TILE_ELEMENT_TYPE_PATH) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) + { + pathElement->AsPath()->SetIsBroken(broken); + + map_invalidate_tile_full(loc.x, loc.y); + + rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) + { + tileInspectorWindow->Invalidate(); + } + } + + return std::make_unique(); +} + +GameActionResult::Ptr tile_inspector_path_toggle_edge(CoordsXY loc, int32_t elementIndex, int32_t edgeIndex, bool isExecuting) +{ + TileElement* const pathElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); + + if (pathElement == nullptr || pathElement->GetType() != TILE_ELEMENT_TYPE_PATH) + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); + + if (isExecuting) { uint8_t newEdges = pathElement->AsPath()->GetEdgesAndCorners() ^ (1 << edgeIndex); pathElement->AsPath()->SetEdgesAndCorners(newEdges); - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_entrance_make_usable(int32_t x, int32_t y, int32_t elementIndex, int32_t flags) +GameActionResult::Ptr tile_inspector_entrance_make_usable(CoordsXY loc, int32_t elementIndex, bool isExecuting) { - TileElement* const entranceElement = map_get_nth_element_at(x, y, elementIndex); + TileElement* const entranceElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (entranceElement == nullptr || entranceElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) - return MONEY32_UNDEFINED; - - Ride* ride = get_ride(entranceElement->AsEntrance()->GetRideIndex()); + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); + auto ride = get_ride(entranceElement->AsEntrance()->GetRideIndex()); if (ride == nullptr) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { uint8_t stationIndex = entranceElement->AsEntrance()->GetStationIndex(); @@ -670,130 +692,108 @@ int32_t tile_inspector_entrance_make_usable(int32_t x, int32_t y, int32_t elemen { case ENTRANCE_TYPE_RIDE_ENTRANCE: ride_set_entrance_location( - ride, stationIndex, { x, y, entranceElement->base_height, (uint8_t)entranceElement->GetDirection() }); + ride, stationIndex, + { loc.x / 32, loc.y / 32, entranceElement->base_height, (uint8_t)entranceElement->GetDirection() }); break; case ENTRANCE_TYPE_RIDE_EXIT: ride_set_exit_location( - ride, stationIndex, { x, y, entranceElement->base_height, (uint8_t)entranceElement->GetDirection() }); + ride, stationIndex, + { loc.x / 32, loc.y / 32, entranceElement->base_height, (uint8_t)entranceElement->GetDirection() }); break; } rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_wall_set_slope(int32_t x, int32_t y, int32_t elementIndex, int32_t slopeValue, int32_t flags) +GameActionResult::Ptr tile_inspector_wall_set_slope(CoordsXY loc, int32_t elementIndex, int32_t slopeValue, bool isExecuting) { - TileElement* const wallElement = map_get_nth_element_at(x, y, elementIndex); + TileElement* const wallElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (wallElement == nullptr || wallElement->GetType() != TILE_ELEMENT_TYPE_WALL) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { // Set new slope value wallElement->AsWall()->SetSlope(slopeValue); - map_invalidate_tile_full(x << 5, y << 5); + map_invalidate_tile_full(loc.x, loc.y); rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); - if (tileInspectorWindow != nullptr && (uint32_t)x == windowTileInspectorTileX - && (uint32_t)y == windowTileInspectorTileY) + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { - window_invalidate(tileInspectorWindow); + tileInspectorWindow->Invalidate(); } } - return 0; + return std::make_unique(); } // Changes the height of all track elements that belong to the same track piece // Broxzier: Copied from track_remove and stripped of unneeded code, but I think this should be smaller -int32_t tile_inspector_track_base_height_offset(int32_t x, int32_t y, int32_t elementIndex, int8_t offset, int32_t flags) +GameActionResult::Ptr tile_inspector_track_base_height_offset( + CoordsXY loc, int32_t elementIndex, int8_t offset, bool isExecuting) { - TileElement* const trackElement = map_get_nth_element_at(x, y, elementIndex); - if (offset == 0) - return 0; + return std::make_unique(); + + TileElement* const trackElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (trackElement == nullptr || trackElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { uint8_t type = trackElement->AsTrack()->GetTrackType(); - int16_t originX = x << 5; - int16_t originY = y << 5; + int16_t originX = loc.x; + int16_t originY = loc.y; int16_t originZ = trackElement->base_height * 8; uint8_t rotation = trackElement->GetDirection(); - ride_id_t rideIndex = trackElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - const rct_preview_track* trackBlock = get_track_def_from_ride(ride, type); + auto rideIndex = trackElement->AsTrack()->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); + + auto trackBlock = get_track_def_from_ride(ride, type); trackBlock += trackElement->AsTrack()->GetSequenceIndex(); uint8_t originDirection = trackElement->GetDirection(); - switch (originDirection) - { - case 0: - originX -= trackBlock->x; - originY -= trackBlock->y; - break; - case 1: - originX -= trackBlock->y; - originY += trackBlock->x; - break; - case 2: - originX += trackBlock->x; - originY += trackBlock->y; - break; - case 3: - originX += trackBlock->y; - originY -= trackBlock->x; - break; - } + CoordsXY offsets = { trackBlock->x, trackBlock->y }; + CoordsXY coords = { originX, originY }; + coords += offsets.Rotate(direction_reverse(originDirection)); + originX = (int16_t)coords.x; + originY = (int16_t)coords.y; originZ -= trackBlock->z; trackBlock = get_track_def_from_ride(ride, type); for (; trackBlock->index != 255; trackBlock++) { - int16_t elemX = originX, elemY = originY, elemZ = originZ; - - switch (originDirection) - { - case 0: - elemX += trackBlock->x; - elemY += trackBlock->y; - break; - case 1: - elemX += trackBlock->y; - elemY -= trackBlock->x; - break; - case 2: - elemX -= trackBlock->x; - elemY -= trackBlock->y; - break; - case 3: - elemX -= trackBlock->y; - elemY += trackBlock->x; - break; - } - + CoordsXY elem = { originX, originY }; + int16_t elemZ = originZ; + offsets.x = trackBlock->x; + offsets.y = trackBlock->y; + elem += offsets.Rotate(originDirection); elemZ += trackBlock->z; - map_invalidate_tile_full(elemX, elemY); + map_invalidate_tile_full(elem.x, elem.y); bool found = false; - TileElement* tileElement = map_get_first_element_at(elemX >> 5, elemY >> 5); + TileElement* tileElement = map_get_first_element_at(elem.x >> 5, elem.y >> 5); do { + if (tileElement == nullptr) + break; + if (tileElement->base_height != elemZ / 8) continue; @@ -816,13 +816,12 @@ int32_t tile_inspector_track_base_height_offset(int32_t x, int32_t y, int32_t el if (!found) { log_error("Track map element part not found!"); - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } // track_remove returns here on failure, not sure when this would ever be hit. Only thing I can think of is for when // you decrease the map size. - openrct2_assert( - map_get_surface_element_at({ elemX, elemY }) != nullptr, "No surface at %d,%d", elemX >> 5, elemY >> 5); + openrct2_assert(map_get_surface_element_at(elem) != nullptr, "No surface at %d,%d", elem.x >> 5, elem.y >> 5); // Keep? // invalidate_test_results(ride); @@ -835,20 +834,20 @@ int32_t tile_inspector_track_base_height_offset(int32_t x, int32_t y, int32_t el // TODO: Only invalidate when one of the affected tiles is selected window_invalidate_by_class(WC_TILE_INSPECTOR); - return 0; + return std::make_unique(); } // Sets chainlift, optionally for an entire track block // Broxzier: Basically a copy of the above function, with just two different lines... should probably be combined somehow -int32_t tile_inspector_track_set_chain( - int32_t x, int32_t y, int32_t elementIndex, bool entireTrackBlock, bool setChain, int32_t flags) +GameActionResult::Ptr tile_inspector_track_set_chain( + CoordsXY loc, int32_t elementIndex, bool entireTrackBlock, bool setChain, bool isExecuting) { - TileElement* const trackElement = map_get_nth_element_at(x, y, elementIndex); + TileElement* const trackElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (trackElement == nullptr || trackElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { if (!entireTrackBlock) { @@ -858,75 +857,50 @@ int32_t tile_inspector_track_set_chain( trackElement->AsTrack()->SetHasChain(setChain); } - return 0; + return std::make_unique(); } uint8_t type = trackElement->AsTrack()->GetTrackType(); - int16_t originX = x << 5; - int16_t originY = y << 5; + int16_t originX = loc.x; + int16_t originY = loc.y; int16_t originZ = trackElement->base_height * 8; uint8_t rotation = trackElement->GetDirection(); - ride_id_t rideIndex = trackElement->AsTrack()->GetRideIndex(); - Ride* ride = get_ride(rideIndex); - const rct_preview_track* trackBlock = get_track_def_from_ride(ride, type); + auto rideIndex = trackElement->AsTrack()->GetRideIndex(); + auto ride = get_ride(rideIndex); + if (ride == nullptr) + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); + + auto trackBlock = get_track_def_from_ride(ride, type); trackBlock += trackElement->AsTrack()->GetSequenceIndex(); uint8_t originDirection = trackElement->GetDirection(); - switch (originDirection) - { - case 0: - originX -= trackBlock->x; - originY -= trackBlock->y; - break; - case 1: - originX -= trackBlock->y; - originY += trackBlock->x; - break; - case 2: - originX += trackBlock->x; - originY += trackBlock->y; - break; - case 3: - originX += trackBlock->y; - originY -= trackBlock->x; - break; - } + CoordsXY offsets = { trackBlock->x, trackBlock->y }; + CoordsXY coords = { originX, originY }; + coords += offsets.Rotate(direction_reverse(originDirection)); + originX = (int16_t)coords.x; + originY = (int16_t)coords.y; originZ -= trackBlock->z; trackBlock = get_track_def_from_ride(ride, type); for (; trackBlock->index != 255; trackBlock++) { - int16_t elemX = originX, elemY = originY, elemZ = originZ; - - switch (originDirection) - { - case 0: - elemX += trackBlock->x; - elemY += trackBlock->y; - break; - case 1: - elemX += trackBlock->y; - elemY -= trackBlock->x; - break; - case 2: - elemX -= trackBlock->x; - elemY -= trackBlock->y; - break; - case 3: - elemX -= trackBlock->y; - elemY += trackBlock->x; - break; - } - + CoordsXY elem = { originX, originY }; + int16_t elemZ = originZ; + offsets.x = trackBlock->x; + offsets.y = trackBlock->y; + elem += offsets.Rotate(originDirection); elemZ += trackBlock->z; - map_invalidate_tile_full(elemX, elemY); + map_invalidate_tile_full(elem.x, elem.y); bool found = false; - TileElement* tileElement = map_get_first_element_at(elemX >> 5, elemY >> 5); + TileElement* tileElement = map_get_first_element_at(elem.x >> 5, elem.y >> 5); do { + if (tileElement == nullptr) + break; + if (tileElement->base_height != elemZ / 8) continue; @@ -949,13 +923,12 @@ int32_t tile_inspector_track_set_chain( if (!found) { log_error("Track map element part not found!"); - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } // track_remove returns here on failure, not sure when this would ever be hit. Only thing I can think of is for when // you decrease the map size. - openrct2_assert( - map_get_surface_element_at({ elemX, elemY }) != nullptr, "No surface at %d,%d", elemX >> 5, elemY >> 5); + openrct2_assert(map_get_surface_element_at(elem) != nullptr, "No surface at %d,%d", elem.x >> 5, elem.y >> 5); // Keep? // invalidate_test_results(ride); @@ -970,100 +943,152 @@ int32_t tile_inspector_track_set_chain( // TODO: Only invalidate when one of the affected tiles is selected window_invalidate_by_class(WC_TILE_INSPECTOR); - return 0; + return std::make_unique(); } -int32_t tile_inspector_scenery_set_quarter_location( - int32_t x, int32_t y, int32_t elementIndex, int32_t quarterIndex, int32_t flags) +GameActionResult::Ptr tile_inspector_track_set_block_brake( + CoordsXY loc, int32_t elementIndex, bool blockBrake, bool isExecuting) { - TileElement* const tileElement = map_get_nth_element_at(x, y, elementIndex); + TileElement* const trackElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); + + if (trackElement == nullptr || trackElement->GetType() != TILE_ELEMENT_TYPE_TRACK) + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); + + if (isExecuting) + { + trackElement->AsTrack()->SetBlockBrakeClosed(blockBrake); + + map_invalidate_tile_full(loc.x, loc.y); + + rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) + { + tileInspectorWindow->Invalidate(); + } + } + + return std::make_unique(); +} + +GameActionResult::Ptr tile_inspector_track_set_indestructible( + CoordsXY loc, int32_t elementIndex, bool isIndestructible, bool isExecuting) +{ + TileElement* const trackElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); + + if (trackElement == nullptr || trackElement->GetType() != TILE_ELEMENT_TYPE_TRACK) + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); + + if (isExecuting) + { + trackElement->AsTrack()->SetIsIndestructible(isIndestructible); + + map_invalidate_tile_full(loc.x, loc.y); + + rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR); + if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX + && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) + { + tileInspectorWindow->Invalidate(); + } + } + + return std::make_unique(); +} + +GameActionResult::Ptr tile_inspector_scenery_set_quarter_location( + CoordsXY loc, int32_t elementIndex, int32_t quarterIndex, bool isExecuting) +{ + TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (tileElement == nullptr || tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { // Set quadrant index tileElement->AsSmallScenery()->SetSceneryQuadrant(quarterIndex); // Update collision - tileElement->flags &= 0xF0; - tileElement->flags |= 1 << ((quarterIndex + 2) & 3); + tileElement->SetOccupiedQuadrants(1 << ((quarterIndex + 2) & 3)); - map_invalidate_tile_full(x << 5, y << 5); - if ((uint32_t)x == windowTileInspectorTileX && (uint32_t)y == windowTileInspectorTileY) + map_invalidate_tile_full(loc.x, loc.y); + if ((uint32_t)(loc.x / 32) == windowTileInspectorTileX && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { window_invalidate_by_class(WC_TILE_INSPECTOR); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_scenery_set_quarter_collision( - int32_t x, int32_t y, int32_t elementIndex, int32_t quarterIndex, int32_t flags) +GameActionResult::Ptr tile_inspector_scenery_set_quarter_collision( + CoordsXY loc, int32_t elementIndex, int32_t quarterIndex, bool isExecuting) { - TileElement* const tileElement = map_get_nth_element_at(x, y, elementIndex); + TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (tileElement == nullptr || tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { - tileElement->flags ^= 1 << quarterIndex; + auto occupiedQuadrants = tileElement->GetOccupiedQuadrants(); + occupiedQuadrants ^= 1 << quarterIndex; + tileElement->SetOccupiedQuadrants(occupiedQuadrants); - map_invalidate_tile_full(x << 5, y << 5); - if ((uint32_t)x == windowTileInspectorTileX && (uint32_t)y == windowTileInspectorTileY) + map_invalidate_tile_full(loc.x, loc.y); + if ((uint32_t)(loc.x / 32) == windowTileInspectorTileX && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { window_invalidate_by_class(WC_TILE_INSPECTOR); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_banner_toggle_blocking_edge(int32_t x, int32_t y, int32_t elementIndex, int32_t edgeIndex, int32_t flags) +GameActionResult::Ptr tile_inspector_banner_toggle_blocking_edge( + CoordsXY loc, int32_t elementIndex, int32_t edgeIndex, bool isExecuting) { - TileElement* const bannerElement = map_get_nth_element_at(x, y, elementIndex); + TileElement* const bannerElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (bannerElement == nullptr || bannerElement->GetType() != TILE_ELEMENT_TYPE_BANNER) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { uint8_t edges = bannerElement->AsBanner()->GetAllowedEdges(); edges ^= (1 << edgeIndex); bannerElement->AsBanner()->SetAllowedEdges(edges); - if ((uint32_t)x == windowTileInspectorTileX && (uint32_t)y == windowTileInspectorTileY) + if ((uint32_t)(loc.x / 32) == windowTileInspectorTileX && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { window_invalidate_by_class(WC_TILE_INSPECTOR); } } - return 0; + return std::make_unique(); } -int32_t tile_inspector_corrupt_clamp(int32_t x, int32_t y, int32_t elementIndex, int32_t flags) +GameActionResult::Ptr tile_inspector_corrupt_clamp(CoordsXY loc, int32_t elementIndex, bool isExecuting) { - TileElement* const corruptElement = map_get_nth_element_at(x, y, elementIndex); + TileElement* const corruptElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (corruptElement == nullptr || corruptElement->GetType() != TILE_ELEMENT_TYPE_CORRUPT) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); if (corruptElement->IsLastForTile()) - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); - if (flags & GAME_COMMAND_FLAG_APPLY) + if (isExecuting) { TileElement* const nextElement = corruptElement + 1; corruptElement->base_height = corruptElement->clearance_height = nextElement->base_height; - if ((uint32_t)x == windowTileInspectorTileX && (uint32_t)y == windowTileInspectorTileY) + if ((uint32_t)(loc.x / 32) == windowTileInspectorTileX && (uint32_t)(loc.y / 32) == windowTileInspectorTileY) { window_invalidate_by_class(WC_TILE_INSPECTOR); } } - return 0; + return std::make_unique(); } diff --git a/src/openrct2/world/TileInspector.h b/src/openrct2/world/TileInspector.h index 426a7617fe..05a24484c9 100644 --- a/src/openrct2/world/TileInspector.h +++ b/src/openrct2/world/TileInspector.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -26,51 +26,35 @@ enum TILE_INSPECTOR_ELEMENT_TYPE TILE_INSPECTOR_ELEMENT_CORRUPT, }; -enum TILE_INSPECTOR_INSTRUCTION_TYPE -{ - TILE_INSPECTOR_ANY_REMOVE, - TILE_INSPECTOR_ANY_SWAP, - TILE_INSPECTOR_ANY_INSERT_CORRUPT, - TILE_INSPECTOR_ANY_ROTATE, - TILE_INSPECTOR_ANY_PASTE, - TILE_INSPECTOR_ANY_SORT, - TILE_INSPECTOR_ANY_BASE_HEIGHT_OFFSET, - TILE_INSPECTOR_SURFACE_SHOW_PARK_FENCES, - TILE_INSPECTOR_SURFACE_TOGGLE_CORNER, - TILE_INSPECTOR_SURFACE_TOGGLE_DIAGONAL, - TILE_INSPECTOR_PATH_SET_SLOPE, - TILE_INSPECTOR_PATH_TOGGLE_EDGE, - TILE_INSPECTOR_ENTRANCE_MAKE_USABLE, - TILE_INSPECTOR_WALL_SET_SLOPE, - TILE_INSPECTOR_TRACK_BASE_HEIGHT_OFFSET, - TILE_INSPECTOR_TRACK_SET_CHAIN, - TILE_INSPECTOR_SCENERY_SET_QUARTER_LOCATION, - TILE_INSPECTOR_SCENERY_SET_QUARTER_COLLISION, - TILE_INSPECTOR_BANNER_TOGGLE_BLOCKING_EDGE, - TILE_INSPECTOR_CORRUPT_CLAMP, -}; - -int32_t tile_inspector_insert_corrupt_at(int32_t x, int32_t y, int16_t elementIndex, int32_t flags); -int32_t tile_inspector_remove_element_at(int32_t x, int32_t y, int16_t elementIndex, int32_t flags); -int32_t tile_inspector_swap_elements_at(int32_t x, int32_t y, int16_t first, int16_t second, int32_t flags); -int32_t tile_inspector_rotate_element_at(int32_t x, int32_t y, int32_t elementIndex, int32_t flags); -int32_t tile_inspector_paste_element_at(int32_t x, int32_t y, TileElement element, int32_t flags); -int32_t tile_inspector_sort_elements_at(int32_t x, int32_t y, int32_t flags); -int32_t tile_inspector_any_base_height_offset(int32_t x, int32_t y, int16_t elementIndex, int8_t heightOffset, int32_t flags); -int32_t tile_inspector_surface_show_park_fences(int32_t x, int32_t y, bool enabled, int32_t flags); -int32_t tile_inspector_surface_toggle_corner(int32_t x, int32_t y, int32_t cornerIndex, int32_t flags); -int32_t tile_inspector_surface_toggle_diagonal(int32_t x, int32_t y, int32_t flags); -int32_t tile_inspector_path_set_sloped(int32_t x, int32_t y, int32_t elementIndex, bool sloped, int32_t flags); -int32_t tile_inspector_path_toggle_edge(int32_t x, int32_t y, int32_t elementIndex, int32_t cornerIndex, int32_t flags); -int32_t tile_inspector_entrance_make_usable(int32_t x, int32_t y, int32_t elementIndex, int32_t flags); -int32_t tile_inspector_wall_set_slope(int32_t x, int32_t y, int32_t elementIndex, int32_t slopeValue, int32_t flags); -int32_t tile_inspector_track_base_height_offset(int32_t x, int32_t y, int32_t elementIndex, int8_t offset, int32_t flags); -int32_t tile_inspector_track_set_chain( - int32_t x, int32_t y, int32_t elementIndex, bool entireTrackBlock, bool setChain, int32_t flags); -int32_t tile_inspector_scenery_set_quarter_location( - int32_t x, int32_t y, int32_t elementIndex, int32_t quarterIndex, int32_t flags); -int32_t tile_inspector_scenery_set_quarter_collision( - int32_t x, int32_t y, int32_t elementIndex, int32_t quarterIndex, int32_t flags); -int32_t tile_inspector_banner_toggle_blocking_edge( - int32_t x, int32_t y, int32_t elementIndex, int32_t edgeIndex, int32_t flags); -int32_t tile_inspector_corrupt_clamp(int32_t x, int32_t y, int32_t elementIndex, int32_t flags); +class GameActionResult; +using GameActionResultPtr = std::unique_ptr; +GameActionResultPtr tile_inspector_insert_corrupt_at(CoordsXY loc, int16_t elementIndex, bool isExecuting); +GameActionResultPtr tile_inspector_remove_element_at(CoordsXY loc, int16_t elementIndex, bool isExecuting); +GameActionResultPtr tile_inspector_swap_elements_at(CoordsXY loc, int16_t first, int16_t second, bool isExecuting); +GameActionResultPtr tile_inspector_rotate_element_at(CoordsXY loc, int32_t elementIndex, bool isExecuting); +GameActionResultPtr tile_inspector_paste_element_at(CoordsXY loc, TileElement element, bool isExecuting); +GameActionResultPtr tile_inspector_sort_elements_at(CoordsXY loc, bool isExecuting); +GameActionResultPtr tile_inspector_any_base_height_offset( + CoordsXY loc, int16_t elementIndex, int8_t heightOffset, bool isExecuting); +GameActionResultPtr tile_inspector_surface_show_park_fences(CoordsXY loc, bool enabled, bool isExecuting); +GameActionResultPtr tile_inspector_surface_toggle_corner(CoordsXY loc, int32_t cornerIndex, bool isExecuting); +GameActionResultPtr tile_inspector_surface_toggle_diagonal(CoordsXY loc, bool isExecuting); +GameActionResultPtr tile_inspector_path_set_sloped(CoordsXY loc, int32_t elementIndex, bool sloped, bool isExecuting); +GameActionResultPtr tile_inspector_path_set_broken(CoordsXY loc, int32_t elementIndex, bool broken, bool isExecuting); +GameActionResultPtr tile_inspector_path_toggle_edge(CoordsXY loc, int32_t elementIndex, int32_t cornerIndex, bool isExecuting); +GameActionResultPtr tile_inspector_entrance_make_usable(CoordsXY loc, int32_t elementIndex, bool isExecuting); +GameActionResultPtr tile_inspector_wall_set_slope(CoordsXY loc, int32_t elementIndex, int32_t slopeValue, bool isExecuting); +GameActionResultPtr tile_inspector_track_base_height_offset( + CoordsXY loc, int32_t elementIndex, int8_t offset, bool isExecuting); +GameActionResultPtr tile_inspector_track_set_block_brake(CoordsXY loc, int32_t elementIndex, bool blockBrake, bool isExecuting); +GameActionResultPtr tile_inspector_track_set_indestructible( + CoordsXY loc, int32_t elementIndex, bool isIndestructible, bool isExecuting); +GameActionResultPtr tile_inspector_track_set_chain( + CoordsXY loc, int32_t elementIndex, bool entireTrackBlock, bool setChain, bool isExecuting); +GameActionResultPtr tile_inspector_scenery_set_quarter_location( + CoordsXY loc, int32_t elementIndex, int32_t quarterIndex, bool isExecuting); +GameActionResultPtr tile_inspector_scenery_set_quarter_collision( + CoordsXY loc, int32_t elementIndex, int32_t quarterIndex, bool isExecuting); +GameActionResultPtr tile_inspector_banner_toggle_blocking_edge( + CoordsXY loc, int32_t elementIndex, int32_t edgeIndex, bool isExecuting); +GameActionResultPtr tile_inspector_corrupt_clamp(CoordsXY loc, int32_t elementIndex, bool isExecuting); diff --git a/src/openrct2/world/Wall.cpp b/src/openrct2/world/Wall.cpp index 44606ae9ea..67aec95153 100644 --- a/src/openrct2/world/Wall.cpp +++ b/src/openrct2/world/Wall.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -29,575 +29,6 @@ #include "Surface.h" #include "Wall.h" -/** - * Gets whether the given track type can have a wall placed on the edge of the given direction. - * Some thin tracks for example are allowed to have walls either side of the track, but wider tracks can not. - */ -static bool TrackIsAllowedWallEdges(uint8_t rideType, uint8_t trackType, uint8_t trackSequence, uint8_t direction) -{ - if (!ride_type_has_flag(rideType, RIDE_TYPE_FLAG_TRACK_NO_WALLS)) - { - if (ride_type_has_flag(rideType, RIDE_TYPE_FLAG_FLAT_RIDE)) - { - if (FlatRideTrackSequenceElementAllowedWallEdges[trackType][trackSequence] & (1 << direction)) - { - return true; - } - } - else - { - if (TrackSequenceElementAllowedWallEdges[trackType][trackSequence] & (1 << direction)) - { - return true; - } - } - } - return false; -} - -/** - * - * rct2: 0x006E5CBA - */ -static bool WallCheckObstructionWithTrack( - rct_scenery_entry* wall, int32_t z0, int32_t edge, TileElement* trackElement, bool* wallAcrossTrack) -{ - int32_t trackType = trackElement->AsTrack()->GetTrackType(); - int32_t sequence = trackElement->AsTrack()->GetSequenceIndex(); - int32_t direction = (edge - trackElement->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK; - Ride* ride = get_ride(trackElement->AsTrack()->GetRideIndex()); - - if (TrackIsAllowedWallEdges(ride->type, trackType, sequence, direction)) - { - return true; - } - - if (!(wall->wall.flags & WALL_SCENERY_IS_DOOR)) - { - return false; - } - - if (RideGroupManager::RideTypeHasRideGroups(ride->type)) - { - auto rideGroup = RideGroupManager::GetRideGroup(ride->type, get_ride_entry(ride->subtype)); - if (!(rideGroup->Flags & RIDE_GROUP_FLAG_ALLOW_DOORS_ON_TRACK)) - { - return false; - } - } - else if (!(RideData4[ride->type].flags & RIDE_TYPE_FLAG4_ALLOW_DOORS_ON_TRACK)) - { - return false; - } - - *wallAcrossTrack = true; - if (z0 & 1) - { - return false; - } - - int32_t z; - if (sequence == 0) - { - if (TrackSequenceProperties[trackType][0] & TRACK_SEQUENCE_FLAG_DISALLOW_DOORS) - { - return false; - } - - if (TrackDefinitions[trackType].bank_start == 0) - { - if (!(TrackCoordinates[trackType].rotation_begin & 4)) - { - direction = trackElement->GetDirectionWithOffset(2); - if (direction == edge) - { - const rct_preview_track* trackBlock = &TrackBlocks[trackType][sequence]; - z = TrackCoordinates[trackType].z_begin; - z = trackElement->base_height + ((z - trackBlock->z) * 8); - if (z == z0) - { - return true; - } - } - } - } - } - - const rct_preview_track* trackBlock = &TrackBlocks[trackType][sequence + 1]; - if (trackBlock->index != 0xFF) - { - return false; - } - - if (TrackDefinitions[trackType].bank_end != 0) - { - return false; - } - - direction = TrackCoordinates[trackType].rotation_end; - if (direction & 4) - { - return false; - } - - direction = trackElement->GetDirection(); - if (direction != edge) - { - return false; - } - - trackBlock = &TrackBlocks[trackType][sequence]; - z = TrackCoordinates[trackType].z_end; - z = trackElement->base_height + ((z - trackBlock->z) * 8); - return z == z0; -} - -/** - * - * rct2: 0x006E5C1A - */ -static bool WallCheckObstruction( - rct_scenery_entry* wall, int32_t x, int32_t y, int32_t z0, int32_t z1, int32_t edge, bool* wallAcrossTrack) -{ - int32_t entryType, sequence; - rct_scenery_entry* entry; - rct_large_scenery_tile* tile; - - *wallAcrossTrack = false; - gMapGroundFlags = ELEMENT_IS_ABOVE_GROUND; - if (map_is_location_at_edge(x, y)) - { - gGameCommandErrorText = STR_OFF_EDGE_OF_MAP; - return false; - } - - TileElement* tileElement = map_get_first_element_at(x / 32, y / 32); - do - { - int32_t elementType = tileElement->GetType(); - if (elementType == TILE_ELEMENT_TYPE_SURFACE) - continue; - if (z0 >= tileElement->clearance_height) - continue; - if (z1 <= tileElement->base_height) - continue; - if (elementType == TILE_ELEMENT_TYPE_WALL) - { - int32_t direction = tileElement->GetDirection(); - if (edge == direction) - { - map_obstruction_set_error_text(tileElement); - return false; - } - continue; - } - if ((tileElement->flags & 0x0F) == 0) - continue; - - switch (elementType) - { - case TILE_ELEMENT_TYPE_ENTRANCE: - map_obstruction_set_error_text(tileElement); - return false; - case TILE_ELEMENT_TYPE_PATH: - if (tileElement->AsPath()->GetEdges() & (1 << edge)) - { - map_obstruction_set_error_text(tileElement); - return false; - } - break; - case TILE_ELEMENT_TYPE_LARGE_SCENERY: - entryType = tileElement->AsLargeScenery()->GetEntryIndex(); - sequence = tileElement->AsLargeScenery()->GetSequenceIndex(); - entry = get_large_scenery_entry(entryType); - tile = &entry->large_scenery.tiles[sequence]; - { - int32_t direction = ((edge - tileElement->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK) + 8; - if (!(tile->flags & (1 << direction))) - { - map_obstruction_set_error_text(tileElement); - return false; - } - } - break; - case TILE_ELEMENT_TYPE_SMALL_SCENERY: - entry = tileElement->AsSmallScenery()->GetEntry(); - if (scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_NO_WALLS)) - { - map_obstruction_set_error_text(tileElement); - return false; - } - break; - case TILE_ELEMENT_TYPE_TRACK: - if (!WallCheckObstructionWithTrack(wall, z0, edge, tileElement, wallAcrossTrack)) - { - return false; - } - break; - } - } while (!(tileElement++)->IsLastForTile()); - - return true; -} - -#pragma region Edge Slopes Table - -// clang-format off -enum EDGE_SLOPE -{ - EDGE_SLOPE_ELEVATED = (1 << 0), // 0x01 - EDGE_SLOPE_UPWARDS = (1 << 6), // 0x40 - EDGE_SLOPE_DOWNWARDS = (1 << 7), // 0x80 - - EDGE_SLOPE_UPWARDS_ELEVATED = EDGE_SLOPE_UPWARDS | EDGE_SLOPE_ELEVATED, - EDGE_SLOPE_DOWNWARDS_ELEVATED = EDGE_SLOPE_DOWNWARDS | EDGE_SLOPE_ELEVATED, -}; - -/** rct2: 0x009A3FEC */ -static constexpr const uint8_t EdgeSlopes[][4] = { -// Top right Bottom right Bottom left Top left - { 0, 0, 0, 0 }, - { 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, 0 }, - { 0, 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS }, - { 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS }, - { EDGE_SLOPE_DOWNWARDS, 0, 0, EDGE_SLOPE_UPWARDS }, - { EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS }, - { EDGE_SLOPE_DOWNWARDS, 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED }, - { EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED }, - { EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, 0, 0 }, - { EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, 0 }, - { EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS }, - { EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS }, - { EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, 0, EDGE_SLOPE_UPWARDS }, - { EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS }, - { EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED }, - { EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED }, - { 0, 0, 0, 0 }, - { 0, 0, 0, 0 }, - { 0, 0, 0, 0 }, - { 0, 0, 0, 0 }, - { 0, 0, 0, 0 }, - { 0, 0, 0, 0 }, - { 0, 0, 0, 0 }, - { EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED }, - { 0, 0, 0, 0 }, - { 0, 0, 0, 0 }, - { 0, 0, 0, 0 }, - { EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS }, - { 0, 0, 0, 0 }, - { EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS }, - { EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED }, - { 0, 0, 0, 0 }, -}; -// clang-format on - -#pragma endregion - -static money32 WallPlace( - uint8_t wallType, int16_t x, int16_t y, int16_t z, uint8_t edge, uint8_t primaryColour, uint8_t secondaryColour, - uint8_t tertiaryColour, uint8_t flags) -{ - LocationXYZ16 position = { x, y, z }; - - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - gCommandPosition.x = position.x + 16; - gCommandPosition.y = position.y + 16; - gCommandPosition.z = position.z; - - if (position.z == 0) - { - gCommandPosition.z = tile_element_height(position.x, position.y) & 0xFFFF; - } - - if (game_is_paused() && !gCheatsBuildInPauseMode) - { - gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - return MONEY32_UNDEFINED; - } - - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(flags & GAME_COMMAND_FLAG_PATH_SCENERY) && !gCheatsSandboxMode) - { - if (position.z == 0) - { - if (!map_is_location_in_park({ position.x, position.y })) - { - return MONEY32_UNDEFINED; - } - } - else if (!map_is_location_owned(position.x, position.y, position.z)) - { - return MONEY32_UNDEFINED; - } - } - - uint8_t edgeSlope = 0; - if (position.z == 0) - { - TileElement* surfaceElement = map_get_surface_element_at({ position.x, position.y }); - if (surfaceElement == nullptr) - { - return MONEY32_UNDEFINED; - } - position.z = surfaceElement->base_height * 8; - - uint8_t slope = surfaceElement->AsSurface()->GetSlope(); - edgeSlope = EdgeSlopes[slope][edge & 3]; - if (edgeSlope & EDGE_SLOPE_ELEVATED) - { - position.z += 16; - edgeSlope &= ~EDGE_SLOPE_ELEVATED; - } - } - - TileElement* surfaceElement = map_get_surface_element_at({ position.x, position.y }); - if (surfaceElement == nullptr) - { - return MONEY32_UNDEFINED; - } - - if (surfaceElement->AsSurface()->GetWaterHeight() > 0) - { - uint16_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight() * 16; - - if (position.z < waterHeight && !gCheatsDisableClearanceChecks) - { - gGameCommandErrorText = STR_CANT_BUILD_THIS_UNDERWATER; - return MONEY32_UNDEFINED; - } - } - - if (position.z / 8 < surfaceElement->base_height && !gCheatsDisableClearanceChecks) - { - gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND; - return MONEY32_UNDEFINED; - } - - if (!(edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS))) - { - uint8_t newEdge = (edge + 2) & 3; - uint8_t newBaseHeight = surfaceElement->base_height; - newBaseHeight += 2; - if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge)) - { - if (position.z / 8 < newBaseHeight) - { - gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND; - return MONEY32_UNDEFINED; - } - - if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) - { - newEdge = (newEdge - 1) & 3; - - if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge)) - { - newEdge = (newEdge + 2) & 3; - if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge)) - { - newBaseHeight += 2; - if (position.z / 8 < newBaseHeight) - { - gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND; - return MONEY32_UNDEFINED; - } - newBaseHeight -= 2; - } - } - } - } - - newEdge = (edge + 3) & 3; - if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge)) - { - if (position.z / 8 < newBaseHeight) - { - gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND; - return MONEY32_UNDEFINED; - } - - if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) - { - newEdge = (newEdge - 1) & 3; - - if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge)) - { - newEdge = (newEdge + 2) & 3; - if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge)) - { - newBaseHeight += 2; - if (position.z / 8 < newBaseHeight) - { - gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND; - return MONEY32_UNDEFINED; - } - } - } - } - } - } - BannerIndex bannerIndex = BANNER_INDEX_NULL; - rct_scenery_entry* wallEntry = get_wall_entry(wallType); - - if (wallEntry == nullptr) - { - return MONEY32_UNDEFINED; - } - - if (wallEntry->wall.scrolling_mode != SCROLLING_MODE_NONE) - { - bannerIndex = create_new_banner(flags); - - if (bannerIndex == 0xFF) - { - return MONEY32_UNDEFINED; - } - - rct_banner* banner = &gBanners[bannerIndex]; - if (flags & GAME_COMMAND_FLAG_APPLY) - { - banner->flags |= BANNER_FLAG_IS_WALL; - banner->type = 0; - banner->x = position.x / 32; - banner->y = position.y / 32; - - ride_id_t rideIndex = banner_get_closest_ride_index(position.x, position.y, position.z); - if (rideIndex != RIDE_ID_NULL) - { - banner->ride_index = rideIndex; - banner->flags |= BANNER_FLAG_LINKED_TO_RIDE; - } - } - } - - uint8_t clearanceHeight = position.z / 8; - if (edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS)) - { - if (wallEntry->wall.flags & WALL_SCENERY_CANT_BUILD_ON_SLOPE) - { - gGameCommandErrorText = STR_ERR_UNABLE_TO_BUILD_THIS_ON_SLOPE; - return MONEY32_UNDEFINED; - } - clearanceHeight += 2; - } - clearanceHeight += wallEntry->wall.height; - - bool wallAcrossTrack = false; - if (!(flags & GAME_COMMAND_FLAG_PATH_SCENERY) && !gCheatsDisableClearanceChecks) - { - if (!WallCheckObstruction(wallEntry, position.x, position.y, position.z / 8, clearanceHeight, edge, &wallAcrossTrack)) - { - return MONEY32_UNDEFINED; - } - } - - if (!map_check_free_elements_and_reorganise(1)) - { - return MONEY32_UNDEFINED; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST)) - { - LocationXYZ16 coord; - coord.x = position.x + 16; - coord.y = position.y + 16; - coord.z = tile_element_height(coord.x, coord.y); - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - - TileElement* tileElement = tile_element_insert(position.x / 32, position.y / 32, position.z / 8, 0); - assert(tileElement != nullptr); - - map_animation_create(MAP_ANIMATION_TYPE_WALL, position.x, position.y, position.z / 8); - - tileElement->clearance_height = clearanceHeight; - tileElement->SetType(TILE_ELEMENT_TYPE_WALL); - tileElement->SetDirection(edge); - // TODO: Normalise the edge slope code. - tileElement->AsWall()->SetSlope(edgeSlope >> 6); - - tileElement->AsWall()->SetPrimaryColour(primaryColour); - tileElement->AsWall()->SetSecondaryColour(secondaryColour); - - if (wallAcrossTrack) - { - tileElement->AsWall()->SetAcrossTrack(true); - } - - tileElement->AsWall()->SetEntryIndex(wallType); - if (bannerIndex != 0xFF) - { - tileElement->AsWall()->SetBannerIndex(bannerIndex); - } - - if (wallEntry->wall.flags & WALL_SCENERY_HAS_TERNARY_COLOUR) - { - tileElement->AsWall()->SetTertiaryColour(tertiaryColour); - } - - if (flags & GAME_COMMAND_FLAG_GHOST) - { - tileElement->SetGhost(true); - } - - gSceneryTileElement = tileElement; - map_invalidate_tile_zoom1(position.x, position.y, tileElement->base_height * 8, tileElement->base_height * 8 + 72); - } - - if (gParkFlags & PARK_FLAGS_NO_MONEY) - { - return 0; - } - else - { - return wallEntry->wall.price; - } -} - -static money32 WallSetColour( - int16_t x, int16_t y, uint8_t baseHeight, uint8_t direction, uint8_t primaryColour, uint8_t secondaryColour, - uint8_t tertiaryColour, uint8_t flags) -{ - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - int32_t z = baseHeight * 8; - - gCommandPosition.x = x + 16; - gCommandPosition.y = y + 16; - gCommandPosition.z = z; - - if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_in_park({ x, y }) && !gCheatsSandboxMode) - { - return MONEY32_UNDEFINED; - } - - TileElement* wallElement = map_get_wall_element_at(x, y, baseHeight, direction); - if (wallElement == nullptr) - { - return 0; - } - - if ((flags & GAME_COMMAND_FLAG_GHOST) && !(wallElement->IsGhost())) - { - return 0; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - rct_scenery_entry* scenery_entry = wallElement->AsWall()->GetEntry(); - wallElement->AsWall()->SetPrimaryColour(primaryColour); - wallElement->AsWall()->SetSecondaryColour(secondaryColour); - - if (scenery_entry->wall.flags & WALL_SCENERY_HAS_TERNARY_COLOUR) - { - wallElement->AsWall()->SetTertiaryColour(tertiaryColour); - } - map_invalidate_tile_zoom1(x, y, z, z + 72); - } - - return 0; -} - /** * * rct2: 0x006E588E @@ -610,6 +41,8 @@ void wall_remove_at(int32_t x, int32_t y, int32_t z0, int32_t z1) z1 /= 8; repeat: tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_WALL) @@ -644,6 +77,8 @@ void wall_remove_intersecting_walls(int32_t x, int32_t y, int32_t z0, int32_t z1 TileElement* tileElement; tileElement = map_get_first_element_at(x >> 5, y >> 5); + if (tileElement == nullptr) + return; do { if (tileElement->GetType() != TILE_ELEMENT_TYPE_WALL) @@ -662,46 +97,6 @@ void wall_remove_intersecting_walls(int32_t x, int32_t y, int32_t z0, int32_t z1 } while (!(tileElement++)->IsLastForTile()); } -/** - * - * rct2: 0x006E519A - */ -void game_command_place_wall( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp) -{ - *ebx = WallPlace( - (*ebx >> 8) & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edi & 0xFFFF, *edx & 0xFF, (*edx >> 8) & 0xFF, *ebp & 0xFF, - (*ebp >> 8) & 0xFF, *ebx & 0xFF); -} - -money32 wall_place( - int32_t type, int32_t x, int32_t y, int32_t z, int32_t edge, int32_t primaryColour, int32_t secondaryColour, - int32_t tertiaryColour, int32_t flags) -{ - int32_t eax = x; - int32_t ebx = flags | (type << 8); - int32_t ecx = y; - int32_t edx = edge | (primaryColour << 8); - int32_t esi = 0; - int32_t edi = z; - int32_t ebp = secondaryColour | (tertiaryColour << 8); - game_command_place_wall(&eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); - return ebx; -} - -/** - * - * rct2: 0x006E56B5 - */ -void game_command_set_wall_colour( - int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, [[maybe_unused]] int32_t* edi, - int32_t* ebp) -{ - *ebx = WallSetColour( - *eax & 0xFFFF, *ecx & 0xFFFF, (*edx >> 8) & 0xFF, *edx & 0xFF, (*ebx >> 8) & 0xFF, *ebp & 0xFF, (*ebp >> 8) & 0xFF, - *ebx & 0xFF); -} - uint8_t WallElement::GetSlope() const { return (type & TILE_ELEMENT_QUADRANT_MASK) >> 6; @@ -778,6 +173,11 @@ void WallElement::SetEntryIndex(uint8_t newIndex) entryIndex = newIndex; } +Banner* WallElement::GetBanner() const +{ + return ::GetBanner(GetBannerIndex()); +} + BannerIndex WallElement::GetBannerIndex() const { return banner_index; diff --git a/src/openrct2/world/Wall.h b/src/openrct2/world/Wall.h index 3b80c5baa0..5ed51fb252 100644 --- a/src/openrct2/world/Wall.h +++ b/src/openrct2/world/Wall.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/src/openrct2/world/Water.h b/src/openrct2/world/Water.h index 04094813b8..e15ba65b96 100644 --- a/src/openrct2/world/Water.h +++ b/src/openrct2/world/Water.h @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 diff --git a/test/testpaint/Compat.cpp b/test/testpaint/Compat.cpp index 829aa80eb8..c73715dc9d 100644 --- a/test/testpaint/Compat.cpp +++ b/test/testpaint/Compat.cpp @@ -118,9 +118,9 @@ void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t hei { } -Ride* get_ride(int index) +Ride* get_ride(ride_id_t index) { - if (index < 0 || index >= MAX_RIDES) + if (index >= RCT12_MAX_RIDES_IN_PARK) { log_error("invalid index %d for ride", index); return nullptr; @@ -139,14 +139,14 @@ rct_ride_entry* get_ride_entry(int index) return gRideEntries[index]; } -rct_ride_entry* get_ride_entry_by_ride(const Ride* ride) +rct_ride_entry* Ride::GetRideEntry() const { - rct_ride_entry* type = get_ride_entry(ride->subtype); - if (type == nullptr) + rct_ride_entry* rideEntry = get_ride_entry(subtype); + if (rideEntry == nullptr) { log_error("Invalid ride subtype for ride"); } - return type; + return rideEntry; } rct_sprite* get_sprite(size_t sprite_idx) @@ -160,6 +160,14 @@ bool TileElementBase::IsLastForTile() const return (this->flags & TILE_ELEMENT_FLAG_LAST_TILE) != 0; } +void TileElementBase::SetLastForTile(bool on) +{ + if (on) + flags |= TILE_ELEMENT_FLAG_LAST_TILE; + else + flags &= ~TILE_ELEMENT_FLAG_LAST_TILE; +} + uint8_t TileElementBase::GetType() const { return this->type & TILE_ELEMENT_TYPE_MASK; @@ -202,186 +210,188 @@ bool is_csg_loaded() uint8_t TrackElement::GetSeatRotation() const { - return colour >> 4; + return ColourScheme >> 4; } void TrackElement::SetSeatRotation(uint8_t newSeatRotation) { - colour &= 0x0F; - colour |= (newSeatRotation << 4); + ColourScheme &= ~TRACK_ELEMENT_COLOUR_SEAT_ROTATION_MASK; + ColourScheme |= (newSeatRotation << 4); } bool TrackElement::IsTakingPhoto() const { - return (sequence & MAP_ELEM_TRACK_SEQUENCE_TAKING_PHOTO_MASK) != 0; + return OnridePhotoBits != 0; } void TrackElement::SetPhotoTimeout() { - sequence &= MAP_ELEM_TRACK_SEQUENCE_SEQUENCE_MASK; - sequence |= (3 << 4); + OnridePhotoBits = 3; +} + +void TrackElement::SetPhotoTimeout(uint8_t value) +{ + OnridePhotoBits = value; +} + +uint8_t TrackElement::GetPhotoTimeout() const +{ + return OnridePhotoBits; } void TrackElement::DecrementPhotoTimeout() { - // We should only touch the upper 4 bits, avoid underflow into the lower 4. - if (sequence & MAP_ELEM_TRACK_SEQUENCE_TAKING_PHOTO_MASK) - { - sequence -= (1 << 4); - } + OnridePhotoBits = std::max(0, OnridePhotoBits - 1); } uint16_t TrackElement::GetMazeEntry() const { - return mazeEntry; + return MazeEntry; } void TrackElement::SetMazeEntry(uint16_t newMazeEntry) { - mazeEntry = newMazeEntry; + MazeEntry = newMazeEntry; } void TrackElement::MazeEntryAdd(uint16_t addVal) { - mazeEntry |= addVal; + MazeEntry |= addVal; } void TrackElement::MazeEntrySubtract(uint16_t subVal) { - mazeEntry &= ~subVal; + MazeEntry &= ~subVal; } -uint8_t TrackElement::GetTrackType() const +track_type_t TrackElement::GetTrackType() const { - return trackType; + return TrackType; } -void TrackElement::SetTrackType(uint8_t newType) +void TrackElement::SetTrackType(track_type_t newType) { - trackType = newType; + TrackType = newType; } uint8_t TrackElement::GetSequenceIndex() const { - return sequence & MAP_ELEM_TRACK_SEQUENCE_SEQUENCE_MASK; + return Sequence; } void TrackElement::SetSequenceIndex(uint8_t newSequenceIndex) { - sequence &= ~MAP_ELEM_TRACK_SEQUENCE_SEQUENCE_MASK; - sequence |= (newSequenceIndex & MAP_ELEM_TRACK_SEQUENCE_SEQUENCE_MASK); + Sequence = newSequenceIndex; } uint8_t TrackElement::GetStationIndex() const { - return (sequence & MAP_ELEM_TRACK_SEQUENCE_STATION_INDEX_MASK) >> 4; + return StationIndex; } void TrackElement::SetStationIndex(uint8_t newStationIndex) { - sequence &= ~MAP_ELEM_TRACK_SEQUENCE_STATION_INDEX_MASK; - sequence |= (newStationIndex << 4); + StationIndex = newStationIndex; } uint8_t TrackElement::GetDoorAState() const { - return (colour & TRACK_ELEMENT_DOOR_A_MASK) >> 2; + return (ColourScheme & TRACK_ELEMENT_COLOUR_DOOR_A_MASK) >> 2; } uint8_t TrackElement::GetDoorBState() const { - return (colour & TRACK_ELEMENT_DOOR_B_MASK) >> 5; + return (ColourScheme & TRACK_ELEMENT_COLOUR_DOOR_B_MASK) >> 5; } -uint8_t TrackElement::GetRideIndex() const +ride_idnew_t TrackElement::GetRideIndex() const { - return rideIndex; + return RideIndex; } -void TrackElement::SetRideIndex(uint8_t newRideIndex) +void TrackElement::SetRideIndex(ride_idnew_t newRideIndex) { - rideIndex = newRideIndex; + RideIndex = newRideIndex; } uint8_t TrackElement::GetColourScheme() const { - return colour & 0x3; + return ColourScheme & TRACK_ELEMENT_COLOUR_SCHEME_MASK; } void TrackElement::SetColourScheme(uint8_t newColourScheme) { - colour &= ~0x3; - colour |= (newColourScheme & 0x3); + ColourScheme &= ~TRACK_ELEMENT_COLOUR_SCHEME_MASK; + ColourScheme |= (newColourScheme & TRACK_ELEMENT_COLOUR_SCHEME_MASK); } bool TrackElement::HasCableLift() const { - return colour & TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; + return Flags2 & TRACK_ELEMENT_FLAGS2_CABLE_LIFT; } void TrackElement::SetHasCableLift(bool on) { - colour &= ~TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; + Flags2 &= ~TRACK_ELEMENT_FLAGS2_CABLE_LIFT; if (on) - colour |= TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; + Flags2 |= TRACK_ELEMENT_FLAGS2_CABLE_LIFT; } bool TrackElement::IsInverted() const { - return colour & TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + return Flags2 & TRACK_ELEMENT_FLAGS2_INVERTED; } void TrackElement::SetInverted(bool inverted) { if (inverted) { - colour |= TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + Flags2 |= TRACK_ELEMENT_FLAGS2_INVERTED; } else { - colour &= ~TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + Flags2 &= ~TRACK_ELEMENT_FLAGS2_INVERTED; } } uint8_t TrackElement::GetBrakeBoosterSpeed() const { - return (sequence >> 4) << 1; + return BrakeBoosterSpeed << 1; } void TrackElement::SetBrakeBoosterSpeed(uint8_t speed) { - sequence &= ~0b11110000; - sequence |= ((speed >> 1) << 4); + BrakeBoosterSpeed = (speed >> 1); } -uint8_t TrackElement::HasGreenLight() const +bool TrackElement::HasGreenLight() const { - return (sequence & MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT) != 0; + return (Flags2 & TRACK_ELEMENT_FLAGS2_HAS_GREEN_LIGHT) != 0; } -void TrackElement::SetHasGreenLight(uint8_t greenLight) +void TrackElement::SetHasGreenLight(bool on) { - sequence &= ~MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT; - if (greenLight) + Flags2 &= ~TRACK_ELEMENT_FLAGS2_HAS_GREEN_LIGHT; + if (on) { - sequence |= MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT; + Flags2 |= TRACK_ELEMENT_FLAGS2_HAS_GREEN_LIGHT; } } bool TrackElement::HasChain() const { - return type & TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; + return (Flags2 & TRACK_ELEMENT_FLAGS2_CHAIN_LIFT) != 0; } void TrackElement::SetHasChain(bool on) { if (on) { - type |= TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; + Flags2 |= TRACK_ELEMENT_FLAGS2_CHAIN_LIFT; } else { - type &= ~TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; + Flags2 &= ~TRACK_ELEMENT_FLAGS2_CHAIN_LIFT; } } @@ -413,17 +423,17 @@ uint8_t TileElementBase::GetDirectionWithOffset(uint8_t offset) const uint8_t SurfaceElement::GetSlope() const { - return (slope & TILE_ELEMENT_SURFACE_SLOPE_MASK); + return Slope; } uint32_t SurfaceElement::GetWaterHeight() const { - return terrain & TILE_ELEMENT_SURFACE_WATER_HEIGHT_MASK; + return WaterHeight; } bool TrackElement::IsHighlighted() const { - return (type & TILE_ELEMENT_TYPE_FLAG_HIGHLIGHT); + return (Flags2 & TRACK_ELEMENT_FLAGS2_HIGHLIGHT); } uint8_t PathElement::GetEdges() const @@ -435,3 +445,20 @@ StationObject* ride_get_station_object(const Ride* ride) { return nullptr; } + +bool rct_vehicle::IsGhost() const +{ + auto r = get_ride(ride); + return r != nullptr && r->status == RIDE_STATUS_SIMULATING; +} + +uint8_t TileElementBase::GetOccupiedQuadrants() const +{ + return flags & TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK; +} + +void TileElementBase::SetOccupiedQuadrants(uint8_t quadrants) +{ + flags &= ~TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK; + flags |= (quadrants & TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK); +} diff --git a/test/testpaint/TestPaint.cpp b/test/testpaint/TestPaint.cpp index 97c72195d5..61e39a24c6 100644 --- a/test/testpaint/TestPaint.cpp +++ b/test/testpaint/TestPaint.cpp @@ -43,16 +43,14 @@ namespace TestPaint rct_drawpixelinfo dpi = {}; dpi.zoom_level = 1; RCT2_Unk140E9A8 = &dpi; - gPaintSession.DPI = &dpi; + gPaintSession.DPI = dpi; { - Ride ride = {}; - ride.entrance_style = 0; static rct_ride_entry rideEntry = {}; rct_ride_entry_vehicle vehicleEntry{}; vehicleEntry.base_image_id = 0x70000; rideEntry.vehicles[0] = vehicleEntry; - gRideList[0] = ride; + gRideList[0] = {}; gRideEntries[0] = &rideEntry; } { diff --git a/test/testpaint/TestPaint.hpp b/test/testpaint/TestPaint.hpp index 34315529c8..8098769f0c 100644 --- a/test/testpaint/TestPaint.hpp +++ b/test/testpaint/TestPaint.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #define gRideEntries RCT2_ADDRESS(0x009ACFA4, rct_ride_entry*) @@ -79,4 +80,6 @@ enum Verbosity NORMAL, }; +extern Ride gRideList[RCT12_MAX_RIDES_IN_PARK]; + int generatePaintCode(uint8_t rideType); diff --git a/test/testpaint/TestTrack.cpp b/test/testpaint/TestTrack.cpp index 13617be7dd..a97cd823dc 100644 --- a/test/testpaint/TestTrack.cpp +++ b/test/testpaint/TestTrack.cpp @@ -101,14 +101,7 @@ public: uint8_t rideType, uint8_t trackType, int variant, TileElement* tileElement, TileElement* surfaceElement, Ride* ride, rct_ride_entry* rideEntry) override { - if (variant == 0) - { - tileElement->type &= ~TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; - } - else - { - tileElement->type |= TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; - } + tileElement->AsTrack()->SetHasChain(variant != 0); } }; @@ -269,13 +262,13 @@ static uint8_t TestTrackElementPaintCalls(uint8_t rideType, uint8_t trackType, u TileElement tileElement = {}; tileElement.SetType(TILE_ELEMENT_TYPE_TRACK); - tileElement.flags |= TILE_ELEMENT_FLAG_LAST_TILE; + tileElement.SetLastForTile(true); tileElement.AsTrack()->SetTrackType(trackType); tileElement.base_height = height / 16; g_currently_drawn_item = &tileElement; TileElement surfaceElement = {}; - surfaceElement.type = TILE_ELEMENT_TYPE_SURFACE; + surfaceElement.SetType(TILE_ELEMENT_TYPE_SURFACE); surfaceElement.base_height = 2; gSurfaceElement = &surfaceElement; gDidPassSurface = true; @@ -432,13 +425,13 @@ static uint8_t TestTrackElementSegmentSupportHeight( TileElement tileElement = {}; tileElement.SetType(TILE_ELEMENT_TYPE_TRACK); - tileElement.flags |= TILE_ELEMENT_FLAG_LAST_TILE; + tileElement.SetLastForTile(true); tileElement.AsTrack()->SetTrackType(trackType); tileElement.base_height = height / 16; g_currently_drawn_item = &tileElement; TileElement surfaceElement = {}; - surfaceElement.type = TILE_ELEMENT_TYPE_SURFACE; + surfaceElement.SetType(TILE_ELEMENT_TYPE_SURFACE); surfaceElement.base_height = 2; gSurfaceElement = &surfaceElement; gDidPassSurface = true; @@ -519,13 +512,13 @@ static uint8_t TestTrackElementGeneralSupportHeight( TileElement tileElement = {}; tileElement.SetType(TILE_ELEMENT_TYPE_TRACK); - tileElement.flags |= TILE_ELEMENT_FLAG_LAST_TILE; + tileElement.SetLastForTile(true); tileElement.AsTrack()->SetTrackType(trackType); tileElement.base_height = height / 16; g_currently_drawn_item = &tileElement; TileElement surfaceElement = {}; - surfaceElement.type = TILE_ELEMENT_TYPE_SURFACE; + surfaceElement.SetType(TILE_ELEMENT_TYPE_SURFACE); surfaceElement.base_height = 2; gSurfaceElement = &surfaceElement; gDidPassSurface = true; @@ -620,13 +613,13 @@ static uint8_t TestTrackElementSideTunnels(uint8_t rideType, uint8_t trackType, TileElement tileElement = {}; tileElement.SetType(TILE_ELEMENT_TYPE_TRACK); - tileElement.flags |= TILE_ELEMENT_FLAG_LAST_TILE; + tileElement.SetLastForTile(true); tileElement.AsTrack()->SetTrackType(trackType); tileElement.base_height = height / 16; g_currently_drawn_item = &tileElement; TileElement surfaceElement = {}; - surfaceElement.type = TILE_ELEMENT_TYPE_SURFACE; + surfaceElement.SetType(TILE_ELEMENT_TYPE_SURFACE); surfaceElement.base_height = 2; gSurfaceElement = &surfaceElement; gDidPassSurface = true; @@ -748,13 +741,13 @@ static uint8_t TestTrackElementVerticalTunnels(uint8_t rideType, uint8_t trackTy TileElement tileElement = {}; tileElement.SetType(TILE_ELEMENT_TYPE_TRACK); - tileElement.flags |= TILE_ELEMENT_FLAG_LAST_TILE; + tileElement.SetLastForTile(true); tileElement.AsTrack()->SetTrackType(trackType); tileElement.base_height = height / 16; g_currently_drawn_item = &tileElement; TileElement surfaceElement = {}; - surfaceElement.type = TILE_ELEMENT_TYPE_SURFACE; + surfaceElement.SetType(TILE_ELEMENT_TYPE_SURFACE); surfaceElement.base_height = 2; gSurfaceElement = &surfaceElement; gDidPassSurface = true; diff --git a/test/testpaint/generate.cpp b/test/testpaint/generate.cpp index dc06733518..1ef2cf8ba9 100644 --- a/test/testpaint/generate.cpp +++ b/test/testpaint/generate.cpp @@ -446,7 +446,7 @@ private: { TileElement tileElement = {}; tileElement.SetType(TILE_ELEMENT_TYPE_TRACK); - tileElement.flags |= TILE_ELEMENT_FLAG_LAST_TILE; + tileElement.SetLastForTile(true); tileElement.AsTrack()->SetTrackType(trackType); tileElement.base_height = 3; if (_invertedTrack) @@ -482,7 +482,7 @@ private: } // Get chain lift calls - tileElement.type |= 0x80; + tileElement.AsTrack()->SetHasChain(true); PaintIntercept::ClearCalls(); CallOriginal(trackType, direction, trackSequence, height, &tileElement); numCalls = PaintIntercept::GetCalls(callBuffer); @@ -606,7 +606,7 @@ private: WriteLine(tabs, "switch (direction) {"); for (int direction = 0; direction < 4; direction++) { - if (calls[direction].size() == 0) + if (calls[direction].empty()) continue; WriteLine(tabs, "case %d:", direction); @@ -720,7 +720,7 @@ private: function_call lastCall = calls[0].back(); for (int i = 0; i < 4; i++) { - if (calls[i].size() == 0 || !CompareFunctionCall(calls[i].back(), lastCall)) + if (calls[i].empty() || !CompareFunctionCall(calls[i].back(), lastCall)) { goto finished; } diff --git a/test/testpaint/main.cpp b/test/testpaint/main.cpp index 11328c5aca..ffa0666d71 100644 --- a/test/testpaint/main.cpp +++ b/test/testpaint/main.cpp @@ -620,7 +620,7 @@ int main(int argc, char* argv[]) Write(Verbosity::QUIET, CLIColour::GREEN, "[ PASSED ] "); Write(Verbosity::QUIET, "%d tests.\n", successCount); - if (failures.size() > 0) + if (!failures.empty()) { Write(Verbosity::QUIET, CLIColour::RED, "[ FAILED ] "); Write(Verbosity::QUIET, "%d tests, listed below:\n", (int)failures.size()); diff --git a/test/tests/CMakeLists.txt b/test/tests/CMakeLists.txt index 842b6e7e54..3c516443ea 100644 --- a/test/tests/CMakeLists.txt +++ b/test/tests/CMakeLists.txt @@ -144,6 +144,13 @@ target_link_libraries(test_ini ${GTEST_LIBRARIES} test-common ${LDL} z) target_link_platform_libraries(test_ini) add_test(NAME ini COMMAND test_ini) +# Platform +add_executable(test_platform ${CMAKE_CURRENT_LIST_DIR}/Platform.cpp) +SET_CHECK_CXX_FLAGS(test_platform) +target_link_libraries(test_platform ${GTEST_LIBRARIES} test-common ${LDL} z libopenrct2) +target_link_platform_libraries(test_platform) +add_test(NAME platform COMMAND test_platform) + # String test set(STRING_TEST_SOURCES "${CMAKE_CURRENT_LIST_DIR}/StringTest.cpp" @@ -207,16 +214,14 @@ target_link_libraries(test_tile_elements ${GTEST_LIBRARIES} libopenrct2 ${LDL} z target_link_platform_libraries(test_tile_elements) add_test(NAME tile_elements COMMAND test_tile_elements) -if (NOT DISABLE_NETWORK) - # Replay tests - set(REPLAY_TEST_SOURCES "${CMAKE_CURRENT_LIST_DIR}/ReplayTests.cpp" - "${CMAKE_CURRENT_LIST_DIR}/TestData.cpp") - add_executable(test_replays ${REPLAY_TEST_SOURCES}) - SET_CHECK_CXX_FLAGS(test_replays) - target_link_libraries(test_replays ${GTEST_LIBRARIES} libopenrct2 ${LDL} z) - target_link_platform_libraries(test_replays) - add_test(NAME replay_tests COMMAND test_replays) -endif () +# Replay tests +set(REPLAY_TEST_SOURCES "${CMAKE_CURRENT_LIST_DIR}/ReplayTests.cpp" + "${CMAKE_CURRENT_LIST_DIR}/TestData.cpp") +add_executable(test_replays ${REPLAY_TEST_SOURCES}) +SET_CHECK_CXX_FLAGS(test_replays) +target_link_libraries(test_replays ${GTEST_LIBRARIES} libopenrct2 ${LDL} z) +target_link_platform_libraries(test_replays) +add_test(NAME replay_tests COMMAND test_replays) # Pathfinding test set(PATHFINDING_TEST_SOURCES "${CMAKE_CURRENT_LIST_DIR}/Pathfinding.cpp" diff --git a/test/tests/Endianness.cpp b/test/tests/Endianness.cpp new file mode 100644 index 0000000000..b673360797 --- /dev/null +++ b/test/tests/Endianness.cpp @@ -0,0 +1,43 @@ +#include "openrct2/core/Endianness.h" + +#include + +TEST(SwapBETest, ForUInt8_DoesNothing) +{ + uint8_t before = 0x12; + uint8_t after = ByteSwapBE(before); + ASSERT_EQ(before, after); +} + +TEST(SwapBETest, ForUInt16_SwapsBytes) +{ + uint16_t before = 0x1234; + uint16_t after = ByteSwapBE(before); + ASSERT_EQ(0x3412u, after); +} + +TEST(SwapBETest, ForUInt32_SwapsBytes) +{ + uint32_t before = 0x12345678; + uint32_t after = ByteSwapBE(before); + ASSERT_EQ(0x78563412u, after); +} + +TEST(SwapBETest, ForUInt64_SwapsBytes) +{ + uint64_t before = 0x1234567887654321; + uint64_t after = ByteSwapBE(before); + ASSERT_EQ(0x2143658778563412u, after); +} + +TEST(SwapBETest, ForCustomBlittableType_SwapsBytes) +{ + struct MyStruct + { + uint16_t value; + }; + + MyStruct before = { 0x1234 }; + MyStruct after = ByteSwapBE(before); + ASSERT_EQ(0x3412, after.value); +} diff --git a/test/tests/MultiLaunch.cpp b/test/tests/MultiLaunch.cpp index f83c4076af..b3a37db893 100644 --- a/test/tests/MultiLaunch.cpp +++ b/test/tests/MultiLaunch.cpp @@ -45,7 +45,7 @@ TEST(MultiLaunchTest, all) game_load_init(); // Check ride count to check load was successful - ASSERT_EQ(gRideCount, 134); + ASSERT_EQ(ride_get_count(), 134); auto gs = context->GetGameState(); ASSERT_NE(gs, nullptr); auto& date = gs->GetDate(); @@ -59,7 +59,7 @@ TEST(MultiLaunchTest, all) ASSERT_EQ(date.GetMonthTicks(), 7862 + updatesToTest); // Check ride count again - ASSERT_EQ(gRideCount, 134); + ASSERT_EQ(ride_get_count(), 134); } SUCCEED(); } diff --git a/test/tests/NetworkLoadSave.cpp b/test/tests/NetworkLoadSave.cpp index 0e65ee504e..f088d58c64 100644 --- a/test/tests/NetworkLoadSave.cpp +++ b/test/tests/NetworkLoadSave.cpp @@ -90,6 +90,7 @@ static bool LoadMap(IStream* stream) static bool SaveMap(IStream* stream, const std::vector& objects) { bool result = false; + map_reorganise_elements(); viewport_set_saved_view(); try { diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp index 86140c7647..57a05cb9be 100644 --- a/test/tests/Pathfinding.cpp +++ b/test/tests/Pathfinding.cpp @@ -50,17 +50,16 @@ public: } protected: - static Ride* FindRideByName(const char* name, ride_id_t* outRideIndex) + static Ride* FindRideByName(const char* name) { - Ride* ride; - FOR_ALL_RIDES ((*outRideIndex), ride) + for (auto& ride : GetRideManager()) { - char thisName[256]; - format_string(thisName, sizeof(thisName), ride->name, &ride->name_arguments); - if (!_strnicmp(thisName, name, sizeof(thisName))) - return ride; + auto thisName = ride.GetName(); + if (!_strnicmp(thisName.c_str(), name, sizeof(thisName))) + { + return &ride; + } } - return nullptr; } @@ -69,7 +68,7 @@ protected: // Our start position is in tile coordinates, but we need to give the peep spawn // position in actual world coords (32 units per tile X/Y, 8 per Z level). // Add 16 so the peep spawns in the center of the tile. - Peep* peep = peep_generate(pos->x * 32 + 16, pos->y * 32 + 16, pos->z * 8); + Peep* peep = Peep::Generate({ pos->x * 32 + 16, pos->y * 32 + 16, pos->z * 8 }); // Peeps that are outside of the park use specialized pathfinding which we don't want to // use here @@ -84,8 +83,8 @@ protected: // Pick the direction the peep should initially move in, given the goal position. // This will also store the goal position and initialize pathfinding data for the peep. gPeepPathFindGoalPosition = goal; - const int32_t moveDir = peep_pathfind_choose_direction(*pos, peep); - if (moveDir < 0) + const Direction moveDir = peep_pathfind_choose_direction(*pos, peep); + if (moveDir == INVALID_DIRECTION) { // Couldn't determine a direction to move off in return false; @@ -137,7 +136,7 @@ protected: static ::testing::AssertionResult AssertIsStartPosition(const char*, const TileCoordsXYZ& location) { const uint32_t expectedSurfaceStyle = 11u; - const uint32_t style = map_get_surface_element_at(location.x, location.y)->AsSurface()->GetSurfaceStyle(); + const uint32_t style = map_get_surface_element_at(location.x, location.y)->GetSurfaceStyle(); if (style != expectedSurfaceStyle) return ::testing::AssertionFailure() @@ -152,7 +151,7 @@ protected: { const uint32_t forbiddenSurfaceStyle = 8u; - const uint32_t style = map_get_surface_element_at(location.x, location.y)->AsSurface()->GetSurfaceStyle(); + const uint32_t style = map_get_surface_element_at(location.x, location.y)->GetSurfaceStyle(); if (style == forbiddenSurfaceStyle) return ::testing::AssertionFailure() @@ -198,8 +197,7 @@ TEST_P(SimplePathfindingTest, CanFindPathFromStartToGoal) ASSERT_PRED_FORMAT1(AssertIsStartPosition, scenario.start); TileCoordsXYZ pos = scenario.start; - ride_id_t rideIndex; - Ride* ride = FindRideByName(scenario.name, &rideIndex); + auto ride = FindRideByName(scenario.name); ASSERT_NE(ride, nullptr); auto entrancePos = ride_get_entrance_location(ride, 0); @@ -207,8 +205,8 @@ TEST_P(SimplePathfindingTest, CanFindPathFromStartToGoal) entrancePos.x - TileDirectionDelta[entrancePos.direction].x, entrancePos.y - TileDirectionDelta[entrancePos.direction].y, entrancePos.z); - const auto succeeded = FindPath(&pos, goal, scenario.steps, rideIndex) ? ::testing::AssertionSuccess() - : ::testing::AssertionFailure() + const auto succeeded = FindPath(&pos, goal, scenario.steps, ride->id) ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() << "Failed to find path from " << scenario.start << " to " << goal << " in " << scenario.steps << " steps; reached " << pos << " before giving up."; @@ -237,8 +235,7 @@ TEST_P(ImpossiblePathfindingTest, CannotFindPathFromStartToGoal) TileCoordsXYZ pos = scenario.start; ASSERT_PRED_FORMAT1(AssertIsStartPosition, scenario.start); - ride_id_t rideIndex; - Ride* ride = FindRideByName(scenario.name, &rideIndex); + auto ride = FindRideByName(scenario.name); ASSERT_NE(ride, nullptr); auto entrancePos = ride_get_entrance_location(ride, 0); @@ -246,7 +243,7 @@ TEST_P(ImpossiblePathfindingTest, CannotFindPathFromStartToGoal) entrancePos.x + TileDirectionDelta[entrancePos.direction].x, entrancePos.y + TileDirectionDelta[entrancePos.direction].y, entrancePos.z); - EXPECT_FALSE(FindPath(&pos, goal, 10000, rideIndex)); + EXPECT_FALSE(FindPath(&pos, goal, 10000, ride->id)); } INSTANTIATE_TEST_CASE_P( diff --git a/test/tests/Platform.cpp b/test/tests/Platform.cpp new file mode 100644 index 0000000000..dccea3aad0 --- /dev/null +++ b/test/tests/Platform.cpp @@ -0,0 +1,28 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include +#include + +TEST(platform, sanitise_filename) +{ +#ifndef _WIN32 + ASSERT_EQ("normal-filename.png", platform_sanitise_filename("normal-filename.png")); + ASSERT_EQ("utf🎱", platform_sanitise_filename("utf🎱")); + ASSERT_EQ("forbidden_char", platform_sanitise_filename("forbidden/char")); + ASSERT_EQ("forbidden_\\:\"|?*chars", platform_sanitise_filename("forbidden/\\:\"|?*chars")); + ASSERT_EQ(" non trimmed ", platform_sanitise_filename(" non trimmed ")); +#else + ASSERT_EQ("normal-filename.png", platform_sanitise_filename("normal-filename.png")); + ASSERT_EQ("utf🎱", platform_sanitise_filename("utf🎱")); + ASSERT_EQ("forbidden_char", platform_sanitise_filename("forbidden/char")); + ASSERT_EQ("forbidden_______chars", platform_sanitise_filename("forbidden/\\:\"|?*chars")); + ASSERT_EQ("non trimmed", platform_sanitise_filename(" non trimmed ")); +#endif +} diff --git a/test/tests/RideRatings.cpp b/test/tests/RideRatings.cpp index a556c06090..109f3622ab 100644 --- a/test/tests/RideRatings.cpp +++ b/test/tests/RideRatings.cpp @@ -28,34 +28,26 @@ class RideRatings : public testing::Test protected: void CalculateRatingsForAllRides() { - for (int rideId = 0; rideId < MAX_RIDES; rideId++) + for (const auto& ride : GetRideManager()) { - Ride* ride = get_ride(rideId); - if (ride->type != RIDE_TYPE_NULL) - { - ride_ratings_update_ride(rideId); - } + ride_ratings_update_ride(ride); } } void DumpRatings() { - for (int rideId = 0; rideId < MAX_RIDES; rideId++) + for (const auto& ride : GetRideManager()) { - Ride* ride = get_ride(rideId); - if (ride->type != RIDE_TYPE_NULL) - { - std::string line = FormatRatings(ride); - printf("%s\n", line.c_str()); - } + std::string line = FormatRatings(ride); + printf("%s\n", line.c_str()); } } - std::string FormatRatings(Ride* ride) + std::string FormatRatings(const Ride& ride) { - rating_tuple ratings = ride->ratings; + rating_tuple ratings = ride.ratings; std::string line = String::StdFormat( - "%s: (%d, %d, %d)", ride_type_get_enum_name(ride->type), (int)ratings.excitement, (int)ratings.intensity, + "%s: (%d, %d, %d)", ride_type_get_enum_name(ride.type), (int)ratings.excitement, (int)ratings.intensity, (int)ratings.nausea); return line; } @@ -76,7 +68,7 @@ TEST_F(RideRatings, all) load_from_sv6(path.c_str()); // Check ride count to check load was successful - ASSERT_EQ(gRideCount, 134); + ASSERT_EQ(ride_get_count(), 134); CalculateRatingsForAllRides(); @@ -86,16 +78,12 @@ TEST_F(RideRatings, all) // Check ride ratings int expI = 0; - for (int rideId = 0; rideId < MAX_RIDES; rideId++) + for (const auto& ride : GetRideManager()) { - Ride* ride = get_ride(rideId); - if (ride->type != RIDE_TYPE_NULL) - { - std::string actual = FormatRatings(ride); - std::string expected = expectedRatings[expI]; - ASSERT_STREQ(actual.c_str(), expected.c_str()); + auto actual = FormatRatings(ride); + auto expected = expectedRatings[expI]; + ASSERT_STREQ(actual.c_str(), expected.c_str()); - expI++; - } + expI++; } } diff --git a/test/tests/TileElements.cpp b/test/tests/TileElements.cpp index 5456a0cf2a..83d50c1fd0 100644 --- a/test/tests/TileElements.cpp +++ b/test/tests/TileElements.cpp @@ -82,7 +82,7 @@ TEST_F(TileElementWantsFootpathConnection, SlopedPath) TEST_F(TileElementWantsFootpathConnection, Stall) { // Stalls usually have one path direction flag, but can have multiple (info kiosk for example) - const TileElement* const stallElement = map_get_track_element_at(19 << 5, 15 << 5, 14); + const TrackElement* const stallElement = map_get_track_element_at(19 << 5, 15 << 5, 14); ASSERT_NE(stallElement, nullptr); EXPECT_TRUE(tile_element_wants_path_connection_towards({ 19, 15, 14, 0 }, nullptr)); EXPECT_FALSE(tile_element_wants_path_connection_towards({ 19, 15, 14, 1 }, nullptr)); diff --git a/test/tests/testdata/replays/bpb.sv6r b/test/tests/testdata/replays/bpb.sv6r deleted file mode 100644 index 5effad9f1f..0000000000 Binary files a/test/tests/testdata/replays/bpb.sv6r and /dev/null differ diff --git a/test/tests/testdata/replays/issue5953.sv6r b/test/tests/testdata/replays/issue5953.sv6r deleted file mode 100644 index 0bcafc5a26..0000000000 Binary files a/test/tests/testdata/replays/issue5953.sv6r and /dev/null differ diff --git a/test/tests/testdata/replays/issue6807.sv6r b/test/tests/testdata/replays/issue6807.sv6r deleted file mode 100644 index b885011658..0000000000 Binary files a/test/tests/testdata/replays/issue6807.sv6r and /dev/null differ diff --git a/test/tests/testdata/replays/issue7828.sv6r b/test/tests/testdata/replays/issue7828.sv6r deleted file mode 100644 index 7e82031945..0000000000 Binary files a/test/tests/testdata/replays/issue7828.sv6r and /dev/null differ diff --git a/test/tests/testdata/replays/issue7867.sv6r b/test/tests/testdata/replays/issue7867.sv6r deleted file mode 100644 index bd5161d2f3..0000000000 Binary files a/test/tests/testdata/replays/issue7867.sv6r and /dev/null differ diff --git a/test/tests/testdata/replays/issue8203.sv6r b/test/tests/testdata/replays/issue8203.sv6r deleted file mode 100644 index 886b7d8df0..0000000000 Binary files a/test/tests/testdata/replays/issue8203.sv6r and /dev/null differ diff --git a/test/tests/testdata/replays/issue8216.sv6r b/test/tests/testdata/replays/issue8216.sv6r deleted file mode 100644 index e261dbe680..0000000000 Binary files a/test/tests/testdata/replays/issue8216.sv6r and /dev/null differ diff --git a/test/tests/testdata/replays/issue8345.sv6r b/test/tests/testdata/replays/issue8345.sv6r deleted file mode 100644 index 3710c1ac43..0000000000 Binary files a/test/tests/testdata/replays/issue8345.sv6r and /dev/null differ diff --git a/test/tests/testdata/replays/issue8426.sv6r b/test/tests/testdata/replays/issue8426.sv6r deleted file mode 100644 index 8f558ea701..0000000000 Binary files a/test/tests/testdata/replays/issue8426.sv6r and /dev/null differ diff --git a/test/tests/tests.vcxproj b/test/tests/tests.vcxproj index f4d448af46..cc19c85c7f 100644 --- a/test/tests/tests.vcxproj +++ b/test/tests/tests.vcxproj @@ -58,6 +58,7 @@ +