mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-21 14:02:59 +01:00
MapCanConstructAt refactor (#11977)
* Start using MapCanConstructAt * Fix #11675. Pass the full error message arguments on peep pickup This was causing a crash as the error message arguments were being cleared which would mean eventually a nullptr dereference would happen.
This commit is contained in:
@@ -140,6 +140,12 @@ public:
|
||||
std::string GetErrorMessage() const;
|
||||
};
|
||||
|
||||
class ConstructClearResult final : public GameActionResult
|
||||
{
|
||||
public:
|
||||
uint8_t GroundFlags{ 0 };
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -141,21 +141,22 @@ public:
|
||||
res->ErrorMessage = STR_INVALID_SELECTION_OF_OBJECTS;
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!map_can_construct_at({ _loc.ToTileStart(), baseHeight, clearanceHeight }, { 0b1111, 0 }))
|
||||
auto constructResult = MapCanConstructAt({ _loc.ToTileStart(), baseHeight, clearanceHeight }, { 0b1111, 0 });
|
||||
if (constructResult->Error != GA_ERROR::OK)
|
||||
{
|
||||
return MakeResult(
|
||||
GA_ERROR::NO_CLEARANCE, res->ErrorTitle.GetStringId(), gGameCommandErrorText, gCommonFormatArgs);
|
||||
GA_ERROR::NO_CLEARANCE, res->ErrorTitle.GetStringId(), constructResult->ErrorMessage.GetStringId(),
|
||||
constructResult->ErrorMessageArgs.data());
|
||||
}
|
||||
|
||||
if (gMapGroundFlags & ELEMENT_IS_UNDERWATER)
|
||||
if (constructResult->GroundFlags & ELEMENT_IS_UNDERWATER)
|
||||
{
|
||||
res->Error = GA_ERROR::NO_CLEARANCE;
|
||||
res->ErrorMessage = STR_RIDE_CANT_BUILD_THIS_UNDERWATER;
|
||||
return res;
|
||||
}
|
||||
|
||||
if (gMapGroundFlags & ELEMENT_IS_UNDERGROUND)
|
||||
if (constructResult->GroundFlags & ELEMENT_IS_UNDERGROUND)
|
||||
{
|
||||
res->Error = GA_ERROR::NO_CLEARANCE;
|
||||
res->ErrorMessage = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND;
|
||||
|
||||
@@ -109,9 +109,9 @@ public:
|
||||
return MakeResult(GA_ERROR::UNKNOWN, STR_ERR_CANT_PLACE_PERSON_HERE);
|
||||
}
|
||||
|
||||
if (!peep->Place(TileCoordsXYZ(_loc), false))
|
||||
if (auto res2 = peep->Place(TileCoordsXYZ(_loc), false); res2->Error != GA_ERROR::OK)
|
||||
{
|
||||
return MakeResult(GA_ERROR::UNKNOWN, STR_ERR_CANT_PLACE_PERSON_HERE, gGameCommandErrorText);
|
||||
return res2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -179,9 +179,9 @@ public:
|
||||
break;
|
||||
case PeepPickupType::Place:
|
||||
res->Position = _loc;
|
||||
if (!peep->Place(TileCoordsXYZ(_loc), true))
|
||||
if (auto res2 = peep->Place(TileCoordsXYZ(_loc), true); res2->Error != GA_ERROR::OK)
|
||||
{
|
||||
return MakeResult(GA_ERROR::UNKNOWN, STR_ERR_CANT_PLACE_PERSON_HERE, gGameCommandErrorText);
|
||||
return res2;
|
||||
}
|
||||
CancelConcurrentPickups(peep);
|
||||
break;
|
||||
|
||||
@@ -93,10 +93,11 @@ public:
|
||||
entranceLoc.y += CoordsDirectionDelta[(_loc.direction + 1) & 0x3].y * 2;
|
||||
}
|
||||
|
||||
if (!map_can_construct_at({ entranceLoc, zLow, zHigh }, { 0b1111, 0 }))
|
||||
if (auto res2 = MapCanConstructAt({ entranceLoc, zLow, zHigh }, { 0b1111, 0 }); res2->Error != GA_ERROR::OK)
|
||||
{
|
||||
return std::make_unique<GameActionResult>(
|
||||
GA_ERROR::NO_CLEARANCE, STR_CANT_BUILD_PARK_ENTRANCE_HERE, gGameCommandErrorText, gCommonFormatArgs);
|
||||
GA_ERROR::NO_CLEARANCE, STR_CANT_BUILD_PARK_ENTRANCE_HERE, res2->ErrorMessage.GetStringId(),
|
||||
res2->ErrorMessageArgs.data());
|
||||
}
|
||||
|
||||
// Check that entrance element does not already exist at this location
|
||||
|
||||
@@ -94,9 +94,10 @@ public:
|
||||
zLow = temp;
|
||||
}
|
||||
|
||||
if (!map_can_construct_at({ _coords, zLow, zHigh }, { 0b1111, 0b1111 }))
|
||||
if (auto res2 = MapCanConstructAt({ _coords, zLow, zHigh }, { 0b1111, 0b1111 }); res2->Error != GA_ERROR::OK)
|
||||
{
|
||||
return MakeResult(GA_ERROR::NO_CLEARANCE, STR_NONE, gGameCommandErrorText, gCommonFormatArgs);
|
||||
return MakeResult(
|
||||
GA_ERROR::NO_CLEARANCE, STR_NONE, res2->ErrorMessage.GetStringId(), res2->ErrorMessageArgs.data());
|
||||
}
|
||||
if (surfaceElement->HasTrackThatNeedsWater())
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "../Game.h"
|
||||
#include "../Input.h"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../actions/GameAction.h"
|
||||
#include "../audio/AudioMixer.h"
|
||||
#include "../audio/audio.h"
|
||||
#include "../config/Config.h"
|
||||
@@ -743,8 +744,8 @@ void Peep::PickupAbort(int32_t old_x)
|
||||
gPickupPeepImage = UINT32_MAX;
|
||||
}
|
||||
|
||||
// Returns true when a peep can be dropped at the given location. When apply is set to true the peep gets dropped.
|
||||
bool Peep::Place(const TileCoordsXYZ& location, bool apply)
|
||||
// Returns GA_ERROR::OK when a peep can be dropped at the given location. When apply is set to true the peep gets dropped.
|
||||
std::unique_ptr<GameActionResult> Peep::Place(const TileCoordsXYZ& location, bool apply)
|
||||
{
|
||||
auto* pathElement = map_get_path_element_at(location);
|
||||
TileElement* tileElement = reinterpret_cast<TileElement*>(pathElement);
|
||||
@@ -754,7 +755,7 @@ bool Peep::Place(const TileCoordsXYZ& location, bool apply)
|
||||
}
|
||||
|
||||
if (!tileElement)
|
||||
return false;
|
||||
return std::make_unique<GameActionResult>(GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PERSON_HERE);
|
||||
|
||||
// Set the coordinate of destination to be exactly
|
||||
// in the middle of a tile.
|
||||
@@ -762,18 +763,19 @@ bool Peep::Place(const TileCoordsXYZ& location, bool apply)
|
||||
|
||||
if (!map_is_location_owned(destination))
|
||||
{
|
||||
gGameCommandErrorTitle = STR_ERR_CANT_PLACE_PERSON_HERE;
|
||||
return false;
|
||||
return std::make_unique<GameActionResult>(GA_ERROR::NOT_OWNED, STR_ERR_CANT_PLACE_PERSON_HERE);
|
||||
}
|
||||
|
||||
if (!map_can_construct_at({ destination, destination.z, destination.z + (1 * 8) }, { 0b1111, 0 }))
|
||||
if (auto res = MapCanConstructAt({ destination, destination.z, destination.z + (1 * 8) }, { 0b1111, 0 });
|
||||
res->Error != GA_ERROR::OK)
|
||||
{
|
||||
if (gGameCommandErrorText != STR_RAISE_OR_LOWER_LAND_FIRST)
|
||||
if (res->ErrorMessage.GetStringId() != STR_RAISE_OR_LOWER_LAND_FIRST)
|
||||
{
|
||||
if (gGameCommandErrorText != STR_FOOTPATH_IN_THE_WAY)
|
||||
if (res->ErrorMessage.GetStringId() != STR_FOOTPATH_IN_THE_WAY)
|
||||
{
|
||||
gGameCommandErrorTitle = STR_ERR_CANT_PLACE_PERSON_HERE;
|
||||
return false;
|
||||
return std::make_unique<GameActionResult>(
|
||||
GA_ERROR::NO_CLEARANCE, STR_ERR_CANT_PLACE_PERSON_HERE, res->ErrorMessage.GetStringId(),
|
||||
res->ErrorMessageArgs.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -797,7 +799,7 @@ bool Peep::Place(const TileCoordsXYZ& location, bool apply)
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return std::make_unique<GameActionResult>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,6 +48,7 @@ constexpr auto PEEP_CLEARANCE_HEIGHT = 4 * COORDS_Z_STEP;
|
||||
class Formatter;
|
||||
struct TileElement;
|
||||
struct Ride;
|
||||
class GameActionResult;
|
||||
|
||||
enum PeepType : uint8_t
|
||||
{
|
||||
@@ -769,7 +770,7 @@ public: // Peep
|
||||
void SetNextFlags(uint8_t next_direction, bool is_sloped, bool is_surface);
|
||||
void Pickup();
|
||||
void PickupAbort(int32_t old_x);
|
||||
bool Place(const TileCoordsXYZ& location, bool apply);
|
||||
std::unique_ptr<GameActionResult> Place(const TileCoordsXYZ& location, bool apply);
|
||||
static Peep* Generate(const CoordsXYZ& coords);
|
||||
void RemoveFromQueue();
|
||||
void RemoveFromRide();
|
||||
|
||||
@@ -1218,12 +1218,6 @@ TileElement* tile_element_insert(const CoordsXYZ& loc, int32_t occupiedQuadrants
|
||||
return insertedElement;
|
||||
}
|
||||
|
||||
class ConstructClearResult final : public GameActionResult
|
||||
{
|
||||
public:
|
||||
uint8_t GroundFlags{ 0 };
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0068BB18
|
||||
@@ -1301,7 +1295,7 @@ void map_obstruction_set_error_text(TileElement* tileElement, GameActionResult&
|
||||
* ebp = clearFunc
|
||||
* bl = bl
|
||||
*/
|
||||
static GameActionResult::Ptr map_can_construct_with_clear_at(
|
||||
std::unique_ptr<ConstructClearResult> MapCanConstructWithClearAt(
|
||||
const CoordsXYRangedZ& pos, CLEAR_FUNC clearFunc, QuarterTile quarterTile, uint8_t flags, uint8_t crossingMode)
|
||||
{
|
||||
int32_t northZ, eastZ, baseHeight, southZ, westZ, water_height;
|
||||
@@ -1483,7 +1477,7 @@ bool map_can_construct_with_clear_at(
|
||||
const CoordsXYRangedZ& pos, CLEAR_FUNC clearFunc, QuarterTile quarterTile, uint8_t flags, money32* price,
|
||||
uint8_t crossingMode)
|
||||
{
|
||||
GameActionResult::Ptr res = map_can_construct_with_clear_at(pos, clearFunc, quarterTile, flags, crossingMode);
|
||||
auto res = MapCanConstructWithClearAt(pos, clearFunc, quarterTile, flags, crossingMode);
|
||||
if (auto message = res->ErrorMessage.AsStringId())
|
||||
gGameCommandErrorText = *message;
|
||||
else
|
||||
@@ -1493,12 +1487,8 @@ bool map_can_construct_with_clear_at(
|
||||
{
|
||||
*price += res->Cost;
|
||||
}
|
||||
auto ccr = dynamic_cast<ConstructClearResult*>(res.get());
|
||||
if (ccr == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
gMapGroundFlags = ccr->GroundFlags;
|
||||
|
||||
gMapGroundFlags = res->GroundFlags;
|
||||
return res->Error == GA_ERROR::OK;
|
||||
}
|
||||
|
||||
@@ -1511,6 +1501,10 @@ int32_t map_can_construct_at(const CoordsXYRangedZ& pos, QuarterTile bl)
|
||||
return map_can_construct_with_clear_at(pos, nullptr, bl, 0, nullptr, CREATE_CROSSING_MODE_NONE);
|
||||
}
|
||||
|
||||
std::unique_ptr<ConstructClearResult> MapCanConstructAt(const CoordsXYRangedZ& pos, QuarterTile bl)
|
||||
{
|
||||
return MapCanConstructWithClearAt(pos, nullptr, bl, 0, CREATE_CROSSING_MODE_NONE);
|
||||
}
|
||||
/**
|
||||
* Updates grass length, scenery age and jumping fountains.
|
||||
*
|
||||
|
||||
@@ -187,6 +187,9 @@ void map_reorganise_elements();
|
||||
bool map_check_free_elements_and_reorganise(int32_t num_elements);
|
||||
TileElement* tile_element_insert(const CoordsXYZ& loc, int32_t occupiedQuadrants);
|
||||
|
||||
class GameActionResult;
|
||||
class ConstructClearResult;
|
||||
|
||||
using CLEAR_FUNC = int32_t (*)(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price);
|
||||
|
||||
int32_t map_place_non_scenery_clear_func(TileElement** tile_element, const CoordsXY& coords, uint8_t flags, money32* price);
|
||||
@@ -194,6 +197,9 @@ int32_t map_place_scenery_clear_func(TileElement** tile_element, const CoordsXY&
|
||||
bool map_can_construct_with_clear_at(
|
||||
const CoordsXYRangedZ& pos, CLEAR_FUNC clearFunc, QuarterTile quarterTile, uint8_t flags, money32* price,
|
||||
uint8_t crossingMode);
|
||||
std::unique_ptr<ConstructClearResult> MapCanConstructWithClearAt(
|
||||
const CoordsXYRangedZ& pos, CLEAR_FUNC clearFunc, QuarterTile quarterTile, uint8_t flags, uint8_t crossingMode);
|
||||
std::unique_ptr<ConstructClearResult> MapCanConstructAt(const CoordsXYRangedZ& pos, QuarterTile bl);
|
||||
int32_t map_can_construct_at(const CoordsXYRangedZ& pos, QuarterTile bl);
|
||||
|
||||
struct tile_element_iterator
|
||||
@@ -253,7 +259,6 @@ TileElement* map_get_track_element_at_from_ride(const CoordsXYZ& trackPos, ride_
|
||||
TileElement* map_get_track_element_at_with_direction_from_ride(const CoordsXYZD& trackPos, ride_id_t rideIndex);
|
||||
|
||||
bool map_is_location_at_edge(const CoordsXY& loc);
|
||||
class GameActionResult;
|
||||
void map_obstruction_set_error_text(TileElement* tileElement, GameActionResult& res);
|
||||
|
||||
uint16_t check_max_allowable_land_rights_for_tile(const CoordsXYZ& tileMapPos);
|
||||
|
||||
Reference in New Issue
Block a user