1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-22 14:24:33 +01:00
Files
OpenRCT2/src/openrct2/actions/ClearAction.cpp

232 lines
8.0 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2020 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 "ClearAction.h"
#include "../Context.h"
#include "../core/MemoryStream.h"
#include "../drawing/Drawing.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/LargeScenery.h"
#include "../world/Location.hpp"
#include "../world/Map.h"
#include "FootpathRemoveAction.h"
#include "LargeSceneryRemoveAction.h"
#include "SmallSceneryRemoveAction.h"
#include "WallRemoveAction.h"
#include <algorithm>
ClearAction::ClearAction(MapRange range, ClearableItems itemsToClear)
: _range(range)
, _itemsToClear(itemsToClear)
{
}
void ClearAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_itemsToClear);
}
GameActions::Result ClearAction::Query() const
{
return QueryExecute(false);
}
GameActions::Result ClearAction::Execute() const
{
return QueryExecute(true);
}
GameActions::Result ClearAction::CreateResult() const
{
auto result = GameActions::Result();
result.ErrorTitle = STR_UNABLE_TO_REMOVE_ALL_SCENERY_FROM_HERE;
result.Expenditure = ExpenditureType::Landscaping;
auto x = (_range.GetLeft() + _range.GetRight()) / 2 + 16;
auto y = (_range.GetTop() + _range.GetBottom()) / 2 + 16;
auto z = tile_element_height({ x, y });
result.Position = CoordsXYZ(x, y, z);
return result;
}
GameActions::Result ClearAction::QueryExecute(bool executing) const
{
auto result = CreateResult();
auto noValidTiles = true;
auto error = GameActions::Status::Ok;
rct_string_id errorMessage = STR_NONE;
money32 totalCost = 0;
auto validRange = ClampRangeWithinMap(_range);
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
if (LocationValid({ x, y }) && MapCanClearAt({ x, y }))
{
auto cost = ClearSceneryFromTile({ x, y }, executing);
if (cost != MONEY32_UNDEFINED)
{
noValidTiles = false;
totalCost += cost;
}
}
else
{
error = GameActions::Status::NotOwned;
errorMessage = STR_LAND_NOT_OWNED_BY_PARK;
}
}
}
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_LARGE)
{
ResetClearLargeSceneryFlag();
}
if (noValidTiles)
{
result.Error = error;
result.ErrorMessage = errorMessage;
}
result.Cost = totalCost;
return result;
}
money32 ClearAction::ClearSceneryFromTile(const CoordsXY& tilePos, bool executing) const
{
// Pass down all flags.
TileElement* tileElement = nullptr;
money32 totalCost = 0;
bool tileEdited;
do
{
tileEdited = false;
tileElement = map_get_first_element_at(tilePos);
if (tileElement == nullptr)
return totalCost;
do
{
if (tileElement->IsGhost())
continue;
switch (tileElement->GetType())
{
case TileElementType::Path:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_FOOTPATH)
{
auto footpathRemoveAction = FootpathRemoveAction({ tilePos, tileElement->GetBaseZ() });
footpathRemoveAction.SetFlags(GetFlags());
auto res = executing ? GameActions::ExecuteNested(&footpathRemoveAction)
: GameActions::QueryNested(&footpathRemoveAction);
if (res.Error == GameActions::Status::Ok)
{
totalCost += res.Cost;
tileEdited = executing;
}
}
break;
case TileElementType::SmallScenery:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_SMALL)
{
auto removeSceneryAction = SmallSceneryRemoveAction(
{ tilePos, tileElement->GetBaseZ() }, tileElement->AsSmallScenery()->GetSceneryQuadrant(),
tileElement->AsSmallScenery()->GetEntryIndex());
removeSceneryAction.SetFlags(GetFlags());
auto res = executing ? GameActions::ExecuteNested(&removeSceneryAction)
: GameActions::QueryNested(&removeSceneryAction);
if (res.Error == GameActions::Status::Ok)
{
totalCost += res.Cost;
tileEdited = executing;
}
}
break;
case TileElementType::Wall:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_SMALL)
{
CoordsXYZD wallLocation = { tilePos, tileElement->GetBaseZ(), tileElement->GetDirection() };
auto wallRemoveAction = WallRemoveAction(wallLocation);
wallRemoveAction.SetFlags(GetFlags());
auto res = executing ? GameActions::ExecuteNested(&wallRemoveAction)
: GameActions::QueryNested(&wallRemoveAction);
if (res.Error == GameActions::Status::Ok)
{
totalCost += res.Cost;
tileEdited = executing;
}
}
break;
case TileElementType::LargeScenery:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_LARGE)
{
auto removeSceneryAction = LargeSceneryRemoveAction(
{ tilePos, tileElement->GetBaseZ(), tileElement->GetDirection() },
tileElement->AsLargeScenery()->GetSequenceIndex());
removeSceneryAction.SetFlags(GetFlags() | GAME_COMMAND_FLAG_TRACK_DESIGN);
auto res = executing ? GameActions::ExecuteNested(&removeSceneryAction)
: GameActions::QueryNested(&removeSceneryAction);
if (res.Error == GameActions::Status::Ok)
{
totalCost += res.Cost;
tileEdited = executing;
}
}
break;
default:
break;
}
} while (!tileEdited && !(tileElement++)->IsLastForTile());
} while (tileEdited);
return totalCost;
}
void ClearAction::ResetClearLargeSceneryFlag()
{
// TODO: Improve efficiency of this
for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++)
{
for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++)
{
auto tileElement = map_get_first_element_at(TileCoordsXY{ x, y });
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() == TileElementType::LargeScenery)
{
tileElement->AsLargeScenery()->SetIsAccounted(false);
}
} while (!(tileElement++)->IsLastForTile());
}
}
}
bool ClearAction::MapCanClearAt(const CoordsXY& location)
{
return (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode || map_is_location_owned_or_has_rights(location);
}