1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "FootpathRemoveAction.h"
11 
12 #include "../Cheats.h"
13 #include "../OpenRCT2.h"
14 #include "../core/MemoryStream.h"
15 #include "../interface/Window.h"
16 #include "../localisation/StringIds.h"
17 #include "../management/Finance.h"
18 #include "../world/Footpath.h"
19 #include "../world/Location.hpp"
20 #include "../world/Park.h"
21 #include "../world/Wall.h"
22 #include "BannerRemoveAction.h"
23 
FootpathRemoveAction(const CoordsXYZ & location)24 FootpathRemoveAction::FootpathRemoveAction(const CoordsXYZ& location)
25     : _loc(location)
26 {
27 }
28 
AcceptParameters(GameActionParameterVisitor & visitor)29 void FootpathRemoveAction::AcceptParameters(GameActionParameterVisitor& visitor)
30 {
31     visitor.Visit(_loc);
32 }
33 
GetActionFlags() const34 uint16_t FootpathRemoveAction::GetActionFlags() const
35 {
36     return GameAction::GetActionFlags();
37 }
38 
Serialise(DataSerialiser & stream)39 void FootpathRemoveAction::Serialise(DataSerialiser& stream)
40 {
41     GameAction::Serialise(stream);
42 
43     stream << DS_TAG(_loc);
44 }
45 
Query() const46 GameActions::Result::Ptr FootpathRemoveAction::Query() const
47 {
48     GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
49     res->Cost = 0;
50     res->Expenditure = ExpenditureType::Landscaping;
51     res->Position = { _loc.x + 16, _loc.y + 16, _loc.z };
52 
53     if (!LocationValid(_loc))
54     {
55         return MakeResult(GameActions::Status::NotOwned, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_LAND_NOT_OWNED_BY_PARK);
56     }
57 
58     if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
59     {
60         return MakeResult(GameActions::Status::NotOwned, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_LAND_NOT_OWNED_BY_PARK);
61     }
62 
63     TileElement* footpathElement = GetFootpathElement();
64     if (footpathElement == nullptr)
65     {
66         return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_NONE);
67     }
68 
69     res->Cost = GetRefundPrice(footpathElement);
70 
71     return res;
72 }
73 
Execute() const74 GameActions::Result::Ptr FootpathRemoveAction::Execute() const
75 {
76     GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
77     res->Cost = 0;
78     res->Expenditure = ExpenditureType::Landscaping;
79     res->Position = { _loc.x + 16, _loc.y + 16, _loc.z };
80 
81     if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
82     {
83         footpath_interrupt_peeps(_loc);
84         footpath_remove_litter(_loc);
85     }
86 
87     TileElement* footpathElement = GetFootpathElement();
88     if (footpathElement != nullptr)
89     {
90         footpath_queue_chain_reset();
91         auto bannerRes = RemoveBannersAtElement(_loc, footpathElement);
92         if (bannerRes->Error == GameActions::Status::Ok)
93         {
94             res->Cost += bannerRes->Cost;
95         }
96         footpath_remove_edges_at(_loc, footpathElement);
97         map_invalidate_tile_full(_loc);
98         tile_element_remove(footpathElement);
99         footpath_update_queue_chains();
100 
101         // Remove the spawn point (if there is one in the current tile)
102         gPeepSpawns.erase(
103             std::remove_if(
104                 gPeepSpawns.begin(), gPeepSpawns.end(),
105                 [this](const CoordsXYZ& spawn) {
106                     {
107                         return spawn.ToTileStart() == _loc.ToTileStart();
108                     }
109                 }),
110             gPeepSpawns.end());
111     }
112     else
113     {
114         return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_NONE);
115     }
116 
117     res->Cost += GetRefundPrice(footpathElement);
118 
119     return res;
120 }
121 
GetFootpathElement() const122 TileElement* FootpathRemoveAction::GetFootpathElement() const
123 {
124     bool getGhostPath = GetFlags() & GAME_COMMAND_FLAG_GHOST;
125 
126     TileElement* tileElement = map_get_footpath_element(_loc);
127     TileElement* footpathElement = nullptr;
128     if (tileElement != nullptr)
129     {
130         if (getGhostPath && !tileElement->IsGhost())
131         {
132             while (!(tileElement++)->IsLastForTile())
133             {
134                 if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH && !tileElement->IsGhost())
135                 {
136                     continue;
137                 }
138                 footpathElement = tileElement;
139                 break;
140             }
141         }
142         else
143         {
144             footpathElement = tileElement;
145         }
146     }
147 
148     return footpathElement;
149 }
150 
GetRefundPrice(TileElement * footpathElement) const151 money32 FootpathRemoveAction::GetRefundPrice(TileElement* footpathElement) const
152 {
153     money32 cost = -MONEY(10, 00);
154     return cost;
155 }
156 
157 /**
158  *
159  *  rct2: 0x006BA23E
160  */
RemoveBannersAtElement(const CoordsXY & loc,TileElement * tileElement) const161 GameActions::Result::Ptr FootpathRemoveAction::RemoveBannersAtElement(const CoordsXY& loc, TileElement* tileElement) const
162 {
163     auto result = MakeResult();
164     while (!(tileElement++)->IsLastForTile())
165     {
166         if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH)
167             return result;
168 
169         if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER)
170             continue;
171 
172         auto bannerRemoveAction = BannerRemoveAction({ loc, tileElement->GetBaseZ(), tileElement->AsBanner()->GetPosition() });
173         bool isGhost = tileElement->IsGhost();
174         auto bannerFlags = GetFlags() | (isGhost ? static_cast<uint32_t>(GAME_COMMAND_FLAG_GHOST) : 0);
175         bannerRemoveAction.SetFlags(bannerFlags);
176         auto res = GameActions::ExecuteNested(&bannerRemoveAction);
177         // Ghost removal is free
178         if (res->Error == GameActions::Status::Ok && !isGhost)
179         {
180             result->Cost += res->Cost;
181         }
182         tileElement--;
183     }
184     return result;
185 }
186