mirror of https://github.com/OpenRCT2/OpenRCT2.git
Merge pull request #8689 from duncanspumpkin/land_set_heigh_gc
Move Land Set Height into GameAction Framework
This commit is contained in:
commit
f32531c911
|
@ -21,10 +21,11 @@
|
|||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
2A5354E922099C4F00A5440F /* Network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A5354E822099C4F00A5440F /* Network.cpp */; };
|
||||
2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */; };
|
||||
2AA050332209A8E300D3A922 /* StaffSetOrdersAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */; };
|
||||
2AAFD800220DD3D2002461A4 /* LandSetHeightAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AAFD7FF220DD3D2002461A4 /* LandSetHeightAction.hpp */; };
|
||||
2AF7893D220B253E0072754A /* RideSetAppearanceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */; };
|
||||
2A5354E922099C4F00A5440F /* Network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A5354E822099C4F00A5440F /* Network.cpp */; };
|
||||
4C29DEB3218C6AE500E8707F /* RCT12.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C29DEB2218C6AE500E8707F /* RCT12.cpp */; };
|
||||
4C358E5221C445F700ADE6BC /* ReplayManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C358E5021C445F700ADE6BC /* ReplayManager.cpp */; };
|
||||
4C3B4236205914F7000C5BB7 /* InGameConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C3B4234205914F7000C5BB7 /* InGameConsole.cpp */; };
|
||||
|
@ -616,12 +617,13 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
2A5354E822099C4F00A5440F /* Network.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Network.cpp; sourceTree = "<group>"; };
|
||||
2A5354EA22099C7200A5440F /* CircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircularBuffer.h; sourceTree = "<group>"; };
|
||||
2A5354EB22099D7700A5440F /* SignSetStyleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SignSetStyleAction.hpp; sourceTree = "<group>"; };
|
||||
2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetCostumeAction.hpp; sourceTree = "<group>"; };
|
||||
2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetOrdersAction.hpp; sourceTree = "<group>"; };
|
||||
2AAFD7FF220DD3D2002461A4 /* LandSetHeightAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandSetHeightAction.hpp; sourceTree = "<group>"; };
|
||||
2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetAppearanceAction.hpp; sourceTree = "<group>"; };
|
||||
2A5354E822099C4F00A5440F /* Network.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Network.cpp; sourceTree = "<group>"; };
|
||||
2A5354EA22099C7200A5440F /* CircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircularBuffer.h; sourceTree = "<group>"; };
|
||||
4C04D69F2056AA9600F82EBA /* linenoise.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = linenoise.hpp; sourceTree = "<group>"; };
|
||||
4C1A53EC205FD19F000F8EF5 /* SceneryObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SceneryObject.cpp; sourceTree = "<group>"; };
|
||||
4C29DEB2218C6AE500E8707F /* RCT12.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RCT12.cpp; sourceTree = "<group>"; };
|
||||
|
@ -2000,6 +2002,7 @@
|
|||
C6352B871F477032006CCEE3 /* actions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2AAFD7FF220DD3D2002461A4 /* LandSetHeightAction.hpp */,
|
||||
2AF7893C220B253E0072754A /* RideSetAppearanceAction.hpp */,
|
||||
2A5354EB22099D7700A5440F /* SignSetStyleAction.hpp */,
|
||||
2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */,
|
||||
|
@ -3323,6 +3326,7 @@
|
|||
2AF7893D220B253E0072754A /* RideSetAppearanceAction.hpp in Headers */,
|
||||
C67B28162002D67A00109C93 /* Window.h in Headers */,
|
||||
C6352B961F477032006CCEE3 /* RideSetStatus.hpp in Headers */,
|
||||
2AAFD800220DD3D2002461A4 /* LandSetHeightAction.hpp in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -1455,7 +1455,7 @@ void game_load_or_quit_no_save_prompt()
|
|||
|
||||
GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
|
||||
nullptr,
|
||||
game_command_set_land_height,
|
||||
nullptr,
|
||||
game_pause_toggle,
|
||||
game_command_place_track,
|
||||
game_command_remove_track,
|
||||
|
|
|
@ -19,7 +19,7 @@ struct rct_s6_data;
|
|||
enum GAME_COMMAND
|
||||
{
|
||||
GAME_COMMAND_SET_RIDE_APPEARANCE, // GA
|
||||
GAME_COMMAND_SET_LAND_HEIGHT,
|
||||
GAME_COMMAND_SET_LAND_HEIGHT, // GA
|
||||
GAME_COMMAND_TOGGLE_PAUSE,
|
||||
GAME_COMMAND_PLACE_TRACK,
|
||||
GAME_COMMAND_REMOVE_TRACK,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "FootpathRemoveAction.hpp"
|
||||
#include "GameAction.h"
|
||||
#include "GuestSetNameAction.hpp"
|
||||
#include "LandSetHeightAction.hpp"
|
||||
#include "LargeSceneryRemoveAction.hpp"
|
||||
#include "MazeSetTrackAction.hpp"
|
||||
#include "ParkMarketingAction.hpp"
|
||||
|
@ -68,6 +69,7 @@ namespace GameActions
|
|||
Register<WallRemoveAction>();
|
||||
Register<SmallSceneryRemoveAction>();
|
||||
Register<LargeSceneryRemoveAction>();
|
||||
Register<LandSetHeightAction>();
|
||||
Register<ClearAction>();
|
||||
}
|
||||
} // namespace GameActions
|
||||
|
|
|
@ -0,0 +1,385 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../OpenRCT2.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(LandSetHeightAction, GAME_COMMAND_SET_LAND_HEIGHT, GameActionResult)
|
||||
{
|
||||
private:
|
||||
CoordsXY _coords;
|
||||
uint8_t _height;
|
||||
uint8_t _style;
|
||||
|
||||
public:
|
||||
LandSetHeightAction()
|
||||
{
|
||||
}
|
||||
LandSetHeightAction(CoordsXY coords, uint8_t height, uint8_t style)
|
||||
: _coords(coords)
|
||||
, _height(height)
|
||||
, _style(style)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t GetActionFlags() const override
|
||||
{
|
||||
return GameAction::GetActionFlags() | GA_FLAGS::EDITOR_ONLY;
|
||||
}
|
||||
|
||||
void Serialise(DataSerialiser & stream) override
|
||||
{
|
||||
GameAction::Serialise(stream);
|
||||
|
||||
stream << DS_TAG(_coords) << DS_TAG(_height) << DS_TAG(_style);
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Query() const override
|
||||
{
|
||||
if (gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES)
|
||||
{
|
||||
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY);
|
||||
}
|
||||
|
||||
rct_string_id errorTitle = CheckParameters();
|
||||
if (errorTitle != STR_NONE)
|
||||
{
|
||||
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, errorTitle);
|
||||
}
|
||||
|
||||
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
|
||||
{
|
||||
if (!map_is_location_in_park(_coords))
|
||||
{
|
||||
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, STR_LAND_NOT_OWNED_BY_PARK);
|
||||
}
|
||||
}
|
||||
|
||||
money32 cost{};
|
||||
if (!gCheatsDisableClearanceChecks)
|
||||
{
|
||||
if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
|
||||
{
|
||||
// Check for obstructing large trees
|
||||
TileElement* tileElement = CheckTallTreeObstructions();
|
||||
if (tileElement != nullptr)
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, gGameCommandErrorText);
|
||||
}
|
||||
}
|
||||
cost = GetSmallSceneryRemovalCost();
|
||||
}
|
||||
|
||||
// Check for ride support limits
|
||||
if (!gCheatsDisableSupportLimits)
|
||||
{
|
||||
errorTitle = CheckRideSupports();
|
||||
if (errorTitle != STR_NONE)
|
||||
{
|
||||
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, errorTitle);
|
||||
}
|
||||
}
|
||||
|
||||
TileElement* surfaceElement = map_get_surface_element_at(_coords);
|
||||
TileElement* tileElement = CheckFloatingStructures(surfaceElement, _height);
|
||||
if (tileElement != nullptr)
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, gGameCommandErrorText);
|
||||
}
|
||||
|
||||
if (!gCheatsDisableClearanceChecks)
|
||||
{
|
||||
uint8_t zCorner = _height;
|
||||
if (_style & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK)
|
||||
{
|
||||
zCorner += 2;
|
||||
if (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG)
|
||||
{
|
||||
zCorner += 2;
|
||||
}
|
||||
}
|
||||
if (!map_can_construct_with_clear_at(
|
||||
_coords.x, _coords.y, _height, zCorner, &map_set_land_height_clear_func, 0xF, 0, nullptr,
|
||||
CREATE_CROSSING_MODE_NONE))
|
||||
{
|
||||
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, gGameCommandErrorText);
|
||||
}
|
||||
|
||||
tileElement = CheckUnremovableObstructions(surfaceElement, zCorner);
|
||||
if (tileElement != nullptr)
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return std::make_unique<GameActionResult>(GA_ERROR::DISALLOWED, gGameCommandErrorText);
|
||||
}
|
||||
}
|
||||
auto res = std::make_unique<GameActionResult>();
|
||||
res->Cost = cost + GetSurfaceHeightChangeCost(surfaceElement);
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
return res;
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Execute() const override
|
||||
{
|
||||
money32 cost = MONEY(0, 0);
|
||||
auto surfaceHeight = tile_element_height(_coords.x, _coords.y) & 0xFFFF;
|
||||
footpath_remove_litter(_coords.x, _coords.y, surfaceHeight);
|
||||
|
||||
if (!gCheatsDisableClearanceChecks)
|
||||
{
|
||||
wall_remove_at(_coords.x, _coords.y, _height * 8 - 16, _height * 8 + 32);
|
||||
cost += GetSmallSceneryRemovalCost();
|
||||
SmallSceneryRemoval();
|
||||
}
|
||||
|
||||
TileElement* surfaceElement = map_get_surface_element_at(_coords);
|
||||
cost += GetSurfaceHeightChangeCost(surfaceElement);
|
||||
SetSurfaceHeight(surfaceElement);
|
||||
|
||||
LocationXYZ16 coord;
|
||||
coord.x = _coords.x + 16;
|
||||
coord.y = _coords.y + 16;
|
||||
coord.z = surfaceHeight;
|
||||
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
|
||||
|
||||
auto res = std::make_unique<GameActionResult>();
|
||||
res->Position = { _coords.x + 16, _coords.y + 16, surfaceHeight };
|
||||
res->Cost = cost;
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
rct_string_id CheckParameters() const
|
||||
{
|
||||
if (_coords.x > gMapSizeMaxXY || _coords.y > gMapSizeMaxXY)
|
||||
{
|
||||
return STR_OFF_EDGE_OF_MAP;
|
||||
}
|
||||
|
||||
if (_height < MINIMUM_LAND_HEIGHT)
|
||||
{
|
||||
return STR_TOO_LOW;
|
||||
}
|
||||
|
||||
// Divide by 2 and subtract 7 to get the in-game units.
|
||||
if (_height > MAXIMUM_LAND_HEIGHT)
|
||||
{
|
||||
return STR_TOO_HIGH;
|
||||
}
|
||||
else if (_height > MAXIMUM_LAND_HEIGHT - 2 && (_style & TILE_ELEMENT_SURFACE_SLOPE_MASK) != 0)
|
||||
{
|
||||
return STR_TOO_HIGH;
|
||||
}
|
||||
|
||||
if (_height == MAXIMUM_LAND_HEIGHT - 2 && (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG))
|
||||
{
|
||||
return STR_TOO_HIGH;
|
||||
}
|
||||
|
||||
return STR_NONE;
|
||||
}
|
||||
|
||||
TileElement* CheckTallTreeObstructions() const
|
||||
{
|
||||
TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32);
|
||||
do
|
||||
{
|
||||
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
|
||||
continue;
|
||||
if (_height > tileElement->clearance_height)
|
||||
continue;
|
||||
if (_height + 4 < tileElement->base_height)
|
||||
continue;
|
||||
rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
|
||||
if (sceneryEntry->small_scenery.height > 64)
|
||||
{
|
||||
return tileElement;
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
money32 GetSmallSceneryRemovalCost() const
|
||||
{
|
||||
money32 cost{ 0 };
|
||||
TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32);
|
||||
do
|
||||
{
|
||||
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
|
||||
continue;
|
||||
if (_height > tileElement->clearance_height)
|
||||
continue;
|
||||
if (_height + 4 < tileElement->base_height)
|
||||
continue;
|
||||
rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
|
||||
cost += MONEY(sceneryEntry->small_scenery.removal_price, 0);
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
return cost;
|
||||
}
|
||||
|
||||
void SmallSceneryRemoval() const
|
||||
{
|
||||
TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32);
|
||||
do
|
||||
{
|
||||
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
|
||||
continue;
|
||||
if (_height > tileElement->clearance_height)
|
||||
continue;
|
||||
if (_height + 4 < tileElement->base_height)
|
||||
continue;
|
||||
tile_element_remove(tileElement--);
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
}
|
||||
|
||||
rct_string_id CheckRideSupports() const
|
||||
{
|
||||
TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32);
|
||||
do
|
||||
{
|
||||
if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK)
|
||||
{
|
||||
ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex();
|
||||
Ride* ride = get_ride(rideIndex);
|
||||
if (ride != nullptr)
|
||||
{
|
||||
rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride);
|
||||
if (rideEntry != nullptr)
|
||||
{
|
||||
int32_t maxHeight = rideEntry->max_height;
|
||||
if (maxHeight == 0)
|
||||
{
|
||||
maxHeight = RideData5[get_ride(rideIndex)->type].max_height;
|
||||
}
|
||||
int32_t zDelta = tileElement->clearance_height - _height;
|
||||
if (zDelta >= 0 && zDelta / 2 > maxHeight)
|
||||
{
|
||||
return STR_SUPPORTS_CANT_BE_EXTENDED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
return STR_NONE;
|
||||
}
|
||||
|
||||
TileElement* CheckFloatingStructures(TileElement * surfaceElement, uint8_t zCorner) const
|
||||
{
|
||||
if (surfaceElement->AsSurface()->HasTrackThatNeedsWater())
|
||||
{
|
||||
uint32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight();
|
||||
if (waterHeight != 0)
|
||||
{
|
||||
if (_style & TILE_ELEMENT_SURFACE_SLOPE_MASK)
|
||||
{
|
||||
zCorner += 2;
|
||||
if (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG)
|
||||
{
|
||||
zCorner += 2;
|
||||
}
|
||||
}
|
||||
if (zCorner > waterHeight * 2 - 2)
|
||||
{
|
||||
return ++surfaceElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TileElement* CheckUnremovableObstructions(TileElement * surfaceElement, uint8_t zCorner) const
|
||||
{
|
||||
TileElement* tileElement = map_get_first_element_at(_coords.x / 32, _coords.y / 32);
|
||||
do
|
||||
{
|
||||
int32_t elementType = tileElement->GetType();
|
||||
|
||||
// Wall's and Small Scenery are removed and therefore do not need checked
|
||||
if (elementType == TILE_ELEMENT_TYPE_WALL)
|
||||
continue;
|
||||
if (elementType == TILE_ELEMENT_TYPE_SMALL_SCENERY)
|
||||
continue;
|
||||
if (tileElement->IsGhost())
|
||||
continue;
|
||||
if (tileElement == surfaceElement)
|
||||
continue;
|
||||
if (tileElement > surfaceElement)
|
||||
{
|
||||
if (zCorner > tileElement->base_height)
|
||||
{
|
||||
return tileElement;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (_height < tileElement->clearance_height)
|
||||
{
|
||||
return tileElement;
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
money32 GetSurfaceHeightChangeCost(TileElement * surfaceElement) const
|
||||
{
|
||||
money32 cost{ 0 };
|
||||
for (int32_t i = 0; i < 4; i += 1)
|
||||
{
|
||||
int32_t cornerHeight = tile_element_get_corner_height(surfaceElement, i);
|
||||
cornerHeight -= map_get_corner_height(_height, _style & TILE_ELEMENT_SURFACE_SLOPE_MASK, i);
|
||||
cost += MONEY(abs(cornerHeight) * 5 / 2, 0);
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
void SetSurfaceHeight(TileElement * surfaceElement) const
|
||||
{
|
||||
surfaceElement->base_height = _height;
|
||||
surfaceElement->clearance_height = _height;
|
||||
surfaceElement->AsSurface()->SetSlope(_style);
|
||||
int32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight();
|
||||
if (waterHeight != 0 && waterHeight <= _height / 2)
|
||||
{
|
||||
surfaceElement->AsSurface()->SetWaterHeight(0);
|
||||
}
|
||||
|
||||
map_invalidate_tile_full(_coords.x, _coords.y);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x00663CB9
|
||||
*/
|
||||
static int32_t map_set_land_height_clear_func(
|
||||
TileElement * *tile_element, [[maybe_unused]] int32_t x, [[maybe_unused]] int32_t y, [[maybe_unused]] uint8_t flags,
|
||||
[[maybe_unused]] money32 * price)
|
||||
{
|
||||
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SURFACE)
|
||||
return 0;
|
||||
|
||||
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SMALL_SCENERY)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
};
|
|
@ -321,3 +321,25 @@ template<> struct DataSerializerTraits<MapRange>
|
|||
stream->Write(coords, strlen(coords));
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits<CoordsXY>
|
||||
{
|
||||
static void encode(IStream* stream, const CoordsXY& coords)
|
||||
{
|
||||
stream->WriteValue(ByteSwapBE(coords.x));
|
||||
stream->WriteValue(ByteSwapBE(coords.y));
|
||||
}
|
||||
static void decode(IStream* stream, CoordsXY& coords)
|
||||
{
|
||||
auto x = ByteSwapBE(stream->ReadValue<int16_t>());
|
||||
auto y = ByteSwapBE(stream->ReadValue<int16_t>());
|
||||
coords = CoordsXY(x, y);
|
||||
}
|
||||
static void log(IStream* stream, const CoordsXY& coords)
|
||||
{
|
||||
char msg[128] = {};
|
||||
snprintf(msg, sizeof(msg), "CoordsXY(x = %d, y = %d)", coords.x, coords.y);
|
||||
|
||||
stream->Write(msg, strlen(msg));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -30,7 +30,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 "31"
|
||||
#define NETWORK_STREAM_VERSION "32"
|
||||
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
||||
|
||||
static rct_peep* _pickup_peep = nullptr;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "../Input.h"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../actions/FootpathRemoveAction.hpp"
|
||||
#include "../actions/LandSetHeightAction.hpp"
|
||||
#include "../actions/LargeSceneryRemoveAction.hpp"
|
||||
#include "../actions/SmallSceneryRemoveAction.hpp"
|
||||
#include "../actions/WallRemoveAction.hpp"
|
||||
|
@ -1064,24 +1065,7 @@ static constexpr const uint8_t tile_element_lower_styles[9][32] = {
|
|||
0x29, 0x29, 0x00, 0x01, 0x00, 0x01, 0x00, 0x07, 0x29, 0x29, 0x08, 0x09, 0x08, 0x09, 0x0E, 0x09 }, // MAP_SELECT_TYPE_EDGE_3
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x00663CB9
|
||||
*/
|
||||
static int32_t map_set_land_height_clear_func(
|
||||
TileElement** tile_element, [[maybe_unused]] int32_t x, [[maybe_unused]] int32_t y, [[maybe_unused]] uint8_t flags,
|
||||
[[maybe_unused]] money32* price)
|
||||
{
|
||||
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SURFACE)
|
||||
return 0;
|
||||
|
||||
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SMALL_SCENERY)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int32_t map_get_corner_height(int32_t z, int32_t slope, int32_t direction)
|
||||
int32_t map_get_corner_height(int32_t z, int32_t slope, int32_t direction)
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
|
@ -1129,248 +1113,13 @@ static int32_t map_get_corner_height(int32_t z, int32_t slope, int32_t direction
|
|||
return z;
|
||||
}
|
||||
|
||||
static int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t direction)
|
||||
int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t direction)
|
||||
{
|
||||
int32_t z = tileElement->base_height;
|
||||
int32_t slope = tileElement->AsSurface()->GetSlope();
|
||||
return map_get_corner_height(z, slope, direction);
|
||||
}
|
||||
|
||||
static money32 map_set_land_height(int32_t flags, int32_t x, int32_t y, int32_t height, int32_t style)
|
||||
{
|
||||
TileElement* tileElement;
|
||||
|
||||
if (game_is_paused() && !gCheatsBuildInPauseMode)
|
||||
{
|
||||
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
|
||||
{
|
||||
if (gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES)
|
||||
{
|
||||
gGameCommandErrorText = STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
if (x > gMapSizeMaxXY || y > gMapSizeMaxXY)
|
||||
{
|
||||
gGameCommandErrorText = STR_OFF_EDGE_OF_MAP;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (height < MINIMUM_LAND_HEIGHT)
|
||||
{
|
||||
gGameCommandErrorText = STR_TOO_LOW;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
// Divide by 2 and subtract 7 to get the in-game units.
|
||||
if (height > MAXIMUM_LAND_HEIGHT)
|
||||
{
|
||||
gGameCommandErrorText = STR_TOO_HIGH;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
else if (height > MAXIMUM_LAND_HEIGHT - 2 && (style & 0x1F) != 0)
|
||||
{
|
||||
gGameCommandErrorText = STR_TOO_HIGH;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (height == MAXIMUM_LAND_HEIGHT - 2 && (style & 0x10))
|
||||
{
|
||||
gGameCommandErrorText = STR_TOO_HIGH;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
|
||||
{
|
||||
if (!map_is_location_in_park({ x, y }))
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
money32 cost = MONEY(0, 0);
|
||||
if (flags & GAME_COMMAND_FLAG_APPLY)
|
||||
{
|
||||
footpath_remove_litter(x, y, tile_element_height(x, y));
|
||||
if (!gCheatsDisableClearanceChecks)
|
||||
wall_remove_at(x, y, height * 8 - 16, height * 8 + 32);
|
||||
}
|
||||
|
||||
if (!gCheatsDisableClearanceChecks)
|
||||
{
|
||||
// Check for obstructing scenery
|
||||
tileElement = map_get_first_element_at(x / 32, y / 32);
|
||||
do
|
||||
{
|
||||
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
|
||||
continue;
|
||||
if (height > tileElement->clearance_height)
|
||||
continue;
|
||||
if (height + 4 < tileElement->base_height)
|
||||
continue;
|
||||
rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
|
||||
if (sceneryEntry->small_scenery.height > 64 && gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
cost += MONEY(sceneryEntry->small_scenery.removal_price, 0);
|
||||
if (flags & GAME_COMMAND_FLAG_APPLY)
|
||||
tile_element_remove(tileElement--);
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
}
|
||||
|
||||
// Check for ride support limits
|
||||
if (!gCheatsDisableSupportLimits)
|
||||
{
|
||||
tileElement = map_get_first_element_at(x / 32, y / 32);
|
||||
do
|
||||
{
|
||||
if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK)
|
||||
{
|
||||
ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex();
|
||||
Ride* ride = get_ride(rideIndex);
|
||||
if (ride != nullptr)
|
||||
{
|
||||
rct_ride_entry* rideEntry = get_ride_entry_by_ride(ride);
|
||||
if (rideEntry != nullptr)
|
||||
{
|
||||
int32_t maxHeight = rideEntry->max_height;
|
||||
if (maxHeight == 0)
|
||||
{
|
||||
maxHeight = RideData5[get_ride(rideIndex)->type].max_height;
|
||||
}
|
||||
int32_t zDelta = tileElement->clearance_height - height;
|
||||
if (zDelta >= 0 && zDelta / 2 > maxHeight)
|
||||
{
|
||||
gGameCommandErrorText = STR_SUPPORTS_CANT_BE_EXTENDED;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
}
|
||||
|
||||
uint8_t zCorner = height; // z position of highest corner of tile
|
||||
TileElement* surfaceElement = map_get_surface_element_at({ x, y });
|
||||
if (surfaceElement->AsSurface()->HasTrackThatNeedsWater())
|
||||
{
|
||||
uint32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight();
|
||||
if (waterHeight != 0)
|
||||
{
|
||||
if (style & 0x1F)
|
||||
{
|
||||
zCorner += 2;
|
||||
if (style & 0x10)
|
||||
{
|
||||
zCorner += 2;
|
||||
}
|
||||
}
|
||||
if (zCorner > waterHeight * 2 - 2)
|
||||
{
|
||||
surfaceElement++;
|
||||
map_obstruction_set_error_text(surfaceElement);
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zCorner = height;
|
||||
if (style & 0xF)
|
||||
{
|
||||
zCorner += 2;
|
||||
if (style & 0x10)
|
||||
{
|
||||
zCorner += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gCheatsDisableClearanceChecks)
|
||||
{
|
||||
if (!map_can_construct_with_clear_at(
|
||||
x, y, height, zCorner, &map_set_land_height_clear_func, 0xF, 0, nullptr, CREATE_CROSSING_MODE_NONE))
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gCheatsDisableClearanceChecks)
|
||||
{
|
||||
tileElement = map_get_first_element_at(x / 32, y / 32);
|
||||
do
|
||||
{
|
||||
int32_t elementType = tileElement->GetType();
|
||||
|
||||
if (elementType == TILE_ELEMENT_TYPE_WALL)
|
||||
continue;
|
||||
if (elementType == TILE_ELEMENT_TYPE_SMALL_SCENERY)
|
||||
continue;
|
||||
if (tileElement->flags & 0x10)
|
||||
continue;
|
||||
if (tileElement == surfaceElement)
|
||||
continue;
|
||||
if (tileElement > surfaceElement)
|
||||
{
|
||||
if (zCorner > tileElement->base_height)
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (height < tileElement->clearance_height)
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < 4; i += 1)
|
||||
{
|
||||
int32_t cornerHeight = tile_element_get_corner_height(surfaceElement, i);
|
||||
cornerHeight -= map_get_corner_height(height, style & TILE_ELEMENT_SURFACE_SLOPE_MASK, i);
|
||||
cost += MONEY(abs(cornerHeight) * 5 / 2, 0);
|
||||
}
|
||||
|
||||
if (flags & GAME_COMMAND_FLAG_APPLY)
|
||||
{
|
||||
if (gGameCommandNestLevel == 1)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
surfaceElement = map_get_surface_element_at({ x, y });
|
||||
surfaceElement->base_height = height;
|
||||
surfaceElement->clearance_height = height;
|
||||
surfaceElement->AsSurface()->SetSlope(style);
|
||||
int32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight();
|
||||
if (waterHeight != 0 && waterHeight <= height / 2)
|
||||
surfaceElement->AsSurface()->SetWaterHeight(0);
|
||||
map_invalidate_tile_full(x, y);
|
||||
}
|
||||
if (gParkFlags & PARK_FLAGS_NO_MONEY)
|
||||
return 0;
|
||||
return cost;
|
||||
}
|
||||
|
||||
void game_command_set_land_height(
|
||||
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 = map_set_land_height(*ebx & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*edx >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -1508,12 +1257,14 @@ static money32 raise_land(
|
|||
height += 2;
|
||||
|
||||
slope &= TILE_ELEMENT_SURFACE_SLOPE_MASK;
|
||||
money32 tileCost = map_set_land_height(flags, x_coord, y_coord, height, slope);
|
||||
if (tileCost == MONEY32_UNDEFINED)
|
||||
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 += tileCost;
|
||||
cost += res->Cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1575,12 +1326,15 @@ static money32 lower_land(
|
|||
height -= 2;
|
||||
|
||||
newSlope &= TILE_ELEMENT_SURFACE_SLOPE_MASK;
|
||||
money32 tileCost = map_set_land_height(flags, x_coord, y_coord, height, newSlope);
|
||||
if (tileCost == MONEY32_UNDEFINED)
|
||||
|
||||
auto landSetHeightAction = LandSetHeightAction({ x_coord, y_coord }, height, newSlope);
|
||||
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? landSetHeightAction.Execute() : landSetHeightAction.Query();
|
||||
|
||||
if (res->Error != GA_ERROR::OK)
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
cost += tileCost;
|
||||
cost += res->Cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1824,7 +1578,18 @@ static money32 smooth_land_tile(
|
|||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
return game_do_command(x, flags, y, targetBaseZ | (slope << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0);
|
||||
|
||||
auto landSetHeightAction = LandSetHeightAction({ x, y }, targetBaseZ, slope);
|
||||
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? landSetHeightAction.Execute() : landSetHeightAction.Query();
|
||||
|
||||
if (res->Error == GA_ERROR::OK)
|
||||
{
|
||||
return res->Cost;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
static money32 smooth_land_row_by_edge(
|
||||
|
@ -1835,7 +1600,6 @@ static money32 smooth_land_row_by_edge(
|
|||
int32_t landChangePerTile = raiseLand ? -2 : 2;
|
||||
TileElement *tileElement, *nextTileElement;
|
||||
money32 totalCost = 0;
|
||||
money32 result;
|
||||
|
||||
// check if we need to start at all
|
||||
if (!map_is_location_valid({ x, y }) || !map_is_location_valid({ x + stepX, y + stepY }))
|
||||
|
@ -1960,10 +1724,12 @@ static money32 smooth_land_row_by_edge(
|
|||
}
|
||||
}
|
||||
}
|
||||
result = game_do_command(x, flags, y, targetBaseZ | (slope << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0);
|
||||
if (result != MONEY32_UNDEFINED)
|
||||
auto landSetHeightAction = LandSetHeightAction({ x, y }, targetBaseZ, slope);
|
||||
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? landSetHeightAction.Execute() : landSetHeightAction.Query();
|
||||
|
||||
if (res->Error == GA_ERROR::OK)
|
||||
{
|
||||
totalCost += result;
|
||||
totalCost += res->Cost;
|
||||
}
|
||||
}
|
||||
return totalCost;
|
||||
|
|
|
@ -183,8 +183,6 @@ 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_height(
|
||||
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_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(
|
||||
|
@ -258,6 +256,8 @@ void map_invalidate_region(const LocationXY16& mins, const LocationXY16& maxs);
|
|||
|
||||
int32_t map_get_tile_side(int32_t mapX, int32_t mapY);
|
||||
int32_t map_get_tile_quadrant(int32_t mapX, int32_t mapY);
|
||||
int32_t map_get_corner_height(int32_t z, int32_t slope, int32_t direction);
|
||||
int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t direction);
|
||||
|
||||
void map_clear_all_elements();
|
||||
|
||||
|
|
Loading…
Reference in New Issue