Use game action for track design. Fix error titles.

Add files to xcode project (#20)

Finally get the placement code working

Use game action for ghosts. Fix basequarter

Remove references to old command

Fix formating remove unused var

Fix clang format
This commit is contained in:
duncanspumpkin 2019-02-26 13:19:17 +00:00
parent 4714350216
commit cbb02ec98a
9 changed files with 126 additions and 382 deletions

View file

@ -24,6 +24,9 @@
2A1F4FE0221FF4B0003CA045 /* Twitch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C840F1EC4E7CC00FA49E2 /* Twitch.cpp */; };
2A1F4FE1221FF4B0003CA045 /* Audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F76C83571EC4E7CC00FA49E2 /* Audio.cpp */; };
2A1F4FE2221FF4B0003CA045 /* macos.mm in Sources */ = {isa = PBXBuildFile; fileRef = F76C845D1EC4E7CC00FA49E2 /* macos.mm */; };
2A43D2BA2225B8D900E8F73B /* RideSetVehiclesAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A43D2B72225B8D900E8F73B /* RideSetVehiclesAction.hpp */; };
2A43D2BB2225B8D900E8F73B /* SmallSceneryPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A43D2B82225B8D900E8F73B /* SmallSceneryPlaceAction.hpp */; };
2A43D2BC2225B8D900E8F73B /* LoadOrQuitAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A43D2B92225B8D900E8F73B /* LoadOrQuitAction.hpp */; };
2A5354E922099C4F00A5440F /* Network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A5354E822099C4F00A5440F /* Network.cpp */; };
2A5C1368221E9F9000F8C245 /* TrackRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */; };
2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */; };
@ -622,6 +625,9 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
2A43D2B72225B8D900E8F73B /* RideSetVehiclesAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetVehiclesAction.hpp; sourceTree = "<group>"; };
2A43D2B82225B8D900E8F73B /* SmallSceneryPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SmallSceneryPlaceAction.hpp; sourceTree = "<group>"; };
2A43D2B92225B8D900E8F73B /* LoadOrQuitAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LoadOrQuitAction.hpp; sourceTree = "<group>"; };
2A5354E822099C4F00A5440F /* Network.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Network.cpp; sourceTree = "<group>"; };
2A5354EA22099C7200A5440F /* CircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircularBuffer.h; sourceTree = "<group>"; };
2A5354EB22099D7700A5440F /* SignSetStyleAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SignSetStyleAction.hpp; sourceTree = "<group>"; };
@ -2013,6 +2019,9 @@
isa = PBXGroup;
children = (
2ACBAB162226850A0034FB91 /* RideSetSetting.hpp */,
2A43D2B92225B8D900E8F73B /* LoadOrQuitAction.hpp */,
2A43D2B72225B8D900E8F73B /* RideSetVehiclesAction.hpp */,
2A43D2B82225B8D900E8F73B /* SmallSceneryPlaceAction.hpp */,
2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */,
2AAFD7FF220DD3D2002461A4 /* LandSetHeightAction.hpp */,
2AAFD7FB220DD336002461A4 /* RideSetPriceAction.hpp */,
@ -3332,8 +3341,10 @@
2AAFD7FE220DD374002461A4 /* PauseToggleAction.hpp in Headers */,
C6352B941F477032006CCEE3 /* PlaceParkEntranceAction.hpp in Headers */,
C6352B911F477032006CCEE3 /* GameAction.h in Headers */,
2A43D2BA2225B8D900E8F73B /* RideSetVehiclesAction.hpp in Headers */,
2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */,
C62D838B1FD36D6F008C04F1 /* EditorObjectSelectionSession.h in Headers */,
2A43D2BC2225B8D900E8F73B /* LoadOrQuitAction.hpp in Headers */,
9344BEF920C1E6180047D165 /* Crypt.h in Headers */,
939A35A220C12FFD00630B3F /* InteractiveConsole.h in Headers */,
2ACBAB172226850A0034FB91 /* RideSetSetting.hpp in Headers */,
@ -3346,6 +3357,7 @@
2AAFD7FC220DD336002461A4 /* RideSetPriceAction.hpp in Headers */,
C67B28162002D67A00109C93 /* Window.h in Headers */,
C6352B961F477032006CCEE3 /* RideSetStatus.hpp in Headers */,
2A43D2BB2225B8D900E8F73B /* SmallSceneryPlaceAction.hpp in Headers */,
2AAFD800220DD3D2002461A4 /* LandSetHeightAction.hpp in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;

View file

@ -28,6 +28,7 @@
#include <openrct2/actions/ClearAction.hpp>
#include <openrct2/actions/LoadOrQuitAction.hpp>
#include <openrct2/actions/PauseToggleAction.hpp>
#include <openrct2/actions/SmallSceneryPlaceAction.hpp>
#include <openrct2/audio/audio.h>
#include <openrct2/config/Config.h>
#include <openrct2/interface/Chat.h>
@ -1723,7 +1724,8 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo
{
quantity = 35;
}
int32_t successfulPlacements = 0;
bool forceError = true;
for (int32_t q = 0; q < quantity; q++)
{
int32_t zCoordinate = gSceneryPlaceZ;
@ -1755,58 +1757,61 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo
zAttemptRange = 20;
}
bool success = false;
uint8_t quadrant = parameter_2 & 0xFF;
uint8_t primaryColour = (parameter_2 >> 8) & 0xFF;
uint8_t secondaryColour = (parameter_3 >> 16) & 0xFF;
uint8_t type = (parameter_1 >> 8) & 0xFF;
auto success = GA_ERROR::UNKNOWN;
// Try find a valid z coordinate
for (; zAttemptRange != 0; zAttemptRange--)
{
int32_t flags = GAME_COMMAND_FLAG_APPLY | (parameter_1 & 0xFF00);
gDisableErrorWindowSound = true;
gGameCommandErrorTitle = STR_CANT_POSITION_THIS_HERE;
int32_t cost = game_do_command(
cur_grid_x, flags, cur_grid_y, parameter_2, GAME_COMMAND_PLACE_SCENERY,
gSceneryPlaceRotation | (parameter_3 & 0xFFFF0000), gSceneryPlaceZ);
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);
success = true;
break;
}
if (gGameCommandErrorText == STR_NOT_ENOUGH_CASH_REQUIRES
|| gGameCommandErrorText == STR_CAN_ONLY_BUILD_THIS_ON_WATER)
auto smallSceneryPlaceAction = SmallSceneryPlaceAction(
{ cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, type, primaryColour,
secondaryColour);
auto res = GameActions::Query(&smallSceneryPlaceAction);
success = res->Error;
if (res->Error == GA_ERROR::OK)
{
break;
}
gSceneryPlaceZ += 8;
if (res->Error == GA_ERROR::INSUFFICIENT_FUNDS)
{
break;
}
if (zAttemptRange != 1)
{
gSceneryPlaceZ += 8;
}
}
if (success)
// Actually place
if (success == GA_ERROR::OK || ((q + 1 == quantity) && (forceError == true)))
{
successfulPlacements++;
}
else
{
if (gGameCommandErrorText == STR_NOT_ENOUGH_CASH_REQUIRES)
auto smallSceneryPlaceAction = SmallSceneryPlaceAction(
{ cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, type, primaryColour,
secondaryColour);
smallSceneryPlaceAction.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(&smallSceneryPlaceAction);
if (res->Error == GA_ERROR::OK)
{
forceError = false;
}
if (res->Error == GA_ERROR::INSUFFICIENT_FUNDS)
{
break;
}
}
gSceneryPlaceZ = zCoordinate;
}
if (successfulPlacements > 0)
{
window_close_by_class(WC_ERROR);
}
else
{
audio_play_sound_at_location(SOUND_ERROR, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z);
}
break;
}
case SCENERY_TYPE_PATH_ITEM:
@ -2444,16 +2449,22 @@ static money32 try_place_ghost_scenery(
switch (scenery_type)
{
case 0:
{
// Small Scenery
// 6e252b
cost = game_do_command(
map_tile.x,
parameter_1 | GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5
| GAME_COMMAND_FLAG_GHOST,
map_tile.y, parameter_2, GAME_COMMAND_PLACE_SCENERY, parameter_3, gSceneryPlaceZ);
uint8_t quadrant = parameter_2 & 0xFF;
uint8_t primaryColour = (parameter_2 >> 8) & 0xFF;
uint8_t secondaryColour = (parameter_3 >> 16) & 0xFF;
uint8_t type = (parameter_1 >> 8) & 0xFF;
uint8_t rotation = parameter_3 & 0xFF;
auto smallSceneryPlaceAction = SmallSceneryPlaceAction(
{ map_tile.x, map_tile.y, gSceneryPlaceZ, rotation }, quadrant, type, primaryColour, secondaryColour);
smallSceneryPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
auto res = GameActions::Execute(&smallSceneryPlaceAction);
if (cost == MONEY32_UNDEFINED)
return cost;
cost = res->Cost;
if (res->Error != GA_ERROR::OK)
return MONEY32_UNDEFINED;
gSceneryGhostPosition.x = map_tile.x;
gSceneryGhostPosition.y = map_tile.y;
@ -2476,6 +2487,7 @@ static money32 try_place_ghost_scenery(
gSceneryGhostType |= SCENERY_GHOST_FLAG_0;
break;
}
case 1:
// Path Bits
// 6e265b

View file

@ -630,8 +630,8 @@ void game_log_multiplayer_command(int command, const int* eax, const int* ebx, c
network_append_server_log(log_msg);
}
else if (
command == GAME_COMMAND_PLACE_SCENERY || command == GAME_COMMAND_PLACE_WALL
|| command == GAME_COMMAND_PLACE_LARGE_SCENERY || command == GAME_COMMAND_PLACE_BANNER)
command == GAME_COMMAND_PLACE_WALL || command == GAME_COMMAND_PLACE_LARGE_SCENERY
|| command == GAME_COMMAND_PLACE_BANNER)
{
uint8_t flags = *ebx & 0xFF;
if (flags & GAME_COMMAND_FLAG_GHOST)
@ -1275,7 +1275,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
game_command_place_ride_entrance_or_exit,
game_command_remove_ride_entrance_or_exit,
nullptr,
game_command_place_scenery,
nullptr,
game_command_set_water_height,
game_command_place_footpath,
game_command_place_footpath_from_track,

View file

@ -33,7 +33,7 @@ enum GAME_COMMAND
GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT,
GAME_COMMAND_REMOVE_RIDE_ENTRANCE_OR_EXIT,
GAME_COMMAND_REMOVE_SCENERY, // GA
GAME_COMMAND_PLACE_SCENERY,
GAME_COMMAND_PLACE_SCENERY, // GA
GAME_COMMAND_SET_WATER_HEIGHT,
GAME_COMMAND_PLACE_PATH,
GAME_COMMAND_PLACE_PATH_FROM_TRACK,

View file

@ -18,17 +18,19 @@
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/Ride.h"
#include "../world/TileElement.h"
#include "../ride/TrackDesign.h"
#include "../world/MapAnimation.h"
#include "../world/Park.h"
#include "../world/SmallScenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
#include "../world/TileElement.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(SmallSceneryPlaceAction, GAME_COMMAND_PLACE_SCENERY, GameActionResult)
{
private:
CoordsXYZ _loc;
uint8_t _rotation;
CoordsXYZD _loc;
uint8_t _quadrant;
uint8_t _sceneryType;
uint8_t _primaryColour;
@ -38,10 +40,8 @@ public:
SmallSceneryPlaceAction() = default;
SmallSceneryPlaceAction(
CoordsXYZ loc, uint8_t quadrant, uint8_t rotation, uint8_t sceneryType,
uint8_t primaryColour, uint8_t secondaryColour)
CoordsXYZD loc, uint8_t quadrant, uint8_t sceneryType, uint8_t primaryColour, uint8_t secondaryColour)
: _loc(loc)
, _rotation(rotation)
, _quadrant(quadrant)
, _sceneryType(sceneryType)
, _primaryColour(primaryColour)
@ -58,7 +58,8 @@ public:
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_rotation) << DS_TAG(_quadrant) << DS_TAG(_sceneryType) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour);
stream << DS_TAG(_loc) << DS_TAG(_quadrant) << DS_TAG(_sceneryType) << DS_TAG(_primaryColour)
<< DS_TAG(_secondaryColour);
}
GameActionResult::Ptr Query() const override
@ -87,18 +88,18 @@ public:
if (!map_check_free_elements_and_reorganise(1))
{
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_NONE);
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_POSITION_THIS_HERE);
}
if (!byte_9D8150 && (_loc.x > gMapSizeMaxXY || _loc.y > gMapSizeMaxXY))
{
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE);
}
rct_scenery_entry* sceneryEntry = get_small_scenery_entry(_sceneryType);
if (sceneryEntry == nullptr)
{
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE);
}
auto quadrant = _quadrant;
@ -146,7 +147,7 @@ public:
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode
&& !map_is_location_owned(_loc.x, _loc.y, targetHeight))
{
return MakeResult(GA_ERROR::NOT_OWNED, STR_NONE);
return MakeResult(GA_ERROR::NOT_OWNED, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
TileElement* surfaceElement = map_get_surface_element_at({ _loc.x, _loc.y });
@ -156,7 +157,7 @@ public:
int32_t water_height = (surfaceElement->AsSurface()->GetWaterHeight() * 16) - 1;
if (water_height > targetHeight)
{
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_THIS_UNDERWATER);
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CANT_BUILD_THIS_UNDERWATER);
}
}
@ -164,14 +165,14 @@ public:
{
if (isOnWater)
{
return MakeResult(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_LAND);
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_LAND);
}
if (surfaceElement != nullptr && surfaceElement->AsSurface()->GetWaterHeight() > 0)
{
if ((surfaceElement->AsSurface()->GetWaterHeight() * 16) > targetHeight)
if (static_cast<int32_t>((surfaceElement->AsSurface()->GetWaterHeight() * 16)) > targetHeight)
{
return MakeResult(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_LAND);
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_LAND);
}
}
}
@ -180,7 +181,7 @@ public:
&& (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_REQUIRE_FLAT_SURFACE)) && !supportsRequired
&& !isOnWater && surfaceElement != nullptr && (surfaceElement->AsSurface()->GetSlope() != TILE_ELEMENT_SLOPE_FLAT))
{
return MakeResult(GA_ERROR::DISALLOWED, STR_LEVEL_LAND_REQUIRED);
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_LEVEL_LAND_REQUIRED);
}
if (!gCheatsDisableSupportLimits && !(scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_STACKABLE))
@ -192,13 +193,13 @@ public:
{
if (surfaceElement->AsSurface()->GetWaterHeight() || (surfaceElement->base_height * 8) != targetHeight)
{
return MakeResult(GA_ERROR::DISALLOWED, STR_LEVEL_LAND_REQUIRED);
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_LEVEL_LAND_REQUIRED);
}
}
}
else
{
return MakeResult(GA_ERROR::DISALLOWED, STR_CAN_ONLY_BUILD_THIS_ON_LAND);
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BUILD_THIS_ON_LAND);
}
}
@ -218,41 +219,39 @@ public:
{
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_THREE_QUARTERS))
{
quadRotation = ((quadrant ^ 2) + _rotation) & 3;
quadRotation = ((quadrant ^ 2) + _loc.direction) & 3;
collisionQuadrants = 0b1011;
}
else
{
quadRotation = (quadrant + _rotation) & 1;
quadRotation = (quadrant + _loc.direction) & 1;
collisionQuadrants = 0b1010;
}
}
}
else
{
quadRotation = ((quadrant ^ 2) + _rotation) & 3;
quadRotation = ((quadrant ^ 2) + _loc.direction) & 3;
collisionQuadrants = 0b0011;
}
uint8_t supports = 0;
if (!supportsRequired)
{
supports |= 0xF0;
supports = 0b1111;
}
QuarterTile quarterTile = QuarterTile{ collisionQuadrants, supports }.Rotate(quadRotation);
money32 clearCost = 0;
if (!gCheatsDisableClearanceChecks
&& !map_can_construct_with_clear_at(
_loc.x, _loc.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &clearCost,
CREATE_CROSSING_MODE_NONE))
if (!map_can_construct_with_clear_at(
_loc.x, _loc.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &clearCost,
CREATE_CROSSING_MODE_NONE))
{
return MakeResult(GA_ERROR::DISALLOWED, STR_NONE, gGameCommandErrorText, gCommonFormatArgs);
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, gGameCommandErrorText, gCommonFormatArgs);
}
gSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND);
auto res = MakeResult();
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
res->Cost = (sceneryEntry->small_scenery.price * 10) + clearCost;
@ -261,7 +260,6 @@ public:
GameActionResult::Ptr Execute() const override
{
bool isOnWater = false;
bool supportsRequired = false;
if (_loc.z != 0)
{
@ -286,7 +284,7 @@ public:
rct_scenery_entry* sceneryEntry = get_small_scenery_entry(_sceneryType);
if (sceneryEntry == nullptr)
{
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE);
}
auto quadrant = _quadrant;
@ -320,10 +318,6 @@ public:
{
// base_height2 is now the water height
baseHeight >>= 16;
if (_loc.z == 0)
{
isOnWater = true;
}
}
auto targetHeight = _loc.z;
if (_loc.z == 0)
@ -356,56 +350,54 @@ public:
{
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_THREE_QUARTERS))
{
quadRotation = ((quadrant ^ 2) + _rotation) & 3;
quadRotation = ((quadrant ^ 2) + _loc.direction) & 3;
collisionQuadrants = 0b1011;
}
else
{
quadRotation = (quadrant + _rotation) & 1;
quadRotation = (quadrant + _loc.direction) & 1;
collisionQuadrants = 0b1010;
}
}
}
else
{
quadRotation = ((quadrant ^ 2) + _rotation) & 3;
quadRotation = ((quadrant ^ 2) + _loc.direction) & 3;
collisionQuadrants = 0b0011;
}
uint8_t supports = 0;
if (!supportsRequired)
{
supports |= 0xF0;
supports = 0b1111;
}
QuarterTile quarterTile = QuarterTile{ collisionQuadrants, supports }.Rotate(quadRotation);
money32 clearCost = 0;
if (!gCheatsDisableClearanceChecks
&& !map_can_construct_with_clear_at(
_loc.x, _loc.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &clearCost,
CREATE_CROSSING_MODE_NONE))
if (!map_can_construct_with_clear_at(
_loc.x, _loc.y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, GetFlags(), &clearCost,
CREATE_CROSSING_MODE_NONE))
{
return MakeResult(GA_ERROR::DISALLOWED, STR_NONE, gGameCommandErrorText, gCommonFormatArgs);
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, gGameCommandErrorText, gCommonFormatArgs);
}
gSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND);
auto res = MakeResult();
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
res->Cost = (sceneryEntry->small_scenery.price * 10) + clearCost;
TileElement* newElement = tile_element_insert(_loc.x / 32, _loc.y / 32, zLow, collisionQuadrants);
TileElement* newElement = tile_element_insert(_loc.x / 32, _loc.y / 32, zLow, quarterTile.GetBaseQuarterOccupied());
assert(newElement != nullptr);
gSceneryTileElement = newElement;
newElement->SetType(TILE_ELEMENT_TYPE_SMALL_SCENERY);
newElement->SetDirection(_rotation);
newElement->SetDirection(_loc.direction);
SmallSceneryElement* sceneryElement = newElement->AsSmallScenery();
sceneryElement->SetSceneryQuadrant(quadrant);
sceneryElement->SetEntryIndex(_sceneryType);
sceneryElement->SetAge(0);
sceneryElement->SetPrimaryColour(_primaryColour);
sceneryElement->SetSecondaryColour(_secondaryColour);
newElement->clearance_height = newElement->base_height + ((sceneryEntry->small_scenery.height + 7) / 8);
sceneryElement->clearance_height = sceneryElement->base_height + ((sceneryEntry->small_scenery.height + 7) / 8);
if (supportsRequired)
{
@ -414,13 +406,13 @@ public:
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
newElement->flags |= TILE_ELEMENT_FLAG_GHOST;
sceneryElement->SetGhost(true);
}
map_invalidate_tile_full(_loc.x, _loc.y);
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_ANIMATED))
{
map_animation_create(2, _loc.x, _loc.y, newElement->base_height);
map_animation_create(2, _loc.x, _loc.y, sceneryElement->base_height);
}
return res;

View file

@ -1958,6 +1958,7 @@ void Network::ProcessGameCommands()
if (mode == NETWORK_MODE_SERVER)
{
// Note these are currently not reached as both commands are ported to GameActions
if (command == GAME_COMMAND_PLACE_SCENERY)
{
player->LastPlaceSceneryTime = player->LastActionTime;

View file

@ -15,6 +15,7 @@
#include "../actions/LargeSceneryRemoveAction.hpp"
#include "../actions/RideSetSetting.hpp"
#include "../actions/RideSetVehiclesAction.hpp"
#include "../actions/SmallSceneryPlaceAction.hpp"
#include "../actions/SmallSceneryRemoveAction.hpp"
#include "../actions/TrackPlaceAction.hpp"
#include "../actions/TrackRemoveAction.hpp"
@ -932,6 +933,7 @@ static int32_t track_design_place_scenery(
switch (entry_type)
{
case OBJECT_TYPE_SMALL_SCENERY:
{
if (mode != 0)
{
continue;
@ -964,15 +966,17 @@ static int32_t track_design_place_scenery(
gGameCommandErrorTitle = STR_CANT_POSITION_THIS_HERE;
cost = game_do_command(
mapCoord.x, flags | (entry_index << 8), mapCoord.y, quadrant | (scenery->primary_colour << 8),
GAME_COMMAND_PLACE_SCENERY, rotation | (scenery->secondary_colour << 16), z);
auto smallSceneryPlace = SmallSceneryPlaceAction(
{ mapCoord.x, mapCoord.y, z, rotation }, quadrant, entry_index, scenery->primary_colour,
scenery->secondary_colour);
if (cost == MONEY32_UNDEFINED)
{
cost = 0;
}
smallSceneryPlace.SetFlags(flags);
auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&smallSceneryPlace)
: GameActions::QueryNested(&smallSceneryPlace);
cost = res->Error == GA_ERROR::OK ? res->Cost : 0;
break;
}
case OBJECT_TYPE_LARGE_SCENERY:
if (mode != 0)
{

View file

@ -206,8 +206,6 @@ void game_command_set_water_height(
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_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_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);

View file

@ -65,269 +65,6 @@ static money32 SmallScenerySetColour(
return 0;
}
static money32 SmallSceneryPlace(
int16_t x, int16_t y, uint16_t targetHeight, uint8_t quadrant, uint8_t rotation, uint8_t sceneryType, uint8_t primaryColour,
uint8_t secondaryColour, uint8_t flags)
{
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
money32 clearCost = 0;
bool isOnWater = false;
bool supportsRequired = false;
if (targetHeight != 0)
{
supportsRequired = true;
}
int32_t baseHeight = tile_element_height(x, y);
// If on water
if (baseHeight & 0xFFFF0000)
{
baseHeight >>= 16;
}
gCommandPosition.x = x;
gCommandPosition.y = y;
gCommandPosition.z = baseHeight;
if (targetHeight != 0)
{
baseHeight = targetHeight;
gCommandPosition.z = baseHeight;
}
gCommandPosition.x += 16;
gCommandPosition.y += 16;
if (game_is_paused() && !gCheatsBuildInPauseMode)
{
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
if (!map_check_free_elements_and_reorganise(1))
{
return MONEY32_UNDEFINED;
}
if (!byte_9D8150 && (x > gMapSizeMaxXY || y > gMapSizeMaxXY))
{
return MONEY32_UNDEFINED;
}
rct_scenery_entry* sceneryEntry = get_small_scenery_entry(sceneryType);
if (sceneryEntry == nullptr)
{
return MONEY32_UNDEFINED;
}
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_FULL_TILE)
|| !scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_DIAGONAL))
{
if (scenery_small_entry_has_flag(
sceneryEntry, SMALL_SCENERY_FLAG_DIAGONAL | SMALL_SCENERY_FLAG_HALF_SPACE | SMALL_SCENERY_FLAG_THREE_QUARTERS))
{
quadrant = 0;
}
}
// Check if sub tile height is any different compared to actual surface tile height
int32_t x2 = x;
int32_t y2 = y;
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_FULL_TILE))
{
x2 += 16;
y2 += 16;
}
else
{
x2 += ScenerySubTileOffsets[quadrant & 3].x - 1;
y2 += ScenerySubTileOffsets[quadrant & 3].y - 1;
}
baseHeight = tile_element_height(x2, y2);
// If on water
if (baseHeight & 0xFFFF0000)
{
// base_height2 is now the water height
baseHeight >>= 16;
if (targetHeight == 0)
{
isOnWater = true;
}
}
if (targetHeight == 0)
{
targetHeight = baseHeight;
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode && !map_is_location_owned(x, y, targetHeight))
{
return MONEY32_UNDEFINED;
}
if (flags & GAME_COMMAND_FLAG_APPLY && !(flags & GAME_COMMAND_FLAG_GHOST))
{
footpath_remove_litter(x, y, targetHeight);
if (!gCheatsDisableClearanceChecks && (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_NO_WALLS)))
{
wall_remove_at(x, y, targetHeight, targetHeight + sceneryEntry->small_scenery.height);
}
}
TileElement* surfaceElement = map_get_surface_element_at({ x, y });
if (surfaceElement != nullptr && !gCheatsDisableClearanceChecks && surfaceElement->AsSurface()->GetWaterHeight() > 0)
{
int32_t water_height = (surfaceElement->AsSurface()->GetWaterHeight() * 16) - 1;
if (water_height > targetHeight)
{
gGameCommandErrorText = STR_CANT_BUILD_THIS_UNDERWATER;
return MONEY32_UNDEFINED;
}
}
if (!gCheatsDisableClearanceChecks && !(scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_STACKABLE)))
{
if (isOnWater)
{
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ON_LAND;
return MONEY32_UNDEFINED;
}
if (surfaceElement != nullptr && surfaceElement->AsSurface()->GetWaterHeight() > 0)
{
if ((surfaceElement->AsSurface()->GetWaterHeight() * 16) > targetHeight)
{
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ON_LAND;
return MONEY32_UNDEFINED;
}
}
}
if (!gCheatsDisableClearanceChecks && (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_REQUIRE_FLAT_SURFACE))
&& !supportsRequired && !isOnWater && surfaceElement != nullptr
&& (surfaceElement->AsSurface()->GetSlope() != TILE_ELEMENT_SLOPE_FLAT))
{
gGameCommandErrorText = STR_LEVEL_LAND_REQUIRED;
return MONEY32_UNDEFINED;
}
if (!gCheatsDisableSupportLimits && !(scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_STACKABLE))
&& supportsRequired)
{
if (!isOnWater)
{
if (surfaceElement != nullptr)
{
if (surfaceElement->AsSurface()->GetWaterHeight() || (surfaceElement->base_height * 8) != targetHeight)
{
gGameCommandErrorText = STR_LEVEL_LAND_REQUIRED;
return MONEY32_UNDEFINED;
}
}
}
else
{
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ON_LAND;
return MONEY32_UNDEFINED;
}
}
int32_t zLow = targetHeight / 8;
int32_t zHigh = zLow + ceil2(sceneryEntry->small_scenery.height, 8) / 8;
uint8_t collisionQuadrants = 0b1111;
auto quadRotation{ 0 };
if (!(scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_FULL_TILE)))
{
quadRotation = (quadrant ^ 2);
collisionQuadrants = 0b0001;
}
if (!(scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_HALF_SPACE)))
{
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_DIAGONAL)
&& scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_FULL_TILE))
{
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_THREE_QUARTERS))
{
quadRotation = ((quadrant ^ 2) + rotation) & 3;
collisionQuadrants = 0b1011;
}
else
{
quadRotation = (quadrant + rotation) & 1;
collisionQuadrants = 0b1010;
}
}
}
else
{
quadRotation = ((quadrant ^ 2) + rotation) & 3;
collisionQuadrants = 0b0011;
}
uint8_t supports = 0;
if (!supportsRequired)
{
supports |= 0xF0;
}
QuarterTile quarterTile = QuarterTile{ collisionQuadrants, supports }.Rotate(quadRotation);
if (!map_can_construct_with_clear_at(
x, y, zLow, zHigh, &map_place_scenery_clear_func, quarterTile, flags, &clearCost, CREATE_CROSSING_MODE_NONE))
{
return MONEY32_UNDEFINED;
}
gSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND);
money32 cost = (sceneryEntry->small_scenery.price * 10) + clearCost;
if (gParkFlags & PARK_FLAGS_NO_MONEY)
{
cost = 0;
}
if (!(flags & GAME_COMMAND_FLAG_APPLY))
{
return cost;
}
if (gGameCommandNestLevel == 1 && !(flags & 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* newElement = tile_element_insert(x / 32, y / 32, zLow, collisionQuadrants);
assert(newElement != nullptr);
gSceneryTileElement = newElement;
newElement->SetType(TILE_ELEMENT_TYPE_SMALL_SCENERY);
newElement->SetDirection(rotation);
SmallSceneryElement* sceneryElement = newElement->AsSmallScenery();
sceneryElement->SetSceneryQuadrant(quadrant);
sceneryElement->SetEntryIndex(sceneryType);
sceneryElement->SetAge(0);
sceneryElement->SetPrimaryColour(primaryColour);
sceneryElement->SetSecondaryColour(secondaryColour);
newElement->clearance_height = newElement->base_height + ((sceneryEntry->small_scenery.height + 7) / 8);
if (supportsRequired)
{
sceneryElement->SetNeedsSupports();
}
if (flags & GAME_COMMAND_FLAG_GHOST)
{
newElement->SetGhost(true);
}
map_invalidate_tile_full(x, y);
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_ANIMATED))
{
map_animation_create(2, x, y, newElement->base_height);
}
return cost;
}
/**
*
* rct2: 0x006E0F26
@ -412,18 +149,6 @@ int32_t map_place_non_scenery_clear_func(TileElement** tile_element, int32_t x,
return 0;
}
/**
*
* rct2: 0x006E08F4
*/
void game_command_place_scenery(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp)
{
*ebx = SmallSceneryPlace(
*eax & 0xFFFF, *ecx & 0xFFFF, *ebp & 0xFFFF, *edx & 0xFF, *edi & 0xFF, (*ebx >> 8) & 0xFF, (*edx >> 8) & 0xFF,
(*edi >> 16) & 0xFF, *ebx & 0xFF);
}
bool scenery_small_entry_has_flag(const rct_scenery_entry* sceneryEntry, uint32_t flags)
{
return (bool)(sceneryEntry->small_scenery.flags & flags);