mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-23 14:54:30 +01:00
217 lines
7.8 KiB
C++
217 lines
7.8 KiB
C++
/*****************************************************************************
|
|
* Copyright (c) 2014-2025 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 "ParkEntrancePlaceAction.h"
|
|
|
|
#include "../Cheats.h"
|
|
#include "../GameState.h"
|
|
#include "../OpenRCT2.h"
|
|
#include "../core/MemoryStream.h"
|
|
#include "../localisation/StringIds.h"
|
|
#include "../management/Finance.h"
|
|
#include "../world/ConstructionClearance.h"
|
|
#include "../world/Entrance.h"
|
|
#include "../world/Footpath.h"
|
|
#include "../world/Map.h"
|
|
#include "../world/MapAnimation.h"
|
|
#include "../world/Park.h"
|
|
#include "../world/tile_element/EntranceElement.h"
|
|
#include "../world/tile_element/SurfaceElement.h"
|
|
|
|
namespace OpenRCT2::GameActions
|
|
{
|
|
ParkEntrancePlaceAction::ParkEntrancePlaceAction(
|
|
const CoordsXYZD& location, ObjectEntryIndex pathType, ObjectEntryIndex entranceType, bool pathTypeIsLegacy)
|
|
: _loc(location)
|
|
, _pathType(pathType)
|
|
, _entranceType(entranceType)
|
|
{
|
|
}
|
|
|
|
void ParkEntrancePlaceAction::AcceptParameters(GameActionParameterVisitor& visitor)
|
|
{
|
|
visitor.Visit(_loc);
|
|
visitor.Visit("footpathSurfaceObject", _pathType);
|
|
visitor.Visit("entranceObject", _entranceType);
|
|
visitor.Visit("footpathTypeIsLegacy", _pathTypeIsLegacy);
|
|
}
|
|
|
|
uint16_t ParkEntrancePlaceAction::GetActionFlags() const
|
|
{
|
|
return GameActionBase::GetActionFlags() | Flags::EditorOnly;
|
|
}
|
|
|
|
void ParkEntrancePlaceAction::Serialise(DataSerialiser& stream)
|
|
{
|
|
GameAction::Serialise(stream);
|
|
|
|
stream << DS_TAG(_loc);
|
|
stream << DS_TAG(_pathType);
|
|
stream << DS_TAG(_entranceType);
|
|
stream << DS_TAG(_pathTypeIsLegacy);
|
|
}
|
|
|
|
Result ParkEntrancePlaceAction::Query(GameState_t& gameState) const
|
|
{
|
|
if (!isInEditorMode() && !getGameState().cheats.sandboxMode)
|
|
{
|
|
return Result(Status::NotInEditorMode, STR_CANT_BUILD_THIS_HERE, kStringIdNone);
|
|
}
|
|
|
|
auto res = Result();
|
|
res.Expenditure = ExpenditureType::landPurchase;
|
|
res.Position = _loc;
|
|
|
|
auto mapSizeUnits = GetMapSizeUnits() - CoordsXY{ kCoordsXYStep, kCoordsXYStep };
|
|
if (!LocationValid(_loc) || _loc.x <= kCoordsXYStep || _loc.y <= kCoordsXYStep || _loc.x >= mapSizeUnits.x
|
|
|| _loc.y >= mapSizeUnits.y)
|
|
{
|
|
return Result(Status::InvalidParameters, STR_CANT_BUILD_THIS_HERE, STR_TOO_CLOSE_TO_EDGE_OF_MAP);
|
|
}
|
|
|
|
if (!CheckMapCapacity(3))
|
|
{
|
|
return Result(Status::NoFreeElements, STR_CANT_BUILD_THIS_HERE, STR_ERR_LANDSCAPE_DATA_AREA_FULL);
|
|
}
|
|
|
|
if (gameState.park.entrances.size() >= OpenRCT2::Limits::kMaxParkEntrances)
|
|
{
|
|
return Result(Status::InvalidParameters, STR_CANT_BUILD_THIS_HERE, STR_ERR_TOO_MANY_PARK_ENTRANCES);
|
|
}
|
|
|
|
auto zLow = _loc.z;
|
|
auto zHigh = zLow + ParkEntranceHeight;
|
|
CoordsXYZ entranceLoc = _loc;
|
|
for (uint8_t index = 0; index < 3; index++)
|
|
{
|
|
if (index == 1)
|
|
{
|
|
entranceLoc += CoordsDirectionDelta[(_loc.direction - 1) & 0x3];
|
|
}
|
|
else if (index == 2)
|
|
{
|
|
entranceLoc.x += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].x * 2;
|
|
entranceLoc.y += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].y * 2;
|
|
}
|
|
|
|
if (auto res2 = MapCanConstructAt({ entranceLoc, zLow, zHigh }, { 0b1111, 0 }); res2.Error != Status::Ok)
|
|
{
|
|
res2.ErrorTitle = STR_CANT_BUILD_THIS_HERE;
|
|
return res2;
|
|
}
|
|
|
|
// Check that entrance element does not already exist at this location
|
|
EntranceElement* entranceElement = MapGetParkEntranceElementAt(entranceLoc, false);
|
|
if (entranceElement != nullptr)
|
|
{
|
|
return Result(Status::ItemAlreadyPlaced, STR_CANT_BUILD_THIS_HERE, kStringIdNone);
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
Result ParkEntrancePlaceAction::Execute(GameState_t& gameState) const
|
|
{
|
|
auto res = Result();
|
|
res.Expenditure = ExpenditureType::landPurchase;
|
|
res.Position = CoordsXYZ{ _loc.x, _loc.y, _loc.z };
|
|
|
|
uint32_t flags = GetFlags();
|
|
|
|
getGameState().park.entrances.push_back(_loc);
|
|
|
|
auto zLow = _loc.z;
|
|
auto zHigh = zLow + ParkEntranceHeight;
|
|
CoordsXY entranceLoc = { _loc.x, _loc.y };
|
|
for (uint8_t index = 0; index < 3; index++)
|
|
{
|
|
if (index == 1)
|
|
{
|
|
entranceLoc.x += CoordsDirectionDelta[(_loc.direction - 1) & 0x3].x;
|
|
entranceLoc.y += CoordsDirectionDelta[(_loc.direction - 1) & 0x3].y;
|
|
}
|
|
else if (index == 2)
|
|
{
|
|
entranceLoc.x += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].x * 2;
|
|
entranceLoc.y += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].y * 2;
|
|
}
|
|
|
|
if (!(flags & GAME_COMMAND_FLAG_GHOST))
|
|
{
|
|
SurfaceElement* surfaceElement = MapGetSurfaceElementAt(entranceLoc);
|
|
if (surfaceElement != nullptr)
|
|
{
|
|
surfaceElement->SetOwnership(OWNERSHIP_UNOWNED);
|
|
}
|
|
}
|
|
|
|
auto* entranceElement = TileElementInsert<EntranceElement>(CoordsXYZ{ entranceLoc, zLow }, 0b1111);
|
|
Guard::Assert(entranceElement != nullptr);
|
|
|
|
entranceElement->SetClearanceZ(zHigh);
|
|
entranceElement->SetGhost(flags & GAME_COMMAND_FLAG_GHOST);
|
|
entranceElement->SetDirection(_loc.direction);
|
|
entranceElement->SetSequenceIndex(index);
|
|
entranceElement->SetEntranceType(ENTRANCE_TYPE_PARK_ENTRANCE);
|
|
entranceElement->setEntryIndex(_entranceType);
|
|
if (!_pathTypeIsLegacy)
|
|
{
|
|
entranceElement->SetSurfaceEntryIndex(_pathType);
|
|
}
|
|
else
|
|
{
|
|
entranceElement->SetLegacyPathEntryIndex(_pathType);
|
|
}
|
|
|
|
if (!entranceElement->IsGhost())
|
|
{
|
|
FootpathConnectEdges(entranceLoc, entranceElement->as<TileElement>(), GAME_COMMAND_FLAG_APPLY);
|
|
}
|
|
|
|
Park::UpdateFences(entranceLoc);
|
|
Park::UpdateFences({ entranceLoc.x - kCoordsXYStep, entranceLoc.y });
|
|
Park::UpdateFences({ entranceLoc.x + kCoordsXYStep, entranceLoc.y });
|
|
Park::UpdateFences({ entranceLoc.x, entranceLoc.y - kCoordsXYStep });
|
|
Park::UpdateFences({ entranceLoc.x, entranceLoc.y + kCoordsXYStep });
|
|
|
|
MapInvalidateTile({ entranceLoc, entranceElement->GetBaseZ(), entranceElement->GetClearanceZ() });
|
|
|
|
if (index == 0)
|
|
{
|
|
MapAnimations::MarkTileForInvalidation(TileCoordsXY(entranceLoc));
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
bool ParkEntrancePlaceAction::CheckMapCapacity(int16_t numTiles) const
|
|
{
|
|
CoordsXYZ entranceLoc = _loc;
|
|
for (uint8_t index = 0; index < 3; index++)
|
|
{
|
|
if (index == 1)
|
|
{
|
|
entranceLoc += CoordsDirectionDelta[(_loc.direction - 1) & 0x3];
|
|
}
|
|
else if (index == 2)
|
|
{
|
|
entranceLoc.x += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].x * 2;
|
|
entranceLoc.y += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].y * 2;
|
|
}
|
|
if (!MapCheckCapacityAndReorganise(entranceLoc, numTiles))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
} // namespace OpenRCT2::GameActions
|