diff --git a/.travis.yml b/.travis.yml index 92b8ca0dab..88d63de32c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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,6 +41,7 @@ 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) ; @@ -65,6 +66,7 @@ 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) ; 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..34a22049d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,11 +20,11 @@ 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.2b/title-sequence-v0.1.2b.zip") +set(TITLE_SEQUENCE_SHA1 "19263f8ca383345959473e64da4785a60f00f420") -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.11/objects.zip") +set(OBJECTS_SHA1 "8674120086929f9196560d77cada631fb478d7c0") option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.") option(WITH_TESTS "Build tests") diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 5da21932d7..1fd8cf5444 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -32,11 +32,11 @@ 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 */; }; + 2A61CAF92229E59F0095AD67 /* WaterSetHeightAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */; }; + 2A61CAFB2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.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 */; }; @@ -44,6 +44,46 @@ 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 */; }; + 2ADE2EEA2244183D002598AF /* ParkEntranceRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EE92244183D002598AF /* ParkEntranceRemoveAction.hpp */; }; + 2ADE2F062244187B002598AF /* SurfaceSetStyleAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EEB22441878002598AF /* SurfaceSetStyleAction.hpp */; }; + 2ADE2F072244187B002598AF /* MazeSetTrackAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EEC22441878002598AF /* MazeSetTrackAction.hpp */; }; + 2ADE2F082244187B002598AF /* ClearAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EED22441878002598AF /* ClearAction.hpp */; }; + 2ADE2F092244187B002598AF /* SignSetNameAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EEE22441878002598AF /* SignSetNameAction.hpp */; }; + 2ADE2F0A2244187B002598AF /* ParkSetResearchFundingAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EEF22441878002598AF /* ParkSetResearchFundingAction.hpp */; }; + 2ADE2F0B2244187B002598AF /* RideSetColourScheme.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EF022441878002598AF /* RideSetColourScheme.hpp */; }; + 2ADE2F0C2244187B002598AF /* SmallSceneryRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EF122441878002598AF /* SmallSceneryRemoveAction.hpp */; }; + 2ADE2F0D2244187B002598AF /* LargeSceneryRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EF222441879002598AF /* LargeSceneryRemoveAction.hpp */; }; + 2ADE2F0E2244187B002598AF /* StaffHireNewAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EF322441879002598AF /* StaffHireNewAction.hpp */; }; + 2ADE2F0F2244187B002598AF /* PlacePeepSpawnAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EF422441879002598AF /* PlacePeepSpawnAction.hpp */; }; + 2ADE2F102244187B002598AF /* TrackSetBrakeSpeedAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EF522441879002598AF /* TrackSetBrakeSpeedAction.hpp */; }; + 2ADE2F112244187B002598AF /* GuestSetFlagsAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EF622441879002598AF /* GuestSetFlagsAction.hpp */; }; + 2ADE2F122244187B002598AF /* ParkSetLoanAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EF722441879002598AF /* ParkSetLoanAction.hpp */; }; + 2ADE2F132244187B002598AF /* GuestSetNameAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EF822441879002598AF /* GuestSetNameAction.hpp */; }; + 2ADE2F142244187B002598AF /* FootpathRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EF922441879002598AF /* FootpathRemoveAction.hpp */; }; + 2ADE2F152244187B002598AF /* StaffSetPatrolAreaAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EFA22441879002598AF /* StaffSetPatrolAreaAction.hpp */; }; + 2ADE2F162244187B002598AF /* ParkSetParameterAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EFB22441879002598AF /* ParkSetParameterAction.hpp */; }; + 2ADE2F172244187B002598AF /* RideDemolishAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EFC2244187A002598AF /* RideDemolishAction.hpp */; }; + 2ADE2F182244187B002598AF /* StaffSetNameAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EFD2244187A002598AF /* StaffSetNameAction.hpp */; }; + 2ADE2F192244187B002598AF /* ParkMarketingAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EFE2244187A002598AF /* ParkMarketingAction.hpp */; }; + 2ADE2F1A2244187B002598AF /* ClimateSetAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2EFF2244187A002598AF /* ClimateSetAction.hpp */; }; + 2ADE2F1B2244187B002598AF /* WallRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F002244187A002598AF /* WallRemoveAction.hpp */; }; + 2ADE2F1C2244187B002598AF /* ParkSetNameAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F012244187A002598AF /* ParkSetNameAction.hpp */; }; + 2ADE2F1D2244187B002598AF /* StaffSetColourAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F022244187A002598AF /* StaffSetColourAction.hpp */; }; + 2ADE2F1E2244187B002598AF /* RideSetName.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F032244187A002598AF /* RideSetName.hpp */; }; + 2ADE2F1F2244187B002598AF /* BannerSetNameAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F042244187A002598AF /* BannerSetNameAction.hpp */; }; + 2ADE2F202244187B002598AF /* StaffFireAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE2F052244187B002598AF /* StaffFireAction.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 */; }; 2AF7893D220B253E0072754A /* RideSetAppearanceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */; }; 4C29DEB3218C6AE500E8707F /* RCT12.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C29DEB2218C6AE500E8707F /* RCT12.cpp */; }; 4C358E5221C445F700ADE6BC /* ReplayManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C358E5021C445F700ADE6BC /* ReplayManager.cpp */; }; @@ -78,6 +118,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 */; }; + 937A92152242CDAA00B09278 /* LandSmoothAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 937A92142242CDAA00B09278 /* LandSmoothAction.hpp */; }; + 937A92132242CCB300B09278 /* LandBuyRightsAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 937A92122242CCB300B09278 /* LandBuyRightsAction.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 */; }; @@ -409,6 +451,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 +513,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 */; }; @@ -643,11 +686,11 @@ 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 = ""; }; + 2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterSetHeightAction.hpp; sourceTree = ""; }; + 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideEntranceExitPlaceAction.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 = ""; }; @@ -655,6 +698,46 @@ 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 = ""; }; + 2ADE2EE92244183D002598AF /* ParkEntranceRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkEntranceRemoveAction.hpp; sourceTree = ""; }; + 2ADE2EEB22441878002598AF /* SurfaceSetStyleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SurfaceSetStyleAction.hpp; sourceTree = ""; }; + 2ADE2EEC22441878002598AF /* MazeSetTrackAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MazeSetTrackAction.hpp; sourceTree = ""; }; + 2ADE2EED22441878002598AF /* ClearAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ClearAction.hpp; sourceTree = ""; }; + 2ADE2EEE22441878002598AF /* SignSetNameAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SignSetNameAction.hpp; sourceTree = ""; }; + 2ADE2EEF22441878002598AF /* ParkSetResearchFundingAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkSetResearchFundingAction.hpp; sourceTree = ""; }; + 2ADE2EF022441878002598AF /* RideSetColourScheme.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetColourScheme.hpp; sourceTree = ""; }; + 2ADE2EF122441878002598AF /* SmallSceneryRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SmallSceneryRemoveAction.hpp; sourceTree = ""; }; + 2ADE2EF222441879002598AF /* LargeSceneryRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LargeSceneryRemoveAction.hpp; sourceTree = ""; }; + 2ADE2EF322441879002598AF /* StaffHireNewAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffHireNewAction.hpp; sourceTree = ""; }; + 2ADE2EF422441879002598AF /* PlacePeepSpawnAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PlacePeepSpawnAction.hpp; sourceTree = ""; }; + 2ADE2EF522441879002598AF /* TrackSetBrakeSpeedAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackSetBrakeSpeedAction.hpp; sourceTree = ""; }; + 2ADE2EF622441879002598AF /* GuestSetFlagsAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GuestSetFlagsAction.hpp; sourceTree = ""; }; + 2ADE2EF722441879002598AF /* ParkSetLoanAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkSetLoanAction.hpp; sourceTree = ""; }; + 2ADE2EF822441879002598AF /* GuestSetNameAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GuestSetNameAction.hpp; sourceTree = ""; }; + 2ADE2EF922441879002598AF /* FootpathRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathRemoveAction.hpp; sourceTree = ""; }; + 2ADE2EFA22441879002598AF /* StaffSetPatrolAreaAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetPatrolAreaAction.hpp; sourceTree = ""; }; + 2ADE2EFB22441879002598AF /* ParkSetParameterAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkSetParameterAction.hpp; sourceTree = ""; }; + 2ADE2EFC2244187A002598AF /* RideDemolishAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideDemolishAction.hpp; sourceTree = ""; }; + 2ADE2EFD2244187A002598AF /* StaffSetNameAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetNameAction.hpp; sourceTree = ""; }; + 2ADE2EFE2244187A002598AF /* ParkMarketingAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkMarketingAction.hpp; sourceTree = ""; }; + 2ADE2EFF2244187A002598AF /* ClimateSetAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ClimateSetAction.hpp; sourceTree = ""; }; + 2ADE2F002244187A002598AF /* WallRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WallRemoveAction.hpp; sourceTree = ""; }; + 2ADE2F012244187A002598AF /* ParkSetNameAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ParkSetNameAction.hpp; sourceTree = ""; }; + 2ADE2F022244187A002598AF /* StaffSetColourAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetColourAction.hpp; sourceTree = ""; }; + 2ADE2F032244187A002598AF /* RideSetName.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetName.hpp; sourceTree = ""; }; + 2ADE2F042244187A002598AF /* BannerSetNameAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BannerSetNameAction.hpp; sourceTree = ""; }; + 2ADE2F052244187B002598AF /* StaffFireAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffFireAction.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 = ""; }; 2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetAppearanceAction.hpp; 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 = ""; }; @@ -1160,6 +1243,8 @@ 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 = ""; }; + 937A92142242CDAA00B09278 /* LandSmoothAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandSmoothAction.hpp; sourceTree = ""; }; + 937A92122242CCB300B09278 /* LandBuyRightsAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandBuyRightsAction.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 = ""; }; @@ -1303,6 +1388,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 = ""; }; + 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 = ""; }; + 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 = ""; }; 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,8 +1662,8 @@ 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 = ""; }; F76C84121EC4E7CC00FA49E2 /* BannerObject.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BannerObject.cpp; sourceTree = ""; }; @@ -2034,6 +2126,41 @@ C6352B871F477032006CCEE3 /* actions */ = { isa = PBXGroup; children = ( + 2ADE2EE92244183D002598AF /* ParkEntranceRemoveAction.hpp */, + 2ADE2F042244187A002598AF /* BannerSetNameAction.hpp */, + 2ADE2EED22441878002598AF /* ClearAction.hpp */, + 2ADE2EFF2244187A002598AF /* ClimateSetAction.hpp */, + 2ADE2EF922441879002598AF /* FootpathRemoveAction.hpp */, + 2ADE2EF622441879002598AF /* GuestSetFlagsAction.hpp */, + 2ADE2EF822441879002598AF /* GuestSetNameAction.hpp */, + 2ADE2EF222441879002598AF /* LargeSceneryRemoveAction.hpp */, + 2ADE2EEC22441878002598AF /* MazeSetTrackAction.hpp */, + 2ADE2EFE2244187A002598AF /* ParkMarketingAction.hpp */, + 2ADE2EF722441879002598AF /* ParkSetLoanAction.hpp */, + 2ADE2F012244187A002598AF /* ParkSetNameAction.hpp */, + 2ADE2EFB22441879002598AF /* ParkSetParameterAction.hpp */, + 2ADE2EEF22441878002598AF /* ParkSetResearchFundingAction.hpp */, + 2ADE2EF422441879002598AF /* PlacePeepSpawnAction.hpp */, + 2ADE2EFC2244187A002598AF /* RideDemolishAction.hpp */, + 2ADE2EF022441878002598AF /* RideSetColourScheme.hpp */, + 2ADE2F032244187A002598AF /* RideSetName.hpp */, + 2ADE2EEE22441878002598AF /* SignSetNameAction.hpp */, + 2ADE2EF122441878002598AF /* SmallSceneryRemoveAction.hpp */, + 2ADE2F052244187B002598AF /* StaffFireAction.hpp */, + 2ADE2EF322441879002598AF /* StaffHireNewAction.hpp */, + 2ADE2F022244187A002598AF /* StaffSetColourAction.hpp */, + 2ADE2EFD2244187A002598AF /* StaffSetNameAction.hpp */, + 2ADE2EFA22441879002598AF /* StaffSetPatrolAreaAction.hpp */, + 2ADE2EEB22441878002598AF /* SurfaceSetStyleAction.hpp */, + 2ADE2EF522441879002598AF /* TrackSetBrakeSpeedAction.hpp */, + 2ADE2F002244187A002598AF /* WallRemoveAction.hpp */, + C9C630BD2235A9C6009AD16E /* FootpathPlaceFromTrackAction.hpp */, + C9C630BB2235A7F9009AD16E /* WaterLowerAction.hpp */, + C9C630BC2235A7F9009AD16E /* WaterRaiseAction.hpp */, + 937A92122242CCB300B09278 /* LandBuyRightsAction.hpp */, + C9C630BA2235A7E7009AD16E /* LandLowerAction.hpp */, + C9C630B92235A7E7009AD16E /* LandRaiseAction.hpp */, + 937A92142242CDAA00B09278 /* LandSmoothAction.hpp */, 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */, 2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */, 2A61CAF42229E5720095AD67 /* FootpathPlaceAction.hpp */, @@ -2391,6 +2518,8 @@ F76C83551EC4E7CC00FA49E2 /* libopenrct2 */ = { isa = PBXGroup; children = ( + C9C630B52235A22C009AD16E /* GameStateSnapshots.cpp */, + C9C630B42235A22C009AD16E /* GameStateSnapshots.h */, 4C358E5021C445F700ADE6BC /* ReplayManager.cpp */, 4C358E5121C445F700ADE6BC /* ReplayManager.h */, C6352B871F477032006CCEE3 /* actions */, @@ -2508,6 +2637,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 */, @@ -2617,6 +2752,7 @@ F76C83D71EC4E7CC00FA49E2 /* localisation */ = { isa = PBXGroup; children = ( + 2ADE2F2D224418E7002598AF /* ConversionTables.h */, 4C7B53C61FFF94F900A52E21 /* ConversionTables.cpp */, 4C7B53AA1FFF935B00A52E21 /* Convert.cpp */, 4C7B53AB1FFF935B00A52E21 /* Currency.cpp */, @@ -2662,6 +2798,8 @@ F76C83F51EC4E7CC00FA49E2 /* network */ = { isa = PBXGroup; children = ( + 2ADE2F3022441905002598AF /* DiscordService.cpp */, + 2ADE2F2F22441905002598AF /* DiscordService.h */, F76C83F61EC4E7CC00FA49E2 /* Http.cpp */, F76C83F71EC4E7CC00FA49E2 /* http.h */, F76C83F81EC4E7CC00FA49E2 /* Network.cpp */, @@ -2685,8 +2823,8 @@ 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 */, ); @@ -2750,6 +2888,7 @@ F76C843A1EC4E7CC00FA49E2 /* paint */ = { isa = PBXGroup; children = ( + 2ADE2F332244191E002598AF /* VirtualFloor.h */, F76C84491EC4E7CC00FA49E2 /* sprite */, F76C843B1EC4E7CC00FA49E2 /* tile_element */, 4C6A66AE1FE278C900694CB6 /* Paint.cpp */, @@ -2871,6 +3010,7 @@ F76C84831EC4E7CC00FA49E2 /* ride */ = { isa = PBXGroup; children = ( + 2ADE2F352244195F002598AF /* RideTypes.h */, F76C84861EC4E7CC00FA49E2 /* coaster */, F76C84A91EC4E7CC00FA49E2 /* gentle */, F76C84C01EC4E7CC00FA49E2 /* shops */, @@ -3095,6 +3235,7 @@ F76C855B1EC4E7CD00FA49E2 /* world */ = { isa = PBXGroup; children = ( + 2ADE2F372244198A002598AF /* SpriteBase.h */, 4C7B541D2007646A00A52E21 /* Balloon.cpp */, 4C7B541E2007646A00A52E21 /* Banner.cpp */, 4C7B541F2007646A00A52E21 /* Banner.h */, @@ -3348,48 +3489,89 @@ buildActionMask = 2147483647; files = ( 2A43D2C12225B91A00E8F73B /* RideEntranceExitRemoveAction.hpp in Headers */, + 2ADE2F172244187B002598AF /* RideDemolishAction.hpp in Headers */, + 2ADE2F3122441905002598AF /* DiscordService.h in Headers */, C67B28172002D67A00109C93 /* Viewport.h in Headers */, 939A359C20C12FC800630B3F /* Paint.Sprite.h in Headers */, 9308DA04209908090079EE96 /* TileElement.h in Headers */, + 2ADE2F062244187B002598AF /* SurfaceSetStyleAction.hpp in Headers */, C67B28152002D67A00109C93 /* Widget.h in Headers */, + 2ADE2F1A2244187B002598AF /* ClimateSetAction.hpp in Headers */, + 2ADE2F1B2244187B002598AF /* WallRemoveAction.hpp in Headers */, + 2ADE2F202244187B002598AF /* StaffFireAction.hpp in Headers */, C6352B951F477032006CCEE3 /* RideCreateAction.hpp in Headers */, + 2ADE2F092244187B002598AF /* SignSetNameAction.hpp in Headers */, C6352B851F477022006CCEE3 /* DataSerialiserTraits.h in Headers */, + 2ADE2F082244187B002598AF /* ClearAction.hpp in Headers */, 939A359F20C12FDE00630B3F /* Paint.Surface.h in Headers */, 2A61CAF62229E5720095AD67 /* FootpathSceneryRemoveAction.hpp in Headers */, + 2ADE2F0F2244187B002598AF /* PlacePeepSpawnAction.hpp in Headers */, C67B28192002D7F200109C93 /* Window_internal.h in Headers */, C6352B971F477032006CCEE3 /* SetParkEntranceFeeAction.hpp in Headers */, + 2ADE2F28224418B2002598AF /* DataSerialiserTag.h in Headers */, 2AA050332209A8E300D3A922 /* StaffSetOrdersAction.hpp in Headers */, + 2ADE2F1E2244187B002598AF /* RideSetName.hpp in Headers */, 2AAFD7FA220DD2DC002461A4 /* TrackPlaceAction.hpp in Headers */, + 2ADE2F1D2244187B002598AF /* StaffSetColourAction.hpp in Headers */, 2A61CAF92229E59F0095AD67 /* WaterSetHeightAction.hpp in Headers */, + 2ADE2F2E224418E7002598AF /* ConversionTables.h in Headers */, 933F2CBB20935668001B33FD /* LocalisationService.h in Headers */, 2A5C1368221E9F9000F8C245 /* TrackRemoveAction.hpp in Headers */, + 2ADE2EEA2244183D002598AF /* ParkEntranceRemoveAction.hpp in Headers */, 2A61CAFB2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp in Headers */, + 2ADE2F162244187B002598AF /* ParkSetParameterAction.hpp in Headers */, C6352B861F477022006CCEE3 /* Endianness.h in Headers */, + 2ADE2F2C224418B2002598AF /* FileIndex.hpp in Headers */, 93CBA4CC20A7504500867D56 /* ImageImporter.h in Headers */, + 2ADE2F0B2244187B002598AF /* RideSetColourScheme.hpp in Headers */, + 2ADE2F29224418B2002598AF /* Numerics.hpp in Headers */, + 2ADE2F382244198B002598AF /* SpriteBase.h in Headers */, 2AAFD7FE220DD374002461A4 /* PauseToggleAction.hpp in Headers */, + 2ADE2F192244187B002598AF /* ParkMarketingAction.hpp in Headers */, + 2ADE2F0A2244187B002598AF /* ParkSetResearchFundingAction.hpp in Headers */, + 2ADE2F142244187B002598AF /* FootpathRemoveAction.hpp in Headers */, + 2ADE2F1F2244187B002598AF /* BannerSetNameAction.hpp in Headers */, + 937A92152242CDAA00B09278 /* LandSmoothAction.hpp in Headers */, C6352B941F477032006CCEE3 /* PlaceParkEntranceAction.hpp in Headers */, C6352B911F477032006CCEE3 /* GameAction.h in Headers */, 2A43D2BA2225B8D900E8F73B /* RideSetVehiclesAction.hpp in Headers */, 2A43D2C02225B91A00E8F73B /* RideSetVehiclesAction.hpp in Headers */, + 2ADE2F1C2244187B002598AF /* ParkSetNameAction.hpp in Headers */, 2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */, + 937A92132242CCB300B09278 /* LandBuyRightsAction.hpp in Headers */, 2A61CAF72229E5720095AD67 /* FootpathPlaceAction.hpp in Headers */, 2A43D2C22225B91A00E8F73B /* LoadOrQuitAction.hpp in Headers */, C62D838B1FD36D6F008C04F1 /* EditorObjectSelectionSession.h in Headers */, + 2ADE2F152244187B002598AF /* StaffSetPatrolAreaAction.hpp in Headers */, 2A43D2BC2225B8D900E8F73B /* LoadOrQuitAction.hpp in Headers */, + 2ADE2F0E2244187B002598AF /* StaffHireNewAction.hpp in Headers */, + 2ADE2F112244187B002598AF /* GuestSetFlagsAction.hpp in Headers */, + 2ADE2F27224418B2002598AF /* Random.hpp in Headers */, 9344BEF920C1E6180047D165 /* Crypt.h in Headers */, 939A35A220C12FFD00630B3F /* InteractiveConsole.h in Headers */, 2ACBAB172226850A0034FB91 /* RideSetSetting.hpp in Headers */, + 2ADE2F0C2244187B002598AF /* SmallSceneryRemoveAction.hpp in Headers */, + 2ADE2F0D2244187B002598AF /* LargeSceneryRemoveAction.hpp in Headers */, 93CBA4C320A7502E00867D56 /* Imaging.h in Headers */, + 2ADE2F2B224418B2002598AF /* JobPool.hpp in Headers */, + 2ADE2F182244187B002598AF /* StaffSetNameAction.hpp in Headers */, + 2ADE2F3622441960002598AF /* RideTypes.h in Headers */, 2A61CAF52229E5720095AD67 /* FootpathSceneryPlaceAction.hpp in Headers */, + 2ADE2F122244187B002598AF /* ParkSetLoanAction.hpp in Headers */, 9308DA05209908090079EE96 /* Surface.h in Headers */, 93DE9753209C3C1000FB1CC8 /* GameState.h in Headers */, + 2ADE2F132244187B002598AF /* GuestSetNameAction.hpp 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 */, + 2ADE2F102244187B002598AF /* TrackSetBrakeSpeedAction.hpp in Headers */, C6352B961F477032006CCEE3 /* RideSetStatus.hpp in Headers */, 2A43D2BB2225B8D900E8F73B /* SmallSceneryPlaceAction.hpp in Headers */, + 2ADE2F342244191E002598AF /* VirtualFloor.h in Headers */, + 2ADE2F072244187B002598AF /* MazeSetTrackAction.hpp in Headers */, 2AAFD800220DD3D2002461A4 /* LandSetHeightAction.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3545,7 +3727,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.11\"\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 +3741,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; @@ -3799,6 +3981,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 */, @@ -3944,7 +4127,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 */, @@ -4071,6 +4254,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 */, diff --git a/appveyor.yml b/appveyor.yml index 5313baf79f..5a5d3893b1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ cache: - secure-file -> 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: @@ -17,6 +17,9 @@ platform: - Win32 - x64 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..f875eb6045 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,19 @@ 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) ## Toolchain * (Balletie) - macOS 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/cs-CZ.txt b/data/language/cs-CZ.txt index d45d00f869..c03ebfa871 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: @@ -3772,6 +3771,31 @@ 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. ############################################################################### ## RCT2 Scenarios diff --git a/data/language/da-DK.txt b/data/language/da-DK.txt index 62006072f7..f5c577a61c 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... @@ -3774,6 +3774,31 @@ 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. ############# # Scenarios # @@ -3894,152 +3919,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..af9eae6e7d 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 @@ -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 :] @@ -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... @@ -2430,7 +2430,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: @@ -3590,8 +3589,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 +3625,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 @@ -3733,8 +3732,50 @@ 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} ############# # Scenarios # diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index e11240e1af..d4841c26c4 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 @@ -3724,7 +3724,7 @@ STR_6273 :Music STR_6274 :Can't set colour scheme... STR_6275 :{WINDOW_COLOUR_2}Station style: STR_6276 :{RED}{STRINGID} has guests getting stuck, possibly due to invalid ride type or operating mode. -STR_6277 :{WINDOW_COLOUR_2}Station index: {BLACK}{COMMA16} +STR_6277 :{WINDOW_COLOUR_2}Station index: {BLACK}{STRINGID} STR_6278 :Autosave amount STR_6279 :{SMALLFONT}{BLACK}Number of autosaves that should be kept STR_6280 :{SMALLFONT}{BLACK}Chat @@ -3751,6 +3751,31 @@ 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. ############# # 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/fr-FR.txt b/data/language/fr-FR.txt index 4dfafbbfe5..8c9a8d227c 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 : @@ -3766,6 +3765,31 @@ 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. ############# # Scenarios # diff --git a/data/language/hu-HU.txt b/data/language/hu-HU.txt index ea3aadac3f..ee825a3272 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 @@ -142,6 +142,11 @@ 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_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_0582 :Önvezető légpárnás járművek @@ -149,6 +154,7 @@ STR_0584 :Speciális, az utasok pedálozásával hajtott biciklik futnak egy 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 @@ -413,7 +419,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 +469,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 +518,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 +539,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 +574,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 +728,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 hosszabí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 +745,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,7 +772,11 @@ 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 @@ -756,6 +793,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} @@ -790,6 +829,9 @@ 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) @@ -981,6 +1023,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 +1041,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,6 +1065,8 @@ 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 @@ -1051,18 +1098,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 +1127,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 +1152,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 +1177,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 +1236,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 +1268,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 +1302,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 +1312,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... @@ -1486,6 +1556,7 @@ 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_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 @@ -1501,6 +1572,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 @@ -1648,8 +1720,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 +1734,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ú hullámvasuat +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 +1779,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 @@ -1721,10 +1802,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 +1828,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 +1845,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 :??? @@ -1943,6 +2029,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 +2118,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 @@ -2086,7 +2173,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 +2226,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... @@ -2181,9 +2310,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 +2341,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 +2360,37 @@ 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íszeletelemekre, 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_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 +2401,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 +2422,10 @@ 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_3217 :Birtokolt föld +STR_3218 :Birtokolt építési jogok +STR_3219 :Eladó föld +STR_3220 :Eladó építési jogok STR_3232 :Beállítások - pénzügyi STR_3233 :Beállítások - vendégek STR_3234 :Beállítások - park @@ -2270,9 +2435,12 @@ 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_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_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_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: @@ -2381,6 +2549,7 @@ STR_5123 :Játékok felújítása STR_5125 :Minden lerombolható STR_5126 :Véletlenszerű főcímzene STR_5130 :Térkép mérete +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 @@ -2389,10 +2558,10 @@ 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 @@ -2414,10 +2583,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 @@ -2475,6 +2649,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 +2669,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 +2680,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}Alapmagssá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 +2726,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 +2821,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 @@ -2648,6 +2861,11 @@ 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_5511 :(ISMERETLEN) @@ -2712,6 +2930,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 @@ -2749,6 +2968,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 +3020,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,6 +3031,7 @@ 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 @@ -2779,11 +3042,18 @@ 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 +3062,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 +3078,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 +3086,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 +3119,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 @@ -2935,24 +3223,68 @@ STR_6010 :{COMMA2DP32} m STR_6011 :{COMMA1DP16} láb 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_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 @@ -2983,6 +3315,8 @@ 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 engedélyezése 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 +3328,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 @@ -3108,10 +3443,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 +3469,31 @@ 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. ############# # Scenarios # @@ -3403,6 +3763,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 :Paradicsomi Móló 2 +STR_PARK :Paradicsomi Móló 2 +STR_DTLS :Paradicsomi 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..b0be645a0a 100644 --- a/data/language/it-IT.txt +++ b/data/language/it-IT.txt @@ -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 @@ -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,35 @@ 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à. ############# # Scenarios # diff --git a/data/language/ko-KR.txt b/data/language/ko-KR.txt index c2fd5052e2..5eabb82a68 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,7 +607,7 @@ 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 :열차 @@ -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 :새로운 게임 @@ -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_5863 :{SMALLFONT}{BLACK}알려진 키를 가진 플레이어만 입장을 허용합니다. STR_5864 :이 서버는 알려진 키를 가진 플레이어의 입장만을 허용합니다. -STR_5865 :대화 내역 기록하기 +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 (개발중) @@ -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,8 +3515,8 @@ 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 :모든 땅 소유 @@ -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 :회색 절벽 @@ -3760,6 +3759,31 @@ 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_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}이 설정을 켜면, 초대형 스크린 샷을 찍을 경우 배경색이 기본 검은색 대신 투명으로 처리됩니다. ############# @@ -3810,12 +3834,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 +3849,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 +3894,7 @@ STR_DTLS :무지개 계곡의 지역 당국은 풍경을 바꾸거나 큰 나 STR_SCNR :Thunder Rock STR_PARK :천둥 바위 -STR_DTLS :천둥 바위는 사막 한가운데에서 많은 관광객을 끌어 모으고 있습니다. 사용 가능한 공간을 이용해 놀이기구를 건설해서 더 많은 사람들을 끌어 보세요. +STR_DTLS :천둥 바위는 사막 한가운데에서 많은 관광객을 끌어모으고 있습니다. 사용 가능한 공간을 이용해 놀이기구를 건설해서 더 많은 사람을 끌어 보세요. STR_SCNR :Mega Park @@ -3881,12 +3905,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 +4015,7 @@ STR_DTLS :사막 한가운데의 오아시스에 놀이공원을 건설해보 STR_SCNR :Rotting Heights STR_PARK :썩어버린 언덕 -STR_DTLS :지나치게 성장해서 황폐화되어버린 이 곳을 다시 멋진 공원으로 바꿀 수 있습니까? +STR_DTLS :지나치게 성장해서 황폐화되어버린 이곳을 다시 멋진 공원으로 바꿀 수 있습니까? STR_SCNR :Fiasco Forest @@ -4043,7 +4067,7 @@ STR_DTLS :이번 롤러코스터 건설 도전에서는 휴화산이 무대 STR_SCNR :Arid Heights STR_PARK :건조한 언덕 -STR_DTLS :아무런 재정적 제한 없는 상황에서, 지속적으로 손님들을 행복하게 만들 수 있는 공원을 사막에 건설해보세요. +STR_DTLS :아무런 재정적 제한 없는 상황에서, 손님들을 언제나 행복하게 만들 수 있는 공원을 사막에 건설해보세요. STR_SCNR :Razor Rocks @@ -4058,12 +4082,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 +4284,12 @@ STR_DTLS :이 작은 시장에 고객들을 유치하기 위해 놀이기구 STR_SCNR :이상한 성 STR_PARK :이상한 성 -STR_DTLS :당신은 큰 성을 물려받았습니다. 이제 이 성을 작은 놀이공원으로 바꾸는 것이 당신이 해야할 일입니다. +STR_DTLS :당신은 큰 성을 물려받았습니다. 이제 이 성을 작은 놀이공원으로 바꾸는 것이 당신이 해야 할 일입니다. STR_SCNR :먼지투성이 그린 STR_PARK :먼지투성이 그린 -STR_DTLS :사막의 고속도로 교차로 근처에 있는 먼지투성이 그린을 작은 골프 리조트에서 번화한 놀이공원으로 개발할 수 있는 기회가 생겼습니다. +STR_DTLS :사막의 고속도로 교차로 근처에 있는 먼지투성이 그린을 작은 골프 리조트에서 번화한 놀이공원으로 개발할 기회가 생겼습니다. STR_SCNR :일렉트릭 농장 @@ -4310,7 +4334,7 @@ STR_DTLS :자본에는 제한이 없지만 호수가 있는 곳이기 때문 STR_SCNR :무지개 정상 STR_PARK :무지개 정상 -STR_DTLS :이 공원은 산 허리에 만들어야 하고, 높은 건물은 지을 수 없습니다. 공원을 확장하여 성공할 자신이 있습니까? +STR_DTLS :이 공원은 산허리에 만들어야 하고, 높은 건물은 지을 수 없습니다. 공원을 확장하여 성공할 자신이 있습니까? STR_SCNR :식스 플래그 벨기에 @@ -4383,7 +4407,7 @@ STR_DTLS :문화 인식 프로그램의 일환으로, 오스트레일리아 STR_SCNR :오스트레일리아 - 해변의 즐거움 STR_PARK :해변의 바베큐 파티 -STR_DTLS :근처 지역 사업가의 해안 공원이 파산해버렸습니다. 이미 작은 공원을 운영하고 있는 당신은 건설사로부터 그 공원을 구입하였습니다. 두 공원을 합쳐 크게 사업을 확장해보세요. +STR_DTLS :근처 지역 사업가의 해안 공원이 파산해버렸습니다. 이미 작은 공원을 운영하는 당신은 건설사로부터 그 공원을 구입하였습니다. 두 공원을 합쳐 크게 사업을 확장해보세요. STR_SCNR :유럽 - 유럽 문화 축제 @@ -4393,20 +4417,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 +4447,7 @@ STR_DTLS :소중한 열대우림 속의 제한된 공간만을 받았습니 STR_SCNR :남 아메리카 - 리오 축제 STR_PARK :슈거로프 해안 -STR_DTLS :리오 근처의 작은 공원을 운영하고 있는데 은행이 대출금을 조기 회수하려 합니다. 때이른 빚 독촉을 갚기 위해서 빨리 돈을 벌어야 합니다. +STR_DTLS :리오 근처의 작은 공원을 운영하고 있는데 은행이 대출금을 조기 회수하려 합니다. 때 이른 빚 독촉을 갚기 위해서 빨리 돈을 벌어야 합니다. ############################################################################### ## Time Twister Scenarios @@ -4434,14 +4458,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 +4495,7 @@ STR_DTLS :당신은 쥐라기 시대 놀이공원을 건설하는 임무를 STR_SCNR :선사시대 - 석기시대 STR_PARK :돌무더기 산책 -STR_DTLS :고속도로 개발자들을 막고 고대 열석을 보호하기 위해, 석기 시대 테마파크를 건설하여 수익을 내야 합니다. 하지만 사람이 살기 조금 힘든 곳인만큼 손님을 모으기가 어려울 것입니다. +STR_DTLS :고속도로 개발자들을 막고 고대 열석을 보호하기 위해, 석기 시대 테마파크를 건설하여 수익을 내야 합니다. 하지만 사람이 살기 조금 힘든 곳인 만큼 손님을 모으기가 어려울 것입니다. STR_SCNR :광란의 20년대 - 감옥 섬 diff --git a/data/language/nl-NL.txt b/data/language/nl-NL.txt index 30ec7939d1..af8b24e7ee 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 @@ -3767,6 +3766,31 @@ 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. ############# # Scenarios # diff --git a/data/language/sv-SE.txt b/data/language/sv-SE.txt index 3257ed5c64..e321ff9868 100644 --- a/data/language/sv-SE.txt +++ b/data/language/sv-SE.txt @@ -552,8 +552,8 @@ 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 @@ -1109,7 +1109,7 @@ STR_1726 :Marken är inte till salu! STR_1727 :Bygglov är inte till salu! 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 @@ -2238,7 +2238,7 @@ STR_2977 :Personalnamn STR_2978 :Skriv in nytt namn på personal: STR_2979 :Kan inte namnge personal... 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... @@ -3736,6 +3736,10 @@ STR_6265 :{SMALLFONT}{BLACK}Om aktiverad kommer spelet använda filhanteraren STR_6266 :Öppna mappen för anpassat innehåll STR_6267 :Öppna rutinspekteraren +STR_6307 :Färgschema: {BLACK}{STRINGID} +STR_6308 :“{STRINGID}{OUTLINE}{TOPAZ}”{NEWLINE}{STRINGID} +STR_6309 :Återanslut + ############# diff --git a/data/language/zh-CN.txt b/data/language/zh-CN.txt index ad89e03fbb..7dbb822701 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 :单位 @@ -1740,15 +1740,15 @@ STR_2382 :土地 STR_2383 :水面 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}在{MONTHYEAR} 之前乐园内至少有 {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}秒 @@ -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/distribution/changelog.txt b/distribution/changelog.txt index 71b0695a56..7aef4f3128 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,3 +1,73 @@ +0.2.2+ (in development) +------------------------------------------------------------------------ +- 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 +101,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 +126,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 +143,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/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..c93724bcb9 100644 --- a/openrct2.proj +++ b/openrct2.proj @@ -68,10 +68,10 @@ 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.2b/title-sequence-v0.1.2b.zip + 19263f8ca383345959473e64da4785a60f00f420 + https://github.com/OpenRCT2/objects/releases/download/v1.0.11/objects.zip + 8674120086929f9196560d77cada631fb478d7c0 diff --git a/readme.md b/readme.md index 67314da372..7bc8d6de87 100644 --- a/readme.md +++ b/readme.md @@ -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) 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..bc62e53b35 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.11"; + sha256 = "21e83bd2afad735cc0f5d13f9c99a5dee81ea0ef3a6b937c0bfaadb7f0572c34"; }; 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..884ff052cc 100644 --- a/src/openrct2-android/app/build.gradle +++ b/src/openrct2-android/app/build.gradle @@ -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.2b/title-sequence-v0.1.2b.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.11/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..213147af8a 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 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..e6c9b516c4 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 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..c9b0e99538 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 @@ -109,7 +109,6 @@ namespace OpenRCT2::Ui // 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(); diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index f15ff3e78d..197b38ae9f 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 @@ -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..d897f0f641 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 @@ -100,10 +100,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: 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..4e9872a757 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 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..31ee027ba0 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 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..c50b86bfab 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 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..0316349754 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 @@ -148,6 +148,7 @@ public: { _window = (SDL_Window*)_uiContext->GetWindow(); _drawingContext = new OpenGLDrawingContext(this); + _bitsDPI.DrawingEngine = this; # ifdef __ENABLE_LIGHTFX__ lightfx_set_available(false); # endif @@ -881,7 +882,7 @@ void OpenGLDrawingContext::FlushCommandBuffers() void OpenGLDrawingContext::FlushLines() { - if (_commandBuffers.lines.size() == 0) + if (_commandBuffers.lines.empty()) return; _drawLineShader->Use(); @@ -892,7 +893,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..fe1c15945b 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 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..af63ac516c 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)); @@ -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); 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..8a88d2c557 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; } 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..8da84e3d0d 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; @@ -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); } @@ -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_invalidate(window_toolbar); + 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..b7f29a6069 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 @@ -169,27 +169,17 @@ 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) { @@ -317,4 +307,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..5233dbf71f 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 @@ -95,6 +95,7 @@ enum SHORTCUT_HIGHLIGHT_PATH_ISSUES_TOGGLE, SHORTCUT_TILE_INSPECTOR, SHORTCUT_ADVANCE_TO_NEXT_TICK, + SHORTCUT_SCENERY_PICKER, SHORTCUT_COUNT, diff --git a/src/openrct2-ui/input/MouseInput.cpp b/src/openrct2-ui/input/MouseInput.cpp index 316645ccbb..31ff8695b8 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 @@ -105,12 +105,7 @@ 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(); @@ -136,12 +131,7 @@ void game_handle_input() process_mouse_tool(x, y); } - // 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); }); } /** 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..fb7d288f88 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 @@ -16,8 +16,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -160,10 +162,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; @@ -316,8 +319,11 @@ int32_t viewport_interaction_get_item_right(int32_t x, int32_t y, viewport_inter 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); + banner = &gBanners[tileElement->AsWall()->GetBannerIndex()]; + set_map_tooltip_format_arg(0, rct_string_id, STR_MAP_TOOLTIP_BANNER_STRINGID_STRINGID); + set_map_tooltip_format_arg(2, rct_string_id, banner->string_idx); + set_map_tooltip_format_arg(4, rct_string_id, STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY); + set_map_tooltip_format_arg(6, rct_string_id, sceneryEntry->name); return info->type; } break; @@ -326,8 +332,11 @@ 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); + banner = &gBanners[tileElement->AsLargeScenery()->GetBannerIndex()]; + set_map_tooltip_format_arg(0, rct_string_id, STR_MAP_TOOLTIP_BANNER_STRINGID_STRINGID); + set_map_tooltip_format_arg(2, rct_string_id, banner->string_idx); + set_map_tooltip_format_arg(4, rct_string_id, STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY); + set_map_tooltip_format_arg(6, rct_string_id, sceneryEntry->name); return info->type; } break; @@ -336,8 +345,15 @@ int32_t viewport_interaction_get_item_right(int32_t x, int32_t y, viewport_inter banner = &gBanners[tileElement->AsBanner()->GetIndex()]; 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); + set_map_tooltip_format_arg(0, rct_string_id, STR_MAP_TOOLTIP_BANNER_STRINGID_STRINGID); + + if (banner->flags & BANNER_FLAG_NO_ENTRY) + set_map_tooltip_format_arg(2, rct_string_id, STR_NO_ENTRY); + else + set_map_tooltip_format_arg(2, rct_string_id, banner->string_idx); + + set_map_tooltip_format_arg(4, rct_string_id, STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY); + set_map_tooltip_format_arg(6, rct_string_id, sceneryEntry->name); return info->type; } @@ -530,8 +546,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); } /** 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..784069250c 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 @@ -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; @@ -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..3e6117ada0 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 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..6ea30ba5a1 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 diff --git a/src/openrct2-ui/windows/Banner.cpp b/src/openrct2-ui/windows/Banner.cpp index 44467e0d20..a1597cbe85 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 @@ -186,20 +189,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); 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; + } } } @@ -242,26 +249,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; + } } } diff --git a/src/openrct2-ui/windows/Changelog.cpp b/src/openrct2-ui/windows/Changelog.cpp index 151e356ca6..97c3308f59 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 diff --git a/src/openrct2-ui/windows/Cheats.cpp b/src/openrct2-ui/windows/Cheats.cpp index d00b47db97..e21ce7a0ba 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 @@ -121,8 +127,6 @@ enum WINDOW_CHEATS_WIDGET_IDX WIDX_GENERAL_GROUP = WIDX_TAB_CONTENT, WIDX_OPEN_CLOSE_PARK, WIDX_PARK_PARAMETERS, - WIDX_SANDBOX_MODE, - WIDX_RESET_DATE, WIDX_OWN_ALL_LAND, WIDX_FORCE_PARK_RATING, WIDX_PARK_RATING_SPINNER, @@ -266,8 +270,6 @@ static rct_widget window_cheats_misc_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(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(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) @@ -545,8 +547,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) | @@ -618,7 +618,7 @@ rct_window* window_cheats_open() 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 +638,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 +699,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 +763,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 +777,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 +796,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 +804,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 +826,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 +920,40 @@ 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_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 +961,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 +985,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; } } @@ -1174,7 +1136,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 +1176,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) { diff --git a/src/openrct2-ui/windows/ClearScenery.cpp b/src/openrct2-ui/windows/ClearScenery.cpp index 24bb6e3a0c..95360993af 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 @@ -244,10 +245,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..21e1a8aa60 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 diff --git a/src/openrct2-ui/windows/DemolishRidePrompt.cpp b/src/openrct2-ui/windows/DemolishRidePrompt.cpp index e0e48fc76d..930a72252f 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 diff --git a/src/openrct2-ui/windows/Dropdown.cpp b/src/openrct2-ui/windows/Dropdown.cpp index a6f48a3ec3..2de05a1501 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 diff --git a/src/openrct2-ui/windows/EditorBottomToolbar.cpp b/src/openrct2-ui/windows/EditorBottomToolbar.cpp index 89bea6e32d..4a1c0523c0 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 @@ -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..43fd558152 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 diff --git a/src/openrct2-ui/windows/EditorMain.cpp b/src/openrct2-ui/windows/EditorMain.cpp index ee97ea15ce..fbeaac9c0c 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 diff --git a/src/openrct2-ui/windows/EditorObjectSelection.cpp b/src/openrct2-ui/windows/EditorObjectSelection.cpp index a00d519cdf..9d61f7c1a8 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(); } @@ -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; @@ -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..1a3874d308 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 diff --git a/src/openrct2-ui/windows/EditorScenarioOptions.cpp b/src/openrct2-ui/windows/EditorScenarioOptions.cpp index dfdcf13897..eaa651b547 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 @@ -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); + auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::NoMoney, newMoneySetting); + GameActions::Execute(&scenarioSetSetting); window_invalidate(w); 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); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidMarketingCampaigns, gParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); window_invalidate(w); break; + } } } @@ -561,9 +563,9 @@ 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 { @@ -574,9 +576,9 @@ static void window_editor_scenario_options_financial_mousedown(rct_window* w, rc 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 { @@ -587,9 +589,9 @@ static void window_editor_scenario_options_financial_mousedown(rct_window* w, rc 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 { @@ -600,9 +602,9 @@ static void window_editor_scenario_options_financial_mousedown(rct_window* w, rc 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 { @@ -613,9 +615,9 @@ static void window_editor_scenario_options_financial_mousedown(rct_window* w, rc 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 { @@ -626,9 +628,9 @@ static void window_editor_scenario_options_financial_mousedown(rct_window* w, rc 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 { @@ -639,9 +641,9 @@ static void window_editor_scenario_options_financial_mousedown(rct_window* w, rc 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 { @@ -652,18 +654,9 @@ static void window_editor_scenario_options_financial_mousedown(rct_window* w, rc 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 { @@ -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); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestsPreferLessIntenseRides, gParkFlags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); window_invalidate(w); 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); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestsPreferMoreIntenseRides, gParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); window_invalidate(w); break; + } } } @@ -854,9 +851,9 @@ 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 { @@ -867,9 +864,9 @@ static void window_editor_scenario_options_guests_mousedown(rct_window* w, rct_w 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 { @@ -880,9 +877,9 @@ static void window_editor_scenario_options_guests_mousedown(rct_window* w, rct_w 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 { @@ -893,9 +890,9 @@ static void window_editor_scenario_options_guests_mousedown(rct_window* w, rct_w 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 { @@ -906,9 +903,9 @@ static void window_editor_scenario_options_guests_mousedown(rct_window* w, rct_w 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 { @@ -919,9 +916,9 @@ static void window_editor_scenario_options_guests_mousedown(rct_window* w, rct_w 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 { @@ -932,9 +929,9 @@ static void window_editor_scenario_options_guests_mousedown(rct_window* w, rct_w 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 { @@ -945,9 +942,9 @@ static void window_editor_scenario_options_guests_mousedown(rct_window* w, rct_w 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 { @@ -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); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidTreeRemoval, gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); window_invalidate(w); 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); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidLandscapeChanges, gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); window_invalidate(w); 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); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ForbidHighConstruction, gParkFlags & PARK_FLAGS_FORBID_HIGH_CONSTRUCTION ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); window_invalidate(w); 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); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::ParkRatingHigherDifficultyLevel, gParkFlags & PARK_FLAGS_DIFFICULT_PARK_RATING ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); window_invalidate(w); 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); + { + auto scenarioSetSetting = ScenarioSetSettingAction( + ScenarioSetSetting::GuestGenerationHigherDifficultyLevel, + gParkFlags & PARK_FLAGS_DIFFICULT_GUEST_GENERATION ? 0 : 1); + GameActions::Execute(&scenarioSetSetting); window_invalidate(w); break; + } } } @@ -1151,9 +1159,9 @@ 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 { @@ -1164,9 +1172,9 @@ static void window_editor_scenario_options_park_mousedown(rct_window* w, rct_wid 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 { @@ -1177,9 +1185,9 @@ static void window_editor_scenario_options_park_mousedown(rct_window* w, rct_wid 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 { @@ -1190,9 +1198,9 @@ static void window_editor_scenario_options_park_mousedown(rct_window* w, rct_wid 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 { @@ -1203,9 +1211,9 @@ static void window_editor_scenario_options_park_mousedown(rct_window* w, rct_wid 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 { @@ -1216,9 +1224,9 @@ static void window_editor_scenario_options_park_mousedown(rct_window* w, rct_wid 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 { @@ -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); + { + auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::ParkChargeMethod, dropdownIndex); + GameActions::Execute(&scenarioSetSetting); window_invalidate(w); 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..1fc785bc4d 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 diff --git a/src/openrct2-ui/windows/Finances.cpp b/src/openrct2-ui/windows/Finances.cpp index 4f0cc9306e..3c6ce80dd2 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 @@ -611,14 +611,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 +737,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; @@ -1197,7 +1197,7 @@ static void window_finances_marketing_paint(rct_window* w, rct_drawpixelinfo* dp 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; } diff --git a/src/openrct2-ui/windows/Footpath.cpp b/src/openrct2-ui/windows/Footpath.cpp index de3891bbeb..eeab5df7c7 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 @@ -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; @@ -1172,9 +1165,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(); } diff --git a/src/openrct2-ui/windows/GameBottomToolbar.cpp b/src/openrct2-ui/windows/GameBottomToolbar.cpp index c50653b43c..c334f9721a 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 diff --git a/src/openrct2-ui/windows/Guest.cpp b/src/openrct2-ui/windows/Guest.cpp index 8eaa297c2f..67f9c4997b 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 @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -35,7 +37,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 +52,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 +62,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 +132,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 +155,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 +167,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 +213,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 +236,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 +244,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 +275,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 +298,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 +306,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 +329,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 +337,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 +360,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 +403,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 +418,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 +430,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 +440,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 +449,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 +458,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 +467,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 +508,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 +523,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; @@ -523,6 +549,52 @@ 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); + set_format_arg(0, rct_string_id, peep->name_string_idx); + set_format_arg(2, uint32_t, peep->id); + + 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. @@ -548,6 +620,10 @@ void window_guest_disable_widgets(rct_window* w) { 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,17 +689,29 @@ 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); @@ -636,8 +720,13 @@ void window_guest_overview_mouse_up(rct_window* w, rct_widgetindex widgetIndex) window_scroll_to_viewport(w); 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; } } @@ -945,6 +1034,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 +1065,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) @@ -1024,38 +1132,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 +1159,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 +1175,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 +1200,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 +1287,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 +1308,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 +1331,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 @@ -1267,38 +1350,6 @@ void window_guest_stats_update(rct_window* w) 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); -} - /** * * rct2: 0x0066ECC1 @@ -1343,6 +1394,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 +1561,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 @@ -1629,33 +1672,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 +1691,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); @@ -1734,15 +1755,6 @@ void window_guest_rides_scroll_paint(rct_window* w, rct_drawpixelinfo* dpi, int3 } } -/** - * - * rct2: 0x00697C16 - */ -void window_guest_finance_resize(rct_window* w) -{ - window_set_resize(w, 210, 148, 210, 148); -} - /** * * rct2: 0x00697BF8 @@ -1755,39 +1767,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 +1780,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 +1858,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 +1868,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; + window_invalidate(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_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 +1890,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 +1921,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,39 +1931,13 @@ 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; + window_invalidate(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_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) @@ -2048,8 +1945,8 @@ static rct_string_id window_guest_inventory_format_item(Peep* peep, int32_t item Ride* ride; // Default arguments - set_format_arg(0, uint32_t, ShopItemImage[item]); - set_format_arg(4, rct_string_id, ShopItemStringIds[item].display); + 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, gParkName); set_format_arg(8, uint32_t, gParkNameArgs); @@ -2057,7 +1954,7 @@ static rct_string_id window_guest_inventory_format_item(Peep* peep, int32_t item 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); @@ -2065,7 +1962,7 @@ static rct_string_id window_guest_inventory_format_item(Peep* peep, int32_t item set_format_arg(8, uint32_t, ride->name_arguments); 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) @@ -2088,15 +1985,15 @@ static rct_string_id window_guest_inventory_format_item(Peep* peep, int32_t item 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); @@ -2131,6 +2028,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 +2062,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++; + window_invalidate(w); +} + +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..7cc6422a24 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 diff --git a/src/openrct2-ui/windows/InstallTrack.cpp b/src/openrct2-ui/windows/InstallTrack.cpp index 7abd2ad2b9..fe669ad2f3 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 @@ -238,6 +238,7 @@ 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; diff --git a/src/openrct2-ui/windows/Land.cpp b/src/openrct2-ui/windows/Land.cpp index 887f7f4cb1..93dc64cc39 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 @@ -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..85b090d743 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 @@ -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..95875d1d0f 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 @@ -240,17 +240,10 @@ 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; } @@ -438,7 +431,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; @@ -793,15 +786,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; } } diff --git a/src/openrct2-ui/windows/Main.cpp b/src/openrct2-ui/windows/Main.cpp index 9464562608..78f29e9da4 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 diff --git a/src/openrct2-ui/windows/Map.cpp b/src/openrct2-ui/windows/Map.cpp index 0b4b29c4e5..d432cb10c8 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 @@ -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; } @@ -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); } } @@ -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) @@ -1235,14 +1237,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; @@ -1552,6 +1552,12 @@ static uint16_t map_window_get_pixel_colour_peep(CoordsXY c) const int32_t maxSupportedTileElementType = (int32_t)std::size(ElementTypeAddColour); while (!(tileElement++)->IsLastForTile()) { + if (tileElement->IsGhost()) + { + colour = MAP_COLOUR(PALETTE_INDEX_21); + break; + } + int32_t tileElementType = tileElement->GetType() >> 2; if (tileElementType >= maxSupportedTileElementType) { @@ -1574,6 +1580,12 @@ static uint16_t map_window_get_pixel_colour_ride(CoordsXY c) TileElement* tileElement = 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: diff --git a/src/openrct2-ui/windows/MapGen.cpp b/src/openrct2-ui/windows/MapGen.cpp index 39ee2ffa29..05727b331d 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 diff --git a/src/openrct2-ui/windows/MapTooltip.cpp b/src/openrct2-ui/windows/MapTooltip.cpp index 0963afc765..83b9032c2e 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 diff --git a/src/openrct2-ui/windows/MazeConstruction.cpp b/src/openrct2-ui/windows/MazeConstruction.cpp index 9f60d6146f..39510dc001 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 diff --git a/src/openrct2-ui/windows/Multiplayer.cpp b/src/openrct2-ui/windows/Multiplayer.cpp index c9cbfc04c5..00fc390038 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 @@ -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); @@ -788,10 +795,12 @@ 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; @@ -838,7 +847,9 @@ static void window_multiplayer_groups_scrollmousedown(rct_window* w, int32_t scr w->selected_list_item = index; window_invalidate(w); - 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) @@ -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/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..97222862ab 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 @@ -271,7 +271,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++; } @@ -368,7 +368,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..f820aa3fea 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 diff --git a/src/openrct2-ui/windows/News.cpp b/src/openrct2-ui/windows/News.cpp index e642ec077e..b0f863dfd4 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 diff --git a/src/openrct2-ui/windows/NewsOptions.cpp b/src/openrct2-ui/windows/NewsOptions.cpp index ec47cbd4a7..dfd7fcf95b 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 diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index 3624587123..fa8ea6855a 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -34,6 +34,7 @@ private: std::vector _downloadedEntries; size_t _currentDownloadIndex{}; std::mutex _downloadedEntriesMutex; + std::mutex _queueMutex; bool _nextDownloadQueued{}; // TODO static due to INTENT_EXTRA_CALLBACK not allowing a std::function @@ -61,6 +62,7 @@ public: void Update() { + std::lock_guard guard(_queueMutex); if (_nextDownloadQueued) { _nextDownloadQueued = false; @@ -87,6 +89,7 @@ private: void QueueNextDownload() { + std::lock_guard guard(_queueMutex); _nextDownloadQueued = true; } diff --git a/src/openrct2-ui/windows/Options.cpp b/src/openrct2-ui/windows/Options.cpp index e45f8d35ce..946f377da4 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) | @@ -693,6 +696,11 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) config_save_default(); window_invalidate(w); break; + case WIDX_MULTITHREADING_CHECKBOX: + gConfigGeneral.multithreading ^= 1; + config_save_default(); + window_invalidate(w); + break; case WIDX_MINIMIZE_FOCUS_LOSS: gConfigGeneral.minimize_fullscreen_focus_loss ^= 1; platform_refresh_video(false); @@ -759,6 +767,11 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex) config_save_default(); window_invalidate(w); break; + case WIDX_TRANSPARENT_SCREENSHOTS_CHECKBOX: + gConfigGeneral.transparent_screenshot ^= 1; + config_save_default(); + window_invalidate(w); + break; } break; @@ -916,11 +929,6 @@ 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; @@ -1711,6 +1719,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 +1739,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 +1927,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); diff --git a/src/openrct2-ui/windows/Park.cpp b/src/openrct2-ui/windows/Park.cpp index ebd224fc6d..4042c34dc5 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 @@ -658,10 +658,10 @@ static void window_park_entrance_mouseup(rct_window* w, rct_widgetindex widgetIn window_text_input_open(w, WIDX_RENAME, STR_PARK_NAME, STR_ENTER_PARK_NAME, gParkName, 0, 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 +718,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); } } } @@ -878,7 +876,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; diff --git a/src/openrct2-ui/windows/Player.cpp b/src/openrct2-ui/windows/Player.cpp index 88316cbdff..a1794e3167 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 @@ -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) + { + window_invalidate(w); + } + }); + 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); } diff --git a/src/openrct2-ui/windows/Research.cpp b/src/openrct2-ui/windows/Research.cpp index b64e2c5f6c..ce8fad6617 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 diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 01d596c6b6..2f941ed953 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 @@ -49,6 +51,7 @@ #include #include #include + using namespace OpenRCT2; enum @@ -95,6 +98,7 @@ enum { WIDX_LOCATE, WIDX_DEMOLISH, WIDX_CLOSE_LIGHT, + WIDX_SIMULATE_LIGHT, WIDX_TEST_LIGHT, WIDX_OPEN_LIGHT, WIDX_RIDE_TYPE, @@ -228,6 +232,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 +399,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) | @@ -977,7 +983,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, @@ -1319,7 +1325,7 @@ static void window_ride_draw_tab_vehicle(rct_drawpixelinfo* dpi, rct_window* w) Ride* ride = get_ride(w->number); - rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride); + rct_ride_entry* rideEntry = ride->GetRideEntry(); if (rideEntry->flags & RIDE_ENTRY_FLAG_VEHICLE_TAB_SCALE_HALF) { clipDPI.zoom_level = 1; @@ -1855,7 +1861,7 @@ 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); @@ -2021,6 +2027,7 @@ static void window_ride_main_mouseup(rct_window* w, rct_widgetindex widgetIndex) 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) @@ -2029,6 +2036,9 @@ static void window_ride_main_mouseup(rct_window* w, rct_widgetindex widgetIndex) 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; @@ -2052,12 +2062,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); } @@ -2120,78 +2150,80 @@ 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); + 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 +2241,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(); @@ -2249,7 +2281,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 +2344,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; @@ -2380,23 +2412,25 @@ 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; @@ -2404,27 +2438,37 @@ static void window_ride_main_dropdown(rct_window* w, rct_widgetindex widgetIndex window_invalidate(w); 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 +2478,6 @@ static void window_ride_main_dropdown(rct_window* w, rct_widgetindex widgetIndex { set_operating_setting(w->number, RideSetSetting::RideType, rideType); } - window_invalidate_all(); } } } @@ -2465,7 +2508,8 @@ static void window_ride_main_update(rct_window* w) return; rct_vehicle* vehicle = &(get_sprite(vehicleSpriteIndex)->vehicle); - if (vehicle->status != 4 && vehicle->status != 22 && vehicle->status != 10 && vehicle->status != 7) + 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; } @@ -2531,13 +2575,26 @@ static void window_ride_main_invalidate(rct_window* w) 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); @@ -2578,11 +2635,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 +2659,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; @@ -2870,20 +2934,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 +2970,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; @@ -2946,7 +3010,7 @@ 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); + rideEntry = ride->GetRideEntry(); set_format_arg(0, rct_string_id, ride->name); set_format_arg(2, uint32_t, ride->name_arguments); @@ -3041,7 +3105,7 @@ static void window_ride_vehicle_paint(rct_window* w, rct_drawpixelinfo* dpi) window_ride_draw_tab_images(dpi, w); ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); + rideEntry = ride->GetRideEntry(); x = w->x + 8; y = w->y + 64; @@ -3103,7 +3167,7 @@ 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); + 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); @@ -3533,7 +3597,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; @@ -4363,7 +4427,7 @@ static void window_ride_colour_mousedown(rct_window* w, rct_widgetindex widgetIn rct_string_id stringId; ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); + rideEntry = ride->GetRideEntry(); colourSchemeIndex = w->ride_colour; dropdownWidget = widget - 1; @@ -4632,7 +4696,7 @@ 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); + rideEntry = ride->GetRideEntry(); set_format_arg(0, rct_string_id, ride->name); set_format_arg(2, uint32_t, ride->name_arguments); @@ -4848,7 +4912,7 @@ static void window_ride_colour_paint(rct_window* w, rct_drawpixelinfo* dpi) rct_ride_entry* rideEntry; ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); + rideEntry = ride->GetRideEntry(); window_draw_widgets(w, dpi); window_ride_draw_tab_images(dpi, w); @@ -4899,7 +4963,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); @@ -4957,7 +5021,7 @@ static void window_ride_colour_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi vehicle_colour vehicleColour; ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); + rideEntry = ride->GetRideEntry(); vehiclePreviewWidget = &window_ride_colour_widgets[WIDX_VEHICLE_PREVIEW]; vehicleColour = ride_get_vehicle_colour(ride, w->vehicleIndex); @@ -5582,7 +5646,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 +5770,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); @@ -5817,7 +5881,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 +5896,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 +5912,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 +5921,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 +5950,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); @@ -5990,11 +6052,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 +6213,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); } } @@ -6474,7 +6526,7 @@ static void window_ride_income_invalidate(rct_window* w) set_format_arg(0, rct_string_id, ride->name); set_format_arg(2, uint32_t, ride->name_arguments); - rideEntry = get_ride_entry_by_ride(ride); + rideEntry = ride->GetRideEntry(); // Primary item w->pressed_widgets &= ~(1 << WIDX_PRIMARY_PRICE_SAME_THROUGHOUT_PARK); @@ -6509,7 +6561,7 @@ 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 @@ -6518,7 +6570,7 @@ static void window_ride_income_invalidate(rct_window* w) { 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; } } @@ -6572,7 +6624,7 @@ static void window_ride_income_paint(rct_window* w, rct_drawpixelinfo* dpi) window_ride_draw_tab_images(dpi, w); ride = get_ride(w->number); - rideEntry = get_ride_entry_by_ride(ride); + rideEntry = ride->GetRideEntry(); 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 +6636,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 +6657,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; @@ -6829,17 +6881,17 @@ static void window_ride_customer_paint(rct_window* w, rct_drawpixelinfo* dpi) // Queue time if (gRideClassifications[ride->type] == RIDE_CLASS_RIDE) { - 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 +6899,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; diff --git a/src/openrct2-ui/windows/RideConstruction.cpp b/src/openrct2-ui/windows/RideConstruction.cpp index 9e11e4bee9..dcf92f6bb2 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 } }; @@ -539,8 +542,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); @@ -624,7 +627,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 +676,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; + } } } @@ -1653,8 +1666,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 +1689,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 +1736,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 +1803,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,7 +1813,16 @@ 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) { @@ -1814,7 +1835,7 @@ static void window_ride_construction_construct(rct_window* w) _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 +1944,15 @@ 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 + { + _stationConstructed = get_ride(w->number)->num_stations != 0; + window_ride_construction_mouseup_demolish_next_piece(x, y, z, direction, type); + } }); GameActions::Execute(&trackRemoveAction); @@ -2017,7 +2041,7 @@ static void window_ride_construction_update(rct_window* w) // 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; @@ -2243,6 +2267,22 @@ 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); @@ -2316,26 +2356,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); @@ -2376,7 +2397,6 @@ static void sub_6CBCE2( { Ride* ride; const rct_preview_track* trackBlock; - int32_t offsetX, offsetY; paint_session* session = paint_session_alloc(dpi, 0); trackDirection &= 3; @@ -2397,33 +2417,15 @@ static void sub_6CBCE2( 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 +2442,20 @@ 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.AsTrack()->SetHasChain((edx & 0x10000) != 0); _tempTrackTileElement.flags = quarterTile.GetBaseQuarterOccupied() | TILE_ELEMENT_FLAG_LAST_TILE; _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]); @@ -2517,7 +2519,7 @@ 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); + rct_ride_entry* rideEntry = ride->GetRideEntry(); int32_t rideType = (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_TYPE) ? RideData4[ride->type].alternate_type : ride->type; @@ -3332,40 +3334,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; } /** @@ -3458,9 +3439,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(); } @@ -3497,10 +3482,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; @@ -3526,16 +3509,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.x / 32, selectedTile.y / 32); if (z > highestZ) highestZ = z; } - selectedTile++; } } // loc_6CC8BF: @@ -3751,17 +3732,14 @@ 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.x / 32, selectedTile.y / 32); if (z > highestZ) highestZ = z; - - selectedTile++; } } diff --git a/src/openrct2-ui/windows/RideList.cpp b/src/openrct2-ui/windows/RideList.cpp index f66b137540..4c5f5491f3 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 @@ -695,7 +695,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 +708,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; @@ -906,7 +906,7 @@ void window_ride_list_refresh_list(rct_window* w) 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)) + if (ride->GetTotalQueueLength() <= otherRide->GetTotalQueueLength()) break; window_bubble_list_item(w, current_list_position); @@ -916,7 +916,7 @@ void window_ride_list_refresh_list(rct_window* w) 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)) + if (ride->GetMaxQueueTime() <= otherRide->GetMaxQueueTime()) break; window_bubble_list_item(w, current_list_position); diff --git a/src/openrct2-ui/windows/SavePrompt.cpp b/src/openrct2-ui/windows/SavePrompt.cpp index 92fa50696e..ba95404a92 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 diff --git a/src/openrct2-ui/windows/Scenery.cpp b/src/openrct2-ui/windows/Scenery.cpp index d20843848e..3a1db09df6 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 @@ -579,6 +580,7 @@ static void window_scenery_mouseup(rct_window* w, rct_widgetindex widgetIndex) gWindowSceneryPaintEnabled = 0; gWindowSceneryClusterEnabled = 0; gWindowSceneryEyedropperEnabled = !gWindowSceneryEyedropperEnabled; + scenery_remove_ghost_tool_placement(); window_invalidate(w); break; case WIDX_SCENERY_BUILD_CLUSTER_BUTTON: @@ -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; diff --git a/src/openrct2-ui/windows/ServerList.cpp b/src/openrct2-ui/windows/ServerList.cpp index 18256723c6..7bc4c7a175 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) @@ -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(); - } + _serverList.Add(entry); + _serverList.WriteFavourites(); window_invalidate(w); 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 = {}; + window_invalidate(w); } } - - 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..631ea62f60 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]; @@ -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..95c4ac7720 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 diff --git a/src/openrct2-ui/windows/Sign.cpp b/src/openrct2-ui/windows/Sign.cpp index 5c02229952..938e0e5ef2 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 diff --git a/src/openrct2-ui/windows/Staff.cpp b/src/openrct2-ui/windows/Staff.cpp index c31fc30c34..cd11b31c85 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 @@ -461,13 +463,19 @@ void window_staff_overview_mouseup(rct_window* w, rct_widgetindex widgetIndex) 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: @@ -1197,9 +1205,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 +1225,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 +1240,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 +1266,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 +1274,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 +1299,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) { diff --git a/src/openrct2-ui/windows/StaffFirePrompt.cpp b/src/openrct2-ui/windows/StaffFirePrompt.cpp index 31acc4e44d..f3ae0ded12 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); diff --git a/src/openrct2-ui/windows/StaffList.cpp b/src/openrct2-ui/windows/StaffList.cpp index d969fcb494..289e3dba9e 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: @@ -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); diff --git a/src/openrct2-ui/windows/TextInput.cpp b/src/openrct2-ui/windows/TextInput.cpp index 6ac78dcec5..70148edac3 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 diff --git a/src/openrct2-ui/windows/Themes.cpp b/src/openrct2-ui/windows/Themes.cpp index 85f41a37ea..0b29e1f178 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 @@ -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..44dc7a9879 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[] = { @@ -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 @@ -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), @@ -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) @@ -596,7 +599,7 @@ 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 { @@ -616,25 +619,22 @@ static void window_tile_inspector_load_tile(rct_window* w, TileElement* elementT 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 +642,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) @@ -669,105 +667,115 @@ static void window_tile_inspector_paste_element(rct_window* w) 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 +784,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) @@ -867,6 +873,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 +919,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; @@ -988,18 +1005,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 +1245,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 +1291,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; @@ -1391,12 +1414,14 @@ static void window_tile_inspector_invalidate(rct_window* w) // 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)); @@ -1495,25 +1520,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 +1570,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: { @@ -1711,12 +1745,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( @@ -1729,8 +1757,9 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) gfx_draw_string(dpi, (char*)"Y:", COLOUR_DARK_GREEN, 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_DARK_GREEN, w->x + 43, w->y + 24); + gfx_draw_string_right(dpi, STR_FORMAT_INTEGER, &tileCoords.y, COLOUR_DARK_GREEN, w->x + 113, w->y + 24); } else { @@ -1858,8 +1887,22 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) 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_DARK_GREEN, 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_DARK_GREEN, x, y + 55); + } + + rct_string_id colourScheme = ColourSchemeNames[trackElement->GetColourScheme()]; + gfx_draw_string_left(dpi, STR_TILE_INSPECTOR_COLOUR_SCHEME, &colourScheme, COLOUR_DARK_GREEN, x, y + 66); // Properties // Raise / lower label @@ -1927,11 +1970,9 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi) 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); } @@ -2116,7 +2157,8 @@ 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 @@ -2186,7 +2228,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 +2249,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..ccde014f22 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 diff --git a/src/openrct2-ui/windows/TitleEditor.cpp b/src/openrct2-ui/windows/TitleEditor.cpp index 022d605fae..f6855bda54 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 diff --git a/src/openrct2-ui/windows/TitleExit.cpp b/src/openrct2-ui/windows/TitleExit.cpp index a8da964a7f..f3b7225c48 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 diff --git a/src/openrct2-ui/windows/TitleLogo.cpp b/src/openrct2-ui/windows/TitleLogo.cpp index d9cf99afa1..392fccfd69 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 diff --git a/src/openrct2-ui/windows/TitleMenu.cpp b/src/openrct2-ui/windows/TitleMenu.cpp index 54ed089a81..b204728c0a 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 diff --git a/src/openrct2-ui/windows/TitleOptions.cpp b/src/openrct2-ui/windows/TitleOptions.cpp index dc4ec747fa..e744097a1d 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 diff --git a/src/openrct2-ui/windows/TitleScenarioSelect.cpp b/src/openrct2-ui/windows/TitleScenarioSelect.cpp index 6be6a7113d..496e30fb56 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 diff --git a/src/openrct2-ui/windows/Tooltip.cpp b/src/openrct2-ui/windows/Tooltip.cpp index 876ee10024..fa5c25e7d5 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 diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index d28c609c2c..dadbdc6735 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,11 +25,25 @@ #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); @@ -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,11 +1071,11 @@ 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: @@ -1084,10 +1084,11 @@ static void repaint_scenery_tool_down(int16_t x, int16_t y, rct_widgetindex widg rct_scenery_entry* scenery_entry = get_banner_entry(banner->type); if (scenery_entry->banner.flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR) { - 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 repaintScenery = BannerSetColourAction( + { grid_x, grid_y, tile_element->base_height * 8, tile_element->AsBanner()->GetPosition() }, + gWindowSceneryPrimaryColour); + + GameActions::Execute(&repaintScenery); } break; } @@ -1423,12 +1424,8 @@ 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) @@ -1787,7 +1784,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, @@ -1841,32 +1838,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(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z); + } + }); + auto res = GameActions::Execute(&wallPlaceAction); break; } case SCENERY_TYPE_LARGE: @@ -1879,41 +1886,72 @@ 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(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z); + } + else + { + audio_play_sound_at_location(SOUND_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(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z); + context_open_detail_window(WD_BANNER, bannerIndex); + } + }); + GameActions::Execute(&bannerPlaceAction); break; } } @@ -2331,16 +2369,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 +2465,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); } } @@ -2476,10 +2524,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); @@ -2521,42 +2569,60 @@ static money32 try_place_ghost_scenery( 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 res->Cost; 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); @@ -2569,21 +2635,38 @@ static money32 try_place_ghost_scenery( gSceneryGhostType |= SCENERY_GHOST_FLAG_3; 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 (cost == MONEY32_UNDEFINED) - return cost; + if (res->Error != GA_ERROR::OK) + return MONEY32_UNDEFINED; - 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 +2845,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 +2857,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 +2967,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 +2999,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 +3032,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; } } @@ -3028,11 +3125,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 +3141,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 +3173,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; @@ -3238,24 +3334,37 @@ static void top_toolbar_rotate_menu_dropdown(int16_t dropdownIndex) } } -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 +3373,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 +3475,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 +3502,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; } } } diff --git a/src/openrct2-ui/windows/TrackDesignManage.cpp b/src/openrct2-ui/windows/TrackDesignManage.cpp index b74e679e0d..b2d7f0d7bf 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 diff --git a/src/openrct2-ui/windows/TrackDesignPlace.cpp b/src/openrct2-ui/windows/TrackDesignPlace.cpp index 34aad6ab1c..36a418bec6 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 @@ -283,7 +283,7 @@ 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; + uint16_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; window_track_place_attempt_placement(_trackDesign, mapX, mapY, mapZ, flags, &cost, &rideIndex); if (cost != MONEY32_UNDEFINED) { @@ -404,7 +404,7 @@ static void window_track_place_clear_provisional() { 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, + _trackDesign, 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; } diff --git a/src/openrct2-ui/windows/TrackList.cpp b/src/openrct2-ui/windows/TrackList.cpp index b81e0111da..9ebad3bc71 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 @@ -499,7 +499,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 @@ -544,20 +544,14 @@ 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; @@ -718,7 +712,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); @@ -810,10 +804,7 @@ static bool track_list_load_design_for_preview(utf8* path) _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, _trackDesignPreviewPixels.data()); return true; } return false; diff --git a/src/openrct2-ui/windows/ViewClipping.cpp b/src/openrct2-ui/windows/ViewClipping.cpp index 0a461e1c74..ab720aa3ab 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 diff --git a/src/openrct2-ui/windows/Viewport.cpp b/src/openrct2-ui/windows/Viewport.cpp index dd6d7c96dc..bc61db577d 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 diff --git a/src/openrct2-ui/windows/Water.cpp b/src/openrct2-ui/windows/Water.cpp index aaaf504ddd..779bfab5bf 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 { @@ -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..76c45cb3f4 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(); 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..29831bc446 100644 --- a/src/openrct2/CMakeLists.txt +++ b/src/openrct2/CMakeLists.txt @@ -1,12 +1,86 @@ -# 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 @@ -34,65 +108,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 +122,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 +146,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 +170,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 +204,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..a7758e77d7 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,6 +11,7 @@ #include "GameState.h" #include "actions/ParkSetLoanAction.hpp" +#include "actions/SetCheatAction.hpp" #include "config/Config.h" #include "localisation/Localisation.h" #include "network/network.h" @@ -49,620 +50,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 +74,109 @@ 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); +} + +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..a582b98c1e 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,56 @@ 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, + Count, }; enum @@ -113,15 +112,8 @@ enum #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); #endif diff --git a/src/openrct2/CmdlineSprite.cpp b/src/openrct2/CmdlineSprite.cpp index 89671fadd0..d3b4bf375b 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 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..73f44ed6b9 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" @@ -92,6 +93,7 @@ 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 @@ -133,6 +135,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); @@ -210,6 +213,11 @@ namespace OpenRCT2 return _replayManager.get(); } + IGameStateSnapshots* GetGameStateSnapshots() override + { + return _gameStateSnapshots.get(); + } + int32_t GetDrawingEngineType() override { return _drawingEngineType; @@ -220,6 +228,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 +347,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 +427,8 @@ namespace OpenRCT2 lightfx_init(); #endif } + gScenarioTicks = 0; - util_srand((uint32_t)time(nullptr)); input_reset_place_obj_modifier(); viewport_init_all(); @@ -425,7 +442,6 @@ namespace OpenRCT2 void InitialiseDrawingEngine() final override { assert(_drawingEngine == nullptr); - assert(_painter == nullptr); _drawingEngineType = gConfigGeneral.drawing_engine; @@ -452,7 +468,6 @@ namespace OpenRCT2 } else { - _painter = std::make_unique(_uiContext); try { drawingEngine->Initialise(); @@ -461,7 +476,6 @@ namespace OpenRCT2 } catch (const std::exception& ex) { - _painter = nullptr; if (_drawingEngineType == DRAWING_ENGINE_SOFTWARE) { _drawingEngineType = DRAWING_ENGINE_NONE; @@ -486,7 +500,6 @@ namespace OpenRCT2 void DisposeDrawingEngine() final override { _drawingEngine = nullptr; - _painter = nullptr; } bool LoadParkFromFile(const std::string& path, bool loadTitleScreenOnFail) final override @@ -535,6 +548,8 @@ namespace OpenRCT2 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 +559,7 @@ namespace OpenRCT2 game_load_init(); if (network_get_mode() == NETWORK_MODE_SERVER) { - network_send_map(); + sendMap = true; } } else @@ -552,7 +567,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 +577,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) @@ -991,6 +1010,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..45232073a9 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,6 +16,8 @@ #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 "localisation/Localisation.h" @@ -193,7 +195,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); } /** @@ -491,7 +500,7 @@ namespace Editor return false; } - if (gParkEntrances.size() == 0) + if (gParkEntrances.empty()) { gGameCommandErrorText = STR_NO_PARK_ENTRANCES; return false; @@ -520,7 +529,7 @@ namespace Editor } } - if (gPeepSpawns.size() == 0) + if (gPeepSpawns.empty()) { gGameCommandErrorText = STR_PEEP_SPAWNS_NOT_SET; return false; @@ -529,219 +538,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 +574,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..4bc2921f7f 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 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..235a88968a 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" @@ -92,11 +93,11 @@ static GAME_COMMAND_CALLBACK_POINTER * const game_command_callback_table[] = { 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 + nullptr, + nullptr, + nullptr, + nullptr }; // clang-format on int32_t game_command_playerid = -1; @@ -407,15 +408,6 @@ int32_t game_do_command_p( // 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(); @@ -446,7 +438,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) @@ -465,7 +457,7 @@ int32_t game_do_command_p( } if (network_get_mode() != NETWORK_MODE_NONE && !(flags & GAME_COMMAND_FLAG_NETWORKED) - && !(flags & GAME_COMMAND_FLAG_GHOST) && !(flags & GAME_COMMAND_FLAG_5) + && !(flags & GAME_COMMAND_FLAG_GHOST) && !(flags & GAME_COMMAND_FLAG_NO_SPEND) && gGameCommandNestLevel == 1) /* Send only top-level commands */ { network_send_gamecmd( @@ -488,7 +480,7 @@ int32_t game_do_command_p( { bool recordCommand = false; bool commandExecutes = (flags & GAME_COMMAND_FLAG_APPLY) && (flags & GAME_COMMAND_FLAG_GHOST) == 0 - && (flags & GAME_COMMAND_FLAG_5) == 0; + && (flags & GAME_COMMAND_FLAG_NO_SPEND) == 0; if (replayManager->IsRecording() && commandExecutes) recordCommand = true; @@ -536,7 +528,7 @@ int32_t game_do_command_p( { // Create a +/- money text effect if (cost != 0 && game_is_not_paused()) - money_effect_create(cost); + rct_money_effect::Create(cost); } } @@ -586,18 +578,7 @@ void game_log_multiplayer_command(int command, const int* eax, const int* ebx, c } 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)) + 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); @@ -611,95 +592,6 @@ void game_log_multiplayer_command(int command, const int* eax, const int* ebx, c 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() @@ -895,7 +787,7 @@ void game_fix_save_vars() } } - if (peepsToRemove.size() > 0) + if (!peepsToRemove.empty()) { // Some broken saves have broken spatial indexes reset_sprite_spatial_index(); @@ -956,6 +848,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) @@ -1266,11 +1161,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 +1168,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..12e733ad51 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 }; diff --git a/src/openrct2/GameState.cpp b/src/openrct2/GameState.cpp index cb6de02a1e..20a32182a2 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,11 @@ #include "Context.h" #include "Editor.h" #include "Game.h" +#include "GameStateSnapshots.h" #include "Input.h" #include "OpenRCT2.h" #include "ReplayManager.h" +#include "config/Config.h" #include "interface/Screenshot.h" #include "localisation/Date.h" #include "localisation/Localisation.h" @@ -228,23 +230,39 @@ void GameState::UpdateLogic() 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 ahead of the server but can be on same tick. + if (gCurrentTicks > network_get_server_tick()) { - // Don't run past the server return; } } 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 +278,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); } @@ -311,3 +329,12 @@ void GameState::UpdateLogic() 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..8349c2961e --- /dev/null +++ b/src/openrct2/GameStateSnapshots.cpp @@ -0,0 +1,578 @@ +#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_type_offset); + 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, name_string_idx); + 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..de73ff7800 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 diff --git a/src/openrct2/Intro.cpp b/src/openrct2/Intro.cpp index 3feaf93ccb..f245d4d342 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 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..eb4ec432bf 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" @@ -442,12 +444,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; @@ -526,6 +528,38 @@ namespace OpenRCT2 result.action->SetFlags(command.ebx & 0xFF); break; } + case GAME_COMMAND_CHEAT: + { + int32_t param1 = command.edx; + int32_t param2 = command.edi; + CheatType cheatType = static_cast(command.ecx); + + result.action = std::make_unique(cheatType, param1, param2); + result.action->SetFlags(command.ebx & 0xFF); + break; + } + case GAME_COMMAND_MODIFY_TILE: + { + int32_t param1 = command.edx; + int32_t param2 = command.edi; + CoordsXY loc = { static_cast((command.ecx & 0xFF) * 32), + static_cast(((command.ecx >> 8) & 0xFF) * 32) }; + TileModifyType type = static_cast(command.eax & 0xFF); + + if (type == TileModifyType::AnyPaste) + { + TileElement copiedElement{}; + uint32_t data[2] = { command.edx, command.edi }; + std::memcpy(&copiedElement, &data[0], 8); + result.action = std::make_unique(loc, type, 0, 0, copiedElement); + } + else + { + result.action = std::make_unique(loc, type, param1, param2); + } + result.action->SetFlags(command.ebx & 0xFF); + break; + } default: throw std::runtime_error("Deprecated game command requires replay translation."); } @@ -610,7 +644,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) @@ -774,10 +808,7 @@ namespace OpenRCT2 bool Compatible(ReplayRecordData& data) { - if (data.version == 1 && ReplayVersion == 2) - return true; - - return false; + return data.version == 1 && ReplayVersion == 2; } bool Serialise(DataSerialiser& serialiser, ReplayRecordData& data) diff --git a/src/openrct2/ReplayManager.h b/src/openrct2/ReplayManager.h index 976ae4dcb9..04ca15ba85 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 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..5a1a3212e9 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 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..25354dc617 --- /dev/null +++ b/src/openrct2/actions/BannerPlaceAction.hpp @@ -0,0 +1,199 @@ +/***************************************************************************** + * Copyright (c) 2014-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.x, _loc.y })) + { + 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.x, _loc.y, _loc.z)) + { + 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); + } + + if (gBanners[_bannerIndex].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); + } + + if (gBanners[_bannerIndex].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, 0); + assert(newTileElement != nullptr); + rct_banner* banner = &gBanners[_bannerIndex]; + + banner->flags = 0; + banner->string_idx = STR_DEFAULT_SIGN; + banner->text_colour = 2; + banner->type = _bannerType; + banner->colour = _primaryColour; + banner->x = _loc.x / 32; + banner->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..6ee339b408 --- /dev/null +++ b/src/openrct2/actions/BannerRemoveAction.hpp @@ -0,0 +1,155 @@ +/***************************************************************************** + * Copyright (c) 2014-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); + } + + rct_banner* banner = &gBanners[bannerElement->GetIndex()]; + + 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); + } + + rct_banner* banner = &gBanners[bannerElement->GetIndex()]; + + 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..fc05c1e749 --- /dev/null +++ b/src/openrct2/actions/BannerSetColourAction.hpp @@ -0,0 +1,110 @@ +/***************************************************************************** + * Copyright (c) 2014-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); + + gBanners[index].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..f58ad620bc 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 diff --git a/src/openrct2/actions/BannerSetStyleAction.hpp b/src/openrct2/actions/BannerSetStyleAction.hpp new file mode 100644 index 0000000000..82488307bf --- /dev/null +++ b/src/openrct2/actions/BannerSetStyleAction.hpp @@ -0,0 +1,196 @@ +/***************************************************************************** + * Copyright (c) 2014-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); + } + + rct_banner* banner = &gBanners[_bannerIndex]; + + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = banner->x * 32 + 16; + res->Position.y = banner->y * 32 + 16; + res->Position.z = tile_element_height(banner->x, banner->y); + + 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(); + + rct_banner* banner = &gBanners[_bannerIndex]; + + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + res->Position.x = banner->x * 32 + 16; + res->Position.y = banner->y * 32 + 16; + res->Position.z = tile_element_height(banner->x, banner->y); + + 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; + 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); + } + else + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_SET_BANNER_TEXT); + } + 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/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..be47524587 100644 --- a/src/openrct2/actions/FootpathPlaceAction.hpp +++ b/src/openrct2/actions/FootpathPlaceAction.hpp @@ -79,12 +79,12 @@ 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); } @@ -399,7 +399,7 @@ private: } } - if (gPeepSpawns.size() == 0) + if (gPeepSpawns.empty()) { gPeepSpawns.emplace_back(); } diff --git a/src/openrct2/actions/FootpathPlaceFromTrackAction.hpp b/src/openrct2/actions/FootpathPlaceFromTrackAction.hpp new file mode 100644 index 0000000000..0a1f675a3b --- /dev/null +++ b/src/openrct2/actions/FootpathPlaceFromTrackAction.hpp @@ -0,0 +1,285 @@ +/***************************************************************************** + * Copyright (c) 2014-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.x, _loc.y })) + { + 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.x, _loc.y, _loc.z)) + { + 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 tileElement = map_get_surface_element_at({ _loc.x, _loc.y }); + if (tileElement == nullptr) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE); + } + auto surfaceElement = tileElement->AsSurface(); + 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 tileElement = map_get_surface_element_at({ _loc.x, _loc.y }); + if (tileElement == nullptr) + { + return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE); + } + auto surfaceElement = tileElement->AsSurface(); + 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 + { + 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..7f7adb7858 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,6 +19,7 @@ #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) @@ -89,7 +90,11 @@ public: if (footpathElement != nullptr) { footpath_queue_chain_reset(); - remove_banners_at_element(_x, _y, footpathElement); + auto bannerRes = RemoveBannersAtElement(_x, _y, footpathElement); + if (bannerRes->Error == GA_ERROR::OK) + { + res->Cost += bannerRes->Cost; + } footpath_remove_edges_at(_x, _y, footpathElement); map_invalidate_tile_full(_x, _y); tile_element_remove(footpathElement); @@ -100,7 +105,7 @@ public: return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_FOOTPATH_FROM_HERE); } - res->Cost = GetRefundPrice(footpathElement); + res->Cost += GetRefundPrice(footpathElement); return res; } @@ -140,4 +145,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..b8fc4e21e2 100644 --- a/src/openrct2/actions/FootpathSceneryPlaceAction.hpp +++ b/src/openrct2/actions/FootpathSceneryPlaceAction.hpp @@ -75,21 +75,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 +126,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..70b40ff6d9 100644 --- a/src/openrct2/actions/FootpathSceneryRemoveAction.hpp +++ b/src/openrct2/actions/FootpathSceneryRemoveAction.hpp @@ -69,17 +69,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..b6789b86e5 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 @@ -131,7 +131,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 +154,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; @@ -290,7 +290,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 +301,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 +326,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 +357,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..352bcab7bb 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 @@ -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. */ diff --git a/src/openrct2/actions/GameActionCompat.cpp b/src/openrct2/actions/GameActionCompat.cpp index fd235d54f7..9002b26a38 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 @@ -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 @@ -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 @@ -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..31cd19b604 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 diff --git a/src/openrct2/actions/LandBuyRightsAction.hpp b/src/openrct2/actions/LandBuyRightsAction.hpp new file mode 100644 index 0000000000..0ac03002ad --- /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.x, centre.y); + + 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)->AsSurface(); + 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..e6d4e4e9ec --- /dev/null +++ b/src/openrct2/actions/LandLowerAction.hpp @@ -0,0 +1,144 @@ +/***************************************************************************** + * Copyright (c) 2014-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.x, _coords.y) }; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + if (isExecuting) + { + audio_play_sound_at_location(SOUND_PLACE_ITEM, _coords.x, _coords.y, tile_element_height(_coords.x, _coords.y)); + } + + uint8_t maxHeight = map_get_highest_land_height( + validRange.GetLeft(), validRange.GetRight(), validRange.GetTop(), validRange.GetBottom()); + + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + TileElement* tileElement = map_get_surface_element_at(x / 32, y / 32); + if (tileElement == nullptr) + continue; + + SurfaceElement* surfaceElement = tileElement->AsSurface(); + 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..b5d2f36090 --- /dev/null +++ b/src/openrct2/actions/LandRaiseAction.hpp @@ -0,0 +1,140 @@ +/***************************************************************************** + * Copyright (c) 2014-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.x, _coords.y) }; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + if (isExecuting) + { + audio_play_sound_at_location(SOUND_PLACE_ITEM, _coords.x, _coords.y, tile_element_height(_coords.x, _coords.y)); + } + + uint8_t minHeight = map_get_lowest_land_height( + validRange.GetLeft(), validRange.GetRight(), validRange.GetTop(), validRange.GetBottom()); + + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32) + { + TileElement* tileElement = map_get_surface_element_at(x / 32, y / 32); + if (tileElement == nullptr) + continue; + + SurfaceElement* surfaceElement = tileElement->AsSurface(); + 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..47bd9b8b51 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); @@ -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.x, _coords.y); footpath_remove_litter(_coords.x, _coords.y, surfaceHeight); if (!gCheatsDisableClearanceChecks) @@ -195,7 +196,7 @@ private: return STR_NONE; } - TileElement* CheckTallTreeObstructions() const + TileElement* CheckTreeObstructions() const { TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32); do @@ -207,7 +208,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; } @@ -259,7 +260,7 @@ private: Ride* 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; diff --git a/src/openrct2/actions/LandSetRightsAction.hpp b/src/openrct2/actions/LandSetRightsAction.hpp new file mode 100644 index 0000000000..533dfb775b --- /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.x, centre.y); + + 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(SOUND_PLACE_ITEM, centre.x, centre.y, centre.z); + } + return res; + } + + GameActionResult::Ptr map_buy_land_rights_for_tile(const CoordsXY loc, bool isExecuting) const + { + SurfaceElement* surfaceElement = map_get_surface_element_at(loc)->AsSurface(); + 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..779a7d6ab5 --- /dev/null +++ b/src/openrct2/actions/LandSmoothAction.hpp @@ -0,0 +1,641 @@ +/***************************************************************************** + * Copyright (c) 2014-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, TileElement* surfaceElement) + const + { + int32_t targetBaseZ = surfaceElement->base_height; + int32_t slope = surfaceElement->AsSurface()->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->AsSurface()->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.x, _coords.y); + + 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.GetLeft(), validRange.GetRight(), validRange.GetTop(), + validRange.GetBottom()); + uint8_t maxHeight = heightOffset + + map_get_highest_land_height( + validRange.GetLeft(), validRange.GetRight(), validRange.GetTop(), + validRange.GetBottom()); + + // 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 + TileElement* surfaceElement = nullptr; + int32_t z1, z2; + for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32) + { + 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) + { + 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->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); + 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->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 + 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(SOUND_PLACE_ITEM, _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..4cd296f0a9 --- /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.x, _loc.y); + 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); + } + + if (gBanners[_bannerId].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.x, curTile.y, 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.x, _loc.y); + 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); + } + + if (gBanners[_bannerId].type != BANNER_NULL) + { + log_error("No free banners available"); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); + } + + rct_banner* banner = &gBanners[_bannerId]; + banner->string_idx = STR_DEFAULT_SIGN; + banner->colour = 2; + banner->text_colour = 2; + banner->flags = BANNER_FLAG_IS_LARGE_SCENERY; + banner->type = 0; + banner->x = _loc.x / 32; + banner->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; + } + + TileElement* tileElement = map_get_surface_element_at({ curTile.x, curTile.y }); + if (tileElement == nullptr) + continue; + + SurfaceElement* surfaceElement = tileElement->AsSurface(); + 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..b51adab4b3 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 @@ -121,7 +121,7 @@ public: } } - if (calculate_cost == true) + if (calculate_cost) res->Cost = scenery_entry->large_scenery.removal_price * 10; return res; diff --git a/src/openrct2/actions/LargeScenerySetColourAction.hpp b/src/openrct2/actions/LargeScenerySetColourAction.hpp new file mode 100644 index 0000000000..7cfff25de3 --- /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.x, _loc.y); + 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.x, currentTile.y, currentTile.z)) + { + 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..80e53ec5bd 100644 --- a/src/openrct2/actions/MazeSetTrackAction.hpp +++ b/src/openrct2/actions/MazeSetTrackAction.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 @@ -202,7 +202,7 @@ 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); @@ -302,7 +302,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; 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..1896e00c26 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 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..007217fcc7 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 diff --git a/src/openrct2/actions/PlacePeepSpawnAction.hpp b/src/openrct2/actions/PlacePeepSpawnAction.hpp index 60c5269bde..4d3472eda4 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 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..2c0e195428 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) @@ -79,42 +78,45 @@ public: 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(); @@ -134,7 +136,7 @@ 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 @@ -184,7 +186,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 +208,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 +303,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..1d8410f50c 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); @@ -61,7 +66,8 @@ public: 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); @@ -124,7 +130,7 @@ 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); @@ -223,10 +229,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; @@ -240,6 +242,9 @@ private: 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)) { @@ -302,7 +307,7 @@ private: { 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); @@ -349,7 +354,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; diff --git a/src/openrct2/actions/RideEntranceExitPlaceAction.hpp b/src/openrct2/actions/RideEntranceExitPlaceAction.hpp index 7c584689d2..3dd6ebc504 100644 --- a/src/openrct2/actions/RideEntranceExitPlaceAction.hpp +++ b/src/openrct2/actions/RideEntranceExitPlaceAction.hpp @@ -79,7 +79,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 +89,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); @@ -156,8 +153,11 @@ public: 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); diff --git a/src/openrct2/actions/RideEntranceExitRemoveAction.hpp b/src/openrct2/actions/RideEntranceExitRemoveAction.hpp index c13f1deaa3..0b67bbddf0 100644 --- a/src/openrct2/actions/RideEntranceExitRemoveAction.hpp +++ b/src/openrct2/actions/RideEntranceExitRemoveAction.hpp @@ -60,7 +60,7 @@ public: 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); } @@ -123,9 +123,12 @@ public: 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); diff --git a/src/openrct2/actions/RideSetAppearanceAction.hpp b/src/openrct2/actions/RideSetAppearanceAction.hpp index 4f58296046..0701910143 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 diff --git a/src/openrct2/actions/RideSetColourScheme.hpp b/src/openrct2/actions/RideSetColourScheme.hpp index a91a0255d1..f68d531a74 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 diff --git a/src/openrct2/actions/RideSetName.hpp b/src/openrct2/actions/RideSetName.hpp index d94f533d0f..076c32ce21 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 diff --git a/src/openrct2/actions/RideSetSetting.hpp b/src/openrct2/actions/RideSetSetting.hpp index 6478bde798..ed560e9053 100644 --- a/src/openrct2/actions/RideSetSetting.hpp +++ b/src/openrct2/actions/RideSetSetting.hpp @@ -82,7 +82,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); } @@ -186,7 +186,7 @@ public: ride_remove_peeps(ride); ride->mode = _value; - ride_update_max_vehicles(ride); + ride->UpdateMaxVehicles(); break; case RideSetSetting::Departure: ride->depart_flags = _value; @@ -243,6 +243,7 @@ public: break; case RideSetSetting::RideType: ride->type = _value; + gfx_invalidate_screen(); break; } diff --git a/src/openrct2/actions/RideSetStatus.hpp b/src/openrct2/actions/RideSetStatus.hpp index 09cd1246f1..66662bc996 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) @@ -73,9 +74,16 @@ public: 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; @@ -123,7 +131,7 @@ public: 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 +147,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 +178,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 +194,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 +212,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..69b89c02ac 100644 --- a/src/openrct2/actions/RideSetVehiclesAction.hpp +++ b/src/openrct2/actions/RideSetVehiclesAction.hpp @@ -92,7 +92,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); } @@ -204,7 +204,7 @@ 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) 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..9835ec4946 --- /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; + 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 } }; + 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 tileElement = map_get_surface_element_at(x, y); + if (tileElement == nullptr) + continue; + + auto surfaceElement = tileElement->AsSurface(); + 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 + { + 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; + } + } + } + + void RenewRides() const + { + int32_t i; + Ride* ride; + + FOR_ALL_RIDES (i, ride) + { + ride->Renew(); + } + window_invalidate_by_class(WC_RIDE); + } + + void MakeDestructible() const + { + 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); + } + + void ResetRideCrashStatus() const + { + 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); + } + + void Set10MinuteInspection() const + { + 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); + } + + 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 + { + 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(); + } + + 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) + { + 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(); + } + + void ParkSetOpen(bool isOpen) const + { + auto parkSetParameter = ParkSetParameterAction(isOpen ? ParkParameter::Open : ParkParameter::Close); + GameActions::ExecuteNested(&parkSetParameter); + } +}; 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..6e0ee08df0 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 diff --git a/src/openrct2/actions/SignSetStyleAction.hpp b/src/openrct2/actions/SignSetStyleAction.hpp index dae643cf23..7493d0fc24 100644 --- a/src/openrct2/actions/SignSetStyleAction.hpp +++ b/src/openrct2/actions/SignSetStyleAction.hpp @@ -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); @@ -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..c616dd80e3 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.x, _loc.y); + int16_t waterHeight = tile_element_water_height(_loc.x, _loc.y); + + 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,13 +176,13 @@ 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)) { - 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 }); @@ -157,7 +192,7 @@ public: int32_t water_height = (surfaceElement->AsSurface()->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,14 +200,15 @@ 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 (static_cast((surfaceElement->AsSurface()->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); } } } @@ -181,7 +217,7 @@ public: && (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_REQUIRE_FLAT_SURFACE)) && !supportsRequired && !isOnWater && surfaceElement != nullptr && (surfaceElement->AsSurface()->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)) @@ -193,13 +229,13 @@ public: { if (surfaceElement->AsSurface()->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.x, _loc.y); + int16_t waterHeight = tile_element_water_height(_loc.x, _loc.y); + + 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()); assert(newElement != nullptr); - gSceneryTileElement = newElement; + res->tileElement = newElement; newElement->SetType(TILE_ELEMENT_TYPE_SMALL_SCENERY); newElement->SetDirection(_loc.direction); SmallSceneryElement* sceneryElement = newElement->AsSmallScenery(); diff --git a/src/openrct2/actions/SmallSceneryRemoveAction.hpp b/src/openrct2/actions/SmallSceneryRemoveAction.hpp index 499099afb5..60344bf39a 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 @@ -82,7 +82,7 @@ public: // 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; diff --git a/src/openrct2/actions/SmallScenerySetColourAction.hpp b/src/openrct2/actions/SmallScenerySetColourAction.hpp new file mode 100644 index 0000000000..937ac551a9 --- /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.x, _loc.y, _loc.z)) + { + 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..c4664833d6 --- /dev/null +++ b/src/openrct2/actions/StaffHireNewAction.hpp @@ -0,0 +1,347 @@ +/***************************************************************************** + * Copyright (c) 2014-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" + +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, +}; + +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(GetFlags())->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_string_idx = staffNames[_staffType]; + 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; + 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.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..5a912e1d2a 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 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..45e8d965e0 --- /dev/null +++ b/src/openrct2/actions/SurfaceSetStyleAction.hpp @@ -0,0 +1,251 @@ +/***************************************************************************** + * Copyright (c) 2014-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 tileElement = map_get_surface_element_at({ x, y }); + if (tileElement == nullptr) + { + continue; + } + + auto surfaceElement = tileElement->AsSurface(); + 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 tileElement = map_get_surface_element_at({ x, y }); + if (tileElement == nullptr) + { + continue; + } + + auto surfaceElement = tileElement->AsSurface(); + 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..ef063b3856 --- /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.x, _loc.y); + + return res; + } +}; diff --git a/src/openrct2/actions/TrackPlaceAction.hpp b/src/openrct2/actions/TrackPlaceAction.hpp index 74dab23c72..ef8e7c9443 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; @@ -68,53 +91,47 @@ public: 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); + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, 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 +141,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,8 +159,7 @@ 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); } } } @@ -166,8 +179,7 @@ public: if (!map_is_location_owned(tileCoords.x, tileCoords.y, tileCoords.z) && !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 +187,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 +220,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 +239,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 +249,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 +276,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 +290,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,17 +301,15 @@ 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) @@ -319,14 +319,12 @@ public: uint8_t waterHeight = tileElement->AsSurface()->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) @@ -335,9 +333,7 @@ public: 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,8 +351,7 @@ 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); } } @@ -386,8 +381,7 @@ 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); } } } @@ -415,25 +409,23 @@ public: 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; @@ -483,9 +475,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 +491,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,14 +503,13 @@ 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 }); @@ -576,7 +567,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)) { @@ -682,7 +673,7 @@ 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) diff --git a/src/openrct2/actions/TrackRemoveAction.hpp b/src/openrct2/actions/TrackRemoveAction.hpp index fb73e5c763..5ad20a6b4b 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 @@ -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..f8bb95666b --- /dev/null +++ b/src/openrct2/actions/WallPlaceAction.hpp @@ -0,0 +1,719 @@ +/***************************************************************************** + * Copyright (c) 2014-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; + uint8_t _edge{ std::numeric_limits::max() }; + 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.x, res->Position.y); + } + + if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY) + && !gCheatsSandboxMode) + { + if (_loc.z == 0) + { + if (!map_is_location_in_park({ _loc.x, _loc.y })) + { + return std::make_unique(GA_ERROR::NOT_OWNED); + } + } + else if (!map_is_location_owned(_loc.x, _loc.y, _loc.z)) + { + 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) + { + TileElement* surfaceElement = map_get_surface_element_at({ _loc.x, _loc.y }); + 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->AsSurface()->GetSlope(); + edgeSlope = EdgeSlopes[slope][_edge & 3]; + if (edgeSlope & EDGE_SLOPE_ELEVATED) + { + targetHeight += 16; + edgeSlope &= ~EDGE_SLOPE_ELEVATED; + } + } + + TileElement* surfaceElement = map_get_surface_element_at({ _loc.x, _loc.y }); + 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->AsSurface()->GetWaterHeight() > 0) + { + uint16_t waterHeight = surfaceElement->AsSurface()->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->AsSurface()->GetSlope() & (1 << newEdge)) + { + if (targetHeight / 8 < newBaseHeight) + { + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); + } + + 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 (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->AsSurface()->GetSlope() & (1 << newEdge)) + { + if (targetHeight / 8 < newBaseHeight) + { + return std::make_unique(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND); + } + + 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 (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); + } + + if (gBanners[_bannerId].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.x, res->Position.y); + } + + uint8_t edgeSlope = 0; + auto targetHeight = _loc.z; + if (targetHeight == 0) + { + TileElement* surfaceElement = map_get_surface_element_at({ _loc.x, _loc.y }); + 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->AsSurface()->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); + } + + if (gBanners[_bannerId].type != BANNER_NULL) + { + log_error("No free banners available"); + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS); + } + + rct_banner* banner = &gBanners[_bannerId]; + banner->string_idx = STR_DEFAULT_SIGN; + banner->colour = 2; + banner->text_colour = 2; + banner->flags = BANNER_FLAG_IS_WALL; + banner->type = 0; + banner->x = _loc.x / 32; + banner->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, 0); + 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; + Ride* 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 = 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 + */ + 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.x, _loc.y)) + { + 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->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, 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..b11059b920 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 diff --git a/src/openrct2/actions/WallSetColourAction.hpp b/src/openrct2/actions/WallSetColourAction.hpp new file mode 100644 index 0000000000..0589114ac1 --- /dev/null +++ b/src/openrct2/actions/WallSetColourAction.hpp @@ -0,0 +1,161 @@ +/***************************************************************************** + * Copyright (c) 2014-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.x, _loc.y }) + && !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..6b7e984f64 --- /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.x, res->Position.y); + int16_t waterHeight = tile_element_water_height(res->Position.x, res->Position.y); + if (waterHeight != 0) + { + z = waterHeight; + } + res->Position.z = z; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + uint8_t minHeight = GetLowestHeight(); + 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) + { + TileElement* tileElement = map_get_surface_element_at(x / 32, y / 32); + if (tileElement == nullptr) + continue; + + SurfaceElement* surfaceElement = tileElement->AsSurface(); + 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(SOUND_LAYING_OUT_WATER, res->Position.x, res->Position.y, res->Position.z); + } + // Force ride construction to recheck area + _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; + + return res; + } + +private: + uint8_t GetLowestHeight() const + { + uint8_t minHeight{ 0 }; + for (int32_t y = _range.GetTop(); y <= _range.GetBottom(); y += 32) + { + for (int32_t x = _range.GetLeft(); x <= _range.GetRight(); x += 32) + { + TileElement* tile_element = map_get_surface_element_at({ x, y }); + if (tile_element == nullptr) + continue; + + uint8_t height = tile_element->AsSurface()->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..28304d1732 --- /dev/null +++ b/src/openrct2/actions/WaterRaiseAction.hpp @@ -0,0 +1,157 @@ +/***************************************************************************** + * Copyright (c) 2014-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.x, res->Position.y); + int16_t waterHeight = tile_element_water_height(res->Position.x, res->Position.y); + if (waterHeight != 0) + { + z = waterHeight; + } + res->Position.z = z; + res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; + + uint8_t maxHeight = GetHighestHeight(); + 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) + { + TileElement* tileElement = map_get_surface_element_at(x / 32, y / 32); + if (tileElement == nullptr) + continue; + + SurfaceElement* surfaceElement = tileElement->AsSurface(); + 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(SOUND_LAYING_OUT_WATER, res->Position.x, res->Position.y, res->Position.z); + } + // Force ride construction to recheck area + _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK; + + return res; + } + +private: + uint8_t GetHighestHeight() const + { + uint8_t maxHeight{ 255 }; + for (int32_t y = _range.GetTop(); y <= _range.GetBottom(); y += 32) + { + for (int32_t x = _range.GetLeft(); x <= _range.GetRight(); x += 32) + { + TileElement* tile_element = map_get_surface_element_at({ x, y }); + if (tile_element == nullptr) + continue; + + uint8_t height = tile_element->base_height; + if (tile_element->AsSurface()->GetWaterHeight() > 0) + { + height = tile_element->AsSurface()->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..b20747adc3 100644 --- a/src/openrct2/actions/WaterSetHeightAction.hpp +++ b/src/openrct2/actions/WaterSetHeightAction.hpp @@ -113,7 +113,7 @@ 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.x, _coords.y); footpath_remove_litter(_coords.x, _coords.y, surfaceHeight); if (!gCheatsDisableClearanceChecks) wall_remove_at_z(_coords.x, _coords.y, surfaceHeight); diff --git a/src/openrct2/audio/Audio.cpp b/src/openrct2/audio/Audio.cpp index a52ab8e2b3..d96e24b70d 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 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..28b3ab21a5 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 diff --git a/src/openrct2/audio/AudioMixer.h b/src/openrct2/audio/AudioMixer.h index e5b88bc514..5e20af6ad4 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 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..52a6d33731 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 diff --git a/src/openrct2/audio/audio.h b/src/openrct2/audio/audio.h index b855813b55..85e2b079cf 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 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..aa38244595 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..c0e8725ab7 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 @@ -24,6 +24,7 @@ static constexpr const CommandLineOptionDefinition ScreenshotOptionsDef[] { 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 }; 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..2110a4415c 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 @@ -65,7 +65,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..626b907e76 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) @@ -393,6 +390,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 +416,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..8274ec4fbb 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; @@ -155,6 +155,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..367567d6f9 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 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..7d16b0c847 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" @@ -252,6 +254,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 +382,40 @@ 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]); + } + } + 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(); + } + } + 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 +489,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..16d024cfbc 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 diff --git a/src/openrct2/core/File.cpp b/src/openrct2/core/File.cpp index 97e5f01db4..bae6d23440 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 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..718d97cc49 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 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..0839b9994f 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 { @@ -73,7 +76,19 @@ public: free(pathW); free(modeW); #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) { 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..c578aaf7b9 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 diff --git a/src/openrct2/core/Imaging.cpp b/src/openrct2/core/Imaging.cpp index e48537977d..3772b7d02e 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 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..f4a6f334ec 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); } 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..e1ac6870fb 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,11 +34,14 @@ 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(); + MemoryStream& operator=(MemoryStream&& mv); + const void* GetData() const; 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..35064173d0 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 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..68da671696 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 @@ -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; } diff --git a/src/openrct2/core/String.hpp b/src/openrct2/core/String.hpp index 73ce7788f1..8314655ffc 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 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..855de4ce9d 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 @@ -619,7 +619,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; 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..7d05c50978 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 @@ -19,11 +19,11 @@ #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; @@ -591,12 +591,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..be47eac4dc 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 @@ -237,8 +244,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 +257,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; diff --git a/src/openrct2/drawing/DrawingFast.cpp b/src/openrct2/drawing/DrawingFast.cpp index 26133cbccd..3b2d977f85 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 diff --git a/src/openrct2/drawing/Font.cpp b/src/openrct2/drawing/Font.cpp index c172e3018b..41de488ae3 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 @@ -185,12 +197,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 +254,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..2c05826a80 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 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..7965d478e1 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(); 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..591e4c59e6 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 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..0cd93dd614 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,6 +17,7 @@ #include "TTF.h" #include +#include #pragma pack(push, 1) /* size: 0xA12 */ @@ -38,6 +39,7 @@ assert_struct_size(rct_draw_scroll_text, 0xA12); 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); @@ -47,15 +49,10 @@ static void scrolling_text_set_bitmap_for_ttf( 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++) { @@ -1472,9 +1469,11 @@ void scrolling_text_invalidate() */ int32_t scrolling_text_setup(paint_session* session, rct_string_id stringId, uint16_t scroll, uint16_t scrollingMode) { + 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; 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..6569f5bbe8 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 @@ -131,6 +131,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); 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..5ff82edb79 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 diff --git a/src/openrct2/interface/Chat.h b/src/openrct2/interface/Chat.h index 4262f60a60..2653f9a1d5 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 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..4c16ee350b 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 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..08a56d1d20 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 diff --git a/src/openrct2/interface/Fonts.h b/src/openrct2/interface/Fonts.h index afe7aac0d2..af8ec51d50 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 diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index 0c12365fc9..92d71a1d02 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" @@ -169,6 +171,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; } @@ -358,6 +361,70 @@ 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]) + { + uint16_t rideId{}; + Ride* ride; + FOR_ALL_RIDES (rideId, ride) + { + auto rideSetPrice = RideSetPriceAction(rideId, 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]) + { + uint16_t rideId{}; + Ride* ride; + FOR_ALL_RIDES (rideId, ride) + { + if (ride->type == rideType) + { + auto rideSetPrice = RideSetPriceAction(rideId, 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 @@ -583,14 +650,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 +746,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 +908,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(); @@ -913,57 +952,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])) { @@ -1379,7 +1416,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 +1529,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 +1653,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", @@ -1613,6 +1708,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..960752787c 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; @@ -93,88 +100,80 @@ static void screenshot_get_rendered_palette(rct_palette* palette) } } -static int32_t screenshot_get_next_path(char* path, size_t size) +static std::string screenshot_get_park_name() +{ + char buffer[512]; + format_string(buffer, sizeof(buffer), gParkName, &gParkNameArgs); + return buffer; +} + +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 ""; } @@ -182,9 +181,9 @@ std::string screenshot_dump_png(rct_drawpixelinfo* dpi) rct_palette renderedPalette; screenshot_get_rendered_palette(&renderedPalette); - if (WriteDpiToFile(path, dpi, renderedPalette)) + if (WriteDpiToFile(path->c_str(), dpi, renderedPalette)) { - return std::string(path); + return *path; } else { @@ -194,9 +193,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 +211,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) { @@ -254,30 +253,13 @@ void screenshot_giant() 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) - { - 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; - } + int32_t z = tile_element_height(centreX, centreY); - viewport.view_x = x - ((viewport.view_width << zoom) / 2); - viewport.view_y = y - ((viewport.view_height << zoom) / 2); + CoordsXYZ centreCoords3d = { centreX, centreY, z }; + CoordsXY centreCoords2d = translate_3d_to_2d_with_z(rotation, centreCoords3d); + + viewport.view_x = centreCoords2d.x - ((viewport.view_width << zoom) / 2); + viewport.view_y = centreCoords2d.y - ((viewport.view_height << zoom) / 2); viewport.zoom = zoom; gCurrentRotation = rotation; @@ -293,11 +275,19 @@ void screenshot_giant() dpi.zoom_level = 0; dpi.bits = (uint8_t*)malloc(dpi.width * dpi.height); + if (gConfigGeneral.transparent_screenshot) + { + std::memset(dpi.bits, PALETTE_INDEX_0, dpi.width * dpi.height); + viewport.flags |= VIEWPORT_FLAG_TRANSPARENT_BACKGROUND; + } + + auto drawingEngine = std::make_unique(GetContext()->GetUiContext()); + dpi.DrawingEngine = drawingEngine.get(); + 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) + auto path = screenshot_get_next_path(); + if (path == opt::nullopt) { log_error("Giant screenshot failed, unable to find a suitable destination path."); context_show_error(STR_SCREENSHOT_FAILED, STR_NONE); @@ -307,13 +297,13 @@ void screenshot_giant() rct_palette renderedPalette; screenshot_get_rendered_palette(&renderedPalette); - WriteDpiToFile(path, &dpi, renderedPalette); + WriteDpiToFile(path->c_str(), &dpi, renderedPalette); 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)); + set_format_arg(2, char*, path_get_filename(path->c_str())); context_show_error(STR_SCREENSHOT_SAVED_AS, STR_NONE); } @@ -348,7 +338,7 @@ static void benchgfx_render_screenshots(const char* inputPath, std::unique_ptrInitialise()) + if (!context->Initialise()) { - drawing_engine_init(); + std::puts("Failed to initialize context."); + return -1; + } - try + drawing_engine_init(); + + try + { + context->LoadParkFromFile(inputPath); + } + catch (const std::exception& e) + { + std::printf("%s\n", e.what()); + drawing_engine_dispose(); + return -1; + } + + gIntroState = INTRO_STATE_NONE; + gScreenFlags = SCREEN_FLAGS_PLAYING; + + int32_t mapSize = gMapSize; + if (resolutionWidth == 0 || resolutionHeight == 0) + { + 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 z = tile_element_height(customX, customY); + CoordsXYZ coords3d = { customX, customY, z }; + + CoordsXY 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; + } + + if (options->weather != 0) + { + if (options->weather < 1 || options->weather > 6) { - context->LoadParkFromFile(inputPath); - } - catch (const std::exception& e) - { - std::printf("%s\n", e.what()); + std::printf("Weather can only be set to an integer value from 1 till 6."); drawing_engine_dispose(); return -1; } - gIntroState = INTRO_STATE_NONE; - gScreenFlags = SCREEN_FLAGS_PLAYING; - - int32_t mapSize = gMapSize; - if (resolutionWidth == 0 || resolutionHeight == 0) - { - 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; - } - 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) - { - std::printf("Weather can only be set to an integer value from 1 till 6."); - drawing_engine_dispose(); - return -1; - } - - uint8_t customWeather = options->weather - 1; - climate_force_weather(customWeather); - } - - // 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); - - WriteDpiToFile(outputPath, &dpi, renderedPalette); - - free(dpi.bits); - drawing_engine_dispose(); + uint8_t customWeather = options->weather - 1; + climate_force_weather(customWeather); } + + // 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); + dpi.DrawingEngine = context->GetDrawingEngine(); + + std::memset(dpi.bits, PALETTE_INDEX_0, 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) + { + 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; + } + + viewport_render(&dpi, &viewport, 0, 0, viewport.width, viewport.height); + + rct_palette renderedPalette; + screenshot_get_rendered_palette(&renderedPalette); + + WriteDpiToFile(outputPath, &dpi, renderedPalette); + + free(dpi.bits); + drawing_engine_dispose(); + return 1; } 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..4063fe882b 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,7 @@ #include "../Input.h" #include "../OpenRCT2.h" #include "../config/Config.h" +#include "../core/JobPool.hpp" #include "../drawing/Drawing.h" #include "../paint/Paint.h" #include "../peep/Staff.h" @@ -42,6 +43,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; @@ -60,7 +62,6 @@ static int16_t _interactionMapX; static int16_t _interactionMapY; static uint16_t _unk9AC154; -static void viewport_paint_column(rct_drawpixelinfo* dpi, uint32_t viewFlags, std::vector* sessions); static void viewport_paint_weather_gloom(rct_drawpixelinfo* dpi); /** @@ -253,9 +254,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 +369,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) @@ -637,7 +640,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(0xFFFF & sprite->generic.x, 0xFFFF & sprite->generic.y)) - 16; int32_t underground = sprite->generic.z < height; viewport_set_underground_flag(underground, window, window->viewport); @@ -833,6 +836,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 @@ -867,7 +907,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 +920,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); } } @@ -1590,7 +1613,7 @@ static bool sub_679023(rct_drawpixelinfo* dpi, int32_t imageId, int32_t x, int32 static void sub_68862C(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) { diff --git a/src/openrct2/interface/Viewport.h b/src/openrct2/interface/Viewport.h index a348556d8e..ec5a567c68 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 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..45d806ca36 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,17 +139,12 @@ 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; @@ -156,7 +153,7 @@ void window_update_all() window_invalidate(w); } } - } + }); auto windowManager = OpenRCT2::GetContext()->GetUiContext()->GetWindowManager(); windowManager->UpdateMouseWheel(); @@ -238,13 +235,67 @@ void window_close(rct_window* window) // Invalidate the window (area) window_invalidate(window); - auto index = window_get_index(window); - if (index != std::numeric_limits::max()) + for (auto it = g_window_list.begin(); it != g_window_list.end(); it++) { - g_window_list.erase(g_window_list.begin() + index); + if ((*it).get() == window) + { + g_window_list.erase(it); + break; + } } } +template static void window_close_by_condition(_TPred pred, uint32_t flags = WindowCloseFlags::None) +{ + bool listUpdated; + do + { + 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); +} + /** * Closes all windows with the specified window class. * rct2: 0x006ECCF4 @@ -252,15 +303,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 +314,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 +364,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 +381,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,14 +398,7 @@ 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); }); } /** @@ -397,18 +409,18 @@ rct_window* window_find_from_point(int32_t x, int32_t y) { 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 (x < w->x || x >= w->x + w->width || y < w->y || 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(), x, y); if (widgetIndex == -1) continue; } - return &w; + return w.get(); } return nullptr; @@ -466,6 +478,16 @@ void window_invalidate(rct_window* window) gfx_set_dirty_blocks(window->x, window->y, window->x + window->width, window->y + window->height); } +template static void window_invalidate_by_condition(_TPred pred) +{ + window_visit_each([pred](rct_window* w) { + if (pred(w)) + { + window_invalidate(w); + } + }); +} + /** * Invalidates all windows with the specified window class. * rct2: 0x006EC3AC @@ -473,13 +495,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 +504,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 +513,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) { window_invalidate(w); }); } /** @@ -531,18 +539,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)) + { + window_invalidate(w); + } + }); +} + /** * 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 +568,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); } - } + }); } /** @@ -640,25 +656,22 @@ 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)); + g_window_list.splice(itDestPos, g_window_list, itSourcePos); window_invalidate(w); if (w->x + w->width < 20) @@ -721,30 +734,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()); + window_invalidate(w); 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()); + window_invalidate(w); if (w->viewport != nullptr) w->viewport->x += push_amount; - } + }); } /** @@ -753,41 +765,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()); + window_invalidate(w2); - 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()); + window_invalidate(w2); // Update viewport position if necessary if (w2->viewport != nullptr) w2->viewport->y += push_amount; - } + }); } /** @@ -890,15 +897,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 +915,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; @@ -938,11 +945,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); }); } /** @@ -1151,10 +1154,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 +1173,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) @@ -1621,8 +1625,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 +1633,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 +1657,7 @@ void window_relocate_windows(int32_t width, int32_t height) w->viewport->x -= x - w->x; w->viewport->y -= y - w->y; } - } + }); } /** @@ -1662,7 +1665,7 @@ 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)) + if (gScreenFlags & SCREEN_FLAGS_EDITOR) { window_resize_gui_scenario_editor(width, height); return; @@ -1827,21 +1830,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 +1860,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 +1891,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 +1921,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) @@ -2068,9 +2071,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 +2102,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 +2111,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 +2146,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..a9f9f96126 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 @@ -13,7 +13,10 @@ #include "../common.h" #include "../ride/RideTypes.h" +#include #include +#include +#include struct rct_drawpixelinfo; struct rct_window; @@ -487,9 +490,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 +583,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(); @@ -673,11 +679,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); diff --git a/src/openrct2/interface/Window_internal.h b/src/openrct2/interface/Window_internal.h index f030c0a9ac..edd3c7799d 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,8 +11,8 @@ #include "Window.h" +#include #include -#include struct rct_research_item; struct rct_object_entry; @@ -104,4 +104,4 @@ struct rct_window }; // 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..e15c1d2409 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 diff --git a/src/openrct2/localisation/FormatCodes.h b/src/openrct2/localisation/FormatCodes.h index d87da883d3..c4a4f39434 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 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..17d0340456 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 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..3166e0038b 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. @@ -1512,8 +1513,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..ef848aec28 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 @@ -74,9 +74,9 @@ 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..fa2a3c6ce8 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,40 @@ 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, + // 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..708e8df85d 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 @@ -276,7 +276,5 @@ bool utf8_is_format_code(int32_t codepoint) bool utf8_is_colour_code(int32_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 index 8a23d266ce..fa91b549d8 100644 --- a/src/openrct2/localisation/User.cpp +++ b/src/openrct2/localisation/User.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a 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/User.h b/src/openrct2/localisation/User.h index c6c4d9bbd3..d20633ed0f 100644 --- a/src/openrct2/localisation/User.h +++ b/src/openrct2/localisation/User.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/Award.cpp b/src/openrct2/management/Award.cpp index 550ca8cea7..e6a3b07ea2 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 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..9eedd38245 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; } /** @@ -192,7 +186,7 @@ void finance_pay_ride_upkeep() { 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)) 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..23e342af3e 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 @@ -79,7 +79,7 @@ static void marketing_raise_finished_notification(const MarketingCampaign& campa } 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); 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..9343c7bafd 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 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..581beed56e 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 diff --git a/src/openrct2/management/Research.h b/src/openrct2/management/Research.h index 815e40d3ae..339d8115be 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 diff --git a/src/openrct2/network/DiscordService.cpp b/src/openrct2/network/DiscordService.cpp index 1e4fa5b615..7c3e1901c2 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 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..6d8d140752 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 diff --git a/src/openrct2/network/Http.h b/src/openrct2/network/Http.h index 54204e0fb3..399650320e 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 diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index 95cd5e38b4..781d69bb70 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,12 +34,16 @@ // 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 "46" #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" @@ -70,7 +77,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 @@ -113,6 +120,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,6 +133,8 @@ public: void Flush(); void ProcessPending(); void ProcessPlayerList(); + void ProcessPlayerInfo(); + void ProcessDisconnectedClients(); void ProcessGameCommands(); void EnqueueGameAction(const GameAction* action); std::vector>::iterator GetPlayerIteratorByID(uint8_t id); @@ -134,10 +144,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 +170,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); @@ -173,6 +188,7 @@ public: 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 +204,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 +212,6 @@ public: std::vector _challenge; std::map _gameActionCallbacks; NetworkUserManager _userManager; - std::string ServerName; std::string ServerDescription; std::string ServerGreeting; @@ -204,13 +220,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); @@ -271,45 +289,48 @@ private: 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,6 +343,7 @@ 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); @@ -333,6 +355,7 @@ private: 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 +369,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,7 +385,6 @@ 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; @@ -373,6 +396,7 @@ Network::Network() 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,6 +406,7 @@ 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; @@ -391,6 +416,7 @@ Network::Network() 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 +445,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) @@ -440,7 +480,9 @@ void Network::Close() game_command_queue.clear(); player_list.clear(); group_list.clear(); - _pendingPlayerList.reset(); + _serverTickData.clear(); + _pendingPlayerLists.clear(); + _pendingPlayerInfo.clear(); gfx_invalidate_screen(); @@ -448,6 +490,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,13 +537,19 @@ 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(); @@ -590,7 +653,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,10 +678,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); - } + _serverState.gamestateSnapshotsEnabled = gConfigNetwork.desync_debugging; + _advertiser = CreateServerAdvertiser(listening_port); if (gConfigNetwork.pause_server_if_no_clients) { @@ -654,7 +715,7 @@ int32_t Network::GetAuthStatus() uint32_t Network::GetServerTick() { - return server_tick; + return _serverState.tick; } uint8_t Network::GetPlayerID() @@ -666,6 +727,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 +747,10 @@ void Network::Update() if (_requireClose) { Close(); + if (_requireReconnect) + { + Reconnect(); + } } } @@ -701,17 +771,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 +981,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 +1004,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 +1035,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 +1060,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 +1101,7 @@ void Network::SetPassword(const char* password) _password = password == nullptr ? "" : password; } -void Network::ShutdownClient() +void Network::ServerClientDisconnected() { if (GetMode() == NETWORK_MODE_CLIENT) { @@ -1253,7 +1350,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); @@ -1305,9 +1402,9 @@ void Network::BeginServerLog() # 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); + _server_log_fs.open(pathW.get(), 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 +1444,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 +1586,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); @@ -1618,14 +1729,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 +1754,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 +1816,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 +1827,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 +1845,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,46 +1960,122 @@ 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 { @@ -1883,7 +2083,11 @@ void Network::ProcessPlayerList() } } - _pendingPlayerList.reset(); + if (gConfigNetwork.pause_server_if_no_clients && game_is_not_paused() && client_connection_list.empty()) + { + auto pauseToggleAction = PauseToggleAction(); + GameActions::Execute(&pauseToggleAction); + } } void Network::ProcessGameCommands() @@ -1916,6 +2120,17 @@ void Network::ProcessGameCommands() if (gc.action != nullptr) { + // Remove ghost scenery so it doesn't interfere with incoming network command + switch (gc.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 = gc.action.get(); action->SetFlags(action->GetFlags() | GAME_COMMAND_FLAG_NETWORKED); @@ -1924,8 +2139,8 @@ void Network::ProcessGameCommands() GameActionResult::Ptr result = GameActions::Execute(action); if (result->Error == GA_ERROR::OK) { - game_commands_processed_this_tick++; Server_Send_GAME_ACTION(action); + Server_Send_PLAYERINFO(action->GetPlayer()); } } else @@ -1946,7 +2161,6 @@ void Network::ProcessGameCommands() if (cost != MONEY32_UNDEFINED) { - game_commands_processed_this_tick++; NetworkPlayer* player = GetPlayerByID(gc.playerid); if (!player) return; @@ -1968,6 +2182,7 @@ void Network::ProcessGameCommands() } Server_Send_GAMECMD(gc.eax, gc.ebx, gc.ecx, gc.edx, gc.esi, gc.edi, gc.ebp, gc.playerid, gc.callback); + Server_Send_PLAYERINFO(gc.playerid); } } } @@ -1977,16 +2192,13 @@ void Network::ProcessGameCommands() 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); + std::unique_ptr ga = GameActions::Clone(action); + if (ga->GetPlayer() == -1 && GetMode() != 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()); + } game_command_queue.emplace(gCurrentTicks, std::move(ga), _commandId++); } @@ -2011,53 +2223,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 +2316,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 +2326,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 +2434,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 +2544,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 +2597,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 +2705,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) @@ -2545,10 +2870,12 @@ void Network::Client_Handle_MAP([[maybe_unused]] NetworkConnection& connection, { game_load_init(); game_command_queue.clear(); - server_tick = gCurrentTicks; - server_srand0_tick = 0; + _serverTickData.clear(); + _serverState.tick = gCurrentTicks; + _serverTickData.clear(); // 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 @@ -2592,6 +2919,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; @@ -2639,6 +2967,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); @@ -2750,58 +3079,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 +3111,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); @@ -2887,27 +3207,42 @@ void Network::Client_Handle_TICK([[maybe_unused]] NetworkConnection& connection, { 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 +3251,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 +3387,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 +3418,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 +3463,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 +3644,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 +3698,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 +3777,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() @@ -3893,6 +4120,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,7 +4157,19 @@ void network_flush() void network_send_tick() { } -void network_check_desynchronization() +bool network_is_desynchronised() +{ + return false; +} +bool network_gamestate_snapshots_enabled() +{ + return false; +} +bool network_check_desynchronisation() +{ + return false; +} +void network_request_gamestate_snapshot() { } void network_enqueue_game_action(const GameAction* action) @@ -4018,16 +4271,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 +4332,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 +4390,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..2f8c14725d 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; 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..e712a1a38a 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::Network; + _lastAdvertiseTime = platform_get_ticks(); // Send the registration request @@ -132,6 +199,8 @@ private: void SendHeartbeat() { + using namespace OpenRCT2::Network; + 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..7a99db8022 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 @@ -65,10 +65,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 +115,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..d2efbb0fac 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::Network; + + 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..12fed91b78 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 @@ -36,13 +36,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 +60,7 @@ #endif // _WIN32 // clang-format on -# include "TcpSocket.h" +# include "Socket.h" constexpr auto CONNECT_TIMEOUT = std::chrono::milliseconds(3000); @@ -66,8 +68,6 @@ constexpr auto CONNECT_TIMEOUT = std::chrono::milliseconds(3000); static bool _wsaInitialised = false; # endif -class TcpSocket; - class SocketException : public std::runtime_error { public: @@ -77,7 +77,123 @@ public: } }; -class TcpSocket final : public ITcpSocket +class NetworkEndpoint final : public INetworkEndpoint +{ +private: + 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: SOCKET_STATUS _status = SOCKET_STATUS_CLOSED; @@ -100,12 +216,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 +239,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 +325,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 +352,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 +365,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 +562,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 +799,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..0d812807c4 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 @@ -24,6 +24,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" @@ -442,30 +443,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); } } } @@ -498,11 +510,15 @@ namespace Twitch if (newStringId != 0) { peep->name_string_idx = newStringId; - 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); } } else diff --git a/src/openrct2/network/network.h b/src/openrct2/network/network.h index b1f6a75a3e..12f946d475 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); @@ -103,3 +114,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/network/twitch.h b/src/openrct2/network/twitch.h index 293293bb4f..2c39a99429 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 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..2eb12afba7 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 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..68b8855c65 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 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..211c84b7d5 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 @@ -85,6 +85,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++) { @@ -550,6 +563,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 @@ -623,7 +638,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( 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..aa984b5d2a 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 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..85fb54f74a 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); @@ -103,21 +84,8 @@ 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; - } + uint8_t swappedRotation = (session->CurrentRotation * 3) % 4; // swaps 1 and 3 + rotate_map_coordinates(&offset.x, &offset.y, swappedRotation); offset.x += session->SpritePosition.x; offset.y += session->SpritePosition.y; @@ -132,7 +100,7 @@ static paint_struct* sub_9819_c( 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 +160,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 +426,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 +444,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; @@ -732,18 +699,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); } /** @@ -845,7 +806,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; 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..06fecbd57b 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; 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..7525e715ca 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 @@ -30,7 +30,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 +49,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); 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..d23b69c2b2 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 @@ -34,7 +34,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; @@ -95,19 +95,23 @@ void banner_paint(paint_session* session, uint8_t direction, int32_t height, con set_format_arg(0, uint32_t, 0); set_format_arg(4, uint32_t, 0); - rct_string_id string_id = STR_NO_ENTRY; - if (!(gBanners[tile_element->AsBanner()->GetIndex()].flags & BANNER_FLAG_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); + set_format_arg(0, rct_string_id, STR_NO_ENTRY); } else { - format_string(gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), string_id, gCommonFormatArgs); + set_format_arg(0, rct_string_id, gBanners[tile_element->AsBanner()->GetIndex()].string_idx); + } + + if (gConfigGeneral.upper_case_banners) + { + format_string_to_upper( + gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), STR_BANNER_TEXT_FORMAT, gCommonFormatArgs); + } + else + { + format_string(gCommonStringFormatBuffer, sizeof(gCommonStringFormatBuffer), STR_BANNER_TEXT_FORMAT, gCommonFormatArgs); } gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY; @@ -116,6 +120,6 @@ void banner_paint(paint_session* session, uint8_t direction, int32_t height, con 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, + session, scrolling_text_setup(session, STR_BANNER_TEXT_FORMAT, scroll, scrollingMode), 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..9ea0ed6093 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 @@ -23,8 +23,6 @@ #include "../Supports.h" #include "Paint.TileElement.h" -static uint32_t _unk9E32BC; - /** * * rct2: 0x0066508C, 0x00665540 @@ -65,7 +63,7 @@ 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 @@ -89,13 +87,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 +158,27 @@ 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; + set_format_arg(2, rct_string_id, ride->name); + set_format_arg(4, uint32_t, ride->name_arguments); + } + 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 +187,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), 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 +221,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,7 +264,6 @@ 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); @@ -276,18 +271,21 @@ static void park_entrance_paint(paint_session* session, uint8_t direction, int32 { set_format_arg(0, rct_string_id, gParkName); set_format_arg(2, uint32_t, gParkNameArgs); - - park_text_id = STR_BANNER_TEXT_FORMAT; + } + 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 +296,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); 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 +333,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..3ee465040b 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); @@ -327,8 +327,9 @@ void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t hei // 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; - 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) @@ -400,7 +401,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 > 0) { 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..16525683ef 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 @@ -451,21 +451,25 @@ static void sub_6A4101( 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); + set_format_arg(2, rct_string_id, ride->name); + set_format_arg(4, uint32_t, ride->name_arguments); + } + 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 +478,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), 0, 0, 1, 1, 21, + height + 7, boundBoxOffsets.x, boundBoxOffsets.y, boundBoxOffsets.z); } session->InteractionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; @@ -682,10 +686,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 +707,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 @@ -932,7 +943,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 +1019,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 +1168,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..f911f2663c 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; @@ -976,7 +977,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 +1088,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 +1105,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 +1194,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..4bf1a891f1 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 diff --git a/src/openrct2/paint/tile_element/Paint.TileElement.cpp b/src/openrct2/paint/tile_element/Paint.TileElement.cpp index a9b87f8cf5..619c421b4b 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)) { 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..f2ebe4a6df 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)) diff --git a/src/openrct2/peep/Guest.cpp b/src/openrct2/peep/Guest.cpp index 597711ab71..1db93bee66 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 @@ -382,7 +382,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); } } @@ -443,7 +443,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 +458,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) @@ -473,7 +473,7 @@ void Guest::Tick128UpdateGuest(int32_t index) ? 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 +560,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 +596,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); } } @@ -1087,7 +1087,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 +1105,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 +1137,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 +1172,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 +1181,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 +1208,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; } @@ -1236,22 +1236,22 @@ loc_69B119: { 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 +1275,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 +1292,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 +1306,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; @@ -1371,7 +1371,7 @@ loc_69B221: { 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); + set_format_arg(6, 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 +1403,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 +1416,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++; @@ -1485,13 +1485,7 @@ void Guest::OnExitRide(ride_id_t rideIndex) 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)) @@ -1504,7 +1498,7 @@ void Guest::OnExitRide(ride_id_t rideIndex) if (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) @@ -1550,14 +1544,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) @@ -1757,11 +1744,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 +1761,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 +1787,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 +1813,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 +1836,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 +1892,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 +1910,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 +1959,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 +1987,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); @@ -2054,7 +2041,7 @@ 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); } } @@ -2133,22 +2120,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; @@ -2349,14 +2329,14 @@ static bool peep_check_ride_price_at_entrance(Guest* peep, Ride* ride, money32 r if (peep->cash_in_pocket <= 0) { - 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 +2346,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 +2616,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; @@ -2837,7 +2817,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) @@ -2956,14 +2936,7 @@ static void peep_head_for_nearest_ride_type(Guest* peep, int32_t rideType) 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); - widget_invalidate(w, WC_PEEP__WIDX_ACTION_LBL); - } + peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_ACTION; peep->time_lost = 0; } @@ -3763,7 +3736,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); } /** @@ -4055,22 +4028,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; @@ -4714,20 +4697,12 @@ void Guest::UpdateRideMazePathfinding() 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(actionX, actionY, 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 @@ -4780,7 +4755,7 @@ 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(actionX / 32, actionY / 32); do { if (stationHeight != tileElement->base_height) @@ -5409,11 +5384,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); } } @@ -5429,7 +5404,7 @@ void Guest::UpdateQueuing() return; } Ride* ride = get_ride(current_ride); - if (ride->status == RIDE_STATUS_CLOSED || ride->status == RIDE_STATUS_TESTING) + if (ride->status != RIDE_STATUS_OPEN) { RemoveFromQueue(); SetState(PEEP_STATE_1); @@ -5488,7 +5463,7 @@ void Guest::UpdateQueuing() 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 @@ -5909,12 +5884,7 @@ bool Guest::ShouldFindBench() return false; } - if (!GetNextIsSurface() && !GetNextIsSloped()) - { - return true; - } - - return false; + return !GetNextIsSurface() && !GetNextIsSloped(); } /** @@ -6308,6 +6278,11 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV surfaceElement = map_get_surface_element_at({ peep->next_x, peep->next_y }); tileElement = surfaceElement; + if (tileElement == nullptr) + { + return false; + } + do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6342,6 +6317,11 @@ 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; + if (tileElement == nullptr) + { + return false; + } + do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6571,6 +6551,11 @@ static bool peep_find_ride_to_look_at(Peep* peep, uint8_t edge, uint8_t* rideToV // TODO: extract loop A tileElement = surfaceElement; + if (tileElement == nullptr) + { + return false; + } + do { // Ghosts are purely this-client-side and should not cause any interaction, @@ -6696,7 +6681,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 }, diff --git a/src/openrct2/peep/GuestPathfinding.cpp b/src/openrct2/peep/GuestPathfinding.cpp index f667799221..7837749dc4 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 @@ -1797,7 +1797,7 @@ static void get_ride_queue_end(TileCoordsXYZ& loc) } } while (!(tileElement++)->IsLastForTile()); - if (found == false) + if (!found) break; if (!tileElement->AsPath()->IsQueue()) diff --git a/src/openrct2/peep/Peep.cpp b/src/openrct2/peep/Peep.cpp index 4c92b2a201..09f09e69cb 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 @@ -435,7 +435,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]; @@ -846,106 +846,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 @@ -1074,7 +979,7 @@ 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; @@ -1086,7 +991,7 @@ void Peep::UpdateFalling() return; } } - int32_t map_height = tile_element_height(0xFFFF & x, 0xFFFF & y) & 0xFFFF; + int32_t map_height = tile_element_height(0xFFFF & x, 0xFFFF & y); if (map_height < z || map_height - 4 > z) continue; saved_height = map_height; @@ -1173,7 +1078,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); } } @@ -1704,18 +1609,67 @@ 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(); + Invalidate(); + } + + 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); + move_sprite_to_list((rct_sprite*)peep, SPRITE_LIST_PEEP); peep->sprite_identifier = SPRITE_IDENTIFIER_PEEP; peep->sprite_type = PEEP_SPRITE_TYPE_NORMAL; @@ -1737,14 +1691,14 @@ Peep* peep_generate(int32_t x, int32_t y, int32_t z) peep->sprite_direction = 0; - sprite_move(x, y, z, (rct_sprite*)peep); + sprite_move(coords.x, coords.y, coords.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 +1731,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 +1748,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 +1760,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; @@ -1842,7 +1796,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 +1810,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 +1826,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% */ @@ -2054,11 +2008,11 @@ void peep_thought_set_format_args(rct_peep_thought* thought) } 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); } } @@ -2229,55 +2183,6 @@ int32_t peep_get_easteregg_name_id(Peep* peep) 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) @@ -2736,7 +2641,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 +2650,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 +2698,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 +2724,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,7 +2750,7 @@ 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; @@ -3444,7 +3349,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; diff --git a/src/openrct2/peep/Peep.h b/src/openrct2/peep/Peep.h index 1fb258cc98..5ce95b4dde 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 @@ -35,6 +35,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 +515,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() @@ -709,8 +714,10 @@ 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); // TODO: Make these private again when done refactoring public: // Peep @@ -940,27 +947,18 @@ 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); 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); @@ -971,8 +969,6 @@ 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); void peep_reset_pathfind_goal(Peep* peep); 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..ae167fd612 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; } /** @@ -1007,7 +644,7 @@ static bool staff_path_finding_handyman(Peep* peep) } } - if (chooseRandom == true) + if (chooseRandom) { do { @@ -1064,16 +701,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; } @@ -1452,24 +1089,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) @@ -1565,7 +1184,7 @@ void Staff::UpdateMowing() int16_t xy_distance; if (UpdateAction(&actionX, &actionY, &xy_distance)) { - int16_t checkZ = tile_element_height(actionX, actionY) & 0xFFFF; + int16_t checkZ = tile_element_height(actionX, actionY); MoveTo(actionX, actionY, checkZ); Invalidate(); return; @@ -2748,7 +2367,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"); 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..45b0dd0525 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 @@ -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 { 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..62ecff739c 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 @@ -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..733df1b6fc 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 @@ -130,11 +130,7 @@ bool platform_directory_exists(const utf8* 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; + return result == 0 && S_ISDIR(dirinfo.st_mode); } bool platform_original_game_data_exists(const utf8* path) 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..78535d3bcf 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 @@ -167,7 +170,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; } @@ -253,6 +256,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]; @@ -396,7 +413,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; } @@ -567,8 +584,8 @@ 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); @@ -720,7 +737,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 +745,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..bad0ad63eb 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 @@ -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..372eeef042 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 @@ -428,8 +428,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 { @@ -681,7 +681,7 @@ struct rct1_s4 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]; diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 27317c6021..83781c621c 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(); @@ -197,7 +200,9 @@ 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. @@ -789,10 +794,6 @@ private: } } } - if (dst->name == 0) - { - ride_set_name_to_default(dst, rideEntry); - } dst->status = src->status; @@ -944,10 +945,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++) { @@ -1103,23 +1107,32 @@ 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); + ride->measurement = std::make_unique(); + ride->measurement->ride = ride; + 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 +1165,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_TRAIN); } } } @@ -1238,6 +1251,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 +1265,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 +1362,7 @@ 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); + move_sprite_to_list((rct_sprite*)peep, SPRITE_LIST_PEEP); spriteIndexMap[i] = peep->sprite_index; ImportPeep(peep, srcPeep); @@ -1660,7 +1678,7 @@ 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); + move_sprite_to_list((rct_sprite*)litter, SPRITE_LIST_LITTER); litter->sprite_identifier = srcLitter->sprite_identifier; litter->type = srcLitter->type; @@ -1687,7 +1705,7 @@ 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); + move_sprite_to_list((rct_sprite*)dst, SPRITE_LIST_MISC); dst->sprite_identifier = src->sprite_identifier; dst->type = src->type; @@ -2769,10 +2787,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; @@ -2940,6 +2961,50 @@ 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. + Ride* ride = get_ride(0); + 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. @@ -2979,6 +3044,24 @@ private: } } } + + /** + * 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() + { + ride_id_t i; + Ride* ride; + FOR_ALL_RIDES (i, ride) + { + if (ride->name == 0) + { + auto rideEntry = get_ride_entry(ride->subtype); + ride_set_name_to_default(ride, rideEntry); + } + } + } }; std::unique_ptr ParkImporter::CreateS4() 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..017a71f932 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 diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h index d8277f102f..e93b7f2622 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 @@ -39,6 +39,13 @@ #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; + #pragma pack(push, 1) struct rct12_award @@ -498,4 +505,20 @@ 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); + #pragma pack(pop) 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..ca5d6e16d6 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 diff --git a/src/openrct2/rct12/SawyerChunkReader.h b/src/openrct2/rct12/SawyerChunkReader.h index a1407cbae1..57ca099d5c 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 diff --git a/src/openrct2/rct12/SawyerChunkWriter.cpp b/src/openrct2/rct12/SawyerChunkWriter.cpp index 4a298b5bd5..e6837acbb6 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 diff --git a/src/openrct2/rct12/SawyerChunkWriter.h b/src/openrct2/rct12/SawyerChunkWriter.h index 9fbfc156b9..117b865754 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 diff --git a/src/openrct2/rct12/SawyerEncoding.cpp b/src/openrct2/rct12/SawyerEncoding.cpp index 55758dc54a..d9ac604cbc 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 diff --git a/src/openrct2/rct12/SawyerEncoding.h b/src/openrct2/rct12/SawyerEncoding.h index 1a0ba30541..aa46c5aee2 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 diff --git a/src/openrct2/rct2/RCT2.h b/src/openrct2/rct2/RCT2.h index f42fc51457..5456f6bfea 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 @@ -310,7 +310,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 diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index 21849810c3..e5a3d286a5 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 @@ -316,7 +316,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 +328,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++) { @@ -360,7 +360,7 @@ void S6Exporter::Export() // pad_0138B582 _s6.ride_ratings_calc_data = gRideRatingsCalcData; - std::memcpy(_s6.ride_measurements, gRideMeasurements, sizeof(_s6.ride_measurements)); + ExportRideMeasurements(); _s6.next_guest_index = gNextGuestNumber; _s6.grass_and_scenery_tilepos = gGrassSceneryTileLoopPosition; std::memcpy(_s6.patrol_areas, gStaffPatrolAreas, sizeof(_s6.patrol_areas)); @@ -538,8 +538,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 +558,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 +684,60 @@ void S6Exporter::ExportRide(rct2_ride* dst, const Ride* src) // pad_208[0x58]; } +void S6Exporter::ExportRideMeasurements() +{ + // Get all the ride measurements + std::vector rideMeasurements; + for (ride_id_t i = 0; i < RCT12_MAX_RIDES_IN_PARK; i++) + { + auto ride = get_ride(i); + if (ride != nullptr && ride->measurement != nullptr) + { + rideMeasurements.push_back(ride->measurement.get()); + } + } + + // If there are more than S6 can hold, trim it by LRU + if (rideMeasurements.size() > RCT12_RIDE_MEASUREMENT_MAX_ITEMS) + { + // Sort in order of last recently used + std::sort(rideMeasurements.begin(), rideMeasurements.end(), [](const RideMeasurement* a, const RideMeasurement* b) { + return a->last_use_tick > b->last_use_tick; + }); + rideMeasurements.resize(RCT12_RIDE_MEASUREMENT_MAX_ITEMS); + } + + // Convert ride measurements to S6 format + uint8_t i{}; + for (auto src : rideMeasurements) + { + auto& dst = _s6.ride_measurements[i]; + ExportRideMeasurement(_s6.ride_measurements[i], *src); + + auto rideId = src->ride->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); @@ -739,7 +794,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; diff --git a/src/openrct2/rct2/S6Exporter.h b/src/openrct2/rct2/S6Exporter.h index 5c167a2b3a..68396e86c6 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 @@ -57,4 +57,6 @@ private: void ExportResearchList(); void ExportMarketingCampaigns(); void ExportPeepSpawns(); + void ExportRideMeasurements(); + void ExportRideMeasurement(RCT12RideMeasurement& dst, const RideMeasurement& src); }; diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 1cd13ac8f0..2ce88cd597 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" @@ -114,7 +115,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) @@ -334,7 +335,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 +348,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++) @@ -395,7 +396,7 @@ public: // pad_0138B582 gRideRatingsCalcData = _s6.ride_ratings_calc_data; - std::memcpy(gRideMeasurements, _s6.ride_measurements, sizeof(_s6.ride_measurements)); + ImportRideMeasurements(); gNextGuestNumber = _s6.next_guest_index; gGrassSceneryTileLoopPosition = _s6.grass_and_scenery_tilepos; std::memcpy(gStaffPatrolAreas, _s6.patrol_areas, sizeof(_s6.patrol_areas)); @@ -585,8 +586,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 +606,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 +708,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 +748,37 @@ public: // pad_208[0x58]; } + void ImportRideMeasurements() + { + for (const auto& src : _s6.ride_measurements) + { + if (src.ride_index != RCT12_RIDE_ID_NULL) + { + auto ride = get_ride(src.ride_index); + ride->measurement = std::make_unique(); + ride->measurement->ride = ride; + 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(); @@ -1025,7 +1072,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]; @@ -1054,7 +1105,7 @@ public: 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 +1162,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++) { @@ -1412,10 +1470,11 @@ void load_from_sv6(const char* path) gErrorType = ERROR_TYPE_FILE_LOAD; gErrorStringId = 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()); } catch (const std::exception&) { @@ -1443,15 +1502,17 @@ void load_from_sc6(const char* path) 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()); } - catch (const IOException&) + catch (const IOException& loadError) { gErrorType = ERROR_TYPE_FILE_LOAD; gErrorStringId = STR_GAME_SAVE_FAILED; + log_error("Error loading: %s", loadError.what()); } catch (const std::exception&) { diff --git a/src/openrct2/ride/CableLift.cpp b/src/openrct2/ride/CableLift.cpp index 3474ef89e2..4b660f886e 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 @@ -36,7 +36,7 @@ rct_vehicle* cable_lift_segment_create( current->ride_subtype = RIDE_ENTRY_INDEX_NULL; if (head) { - move_sprite_to_list((rct_sprite*)current, SPRITE_LIST_TRAIN * 2); + move_sprite_to_list((rct_sprite*)current, SPRITE_LIST_TRAIN); ride->cable_lift = current->sprite_index; } current->type = head ? VEHICLE_TYPE_HEAD : VEHICLE_TYPE_TAIL; @@ -84,8 +84,7 @@ rct_vehicle* cable_lift_segment_create( 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 +112,8 @@ void cable_lift_update(rct_vehicle* vehicle) case VEHICLE_STATUS_ARRIVING: cable_lift_update_arriving(vehicle); break; + default: + break; } } @@ -136,7 +137,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 +171,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 +185,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 +207,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,7 +218,7 @@ 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) @@ -423,7 +422,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 +438,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..e72df21799 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 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..8bf3821c47 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" @@ -151,8 +152,6 @@ static constexpr const int32_t RideInspectionInterval[] = { Ride gRideList[MAX_RIDES]; -rct_ride_measurement gRideMeasurements[MAX_RIDE_MEASUREMENTS]; - uint16_t gRideCount; bool gGotoStartPlacementMode = false; @@ -205,7 +204,6 @@ 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); @@ -213,8 +211,6 @@ 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) @@ -258,21 +254,16 @@ void get_ride_entry_name(char* name, int32_t index) name[8] = '\0'; } -rct_ride_measurement* get_ride_measurement(int32_t index) +rct_ride_entry* Ride::GetRideEntry() const { - 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) + rct_ride_entry* rideEntry = get_ride_entry(subtype); + if (rideEntry == nullptr) { char oldname[128]; - format_string(oldname, 128, ride->name, &ride->name_arguments); + format_string(oldname, 128, name, &name_arguments); log_error("Invalid ride subtype for ride %s", oldname); } - return type; + return rideEntry; } /** @@ -344,29 +335,29 @@ int32_t ride_get_count() return count; } -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 +366,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); } /** @@ -440,30 +430,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; @@ -617,33 +606,12 @@ bool track_block_get_next(CoordsXYE* input, CoordsXYE* output, int32_t* z, int32 int32_t OriginZ = input->element->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; - } + + 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 +619,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); } /** @@ -717,25 +685,12 @@ 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 @@ -776,25 +731,9 @@ bool track_block_get_previous(int32_t x, int32_t y, TileElement* tileElement, tr int32_t z = tileElement->base_height * 8; 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; - } + 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 +741,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); } /** @@ -901,7 +840,12 @@ void ride_get_status(const Ride* ride, rct_string_id* formatSecondary, int32_t* return; } - if (ride->status == RIDE_STATUS_TESTING) + else if (ride->status == RIDE_STATUS_SIMULATING) + { + *formatSecondary = STR_SIMULATING; + return; + } + else if (ride->status == RIDE_STATUS_TESTING) { *formatSecondary = STR_TEST_RUN; return; @@ -953,23 +897,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 @@ -987,12 +948,6 @@ void ride_init_all() 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; - } } /** @@ -1014,7 +969,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); @@ -1028,7 +983,7 @@ static int32_t ride_check_if_construction_allowed(Ride* ride) 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); @@ -1125,7 +1080,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; @@ -1303,56 +1258,29 @@ int32_t sub_6C683D( int32_t sequence = tileElement->AsTrack()->GetSequenceIndex(); uint8_t mapDirection = tileElement->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; - } + TileCoordsXY offsets = { trackBlock[sequence].x, trackBlock[sequence].y }; + TileCoordsXY 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; - } + TileCoordsXY cur = { start_x, start_y }; + int32_t cur_z = start_z; + offsets.x = trackBlock[i].x; + offsets.y = trackBlock[i].y; + cur += offsets.Rotate(mapDirection); cur_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); + tileElement = map_get_first_element_at(cur.x / 32, cur.y / 32); successTileElement = nullptr; do { @@ -1451,7 +1379,7 @@ void ride_remove_provisional_track_piece() 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; 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); @@ -1474,7 +1402,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); } } @@ -1973,7 +1902,7 @@ int32_t ride_modify(CoordsXYE* input) { return 0; } - rideEntry = get_ride_entry_by_ride(ride); + rideEntry = ride->GetRideEntry(); if ((rideEntry == nullptr) || !ride_check_if_construction_allowed(ride)) return 0; @@ -1988,7 +1917,10 @@ int32_t ride_modify(CoordsXYE* input) } // 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) @@ -2116,7 +2048,7 @@ int32_t ride_initialise_construction_window(Ride* ride) * * rct2: 0x006ABE4C */ -void ride_update_all() +void Ride::UpdateAll() { Ride* ride; int32_t i; @@ -2126,7 +2058,7 @@ void ride_update_all() { if (gS6Info.editor_step <= EDITOR_STEP_INVENTIONS_LIST_SET_UP) FOR_ALL_RIDES (i, ride) - ride->type = RIDE_TYPE_NULL; + ride->Delete(); return; } @@ -2134,7 +2066,7 @@ void ride_update_all() // Update rides FOR_ALL_RIDES (i, ride) - ride_update(ride); + ride->Update(); ride_music_update_final(); } @@ -2143,76 +2075,62 @@ void ride_update_all() * * 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 +2138,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 +2236,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 +2256,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 +2393,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 +2490,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) @@ -3037,39 +2950,18 @@ 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(RideMeasurement* measurement) { - uint16_t spriteIndex; - Ride* ride; - rct_vehicle* vehicle; - int32_t velocity, altitude, verticalG, lateralG; - - ride = get_ride(measurement->ride_index); - spriteIndex = ride->vehicles[measurement->vehicle_index]; + auto ride = measurement->ride; + auto spriteIndex = ride->vehicles[measurement->vehicle_index]; if (spriteIndex == SPRITE_INDEX_NULL) return; - vehicle = GET_VEHICLE(spriteIndex); + auto vehicle = GET_VEHICLE(spriteIndex); if (measurement->flags & RIDE_MEASUREMENT_FLAG_UNLOADING) { @@ -3094,27 +2986,27 @@ 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) { - 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) { @@ -3142,136 +3034,107 @@ void ride_measurements_update() return; // For each ride measurement - for (int32_t i = 0; i < MAX_RIDE_MEASUREMENTS; i++) + ride_id_t i{}; + Ride* ride{}; + FOR_ALL_RIDES (i, ride) { - 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(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(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_id_t i{}; + Ride* ride{}; + Ride* lruRide{}; + numRideMeasurements = 0; + FOR_ALL_RIDES (i, ride) + { + 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(); + measurement->ride = ride; 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 }; } } @@ -3500,7 +3363,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,16 +3372,15 @@ 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) @@ -4569,7 +4431,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 +4444,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); } @@ -4686,8 +4548,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 +4653,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: @@ -4843,7 +4703,7 @@ static void vehicle_create_trains(ride_id_t rideIndex, int32_t x, int32_t y, int for (int32_t vehicleIndex = 0; vehicleIndex < ride->num_vehicles; vehicleIndex++) { - if (ride_is_block_sectioned(ride)) + if (ride->IsBlockSectioned()) { remainingDistance = 0; } @@ -4861,7 +4721,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_TRAIN); for (int32_t i = 0; i < MAX_VEHICLES_PER_RIDE; i++) { if (ride->vehicles[i] == SPRITE_INDEX_NULL) @@ -4904,57 +4764,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 +4816,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 +4832,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 +4863,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 +4883,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 +4959,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; @@ -5275,15 +5121,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; @@ -5426,7 +5264,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 +5275,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 +5287,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 +5314,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 +5403,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 +5639,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 +5648,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(); @@ -5828,14 +5669,14 @@ ride_id_t ride_get_empty_slot() return RIDE_ID_NULL; } -int32_t ride_get_default_mode(Ride* ride) +uint8_t Ride::GetDefaultMode() const { - 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) { } } @@ -5916,21 +5757,31 @@ 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) @@ -6240,34 +6091,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 +6127,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) @@ -6736,7 +6587,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; @@ -6897,14 +6748,17 @@ uint64_t ride_entry_get_supported_track_pieces(const rct_ride_entry* rideEntry) 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; @@ -6991,12 +6845,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 +6862,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 +6879,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 +6890,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 +6916,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 +6931,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 +6967,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 +6983,33 @@ 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::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); } @@ -7210,7 +7063,7 @@ void sub_6CB945(Ride* ride) break; } while (!(tileElement++)->IsLastForTile()); - if (trackFound == false) + if (!trackFound) { break; } @@ -7225,7 +7078,7 @@ void sub_6CB945(Ride* ride) } } - if (specialTrack == false) + if (!specialTrack) { continue; } @@ -7374,7 +7227,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 +7241,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 +7257,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,11 +7275,11 @@ 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); + set_format_arg(0, rct_string_id, name); + set_format_arg(2, uint32_t, name_arguments); 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); } } @@ -7519,22 +7372,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; + user_string_free(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) +bool Ride::IsRide() const { - switch (ride->type) + switch (type) { case RIDE_TYPE_FOOD_STALL: case RIDE_TYPE_1D: @@ -7552,11 +7406,11 @@ static bool ride_is_ride(Ride* ride) } } -money16 ride_get_price(Ride* 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()) { @@ -7795,13 +7649,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) @@ -7921,28 +7771,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 }; + CoordsXY coords2d = translate_3d_to_2d_with_z(get_current_rotation(), coords3d); + LocationXY16 rotatedCoords = { (int16_t)coords2d.x, (int16_t)coords2d.y }; return rotatedCoords; } diff --git a/src/openrct2/ride/Ride.h b/src/openrct2/ride/Ride.h index 65e347e8c9..484d873b78 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 @@ -21,6 +21,7 @@ interface IObjectManager; class StationObject; struct Peep; +struct Ride; struct Staff; #define MAX_RIDE_TYPES_PER_RIDE_ENTRY 3 @@ -35,11 +36,14 @@ 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_ADJACENCY_CHECK_DISTANCE 5 +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) /** @@ -156,6 +160,23 @@ struct RideStation static constexpr uint8_t NO_TRAIN = std::numeric_limits::max(); }; +struct RideMeasurement +{ + static constexpr size_t MAX_ITEMS = 4800; + + Ride* ride{}; + 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]{}; +}; + /** * Ride structure. * @@ -208,7 +229,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 +253,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 +364,63 @@ 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; + 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(); + + 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; + + static void UpdateAll(); }; #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; @@ -587,7 +630,8 @@ enum { RIDE_STATUS_CLOSED, RIDE_STATUS_OPEN, - RIDE_STATUS_TESTING + RIDE_STATUS_TESTING, + RIDE_STATUS_SIMULATING, }; enum @@ -628,7 +672,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 @@ -933,7 +980,7 @@ extern const rct_ride_properties RideProperties[RIDE_TYPE_COUNT]; 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); +RideMeasurement* get_ride_measurement(int32_t index); /** * Helper macro loop for enumerating through all the non null rides. @@ -947,8 +994,8 @@ extern money16 gTotalRideValueForMoney; extern const uint8_t gRideClassifications[MAX_RIDES]; extern Ride gRideList[MAX_RIDES]; +extern const rct_string_id ColourSchemeNames[4]; -extern rct_ride_measurement gRideMeasurements[MAX_RIDE_MEASUREMENTS]; extern uint16_t gRideCount; extern money32 _currentTrackPrice; @@ -995,16 +1042,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); @@ -1020,21 +1061,18 @@ 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_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,21 +1086,11 @@ 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); @@ -1088,16 +1116,8 @@ 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); @@ -1129,28 +1149,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 +1173,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 +1192,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..839e8dcf1d 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 diff --git a/src/openrct2/ride/RideData.h b/src/openrct2/ride/RideData.h index 80442b2ed0..fea28c63f2 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 diff --git a/src/openrct2/ride/RideGroupManager.cpp b/src/openrct2/ride/RideGroupManager.cpp index ffc89f32c6..76ca9255b0 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) 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..a0790e7bd6 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,6 +65,12 @@ enum PROXIMITY_COUNT }; +struct ShelteredEights +{ + uint8_t TrackShelteredEighths; + uint8_t TotalShelteredEighths; +}; + using ride_ratings_calculation = void (*)(Ride* ride); rct_ride_rating_calc_data gRideRatingsCalcData; @@ -1062,13 +1068,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,16 +1084,16 @@ 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) @@ -1165,7 +1171,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; @@ -1181,7 +1187,7 @@ 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 +1196,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,18 +1205,18 @@ 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; @@ -1261,7 +1266,8 @@ static rating_tuple ride_ratings_get_turns_ratings(Ride* ride) intensity += var_112_rating.intensity; nausea += var_112_rating.nausea; - rating_tuple inversions_rating = get_inversions_ratings(ride->inversions & 0x1F); + auto inversions = (ride->type == RIDE_TYPE_MINI_GOLF) ? ride->holes : ride->inversions; + rating_tuple inversions_rating = get_inversions_ratings(inversions); excitement += inversions_rating.excitement; intensity += inversions_rating.intensity; nausea += inversions_rating.nausea; @@ -1408,7 +1414,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) @@ -1677,12 +1683,12 @@ 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); @@ -1697,8 +1703,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) @@ -1736,8 +1741,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) @@ -1777,8 +1781,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,12 +1807,12 @@ 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); @@ -1821,8 +1824,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) @@ -1860,8 +1862,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 +1893,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 +1928,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) @@ -1972,8 +1971,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) @@ -2005,8 +2003,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) @@ -2047,8 +2044,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) @@ -2088,8 +2084,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 +2118,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 +2169,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) @@ -2214,8 +2207,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 +2233,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 +2245,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,12 +2263,12 @@ 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); @@ -2292,8 +2283,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) @@ -2331,8 +2321,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) @@ -2372,8 +2361,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 +2399,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,12 +2429,12 @@ 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); @@ -2462,8 +2449,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 +2475,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 +2504,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 +2541,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 +2577,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 +2610,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 +2645,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 +2670,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 +2695,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 +2735,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 +2769,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 +2802,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 +2842,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 +2884,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 +2906,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 +2936,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 +2966,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; } @@ -3038,8 +3007,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 +3035,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 +3059,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,12 +3085,12 @@ 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); @@ -3138,8 +3104,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) @@ -3176,8 +3141,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 +3173,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 +3207,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 +3233,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,7 +3242,7 @@ 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); @@ -3295,8 +3257,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 +3288,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) @@ -3380,8 +3340,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,7 +3366,7 @@ 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); @@ -3421,8 +3380,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 +3400,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 +3418,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 +3448,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 +3481,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,12 +3507,12 @@ 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); @@ -3572,8 +3527,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) @@ -3613,8 +3567,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) @@ -3653,8 +3606,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) @@ -3694,8 +3646,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,14 +3672,14 @@ 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); @@ -3740,8 +3691,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,12 +3717,12 @@ 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); @@ -3787,8 +3737,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 +3765,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 +3803,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 +3827,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 +3861,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,12 +3887,12 @@ 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); @@ -3959,8 +3904,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) @@ -4001,8 +3945,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) @@ -4033,8 +3976,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) @@ -4075,8 +4017,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 +4042,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 +4066,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 +4097,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 +4126,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) @@ -4225,8 +4163,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) @@ -4265,8 +4202,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) @@ -4302,8 +4238,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,12 +4264,12 @@ 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); @@ -4349,8 +4284,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 diff --git a/src/openrct2/ride/RideRatings.h b/src/openrct2/ride/RideRatings.h index c46a69d721..3ec272f7e3 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 diff --git a/src/openrct2/ride/RideTypes.h b/src/openrct2/ride/RideTypes.h index d52b7753b5..9a5b081ec4 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 diff --git a/src/openrct2/ride/ShopItem.cpp b/src/openrct2/ride/ShopItem.cpp index b317066dca..a8bb394d9e 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,264 +13,69 @@ #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; @@ -313,14 +118,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..44c0f0a4fc 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 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..6a9920433a 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] = @@ -946,373 +944,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; diff --git a/src/openrct2/ride/Track.h b/src/openrct2/ride/Track.h index ef6bb84501..f24e71a354 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 @@ -197,6 +197,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 @@ -521,8 +522,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 +547,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..4e30f7de72 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 @@ -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..860f3e02e8 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,9 @@ #include "../Cheats.h" #include "../Game.h" #include "../OpenRCT2.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 +23,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 +55,9 @@ #include #include +using namespace OpenRCT2; +using namespace OpenRCT2::Drawing; + struct map_backup { TileElement tile_elements[MAX_TILE_ELEMENTS]; @@ -491,7 +499,7 @@ static void track_design_mirror_scenery(rct_track_td6* td6) rct_td6_scenery_element* scenery = td6->scenery_elements; for (; scenery != nullptr && scenery->scenery_object.end_flag != 0xFF; scenery++) { - uint8_t 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); @@ -499,6 +507,8 @@ static void track_design_mirror_scenery(rct_track_td6* td6) { continue; } + + entry_index = 0; } rct_scenery_entry* scenery_entry = (rct_scenery_entry*)object_entry_get_chunk(entry_type, entry_index); @@ -670,24 +680,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,11 +700,390 @@ 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, rct_td6_scenery_element* 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, rct_td6_scenery_element* 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(mapCoord.x, mapCoord.y, z, quadrant, entry_index); + break; + } + case OBJECT_TYPE_LARGE_SCENERY: + ga = std::make_unique(mapCoord.x, mapCoord.y, z, sceneryRotation, 0); + break; + case OBJECT_TYPE_WALLS: + ga = std::make_unique(TileCoordsXYZD{ mapCoord.x / 32, mapCoord.y / 32, z, sceneryRotation }); + break; + case OBJECT_TYPE_PATHS: + ga = std::make_unique(mapCoord.x, mapCoord.y, z); + break; + default: + return true; + } + ga->SetFlags(flags); + GameActions::ExecuteNested(ga.get()); + return true; +} + +static bool TrackDesignPlaceSceneryElementGetPlaceZ(rct_td6_scenery_element* 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, rct_td6_scenery_element* 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; + } + + TileElement* tile_element = map_get_path_element_at(mapCoord.x / 32, mapCoord.y / 32, z); + + if (tile_element == nullptr) + { + return true; + } + + footpath_queue_chain_reset(); + footpath_remove_edges_at(mapCoord.x, mapCoord.y, tile_element); + + 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, tile_element, 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( +static int32_t track_design_place_all_scenery( rct_td6_scenery_element* scenery_start, int32_t originX, int32_t originY, int32_t originZ) { for (uint8_t mode = 0; mode <= 1; mode++) @@ -722,445 +1101,15 @@ static int32_t track_design_place_scenery( for (rct_td6_scenery_element* scenery = scenery_start; scenery->scenery_object.end_flag != 0xFF; scenery++) { 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; } } @@ -1172,10 +1121,10 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, { 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; } @@ -1200,8 +1149,9 @@ 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; @@ -1216,20 +1166,21 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, 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); @@ -1250,20 +1201,21 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, 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); @@ -1279,16 +1231,16 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, default: 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; } @@ -1362,11 +1314,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; @@ -1385,10 +1338,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; } @@ -1419,21 +1372,21 @@ 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]; @@ -1454,18 +1407,18 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in } 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; } @@ -1568,10 +1521,10 @@ 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; @@ -1580,7 +1533,7 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in isExit = true; } - if (_trackDesignPlaceOperation != PTD_OPERATION_1) + if (_trackDesignPlaceOperation != PTD_OPERATION_PLACE_QUERY) { LocationXY16 tile = { (int16_t)(x + CoordsDirectionDelta[rotation].x), @@ -1603,16 +1556,17 @@ 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; } @@ -1658,10 +1612,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; } @@ -1717,7 +1671,7 @@ int32_t place_virtual_track( rct_td6_scenery_element* scenery = td6->scenery_elements; if (track_place_success && scenery != nullptr) { - if (!track_design_place_scenery(scenery, gTrackPreviewOrigin.x, gTrackPreviewOrigin.y, gTrackPreviewOrigin.z)) + if (!track_design_place_all_scenery(scenery, gTrackPreviewOrigin.x, gTrackPreviewOrigin.y, gTrackPreviewOrigin.z)) { return _trackDesignPlaceCost; } @@ -1760,7 +1714,7 @@ 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; @@ -1819,7 +1773,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) @@ -1844,6 +1798,7 @@ static bool track_design_place_preview(rct_track_td6* td6, money32* cost, Ride** _currentTrackPieceDirection = backup_rotation; user_string_free(ride->name); ride->type = RIDE_TYPE_NULL; + ride->measurement = {}; byte_9D8150 = false; return false; } @@ -1934,11 +1889,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 +1901,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); @@ -1994,7 +1949,7 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags 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; @@ -2278,6 +2233,9 @@ 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++) { @@ -2299,7 +2257,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); } diff --git a/src/openrct2/ride/TrackDesign.h b/src/openrct2/ride/TrackDesign.h index 15f7634177..32982f9b9b 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 @@ -198,12 +198,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 diff --git a/src/openrct2/ride/TrackDesignRepository.cpp b/src/openrct2/ride/TrackDesignRepository.cpp index 39573aceba..967cfe0f28 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 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..ba72309037 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 @@ -306,19 +306,19 @@ static void track_design_save_add_scenery(int32_t x, int32_t y, TileElement* til track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, 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(int32_t x, int32_t y, 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)) { @@ -336,18 +336,18 @@ static void track_design_save_add_large_scenery(int32_t x, int32_t y, TileElemen 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) + auto largeElement = map_get_large_scenery_segment(x, y, z, 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(x, y, tileElement); + track_design_save_push_tile_element(x, y, reinterpret_cast(largeElement)); } } } @@ -402,7 +402,7 @@ static bool track_design_save_add_tile_element(int32_t interactionType, int32_t track_design_save_add_scenery(x, y, tileElement); return true; case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: - track_design_save_add_large_scenery(x, y, tileElement); + track_design_save_add_large_scenery(x, y, tileElement->AsLargeScenery()); return true; case VIEWPORT_INTERACTION_ITEM_WALL: track_design_save_add_wall(x, y, tileElement); @@ -499,19 +499,19 @@ static void track_design_save_remove_scenery(int32_t x, int32_t y, TileElement* track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, 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(int32_t x, int32_t y, 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)) { @@ -529,15 +529,15 @@ static void track_design_save_remove_large_scenery(int32_t x, int32_t y, TileEle 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) + auto largeElement = map_get_large_scenery_segment(x, y, z, direction, sequence); + if (largeElement != nullptr) { if (sequence == 0) { - uint8_t flags = tileElement->GetDirection(); + uint8_t flags = largeElement->GetDirection(); track_design_save_pop_tile_element_desc(entry, x, y, z, flags); } - track_design_save_pop_tile_element(x, y, tileElement); + track_design_save_pop_tile_element(x, y, reinterpret_cast(largeElement)); } } } @@ -584,7 +584,7 @@ static void track_design_save_remove_tile_element(int32_t interactionType, int32 track_design_save_remove_scenery(x, y, tileElement); break; case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: - track_design_save_remove_large_scenery(x, y, tileElement); + track_design_save_remove_large_scenery(x, y, tileElement->AsLargeScenery()); break; case VIEWPORT_INTERACTION_ITEM_WALL: track_design_save_remove_wall(x, y, tileElement); @@ -797,7 +797,11 @@ static rct_track_td6* track_design_save_to_td6(ride_id_t rideIndex) 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; + if (ride->type == RIDE_TYPE_MINI_GOLF) + td6->inversions = ride->holes & 0x1F; + else + td6->inversions = ride->inversions & 0x1F; + td6->inversions |= (ride->sheltered_eighths << 5); td6->drops = ride->drops; td6->highest_drop_height = ride->highest_drop_height; diff --git a/src/openrct2/ride/TrackPaint.cpp b/src/openrct2/ride/TrackPaint.cpp index 183190ce72..b95f39c0e3 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 @@ -2164,7 +2164,7 @@ void track_paint(paint_session* session, uint8_t direction, int32_t height, cons 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..ed93fb17a4 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 @@ -839,9 +839,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) @@ -1392,7 +1392,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); @@ -1449,7 +1449,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 +1460,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 +1500,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); @@ -1557,32 +1557,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 +1776,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; @@ -1855,7 +1857,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; @@ -2020,6 +2022,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); @@ -2073,9 +2077,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 +2112,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 +2130,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; } } @@ -2173,9 +2171,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 +2187,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) @@ -2252,7 +2246,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 +2272,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) { @@ -2414,8 +2408,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 +2415,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); } /** @@ -2444,7 +2436,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 +2452,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); } /** @@ -2492,7 +2482,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 +2498,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 +2514,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 +2523,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 +2543,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 +2557,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 +2565,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 +2617,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 +2626,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 +2643,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; } @@ -2836,7 +2802,7 @@ static bool vehicle_can_depart_synchronised(rct_vehicle* vehicle) 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 +2872,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)) { @@ -3035,7 +3001,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); @@ -3105,6 +3071,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 +3128,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); @@ -3245,7 +3211,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 +3371,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; } @@ -3457,12 +3423,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; } @@ -3511,6 +3473,15 @@ static void vehicle_check_if_missing(rct_vehicle* vehicle) 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 +3490,15 @@ 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 && 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 +3508,7 @@ static void vehicle_update_collision_setup(rct_vehicle* vehicle) return; } - ride_crash(ride, trainIndex); + ride->Crash(trainIndex); if (ride->status != RIDE_STATUS_CLOSED) { @@ -3594,8 +3570,13 @@ 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) @@ -3672,7 +3653,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 +3688,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 +3726,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 +3798,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; } @@ -3859,9 +3835,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 +3941,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,40 +3961,34 @@ 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; } @@ -4031,26 +3997,20 @@ loc_6D8E36: audio_play_sound_at_location(SOUND_RIDE_LAUNCH_2, 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); } /** @@ -4100,9 +4060,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 +4100,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); } /** @@ -4160,7 +4116,7 @@ static void vehicle_update_waiting_for_cable_lift(rct_vehicle* vehicle) 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; } @@ -4207,7 +4163,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 +4178,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 +4224,13 @@ 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); + 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 +4266,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 (;;) { @@ -4536,7 +4490,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: @@ -4656,7 +4610,7 @@ static bool vehicle_boat_is_location_accessible(const TileCoordsXYZ& location) TileElement* tileElement = map_get_first_element_at(location.x, location.y); do { - if (tileElement->IsGhost() == true) + if (tileElement->IsGhost()) continue; if (tileElement->GetType() == TILE_ELEMENT_TYPE_SURFACE) @@ -4710,7 +4664,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 +4692,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; } @@ -4795,7 +4747,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 +4786,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 +4808,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; } @@ -4912,7 +4860,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 +4879,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 +4895,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 +4920,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 +4944,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 +4953,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; } @@ -5026,7 +4966,7 @@ static void vehicle_update_haunted_house_operating(rct_vehicle* vehicle) 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); @@ -5036,7 +4976,7 @@ static void vehicle_update_haunted_house_operating(rct_vehicle* vehicle) 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); @@ -5056,9 +4996,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 +5021,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 +5055,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 +5076,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; } } @@ -5257,10 +5189,14 @@ 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 && 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 +5206,7 @@ static void vehicle_crash_on_land(rct_vehicle* vehicle) return; } - ride_crash(ride, trainIndex); + ride->Crash(trainIndex); if (ride->status != RIDE_STATUS_CLOSED) { @@ -5304,17 +5240,21 @@ 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 && 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 +5264,7 @@ static void vehicle_crash_on_water(rct_vehicle* vehicle) return; } - ride_crash(ride, trainIndex); + ride->Crash(trainIndex); if (ride->status != RIDE_STATUS_CLOSED) { @@ -5359,7 +5299,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; } @@ -5409,9 +5349,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) { @@ -5664,7 +5603,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; @@ -6179,11 +6118,7 @@ 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) @@ -6283,7 +6218,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 +6227,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 +6247,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 +6262,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 +6317,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 +6424,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 +6436,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 +6465,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; } @@ -6655,7 +6588,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 +6604,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()) { @@ -6762,19 +6695,19 @@ 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)) + if (ride->IsBlockSectioned()) { audio_play_sound_at_location(SOUND_48, x, y, z); } @@ -7043,7 +6976,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 +7135,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(); } /** @@ -7277,7 +7210,7 @@ static void vehicle_update_additional_animation(rct_vehicle* vehicle) vehicle->z + SteamParticleOffsets[index].z); } } - vehicle_invalidate(vehicle); + vehicle->Invalidate(); } break; case VEHICLE_ENTRY_ANIMATION_SWAN: // loc_6D6424 @@ -7286,7 +7219,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 +7229,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 +7239,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 +7248,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 +7261,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 +7270,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 +7282,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 +7304,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,9 +7315,9 @@ 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) { @@ -7400,9 +7333,9 @@ 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) { @@ -7432,7 +7365,7 @@ 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; @@ -7440,15 +7373,15 @@ static void vehicle_update_scenery_door(rct_vehicle* vehicle) if (vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) { - 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 { - tileElement->AsWall()->SetAnimationIsBackwards(false); - tileElement->AsWall()->SetAnimationFrame(6); + tileElement->SetAnimationIsBackwards(false); + tileElement->SetAnimationFrame(6); vehicle_play_scenery_door_close_sound(vehicle, tileElement); } } @@ -7512,7 +7445,7 @@ 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; @@ -7520,15 +7453,15 @@ static void vehicle_update_handle_scenery_door(rct_vehicle* vehicle) if (vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) { - 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 { - tileElement->AsWall()->SetAnimationIsBackwards(true); - tileElement->AsWall()->SetAnimationFrame(6); + tileElement->SetAnimationIsBackwards(true); + tileElement->SetAnimationFrame(6); vehicle_play_scenery_door_close_sound(vehicle, tileElement); } } @@ -7661,10 +7594,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 +7676,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; @@ -8398,7 +8328,7 @@ static bool vehicle_update_track_motion_backwards_get_new_track( break; } - if (nextTileBackwards == true) + if (nextTileBackwards) { // loc_6DBB7E:; track_begin_end trackBeginEnd; @@ -8722,7 +8652,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 +8996,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 +9161,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; @@ -9758,17 +9688,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; } } @@ -10012,3 +9939,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..9b886774cf 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 @@ -111,6 +111,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 +202,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 @@ -234,6 +269,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 +351,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 +458,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..0857ec3a3b 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 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..cda0732c84 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 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..50aec667dc 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 diff --git a/src/openrct2/ride/coaster/ReverseFreefallCoaster.cpp b/src/openrct2/ride/coaster/ReverseFreefallCoaster.cpp index cec6c2b122..6323b42c7d 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 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..88896968ce 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 diff --git a/src/openrct2/ride/gentle/CrookedHouse.cpp b/src/openrct2/ride/gentle/CrookedHouse.cpp index 190d9ed233..c1b468a662 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 diff --git a/src/openrct2/ride/gentle/Dodgems.cpp b/src/openrct2/ride/gentle/Dodgems.cpp index c5c2fcee97..924ed4454e 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 diff --git a/src/openrct2/ride/gentle/FerrisWheel.cpp b/src/openrct2/ride/gentle/FerrisWheel.cpp index 1bd1c6537b..193fe84ccc 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 diff --git a/src/openrct2/ride/gentle/FlyingSaucers.cpp b/src/openrct2/ride/gentle/FlyingSaucers.cpp index 57fbec08b9..df97d22ce6 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 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..569e10faf0 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 @@ -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) 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..fb3db1cc96 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 @@ -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) diff --git a/src/openrct2/ride/gentle/MiniGolf.cpp b/src/openrct2/ride/gentle/MiniGolf.cpp index a196bda25d..4c0152393e 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 @@ -1193,7 +1193,7 @@ void vehicle_visual_mini_golf_player( return; } - rct_drawpixelinfo* edi = session->DPI; + rct_drawpixelinfo* edi = &session->DPI; if (edi->zoom_level >= 2) { return; @@ -1226,7 +1226,7 @@ void vehicle_visual_mini_golf_ball( return; } - rct_drawpixelinfo* edi = session->DPI; + rct_drawpixelinfo* edi = &session->DPI; if (edi->zoom_level >= 1) { return; 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..694a2242ce 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) { diff --git a/src/openrct2/ride/gentle/SpaceRings.cpp b/src/openrct2/ride/gentle/SpaceRings.cpp index 0aeb20390f..1d582fdd81 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 diff --git a/src/openrct2/ride/gentle/SpiralSlide.cpp b/src/openrct2/ride/gentle/SpiralSlide.cpp index 355c2f8857..4101cb5e96 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 @@ -121,7 +121,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; diff --git a/src/openrct2/ride/shops/Facility.cpp b/src/openrct2/ride/shops/Facility.cpp index 999a62145e..c4c0a76964 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 diff --git a/src/openrct2/ride/shops/Shop.cpp b/src/openrct2/ride/shops/Shop.cpp index 4c64cf7222..fba47f155c 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 diff --git a/src/openrct2/ride/thrill/3dCinema.cpp b/src/openrct2/ride/thrill/3dCinema.cpp index eea1874b22..d2cf817202 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 diff --git a/src/openrct2/ride/thrill/Enterprise.cpp b/src/openrct2/ride/thrill/Enterprise.cpp index 00d18d5b8d..0a8cb47c50 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) { diff --git a/src/openrct2/ride/thrill/GoKarts.cpp b/src/openrct2/ride/thrill/GoKarts.cpp index 73b268c438..c5f1b4fa93 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 diff --git a/src/openrct2/ride/thrill/LaunchedFreefall.cpp b/src/openrct2/ride/thrill/LaunchedFreefall.cpp index 5f126e87c3..8a157f64b1 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); - } } } diff --git a/src/openrct2/ride/thrill/MagicCarpet.cpp b/src/openrct2/ride/thrill/MagicCarpet.cpp index 0eb94a2a83..f799350792 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); diff --git a/src/openrct2/ride/thrill/MotionSimulator.cpp b/src/openrct2/ride/thrill/MotionSimulator.cpp index 004be7e8f2..fd0cc6adf3 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 @@ -34,7 +34,7 @@ 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); + rct_ride_entry* rideEntry = ride->GetRideEntry(); const TileElement* savedTileElement = static_cast(session->CurrentlyDrawnItem); diff --git a/src/openrct2/ride/thrill/PirateShip.cpp b/src/openrct2/ride/thrill/PirateShip.cpp index ef08368231..4c20195a2a 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) { diff --git a/src/openrct2/ride/thrill/RotoDrop.cpp b/src/openrct2/ride/thrill/RotoDrop.cpp index ccbb55b6c2..bebfc4fb1e 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 diff --git a/src/openrct2/ride/thrill/SwingingInverterShip.cpp b/src/openrct2/ride/thrill/SwingingInverterShip.cpp index f5ed22c227..eb70df3f6d 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 diff --git a/src/openrct2/ride/thrill/TopSpin.cpp b/src/openrct2/ride/thrill/TopSpin.cpp index 01b977eb34..f3527ce196 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 @@ -167,7 +167,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)) diff --git a/src/openrct2/ride/thrill/Twist.cpp b/src/openrct2/ride/thrill/Twist.cpp index 29d991ffd3..a3e0ac979a 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) { diff --git a/src/openrct2/ride/transport/Chairlift.cpp b/src/openrct2/ride/transport/Chairlift.cpp index 11752fa1e8..06eb4676bf 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 diff --git a/src/openrct2/ride/transport/Lift.cpp b/src/openrct2/ride/transport/Lift.cpp index 48804a7aac..d3eacb4b94 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 diff --git a/src/openrct2/ride/transport/MiniatureRailway.cpp b/src/openrct2/ride/transport/MiniatureRailway.cpp index 6ab4d03eb3..7a34f54d73 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], @@ -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..a96fcf2276 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 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..ccb28bd6ff 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...) diff --git a/src/openrct2/ride/water/SplashBoats.cpp b/src/openrct2/ride/water/SplashBoats.cpp index ddbf89e0e9..9a3ac2a625 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 diff --git a/src/openrct2/ride/water/SubmarineRide.cpp b/src/openrct2/ride/water/SubmarineRide.cpp index 42220fcb35..576f5ab0d2 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) { 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..6c1efad7cf 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 @@ -238,7 +238,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; @@ -435,7 +435,7 @@ static int32_t scenario_create_ducks() if (!map_is_location_in_park({ x, y })) return 0; - centreWaterZ = (tile_element_height(x, y) >> 16) & 0xFFFF; + centreWaterZ = (tile_element_water_height(x, y)); if (centreWaterZ == 0) return 0; @@ -447,7 +447,7 @@ static int32_t scenario_create_ducks() { for (j = 0; j < 7; j++) { - waterZ = (tile_element_height(x2, y2) >> 16) & 0xFFFF; + waterZ = (tile_element_water_height(x2, y2)); if (waterZ == centreWaterZ) c++; @@ -493,78 +493,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) diff --git a/src/openrct2/scenario/Scenario.h b/src/openrct2/scenario/Scenario.h index 39c4667a10..6f259956fe 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 @@ -265,7 +265,7 @@ struct rct_s6_data uint8_t pad_0138B582[2]; rct_ride_rating_calc_data 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]; 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..cbb512de93 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 diff --git a/src/openrct2/scenario/ScenarioSources.h b/src/openrct2/scenario/ScenarioSources.h index 31677333ab..4e5194511d 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 diff --git a/src/openrct2/sprites.h b/src/openrct2/sprites.h index 5bad3cefca..8c37667de7 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 @@ -39,6 +39,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 @@ -829,7 +831,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,8 +939,20 @@ 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_ROUBLE_SIGN = SPR_G2_CHAR_BEGIN + 86, SPR_G2_CHAR_END = SPR_G2_ROUBLE_SIGN, SPR_G2_GLYPH_COUNT = (SPR_G2_CHAR_END - SPR_G2_CHAR_BEGIN) + 1, 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..c4ed2920ff 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; @@ -123,7 +52,7 @@ money32 place_provisional_track_piece( 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 +63,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 +82,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 +108,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); @@ -404,28 +334,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 { @@ -611,19 +524,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..628b9746ef 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 @@ -84,24 +84,6 @@ void rct_balloon::Pop() 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; - } -} - void create_balloon(int32_t x, int32_t y, int32_t z, int32_t colour, bool isPopped) { rct_sprite* sprite = create_sprite(2); @@ -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..213e282a67 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 @@ -59,287 +59,6 @@ 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++) @@ -409,6 +128,32 @@ TileElement* banner_get_tile_element(BannerIndex bannerIndex) return nullptr; } +WallElement* banner_get_scrolling_wall_tile_element(BannerIndex bannerIndex) +{ + rct_banner* banner = &gBanners[bannerIndex]; + TileElement* tileElement = map_get_first_element_at(banner->x, banner->y); + + if (tileElement == nullptr) + return nullptr; + + do + { + 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; +} + /** * * rct2: 0x006B7D86 @@ -421,7 +166,7 @@ uint8_t banner_get_closest_ride_index(int32_t x, int32_t y, int32_t z) { 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) @@ -495,7 +240,7 @@ void fix_duplicated_banners() log_error("Failed to create new banner."); continue; } - Guard::Assert(activeBanners[newBannerIndex] == false); + Guard::Assert(!activeBanners[newBannerIndex]); // Copy over the original banner, but update the location rct_banner& newBanner = gBanners[newBannerIndex]; @@ -529,47 +274,6 @@ void fix_duplicated_banners() } } -/** - * - * 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) -{ - *ebx = BannerRemove(*eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*edx >> 8) & 0xFF, *ebx & 0xFF); -} - -/** - * - * 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) -{ - *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); -} - BannerIndex BannerElement::GetIndex() const { return index; diff --git a/src/openrct2/world/Banner.h b/src/openrct2/world/Banner.h index a4a9cb0c5a..b504d66a0f 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 @@ -49,8 +49,7 @@ 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); diff --git a/src/openrct2/world/Climate.cpp b/src/openrct2/world/Climate.cpp index 03d1016866..08e3a13efd 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 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..e9d9c69353 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 @@ -201,9 +201,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) { @@ -224,8 +223,7 @@ void rct_duck::UpdateSwim() 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; + waterZ = tile_element_water_height(newX, newY); if (z >= landZ && z == waterZ) { diff --git a/src/openrct2/world/Entrance.cpp b/src/openrct2/world/Entrance.cpp index 489d68ee04..8bd6f66292 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); } } 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..b17302dc67 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,195 +125,6 @@ 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); @@ -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); } } @@ -1426,7 +1239,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) @@ -1638,7 +1453,7 @@ bool PathElement::IsBroken() const void PathElement::SetIsBroken(bool isBroken) { - if (isBroken == true) + if (isBroken) { flags |= TILE_ELEMENT_FLAG_BROKEN; } @@ -1655,7 +1470,7 @@ bool PathElement::IsBlockedByVehicle() const void PathElement::SetIsBlockedByVehicle(bool isBlocked) { - if (isBlocked == true) + if (isBlocked) { flags |= TILE_ELEMENT_FLAG_BLOCKED_BY_VEHICLE; } @@ -1738,7 +1553,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 @@ -2329,10 +2147,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..ee85628978 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 diff --git a/src/openrct2/world/Fountain.h b/src/openrct2/world/Fountain.h index c0d1a4abef..49a7c4e758 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 diff --git a/src/openrct2/world/LargeScenery.cpp b/src/openrct2/world/LargeScenery.cpp index cc3114c5f2..97f27a4174 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 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..d3b5d84b65 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 @@ -70,6 +70,47 @@ 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; + } + + CoordsXY Rotate(int32_t direction) + { + 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,6 +132,40 @@ 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; }; diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index 2fd1c22770..f778a907cd 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; @@ -138,6 +143,8 @@ LocationXY16 coordinate_3d_to_2d(const LocationXYZ16* coordinate_3d, int32_t rot switch (rotation) { + // this function has to use right-shift (... >> 1) since dividing + // by 2 with (... / 2) can differ by -1 and cause issues (see PR #9301) default: case 0: coordinate_2d.x = coordinate_3d->y - coordinate_3d->x; @@ -454,7 +461,7 @@ 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(int32_t x, int32_t y) { TileElement* tileElement; @@ -474,7 +481,7 @@ int32_t tile_element_height(int32_t x, int32_t y) return 16; } - uint32_t height = (tileElement->AsSurface()->GetWaterHeight() << 20) | (tileElement->base_height << 3); + uint16_t height = (tileElement->base_height << 3); uint32_t slope = tileElement->AsSurface()->GetSlope(); uint8_t extra_height = (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) >> 4; // 0x10 is the 5th bit - sets slope to double height @@ -606,6 +613,31 @@ int32_t tile_element_height(int32_t x, int32_t y) return height; } +int16_t tile_element_water_height(int32_t x, int32_t y) +{ + TileElement* tileElement; + + // Off the map + if ((unsigned)x >= 8192 || (unsigned)y >= 8192) + return 0; + + // 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 }); + + if (tileElement == nullptr) + { + return 0; + } + + uint16_t height = (tileElement->AsSurface()->GetWaterHeight() << 4); + + return height; +} + /** * Checks if the tile at coordinate at height counts as connected. * @return 1 if connected, 0 otherwise @@ -704,12 +736,9 @@ int32_t map_height_from_slope(const CoordsXY coords, int32_t slope, bool isSlope 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) @@ -792,234 +821,11 @@ bool map_is_location_owned_or_has_rights(int32_t x, int32_t y) 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 +851,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, @@ -1121,53 +927,7 @@ int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t d 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) -{ - 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) +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); @@ -1189,7 +949,7 @@ static uint8_t map_get_lowest_land_height(int32_t xMin, int32_t xMax, int32_t yM 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(int32_t xMin, int32_t xMax, int32_t yMin, int32_t yMax) { xMin = std::max(xMin, 32); yMin = std::max(yMin, 32); @@ -1217,1185 +977,11 @@ 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) -{ - 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; - } -} - /** * * rct2: 0x0068B280 @@ -2463,13 +1049,11 @@ 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( @@ -3021,7 +1605,13 @@ void map_remove_out_of_range_elements() { 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); + // 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->AsSurface()->SetOwnership(OWNERSHIP_UNOWNED); + update_park_fences_around_tile({ x, y }); + } clear_elements_at(x, y); } } @@ -3160,8 +1750,8 @@ static void clear_element_at(int32_t x, int32_t y, TileElement** elementPtr) y -= CoordsDirectionDelta[rotation].y; 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({ x, y, element->base_height * 8 }); + GameActions::Execute(&parkEntranceRemoveAction); break; } case TILE_ELEMENT_TYPE_WALL: @@ -3179,11 +1769,12 @@ static void clear_element_at(int32_t x, int32_t y, TileElement** elementPtr) } 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( + { x, y, element->base_height * 8, element->AsBanner()->GetPosition() }); + GameActions::Execute(&bannerRemoveAction); break; + } default: tile_element_remove(element); break; @@ -3234,7 +1825,7 @@ int32_t map_get_highest_z(int32_t tileX, int32_t tileY) 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); if (tileElement == nullptr) @@ -3252,7 +1843,7 @@ 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; } @@ -3273,7 +1864,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(); @@ -3298,7 +1889,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(); @@ -3323,7 +1914,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,7 +1923,7 @@ 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); if (tileElement != nullptr) @@ -3348,7 +1939,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 +1947,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 +1979,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 +1990,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 +2007,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); } @@ -3655,172 +2245,13 @@ void map_clear_all_elements() } } -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); do @@ -3830,7 +2261,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; @@ -3968,28 +2399,15 @@ 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); do @@ -4001,7 +2419,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; } diff --git a/src/openrct2/world/Map.h b/src/openrct2/world/Map.h index 5bf2570356..bf54aa9a46 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,7 +129,12 @@ 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(); @@ -141,12 +146,15 @@ BannerElement* map_get_banner_element_at(int32_t x, int32_t y, int32_t z, uint8_ 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); +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); +int16_t tile_element_height(int32_t x, int32_t y); +int16_t tile_element_water_height(int32_t x, int32_t y); +uint8_t map_get_highest_land_height(int32_t xMin, int32_t xMax, int32_t yMin, int32_t yMax); +uint8_t map_get_lowest_land_height(int32_t xMin, int32_t xMax, int32_t yMin, int32_t yMax); bool map_coord_is_connected(int32_t x, int32_t y, int32_t z, uint8_t faceDirection); void map_remove_provisional_elements(); void map_restore_provisional_elements(); @@ -178,44 +186,6 @@ bool map_can_construct_with_clear_at( 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 { @@ -259,15 +229,15 @@ int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t d 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); -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); TileElement* map_get_track_element_at_of_type_from_ride( diff --git a/src/openrct2/world/MapAnimation.cpp b/src/openrct2/world/MapAnimation.cpp index 84ee86c4a2..7190f93358 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 diff --git a/src/openrct2/world/MapAnimation.h b/src/openrct2/world/MapAnimation.h index 9bf6efd7b5..496c6b7fc5 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 diff --git a/src/openrct2/world/MapGen.cpp b/src/openrct2/world/MapGen.cpp index 06f74aa5d8..b624879531 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 @@ -136,8 +136,6 @@ 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; wallTexture = settings->wall; @@ -354,7 +352,7 @@ static void mapgen_place_trees() 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 +361,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 +369,7 @@ static void mapgen_place_trees() break; case TERRAIN_ICE: - if (snowTreeIds.size() == 0) + if (snowTreeIds.empty()) break; type = snowTreeIds[util_rand() % snowTreeIds.size()]; 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..b77f788a1a 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 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..1b57ecc48b 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,16 +12,33 @@ #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; @@ -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..06ae680b0c 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" @@ -94,7 +95,7 @@ void reset_park_entry() */ static PeepSpawn* get_random_peep_spawn() { - if (gPeepSpawns.size() > 0) + if (!gPeepSpawns.empty()) { return &gPeepSpawns[scenario_rand() % gPeepSpawns.size()]; } @@ -104,55 +105,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); } /** @@ -247,213 +203,6 @@ void park_set_name(const char* name) } } -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; @@ -993,7 +742,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..64e68cc679 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 @@ -87,17 +87,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; @@ -132,20 +121,10 @@ 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..2639c78695 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 @@ -80,9 +80,8 @@ 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) { diff --git a/src/openrct2/world/Scenery.cpp b/src/openrct2/world/Scenery.cpp index 212232ccc2..5d2bf86b0b 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 @@ -189,7 +187,8 @@ 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); + removeSceneryAction.SetFlags( + GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST); removeSceneryAction.Execute(); } @@ -229,16 +228,19 @@ void scenery_remove_ghost_tool_placement() auto removeSceneryAction = LargeSceneryRemoveAction(x, y, z, 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..94a66bc76c 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,60 +24,6 @@ #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) -{ - 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) && !gCheatsSandboxMode) - { - if (!map_is_location_owned(x, y, z)) - { - return MONEY32_UNDEFINED; - } - } - - TileElement* tileElement = map_get_small_scenery_element_at(x, y, baseHeight, sceneryType, quadrant); - - if (tileElement == nullptr) - { - return 0; - } - - if ((flags & GAME_COMMAND_FLAG_GHOST) && !(tileElement->IsGhost())) - { - return 0; - } - - if (flags & GAME_COMMAND_FLAG_APPLY) - { - tileElement->AsSmallScenery()->SetPrimaryColour(primaryColour); - tileElement->AsSmallScenery()->SetSecondaryColour(secondaryColour); - - map_invalidate_tile_full(x, y); - } - - 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 @@ -94,7 +40,7 @@ int32_t map_place_scenery_clear_func(TileElement** tile_element, int32_t x, int3 if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) { - if (scenery->small_scenery.height > 64) + if (scenery_small_entry_has_flag(scenery, SMALL_SCENERY_FLAG_IS_TREE)) return 1; } @@ -128,7 +74,7 @@ int32_t map_place_non_scenery_clear_func(TileElement** tile_element, int32_t x, if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL) { - if (scenery->small_scenery.height > 64) + if (scenery_small_entry_has_flag(scenery, SMALL_SCENERY_FLAG_IS_TREE)) return 1; } 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..1ddf780591 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 @@ -174,13 +174,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,7 +251,10 @@ 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) { @@ -313,13 +316,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_type_offset = SPRITE_LIST_FREE * 2; // 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 @@ -343,25 +346,25 @@ void sprite_clear_all_unused() */ rct_sprite* create_sprite(uint8_t bl) { - size_t linkedListTypeOffset = SPRITE_LIST_UNKNOWN * 2; + SPRITE_LIST linkedListTypeOffset = SPRITE_LIST_UNKNOWN; if ((bl & 2) != 0) { // 69EC96; uint16_t cx = 0x12C - gSpriteListCount[SPRITE_LIST_MISC]; - if (cx >= gSpriteListCount[SPRITE_LIST_NULL]) + if (cx >= gSpriteListCount[SPRITE_LIST_FREE]) { return nullptr; } - linkedListTypeOffset = SPRITE_LIST_MISC * 2; + linkedListTypeOffset = SPRITE_LIST_MISC; } - else if (gSpriteListCount[SPRITE_LIST_NULL] == 0) + else if (gSpriteListCount[SPRITE_LIST_FREE] == 0) { return nullptr; } - rct_sprite_generic* sprite = &(get_sprite(gSpriteListHead[SPRITE_LIST_NULL]))->generic; + rct_sprite_generic* sprite = &(get_sprite(gSpriteListHead[SPRITE_LIST_FREE]))->generic; - move_sprite_to_list((rct_sprite*)sprite, (uint8_t)linkedListTypeOffset); + move_sprite_to_list((rct_sprite*)sprite, linkedListTypeOffset); // Need to reset all sprite data, as the uninitialised values // may contain garbage and cause a desync later on. @@ -389,12 +392,12 @@ rct_sprite* create_sprite(uint8_t bl) * of the desired linked list in a uint16_t array. Known valid values are * 2, 4, 6, 8 or 10 (SPRITE_LIST_... * 2) */ -void move_sprite_to_list(rct_sprite* sprite, uint8_t newListOffset) +void move_sprite_to_list(rct_sprite* sprite, SPRITE_LIST newList) { 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 newListOffset = newList * 2; // No need to move if the sprite is already in the desired list if (oldListOffset == newListOffset) @@ -538,7 +541,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); @@ -639,31 +642,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 }; + CoordsXY newCoords = 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 = newCoords.x - sprite->generic.sprite_width; + sprite->generic.sprite_right = newCoords.x + sprite->generic.sprite_width; + sprite->generic.sprite_top = newCoords.y - sprite->generic.sprite_height_negative; + sprite->generic.sprite_bottom = newCoords.y + sprite->generic.sprite_height_positive; sprite->generic.x = x; sprite->generic.y = y; sprite->generic.z = z; @@ -681,7 +666,7 @@ void sprite_remove(rct_sprite* sprite) user_string_free(peep->name_string_idx); } - 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; @@ -712,10 +697,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; } @@ -762,7 +744,7 @@ void litter_create(int32_t x, int32_t y, int32_t z, int32_t direction, int32_t t if (litter == nullptr) return; - move_sprite_to_list((rct_sprite*)litter, SPRITE_LIST_LITTER * 2); + move_sprite_to_list((rct_sprite*)litter, SPRITE_LIST_LITTER); litter->sprite_direction = direction; litter->sprite_width = 6; litter->sprite_height_negative = 6; @@ -1029,7 +1011,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 +1019,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..0b66a33286 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 @@ -30,7 +30,7 @@ enum SPRITE_IDENTIFIER enum SPRITE_LIST { - SPRITE_LIST_NULL, + SPRITE_LIST_FREE, SPRITE_LIST_TRAIN, SPRITE_LIST_PEEP, SPRITE_LIST_MISC, @@ -93,6 +93,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 @@ -139,9 +144,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); @@ -205,7 +212,7 @@ rct_sprite* create_sprite(uint8_t bl); 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,8 +236,6 @@ 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 @@ -241,14 +246,6 @@ 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/Surface.cpp b/src/openrct2/world/Surface.cpp index 4fa99a6137..decf1594b0 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 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..09172082a8 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 @@ -56,7 +56,7 @@ bool TileElementBase::IsGhost() const void TileElementBase::SetGhost(bool isGhost) { - if (isGhost == true) + if (isGhost) { this->flags |= TILE_ELEMENT_FLAG_GHOST; } diff --git a/src/openrct2/world/TileElement.h b/src/openrct2/world/TileElement.h index 2eb2c7747a..4e350531ce 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 diff --git a/src/openrct2/world/TileInspector.cpp b/src/openrct2/world/TileInspector.cpp index 0e9aaaa54c..5278f341ef 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,6 +11,7 @@ #include "../Context.h" #include "../Game.h" +#include "../actions/GameAction.h" #include "../common.h" #include "../core/Guard.hpp" #include "../interface/Window.h" @@ -34,10 +35,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) { @@ -77,28 +78,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, 0); // 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); + TileElement* const selectedElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex + 1); if (!selectedElement) { - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } corruptElement->base_height = corruptElement->clearance_height = selectedElement->base_height; @@ -106,7 +108,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 +117,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++; @@ -135,7 +137,7 @@ int32_t tile_inspector_insert_corrupt_at(int32_t x, int32_t y, int16_t elementIn } // Nothing went wrong - return 0; + return std::make_unique(); } /** @@ -144,23 +146,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); + TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (!tileElement) { - 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--; @@ -177,23 +179,23 @@ int32_t tile_inspector_remove_element_at(int32_t x, int32_t y, int16_t elementIn } } - 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) @@ -205,19 +207,19 @@ int32_t tile_inspector_swap_elements_at(int32_t x, int32_t y, int16_t first, int } } - 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); + TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (!tileElement) { - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } switch (tileElement->GetType()) { @@ -247,11 +249,12 @@ int32_t tile_inspector_rotate_element_at(int32_t x, int32_t y, int32_t elementIn 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) + 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 == x && exit.y == y && exit.z == z) + 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 }); } @@ -273,41 +276,41 @@ 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; + newBanner.x = loc.x / 32; + newBanner.y = loc.y / 32; // Use the new banner index tile_element_set_banner_index(&element, newBannerIndex); @@ -321,13 +324,13 @@ int32_t tile_inspector_paste_element_at(int32_t x, int32_t y, TileElement elemen rct_string_id newStringIdx = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, buffer); if (newStringIdx == 0) { - return MONEY32_UNDEFINED; + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS, STR_NONE); } gBanners[newBannerIndex].string_idx = newStringIdx; } } - TileElement* const pastedElement = tile_element_insert(x, y, element.base_height, 0); + TileElement* const pastedElement = tile_element_insert(loc.x / 32, loc.y / 32, element.base_height, 0); bool lastForTile = pastedElement->IsLastForTile(); *pastedElement = element; @@ -337,16 +340,16 @@ int32_t tile_inspector_paste_element_at(int32_t x, int32_t y, TileElement elemen pastedElement->flags |= TILE_ELEMENT_FLAG_LAST_TILE; } - 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) @@ -356,14 +359,14 @@ int32_t tile_inspector_paste_element_at(int32_t x, int32_t y, TileElement elemen } } - 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); // Count elements on tile int32_t numElement = 0; @@ -387,7 +390,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 +404,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); } } - 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) { @@ -444,10 +448,11 @@ int32_t tile_inspector_any_base_height_offset(int32_t x, int32_t y, int16_t elem 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) + 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 == x && exit.y == y && exit.z == z) + 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 }); } } @@ -455,56 +460,56 @@ int32_t tile_inspector_any_base_height_offset(int32_t x, int32_t y, int16_t elem 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); } } - 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); + TileElement* 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); 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); } } - 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); + TileElement* 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 bool diagonal = (originalSlope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) >> 4; @@ -550,28 +555,28 @@ int32_t tile_inspector_surface_toggle_corner(int32_t x, int32_t y, int32_t corne 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); } } - 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); + TileElement* 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); @@ -588,81 +593,105 @@ 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); } } - 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); } } - 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) + { + window_invalidate(tileInspectorWindow); + } + } + + 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); } } - 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; + return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); Ride* 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,67 +699,70 @@ 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); } } - 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); } } - 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); + TileElement* const trackElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex); if (offset == 0) - return 0; + return std::make_unique(); 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(); @@ -739,59 +771,28 @@ int32_t tile_inspector_track_base_height_offset(int32_t x, int32_t y, int32_t el 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->base_height != elemZ / 8) @@ -816,13 +817,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 +835,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,12 +858,12 @@ 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(); @@ -872,59 +872,28 @@ int32_t tile_inspector_track_set_chain( 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->base_height != elemZ / 8) @@ -949,13 +918,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,18 +938,68 @@ 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) + { + window_invalidate(tileInspectorWindow); + } + } + + 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) + { + window_invalidate(tileInspectorWindow); + } + } + + 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); @@ -990,80 +1008,81 @@ int32_t tile_inspector_scenery_set_quarter_location( tileElement->flags &= 0xF0; tileElement->flags |= 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; - 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..d238a49a10 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 @@ -662,46 +93,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; 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..d6fe6632ef 100644 --- a/test/testpaint/Compat.cpp +++ b/test/testpaint/Compat.cpp @@ -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) @@ -435,3 +435,9 @@ 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; +} 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/generate.cpp b/test/testpaint/generate.cpp index dc06733518..28cd40dfee 100644 --- a/test/testpaint/generate.cpp +++ b/test/testpaint/generate.cpp @@ -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..72a485032d 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" diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp index 86140c7647..1c0b6c2f3b 100644 --- a/test/tests/Pathfinding.cpp +++ b/test/tests/Pathfinding.cpp @@ -69,7 +69,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 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/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));