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:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>();
|
||||
|
||||
369
src/openrct2/actions/LargeSceneryPlaceAction.hpp
Normal file
369
src/openrct2/actions/LargeSceneryPlaceAction.hpp
Normal 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user