mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-20 05:23:04 +01:00
Merge pull request #8902 from duncanspumpkin/land_smooth_ga
Land Smooth Game Action
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
#include <openrct2/actions/FootpathSceneryPlaceAction.hpp>
|
||||
#include <openrct2/actions/LandLowerAction.hpp>
|
||||
#include <openrct2/actions/LandRaiseAction.hpp>
|
||||
#include <openrct2/actions/LandSmoothAction.hpp>
|
||||
#include <openrct2/actions/LoadOrQuitAction.hpp>
|
||||
#include <openrct2/actions/PauseToggleAction.hpp>
|
||||
#include <openrct2/actions/SmallSceneryPlaceAction.hpp>
|
||||
@@ -2892,13 +2893,15 @@ static money32 selection_raise_land(uint8_t flags)
|
||||
centreX += 16;
|
||||
centreY += 16;
|
||||
|
||||
uint32_t xBounds = (gMapSelectPositionA.x & 0xFFFF) | (gMapSelectPositionB.x << 16);
|
||||
uint32_t yBounds = (gMapSelectPositionA.y & 0xFFFF) | (gMapSelectPositionB.y << 16);
|
||||
|
||||
gGameCommandErrorTitle = STR_CANT_RAISE_LAND_HERE;
|
||||
if (gLandMountainMode)
|
||||
{
|
||||
return game_do_command(centreX, flags, centreY, xBounds, GAME_COMMAND_EDIT_LAND_SMOOTH, gMapSelectType, yBounds);
|
||||
auto landSmoothAction = LandSmoothAction(
|
||||
{ centreX, centreY },
|
||||
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType,
|
||||
false);
|
||||
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landSmoothAction)
|
||||
: GameActions::Query(&landSmoothAction);
|
||||
return res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2923,14 +2926,15 @@ static money32 selection_lower_land(uint8_t flags)
|
||||
centreX += 16;
|
||||
centreY += 16;
|
||||
|
||||
uint32_t xBounds = (gMapSelectPositionA.x & 0xFFFF) | (gMapSelectPositionB.x << 16);
|
||||
uint32_t yBounds = (gMapSelectPositionA.y & 0xFFFF) | (gMapSelectPositionB.y << 16);
|
||||
|
||||
gGameCommandErrorTitle = STR_CANT_LOWER_LAND_HERE;
|
||||
if (gLandMountainMode)
|
||||
{
|
||||
return game_do_command(
|
||||
centreX, flags, centreY, xBounds, GAME_COMMAND_EDIT_LAND_SMOOTH, 0x8000 + gMapSelectType, yBounds);
|
||||
auto landSmoothAction = LandSmoothAction(
|
||||
{ centreX, centreY },
|
||||
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y }, gMapSelectType,
|
||||
true);
|
||||
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&landSmoothAction)
|
||||
: GameActions::Query(&landSmoothAction);
|
||||
return res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1268,7 +1268,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
|
||||
game_command_set_staff_name,
|
||||
nullptr,
|
||||
nullptr,
|
||||
game_command_smooth_land,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
|
||||
@@ -44,16 +44,16 @@ enum GAME_COMMAND
|
||||
GAME_COMMAND_SET_STAFF_NAME, // GA
|
||||
GAME_COMMAND_RAISE_LAND, // GA
|
||||
GAME_COMMAND_LOWER_LAND, // GA
|
||||
GAME_COMMAND_EDIT_LAND_SMOOTH,
|
||||
GAME_COMMAND_RAISE_WATER, // GA
|
||||
GAME_COMMAND_LOWER_WATER, // GA
|
||||
GAME_COMMAND_SET_BRAKES_SPEED, // GA
|
||||
GAME_COMMAND_HIRE_NEW_STAFF_MEMBER, // GA
|
||||
GAME_COMMAND_SET_STAFF_PATROL, // GA
|
||||
GAME_COMMAND_FIRE_STAFF_MEMBER, // GA
|
||||
GAME_COMMAND_SET_STAFF_ORDERS, // GA
|
||||
GAME_COMMAND_SET_PARK_NAME, // GA
|
||||
GAME_COMMAND_SET_PARK_OPEN, // GA
|
||||
GAME_COMMAND_EDIT_LAND_SMOOTH, // GA
|
||||
GAME_COMMAND_RAISE_WATER, // GA
|
||||
GAME_COMMAND_LOWER_WATER, // GA
|
||||
GAME_COMMAND_SET_BRAKES_SPEED, // GA
|
||||
GAME_COMMAND_HIRE_NEW_STAFF_MEMBER, // GA
|
||||
GAME_COMMAND_SET_STAFF_PATROL, // GA
|
||||
GAME_COMMAND_FIRE_STAFF_MEMBER, // GA
|
||||
GAME_COMMAND_SET_STAFF_ORDERS, // GA
|
||||
GAME_COMMAND_SET_PARK_NAME, // GA
|
||||
GAME_COMMAND_SET_PARK_OPEN, // GA
|
||||
GAME_COMMAND_BUY_LAND_RIGHTS,
|
||||
GAME_COMMAND_PLACE_PARK_ENTRANCE, // GA
|
||||
GAME_COMMAND_REMOVE_PARK_ENTRANCE, // GA
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "LandLowerAction.hpp"
|
||||
#include "LandRaiseAction.hpp"
|
||||
#include "LandSetHeightAction.hpp"
|
||||
#include "LandSmoothAction.hpp"
|
||||
#include "LargeSceneryRemoveAction.hpp"
|
||||
#include "LoadOrQuitAction.hpp"
|
||||
#include "MazeSetTrackAction.hpp"
|
||||
@@ -115,6 +116,7 @@ namespace GameActions
|
||||
Register<LandLowerAction>();
|
||||
Register<LandRaiseAction>();
|
||||
Register<LandSetHeightAction>();
|
||||
Register<LandSmoothAction>();
|
||||
Register<TrackPlaceAction>();
|
||||
Register<TrackRemoveAction>();
|
||||
Register<TrackSetBrakeSpeedAction>();
|
||||
|
||||
641
src/openrct2/actions/LandSmoothAction.hpp
Normal file
641
src/openrct2/actions/LandSmoothAction.hpp
Normal file
@@ -0,0 +1,641 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2019 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../actions/LandLowerAction.hpp"
|
||||
#include "../actions/LandRaiseAction.hpp"
|
||||
#include "../actions/LandSetHeightAction.hpp"
|
||||
#include "../audio/audio.h"
|
||||
#include "../interface/Window.h"
|
||||
#include "../localisation/Localisation.h"
|
||||
#include "../localisation/StringIds.h"
|
||||
#include "../management/Finance.h"
|
||||
#include "../ride/RideData.h"
|
||||
#include "../windows/Intent.h"
|
||||
#include "../world/Park.h"
|
||||
#include "../world/Scenery.h"
|
||||
#include "../world/Sprite.h"
|
||||
#include "../world/Surface.h"
|
||||
#include "GameAction.h"
|
||||
|
||||
DEFINE_GAME_ACTION(LandSmoothAction, GAME_COMMAND_EDIT_LAND_SMOOTH, GameActionResult)
|
||||
{
|
||||
private:
|
||||
CoordsXY _coords;
|
||||
MapRange _range;
|
||||
uint8_t _selectionType{ 0 };
|
||||
bool _isLowering{ false };
|
||||
|
||||
constexpr static rct_string_id _ErrorTitles[] = { STR_CANT_LOWER_LAND_HERE, STR_CANT_RAISE_LAND_HERE };
|
||||
|
||||
public:
|
||||
LandSmoothAction()
|
||||
{
|
||||
}
|
||||
LandSmoothAction(CoordsXY coords, MapRange range, uint8_t selectionType, bool isLowering)
|
||||
: _coords(coords)
|
||||
, _range(range)
|
||||
, _selectionType(selectionType)
|
||||
, _isLowering(isLowering)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t GetActionFlags() const override
|
||||
{
|
||||
return GameAction::GetActionFlags();
|
||||
}
|
||||
|
||||
void Serialise(DataSerialiser & stream) override
|
||||
{
|
||||
GameAction::Serialise(stream);
|
||||
|
||||
stream << DS_TAG(_coords) << DS_TAG(_range) << DS_TAG(_selectionType) << DS_TAG(_isLowering);
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Query() const override
|
||||
{
|
||||
return SmoothLand(false);
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Execute() const override
|
||||
{
|
||||
return SmoothLand(true);
|
||||
}
|
||||
|
||||
private:
|
||||
GameActionResult::Ptr SmoothLandTile(int32_t direction, bool isExecuting, int32_t x, int32_t y, TileElement* surfaceElement)
|
||||
const
|
||||
{
|
||||
int32_t targetBaseZ = surfaceElement->base_height;
|
||||
int32_t slope = surfaceElement->AsSurface()->GetSlope();
|
||||
if (_isLowering)
|
||||
{
|
||||
slope = tile_element_lower_styles[direction][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ -= 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
slope = tile_element_raise_styles[direction][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ += 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
auto landSetHeightAction = LandSetHeightAction({ x, y }, targetBaseZ, slope);
|
||||
landSetHeightAction.SetFlags(GetFlags());
|
||||
auto res = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction)
|
||||
: GameActions::QueryNested(&landSetHeightAction);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
money32 SmoothLandRowByEdge(
|
||||
bool isExecuting, CoordsXY loc, int32_t expectedLandHeight1, int32_t expectedLandHeight2, int32_t stepX, int32_t stepY,
|
||||
int32_t direction1, int32_t direction2, int32_t checkDirection1, int32_t checkDirection2) const
|
||||
{
|
||||
uint8_t shouldContinue = 0xF;
|
||||
int32_t landChangePerTile = _isLowering ? 2 : -2;
|
||||
money32 totalCost = 0;
|
||||
|
||||
// check if we need to start at all
|
||||
if (!map_is_location_valid(loc) || !map_is_location_valid({ loc.x + stepX, loc.y + stepY }))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
auto surfaceElement = map_get_surface_element_at(loc);
|
||||
auto nextSurfaceElement = map_get_surface_element_at({ loc.x + stepX, loc.y + stepY });
|
||||
if (surfaceElement == nullptr || nextSurfaceElement == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (tile_element_get_corner_height(surfaceElement, checkDirection1) != expectedLandHeight1 + landChangePerTile)
|
||||
{
|
||||
shouldContinue &= ~0x1;
|
||||
}
|
||||
if (tile_element_get_corner_height(surfaceElement, checkDirection2) != expectedLandHeight2 + landChangePerTile)
|
||||
{
|
||||
shouldContinue &= ~0x2;
|
||||
}
|
||||
if (tile_element_get_corner_height(surfaceElement, checkDirection1)
|
||||
!= tile_element_get_corner_height(nextSurfaceElement, direction1))
|
||||
{
|
||||
shouldContinue &= ~0x1;
|
||||
}
|
||||
if (tile_element_get_corner_height(surfaceElement, checkDirection2)
|
||||
!= tile_element_get_corner_height(nextSurfaceElement, direction2))
|
||||
{
|
||||
shouldContinue &= ~0x2;
|
||||
}
|
||||
auto nextLoc = loc;
|
||||
while ((shouldContinue & 0x3) != 0)
|
||||
{
|
||||
shouldContinue = ((shouldContinue << 2) | 0x3) & shouldContinue;
|
||||
nextLoc.x += stepX;
|
||||
nextLoc.y += stepY;
|
||||
// check if we need to continue after raising the current tile
|
||||
// this needs to be checked before the tile is changed
|
||||
if (!map_is_location_valid({ nextLoc.x + stepX, nextLoc.y + stepY }))
|
||||
{
|
||||
shouldContinue &= ~0x3;
|
||||
}
|
||||
else
|
||||
{
|
||||
surfaceElement = nextSurfaceElement;
|
||||
nextSurfaceElement = map_get_surface_element_at({ nextLoc.x + stepX, nextLoc.y + stepY });
|
||||
if (nextSurfaceElement == nullptr)
|
||||
{
|
||||
shouldContinue &= ~0x3;
|
||||
}
|
||||
if (tile_element_get_corner_height(surfaceElement, direction1) + landChangePerTile
|
||||
!= tile_element_get_corner_height(surfaceElement, checkDirection1))
|
||||
{
|
||||
shouldContinue &= ~0x1;
|
||||
}
|
||||
if (tile_element_get_corner_height(surfaceElement, direction2) + landChangePerTile
|
||||
!= tile_element_get_corner_height(surfaceElement, checkDirection2))
|
||||
{
|
||||
shouldContinue &= ~0x2;
|
||||
}
|
||||
if ((shouldContinue & 0x1)
|
||||
&& tile_element_get_corner_height(surfaceElement, checkDirection1)
|
||||
!= tile_element_get_corner_height(nextSurfaceElement, direction1))
|
||||
{
|
||||
shouldContinue &= ~0x1;
|
||||
}
|
||||
if ((shouldContinue & 0x2)
|
||||
&& tile_element_get_corner_height(surfaceElement, checkDirection2)
|
||||
!= tile_element_get_corner_height(nextSurfaceElement, direction2))
|
||||
{
|
||||
shouldContinue &= ~0x2;
|
||||
}
|
||||
}
|
||||
expectedLandHeight1 += landChangePerTile;
|
||||
|
||||
// change land of current tile
|
||||
int32_t targetBaseZ = surfaceElement->base_height;
|
||||
int32_t slope = surfaceElement->AsSurface()->GetSlope();
|
||||
int32_t oldSlope = slope;
|
||||
if (_isLowering)
|
||||
{
|
||||
if (shouldContinue & 0x4)
|
||||
{
|
||||
slope = tile_element_lower_styles[direction1][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ -= 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
if ((shouldContinue & 0x8)
|
||||
&& map_get_corner_height(surfaceElement->base_height, oldSlope, direction2)
|
||||
== map_get_corner_height(targetBaseZ, slope, direction2))
|
||||
{
|
||||
slope = tile_element_lower_styles[direction2][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ -= 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shouldContinue & 0x4)
|
||||
{
|
||||
slope = tile_element_raise_styles[direction1][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ += 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
if ((shouldContinue & 0x8)
|
||||
&& map_get_corner_height(surfaceElement->base_height, oldSlope, direction2)
|
||||
== map_get_corner_height(targetBaseZ, slope, direction2))
|
||||
{
|
||||
slope = tile_element_raise_styles[direction2][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ += 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto landSetHeightAction = LandSetHeightAction(nextLoc, targetBaseZ, slope);
|
||||
landSetHeightAction.SetFlags(GetFlags());
|
||||
auto res = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction)
|
||||
: GameActions::QueryNested(&landSetHeightAction);
|
||||
if (res->Error == GA_ERROR::OK)
|
||||
{
|
||||
totalCost += res->Cost;
|
||||
}
|
||||
}
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
money32 SmoothLandRowByCorner(
|
||||
bool isExecuting, CoordsXY loc, int32_t expectedLandHeight, int32_t stepX, int32_t stepY, int32_t direction,
|
||||
int32_t checkDirection) const
|
||||
{
|
||||
bool shouldContinue = true;
|
||||
money32 totalCost = 0;
|
||||
int32_t landChangePerTile;
|
||||
if (stepX == 0 || stepY == 0)
|
||||
{
|
||||
landChangePerTile = _isLowering ? 2 : -2;
|
||||
}
|
||||
else
|
||||
{
|
||||
landChangePerTile = _isLowering ? 4 : -4;
|
||||
}
|
||||
|
||||
// check if we need to start at all
|
||||
if (!map_is_location_valid(loc) || !map_is_location_valid({ loc.x + stepX, loc.y + stepY }))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
auto surfaceElement = map_get_surface_element_at(loc);
|
||||
auto nextSurfaceElement = map_get_surface_element_at({ loc.x + stepX, loc.y + stepY });
|
||||
if (surfaceElement == nullptr || nextSurfaceElement == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (tile_element_get_corner_height(surfaceElement, checkDirection) != expectedLandHeight + (_isLowering ? 2 : -2))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (tile_element_get_corner_height(surfaceElement, checkDirection)
|
||||
!= tile_element_get_corner_height(nextSurfaceElement, direction))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto nextLoc = loc;
|
||||
while (shouldContinue)
|
||||
{
|
||||
nextLoc.x += stepX;
|
||||
nextLoc.y += stepY;
|
||||
// check if we need to continue after raising the current tile
|
||||
// this needs to be checked before the tile is changed
|
||||
if (!map_is_location_valid({ nextLoc.x + stepX, nextLoc.y + stepY }))
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
surfaceElement = nextSurfaceElement;
|
||||
nextSurfaceElement = map_get_surface_element_at({ nextLoc.x + stepX, nextLoc.y + stepY });
|
||||
if (nextSurfaceElement == nullptr)
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
if (tile_element_get_corner_height(surfaceElement, direction) + landChangePerTile
|
||||
!= tile_element_get_corner_height(surfaceElement, checkDirection))
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
if (shouldContinue
|
||||
&& tile_element_get_corner_height(surfaceElement, checkDirection)
|
||||
!= tile_element_get_corner_height(nextSurfaceElement, direction))
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
}
|
||||
if (stepX * stepY != 0)
|
||||
{
|
||||
totalCost += SmoothLandRowByCorner(
|
||||
isExecuting, nextLoc, expectedLandHeight + (landChangePerTile / 2), 0, stepY, direction,
|
||||
checkDirection ^ 3);
|
||||
totalCost += SmoothLandRowByCorner(
|
||||
isExecuting, nextLoc, expectedLandHeight + (landChangePerTile / 2), stepX, 0, direction,
|
||||
checkDirection ^ 1);
|
||||
}
|
||||
expectedLandHeight += landChangePerTile;
|
||||
// change land of current tile
|
||||
auto result = SmoothLandTile(direction, isExecuting, nextLoc.x, nextLoc.y, surfaceElement);
|
||||
if (result->Error == GA_ERROR::OK)
|
||||
{
|
||||
totalCost += result->Cost;
|
||||
}
|
||||
}
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
GameActionResult::Ptr SmoothLand(bool isExecuting) const
|
||||
{
|
||||
const bool raiseLand = !_isLowering;
|
||||
const int32_t selectionType = _selectionType;
|
||||
const int32_t heightOffset = raiseLand ? 2 : -2;
|
||||
|
||||
auto normRange = _range.Normalise();
|
||||
// Cap bounds to map
|
||||
auto l = std::max(normRange.GetLeft(), 32);
|
||||
auto t = std::max(normRange.GetTop(), 32);
|
||||
auto r = std::clamp(normRange.GetRight(), 0, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32);
|
||||
auto b = std::clamp(normRange.GetBottom(), 0, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32);
|
||||
auto validRange = MapRange{ l, t, r, b };
|
||||
|
||||
int32_t centreZ = tile_element_height(_coords.x, _coords.y) & 0xFFFF;
|
||||
|
||||
auto res = MakeResult();
|
||||
res->ErrorTitle = _ErrorTitles[_isLowering ? 0 : 1];
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
res->Position = { _coords.x, _coords.y, centreZ };
|
||||
|
||||
// Do the smoothing
|
||||
switch (selectionType)
|
||||
{
|
||||
case MAP_SELECT_TYPE_FULL:
|
||||
{
|
||||
uint8_t minHeight = heightOffset
|
||||
+ map_get_lowest_land_height(
|
||||
validRange.GetLeft(), validRange.GetRight(), validRange.GetTop(),
|
||||
validRange.GetBottom());
|
||||
uint8_t maxHeight = heightOffset
|
||||
+ map_get_highest_land_height(
|
||||
validRange.GetLeft(), validRange.GetRight(), validRange.GetTop(),
|
||||
validRange.GetBottom());
|
||||
|
||||
// Smooth the 4 corners
|
||||
{ // top-left
|
||||
auto surfaceElement = map_get_surface_element_at({ validRange.GetLeft(), validRange.GetTop() });
|
||||
int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 2), minHeight, maxHeight);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2);
|
||||
}
|
||||
{ // bottom-left
|
||||
auto surfaceElement = map_get_surface_element_at({ validRange.GetLeft(), validRange.GetBottom() });
|
||||
int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 3), minHeight, maxHeight);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetBottom() }, z, -32, 32, 1, 3);
|
||||
}
|
||||
{ // bottom-right
|
||||
auto surfaceElement = map_get_surface_element_at({ validRange.GetRight(), validRange.GetBottom() });
|
||||
int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 0), minHeight, maxHeight);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetRight(), validRange.GetBottom() }, z, 32, 32, 2, 0);
|
||||
}
|
||||
{ // top-right
|
||||
auto surfaceElement = map_get_surface_element_at({ validRange.GetRight(), validRange.GetTop() });
|
||||
int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 1), minHeight, maxHeight);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetRight(), validRange.GetTop() }, z, 32, -32, 3, 1);
|
||||
}
|
||||
|
||||
// Smooth the edges
|
||||
TileElement* surfaceElement = nullptr;
|
||||
int32_t z1, z2;
|
||||
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32)
|
||||
{
|
||||
surfaceElement = map_get_surface_element_at({ validRange.GetLeft(), y });
|
||||
z1 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 3), minHeight, maxHeight);
|
||||
z2 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 2), minHeight, maxHeight);
|
||||
res->Cost += SmoothLandRowByEdge(isExecuting, { validRange.GetLeft(), y }, z1, z2, -32, 0, 0, 1, 3, 2);
|
||||
|
||||
surfaceElement = map_get_surface_element_at({ validRange.GetRight(), y });
|
||||
z1 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 1), minHeight, maxHeight);
|
||||
z2 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 0), minHeight, maxHeight);
|
||||
res->Cost += SmoothLandRowByEdge(isExecuting, { validRange.GetRight(), y }, z1, z2, 32, 0, 2, 3, 1, 0);
|
||||
}
|
||||
|
||||
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32)
|
||||
{
|
||||
surfaceElement = map_get_surface_element_at({ x, validRange.GetTop() });
|
||||
z1 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 1), minHeight, maxHeight);
|
||||
z2 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 2), minHeight, maxHeight);
|
||||
res->Cost += SmoothLandRowByEdge(isExecuting, { x, validRange.GetTop() }, z1, z2, 0, -32, 0, 3, 1, 2);
|
||||
|
||||
surfaceElement = map_get_surface_element_at({ x, validRange.GetBottom() });
|
||||
z1 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 0), minHeight, maxHeight);
|
||||
z2 = std::clamp((uint8_t)tile_element_get_corner_height(surfaceElement, 3), minHeight, maxHeight);
|
||||
res->Cost += SmoothLandRowByEdge(isExecuting, { x, validRange.GetBottom() }, z1, z2, 0, 32, 1, 2, 0, 3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MAP_SELECT_TYPE_CORNER_0:
|
||||
case MAP_SELECT_TYPE_CORNER_1:
|
||||
case MAP_SELECT_TYPE_CORNER_2:
|
||||
case MAP_SELECT_TYPE_CORNER_3:
|
||||
{
|
||||
auto surfaceElement = map_get_surface_element_at({ validRange.GetLeft(), validRange.GetTop() });
|
||||
uint8_t newBaseZ = surfaceElement->base_height;
|
||||
uint8_t newSlope = surfaceElement->AsSurface()->GetSlope();
|
||||
|
||||
if (raiseLand)
|
||||
{
|
||||
newSlope = tile_element_raise_styles[selectionType][newSlope];
|
||||
}
|
||||
else
|
||||
{
|
||||
newSlope = tile_element_lower_styles[selectionType][newSlope];
|
||||
}
|
||||
|
||||
if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
newBaseZ += heightOffset;
|
||||
newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
|
||||
// Smooth the corners
|
||||
int32_t z = map_get_corner_height(newBaseZ, newSlope, 2);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 0);
|
||||
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 32, 2, 0);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 3);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 32, 1, 3);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 1);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, -32, 3, 1);
|
||||
|
||||
// Smooth the edges
|
||||
switch (selectionType)
|
||||
{
|
||||
case MAP_SELECT_TYPE_CORNER_0:
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 0);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 3, 0);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 1, 0);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 3);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 0, 3);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 1);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 0, 1);
|
||||
break;
|
||||
case MAP_SELECT_TYPE_CORNER_1:
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 1);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 2, 1);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 0, 1);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 2);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 1, 2);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 0);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 1, 0);
|
||||
break;
|
||||
case MAP_SELECT_TYPE_CORNER_2:
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 2);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 1, 2);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 3, 2);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 1);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 2, 1);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 3);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 2, 3);
|
||||
break;
|
||||
case MAP_SELECT_TYPE_CORNER_3:
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 3);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 0, 3);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 2, 3);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 0);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 3, 0);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 2);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 3, 2);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MAP_SELECT_TYPE_EDGE_0:
|
||||
case MAP_SELECT_TYPE_EDGE_1:
|
||||
case MAP_SELECT_TYPE_EDGE_2:
|
||||
case MAP_SELECT_TYPE_EDGE_3:
|
||||
{
|
||||
// TODO: Handle smoothing by edge
|
||||
// Get the two corners to raise
|
||||
auto surfaceElement = map_get_surface_element_at({ validRange.GetLeft(), validRange.GetTop() });
|
||||
uint8_t newBaseZ = surfaceElement->base_height;
|
||||
uint8_t oldSlope = surfaceElement->AsSurface()->GetSlope();
|
||||
uint8_t newSlope = oldSlope;
|
||||
int32_t rowIndex = selectionType - (MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1);
|
||||
|
||||
if (raiseLand)
|
||||
{
|
||||
newSlope = tile_element_raise_styles[rowIndex][oldSlope];
|
||||
}
|
||||
else
|
||||
{
|
||||
newSlope = tile_element_lower_styles[rowIndex][oldSlope];
|
||||
}
|
||||
|
||||
const bool changeBaseHeight = newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
if (changeBaseHeight)
|
||||
{
|
||||
newBaseZ += heightOffset;
|
||||
newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
|
||||
const uint8_t edge = selectionType - MAP_SELECT_TYPE_EDGE_0;
|
||||
|
||||
// Table with corners for each edge selection. The first two are the selected corners, the latter
|
||||
// two are the opposites
|
||||
static constexpr uint8_t cornerIndices[][4] = {
|
||||
{ 2, 3, 1, 0 }, // MAP_SELECT_TYPE_EDGE_0
|
||||
{ 3, 0, 2, 1 }, // MAP_SELECT_TYPE_EDGE_1
|
||||
{ 0, 1, 3, 2 }, // MAP_SELECT_TYPE_EDGE_2
|
||||
{ 1, 2, 0, 3 }, // MAP_SELECT_TYPE_EDGE_3
|
||||
};
|
||||
// Big coordinate offsets for the neigbouring tile for the given edge selection
|
||||
static constexpr sLocationXY8 stepOffsets[] = {
|
||||
{ -32, 0 },
|
||||
{ 0, 32 },
|
||||
{ 32, 0 },
|
||||
{ 0, -32 },
|
||||
};
|
||||
|
||||
// Smooth higher and lower edges
|
||||
uint8_t c1 = cornerIndices[edge][0];
|
||||
uint8_t c2 = cornerIndices[edge][1];
|
||||
uint8_t c3 = cornerIndices[edge][2];
|
||||
uint8_t c4 = cornerIndices[edge][3];
|
||||
uint8_t z1 = map_get_corner_height(newBaseZ, newSlope, c1);
|
||||
uint8_t z2 = map_get_corner_height(newBaseZ, newSlope, c2);
|
||||
uint8_t z3 = map_get_corner_height(newBaseZ, newSlope, c3);
|
||||
uint8_t z4 = map_get_corner_height(newBaseZ, newSlope, c4);
|
||||
// Smooth the edge at the top of the new slope
|
||||
res->Cost += SmoothLandRowByEdge(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z1, z2, stepOffsets[edge].x,
|
||||
stepOffsets[edge].y, c3, c4, c1, c2);
|
||||
// Smooth the edge at the bottom of the new slope
|
||||
res->Cost += SmoothLandRowByEdge(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z3, z4, -stepOffsets[edge].x,
|
||||
-stepOffsets[edge].y, c1, c2, c3, c4);
|
||||
|
||||
// Smooth corners
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z1, -stepOffsets[edge].y, stepOffsets[edge].x,
|
||||
c2, c1);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z2, stepOffsets[edge].y, -stepOffsets[edge].x,
|
||||
c1, c2);
|
||||
int32_t z = map_get_corner_height(newBaseZ, newSlope, 2);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 0);
|
||||
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 32, 2, 0);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 3);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 32, 1, 3);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 1);
|
||||
res->Cost += SmoothLandRowByCorner(
|
||||
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, -32, 3, 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_error("Invalid map selection %u", _selectionType);
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, res->ErrorTitle);
|
||||
} // switch selectionType
|
||||
|
||||
// Raise / lower the land tool selection area
|
||||
GameActionResult::Ptr result;
|
||||
if (raiseLand)
|
||||
{
|
||||
auto raiseLandAction = LandRaiseAction({ _coords.x, _coords.y }, validRange, selectionType);
|
||||
raiseLandAction.SetFlags(GetFlags());
|
||||
result = isExecuting ? GameActions::ExecuteNested(&raiseLandAction) : GameActions::QueryNested(&raiseLandAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lowerLandAction = LandLowerAction({ _coords.x, _coords.y }, validRange, selectionType);
|
||||
lowerLandAction.SetFlags(GetFlags());
|
||||
result = isExecuting ? GameActions::ExecuteNested(&lowerLandAction) : GameActions::QueryNested(&lowerLandAction);
|
||||
}
|
||||
if (result->Error != GA_ERROR::OK)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (isExecuting)
|
||||
{
|
||||
audio_play_sound_at_location(SOUND_PLACE_ITEM, _coords.x, _coords.y, centreZ);
|
||||
}
|
||||
res->Cost += result->Cost;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
@@ -31,7 +31,7 @@
|
||||
// This string specifies which version of network stream current build uses.
|
||||
// It is used for making sure only compatible builds get connected, even within
|
||||
// single OpenRCT2 version.
|
||||
#define NETWORK_STREAM_VERSION "12"
|
||||
#define NETWORK_STREAM_VERSION "13"
|
||||
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
||||
|
||||
static Peep* _pickup_peep = nullptr;
|
||||
|
||||
@@ -1080,571 +1080,6 @@ uint8_t map_get_highest_land_height(int32_t xMin, int32_t xMax, int32_t yMin, in
|
||||
return max_height;
|
||||
}
|
||||
|
||||
static money32 smooth_land_tile(
|
||||
int32_t direction, uint8_t flags, int32_t x, int32_t y, TileElement* tileElement, bool raiseLand)
|
||||
{
|
||||
int32_t targetBaseZ = tileElement->base_height;
|
||||
int32_t slope = tileElement->AsSurface()->GetSlope();
|
||||
if (raiseLand)
|
||||
{
|
||||
slope = tile_element_raise_styles[direction][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ += 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
slope = tile_element_lower_styles[direction][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ -= 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
auto landSetHeightAction = LandSetHeightAction({ x, y }, targetBaseZ, slope);
|
||||
landSetHeightAction.SetFlags(flags);
|
||||
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::ExecuteNested(&landSetHeightAction)
|
||||
: GameActions::QueryNested(&landSetHeightAction);
|
||||
|
||||
if (res->Error == GA_ERROR::OK)
|
||||
{
|
||||
return res->Cost;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
static money32 smooth_land_row_by_edge(
|
||||
int32_t flags, int32_t x, int32_t y, int32_t expectedLandHeight1, int32_t expectedLandHeight2, int32_t stepX, int32_t stepY,
|
||||
int32_t direction1, int32_t direction2, int32_t checkDirection1, int32_t checkDirection2, bool raiseLand)
|
||||
{
|
||||
uint8_t shouldContinue = 0xF;
|
||||
int32_t landChangePerTile = raiseLand ? -2 : 2;
|
||||
TileElement *tileElement, *nextTileElement;
|
||||
money32 totalCost = 0;
|
||||
|
||||
// check if we need to start at all
|
||||
if (!map_is_location_valid({ x, y }) || !map_is_location_valid({ x + stepX, y + stepY }))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
tileElement = map_get_surface_element_at({ x, y });
|
||||
nextTileElement = map_get_surface_element_at({ x + stepX, y + stepY });
|
||||
if (tileElement == nullptr || nextTileElement == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (tile_element_get_corner_height(tileElement, checkDirection1) != expectedLandHeight1 + (raiseLand ? -2 : 2))
|
||||
{
|
||||
shouldContinue &= ~0x1;
|
||||
}
|
||||
if (tile_element_get_corner_height(tileElement, checkDirection2) != expectedLandHeight2 + (raiseLand ? -2 : 2))
|
||||
{
|
||||
shouldContinue &= ~0x2;
|
||||
}
|
||||
if (tile_element_get_corner_height(tileElement, checkDirection1)
|
||||
!= tile_element_get_corner_height(nextTileElement, direction1))
|
||||
{
|
||||
shouldContinue &= ~0x1;
|
||||
}
|
||||
if (tile_element_get_corner_height(tileElement, checkDirection2)
|
||||
!= tile_element_get_corner_height(nextTileElement, direction2))
|
||||
{
|
||||
shouldContinue &= ~0x2;
|
||||
}
|
||||
while ((shouldContinue & 0x3) != 0)
|
||||
{
|
||||
shouldContinue = ((shouldContinue << 2) | 0x3) & shouldContinue;
|
||||
x += stepX;
|
||||
y += stepY;
|
||||
// check if we need to continue after raising the current tile
|
||||
// this needs to be checked before the tile is changed
|
||||
if (!map_is_location_valid({ x + stepX, y + stepY }))
|
||||
{
|
||||
shouldContinue &= ~0x3;
|
||||
}
|
||||
else
|
||||
{
|
||||
tileElement = nextTileElement;
|
||||
nextTileElement = map_get_surface_element_at({ x + stepX, y + stepY });
|
||||
if (nextTileElement == nullptr)
|
||||
{
|
||||
shouldContinue &= ~0x3;
|
||||
}
|
||||
if (tile_element_get_corner_height(tileElement, direction1) + landChangePerTile
|
||||
!= tile_element_get_corner_height(tileElement, checkDirection1))
|
||||
{
|
||||
shouldContinue &= ~0x1;
|
||||
}
|
||||
if (tile_element_get_corner_height(tileElement, direction2) + landChangePerTile
|
||||
!= tile_element_get_corner_height(tileElement, checkDirection2))
|
||||
{
|
||||
shouldContinue &= ~0x2;
|
||||
}
|
||||
if ((shouldContinue & 0x1)
|
||||
&& tile_element_get_corner_height(tileElement, checkDirection1)
|
||||
!= tile_element_get_corner_height(nextTileElement, direction1))
|
||||
{
|
||||
shouldContinue &= ~0x1;
|
||||
}
|
||||
if ((shouldContinue & 0x2)
|
||||
&& tile_element_get_corner_height(tileElement, checkDirection2)
|
||||
!= tile_element_get_corner_height(nextTileElement, direction2))
|
||||
{
|
||||
shouldContinue &= ~0x2;
|
||||
}
|
||||
}
|
||||
expectedLandHeight1 += landChangePerTile;
|
||||
|
||||
// change land of current tile
|
||||
int32_t targetBaseZ = tileElement->base_height;
|
||||
int32_t slope = tileElement->AsSurface()->GetSlope();
|
||||
int32_t oldSlope = slope;
|
||||
if (raiseLand)
|
||||
{
|
||||
if (shouldContinue & 0x4)
|
||||
{
|
||||
slope = tile_element_raise_styles[direction1][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ += 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
if ((shouldContinue & 0x8)
|
||||
&& map_get_corner_height(tileElement->base_height, oldSlope, direction2)
|
||||
== map_get_corner_height(targetBaseZ, slope, direction2))
|
||||
{
|
||||
slope = tile_element_raise_styles[direction2][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ += 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shouldContinue & 0x4)
|
||||
{
|
||||
slope = tile_element_lower_styles[direction1][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ -= 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
if ((shouldContinue & 0x8)
|
||||
&& map_get_corner_height(tileElement->base_height, oldSlope, direction2)
|
||||
== map_get_corner_height(targetBaseZ, slope, direction2))
|
||||
{
|
||||
slope = tile_element_lower_styles[direction2][slope];
|
||||
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
targetBaseZ -= 2;
|
||||
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto landSetHeightAction = LandSetHeightAction({ x, y }, targetBaseZ, slope);
|
||||
landSetHeightAction.SetFlags(flags);
|
||||
auto res = (flags & GAME_COMMAND_FLAG_APPLY) ? GameActions::ExecuteNested(&landSetHeightAction)
|
||||
: GameActions::QueryNested(&landSetHeightAction);
|
||||
if (res->Error == GA_ERROR::OK)
|
||||
{
|
||||
totalCost += res->Cost;
|
||||
}
|
||||
}
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
static money32 smooth_land_row_by_corner(
|
||||
int32_t flags, int32_t x, int32_t y, int32_t expectedLandHeight, int32_t stepX, int32_t stepY, int32_t direction,
|
||||
int32_t checkDirection, bool raiseLand)
|
||||
{
|
||||
bool shouldContinue = true;
|
||||
TileElement *tileElement, *nextTileElement;
|
||||
money32 totalCost = 0;
|
||||
money32 result;
|
||||
int32_t landChangePerTile;
|
||||
if (stepX == 0 || stepY == 0)
|
||||
{
|
||||
landChangePerTile = raiseLand ? -2 : 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
landChangePerTile = raiseLand ? -4 : 4;
|
||||
}
|
||||
|
||||
// check if we need to start at all
|
||||
if (!map_is_location_valid({ x, y }) || !map_is_location_valid({ x + stepX, y + stepY }))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
tileElement = map_get_surface_element_at({ x, y });
|
||||
nextTileElement = map_get_surface_element_at((x + stepX) >> 5, (y + stepY) >> 5);
|
||||
if (tileElement == nullptr || nextTileElement == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (tile_element_get_corner_height(tileElement, checkDirection) != expectedLandHeight + (raiseLand ? -2 : 2))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (tile_element_get_corner_height(tileElement, checkDirection)
|
||||
!= tile_element_get_corner_height(nextTileElement, direction))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
while (shouldContinue)
|
||||
{
|
||||
x += stepX;
|
||||
y += stepY;
|
||||
// check if we need to continue after raising the current tile
|
||||
// this needs to be checked before the tile is changed
|
||||
if (!map_is_location_valid({ x + stepX, y + stepY }))
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
tileElement = nextTileElement;
|
||||
nextTileElement = map_get_surface_element_at((x + stepX) >> 5, (y + stepY) >> 5);
|
||||
if (nextTileElement == nullptr)
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
if (tile_element_get_corner_height(tileElement, direction) + landChangePerTile
|
||||
!= tile_element_get_corner_height(tileElement, checkDirection))
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
if (shouldContinue
|
||||
&& tile_element_get_corner_height(tileElement, checkDirection)
|
||||
!= tile_element_get_corner_height(nextTileElement, direction))
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
}
|
||||
if (stepX * stepY != 0)
|
||||
{
|
||||
totalCost += smooth_land_row_by_corner(
|
||||
flags, x, y, expectedLandHeight + (landChangePerTile / 2), 0, stepY, direction, checkDirection ^ 3, raiseLand);
|
||||
totalCost += smooth_land_row_by_corner(
|
||||
flags, x, y, expectedLandHeight + (landChangePerTile / 2), stepX, 0, direction, checkDirection ^ 1, raiseLand);
|
||||
}
|
||||
expectedLandHeight += landChangePerTile;
|
||||
// change land of current tile
|
||||
result = smooth_land_tile(direction, flags, x, y, tileElement, raiseLand);
|
||||
if (result != MONEY32_UNDEFINED)
|
||||
{
|
||||
totalCost += result;
|
||||
}
|
||||
}
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
static money32 smooth_land(
|
||||
int32_t flags, int32_t centreX, int32_t centreY, int32_t mapLeft, int32_t mapTop, int32_t mapRight, int32_t mapBottom,
|
||||
int32_t command)
|
||||
{
|
||||
// break up information in command
|
||||
const bool raiseLand = command < 0x7FFF;
|
||||
const int32_t selectionType = command & 0x7FFF;
|
||||
const int32_t heightOffset = raiseLand ? 2 : -2;
|
||||
|
||||
// Cap bounds to map
|
||||
mapLeft = std::max(mapLeft, 32);
|
||||
mapTop = std::max(mapTop, 32);
|
||||
mapRight = std::clamp(mapRight, 0, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32);
|
||||
mapBottom = std::clamp(mapBottom, 0, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32);
|
||||
|
||||
// Play sound (only once)
|
||||
int32_t centreZ = tile_element_height(centreX, centreY);
|
||||
if ((flags & GAME_COMMAND_FLAG_APPLY) && gGameCommandNestLevel == 1)
|
||||
{
|
||||
audio_play_sound_at_location(SOUND_PLACE_ITEM, centreX, centreY, centreZ);
|
||||
}
|
||||
|
||||
if (flags & GAME_COMMAND_FLAG_APPLY)
|
||||
{
|
||||
LocationXYZ16 coord;
|
||||
coord.x = centreX + 16;
|
||||
coord.y = centreY + 16;
|
||||
coord.z = tile_element_height(coord.x, coord.y);
|
||||
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
|
||||
}
|
||||
|
||||
// Do the smoothing
|
||||
money32 totalCost = 0;
|
||||
switch (selectionType)
|
||||
{
|
||||
case MAP_SELECT_TYPE_FULL:
|
||||
{
|
||||
uint8_t minHeight = heightOffset + map_get_lowest_land_height(mapLeft, mapRight, mapTop, mapBottom);
|
||||
uint8_t maxHeight = heightOffset + map_get_highest_land_height(mapLeft, mapRight, mapTop, mapBottom);
|
||||
|
||||
// Smooth the 4 corners
|
||||
{ // top-left
|
||||
TileElement* tileElement = map_get_surface_element_at({ mapLeft, mapTop });
|
||||
int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 2), minHeight, maxHeight);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand);
|
||||
}
|
||||
{ // bottom-left
|
||||
TileElement* tileElement = map_get_surface_element_at(mapLeft >> 5, mapBottom >> 5);
|
||||
int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 3), minHeight, maxHeight);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapBottom, z, -32, 32, 1, 3, raiseLand);
|
||||
}
|
||||
{ // bottom-right
|
||||
TileElement* tileElement = map_get_surface_element_at(mapRight >> 5, mapBottom >> 5);
|
||||
int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 0), minHeight, maxHeight);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapRight, mapBottom, z, 32, 32, 2, 0, raiseLand);
|
||||
}
|
||||
{ // top-right
|
||||
TileElement* tileElement = map_get_surface_element_at(mapRight >> 5, mapTop >> 5);
|
||||
int32_t z = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 1), minHeight, maxHeight);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapRight, mapTop, z, 32, -32, 3, 1, raiseLand);
|
||||
}
|
||||
|
||||
// Smooth the edges
|
||||
TileElement* tileElement = nullptr;
|
||||
int32_t z1, z2;
|
||||
for (int32_t y = mapTop; y <= mapBottom; y += 32)
|
||||
{
|
||||
tileElement = map_get_surface_element_at({ mapLeft, y });
|
||||
z1 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 3), minHeight, maxHeight);
|
||||
z2 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 2), minHeight, maxHeight);
|
||||
totalCost += smooth_land_row_by_edge(flags, mapLeft, y, z1, z2, -32, 0, 0, 1, 3, 2, raiseLand);
|
||||
|
||||
tileElement = map_get_surface_element_at({ mapRight, y });
|
||||
z1 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 1), minHeight, maxHeight);
|
||||
z2 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 0), minHeight, maxHeight);
|
||||
totalCost += smooth_land_row_by_edge(flags, mapRight, y, z1, z2, 32, 0, 2, 3, 1, 0, raiseLand);
|
||||
}
|
||||
|
||||
for (int32_t x = mapLeft; x <= mapRight; x += 32)
|
||||
{
|
||||
tileElement = map_get_surface_element_at({ x, mapTop });
|
||||
z1 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 1), minHeight, maxHeight);
|
||||
z2 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 2), minHeight, maxHeight);
|
||||
totalCost += smooth_land_row_by_edge(flags, x, mapTop, z1, z2, 0, -32, 0, 3, 1, 2, raiseLand);
|
||||
|
||||
tileElement = map_get_surface_element_at({ x, mapBottom });
|
||||
z1 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 0), minHeight, maxHeight);
|
||||
z2 = std::clamp((uint8_t)tile_element_get_corner_height(tileElement, 3), minHeight, maxHeight);
|
||||
totalCost += smooth_land_row_by_edge(flags, x, mapBottom, z1, z2, 0, 32, 1, 2, 0, 3, raiseLand);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MAP_SELECT_TYPE_CORNER_0:
|
||||
case MAP_SELECT_TYPE_CORNER_1:
|
||||
case MAP_SELECT_TYPE_CORNER_2:
|
||||
case MAP_SELECT_TYPE_CORNER_3:
|
||||
{
|
||||
TileElement* tileElement = map_get_surface_element_at({ mapLeft, mapTop });
|
||||
uint8_t newBaseZ = tileElement->base_height;
|
||||
uint8_t newSlope = tileElement->AsSurface()->GetSlope();
|
||||
|
||||
if (raiseLand)
|
||||
{
|
||||
newSlope = tile_element_raise_styles[selectionType][newSlope];
|
||||
}
|
||||
else
|
||||
{
|
||||
newSlope = tile_element_lower_styles[selectionType][newSlope];
|
||||
}
|
||||
|
||||
if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
|
||||
{
|
||||
newBaseZ += heightOffset;
|
||||
newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
|
||||
// Smooth the corners
|
||||
int32_t z = map_get_corner_height(newBaseZ, newSlope, 2);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 0);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 32, 2, 0, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 3);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 32, 1, 3, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 1);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, -32, 3, 1, raiseLand);
|
||||
|
||||
// Smooth the edges
|
||||
switch (selectionType)
|
||||
{
|
||||
case MAP_SELECT_TYPE_CORNER_0:
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 0);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 3, 0, raiseLand);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 1, 0, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 3);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 0, 3, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 1);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 0, 1, raiseLand);
|
||||
break;
|
||||
case MAP_SELECT_TYPE_CORNER_1:
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 1);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 2, 1, raiseLand);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 0, 1, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 2);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 1, 2, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 0);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 1, 0, raiseLand);
|
||||
break;
|
||||
case MAP_SELECT_TYPE_CORNER_2:
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 2);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 1, 2, raiseLand);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 3, 2, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 1);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 2, 1, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 3);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 2, 3, raiseLand);
|
||||
break;
|
||||
case MAP_SELECT_TYPE_CORNER_3:
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 3);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 0, 3, raiseLand);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 2, 3, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 0);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 3, 0, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 2);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 3, 2, raiseLand);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MAP_SELECT_TYPE_EDGE_0:
|
||||
case MAP_SELECT_TYPE_EDGE_1:
|
||||
case MAP_SELECT_TYPE_EDGE_2:
|
||||
case MAP_SELECT_TYPE_EDGE_3:
|
||||
{
|
||||
// TODO: Handle smoothing by edge
|
||||
// Get the two corners to raise
|
||||
TileElement* surfaceElement = map_get_surface_element_at({ mapLeft, mapTop });
|
||||
uint8_t newBaseZ = surfaceElement->base_height;
|
||||
uint8_t oldSlope = surfaceElement->AsSurface()->GetSlope();
|
||||
uint8_t newSlope = oldSlope;
|
||||
int32_t rowIndex = selectionType - (MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1);
|
||||
|
||||
if (raiseLand)
|
||||
{
|
||||
newSlope = tile_element_raise_styles[rowIndex][oldSlope];
|
||||
}
|
||||
else
|
||||
{
|
||||
newSlope = tile_element_lower_styles[rowIndex][oldSlope];
|
||||
}
|
||||
|
||||
const bool changeBaseHeight = newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
if (changeBaseHeight)
|
||||
{
|
||||
newBaseZ += heightOffset;
|
||||
newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
|
||||
}
|
||||
|
||||
const uint8_t edge = selectionType - MAP_SELECT_TYPE_EDGE_0;
|
||||
|
||||
// Table with corners for each edge selection. The first two are the selected corners, the latter two are the
|
||||
// opposites
|
||||
static constexpr uint8_t cornerIndices[][4] = {
|
||||
{ 2, 3, 1, 0 }, // MAP_SELECT_TYPE_EDGE_0
|
||||
{ 3, 0, 2, 1 }, // MAP_SELECT_TYPE_EDGE_1
|
||||
{ 0, 1, 3, 2 }, // MAP_SELECT_TYPE_EDGE_2
|
||||
{ 1, 2, 0, 3 }, // MAP_SELECT_TYPE_EDGE_3
|
||||
};
|
||||
// Big coordinate offsets for the neigbouring tile for the given edge selection
|
||||
static constexpr sLocationXY8 stepOffsets[] = {
|
||||
{ -32, 0 },
|
||||
{ 0, 32 },
|
||||
{ 32, 0 },
|
||||
{ 0, -32 },
|
||||
};
|
||||
|
||||
// Smooth higher and lower edges
|
||||
uint8_t c1 = cornerIndices[edge][0];
|
||||
uint8_t c2 = cornerIndices[edge][1];
|
||||
uint8_t c3 = cornerIndices[edge][2];
|
||||
uint8_t c4 = cornerIndices[edge][3];
|
||||
uint8_t z1 = map_get_corner_height(newBaseZ, newSlope, c1);
|
||||
uint8_t z2 = map_get_corner_height(newBaseZ, newSlope, c2);
|
||||
uint8_t z3 = map_get_corner_height(newBaseZ, newSlope, c3);
|
||||
uint8_t z4 = map_get_corner_height(newBaseZ, newSlope, c4);
|
||||
// Smooth the edge at the top of the new slope
|
||||
totalCost += smooth_land_row_by_edge(
|
||||
flags, mapLeft, mapTop, z1, z2, stepOffsets[edge].x, stepOffsets[edge].y, c3, c4, c1, c2, raiseLand);
|
||||
// Smooth the edge at the bottom of the new slope
|
||||
totalCost += smooth_land_row_by_edge(
|
||||
flags, mapLeft, mapTop, z3, z4, -stepOffsets[edge].x, -stepOffsets[edge].y, c1, c2, c3, c4, raiseLand);
|
||||
|
||||
// Smooth corners
|
||||
totalCost += smooth_land_row_by_corner(
|
||||
flags, mapLeft, mapTop, z1, -stepOffsets[edge].y, stepOffsets[edge].x, c2, c1, raiseLand);
|
||||
totalCost += smooth_land_row_by_corner(
|
||||
flags, mapLeft, mapTop, z2, stepOffsets[edge].y, -stepOffsets[edge].x, c1, c2, raiseLand);
|
||||
int32_t z = map_get_corner_height(newBaseZ, newSlope, 2);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 0);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 32, 2, 0, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 3);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 32, 1, 3, raiseLand);
|
||||
z = map_get_corner_height(newBaseZ, newSlope, 1);
|
||||
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, -32, 3, 1, raiseLand);
|
||||
break;
|
||||
}
|
||||
} // switch selectionType
|
||||
|
||||
// Raise / lower the land tool selection area
|
||||
GameActionResult::Ptr res;
|
||||
if (raiseLand)
|
||||
{
|
||||
auto raiseLandAction = LandRaiseAction({ centreX, centreY }, { mapLeft, mapTop, mapRight, mapBottom }, selectionType);
|
||||
raiseLandAction.SetFlags(flags);
|
||||
res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&raiseLandAction)
|
||||
: GameActions::QueryNested(&raiseLandAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lowerLandAction = LandLowerAction({ centreX, centreY }, { mapLeft, mapTop, mapRight, mapBottom }, selectionType);
|
||||
lowerLandAction.SetFlags(flags);
|
||||
res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&lowerLandAction)
|
||||
: GameActions::QueryNested(&lowerLandAction);
|
||||
}
|
||||
if (res->Error != GA_ERROR::OK)
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
totalCost += res->Cost;
|
||||
|
||||
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
gCommandPosition.x = centreX;
|
||||
gCommandPosition.y = centreY;
|
||||
gCommandPosition.z = centreZ;
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0068BC01
|
||||
*/
|
||||
void game_command_smooth_land(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp)
|
||||
{
|
||||
int32_t flags = *ebx & 0xFF;
|
||||
int32_t centreX = *eax & 0xFFFF;
|
||||
int32_t centreY = *ecx & 0xFFFF;
|
||||
int32_t mapLeft = (int16_t)(*edx & 0xFFFF);
|
||||
int32_t mapTop = (int16_t)(*ebp & 0xFFFF);
|
||||
int32_t mapRight = (int16_t)(*edx >> 16);
|
||||
int32_t mapBottom = (int16_t)(*ebp >> 16);
|
||||
int32_t command = *edi;
|
||||
*ebx = smooth_land(flags, centreX, centreY, mapLeft, mapTop, mapRight, mapBottom, command);
|
||||
}
|
||||
|
||||
bool map_is_location_at_edge(int32_t x, int32_t y)
|
||||
{
|
||||
return x < 32 || y < 32 || x >= ((MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32) || y >= ((MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32);
|
||||
|
||||
@@ -202,7 +202,6 @@ void game_command_set_large_scenery_colour(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
void game_command_set_banner_colour(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
void game_command_smooth_land(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
void game_command_place_banner(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
void game_command_place_wall(int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
|
||||
Reference in New Issue
Block a user