diff --git a/CMakeLists.txt b/CMakeLists.txt index 6182d2398f..1f2f8b043a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,9 +50,9 @@ set(OBJECTS_VERSION "1.2.1") set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v${OBJECTS_VERSION}/objects.zip") set(OBJECTS_SHA1 "540e004abc683b3fe22211f5234e3d78ab023c5f") -set(REPLAYS_VERSION "0.0.54") +set(REPLAYS_VERSION "0.0.55") set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v${REPLAYS_VERSION}/replays.zip") -set(REPLAYS_SHA1 "FF54C3699C3A0DF02269C5EB4C4A9C8E6D5C4EB4") +set(REPLAYS_SHA1 "70B4B7CB26A428801676BCC615F3881E72A45406") option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.") option(WITH_TESTS "Build tests") diff --git a/distribution/changelog.txt b/distribution/changelog.txt index ad73c74f33..432cd2d76f 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -22,6 +22,7 @@ - Fix: [#15170] Plugin: incorrect label text alignment. - Fix: [#15184] Crash when hovering over water types in Object Selection. - Fix: [#15193] Crash when rides/stalls are demolished. +- Fix: [#15197] Cannot place flat ride after removing it in construction window. - Fix: [#15199] Construction window is not closed when a ride gets demolished. - Fix: [#15213] Freeze when hovering over Reverse Freefall Coaster in Russian. - Fix: [#15255] Tile Inspector shows banner information on walls that do not contain one. @@ -30,6 +31,7 @@ - Fix: [#15476] Crash when placing/clearing small scenery. - Fix: [#15487] Map animations do not work correctly when loading an exported SV6 file in vanilla RCT2. - Fix: [#15496] Crash in paint_swinging_inverter_ship_structure(). +- Fix: [#15503] Freeze when doing specific coaster merges with block brakes. - Fix: [#15514] Two different “quit to menu” menu items are available in track designer and track design manager. - Improved: [#3417] Crash dumps are now placed in their own folder. - Change: [#8601] Revert ToonTower base block fix to re-enable support blocking. diff --git a/openrct2.proj b/openrct2.proj index 4455a1c077..0613112165 100644 --- a/openrct2.proj +++ b/openrct2.proj @@ -48,8 +48,8 @@ 304d13a126c15bf2c86ff13b81a2f2cc1856ac8d https://github.com/OpenRCT2/objects/releases/download/v1.2.1/objects.zip 540e004abc683b3fe22211f5234e3d78ab023c5f - https://github.com/OpenRCT2/replays/releases/download/v0.0.54/replays.zip - FF54C3699C3A0DF02269C5EB4C4A9C8E6D5C4EB4 + https://github.com/OpenRCT2/replays/releases/download/v0.0.55/replays.zip + 70B4B7CB26A428801676BCC615F3881E72A45406 diff --git a/src/openrct2-ui/windows/RideConstruction.cpp b/src/openrct2-ui/windows/RideConstruction.cpp index 15de24f541..ca9ea239bd 100644 --- a/src/openrct2-ui/windows/RideConstruction.cpp +++ b/src/openrct2-ui/windows/RideConstruction.cpp @@ -1931,6 +1931,14 @@ static void window_ride_construction_mouseup_demolish(rct_window* w) const rct_preview_track* trackBlock = ted.Block; newCoords->z = (tileElement->GetBaseZ()) - trackBlock->z; gGotoStartPlacementMode = true; + + // When flat rides are deleted, the window should be reset so the ride can be placed again. + const auto rideId = w->rideId; + auto ride = get_ride(rideId); + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE)) + { + ride_initialise_construction_window(ride); + } } auto trackRemoveAction = TrackRemoveAction( diff --git a/src/openrct2-ui/windows/Staff.cpp b/src/openrct2-ui/windows/Staff.cpp index 422dd64116..9e208678c5 100644 --- a/src/openrct2-ui/windows/Staff.cpp +++ b/src/openrct2-ui/windows/Staff.cpp @@ -567,11 +567,10 @@ void window_staff_overview_dropdown(rct_window* w, rct_widgetindex widgetIndex, { return; } - // TODO: THIS SHOULD BE NETWORKED - peep->ClearPatrolArea(); - gfx_invalidate_screen(); - staff_update_greyed_patrol_areas(); + auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction( + peep->sprite_index, {}, StaffSetPatrolAreaMode::ClearAll); + GameActions::Execute(&staffSetPatrolAreaAction); } else { @@ -1241,7 +1240,9 @@ void window_staff_overview_tool_down(rct_window* w, rct_widgetindex widgetIndex, { _staffPatrolAreaPaintValue = PatrolAreaValue::SET; } - auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(w->number, destCoords); + auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction( + w->number, destCoords, + _staffPatrolAreaPaintValue == PatrolAreaValue::SET ? StaffSetPatrolAreaMode::Set : StaffSetPatrolAreaMode::Unset); GameActions::Execute(&staffSetPatrolAreaAction); } } @@ -1275,7 +1276,9 @@ void window_staff_overview_tool_drag(rct_window* w, rct_widgetindex widgetIndex, if (_staffPatrolAreaPaintValue == PatrolAreaValue::UNSET && !patrolAreaValue) return; // Since area is already the value we want, skip... - auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction(w->number, destCoords); + auto staffSetPatrolAreaAction = StaffSetPatrolAreaAction( + w->number, destCoords, + _staffPatrolAreaPaintValue == PatrolAreaValue::SET ? StaffSetPatrolAreaMode::Set : StaffSetPatrolAreaMode::Unset); GameActions::Execute(&staffSetPatrolAreaAction); } diff --git a/src/openrct2/actions/StaffSetPatrolAreaAction.cpp b/src/openrct2/actions/StaffSetPatrolAreaAction.cpp index 306b784482..d6fc7cf81f 100644 --- a/src/openrct2/actions/StaffSetPatrolAreaAction.cpp +++ b/src/openrct2/actions/StaffSetPatrolAreaAction.cpp @@ -14,9 +14,10 @@ #include "../peep/Staff.h" #include "../world/Entity.h" -StaffSetPatrolAreaAction::StaffSetPatrolAreaAction(uint16_t spriteId, const CoordsXY& loc) +StaffSetPatrolAreaAction::StaffSetPatrolAreaAction(uint16_t spriteId, const CoordsXY& loc, const StaffSetPatrolAreaMode mode) : _spriteId(spriteId) , _loc(loc) + , _mode(mode) { } @@ -28,7 +29,7 @@ uint16_t StaffSetPatrolAreaAction::GetActionFlags() const void StaffSetPatrolAreaAction::Serialise(DataSerialiser& stream) { GameAction::Serialise(stream); - stream << DS_TAG(_spriteId) << DS_TAG(_loc); + stream << DS_TAG(_spriteId) << DS_TAG(_loc) << DS_TAG(_mode); } GameActions::Result::Ptr StaffSetPatrolAreaAction::Query() const @@ -54,6 +55,19 @@ GameActions::Result::Ptr StaffSetPatrolAreaAction::Query() const return MakeResult(); } +static void InvalidatePatrolTile(const CoordsXY& loc) +{ + // Align the location to the top left of the patrol square + const auto alignedLoc = CoordsXY{ loc.x & 0x1F80, loc.y & 0x1F80 }; + for (int32_t y = 0; y < 4 * COORDS_XY_STEP; y += COORDS_XY_STEP) + { + for (int32_t x = 0; x < 4 * COORDS_XY_STEP; x += COORDS_XY_STEP) + { + map_invalidate_tile_full(alignedLoc + CoordsXY{ x, y }); + } + } +} + GameActions::Result::Ptr StaffSetPatrolAreaAction::Execute() const { auto staff = TryGetEntity(_spriteId); @@ -63,21 +77,26 @@ GameActions::Result::Ptr StaffSetPatrolAreaAction::Execute() const return MakeResult(GameActions::Status::InvalidParameters, STR_NONE); } - staff->TogglePatrolArea(_loc); - - if (!staff->HasPatrolArea()) + switch (_mode) { - // This frees the data if there is no patrol area - staff->ClearPatrolArea(); + case StaffSetPatrolAreaMode::Set: + staff->SetPatrolArea(_loc, true); + InvalidatePatrolTile(_loc); + break; + case StaffSetPatrolAreaMode::Unset: + staff->SetPatrolArea(_loc, false); + if (!staff->HasPatrolArea()) + { + staff->ClearPatrolArea(); + } + InvalidatePatrolTile(_loc); + break; + case StaffSetPatrolAreaMode::ClearAll: + staff->ClearPatrolArea(); + gfx_invalidate_screen(); + break; } - for (int32_t y = 0; y < 4 * COORDS_XY_STEP; y += COORDS_XY_STEP) - { - for (int32_t x = 0; x < 4 * COORDS_XY_STEP; x += COORDS_XY_STEP) - { - 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/StaffSetPatrolAreaAction.h b/src/openrct2/actions/StaffSetPatrolAreaAction.h index 7d43239aa7..d6b31ce52a 100644 --- a/src/openrct2/actions/StaffSetPatrolAreaAction.h +++ b/src/openrct2/actions/StaffSetPatrolAreaAction.h @@ -11,15 +11,23 @@ #include "GameAction.h" +enum class StaffSetPatrolAreaMode : uint8_t +{ + Set, + Unset, + ClearAll +}; + DEFINE_GAME_ACTION(StaffSetPatrolAreaAction, GameCommand::SetStaffPatrol, GameActions::Result) { private: uint16_t _spriteId{ SPRITE_INDEX_NULL }; CoordsXY _loc; + StaffSetPatrolAreaMode _mode; public: StaffSetPatrolAreaAction() = default; - StaffSetPatrolAreaAction(uint16_t spriteId, const CoordsXY& loc); + StaffSetPatrolAreaAction(uint16_t spriteId, const CoordsXY& loc, const StaffSetPatrolAreaMode mode); uint16_t GetActionFlags() const override; diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 6930ca5785..3b68f82cfc 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -40,7 +40,7 @@ // 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 "12" +#define NETWORK_STREAM_VERSION "13" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION static Peep* _pickup_peep = nullptr; diff --git a/src/openrct2/ride/Ride.cpp b/src/openrct2/ride/Ride.cpp index 28e6afb0a1..e8c5e28213 100644 --- a/src/openrct2/ride/Ride.cpp +++ b/src/openrct2/ride/Ride.cpp @@ -3497,8 +3497,16 @@ void Ride::MoveTrainsToBlockBrakes(TrackElement* firstBlock) continue; } + size_t numIterations = 0; do { + // Fixes both freezing issues in #15503. + // TODO: refactor the code so a tortoise-and-hare algorithm can be used. + if (numIterations++ > 1000000) + { + break; + } + firstBlock->SetBlockBrakeClosed(true); for (Vehicle* car = train; car != nullptr; car = GetEntity(car->next_vehicle_on_train)) {