1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-10 09:32:29 +01:00

Merge pull request #9055 from duncanspumpkin/largescenery_place

LargeSceneryPlaceAction
This commit is contained in:
Duncan
2019-04-08 17:58:05 +01:00
committed by GitHub
9 changed files with 439 additions and 285 deletions

View File

@@ -31,6 +31,7 @@
#include <openrct2/actions/LandLowerAction.hpp>
#include <openrct2/actions/LandRaiseAction.hpp>
#include <openrct2/actions/LandSmoothAction.hpp>
#include <openrct2/actions/LargeSceneryPlaceAction.hpp>
#include <openrct2/actions/LargeScenerySetColourAction.hpp>
#include <openrct2/actions/LoadOrQuitAction.hpp>
#include <openrct2/actions/PauseToggleAction.hpp>
@@ -1859,31 +1860,48 @@ 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 & 0xFF;
auto secondaryColour = (parameter_2 >> 8) & 0xFF;
auto largeSceneryType = parameter_3 & 0xFF;
uint8_t direction = (parameter_1 & 0xFF00) >> 8;
CoordsXYZD loc = { gridX, gridY, gSceneryPlaceZ, direction };
gDisableErrorWindowSound = true;
gGameCommandErrorTitle = STR_CANT_POSITION_THIS_HERE;
int32_t cost = game_do_command(
gridX, flags, gridY, parameter_2, GAME_COMMAND_PLACE_LARGE_SCENERY, parameter_3, gSceneryPlaceZ);
gDisableErrorWindowSound = false;
auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, largeSceneryType, primaryColour, secondaryColour);
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(&sceneryPlaceAction);
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;
}
audio_play_sound_at_location(SOUND_ERROR, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z);
if (zAttemptRange != 1)
{
gSceneryPlaceZ += 8;
}
}
auto primaryColour = parameter_2 & 0xFF;
auto secondaryColour = (parameter_2 >> 8) & 0xFF;
auto largeSceneryType = parameter_3 & 0xFF;
uint8_t direction = (parameter_1 & 0xFF00) >> 8;
CoordsXYZD loc = { gridX, gridY, gSceneryPlaceZ, direction };
auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, largeSceneryType, primaryColour, secondaryColour);
sceneryPlaceAction.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);
}
else
{
audio_play_sound_at_location(SOUND_ERROR, loc.x, loc.y, gSceneryPlaceZ);
}
});
auto res = GameActions::Execute(&sceneryPlaceAction);
break;
}
case SCENERY_TYPE_BANNER:
@@ -2540,18 +2558,26 @@ static money32 try_place_ghost_scenery(
break;
}
case 3:
{
// Large Scenery
// 6e25a7
cost = game_do_command(
map_tile.x, parameter_1 | 0x69, map_tile.y, parameter_2, GAME_COMMAND_PLACE_LARGE_SCENERY, parameter_3,
gSceneryPlaceZ);
auto primaryColour = parameter_2 & 0xFF;
auto secondaryColour = (parameter_2 >> 8) & 0xFF;
auto sceneryType = parameter_3 & 0xFF;
uint8_t direction = (parameter_1 & 0xFF00) >> 8;
CoordsXYZD loc = { map_tile.x, map_tile.y, gSceneryPlaceZ, direction };
if (cost == MONEY32_UNDEFINED)
return cost;
auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, sceneryType, primaryColour, secondaryColour);
sceneryPlaceAction.SetFlags(
GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND);
auto res = GameActions::Execute(&sceneryPlaceAction);
if (res->Error != GA_ERROR::OK)
return res->Cost;
gSceneryGhostPosition.x = map_tile.x;
gSceneryGhostPosition.y = map_tile.y;
gSceneryPlaceRotation = ((parameter_1 >> 8) & 0xFF);
gSceneryPlaceRotation = loc.direction;
tileElement = gSceneryTileElement;
gSceneryGhostPosition.z = tileElement->base_height;
@@ -2569,6 +2595,7 @@ static money32 try_place_ghost_scenery(
gSceneryGhostType |= SCENERY_GHOST_FLAG_3;
break;
}
case 4:
// Banners
// 6e2612

View File

@@ -408,9 +408,7 @@ int32_t game_do_command_p(
gGameCommandNestLevel++;
// 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_SCENERY || command == GAME_COMMAND_PLACE_LARGE_SCENERY
|| command == GAME_COMMAND_PLACE_BANNER || command == GAME_COMMAND_PLACE_PATH))
if ((flags & GAME_COMMAND_FLAG_NETWORKED) && !(flags & GAME_COMMAND_FLAG_GHOST) && (command == GAME_COMMAND_PLACE_BANNER))
{
scenery_remove_ghost_tool_placement();
}
@@ -610,7 +608,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_LARGE_SCENERY || command == GAME_COMMAND_PLACE_BANNER)
else if (command == GAME_COMMAND_PLACE_BANNER)
{
uint8_t flags = *ebx & 0xFF;
if (flags & GAME_COMMAND_FLAG_GHOST)
@@ -1247,7 +1245,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
nullptr,
nullptr,
nullptr,
game_command_place_large_scenery,
nullptr,
nullptr,
nullptr,
nullptr,

View File

@@ -62,10 +62,10 @@ enum GAME_COMMAND
GAME_COMMAND_SET_STAFF_COLOUR, // 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
GAME_COMMAND_SET_RESEARCH_FUNDING, // GA
GAME_COMMAND_PLACE_LARGE_SCENERY, // GA
GAME_COMMAND_REMOVE_LARGE_SCENERY, // GA
GAME_COMMAND_SET_CURRENT_LOAN, // GA
GAME_COMMAND_SET_RESEARCH_FUNDING, // GA
GAME_COMMAND_PLACE_TRACK_DESIGN,
GAME_COMMAND_START_MARKETING_CAMPAIGN, // GA
GAME_COMMAND_PLACE_MAZE_DESIGN,

View File

@@ -24,6 +24,7 @@
#include "LandRaiseAction.hpp"
#include "LandSetHeightAction.hpp"
#include "LandSmoothAction.hpp"
#include "LargeSceneryPlaceAction.hpp"
#include "LargeSceneryRemoveAction.hpp"
#include "LargeScenerySetColourAction.hpp"
#include "LoadOrQuitAction.hpp"
@@ -124,6 +125,7 @@ namespace GameActions
Register<SmallSceneryPlaceAction>();
Register<SmallSceneryRemoveAction>();
Register<SmallScenerySetColourAction>();
Register<LargeSceneryPlaceAction>();
Register<LargeSceneryRemoveAction>();
Register<LargeScenerySetColourAction>();
Register<LandLowerAction>();

View File

@@ -0,0 +1,369 @@
/*****************************************************************************
* 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 "../object/ObjectLimits.h"
#include "../ride/Ride.h"
#include "../world/Banner.h"
#include "../world/LargeScenery.h"
#include "../world/MapAnimation.h"
#include "../world/Scenery.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(LargeSceneryPlaceAction, GAME_COMMAND_PLACE_LARGE_SCENERY, GameActionResult)
{
private:
CoordsXYZD _loc;
uint8_t _sceneryType{ std::numeric_limits<uint8_t>::max() };
uint8_t _primaryColour;
uint8_t _secondaryColour;
public:
LargeSceneryPlaceAction() = default;
LargeSceneryPlaceAction(CoordsXYZD loc, uint8_t sceneryType, uint8_t primaryColour, uint8_t secondaryColour)
: _loc(loc)
, _sceneryType(sceneryType)
, _primaryColour(primaryColour)
, _secondaryColour(secondaryColour)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_sceneryType) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour);
}
GameActionResult::Ptr Query() const override
{
auto res = MakeResult();
res->ErrorTitle = STR_CANT_POSITION_THIS_HERE;
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
int16_t surfaceHeight = tile_element_height(_loc.x, _loc.y) & 0xFFFF;
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = surfaceHeight;
gSceneryGroundFlags = 0;
BannerIndex bannerId = BANNER_INDEX_NULL;
money32 supportsCost = 0;
if (_primaryColour > TILE_ELEMENT_COLOUR_MASK || _secondaryColour > TILE_ELEMENT_COLOUR_MASK)
{
log_error(
"Invalid game command for scenery placement, primaryColour = %u, secondaryColour = %u", _primaryColour,
_secondaryColour);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE);
}
if (_sceneryType >= MAX_LARGE_SCENERY_OBJECTS)
{
log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE);
}
rct_scenery_entry* sceneryEntry = get_large_scenery_entry(_sceneryType);
if (sceneryEntry == nullptr)
{
log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE);
}
uint32_t totalNumTiles = GetTotalNumTiles(sceneryEntry->large_scenery.tiles);
int16_t maxHeight = GetMaxSurfaceHeight(sceneryEntry->large_scenery.tiles);
if (_loc.z != 0)
{
maxHeight = _loc.z;
}
res->Position.z = maxHeight;
if (sceneryEntry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE)
{
bannerId = create_new_banner(0);
if (bannerId == BANNER_INDEX_NULL)
{
log_error("No free banners available");
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE);
}
}
if (!map_check_free_elements_and_reorganise(totalNumTiles))
{
log_error("No free map elements available");
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE);
}
uint8_t tileNum = 0;
for (rct_large_scenery_tile* tile = sceneryEntry->large_scenery.tiles; tile->x_offset != -1; tile++, tileNum++)
{
auto tempX = tile->x_offset;
auto tempY = tile->y_offset;
rotate_map_coordinates(&tempX, &tempY, _loc.direction);
CoordsXY curTile = { tempX, tempY };
curTile.x += _loc.x;
curTile.y += _loc.y;
int32_t zLow = (tile->z_offset + maxHeight) / 8;
int32_t zHigh = (tile->z_clearance / 8) + zLow;
QuarterTile quarterTile = QuarterTile{ static_cast<uint8_t>(tile->flags >> 12), 0 }.Rotate(_loc.direction);
if (!map_can_construct_with_clear_at(
curTile.x, curTile.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &supportsCost,
CREATE_CROSSING_MODE_NONE))
{
return MakeResult(
GA_ERROR::NO_CLEARANCE, STR_CANT_POSITION_THIS_HERE, gGameCommandErrorText, gCommonFormatArgs);
}
int32_t tempSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND);
if (!gCheatsDisableClearanceChecks)
{
if ((gMapGroundFlags & ELEMENT_IS_UNDERWATER) || (gMapGroundFlags & ELEMENT_IS_UNDERGROUND))
{
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CANT_BUILD_THIS_UNDERWATER);
}
if (gSceneryGroundFlags && !(gSceneryGroundFlags & tempSceneryGroundFlags))
{
return MakeResult(
GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND);
}
}
gSceneryGroundFlags = tempSceneryGroundFlags;
if (curTile.x >= gMapSizeUnits || curTile.y >= gMapSizeUnits)
{
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_OFF_EDGE_OF_MAP);
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_owned(curTile.x, curTile.y, zLow * 8)
&& !gCheatsSandboxMode)
{
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
}
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
res->Cost = (sceneryEntry->large_scenery.price * 10) + supportsCost;
return res;
}
GameActionResult::Ptr Execute() const override
{
auto res = MakeResult();
res->ErrorTitle = STR_CANT_POSITION_THIS_HERE;
int16_t surfaceHeight = tile_element_height(_loc.x, _loc.y) & 0xFFFF;
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = surfaceHeight;
gSceneryGroundFlags = 0;
BannerIndex bannerId = BANNER_INDEX_NULL;
money32 supportsCost = 0;
rct_scenery_entry* sceneryEntry = get_large_scenery_entry(_sceneryType);
if (sceneryEntry == nullptr)
{
log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE);
}
if (sceneryEntry->large_scenery.tiles == nullptr)
{
log_error("Invalid large scenery object, sceneryType = %u", _sceneryType);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE);
}
uint32_t totalNumTiles = GetTotalNumTiles(sceneryEntry->large_scenery.tiles);
int16_t maxHeight = GetMaxSurfaceHeight(sceneryEntry->large_scenery.tiles);
if (_loc.z != 0)
{
maxHeight = _loc.z;
}
res->Position.z = maxHeight;
if (sceneryEntry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE)
{
bannerId = create_new_banner(GAME_COMMAND_FLAG_APPLY);
if (bannerId == BANNER_INDEX_NULL)
{
log_error("No free banners available");
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE);
}
rct_banner* banner = &gBanners[bannerId];
banner->flags |= BANNER_FLAG_IS_LARGE_SCENERY;
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, maxHeight);
if (rideIndex != RIDE_ID_NULL)
{
banner->ride_index = rideIndex;
banner->flags |= BANNER_FLAG_LINKED_TO_RIDE;
}
}
if (!map_check_free_elements_and_reorganise(totalNumTiles))
{
log_error("No free map elements available");
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE);
}
uint8_t tileNum = 0;
for (rct_large_scenery_tile* tile = sceneryEntry->large_scenery.tiles; tile->x_offset != -1; tile++, tileNum++)
{
auto tempX = tile->x_offset;
auto tempY = tile->y_offset;
rotate_map_coordinates(&tempX, &tempY, _loc.direction);
CoordsXY curTile = { tempX, tempY };
curTile.x += _loc.x;
curTile.y += _loc.y;
int32_t zLow = (tile->z_offset + maxHeight) / 8;
int32_t zHigh = (tile->z_clearance / 8) + zLow;
QuarterTile quarterTile = QuarterTile{ static_cast<uint8_t>(tile->flags >> 12), 0 }.Rotate(_loc.direction);
if (!map_can_construct_with_clear_at(
curTile.x, curTile.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &supportsCost,
CREATE_CROSSING_MODE_NONE))
{
return MakeResult(
GA_ERROR::NO_CLEARANCE, STR_CANT_POSITION_THIS_HERE, gGameCommandErrorText, gCommonFormatArgs);
}
gSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND);
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_remove_litter(curTile.x, curTile.y, zLow * 8);
if (!gCheatsDisableClearanceChecks)
{
wall_remove_at(curTile.x, curTile.y, zLow * 8, zHigh * 8);
}
}
TileElement* newTileElement = tile_element_insert(
curTile.x / 32, curTile.y / 32, zLow, quarterTile.GetBaseQuarterOccupied());
Guard::Assert(newTileElement != nullptr);
map_animation_create(MAP_ANIMATION_TYPE_LARGE_SCENERY, curTile.x, curTile.y, zLow);
newTileElement->SetType(TILE_ELEMENT_TYPE_LARGE_SCENERY);
newTileElement->clearance_height = zHigh;
auto newSceneryElement = newTileElement->AsLargeScenery();
SetNewLargeSceneryElement(*newSceneryElement, tileNum, bannerId);
if (tileNum == 0)
{
gSceneryTileElement = newTileElement;
}
map_invalidate_tile_full(curTile.x, curTile.y);
}
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
res->Cost = (sceneryEntry->large_scenery.price * 10) + supportsCost;
return res;
}
private:
int16_t GetTotalNumTiles(rct_large_scenery_tile * tiles) const
{
uint32_t totalNumTiles = 0;
for (rct_large_scenery_tile* tile = tiles; tile->x_offset != -1; tile++)
{
totalNumTiles++;
}
return totalNumTiles;
}
int16_t GetMaxSurfaceHeight(rct_large_scenery_tile * tiles) const
{
int16_t maxHeight = -1;
for (rct_large_scenery_tile* tile = tiles; tile->x_offset != -1; tile++)
{
auto tempX = tile->x_offset;
auto tempY = tile->y_offset;
rotate_map_coordinates(&tempX, &tempY, _loc.direction);
CoordsXY curTile = { tempX, tempY };
curTile.x += _loc.x;
curTile.y += _loc.y;
if (curTile.x >= 0x1FFF || curTile.y >= 0x1FFF || curTile.x < 0 || curTile.y < 0)
{
continue;
}
TileElement* tileElement = map_get_surface_element_at({ curTile.x, curTile.y });
if (tileElement == nullptr)
continue;
SurfaceElement* surfaceElement = tileElement->AsSurface();
int32_t height = surfaceElement->base_height * 8;
int32_t slope = surfaceElement->GetSlope();
if (slope & 0xF)
{
height += 16;
if (slope & 0x10)
{
height += 16;
}
}
if (height > maxHeight)
{
maxHeight = height;
}
}
return maxHeight;
}
void SetNewLargeSceneryElement(LargeSceneryElement & sceneryElement, uint8_t tileNum, BannerIndex bannerId) const
{
sceneryElement.SetDirection(_loc.direction);
sceneryElement.SetEntryIndex(_sceneryType);
sceneryElement.SetSequenceIndex(tileNum);
sceneryElement.SetPrimaryColour(_primaryColour);
sceneryElement.SetSecondaryColour(_secondaryColour);
if (bannerId != BANNER_INDEX_NULL)
{
sceneryElement.SetBannerIndex(bannerId);
}
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
sceneryElement.SetGhost(true);
}
}
};

View File

@@ -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 "16"
#define NETWORK_STREAM_VERSION "17"
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
static Peep* _pickup_peep = nullptr;

View File

@@ -14,6 +14,7 @@
#include "../OpenRCT2.h"
#include "../actions/FootpathPlaceFromTrackAction.hpp"
#include "../actions/FootpathRemoveAction.hpp"
#include "../actions/LargeSceneryPlaceAction.hpp"
#include "../actions/LargeSceneryRemoveAction.hpp"
#include "../actions/RideEntranceExitPlaceAction.hpp"
#include "../actions/RideSetSetting.hpp"
@@ -887,6 +888,7 @@ static bool TrackDesignPlaceSceneryElement(
break;
}
case OBJECT_TYPE_LARGE_SCENERY:
{
if (mode != 0)
{
return true;
@@ -917,15 +919,15 @@ static bool TrackDesignPlaceSceneryElement(
flags = GAME_COMMAND_FLAG_PATH_SCENERY;
}
cost = game_do_command(
mapCoord.x, flags | (rotation << 8), mapCoord.y, scenery->primary_colour | (scenery->secondary_colour << 8),
GAME_COMMAND_PLACE_LARGE_SCENERY, entry_index, z);
auto sceneryPlaceAction = LargeSceneryPlaceAction(
{ mapCoord.x, mapCoord.y, z, rotation }, entry_index, scenery->primary_colour, scenery->secondary_colour);
sceneryPlaceAction.SetFlags(flags);
auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::Execute(&sceneryPlaceAction)
: GameActions::Query(&sceneryPlaceAction);
if (cost == MONEY32_UNDEFINED)
{
cost = 0;
}
cost = res->Cost;
break;
}
case OBJECT_TYPE_WALLS:
{
if (mode != 0)

View File

@@ -1002,248 +1002,6 @@ 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);
}
/**
*
* rct2: 0x006B893C
*/
void game_command_place_large_scenery(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp)
{
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
int32_t x = (int16_t)*eax;
int32_t y = (int16_t)*ecx;
int32_t z = (int16_t)*ebp;
colour_t colour1 = *edx & TILE_ELEMENT_COLOUR_MASK;
colour_t colour2 = (*edx >> 8) & TILE_ELEMENT_COLOUR_MASK;
uint8_t flags = *ebx;
uint8_t rotation = *ebx >> 8;
uint8_t entry_index = *edi;
int32_t base_height = tile_element_height(x, y);
gCommandPosition.x = x + 16;
gCommandPosition.y = y + 16;
gCommandPosition.z = base_height;
gSceneryGroundFlags = 0;
BannerIndex banner_id = BANNER_INDEX_NULL;
money32 supportsCost = 0;
if (game_is_paused() && !gCheatsBuildInPauseMode)
{
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
*ebx = MONEY32_UNDEFINED;
return;
}
if (entry_index >= 128)
{
log_warning("Invalid game command for scenery placement, entry_index = %u", entry_index);
*ebx = MONEY32_UNDEFINED;
return;
}
rct_scenery_entry* scenery_entry = get_large_scenery_entry(entry_index);
if (scenery_entry == nullptr)
{
log_warning("Invalid game command for scenery placement, entry_index = %u", entry_index);
*ebx = MONEY32_UNDEFINED;
return;
}
if (scenery_entry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE)
{
banner_id = create_new_banner(flags);
if (banner_id == BANNER_INDEX_NULL)
{
*ebx = MONEY32_UNDEFINED;
return;
}
if (flags & GAME_COMMAND_FLAG_APPLY)
{
rct_banner* banner = &gBanners[banner_id];
banner->flags |= BANNER_FLAG_IS_LARGE_SCENERY;
banner->type = 0;
banner->x = x / 32;
banner->y = y / 32;
ride_id_t rideIndex = banner_get_closest_ride_index(x, y, z);
if (rideIndex != RIDE_ID_NULL)
{
banner->ride_index = rideIndex;
banner->flags |= BANNER_FLAG_LINKED_TO_RIDE;
}
}
}
uint32_t num_elements = 0;
int16_t maxHeight = -1;
for (rct_large_scenery_tile* tile = scenery_entry->large_scenery.tiles; tile->x_offset != -1; tile++)
{
num_elements++;
LocationXY16 curTile = { tile->x_offset, tile->y_offset };
rotate_map_coordinates(&curTile.x, &curTile.y, rotation);
curTile.x += x;
curTile.y += y;
if (curTile.x >= 0x1FFF || curTile.y >= 0x1FFF || curTile.x < 0 || curTile.y < 0)
{
continue;
}
TileElement* tile_element = map_get_surface_element_at({ curTile.x, curTile.y });
if (tile_element != nullptr)
{
int32_t height = tile_element->base_height * 8;
int32_t slope = tile_element->AsSurface()->GetSlope();
if (slope & 0xF)
{
height += 16;
if (slope & 0x10)
{
height += 16;
}
}
if (height > maxHeight)
{
maxHeight = height;
}
}
}
if (z != 0)
{
maxHeight = z;
}
if (!map_check_free_elements_and_reorganise(num_elements))
{
*ebx = MONEY32_UNDEFINED;
return;
}
gCommandPosition.z = maxHeight;
uint8_t tile_num = 0;
for (rct_large_scenery_tile* tile = scenery_entry->large_scenery.tiles; tile->x_offset != -1; tile++, tile_num++)
{
LocationXY16 curTile = { tile->x_offset, tile->y_offset };
rotate_map_coordinates(&curTile.x, &curTile.y, rotation);
curTile.x += x;
curTile.y += y;
int32_t zLow = (tile->z_offset + maxHeight) / 8;
int32_t zHigh = (tile->z_clearance / 8) + zLow;
QuarterTile quarterTile = QuarterTile{ static_cast<uint8_t>(tile->flags >> 12), 0 }.Rotate(rotation);
if (!map_can_construct_with_clear_at(
curTile.x, curTile.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, flags, &supportsCost,
CREATE_CROSSING_MODE_NONE))
{
*ebx = MONEY32_UNDEFINED;
return;
}
if ((gMapGroundFlags & ELEMENT_IS_UNDERWATER) || (gMapGroundFlags & ELEMENT_IS_UNDERGROUND))
{
*ebx = MONEY32_UNDEFINED;
return;
}
int32_t b = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND);
if (!gCheatsDisableClearanceChecks)
{
if (gSceneryGroundFlags && !(gSceneryGroundFlags & b))
{
gGameCommandErrorText = STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND;
*ebx = MONEY32_UNDEFINED;
return;
}
}
gSceneryGroundFlags = b;
if (curTile.x >= gMapSizeUnits || curTile.y >= gMapSizeUnits)
{
gGameCommandErrorText = STR_OFF_EDGE_OF_MAP;
*ebx = MONEY32_UNDEFINED;
return;
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_owned(curTile.x, curTile.y, zLow * 8)
&& !gCheatsSandboxMode)
{
*ebx = MONEY32_UNDEFINED;
return;
}
if (flags & GAME_COMMAND_FLAG_APPLY)
{
if (!(flags & GAME_COMMAND_FLAG_GHOST))
{
footpath_remove_litter(curTile.x, curTile.y, zLow * 8);
if (!gCheatsDisableClearanceChecks)
{
wall_remove_at(curTile.x, curTile.y, zLow * 8, zHigh * 8);
}
}
if (gGameCommandNestLevel == 1 && !(*ebx & GAME_COMMAND_FLAG_GHOST))
{
LocationXYZ16 coord;
coord.x = x + 16;
coord.y = 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* new_tile_element = tile_element_insert(
curTile.x / 32, curTile.y / 32, zLow, quarterTile.GetBaseQuarterOccupied());
assert(new_tile_element != nullptr);
map_animation_create(MAP_ANIMATION_TYPE_LARGE_SCENERY, curTile.x, curTile.y, zLow);
new_tile_element->clearance_height = zHigh;
new_tile_element->SetType(TILE_ELEMENT_TYPE_LARGE_SCENERY);
new_tile_element->SetDirection(rotation);
auto newSceneryElement = new_tile_element->AsLargeScenery();
newSceneryElement->SetEntryIndex(entry_index);
newSceneryElement->SetSequenceIndex(tile_num);
newSceneryElement->SetPrimaryColour(colour1);
newSceneryElement->SetSecondaryColour(colour2);
if (banner_id != BANNER_INDEX_NULL)
{
newSceneryElement->SetBannerIndex(banner_id);
}
if (flags & GAME_COMMAND_FLAG_GHOST)
{
new_tile_element->SetGhost(true);
}
if (tile_num == 0)
{
gSceneryTileElement = new_tile_element;
}
map_invalidate_tile_full(curTile.x, curTile.y);
}
}
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
*ebx = (scenery_entry->large_scenery.price * 10) + supportsCost;
if (gParkFlags & PARK_FLAGS_NO_MONEY)
{
*ebx = 0;
}
}
/**
*
* rct2: 0x0068B280

View File

@@ -193,8 +193,6 @@ void game_command_remove_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_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_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(
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_name(