1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-31 10:45:16 +01:00

Finish land and water game commands

This commit is contained in:
Aaron van Geffen
2025-08-26 16:04:58 +02:00
parent 238b397968
commit 1a37409390
10 changed files with 631 additions and 625 deletions

View File

@@ -521,7 +521,7 @@ namespace OpenRCT2::Ui::Windows
case WIDX_BACKGROUND:
if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)
{
auto surfaceSetStyleAction = SurfaceSetStyleAction(
auto surfaceSetStyleAction = GameActions::SurfaceSetStyleAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
gLandToolTerrainSurface, gLandToolTerrainEdge);
@@ -548,7 +548,7 @@ namespace OpenRCT2::Ui::Windows
{
if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)
{
auto surfaceSetStyleAction = SurfaceSetStyleAction(
auto surfaceSetStyleAction = GameActions::SurfaceSetStyleAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
gLandToolTerrainSurface, gLandToolTerrainEdge);

View File

@@ -260,7 +260,7 @@ namespace OpenRCT2::Ui::Windows
{
gInputDragLast.y += dx;
auto waterRaiseAction = WaterRaiseAction(
auto waterRaiseAction = GameActions::WaterRaiseAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
GameActions::Execute(&waterRaiseAction);
@@ -276,7 +276,7 @@ namespace OpenRCT2::Ui::Windows
{
gInputDragLast.y += dx;
auto waterLowerAction = WaterLowerAction(
auto waterLowerAction = GameActions::WaterLowerAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
GameActions::Execute(&waterLowerAction);
_waterToolRaiseCost = kMoney64Undefined;
@@ -301,9 +301,9 @@ namespace OpenRCT2::Ui::Windows
if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE))
return;
auto waterLowerAction = WaterLowerAction(
auto waterLowerAction = GameActions::WaterLowerAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
auto waterRaiseAction = WaterRaiseAction(
auto waterRaiseAction = GameActions::WaterRaiseAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
auto res = GameActions::Query(&waterLowerAction);
@@ -392,9 +392,9 @@ namespace OpenRCT2::Ui::Windows
if (!state_changed)
return;
auto waterLowerAction = WaterLowerAction(
auto waterLowerAction = GameActions::WaterLowerAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
auto waterRaiseAction = WaterRaiseAction(
auto waterRaiseAction = GameActions::WaterRaiseAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y });
auto res = GameActions::Query(&waterLowerAction);

View File

@@ -21,210 +21,208 @@
#include "../world/Park.h"
#include "../world/tile_element/SurfaceElement.h"
using namespace OpenRCT2;
SurfaceSetStyleAction::SurfaceSetStyleAction(MapRange range, ObjectEntryIndex surfaceStyle, ObjectEntryIndex edgeStyle)
: _range(range)
, _surfaceStyle(surfaceStyle)
, _edgeStyle(edgeStyle)
namespace OpenRCT2::GameActions
{
}
void SurfaceSetStyleAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_range);
visitor.Visit("surfaceStyle", _surfaceStyle);
visitor.Visit("edgeStyle", _edgeStyle);
}
void SurfaceSetStyleAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_surfaceStyle) << DS_TAG(_edgeStyle);
}
GameActions::Result SurfaceSetStyleAction::Query() const
{
auto res = GameActions::Result();
res.ErrorTitle = STR_CANT_CHANGE_LAND_TYPE;
res.Expenditure = ExpenditureType::landscaping;
auto validRange = ClampRangeWithinMap(_range.Normalise());
auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
if (_surfaceStyle != kObjectEntryIndexNull)
SurfaceSetStyleAction::SurfaceSetStyleAction(MapRange range, ObjectEntryIndex surfaceStyle, ObjectEntryIndex edgeStyle)
: _range(range)
, _surfaceStyle(surfaceStyle)
, _edgeStyle(edgeStyle)
{
const auto surfaceObj = static_cast<TerrainSurfaceObject*>(
objManager.GetLoadedObject<TerrainSurfaceObject>(_surfaceStyle));
if (surfaceObj == nullptr)
{
LOG_ERROR("Invalid surface style %u", _surfaceStyle);
return GameActions::Result(
GameActions::Status::InvalidParameters, STR_CANT_CHANGE_LAND_TYPE, STR_UNKNOWN_OBJECT_TYPE);
}
}
if (_edgeStyle != kObjectEntryIndexNull)
void SurfaceSetStyleAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
const auto edgeObj = objManager.GetLoadedObject<TerrainEdgeObject>(_edgeStyle);
if (edgeObj == nullptr)
{
LOG_ERROR("Invalid edge style %u", _edgeStyle);
return GameActions::Result(
GameActions::Status::InvalidParameters, STR_CANT_CHANGE_LAND_TYPE, STR_UNKNOWN_OBJECT_TYPE);
}
visitor.Visit(_range);
visitor.Visit("surfaceStyle", _surfaceStyle);
visitor.Visit("edgeStyle", _edgeStyle);
}
auto xMid = (validRange.GetLeft() + validRange.GetRight()) / 2 + 16;
auto yMid = (validRange.GetTop() + validRange.GetBottom()) / 2 + 16;
auto heightMid = TileElementHeight({ xMid, yMid });
res.Position.x = xMid;
res.Position.y = yMid;
res.Position.z = heightMid;
auto& gameState = getGameState();
// Do nothing if not in editor, sandbox mode or landscaping is forbidden
if (gLegacyScene != LegacyScene::scenarioEditor && !gameState.cheats.sandboxMode
&& (gameState.park.flags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES))
void SurfaceSetStyleAction::Serialise(DataSerialiser& stream)
{
return GameActions::Result(
GameActions::Status::Disallowed, STR_CANT_CHANGE_LAND_TYPE, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY);
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_surfaceStyle) << DS_TAG(_edgeStyle);
}
money64 surfaceCost = 0;
money64 edgeCost = 0;
for (CoordsXY coords = { validRange.GetLeft(), validRange.GetTop() }; coords.x <= validRange.GetRight();
coords.x += kCoordsXYStep)
Result SurfaceSetStyleAction::Query() const
{
for (coords.y = validRange.GetTop(); coords.y <= validRange.GetBottom(); coords.y += kCoordsXYStep)
{
if (!LocationValid(coords))
continue;
auto res = Result();
res.ErrorTitle = STR_CANT_CHANGE_LAND_TYPE;
res.Expenditure = ExpenditureType::landscaping;
if (gLegacyScene != LegacyScene::scenarioEditor && !gameState.cheats.sandboxMode)
auto validRange = ClampRangeWithinMap(_range.Normalise());
auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
if (_surfaceStyle != kObjectEntryIndexNull)
{
const auto surfaceObj = static_cast<TerrainSurfaceObject*>(
objManager.GetLoadedObject<TerrainSurfaceObject>(_surfaceStyle));
if (surfaceObj == nullptr)
{
if (!MapIsLocationInPark(coords))
LOG_ERROR("Invalid surface style %u", _surfaceStyle);
return Result(Status::InvalidParameters, STR_CANT_CHANGE_LAND_TYPE, STR_UNKNOWN_OBJECT_TYPE);
}
}
if (_edgeStyle != kObjectEntryIndexNull)
{
const auto edgeObj = objManager.GetLoadedObject<TerrainEdgeObject>(_edgeStyle);
if (edgeObj == nullptr)
{
LOG_ERROR("Invalid edge style %u", _edgeStyle);
return Result(Status::InvalidParameters, STR_CANT_CHANGE_LAND_TYPE, STR_UNKNOWN_OBJECT_TYPE);
}
}
auto xMid = (validRange.GetLeft() + validRange.GetRight()) / 2 + 16;
auto yMid = (validRange.GetTop() + validRange.GetBottom()) / 2 + 16;
auto heightMid = TileElementHeight({ xMid, yMid });
res.Position.x = xMid;
res.Position.y = yMid;
res.Position.z = heightMid;
auto& gameState = getGameState();
// Do nothing if not in editor, sandbox mode or landscaping is forbidden
if (gLegacyScene != LegacyScene::scenarioEditor && !gameState.cheats.sandboxMode
&& (gameState.park.flags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES))
{
return Result(Status::Disallowed, STR_CANT_CHANGE_LAND_TYPE, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY);
}
money64 surfaceCost = 0;
money64 edgeCost = 0;
for (CoordsXY coords = { validRange.GetLeft(), validRange.GetTop() }; coords.x <= validRange.GetRight();
coords.x += kCoordsXYStep)
{
for (coords.y = validRange.GetTop(); coords.y <= validRange.GetBottom(); coords.y += kCoordsXYStep)
{
if (!LocationValid(coords))
continue;
}
auto surfaceElement = MapGetSurfaceElementAt(coords);
if (surfaceElement == nullptr)
{
continue;
}
if (_surfaceStyle != kObjectEntryIndexNull)
{
uint8_t curSurfaceStyle = surfaceElement->GetSurfaceObjectIndex();
if (_surfaceStyle != curSurfaceStyle)
if (gLegacyScene != LegacyScene::scenarioEditor && !gameState.cheats.sandboxMode)
{
const auto* surfaceObject = objManager.GetLoadedObject<TerrainSurfaceObject>(_surfaceStyle);
if (surfaceObject != nullptr)
if (!MapIsLocationInPark(coords))
continue;
}
auto surfaceElement = MapGetSurfaceElementAt(coords);
if (surfaceElement == nullptr)
{
continue;
}
if (_surfaceStyle != kObjectEntryIndexNull)
{
uint8_t curSurfaceStyle = surfaceElement->GetSurfaceObjectIndex();
if (_surfaceStyle != curSurfaceStyle)
{
surfaceCost += surfaceObject->Price;
const auto* surfaceObject = objManager.GetLoadedObject<TerrainSurfaceObject>(_surfaceStyle);
if (surfaceObject != nullptr)
{
surfaceCost += surfaceObject->Price;
}
}
}
if (_edgeStyle != kObjectEntryIndexNull)
{
uint8_t curEdgeStyle = surfaceElement->GetEdgeObjectIndex();
if (_edgeStyle != curEdgeStyle)
{
edgeCost += 100;
}
}
}
if (_edgeStyle != kObjectEntryIndexNull)
{
uint8_t curEdgeStyle = surfaceElement->GetEdgeObjectIndex();
if (_edgeStyle != curEdgeStyle)
{
edgeCost += 100;
}
}
}
res.Cost = surfaceCost + edgeCost;
return res;
}
res.Cost = surfaceCost + edgeCost;
return res;
}
GameActions::Result SurfaceSetStyleAction::Execute() const
{
auto res = GameActions::Result();
res.ErrorTitle = STR_CANT_CHANGE_LAND_TYPE;
res.Expenditure = ExpenditureType::landscaping;
auto validRange = ClampRangeWithinMap(_range.Normalise());
auto xMid = (validRange.GetLeft() + validRange.GetRight()) / 2 + 16;
auto yMid = (validRange.GetTop() + validRange.GetBottom()) / 2 + 16;
auto heightMid = TileElementHeight({ xMid, yMid });
res.Position.x = xMid;
res.Position.y = yMid;
res.Position.z = heightMid;
money64 surfaceCost = 0;
money64 edgeCost = 0;
for (CoordsXY coords = { validRange.GetLeft(), validRange.GetTop() }; coords.x <= validRange.GetRight();
coords.x += kCoordsXYStep)
Result SurfaceSetStyleAction::Execute() const
{
for (coords.y = validRange.GetTop(); coords.y <= validRange.GetBottom(); coords.y += kCoordsXYStep)
auto res = Result();
res.ErrorTitle = STR_CANT_CHANGE_LAND_TYPE;
res.Expenditure = ExpenditureType::landscaping;
auto validRange = ClampRangeWithinMap(_range.Normalise());
auto xMid = (validRange.GetLeft() + validRange.GetRight()) / 2 + 16;
auto yMid = (validRange.GetTop() + validRange.GetBottom()) / 2 + 16;
auto heightMid = TileElementHeight({ xMid, yMid });
res.Position.x = xMid;
res.Position.y = yMid;
res.Position.z = heightMid;
money64 surfaceCost = 0;
money64 edgeCost = 0;
for (CoordsXY coords = { validRange.GetLeft(), validRange.GetTop() }; coords.x <= validRange.GetRight();
coords.x += kCoordsXYStep)
{
if (!LocationValid(coords))
continue;
if (gLegacyScene != LegacyScene::scenarioEditor && !getGameState().cheats.sandboxMode)
for (coords.y = validRange.GetTop(); coords.y <= validRange.GetBottom(); coords.y += kCoordsXYStep)
{
if (!MapIsLocationInPark(coords))
if (!LocationValid(coords))
continue;
}
auto surfaceElement = MapGetSurfaceElementAt(coords);
if (surfaceElement == nullptr)
{
continue;
}
if (_surfaceStyle != kObjectEntryIndexNull)
{
uint8_t curSurfaceStyle = surfaceElement->GetSurfaceObjectIndex();
if (_surfaceStyle != curSurfaceStyle)
if (gLegacyScene != LegacyScene::scenarioEditor && !getGameState().cheats.sandboxMode)
{
auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
const auto* surfaceObject = objManager.GetLoadedObject<TerrainSurfaceObject>(_surfaceStyle);
if (surfaceObject != nullptr)
if (!MapIsLocationInPark(coords))
continue;
}
auto surfaceElement = MapGetSurfaceElementAt(coords);
if (surfaceElement == nullptr)
{
continue;
}
if (_surfaceStyle != kObjectEntryIndexNull)
{
uint8_t curSurfaceStyle = surfaceElement->GetSurfaceObjectIndex();
if (_surfaceStyle != curSurfaceStyle)
{
surfaceCost += surfaceObject->Price;
auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
const auto* surfaceObject = objManager.GetLoadedObject<TerrainSurfaceObject>(_surfaceStyle);
if (surfaceObject != nullptr)
{
surfaceCost += surfaceObject->Price;
surfaceElement->SetSurfaceObjectIndex(_surfaceStyle);
surfaceElement->SetSurfaceObjectIndex(_surfaceStyle);
MapInvalidateTileFull(coords);
FootpathRemoveLitter({ coords, TileElementHeight(coords) });
}
}
}
if (_edgeStyle != kObjectEntryIndexNull)
{
uint8_t curEdgeStyle = surfaceElement->GetEdgeObjectIndex();
if (_edgeStyle != curEdgeStyle)
{
edgeCost += 100;
surfaceElement->SetEdgeObjectIndex(_edgeStyle);
MapInvalidateTileFull(coords);
FootpathRemoveLitter({ coords, TileElementHeight(coords) });
}
}
}
if (_edgeStyle != kObjectEntryIndexNull)
{
uint8_t curEdgeStyle = surfaceElement->GetEdgeObjectIndex();
if (_edgeStyle != curEdgeStyle)
if (surfaceElement->CanGrassGrow() && (surfaceElement->GetGrassLength() & 7) != GRASS_LENGTH_CLEAR_0)
{
edgeCost += 100;
surfaceElement->SetEdgeObjectIndex(_edgeStyle);
surfaceElement->SetGrassLength(GRASS_LENGTH_CLEAR_0);
MapInvalidateTileFull(coords);
}
}
if (surfaceElement->CanGrassGrow() && (surfaceElement->GetGrassLength() & 7) != GRASS_LENGTH_CLEAR_0)
{
surfaceElement->SetGrassLength(GRASS_LENGTH_CLEAR_0);
MapInvalidateTileFull(coords);
}
}
}
res.Cost = surfaceCost + edgeCost;
res.Cost = surfaceCost + edgeCost;
return res;
}
return res;
}
} // namespace OpenRCT2::GameActions

View File

@@ -11,20 +11,23 @@
#include "GameAction.h"
class SurfaceSetStyleAction final : public GameActionBase<GameCommand::ChangeSurfaceStyle>
namespace OpenRCT2::GameActions
{
private:
MapRange _range;
OpenRCT2::ObjectEntryIndex _surfaceStyle{};
OpenRCT2::ObjectEntryIndex _edgeStyle{};
class SurfaceSetStyleAction final : public GameActionBase<GameCommand::ChangeSurfaceStyle>
{
private:
MapRange _range;
ObjectEntryIndex _surfaceStyle{};
ObjectEntryIndex _edgeStyle{};
public:
SurfaceSetStyleAction() = default;
SurfaceSetStyleAction(MapRange range, OpenRCT2::ObjectEntryIndex surfaceStyle, OpenRCT2::ObjectEntryIndex edgeStyle);
public:
SurfaceSetStyleAction() = default;
SurfaceSetStyleAction(MapRange range, ObjectEntryIndex surfaceStyle, ObjectEntryIndex edgeStyle);
void AcceptParameters(GameActionParameterVisitor& visitor) override;
void AcceptParameters(GameActionParameterVisitor& visitor) override;
void Serialise(DataSerialiser& stream) override;
OpenRCT2::GameActions::Result Query() const override;
OpenRCT2::GameActions::Result Execute() const override;
};
void Serialise(DataSerialiser& stream) override;
Result Query() const override;
Result Execute() const override;
};
} // namespace OpenRCT2::GameActions

View File

@@ -16,151 +16,150 @@
#include "../world/tile_element/SurfaceElement.h"
#include "WaterSetHeightAction.h"
using namespace OpenRCT2;
WaterLowerAction::WaterLowerAction(MapRange range)
: _range(range)
namespace OpenRCT2::GameActions
{
}
void WaterLowerAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_range);
}
uint16_t WaterLowerAction::GetActionFlags() const
{
return GameAction::GetActionFlags();
}
void WaterLowerAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_range);
}
GameActions::Result WaterLowerAction::Query() const
{
return QueryExecute(false);
}
GameActions::Result WaterLowerAction::Execute() const
{
return QueryExecute(true);
}
GameActions::Result WaterLowerAction::QueryExecute(bool isExecuting) const
{
auto res = GameActions::Result();
auto validRange = ClampRangeWithinMap(_range);
res.Position.x = ((validRange.GetLeft() + validRange.GetRight()) / 2) + 16;
res.Position.y = ((validRange.GetTop() + validRange.GetBottom()) / 2) + 16;
int16_t z = TileElementHeight(res.Position);
int16_t waterHeight = TileElementWaterHeight(res.Position);
if (waterHeight != 0)
WaterLowerAction::WaterLowerAction(MapRange range)
: _range(range)
{
z = waterHeight;
}
res.Position.z = z;
res.Expenditure = ExpenditureType::landscaping;
uint8_t minHeight = GetLowestHeight(validRange);
bool hasChanged = false;
bool withinOwnership = false;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += kCoordsXYStep)
void WaterLowerAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += kCoordsXYStep)
visitor.Visit(_range);
}
uint16_t WaterLowerAction::GetActionFlags() const
{
return GameAction::GetActionFlags();
}
void WaterLowerAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_range);
}
Result WaterLowerAction::Query() const
{
return QueryExecute(false);
}
Result WaterLowerAction::Execute() const
{
return QueryExecute(true);
}
Result WaterLowerAction::QueryExecute(bool isExecuting) const
{
auto res = Result();
auto validRange = ClampRangeWithinMap(_range);
res.Position.x = ((validRange.GetLeft() + validRange.GetRight()) / 2) + 16;
res.Position.y = ((validRange.GetTop() + validRange.GetBottom()) / 2) + 16;
int16_t z = TileElementHeight(res.Position);
int16_t waterHeight = TileElementWaterHeight(res.Position);
if (waterHeight != 0)
{
if (!LocationValid({ x, y }))
continue;
z = waterHeight;
}
res.Position.z = z;
res.Expenditure = ExpenditureType::landscaping;
auto* surfaceElement = MapGetSurfaceElementAt(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
if (gLegacyScene != LegacyScene::scenarioEditor && !getGameState().cheats.sandboxMode)
uint8_t minHeight = GetLowestHeight(validRange);
bool hasChanged = false;
bool withinOwnership = false;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += kCoordsXYStep)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += kCoordsXYStep)
{
if (!MapIsLocationInPark(CoordsXY{ x, y }))
{
if (!LocationValid({ x, y }))
continue;
auto* surfaceElement = MapGetSurfaceElementAt(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
if (gLegacyScene != LegacyScene::scenarioEditor && !getGameState().cheats.sandboxMode)
{
if (!MapIsLocationInPark(CoordsXY{ x, y }))
{
continue;
}
}
withinOwnership = true;
uint8_t height = surfaceElement->GetWaterHeight() / kCoordsZStep;
if (height == 0)
continue;
if (height < minHeight)
continue;
height -= 2;
auto waterSetHeightAction = WaterSetHeightAction({ x, y }, height);
waterSetHeightAction.SetFlags(GetFlags());
auto result = isExecuting ? ExecuteNested(&waterSetHeightAction) : QueryNested(&waterSetHeightAction);
if (result.Error == Status::Ok)
{
res.Cost += result.Cost;
hasChanged = true;
}
else
{
result.ErrorTitle = STR_CANT_LOWER_WATER_LEVEL_HERE;
return result;
}
}
withinOwnership = true;
uint8_t height = surfaceElement->GetWaterHeight() / kCoordsZStep;
if (height == 0)
continue;
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 == GameActions::Status::Ok)
{
res.Cost += result.Cost;
hasChanged = true;
}
else
{
result.ErrorTitle = STR_CANT_LOWER_WATER_LEVEL_HERE;
return result;
}
}
}
if (!withinOwnership)
{
return GameActions::Result(
GameActions::Status::Disallowed, STR_CANT_LOWER_WATER_LEVEL_HERE, STR_LAND_NOT_OWNED_BY_PARK);
;
}
if (isExecuting && hasChanged)
{
Audio::Play3D(Audio::SoundId::LayingOutWater, res.Position);
}
// Force ride construction to recheck area
_currentTrackSelectionFlags.set(TrackSelectionFlag::recheck);
return res;
}
uint8_t WaterLowerAction::GetLowestHeight(const MapRange& validRange) const
{
// The lowest height to lower the water to is the highest water level in the selection
uint8_t minHeight{ 0 };
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += kCoordsXYStep)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += kCoordsXYStep)
if (!withinOwnership)
{
if (gLegacyScene != LegacyScene::scenarioEditor && !getGameState().cheats.sandboxMode)
return Result(Status::Disallowed, STR_CANT_LOWER_WATER_LEVEL_HERE, STR_LAND_NOT_OWNED_BY_PARK);
;
}
if (isExecuting && hasChanged)
{
Audio::Play3D(Audio::SoundId::LayingOutWater, res.Position);
}
// Force ride construction to recheck area
_currentTrackSelectionFlags.set(TrackSelectionFlag::recheck);
return res;
}
uint8_t WaterLowerAction::GetLowestHeight(const MapRange& validRange) const
{
// The lowest height to lower the water to is the highest water level in the selection
uint8_t minHeight{ 0 };
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += kCoordsXYStep)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += kCoordsXYStep)
{
if (!MapIsLocationInPark(CoordsXY{ x, y }))
if (gLegacyScene != LegacyScene::scenarioEditor && !getGameState().cheats.sandboxMode)
{
if (!MapIsLocationInPark(CoordsXY{ x, y }))
{
continue;
}
}
auto* surfaceElement = MapGetSurfaceElementAt(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
uint8_t height = surfaceElement->GetWaterHeight() / kCoordsZStep;
if (height == 0)
continue;
if (height > minHeight)
{
minHeight = height;
}
}
auto* surfaceElement = MapGetSurfaceElementAt(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
uint8_t height = surfaceElement->GetWaterHeight() / kCoordsZStep;
if (height == 0)
continue;
if (height > minHeight)
{
minHeight = height;
}
}
}
return minHeight;
}
return minHeight;
}
} // namespace OpenRCT2::GameActions

View File

@@ -11,24 +11,27 @@
#include "GameAction.h"
class WaterLowerAction final : public GameActionBase<GameCommand::LowerWater>
namespace OpenRCT2::GameActions
{
private:
MapRange _range;
class WaterLowerAction final : public GameActionBase<GameCommand::LowerWater>
{
private:
MapRange _range;
public:
WaterLowerAction() = default;
WaterLowerAction(MapRange range);
public:
WaterLowerAction() = default;
WaterLowerAction(MapRange range);
void AcceptParameters(GameActionParameterVisitor& visitor) override;
void AcceptParameters(GameActionParameterVisitor& visitor) override;
uint16_t GetActionFlags() const override;
uint16_t GetActionFlags() const override;
void Serialise(DataSerialiser& stream) override;
OpenRCT2::GameActions::Result Query() const override;
OpenRCT2::GameActions::Result Execute() const override;
void Serialise(DataSerialiser& stream) override;
Result Query() const override;
Result Execute() const override;
private:
OpenRCT2::GameActions::Result QueryExecute(bool isExecuting) const;
uint8_t GetLowestHeight(const MapRange& validRange) const;
};
private:
Result QueryExecute(bool isExecuting) const;
uint8_t GetLowestHeight(const MapRange& validRange) const;
};
} // namespace OpenRCT2::GameActions

View File

@@ -16,166 +16,165 @@
#include "../world/tile_element/SurfaceElement.h"
#include "WaterSetHeightAction.h"
using namespace OpenRCT2;
WaterRaiseAction::WaterRaiseAction(MapRange range)
: _range(range)
namespace OpenRCT2::GameActions
{
}
void WaterRaiseAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_range);
}
uint16_t WaterRaiseAction::GetActionFlags() const
{
return GameAction::GetActionFlags();
}
void WaterRaiseAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_range);
}
GameActions::Result WaterRaiseAction::Query() const
{
return QueryExecute(false);
}
GameActions::Result WaterRaiseAction::Execute() const
{
return QueryExecute(true);
}
GameActions::Result WaterRaiseAction::QueryExecute(bool isExecuting) const
{
auto res = GameActions::Result();
auto validRange = ClampRangeWithinMap(_range);
res.Position.x = ((validRange.GetLeft() + validRange.GetRight()) / 2) + 16;
res.Position.y = ((validRange.GetTop() + validRange.GetBottom()) / 2) + 16;
int32_t z = TileElementHeight(res.Position);
int16_t waterHeight = TileElementWaterHeight(res.Position);
if (waterHeight != 0)
WaterRaiseAction::WaterRaiseAction(MapRange range)
: _range(range)
{
z = waterHeight;
}
res.Position.z = z;
res.Expenditure = ExpenditureType::landscaping;
auto maxHeight = GetHighestHeight(validRange) / kCoordsZStep;
bool hasChanged = false;
bool withinOwnership = false;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += kCoordsXYStep)
void WaterRaiseAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += kCoordsXYStep)
visitor.Visit(_range);
}
uint16_t WaterRaiseAction::GetActionFlags() const
{
return GameAction::GetActionFlags();
}
void WaterRaiseAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_range);
}
Result WaterRaiseAction::Query() const
{
return QueryExecute(false);
}
Result WaterRaiseAction::Execute() const
{
return QueryExecute(true);
}
Result WaterRaiseAction::QueryExecute(bool isExecuting) const
{
auto res = Result();
auto validRange = ClampRangeWithinMap(_range);
res.Position.x = ((validRange.GetLeft() + validRange.GetRight()) / 2) + 16;
res.Position.y = ((validRange.GetTop() + validRange.GetBottom()) / 2) + 16;
int32_t z = TileElementHeight(res.Position);
int16_t waterHeight = TileElementWaterHeight(res.Position);
if (waterHeight != 0)
{
if (!LocationValid({ x, y }))
continue;
z = waterHeight;
}
res.Position.z = z;
res.Expenditure = ExpenditureType::landscaping;
auto surfaceElement = MapGetSurfaceElementAt(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
if (gLegacyScene != LegacyScene::scenarioEditor && !getGameState().cheats.sandboxMode)
auto maxHeight = GetHighestHeight(validRange) / kCoordsZStep;
bool hasChanged = false;
bool withinOwnership = false;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += kCoordsXYStep)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += kCoordsXYStep)
{
if (!MapIsLocationInPark(CoordsXY{ x, y }))
{
if (!LocationValid({ x, y }))
continue;
auto surfaceElement = MapGetSurfaceElementAt(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
if (gLegacyScene != LegacyScene::scenarioEditor && !getGameState().cheats.sandboxMode)
{
if (!MapIsLocationInPark(CoordsXY{ x, y }))
{
continue;
}
}
}
withinOwnership = true;
withinOwnership = true;
uint8_t height = surfaceElement->GetWaterHeight() / kCoordsZStep;
uint8_t height = surfaceElement->GetWaterHeight() / kCoordsZStep;
if (surfaceElement->BaseHeight > maxHeight)
continue;
if (height != 0)
{
if (height > maxHeight)
if (surfaceElement->BaseHeight > maxHeight)
continue;
if (height + 2 > UINT8_MAX)
if (height != 0)
{
height = UINT8_MAX;
if (height > maxHeight)
continue;
if (height + 2 > UINT8_MAX)
{
height = UINT8_MAX;
}
else
{
height += 2;
}
}
else
{
height += 2;
height = surfaceElement->BaseHeight + 2;
}
}
else
{
height = surfaceElement->BaseHeight + 2;
}
auto waterSetHeightAction = WaterSetHeightAction({ x, y }, height);
waterSetHeightAction.SetFlags(GetFlags());
auto result = isExecuting ? GameActions::ExecuteNested(&waterSetHeightAction)
: GameActions::QueryNested(&waterSetHeightAction);
if (result.Error == GameActions::Status::Ok)
{
res.Cost += result.Cost;
hasChanged = true;
}
else
{
result.ErrorTitle = STR_CANT_RAISE_WATER_LEVEL_HERE;
return result;
}
}
}
if (!withinOwnership)
{
return GameActions::Result(
GameActions::Status::Disallowed, STR_CANT_RAISE_WATER_LEVEL_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
if (isExecuting && hasChanged)
{
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::LayingOutWater, res.Position);
}
// Force ride construction to recheck area
_currentTrackSelectionFlags.set(TrackSelectionFlag::recheck);
return res;
}
uint16_t WaterRaiseAction::GetHighestHeight(const MapRange& validRange) const
{
// The highest height to raise the water to is the lowest water level in the selection
uint16_t maxHeight = 255 * kCoordsZStep;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += kCoordsXYStep)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += kCoordsXYStep)
{
if (gLegacyScene != LegacyScene::scenarioEditor && !getGameState().cheats.sandboxMode)
{
if (!MapIsLocationInPark(CoordsXY{ x, y }))
auto waterSetHeightAction = WaterSetHeightAction({ x, y }, height);
waterSetHeightAction.SetFlags(GetFlags());
auto result = isExecuting ? ExecuteNested(&waterSetHeightAction) : QueryNested(&waterSetHeightAction);
if (result.Error == Status::Ok)
{
continue;
res.Cost += result.Cost;
hasChanged = true;
}
else
{
result.ErrorTitle = STR_CANT_RAISE_WATER_LEVEL_HERE;
return result;
}
}
auto* surfaceElement = MapGetSurfaceElementAt(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
auto height = surfaceElement->GetBaseZ();
if (surfaceElement->GetWaterHeight() > 0)
{
height = surfaceElement->GetWaterHeight();
}
if (maxHeight > height)
{
maxHeight = height;
}
}
if (!withinOwnership)
{
return Result(Status::Disallowed, STR_CANT_RAISE_WATER_LEVEL_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
if (isExecuting && hasChanged)
{
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::LayingOutWater, res.Position);
}
// Force ride construction to recheck area
_currentTrackSelectionFlags.set(TrackSelectionFlag::recheck);
return res;
}
return maxHeight;
}
uint16_t WaterRaiseAction::GetHighestHeight(const MapRange& validRange) const
{
// The highest height to raise the water to is the lowest water level in the selection
uint16_t maxHeight = 255 * kCoordsZStep;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += kCoordsXYStep)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += kCoordsXYStep)
{
if (gLegacyScene != LegacyScene::scenarioEditor && !getGameState().cheats.sandboxMode)
{
if (!MapIsLocationInPark(CoordsXY{ x, y }))
{
continue;
}
}
auto* surfaceElement = MapGetSurfaceElementAt(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
auto height = surfaceElement->GetBaseZ();
if (surfaceElement->GetWaterHeight() > 0)
{
height = surfaceElement->GetWaterHeight();
}
if (maxHeight > height)
{
maxHeight = height;
}
}
}
return maxHeight;
}
} // namespace OpenRCT2::GameActions

View File

@@ -11,24 +11,27 @@
#include "GameAction.h"
class WaterRaiseAction final : public GameActionBase<GameCommand::RaiseWater>
namespace OpenRCT2::GameActions
{
private:
MapRange _range;
class WaterRaiseAction final : public GameActionBase<GameCommand::RaiseWater>
{
private:
MapRange _range;
public:
WaterRaiseAction() = default;
WaterRaiseAction(MapRange range);
public:
WaterRaiseAction() = default;
WaterRaiseAction(MapRange range);
void AcceptParameters(GameActionParameterVisitor& visitor) override;
void AcceptParameters(GameActionParameterVisitor& visitor) override;
uint16_t GetActionFlags() const override;
uint16_t GetActionFlags() const override;
void Serialise(DataSerialiser& stream) override;
OpenRCT2::GameActions::Result Query() const override;
OpenRCT2::GameActions::Result Execute() const override;
void Serialise(DataSerialiser& stream) override;
Result Query() const override;
Result Execute() const override;
private:
OpenRCT2::GameActions::Result QueryExecute(bool isExecuting) const;
uint16_t GetHighestHeight(const MapRange& validRange) const;
};
private:
Result QueryExecute(bool isExecuting) const;
uint16_t GetHighestHeight(const MapRange& validRange) const;
};
} // namespace OpenRCT2::GameActions

View File

@@ -19,151 +19,149 @@
#include "../world/Wall.h"
#include "../world/tile_element/SurfaceElement.h"
using namespace OpenRCT2;
WaterSetHeightAction::WaterSetHeightAction(const CoordsXY& coords, uint8_t height)
: _coords(coords)
, _height(height)
namespace OpenRCT2::GameActions
{
}
void WaterSetHeightAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_coords);
visitor.Visit("height", _height);
}
uint16_t WaterSetHeightAction::GetActionFlags() const
{
return GameAction::GetActionFlags();
}
void WaterSetHeightAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_height);
}
GameActions::Result WaterSetHeightAction::Query() const
{
auto res = GameActions::Result();
res.Expenditure = ExpenditureType::landscaping;
res.Position = { _coords, _height * kCoordsZStep };
auto& gameState = getGameState();
if (gLegacyScene != LegacyScene::scenarioEditor && !gameState.cheats.sandboxMode
&& gameState.park.flags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES)
WaterSetHeightAction::WaterSetHeightAction(const CoordsXY& coords, uint8_t height)
: _coords(coords)
, _height(height)
{
return GameActions::Result(GameActions::Status::Disallowed, kStringIdNone, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY);
}
StringId errorMsg = CheckParameters();
if (errorMsg != kStringIdNone)
void WaterSetHeightAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
return GameActions::Result(GameActions::Status::InvalidParameters, kStringIdNone, errorMsg);
visitor.Visit(_coords);
visitor.Visit("height", _height);
}
if (!LocationValid(_coords))
uint16_t WaterSetHeightAction::GetActionFlags() const
{
return GameActions::Result(GameActions::Status::NotOwned, kStringIdNone, STR_LAND_NOT_OWNED_BY_PARK);
return GameAction::GetActionFlags();
}
if (gLegacyScene != LegacyScene::scenarioEditor && !gameState.cheats.sandboxMode)
void WaterSetHeightAction::Serialise(DataSerialiser& stream)
{
if (!MapIsLocationInPark(_coords))
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_height);
}
Result WaterSetHeightAction::Query() const
{
auto res = Result();
res.Expenditure = ExpenditureType::landscaping;
res.Position = { _coords, _height * kCoordsZStep };
auto& gameState = getGameState();
if (gLegacyScene != LegacyScene::scenarioEditor && !gameState.cheats.sandboxMode
&& gameState.park.flags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES)
{
return GameActions::Result(GameActions::Status::Disallowed, kStringIdNone, STR_LAND_NOT_OWNED_BY_PARK);
return Result(Status::Disallowed, kStringIdNone, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY);
}
StringId errorMsg = CheckParameters();
if (errorMsg != kStringIdNone)
{
return Result(Status::InvalidParameters, kStringIdNone, errorMsg);
}
if (!LocationValid(_coords))
{
return Result(Status::NotOwned, kStringIdNone, STR_LAND_NOT_OWNED_BY_PARK);
}
if (gLegacyScene != LegacyScene::scenarioEditor && !gameState.cheats.sandboxMode)
{
if (!MapIsLocationInPark(_coords))
{
return Result(Status::Disallowed, kStringIdNone, STR_LAND_NOT_OWNED_BY_PARK);
}
}
SurfaceElement* surfaceElement = MapGetSurfaceElementAt(_coords);
if (surfaceElement == nullptr)
{
LOG_ERROR("No surface element at: x %u, y %u", _coords.x, _coords.y);
return Result(Status::InvalidParameters, STR_ERR_INVALID_PARAMETER, STR_ERR_SURFACE_ELEMENT_NOT_FOUND);
}
int32_t zHigh = surfaceElement->GetBaseZ();
int32_t zLow = _height * kCoordsZStep;
if (surfaceElement->GetWaterHeight() > 0)
{
zHigh = surfaceElement->GetWaterHeight();
}
if (zLow > zHigh)
{
int32_t temp = zHigh;
zHigh = zLow;
zLow = temp;
}
if (auto res2 = MapCanConstructAt({ _coords, zLow, zHigh }, { 0b1111, 0b1111 }); res2.Error != Status::Ok)
{
return res2;
}
if (surfaceElement->HasTrackThatNeedsWater())
{
return Result(Status::Disallowed, STR_ERR_INVALID_PARAMETER, STR_ERR_TRACK_ON_THIS_TILE_NEEDS_WATER);
}
res.Cost = 250;
return res;
}
SurfaceElement* surfaceElement = MapGetSurfaceElementAt(_coords);
if (surfaceElement == nullptr)
Result WaterSetHeightAction::Execute() const
{
LOG_ERROR("No surface element at: x %u, y %u", _coords.x, _coords.y);
return GameActions::Result(
GameActions::Status::InvalidParameters, STR_ERR_INVALID_PARAMETER, STR_ERR_SURFACE_ELEMENT_NOT_FOUND);
auto res = Result();
res.Expenditure = ExpenditureType::landscaping;
res.Position = { _coords, _height * kCoordsZStep };
int32_t surfaceHeight = TileElementHeight(_coords);
FootpathRemoveLitter({ _coords, surfaceHeight });
if (!getGameState().cheats.disableClearanceChecks)
WallRemoveAtZ({ _coords, surfaceHeight });
SurfaceElement* surfaceElement = MapGetSurfaceElementAt(_coords);
if (surfaceElement == nullptr)
{
LOG_ERROR("No surface element at: x %u, y %u", _coords.x, _coords.y);
return Result(Status::InvalidParameters, STR_ERR_INVALID_PARAMETER, STR_ERR_SURFACE_ELEMENT_NOT_FOUND);
}
if (_height > surfaceElement->BaseHeight)
{
surfaceElement->SetWaterHeight(_height * kCoordsZStep);
}
else
{
surfaceElement->SetWaterHeight(0);
}
MapInvalidateTileFull(_coords);
res.Cost = 250;
return res;
}
int32_t zHigh = surfaceElement->GetBaseZ();
int32_t zLow = _height * kCoordsZStep;
if (surfaceElement->GetWaterHeight() > 0)
StringId WaterSetHeightAction::CheckParameters() const
{
zHigh = surfaceElement->GetWaterHeight();
auto mapSizeMax = GetMapSizeMaxXY();
if (_coords.x > mapSizeMax.x || _coords.y > mapSizeMax.y)
{
return STR_OFF_EDGE_OF_MAP;
}
if (_height < kMinimumWaterHeight)
{
return STR_TOO_LOW;
}
if (_height > kMaximumWaterHeight)
{
return STR_TOO_HIGH;
}
return kStringIdNone;
}
if (zLow > zHigh)
{
int32_t temp = zHigh;
zHigh = zLow;
zLow = temp;
}
if (auto res2 = MapCanConstructAt({ _coords, zLow, zHigh }, { 0b1111, 0b1111 }); res2.Error != GameActions::Status::Ok)
{
return res2;
}
if (surfaceElement->HasTrackThatNeedsWater())
{
return GameActions::Result(
GameActions::Status::Disallowed, STR_ERR_INVALID_PARAMETER, STR_ERR_TRACK_ON_THIS_TILE_NEEDS_WATER);
}
res.Cost = 250;
return res;
}
GameActions::Result WaterSetHeightAction::Execute() const
{
auto res = GameActions::Result();
res.Expenditure = ExpenditureType::landscaping;
res.Position = { _coords, _height * kCoordsZStep };
int32_t surfaceHeight = TileElementHeight(_coords);
FootpathRemoveLitter({ _coords, surfaceHeight });
if (!getGameState().cheats.disableClearanceChecks)
WallRemoveAtZ({ _coords, surfaceHeight });
SurfaceElement* surfaceElement = MapGetSurfaceElementAt(_coords);
if (surfaceElement == nullptr)
{
LOG_ERROR("No surface element at: x %u, y %u", _coords.x, _coords.y);
return GameActions::Result(
GameActions::Status::InvalidParameters, STR_ERR_INVALID_PARAMETER, STR_ERR_SURFACE_ELEMENT_NOT_FOUND);
}
if (_height > surfaceElement->BaseHeight)
{
surfaceElement->SetWaterHeight(_height * kCoordsZStep);
}
else
{
surfaceElement->SetWaterHeight(0);
}
MapInvalidateTileFull(_coords);
res.Cost = 250;
return res;
}
StringId WaterSetHeightAction::CheckParameters() const
{
auto mapSizeMax = GetMapSizeMaxXY();
if (_coords.x > mapSizeMax.x || _coords.y > mapSizeMax.y)
{
return STR_OFF_EDGE_OF_MAP;
}
if (_height < kMinimumWaterHeight)
{
return STR_TOO_LOW;
}
if (_height > kMaximumWaterHeight)
{
return STR_TOO_HIGH;
}
return kStringIdNone;
}
} // namespace OpenRCT2::GameActions

View File

@@ -11,24 +11,27 @@
#include "GameAction.h"
class WaterSetHeightAction final : public GameActionBase<GameCommand::SetWaterHeight>
namespace OpenRCT2::GameActions
{
private:
CoordsXY _coords;
uint8_t _height{};
class WaterSetHeightAction final : public GameActionBase<GameCommand::SetWaterHeight>
{
private:
CoordsXY _coords;
uint8_t _height{};
public:
WaterSetHeightAction() = default;
WaterSetHeightAction(const CoordsXY& coords, uint8_t height);
public:
WaterSetHeightAction() = default;
WaterSetHeightAction(const CoordsXY& coords, uint8_t height);
void AcceptParameters(GameActionParameterVisitor& visitor) override;
void AcceptParameters(GameActionParameterVisitor& visitor) override;
uint16_t GetActionFlags() const override;
uint16_t GetActionFlags() const override;
void Serialise(DataSerialiser& stream) override;
OpenRCT2::GameActions::Result Query() const override;
OpenRCT2::GameActions::Result Execute() const override;
void Serialise(DataSerialiser& stream) override;
Result Query() const override;
Result Execute() const override;
private:
StringId CheckParameters() const;
};
private:
StringId CheckParameters() const;
};
} // namespace OpenRCT2::GameActions