mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2024-08-30 05:56:06 +02:00
Merge pull request #8966 from duncanspumpkin/wall_place_ga
Implement wall place game action
This commit is contained in:
commit
6b9e36f929
10 changed files with 753 additions and 610 deletions
|
@ -34,6 +34,7 @@
|
|||
#include <openrct2/actions/PauseToggleAction.hpp>
|
||||
#include <openrct2/actions/SmallSceneryPlaceAction.hpp>
|
||||
#include <openrct2/actions/SurfaceSetStyleAction.hpp>
|
||||
#include <openrct2/actions/WallPlaceAction.hpp>
|
||||
#include <openrct2/actions/WaterLowerAction.hpp>
|
||||
#include <openrct2/actions/WaterRaiseAction.hpp>
|
||||
#include <openrct2/audio/audio.h>
|
||||
|
@ -1805,32 +1806,42 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo
|
|||
|
||||
for (; zAttemptRange != 0; zAttemptRange--)
|
||||
{
|
||||
int32_t flags = (parameter_1 & 0xFF00) | GAME_COMMAND_FLAG_APPLY;
|
||||
auto primaryColour = (parameter_2 >> 8) & 0xFF;
|
||||
auto edges = parameter_2 & 0xFF;
|
||||
auto type = (parameter_1 >> 8) & 0xFF;
|
||||
auto wallPlaceAction = WallPlaceAction(
|
||||
type, { gridX, gridY, gSceneryPlaceZ }, edges, primaryColour, _secondaryColour, _tertiaryColour);
|
||||
|
||||
gDisableErrorWindowSound = true;
|
||||
gGameCommandErrorTitle = STR_CANT_BUILD_PARK_ENTRANCE_HERE;
|
||||
int32_t cost = game_do_command(
|
||||
gridX, flags, gridY, parameter_2, GAME_COMMAND_PLACE_WALL, gSceneryPlaceZ,
|
||||
_secondaryColour | (_tertiaryColour << 8));
|
||||
gDisableErrorWindowSound = false;
|
||||
|
||||
if (cost != MONEY32_UNDEFINED)
|
||||
{
|
||||
window_close_by_class(WC_ERROR);
|
||||
audio_play_sound_at_location(SOUND_PLACE_ITEM, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gGameCommandErrorText == STR_NOT_ENOUGH_CASH_REQUIRES
|
||||
|| gGameCommandErrorText == STR_CAN_ONLY_BUILD_THIS_ON_WATER)
|
||||
auto res = GameActions::Query(&wallPlaceAction);
|
||||
if (res->Error == GA_ERROR::OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
gSceneryPlaceZ += 8;
|
||||
if (res->ErrorMessage == STR_NOT_ENOUGH_CASH_REQUIRES || res->ErrorMessage == STR_CAN_ONLY_BUILD_THIS_ON_WATER)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (zAttemptRange != 1)
|
||||
{
|
||||
gSceneryPlaceZ += 8;
|
||||
}
|
||||
}
|
||||
|
||||
audio_play_sound_at_location(SOUND_ERROR, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z);
|
||||
auto primaryColour = (parameter_2 >> 8) & 0xFF;
|
||||
auto edges = parameter_2 & 0xFF;
|
||||
auto type = (parameter_1 >> 8) & 0xFF;
|
||||
auto wallPlaceAction = WallPlaceAction(
|
||||
type, { gridX, gridY, gSceneryPlaceZ }, edges, primaryColour, _secondaryColour, _tertiaryColour);
|
||||
|
||||
wallPlaceAction.SetCallback([](const GameAction* ga, const GameActionResult* result) {
|
||||
if (result->Error == GA_ERROR::OK)
|
||||
{
|
||||
audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z);
|
||||
}
|
||||
});
|
||||
auto res = GameActions::Execute(&wallPlaceAction);
|
||||
break;
|
||||
}
|
||||
case SCENERY_TYPE_LARGE:
|
||||
|
@ -2495,24 +2506,34 @@ static money32 try_place_ghost_scenery(
|
|||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// Walls
|
||||
// 6e26b0
|
||||
cost = game_do_command(
|
||||
map_tile.x, parameter_1 | 0x69, map_tile.y, parameter_2, GAME_COMMAND_PLACE_WALL, gSceneryPlaceZ,
|
||||
_secondaryColour | (_tertiaryColour << 8));
|
||||
auto primaryColour = (parameter_2 >> 8) & 0xFF;
|
||||
auto edges = parameter_2 & 0xFF;
|
||||
auto type = (parameter_1 >> 8) & 0xFF;
|
||||
auto wallPlaceAction = WallPlaceAction(
|
||||
type, { map_tile.x, map_tile.y, gSceneryPlaceZ }, edges, primaryColour, _secondaryColour, _tertiaryColour);
|
||||
wallPlaceAction.SetFlags(
|
||||
GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_PATH_SCENERY);
|
||||
wallPlaceAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) {
|
||||
if (result->Error != GA_ERROR::OK)
|
||||
return;
|
||||
|
||||
if (cost == MONEY32_UNDEFINED)
|
||||
return cost;
|
||||
gSceneryGhostPosition.x = map_tile.x;
|
||||
gSceneryGhostPosition.y = map_tile.y;
|
||||
gSceneryGhostWallRotation = edges;
|
||||
gSceneryGhostPosition.z = gSceneryTileElement->base_height;
|
||||
|
||||
gSceneryGhostPosition.x = map_tile.x;
|
||||
gSceneryGhostPosition.y = map_tile.y;
|
||||
gSceneryGhostWallRotation = (parameter_2 & 0xFF);
|
||||
gSceneryGhostType |= SCENERY_GHOST_FLAG_2;
|
||||
});
|
||||
|
||||
tileElement = gSceneryTileElement;
|
||||
gSceneryGhostPosition.z = tileElement->base_height;
|
||||
|
||||
gSceneryGhostType |= SCENERY_GHOST_FLAG_2;
|
||||
auto res = GameActions::Execute(&wallPlaceAction);
|
||||
if (res->Error != GA_ERROR::OK)
|
||||
return MONEY32_UNDEFINED;
|
||||
cost = res->Cost;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
// Large Scenery
|
||||
// 6e25a7
|
||||
|
|
|
@ -409,9 +409,8 @@ int32_t game_do_command_p(
|
|||
|
||||
// Remove ghost scenery so it doesn't interfere with incoming network command
|
||||
if ((flags & GAME_COMMAND_FLAG_NETWORKED) && !(flags & GAME_COMMAND_FLAG_GHOST)
|
||||
&& (command == GAME_COMMAND_PLACE_WALL || command == GAME_COMMAND_PLACE_SCENERY
|
||||
|| command == GAME_COMMAND_PLACE_LARGE_SCENERY || command == GAME_COMMAND_PLACE_BANNER
|
||||
|| command == GAME_COMMAND_PLACE_PATH))
|
||||
&& (command == GAME_COMMAND_PLACE_SCENERY || command == GAME_COMMAND_PLACE_LARGE_SCENERY
|
||||
|| command == GAME_COMMAND_PLACE_BANNER || command == GAME_COMMAND_PLACE_PATH))
|
||||
{
|
||||
scenery_remove_ghost_tool_placement();
|
||||
}
|
||||
|
@ -611,9 +610,7 @@ void game_log_multiplayer_command(int command, const int* eax, const int* ebx, c
|
|||
format_string(log_msg, 256, STR_LOG_DEMOLISH_RIDE, args);
|
||||
network_append_server_log(log_msg);
|
||||
}
|
||||
else if (
|
||||
command == GAME_COMMAND_PLACE_WALL || command == GAME_COMMAND_PLACE_LARGE_SCENERY
|
||||
|| command == GAME_COMMAND_PLACE_BANNER)
|
||||
else if (command == GAME_COMMAND_PLACE_LARGE_SCENERY || command == GAME_COMMAND_PLACE_BANNER)
|
||||
{
|
||||
uint8_t flags = *ebx & 0xFF;
|
||||
if (flags & GAME_COMMAND_FLAG_GHOST)
|
||||
|
@ -1284,7 +1281,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
|
|||
game_command_set_maze_track,
|
||||
game_command_set_park_entrance_fee,
|
||||
nullptr,
|
||||
game_command_place_wall,
|
||||
nullptr,
|
||||
nullptr,
|
||||
game_command_place_large_scenery,
|
||||
nullptr,
|
||||
|
|
|
@ -60,8 +60,8 @@ enum GAME_COMMAND
|
|||
GAME_COMMAND_SET_MAZE_TRACK, // GA
|
||||
GAME_COMMAND_SET_PARK_ENTRANCE_FEE, // GA
|
||||
GAME_COMMAND_SET_STAFF_COLOUR, // GA
|
||||
GAME_COMMAND_PLACE_WALL,
|
||||
GAME_COMMAND_REMOVE_WALL, // GA
|
||||
GAME_COMMAND_PLACE_WALL, // GA
|
||||
GAME_COMMAND_REMOVE_WALL, // GA
|
||||
GAME_COMMAND_PLACE_LARGE_SCENERY,
|
||||
GAME_COMMAND_REMOVE_LARGE_SCENERY, // GA
|
||||
GAME_COMMAND_SET_CURRENT_LOAN, // GA
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
#include "TrackPlaceAction.hpp"
|
||||
#include "TrackRemoveAction.hpp"
|
||||
#include "TrackSetBrakeSpeedAction.hpp"
|
||||
#include "WallPlaceAction.hpp"
|
||||
#include "WallRemoveAction.hpp"
|
||||
#include "WaterLowerAction.hpp"
|
||||
#include "WaterRaiseAction.hpp"
|
||||
|
@ -110,6 +111,7 @@ namespace GameActions
|
|||
Register<StaffSetCostumeAction>();
|
||||
Register<StaffSetPatrolAreaAction>();
|
||||
Register<SurfaceSetStyleAction>();
|
||||
Register<WallPlaceAction>();
|
||||
Register<WallRemoveAction>();
|
||||
Register<SmallSceneryPlaceAction>();
|
||||
Register<SmallSceneryRemoveAction>();
|
||||
|
|
676
src/openrct2/actions/WallPlaceAction.hpp
Normal file
676
src/openrct2/actions/WallPlaceAction.hpp
Normal file
|
@ -0,0 +1,676 @@
|
|||
/*****************************************************************************
|
||||
* 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 "../OpenRCT2.h"
|
||||
#include "../management/Finance.h"
|
||||
#include "../ride/RideGroupManager.h"
|
||||
#include "../ride/Track.h"
|
||||
#include "../ride/TrackData.h"
|
||||
#include "../world/Banner.h"
|
||||
#include "../world/LargeScenery.h"
|
||||
#include "../world/MapAnimation.h"
|
||||
#include "../world/Scenery.h"
|
||||
#include "../world/SmallScenery.h"
|
||||
#include "../world/Surface.h"
|
||||
#include "GameAction.h"
|
||||
|
||||
DEFINE_GAME_ACTION(WallPlaceAction, GAME_COMMAND_PLACE_WALL, GameActionResult)
|
||||
{
|
||||
private:
|
||||
int32_t _wallType{ -1 };
|
||||
CoordsXYZ _loc;
|
||||
uint8_t _edge{ std::numeric_limits<uint8_t>::max() };
|
||||
int32_t _primaryColour;
|
||||
int32_t _secondaryColour;
|
||||
int32_t _tertiaryColour;
|
||||
|
||||
public:
|
||||
WallPlaceAction()
|
||||
{
|
||||
}
|
||||
|
||||
WallPlaceAction(
|
||||
int32_t wallType, CoordsXYZ loc, uint8_t edge, int32_t primaryColour, int32_t secondaryColour, int32_t tertiaryColour)
|
||||
: _wallType(wallType)
|
||||
, _loc(loc)
|
||||
, _edge(edge)
|
||||
, _primaryColour(primaryColour)
|
||||
, _secondaryColour(secondaryColour)
|
||||
, _tertiaryColour(tertiaryColour)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t GetActionFlags() const override
|
||||
{
|
||||
return GameAction::GetActionFlags();
|
||||
}
|
||||
|
||||
void Serialise(DataSerialiser & stream) override
|
||||
{
|
||||
GameAction::Serialise(stream);
|
||||
|
||||
stream << DS_TAG(_wallType) << DS_TAG(_loc) << DS_TAG(_edge) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour)
|
||||
<< DS_TAG(_tertiaryColour);
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Query() const override
|
||||
{
|
||||
auto res = MakeResult();
|
||||
res->ErrorTitle = STR_CANT_BUILD_PARK_ENTRANCE_HERE;
|
||||
res->Position = _loc;
|
||||
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
res->Position.x += 16;
|
||||
res->Position.y += 16;
|
||||
|
||||
if (res->Position.z == 0)
|
||||
{
|
||||
res->Position.z = tile_element_height(res->Position.x, res->Position.y) & 0xFFFF;
|
||||
}
|
||||
|
||||
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY)
|
||||
&& !gCheatsSandboxMode)
|
||||
{
|
||||
if (_loc.z == 0)
|
||||
{
|
||||
if (!map_is_location_in_park({ _loc.x, _loc.y }))
|
||||
{
|
||||
return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_BUILD_PARK_ENTRANCE_HERE);
|
||||
}
|
||||
}
|
||||
else if (!map_is_location_owned(_loc.x, _loc.y, _loc.z))
|
||||
{
|
||||
return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_BUILD_PARK_ENTRANCE_HERE);
|
||||
}
|
||||
}
|
||||
else if ((_loc.x > gMapSizeMaxXY || _loc.y > gMapSizeMaxXY))
|
||||
{
|
||||
log_error("Invalid x/y coordinates. x = %d y = %d", _loc.x, _loc.y);
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_PARK_ENTRANCE_HERE);
|
||||
}
|
||||
|
||||
if (_edge > 3)
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_PARK_ENTRANCE_HERE);
|
||||
}
|
||||
|
||||
uint8_t edgeSlope = 0;
|
||||
auto targetHeight = _loc.z;
|
||||
if (targetHeight == 0)
|
||||
{
|
||||
TileElement* surfaceElement = map_get_surface_element_at({ _loc.x, _loc.y });
|
||||
if (surfaceElement == nullptr)
|
||||
{
|
||||
log_error("Surface element not found at %d, %d.", _loc.x, _loc.y);
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_PARK_ENTRANCE_HERE);
|
||||
}
|
||||
targetHeight = surfaceElement->base_height * 8;
|
||||
|
||||
uint8_t slope = surfaceElement->AsSurface()->GetSlope();
|
||||
edgeSlope = EdgeSlopes[slope][_edge & 3];
|
||||
if (edgeSlope & EDGE_SLOPE_ELEVATED)
|
||||
{
|
||||
targetHeight += 16;
|
||||
edgeSlope &= ~EDGE_SLOPE_ELEVATED;
|
||||
}
|
||||
}
|
||||
|
||||
TileElement* surfaceElement = map_get_surface_element_at({ _loc.x, _loc.y });
|
||||
if (surfaceElement == nullptr)
|
||||
{
|
||||
log_error("Surface element not found at %d, %d.", _loc.x, _loc.y);
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_PARK_ENTRANCE_HERE);
|
||||
}
|
||||
|
||||
if (surfaceElement->AsSurface()->GetWaterHeight() > 0)
|
||||
{
|
||||
uint16_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight() * 16;
|
||||
|
||||
if (targetHeight < waterHeight && !gCheatsDisableClearanceChecks)
|
||||
{
|
||||
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARK_ENTRANCE_HERE, STR_CANT_BUILD_THIS_UNDERWATER);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetHeight / 8 < surfaceElement->base_height && !gCheatsDisableClearanceChecks)
|
||||
{
|
||||
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARK_ENTRANCE_HERE, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND);
|
||||
}
|
||||
|
||||
if (!(edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS)))
|
||||
{
|
||||
uint8_t newEdge = (_edge + 2) & 3;
|
||||
uint8_t newBaseHeight = surfaceElement->base_height;
|
||||
newBaseHeight += 2;
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
if (targetHeight / 8 < newBaseHeight)
|
||||
{
|
||||
return MakeResult(
|
||||
GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARK_ENTRANCE_HERE, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND);
|
||||
}
|
||||
|
||||
if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT)
|
||||
{
|
||||
newEdge = (newEdge - 1) & 3;
|
||||
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
newEdge = (newEdge + 2) & 3;
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
newBaseHeight += 2;
|
||||
if (targetHeight / 8 < newBaseHeight)
|
||||
{
|
||||
return MakeResult(
|
||||
GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARK_ENTRANCE_HERE,
|
||||
STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND);
|
||||
}
|
||||
newBaseHeight -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newEdge = (_edge + 3) & 3;
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
if (targetHeight / 8 < newBaseHeight)
|
||||
{
|
||||
return MakeResult(
|
||||
GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARK_ENTRANCE_HERE, STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND);
|
||||
}
|
||||
|
||||
if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT)
|
||||
{
|
||||
newEdge = (newEdge - 1) & 3;
|
||||
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
newEdge = (newEdge + 2) & 3;
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
newBaseHeight += 2;
|
||||
if (targetHeight / 8 < newBaseHeight)
|
||||
{
|
||||
return MakeResult(
|
||||
GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARK_ENTRANCE_HERE,
|
||||
STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rct_scenery_entry* wallEntry = get_wall_entry(_wallType);
|
||||
|
||||
if (wallEntry == nullptr)
|
||||
{
|
||||
log_error("Wall Type not found %d", _wallType);
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_PARK_ENTRANCE_HERE);
|
||||
}
|
||||
|
||||
if (wallEntry->wall.scrolling_mode != SCROLLING_MODE_NONE)
|
||||
{
|
||||
auto bannerIndex = create_new_banner(0);
|
||||
|
||||
if (bannerIndex == 0xFF)
|
||||
{
|
||||
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_BUILD_PARK_ENTRANCE_HERE, STR_TOO_MANY_BANNERS_IN_GAME);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t clearanceHeight = targetHeight / 8;
|
||||
if (edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS))
|
||||
{
|
||||
if (wallEntry->wall.flags & WALL_SCENERY_CANT_BUILD_ON_SLOPE)
|
||||
{
|
||||
return MakeResult(
|
||||
GA_ERROR::DISALLOWED, STR_CANT_BUILD_PARK_ENTRANCE_HERE, STR_ERR_UNABLE_TO_BUILD_THIS_ON_SLOPE);
|
||||
}
|
||||
clearanceHeight += 2;
|
||||
}
|
||||
clearanceHeight += wallEntry->wall.height;
|
||||
|
||||
bool wallAcrossTrack = false;
|
||||
if (!(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY) && !gCheatsDisableClearanceChecks)
|
||||
{
|
||||
if (!WallCheckObstruction(wallEntry, targetHeight / 8, clearanceHeight, &wallAcrossTrack))
|
||||
{
|
||||
return MakeResult(
|
||||
GA_ERROR::NO_CLEARANCE, STR_CANT_BUILD_PARK_ENTRANCE_HERE, gGameCommandErrorText, gCommonFormatArgs);
|
||||
}
|
||||
}
|
||||
|
||||
if (!map_check_free_elements_and_reorganise(1))
|
||||
{
|
||||
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_BUILD_PARK_ENTRANCE_HERE, gGameCommandErrorText);
|
||||
}
|
||||
|
||||
res->Cost = wallEntry->wall.price;
|
||||
return res;
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Execute() const override
|
||||
{
|
||||
auto res = MakeResult();
|
||||
res->ErrorTitle = STR_CANT_BUILD_PARK_ENTRANCE_HERE;
|
||||
res->Position = _loc;
|
||||
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
res->Position.x += 16;
|
||||
res->Position.y += 16;
|
||||
|
||||
if (res->Position.z == 0)
|
||||
{
|
||||
res->Position.z = tile_element_height(res->Position.x, res->Position.y) & 0xFFFF;
|
||||
}
|
||||
|
||||
uint8_t edgeSlope = 0;
|
||||
auto targetHeight = _loc.z;
|
||||
if (targetHeight == 0)
|
||||
{
|
||||
TileElement* surfaceElement = map_get_surface_element_at({ _loc.x, _loc.y });
|
||||
if (surfaceElement == nullptr)
|
||||
{
|
||||
log_error("Surface element not found at %d, %d.", _loc.x, _loc.y);
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_PARK_ENTRANCE_HERE);
|
||||
}
|
||||
targetHeight = surfaceElement->base_height * 8;
|
||||
|
||||
uint8_t slope = surfaceElement->AsSurface()->GetSlope();
|
||||
edgeSlope = EdgeSlopes[slope][_edge & 3];
|
||||
if (edgeSlope & EDGE_SLOPE_ELEVATED)
|
||||
{
|
||||
targetHeight += 16;
|
||||
edgeSlope &= ~EDGE_SLOPE_ELEVATED;
|
||||
}
|
||||
}
|
||||
|
||||
BannerIndex bannerIndex = BANNER_INDEX_NULL;
|
||||
rct_scenery_entry* wallEntry = get_wall_entry(_wallType);
|
||||
|
||||
if (wallEntry == nullptr)
|
||||
{
|
||||
log_error("Wall Type not found %d", _wallType);
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_PARK_ENTRANCE_HERE);
|
||||
}
|
||||
|
||||
if (wallEntry->wall.scrolling_mode != SCROLLING_MODE_NONE)
|
||||
{
|
||||
bannerIndex = create_new_banner(GAME_COMMAND_FLAG_APPLY);
|
||||
|
||||
if (bannerIndex == 0xFF)
|
||||
{
|
||||
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_BUILD_PARK_ENTRANCE_HERE, STR_TOO_MANY_BANNERS_IN_GAME);
|
||||
}
|
||||
|
||||
rct_banner* banner = &gBanners[bannerIndex];
|
||||
banner->flags |= BANNER_FLAG_IS_WALL;
|
||||
banner->type = 0;
|
||||
banner->x = _loc.x / 32;
|
||||
banner->y = _loc.y / 32;
|
||||
|
||||
ride_id_t rideIndex = banner_get_closest_ride_index(_loc.x, _loc.y, targetHeight);
|
||||
if (rideIndex != RIDE_ID_NULL)
|
||||
{
|
||||
banner->ride_index = rideIndex;
|
||||
banner->flags |= BANNER_FLAG_LINKED_TO_RIDE;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t clearanceHeight = targetHeight / 8;
|
||||
if (edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS))
|
||||
{
|
||||
clearanceHeight += 2;
|
||||
}
|
||||
clearanceHeight += wallEntry->wall.height;
|
||||
|
||||
bool wallAcrossTrack = false;
|
||||
if (!(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY) && !gCheatsDisableClearanceChecks)
|
||||
{
|
||||
if (!WallCheckObstruction(wallEntry, targetHeight / 8, clearanceHeight, &wallAcrossTrack))
|
||||
{
|
||||
return MakeResult(
|
||||
GA_ERROR::NO_CLEARANCE, STR_CANT_BUILD_PARK_ENTRANCE_HERE, gGameCommandErrorText, gCommonFormatArgs);
|
||||
}
|
||||
}
|
||||
|
||||
if (!map_check_free_elements_and_reorganise(1))
|
||||
{
|
||||
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_BUILD_PARK_ENTRANCE_HERE, gGameCommandErrorText);
|
||||
}
|
||||
|
||||
TileElement* tileElement = tile_element_insert(_loc.x / 32, _loc.y / 32, targetHeight / 8, 0);
|
||||
assert(tileElement != nullptr);
|
||||
|
||||
map_animation_create(MAP_ANIMATION_TYPE_WALL, _loc.x, _loc.y, targetHeight / 8);
|
||||
|
||||
tileElement->SetType(TILE_ELEMENT_TYPE_WALL);
|
||||
WallElement* wallElement = tileElement->AsWall();
|
||||
wallElement->clearance_height = clearanceHeight;
|
||||
wallElement->SetDirection(_edge);
|
||||
// TODO: Normalise the edge slope code.
|
||||
wallElement->SetSlope(edgeSlope >> 6);
|
||||
|
||||
wallElement->SetPrimaryColour(_primaryColour);
|
||||
wallElement->SetSecondaryColour(_secondaryColour);
|
||||
|
||||
if (wallAcrossTrack)
|
||||
{
|
||||
wallElement->SetAcrossTrack(true);
|
||||
}
|
||||
|
||||
wallElement->SetEntryIndex(_wallType);
|
||||
if (bannerIndex != 0xFF)
|
||||
{
|
||||
wallElement->SetBannerIndex(bannerIndex);
|
||||
}
|
||||
|
||||
if (wallEntry->wall.flags & WALL_SCENERY_HAS_TERNARY_COLOUR)
|
||||
{
|
||||
wallElement->SetTertiaryColour(_tertiaryColour);
|
||||
}
|
||||
|
||||
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
|
||||
{
|
||||
wallElement->SetGhost(true);
|
||||
}
|
||||
|
||||
gSceneryTileElement = tileElement;
|
||||
map_invalidate_tile_zoom1(_loc.x, _loc.y, wallElement->base_height * 8, wallElement->base_height * 8 + 72);
|
||||
|
||||
res->Cost = wallEntry->wall.price;
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
#pragma region Edge Slopes Table
|
||||
|
||||
// clang-format off
|
||||
enum EDGE_SLOPE
|
||||
{
|
||||
EDGE_SLOPE_ELEVATED = (1 << 0), // 0x01
|
||||
EDGE_SLOPE_UPWARDS = (1 << 6), // 0x40
|
||||
EDGE_SLOPE_DOWNWARDS = (1 << 7), // 0x80
|
||||
|
||||
EDGE_SLOPE_UPWARDS_ELEVATED = EDGE_SLOPE_UPWARDS | EDGE_SLOPE_ELEVATED,
|
||||
EDGE_SLOPE_DOWNWARDS_ELEVATED = EDGE_SLOPE_DOWNWARDS | EDGE_SLOPE_ELEVATED,
|
||||
};
|
||||
|
||||
/** rct2: 0x009A3FEC */
|
||||
static constexpr const uint8_t EdgeSlopes[][4] = {
|
||||
// Top right Bottom right Bottom left Top left
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, 0 },
|
||||
{ 0, 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS },
|
||||
{ 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS },
|
||||
{ EDGE_SLOPE_DOWNWARDS, 0, 0, EDGE_SLOPE_UPWARDS },
|
||||
{ EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS },
|
||||
{ EDGE_SLOPE_DOWNWARDS, 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED },
|
||||
{ EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED },
|
||||
{ EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, 0, 0 },
|
||||
{ EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, 0 },
|
||||
{ EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS },
|
||||
{ EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS },
|
||||
{ EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, 0, EDGE_SLOPE_UPWARDS },
|
||||
{ EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS },
|
||||
{ EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED },
|
||||
{ EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS },
|
||||
{ EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED },
|
||||
{ 0, 0, 0, 0 },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
#pragma endregion
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006E5CBA
|
||||
*/
|
||||
bool WallCheckObstructionWithTrack(rct_scenery_entry * wall, int32_t z0, TrackElement * trackElement, bool* wallAcrossTrack)
|
||||
const
|
||||
{
|
||||
int32_t trackType = trackElement->GetTrackType();
|
||||
int32_t sequence = trackElement->GetSequenceIndex();
|
||||
int32_t direction = (_edge - trackElement->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK;
|
||||
Ride* ride = get_ride(trackElement->GetRideIndex());
|
||||
if (ride == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TrackIsAllowedWallEdges(ride->type, trackType, sequence, direction))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(wall->wall.flags & WALL_SCENERY_IS_DOOR))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (RideGroupManager::RideTypeHasRideGroups(ride->type))
|
||||
{
|
||||
auto rideEntry = get_ride_entry(ride->subtype);
|
||||
if (rideEntry == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto rideGroup = RideGroupManager::GetRideGroup(ride->type, rideEntry);
|
||||
if (rideGroup == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!(rideGroup->Flags & RIDE_GROUP_FLAG_ALLOW_DOORS_ON_TRACK))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!(RideData4[ride->type].flags & RIDE_TYPE_FLAG4_ALLOW_DOORS_ON_TRACK))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*wallAcrossTrack = true;
|
||||
if (z0 & 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t z;
|
||||
if (sequence == 0)
|
||||
{
|
||||
if (TrackSequenceProperties[trackType][0] & TRACK_SEQUENCE_FLAG_DISALLOW_DOORS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TrackDefinitions[trackType].bank_start == 0)
|
||||
{
|
||||
if (!(TrackCoordinates[trackType].rotation_begin & 4))
|
||||
{
|
||||
direction = trackElement->GetDirectionWithOffset(2);
|
||||
if (direction == _edge)
|
||||
{
|
||||
const rct_preview_track* trackBlock = &TrackBlocks[trackType][sequence];
|
||||
z = TrackCoordinates[trackType].z_begin;
|
||||
z = trackElement->base_height + ((z - trackBlock->z) * 8);
|
||||
if (z == z0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rct_preview_track* trackBlock = &TrackBlocks[trackType][sequence + 1];
|
||||
if (trackBlock->index != 0xFF)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TrackDefinitions[trackType].bank_end != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
direction = TrackCoordinates[trackType].rotation_end;
|
||||
if (direction & 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
direction = trackElement->GetDirection();
|
||||
if (direction != _edge)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
trackBlock = &TrackBlocks[trackType][sequence];
|
||||
z = TrackCoordinates[trackType].z_end;
|
||||
z = trackElement->base_height + ((z - trackBlock->z) * 8);
|
||||
return z == z0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006E5C1A
|
||||
*/
|
||||
bool WallCheckObstruction(rct_scenery_entry * wall, int32_t z0, int32_t z1, bool* wallAcrossTrack) const
|
||||
{
|
||||
int32_t entryType, sequence;
|
||||
rct_scenery_entry* entry;
|
||||
rct_large_scenery_tile* tile;
|
||||
|
||||
*wallAcrossTrack = false;
|
||||
gMapGroundFlags = ELEMENT_IS_ABOVE_GROUND;
|
||||
if (map_is_location_at_edge(_loc.x, _loc.y))
|
||||
{
|
||||
gGameCommandErrorText = STR_OFF_EDGE_OF_MAP;
|
||||
return false;
|
||||
}
|
||||
|
||||
TileElement* tileElement = map_get_first_element_at(_loc.x / 32, _loc.y / 32);
|
||||
do
|
||||
{
|
||||
if (tileElement == nullptr)
|
||||
break;
|
||||
int32_t elementType = tileElement->GetType();
|
||||
if (elementType == TILE_ELEMENT_TYPE_SURFACE)
|
||||
continue;
|
||||
if (tileElement->IsGhost())
|
||||
continue;
|
||||
if (z0 >= tileElement->clearance_height)
|
||||
continue;
|
||||
if (z1 <= tileElement->base_height)
|
||||
continue;
|
||||
if (elementType == TILE_ELEMENT_TYPE_WALL)
|
||||
{
|
||||
int32_t direction = tileElement->GetDirection();
|
||||
if (_edge == direction)
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ((tileElement->flags & 0x0F) == 0)
|
||||
continue;
|
||||
|
||||
switch (elementType)
|
||||
{
|
||||
case TILE_ELEMENT_TYPE_ENTRANCE:
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return false;
|
||||
case TILE_ELEMENT_TYPE_PATH:
|
||||
if (tileElement->AsPath()->GetEdges() & (1 << _edge))
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
|
||||
entryType = tileElement->AsLargeScenery()->GetEntryIndex();
|
||||
sequence = tileElement->AsLargeScenery()->GetSequenceIndex();
|
||||
entry = get_large_scenery_entry(entryType);
|
||||
tile = &entry->large_scenery.tiles[sequence];
|
||||
{
|
||||
int32_t direction = ((_edge - tileElement->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK) + 8;
|
||||
if (!(tile->flags & (1 << direction)))
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
|
||||
entry = tileElement->AsSmallScenery()->GetEntry();
|
||||
if (scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_NO_WALLS))
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TILE_ELEMENT_TYPE_TRACK:
|
||||
if (!WallCheckObstructionWithTrack(wall, z0, tileElement->AsTrack(), wallAcrossTrack))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the given track type can have a wall placed on the edge of the given direction.
|
||||
* Some thin tracks for example are allowed to have walls either side of the track, but wider tracks can not.
|
||||
*/
|
||||
static bool TrackIsAllowedWallEdges(uint8_t rideType, uint8_t trackType, uint8_t trackSequence, uint8_t direction)
|
||||
{
|
||||
if (!ride_type_has_flag(rideType, RIDE_TYPE_FLAG_TRACK_NO_WALLS))
|
||||
{
|
||||
if (ride_type_has_flag(rideType, RIDE_TYPE_FLAG_FLAT_RIDE))
|
||||
{
|
||||
if (FlatRideTrackSequenceElementAllowedWallEdges[trackType][trackSequence] & (1 << direction))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TrackSequenceElementAllowedWallEdges[trackType][trackSequence] & (1 << direction))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
|
@ -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 "14"
|
||||
#define NETWORK_STREAM_VERSION "15"
|
||||
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
||||
|
||||
static Peep* _pickup_peep = nullptr;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "../Game.h"
|
||||
#include "../GameState.h"
|
||||
#include "../ParkImporter.h"
|
||||
#include "../actions/WallPlaceAction.hpp"
|
||||
#include "../audio/audio.h"
|
||||
#include "../core/Collections.hpp"
|
||||
#include "../core/Console.hpp"
|
||||
|
@ -2773,10 +2774,13 @@ private:
|
|||
ConvertWall(&type, &colourA, &colourB);
|
||||
|
||||
type = _wallTypeToEntryMap[type];
|
||||
const uint8_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED
|
||||
| GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_PATH_SCENERY;
|
||||
|
||||
wall_place(type, x * 32, y * 32, 0, edge, colourA, colourB, colourC, flags);
|
||||
auto wallPlaceAction = WallPlaceAction(
|
||||
type, { x * 32, y * 32, 0 }, edge, colourA, colourB, colourC);
|
||||
wallPlaceAction.SetFlags(
|
||||
GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5
|
||||
| GAME_COMMAND_FLAG_PATH_SCENERY);
|
||||
GameActions::Execute(&wallPlaceAction);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "../actions/SmallSceneryRemoveAction.hpp"
|
||||
#include "../actions/TrackPlaceAction.hpp"
|
||||
#include "../actions/TrackRemoveAction.hpp"
|
||||
#include "../actions/WallPlaceAction.hpp"
|
||||
#include "../actions/WallRemoveAction.hpp"
|
||||
#include "../audio/audio.h"
|
||||
#include "../core/File.h"
|
||||
|
@ -1021,6 +1022,7 @@ static int32_t track_design_place_scenery(
|
|||
}
|
||||
break;
|
||||
case OBJECT_TYPE_WALLS:
|
||||
{
|
||||
if (mode != 0)
|
||||
{
|
||||
continue;
|
||||
|
@ -1050,17 +1052,15 @@ static int32_t track_design_place_scenery(
|
|||
flags = 0;
|
||||
}
|
||||
|
||||
gGameCommandErrorTitle = STR_CANT_BUILD_PARK_ENTRANCE_HERE;
|
||||
auto wallPlaceAction = WallPlaceAction(
|
||||
entry_index, { mapCoord.x, mapCoord.y, z }, rotation, scenery->primary_colour,
|
||||
scenery->secondary_colour, scenery->flags & 0xFC);
|
||||
auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::Execute(&wallPlaceAction)
|
||||
: GameActions::Query(&wallPlaceAction);
|
||||
|
||||
cost = game_do_command(
|
||||
mapCoord.x, flags | (entry_index << 8), mapCoord.y, rotation | (scenery->primary_colour << 8),
|
||||
GAME_COMMAND_PLACE_WALL, z, scenery->secondary_colour | ((scenery->flags & 0xFC) << 6));
|
||||
|
||||
if (cost == MONEY32_UNDEFINED)
|
||||
{
|
||||
cost = 0;
|
||||
}
|
||||
cost = res->Cost;
|
||||
break;
|
||||
}
|
||||
case OBJECT_TYPE_PATHS:
|
||||
if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z)
|
||||
{
|
||||
|
|
|
@ -186,9 +186,6 @@ int32_t map_can_construct_at(int32_t x, int32_t y, int32_t zLow, int32_t zHigh,
|
|||
void rotate_map_coordinates(int16_t* x, int16_t* y, int32_t rotation);
|
||||
LocationXY16 coordinate_3d_to_2d(const LocationXYZ16* coordinate_3d, int32_t rotation);
|
||||
money32 map_clear_scenery(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t clear, int32_t flags);
|
||||
money32 wall_place(
|
||||
int32_t type, int32_t x, int32_t y, int32_t z, int32_t edge, int32_t primaryColour, int32_t secondaryColour,
|
||||
int32_t tertiaryColour, int32_t flags);
|
||||
|
||||
void game_command_set_land_ownership(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
|
@ -204,7 +201,6 @@ 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_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);
|
||||
void game_command_place_large_scenery(
|
||||
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_park_entrance(
|
||||
|
|
|
@ -29,532 +29,6 @@
|
|||
#include "Surface.h"
|
||||
#include "Wall.h"
|
||||
|
||||
/**
|
||||
* Gets whether the given track type can have a wall placed on the edge of the given direction.
|
||||
* Some thin tracks for example are allowed to have walls either side of the track, but wider tracks can not.
|
||||
*/
|
||||
static bool TrackIsAllowedWallEdges(uint8_t rideType, uint8_t trackType, uint8_t trackSequence, uint8_t direction)
|
||||
{
|
||||
if (!ride_type_has_flag(rideType, RIDE_TYPE_FLAG_TRACK_NO_WALLS))
|
||||
{
|
||||
if (ride_type_has_flag(rideType, RIDE_TYPE_FLAG_FLAT_RIDE))
|
||||
{
|
||||
if (FlatRideTrackSequenceElementAllowedWallEdges[trackType][trackSequence] & (1 << direction))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TrackSequenceElementAllowedWallEdges[trackType][trackSequence] & (1 << direction))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006E5CBA
|
||||
*/
|
||||
static bool WallCheckObstructionWithTrack(
|
||||
rct_scenery_entry* wall, int32_t z0, int32_t edge, TileElement* trackElement, bool* wallAcrossTrack)
|
||||
{
|
||||
int32_t trackType = trackElement->AsTrack()->GetTrackType();
|
||||
int32_t sequence = trackElement->AsTrack()->GetSequenceIndex();
|
||||
int32_t direction = (edge - trackElement->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK;
|
||||
Ride* ride = get_ride(trackElement->AsTrack()->GetRideIndex());
|
||||
|
||||
if (TrackIsAllowedWallEdges(ride->type, trackType, sequence, direction))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(wall->wall.flags & WALL_SCENERY_IS_DOOR))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (RideGroupManager::RideTypeHasRideGroups(ride->type))
|
||||
{
|
||||
auto rideGroup = RideGroupManager::GetRideGroup(ride->type, get_ride_entry(ride->subtype));
|
||||
if (!(rideGroup->Flags & RIDE_GROUP_FLAG_ALLOW_DOORS_ON_TRACK))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!(RideData4[ride->type].flags & RIDE_TYPE_FLAG4_ALLOW_DOORS_ON_TRACK))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*wallAcrossTrack = true;
|
||||
if (z0 & 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t z;
|
||||
if (sequence == 0)
|
||||
{
|
||||
if (TrackSequenceProperties[trackType][0] & TRACK_SEQUENCE_FLAG_DISALLOW_DOORS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TrackDefinitions[trackType].bank_start == 0)
|
||||
{
|
||||
if (!(TrackCoordinates[trackType].rotation_begin & 4))
|
||||
{
|
||||
direction = trackElement->GetDirectionWithOffset(2);
|
||||
if (direction == edge)
|
||||
{
|
||||
const rct_preview_track* trackBlock = &TrackBlocks[trackType][sequence];
|
||||
z = TrackCoordinates[trackType].z_begin;
|
||||
z = trackElement->base_height + ((z - trackBlock->z) * 8);
|
||||
if (z == z0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rct_preview_track* trackBlock = &TrackBlocks[trackType][sequence + 1];
|
||||
if (trackBlock->index != 0xFF)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TrackDefinitions[trackType].bank_end != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
direction = TrackCoordinates[trackType].rotation_end;
|
||||
if (direction & 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
direction = trackElement->GetDirection();
|
||||
if (direction != edge)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
trackBlock = &TrackBlocks[trackType][sequence];
|
||||
z = TrackCoordinates[trackType].z_end;
|
||||
z = trackElement->base_height + ((z - trackBlock->z) * 8);
|
||||
return z == z0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006E5C1A
|
||||
*/
|
||||
static bool WallCheckObstruction(
|
||||
rct_scenery_entry* wall, int32_t x, int32_t y, int32_t z0, int32_t z1, int32_t edge, bool* wallAcrossTrack)
|
||||
{
|
||||
int32_t entryType, sequence;
|
||||
rct_scenery_entry* entry;
|
||||
rct_large_scenery_tile* tile;
|
||||
|
||||
*wallAcrossTrack = false;
|
||||
gMapGroundFlags = ELEMENT_IS_ABOVE_GROUND;
|
||||
if (map_is_location_at_edge(x, y))
|
||||
{
|
||||
gGameCommandErrorText = STR_OFF_EDGE_OF_MAP;
|
||||
return false;
|
||||
}
|
||||
|
||||
TileElement* tileElement = map_get_first_element_at(x / 32, y / 32);
|
||||
do
|
||||
{
|
||||
int32_t elementType = tileElement->GetType();
|
||||
if (elementType == TILE_ELEMENT_TYPE_SURFACE)
|
||||
continue;
|
||||
if (z0 >= tileElement->clearance_height)
|
||||
continue;
|
||||
if (z1 <= tileElement->base_height)
|
||||
continue;
|
||||
if (elementType == TILE_ELEMENT_TYPE_WALL)
|
||||
{
|
||||
int32_t direction = tileElement->GetDirection();
|
||||
if (edge == direction)
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ((tileElement->flags & 0x0F) == 0)
|
||||
continue;
|
||||
|
||||
switch (elementType)
|
||||
{
|
||||
case TILE_ELEMENT_TYPE_ENTRANCE:
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return false;
|
||||
case TILE_ELEMENT_TYPE_PATH:
|
||||
if (tileElement->AsPath()->GetEdges() & (1 << edge))
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
|
||||
entryType = tileElement->AsLargeScenery()->GetEntryIndex();
|
||||
sequence = tileElement->AsLargeScenery()->GetSequenceIndex();
|
||||
entry = get_large_scenery_entry(entryType);
|
||||
tile = &entry->large_scenery.tiles[sequence];
|
||||
{
|
||||
int32_t direction = ((edge - tileElement->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK) + 8;
|
||||
if (!(tile->flags & (1 << direction)))
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
|
||||
entry = tileElement->AsSmallScenery()->GetEntry();
|
||||
if (scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_NO_WALLS))
|
||||
{
|
||||
map_obstruction_set_error_text(tileElement);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TILE_ELEMENT_TYPE_TRACK:
|
||||
if (!WallCheckObstructionWithTrack(wall, z0, edge, tileElement, wallAcrossTrack))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma region Edge Slopes Table
|
||||
|
||||
// clang-format off
|
||||
enum EDGE_SLOPE
|
||||
{
|
||||
EDGE_SLOPE_ELEVATED = (1 << 0), // 0x01
|
||||
EDGE_SLOPE_UPWARDS = (1 << 6), // 0x40
|
||||
EDGE_SLOPE_DOWNWARDS = (1 << 7), // 0x80
|
||||
|
||||
EDGE_SLOPE_UPWARDS_ELEVATED = EDGE_SLOPE_UPWARDS | EDGE_SLOPE_ELEVATED,
|
||||
EDGE_SLOPE_DOWNWARDS_ELEVATED = EDGE_SLOPE_DOWNWARDS | EDGE_SLOPE_ELEVATED,
|
||||
};
|
||||
|
||||
/** rct2: 0x009A3FEC */
|
||||
static constexpr const uint8_t EdgeSlopes[][4] = {
|
||||
// Top right Bottom right Bottom left Top left
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, 0 },
|
||||
{ 0, 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS },
|
||||
{ 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS },
|
||||
{ EDGE_SLOPE_DOWNWARDS, 0, 0, EDGE_SLOPE_UPWARDS },
|
||||
{ EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS },
|
||||
{ EDGE_SLOPE_DOWNWARDS, 0, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED },
|
||||
{ EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED },
|
||||
{ EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, 0, 0 },
|
||||
{ EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, 0 },
|
||||
{ EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_DOWNWARDS },
|
||||
{ EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS },
|
||||
{ EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, 0, EDGE_SLOPE_UPWARDS },
|
||||
{ EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS },
|
||||
{ EDGE_SLOPE_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_ELEVATED },
|
||||
{ EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED, EDGE_SLOPE_ELEVATED },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ EDGE_SLOPE_UPWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS },
|
||||
{ EDGE_SLOPE_DOWNWARDS_ELEVATED, EDGE_SLOPE_DOWNWARDS, EDGE_SLOPE_UPWARDS, EDGE_SLOPE_UPWARDS_ELEVATED },
|
||||
{ 0, 0, 0, 0 },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
#pragma endregion
|
||||
|
||||
static money32 WallPlace(
|
||||
uint8_t wallType, int16_t x, int16_t y, int16_t z, uint8_t edge, uint8_t primaryColour, uint8_t secondaryColour,
|
||||
uint8_t tertiaryColour, uint8_t flags)
|
||||
{
|
||||
LocationXYZ16 position = { x, y, z };
|
||||
|
||||
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
gCommandPosition.x = position.x + 16;
|
||||
gCommandPosition.y = position.y + 16;
|
||||
gCommandPosition.z = position.z;
|
||||
|
||||
if (position.z == 0)
|
||||
{
|
||||
gCommandPosition.z = tile_element_height(position.x, position.y) & 0xFFFF;
|
||||
}
|
||||
|
||||
if (game_is_paused() && !gCheatsBuildInPauseMode)
|
||||
{
|
||||
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(flags & GAME_COMMAND_FLAG_PATH_SCENERY) && !gCheatsSandboxMode)
|
||||
{
|
||||
if (position.z == 0)
|
||||
{
|
||||
if (!map_is_location_in_park({ position.x, position.y }))
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
else if (!map_is_location_owned(position.x, position.y, position.z))
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t edgeSlope = 0;
|
||||
if (position.z == 0)
|
||||
{
|
||||
TileElement* surfaceElement = map_get_surface_element_at({ position.x, position.y });
|
||||
if (surfaceElement == nullptr)
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
position.z = surfaceElement->base_height * 8;
|
||||
|
||||
uint8_t slope = surfaceElement->AsSurface()->GetSlope();
|
||||
edgeSlope = EdgeSlopes[slope][edge & 3];
|
||||
if (edgeSlope & EDGE_SLOPE_ELEVATED)
|
||||
{
|
||||
position.z += 16;
|
||||
edgeSlope &= ~EDGE_SLOPE_ELEVATED;
|
||||
}
|
||||
}
|
||||
|
||||
TileElement* surfaceElement = map_get_surface_element_at({ position.x, position.y });
|
||||
if (surfaceElement == nullptr)
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (surfaceElement->AsSurface()->GetWaterHeight() > 0)
|
||||
{
|
||||
uint16_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight() * 16;
|
||||
|
||||
if (position.z < waterHeight && !gCheatsDisableClearanceChecks)
|
||||
{
|
||||
gGameCommandErrorText = STR_CANT_BUILD_THIS_UNDERWATER;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
if (position.z / 8 < surfaceElement->base_height && !gCheatsDisableClearanceChecks)
|
||||
{
|
||||
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (!(edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS)))
|
||||
{
|
||||
uint8_t newEdge = (edge + 2) & 3;
|
||||
uint8_t newBaseHeight = surfaceElement->base_height;
|
||||
newBaseHeight += 2;
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
if (position.z / 8 < newBaseHeight)
|
||||
{
|
||||
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT)
|
||||
{
|
||||
newEdge = (newEdge - 1) & 3;
|
||||
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
newEdge = (newEdge + 2) & 3;
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
newBaseHeight += 2;
|
||||
if (position.z / 8 < newBaseHeight)
|
||||
{
|
||||
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
newBaseHeight -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newEdge = (edge + 3) & 3;
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
if (position.z / 8 < newBaseHeight)
|
||||
{
|
||||
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT)
|
||||
{
|
||||
newEdge = (newEdge - 1) & 3;
|
||||
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
newEdge = (newEdge + 2) & 3;
|
||||
if (surfaceElement->AsSurface()->GetSlope() & (1 << newEdge))
|
||||
{
|
||||
newBaseHeight += 2;
|
||||
if (position.z / 8 < newBaseHeight)
|
||||
{
|
||||
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BannerIndex bannerIndex = BANNER_INDEX_NULL;
|
||||
rct_scenery_entry* wallEntry = get_wall_entry(wallType);
|
||||
|
||||
if (wallEntry == nullptr)
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (wallEntry->wall.scrolling_mode != SCROLLING_MODE_NONE)
|
||||
{
|
||||
bannerIndex = create_new_banner(flags);
|
||||
|
||||
if (bannerIndex == 0xFF)
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
rct_banner* banner = &gBanners[bannerIndex];
|
||||
if (flags & GAME_COMMAND_FLAG_APPLY)
|
||||
{
|
||||
banner->flags |= BANNER_FLAG_IS_WALL;
|
||||
banner->type = 0;
|
||||
banner->x = position.x / 32;
|
||||
banner->y = position.y / 32;
|
||||
|
||||
ride_id_t rideIndex = banner_get_closest_ride_index(position.x, position.y, position.z);
|
||||
if (rideIndex != RIDE_ID_NULL)
|
||||
{
|
||||
banner->ride_index = rideIndex;
|
||||
banner->flags |= BANNER_FLAG_LINKED_TO_RIDE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t clearanceHeight = position.z / 8;
|
||||
if (edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS))
|
||||
{
|
||||
if (wallEntry->wall.flags & WALL_SCENERY_CANT_BUILD_ON_SLOPE)
|
||||
{
|
||||
gGameCommandErrorText = STR_ERR_UNABLE_TO_BUILD_THIS_ON_SLOPE;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
clearanceHeight += 2;
|
||||
}
|
||||
clearanceHeight += wallEntry->wall.height;
|
||||
|
||||
bool wallAcrossTrack = false;
|
||||
if (!(flags & GAME_COMMAND_FLAG_PATH_SCENERY) && !gCheatsDisableClearanceChecks)
|
||||
{
|
||||
if (!WallCheckObstruction(wallEntry, position.x, position.y, position.z / 8, clearanceHeight, edge, &wallAcrossTrack))
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
if (!map_check_free_elements_and_reorganise(1))
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (flags & GAME_COMMAND_FLAG_APPLY)
|
||||
{
|
||||
if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST))
|
||||
{
|
||||
LocationXYZ16 coord;
|
||||
coord.x = position.x + 16;
|
||||
coord.y = position.y + 16;
|
||||
coord.z = tile_element_height(coord.x, coord.y);
|
||||
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
|
||||
}
|
||||
|
||||
TileElement* tileElement = tile_element_insert(position.x / 32, position.y / 32, position.z / 8, 0);
|
||||
assert(tileElement != nullptr);
|
||||
|
||||
map_animation_create(MAP_ANIMATION_TYPE_WALL, position.x, position.y, position.z / 8);
|
||||
|
||||
tileElement->clearance_height = clearanceHeight;
|
||||
tileElement->SetType(TILE_ELEMENT_TYPE_WALL);
|
||||
tileElement->SetDirection(edge);
|
||||
// TODO: Normalise the edge slope code.
|
||||
tileElement->AsWall()->SetSlope(edgeSlope >> 6);
|
||||
|
||||
tileElement->AsWall()->SetPrimaryColour(primaryColour);
|
||||
tileElement->AsWall()->SetSecondaryColour(secondaryColour);
|
||||
|
||||
if (wallAcrossTrack)
|
||||
{
|
||||
tileElement->AsWall()->SetAcrossTrack(true);
|
||||
}
|
||||
|
||||
tileElement->AsWall()->SetEntryIndex(wallType);
|
||||
if (bannerIndex != 0xFF)
|
||||
{
|
||||
tileElement->AsWall()->SetBannerIndex(bannerIndex);
|
||||
}
|
||||
|
||||
if (wallEntry->wall.flags & WALL_SCENERY_HAS_TERNARY_COLOUR)
|
||||
{
|
||||
tileElement->AsWall()->SetTertiaryColour(tertiaryColour);
|
||||
}
|
||||
|
||||
if (flags & GAME_COMMAND_FLAG_GHOST)
|
||||
{
|
||||
tileElement->SetGhost(true);
|
||||
}
|
||||
|
||||
gSceneryTileElement = tileElement;
|
||||
map_invalidate_tile_zoom1(position.x, position.y, tileElement->base_height * 8, tileElement->base_height * 8 + 72);
|
||||
}
|
||||
|
||||
if (gParkFlags & PARK_FLAGS_NO_MONEY)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return wallEntry->wall.price;
|
||||
}
|
||||
}
|
||||
|
||||
static money32 WallSetColour(
|
||||
int16_t x, int16_t y, uint8_t baseHeight, uint8_t direction, uint8_t primaryColour, uint8_t secondaryColour,
|
||||
uint8_t tertiaryColour, uint8_t flags)
|
||||
|
@ -662,33 +136,6 @@ void wall_remove_intersecting_walls(int32_t x, int32_t y, int32_t z0, int32_t z1
|
|||
} while (!(tileElement++)->IsLastForTile());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006E519A
|
||||
*/
|
||||
void game_command_place_wall(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp)
|
||||
{
|
||||
*ebx = WallPlace(
|
||||
(*ebx >> 8) & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edi & 0xFFFF, *edx & 0xFF, (*edx >> 8) & 0xFF, *ebp & 0xFF,
|
||||
(*ebp >> 8) & 0xFF, *ebx & 0xFF);
|
||||
}
|
||||
|
||||
money32 wall_place(
|
||||
int32_t type, int32_t x, int32_t y, int32_t z, int32_t edge, int32_t primaryColour, int32_t secondaryColour,
|
||||
int32_t tertiaryColour, int32_t flags)
|
||||
{
|
||||
int32_t eax = x;
|
||||
int32_t ebx = flags | (type << 8);
|
||||
int32_t ecx = y;
|
||||
int32_t edx = edge | (primaryColour << 8);
|
||||
int32_t esi = 0;
|
||||
int32_t edi = z;
|
||||
int32_t ebp = secondaryColour | (tertiaryColour << 8);
|
||||
game_command_place_wall(&eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
|
||||
return ebx;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006E56B5
|
||||
|
|
Loading…
Reference in a new issue