Merge pull request #8796 from duncanspumpkin/entranceexit_place_ga

Entranceexit place ga
This commit is contained in:
Duncan 2019-03-02 16:04:01 +00:00 committed by GitHub
commit ec60781a2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 416 additions and 354 deletions

View File

@ -32,6 +32,7 @@
2A43D2C22225B91A00E8F73B /* LoadOrQuitAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A43D2BF2225B91A00E8F73B /* LoadOrQuitAction.hpp */; };
2A5354E922099C4F00A5440F /* Network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A5354E822099C4F00A5440F /* Network.cpp */; };
2A5C1368221E9F9000F8C245 /* TrackRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */; };
2A61CAFB2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */; };
2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */; };
2AA050332209A8E300D3A922 /* StaffSetOrdersAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */; };
2AAFD7FA220DD2DC002461A4 /* TrackPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AAFD7F9220DD2DC002461A4 /* TrackPlaceAction.hpp */; };
@ -638,6 +639,7 @@
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>"; };
2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackRemoveAction.hpp; sourceTree = "<group>"; };
2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideEntranceExitPlaceAction.hpp; sourceTree = "<group>"; };
2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetCostumeAction.hpp; sourceTree = "<group>"; };
2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetOrdersAction.hpp; sourceTree = "<group>"; };
2AAFD7F9220DD2DC002461A4 /* TrackPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackPlaceAction.hpp; sourceTree = "<group>"; };
@ -2024,6 +2026,7 @@
C6352B871F477032006CCEE3 /* actions */ = {
isa = PBXGroup;
children = (
2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */,
2ACBAB162226850A0034FB91 /* RideSetSetting.hpp */,
2A43D2B92225B8D900E8F73B /* LoadOrQuitAction.hpp */,
2A43D2B72225B8D900E8F73B /* RideSetVehiclesAction.hpp */,
@ -3346,6 +3349,7 @@
2AAFD7FA220DD2DC002461A4 /* TrackPlaceAction.hpp in Headers */,
933F2CBB20935668001B33FD /* LocalisationService.h in Headers */,
2A5C1368221E9F9000F8C245 /* TrackRemoveAction.hpp in Headers */,
2A61CAFB2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp in Headers */,
C6352B861F477022006CCEE3 /* Endianness.h in Headers */,
93CBA4CC20A7504500867D56 /* ImageImporter.h in Headers */,
2AAFD7FE220DD374002461A4 /* PauseToggleAction.hpp in Headers */,

View File

@ -13,6 +13,7 @@
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/RideEntranceExitPlaceAction.hpp>
#include <openrct2/audio/audio.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/localisation/Localisation.h>
@ -364,38 +365,32 @@ static void window_maze_construction_entrance_tooldown(int32_t x, int32_t y, rct
return;
ride_id_t rideIndex = gRideEntranceExitPlaceRideIndex;
uint8_t entranceExitType = gRideEntranceExitPlaceType;
if (entranceExitType == ENTRANCE_TYPE_RIDE_ENTRANCE)
{
gGameCommandErrorTitle = STR_CANT_BUILD_MOVE_ENTRANCE_FOR_THIS_RIDE_ATTRACTION;
}
else
{
gGameCommandErrorTitle = STR_CANT_BUILD_MOVE_EXIT_FOR_THIS_RIDE_ATTRACTION;
}
money32 cost = game_do_command(
x, GAME_COMMAND_FLAG_APPLY | (direction_reverse(direction) << 8), y, rideIndex | (entranceExitType << 8),
GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT, gRideEntranceExitPlaceStationIndex, 0);
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(
{ x, y }, direction_reverse(direction), rideIndex, gRideEntranceExitPlaceStationIndex,
gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_EXIT);
if (cost == MONEY32_UNDEFINED)
return;
rideEntranceExitPlaceAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) {
if (result->Error != GA_ERROR::OK)
return;
audio_play_sound_at_location(SOUND_PLACE_ITEM, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z);
audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z);
Ride* ride = get_ride(rideIndex);
if (ride_are_all_possible_entrances_and_exits_built(ride))
{
tool_cancel();
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_NO_TRACK))
window_close(w);
}
else
{
gRideEntranceExitPlaceType = entranceExitType ^ 1;
window_invalidate_by_class(WC_RIDE_CONSTRUCTION);
gCurrentToolWidget.widget_index = entranceExitType ? WIDX_MAZE_ENTRANCE : WIDX_MAZE_EXIT;
}
Ride* ride = get_ride(rideIndex);
if (ride_are_all_possible_entrances_and_exits_built(ride))
{
tool_cancel();
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_NO_TRACK))
window_close_by_class(WC_RIDE_CONSTRUCTION);
}
else
{
gRideEntranceExitPlaceType = gRideEntranceExitPlaceType ^ 1;
window_invalidate_by_class(WC_RIDE_CONSTRUCTION);
gCurrentToolWidget.widget_index = gRideEntranceExitPlaceType ? WIDX_MAZE_ENTRANCE : WIDX_MAZE_EXIT;
}
});
auto res = GameActions::Execute(&rideEntranceExitPlaceAction);
}
/**

View File

@ -17,6 +17,7 @@
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/RideEntranceExitPlaceAction.hpp>
#include <openrct2/actions/TrackPlaceAction.hpp>
#include <openrct2/actions/TrackRemoveAction.hpp>
#include <openrct2/audio/audio.h>
@ -3938,15 +3939,35 @@ static void ride_construction_tooldown_entrance_exit(int32_t screenX, int32_t sc
if (gRideEntranceExitPlaceDirection == 255)
return;
gGameCommandErrorTitle = (gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_ENTRANCE)
? STR_CANT_BUILD_MOVE_ENTRANCE_FOR_THIS_RIDE_ATTRACTION
: STR_CANT_BUILD_MOVE_EXIT_FOR_THIS_RIDE_ATTRACTION;
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(
{ _unkF44188.x, _unkF44188.y }, direction_reverse(gRideEntranceExitPlaceDirection), gRideEntranceExitPlaceRideIndex,
gRideEntranceExitPlaceStationIndex, gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_EXIT);
game_command_callback = game_command_callback_place_ride_entrance_or_exit;
game_do_command(
_unkF44188.x, (GAME_COMMAND_FLAG_APPLY) | (direction_reverse(gRideEntranceExitPlaceDirection) << 8), _unkF44188.y,
gRideEntranceExitPlaceRideIndex | (gRideEntranceExitPlaceType << 8), GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT,
gRideEntranceExitPlaceStationIndex, 0);
rideEntranceExitPlaceAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) {
if (result->Error != GA_ERROR::OK)
return;
audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z);
Ride* ride = get_ride(gRideEntranceExitPlaceRideIndex);
if (ride_are_all_possible_entrances_and_exits_built(ride))
{
tool_cancel();
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_NO_TRACK))
{
window_close_by_class(WC_RIDE_CONSTRUCTION);
}
}
else
{
gRideEntranceExitPlaceType = gRideEntranceExitPlaceType ^ 1;
window_invalidate_by_class(WC_RIDE_CONSTRUCTION);
gCurrentToolWidget.widget_index = (gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_ENTRANCE)
? WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE
: WC_RIDE_CONSTRUCTION__WIDX_EXIT;
}
});
auto res = GameActions::Execute(&rideEntranceExitPlaceAction);
}
void window_ride_construction_keyboard_shortcut_turn_left()

View File

@ -93,7 +93,7 @@ static GAME_COMMAND_CALLBACK_POINTER * const game_command_callback_table[] = {
nullptr,
nullptr,
game_command_callback_place_banner,
game_command_callback_place_ride_entrance_or_exit,
nullptr,
game_command_callback_hire_new_staff_member,
game_command_callback_pickup_guest,
game_command_callback_pickup_staff
@ -1272,7 +1272,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
nullptr,
game_command_set_ride_name,
nullptr,
game_command_place_ride_entrance_or_exit,
nullptr,
nullptr,
nullptr,
nullptr,

View File

@ -18,19 +18,19 @@ struct rct_s6_data;
enum GAME_COMMAND
{
GAME_COMMAND_SET_RIDE_APPEARANCE, // GA
GAME_COMMAND_SET_LAND_HEIGHT, // GA
GAME_COMMAND_TOGGLE_PAUSE, // GA
GAME_COMMAND_PLACE_TRACK, // GA
GAME_COMMAND_REMOVE_TRACK, // GA
GAME_COMMAND_LOAD_OR_QUIT, // GA
GAME_COMMAND_CREATE_RIDE, // GA
GAME_COMMAND_DEMOLISH_RIDE, // GA
GAME_COMMAND_SET_RIDE_STATUS, // GA
GAME_COMMAND_SET_RIDE_VEHICLES, // GA
GAME_COMMAND_SET_RIDE_NAME, // GA
GAME_COMMAND_SET_RIDE_SETTING, // GA
GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT,
GAME_COMMAND_SET_RIDE_APPEARANCE, // GA
GAME_COMMAND_SET_LAND_HEIGHT, // GA
GAME_COMMAND_TOGGLE_PAUSE, // GA
GAME_COMMAND_PLACE_TRACK, // GA
GAME_COMMAND_REMOVE_TRACK, // GA
GAME_COMMAND_LOAD_OR_QUIT, // GA
GAME_COMMAND_CREATE_RIDE, // GA
GAME_COMMAND_DEMOLISH_RIDE, // GA
GAME_COMMAND_SET_RIDE_STATUS, // GA
GAME_COMMAND_SET_RIDE_VEHICLES, // GA
GAME_COMMAND_SET_RIDE_NAME, // GA
GAME_COMMAND_SET_RIDE_SETTING, // GA
GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT, // GA
GAME_COMMAND_REMOVE_RIDE_ENTRANCE_OR_EXIT, // GA
GAME_COMMAND_REMOVE_SCENERY, // GA
GAME_COMMAND_PLACE_SCENERY, // GA

View File

@ -15,6 +15,7 @@
#include "ParkImporter.h"
#include "PlatformEnvironment.h"
#include "actions/GameAction.h"
#include "actions/RideEntranceExitPlaceAction.hpp"
#include "actions/RideSetSetting.hpp"
#include "actions/TrackPlaceAction.hpp"
#include "config/Config.h"
@ -503,6 +504,17 @@ namespace OpenRCT2
result.action->SetFlags(command.ebx & 0xFF);
break;
}
case GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT:
{
CoordsXY loc = { (int32_t)(command.eax & 0xFFFF), (int32_t)(command.ecx & 0xFFFF) };
Direction direction = (command.ebx >> 8) & 0xFF;
ride_id_t rideId = command.edx & 0xFF;
uint8_t stationNum = command.edi & 0xFF;
bool isExit = ((command.edx >> 8) & 0xFF) != 0;
result.action = std::make_unique<RideEntranceExitPlaceAction>(loc, direction, rideId, stationNum, isExit);
result.action->SetFlags(command.ebx & 0xFF);
break;
}
default:
throw std::runtime_error("Deprecated game command requires replay translation.");
}

View File

@ -26,6 +26,7 @@
#include "PlacePeepSpawnAction.hpp"
#include "RideCreateAction.hpp"
#include "RideDemolishAction.hpp"
#include "RideEntranceExitPlaceAction.hpp"
#include "RideEntranceExitRemoveAction.hpp"
#include "RideSetAppearanceAction.hpp"
#include "RideSetColourScheme.hpp"
@ -64,6 +65,7 @@ namespace GameActions
Register<PlacePeepSpawnAction>();
Register<RideCreateAction>();
Register<RideDemolishAction>();
Register<RideEntranceExitPlaceAction>();
Register<RideEntranceExitRemoveAction>();
Register<RideSetColourSchemeAction>();
Register<RideSetNameAction>();

View File

@ -0,0 +1,285 @@
/*****************************************************************************
* 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 "../actions/RideEntranceExitRemoveAction.hpp"
#include "../management/Finance.h"
#include "../ride/Ride.h"
#include "../ride/Station.h"
#include "../world/Entrance.h"
#include "../world/MapAnimation.h"
#include "../world/Sprite.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(RideEntranceExitPlaceAction, GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT, GameActionResult)
{
private:
CoordsXY _loc;
Direction _direction;
NetworkRideId_t _rideIndex;
uint8_t _stationNum;
bool _isExit;
public:
RideEntranceExitPlaceAction() = default;
RideEntranceExitPlaceAction(CoordsXY loc, Direction direction, ride_id_t rideIndex, uint8_t stationNum, bool isExit)
: _loc(loc)
, _direction(direction)
, _rideIndex(rideIndex)
, _stationNum(stationNum)
, _isExit(isExit)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_direction) << DS_TAG(_rideIndex) << DS_TAG(_stationNum) << DS_TAG(_isExit);
}
GameActionResult::Ptr Query() const override
{
auto errorTitle = _isExit ? STR_CANT_BUILD_MOVE_EXIT_FOR_THIS_RIDE_ATTRACTION
: STR_CANT_BUILD_MOVE_ENTRANCE_FOR_THIS_RIDE_ATTRACTION;
if (!map_check_free_elements_and_reorganise(1))
{
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, errorTitle);
}
if (_rideIndex >= MAX_RIDES || _rideIndex == RIDE_ID_NULL)
{
log_warning("Invalid game command for ride %u", _rideIndex);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, errorTitle);
}
Ride* ride = get_ride(_rideIndex);
if (ride == nullptr || ride->type == RIDE_TYPE_NULL)
{
log_warning("Invalid game command for ride %u", _rideIndex);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, errorTitle);
}
if (_stationNum > MAX_STATIONS)
{
log_warning("Invalid station number for ride. stationNum: %u", _stationNum);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, errorTitle);
}
if (ride->status != RIDE_STATUS_CLOSED)
{
return MakeResult(GA_ERROR::NOT_CLOSED, errorTitle, STR_MUST_BE_CLOSED_FIRST);
}
if (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK)
{
return MakeResult(GA_ERROR::DISALLOWED, errorTitle, STR_NOT_ALLOWED_TO_MODIFY_STATION);
}
ride_clear_for_construction(ride);
ride_remove_peeps(ride);
const auto location = _isExit ? ride_get_exit_location(ride, _stationNum)
: ride_get_entrance_location(ride, _stationNum);
if (!location.isNull())
{
auto rideEntranceExitRemove = RideEntranceExitRemoveAction(
{ location.x * 32, location.y * 32 }, _rideIndex, _stationNum, _isExit);
rideEntranceExitRemove.SetFlags(GetFlags());
auto result = GameActions::QueryNested(&rideEntranceExitRemove);
if (result->Error != GA_ERROR::OK)
{
return result;
}
}
auto z = ride->stations[_stationNum].Height * 8;
gCommandPosition.z = z;
if (!gCheatsSandboxMode && !map_is_location_owned(_loc.x, _loc.y, z))
{
return MakeResult(GA_ERROR::NOT_OWNED, errorTitle);
}
int8_t clear_z = (z / 8) + (_isExit ? 5 : 7);
auto cost = MONEY32_UNDEFINED;
if (!map_can_construct_with_clear_at(
_loc.x, _loc.y, z / 8, clear_z, &map_place_non_scenery_clear_func, { 0b1111, 0 }, GetFlags(), &cost,
CREATE_CROSSING_MODE_NONE))
{
return MakeResult(GA_ERROR::NO_CLEARANCE, errorTitle, gGameCommandErrorText, gCommonFormatArgs);
}
if (gMapGroundFlags & ELEMENT_IS_UNDERWATER)
{
return MakeResult(GA_ERROR::DISALLOWED, errorTitle, STR_RIDE_CANT_BUILD_THIS_UNDERWATER);
}
if (z / 8 > MaxRideEntranceOrExitHeight)
{
return MakeResult(GA_ERROR::DISALLOWED, errorTitle, STR_TOO_HIGH);
}
auto res = MakeResult();
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = tile_element_height(_loc.x, _loc.y);
res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
return res;
}
GameActionResult::Ptr Execute() const override
{
// Remember when in unknown station num mode rideIndex is unknown and z is set
// When in known station num mode rideIndex is known and z is unknown
auto errorTitle = _isExit ? STR_CANT_BUILD_MOVE_EXIT_FOR_THIS_RIDE_ATTRACTION
: STR_CANT_BUILD_MOVE_ENTRANCE_FOR_THIS_RIDE_ATTRACTION;
Ride* ride = get_ride(_rideIndex);
if (ride == nullptr || ride->type == RIDE_TYPE_NULL)
{
log_warning("Invalid game command for ride %u", _rideIndex);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, errorTitle);
}
ride_clear_for_construction(ride);
ride_remove_peeps(ride);
const auto location = _isExit ? ride_get_exit_location(ride, _stationNum)
: ride_get_entrance_location(ride, _stationNum);
if (!location.isNull())
{
auto rideEntranceExitRemove = RideEntranceExitRemoveAction(
{ location.x * 32, location.y * 32 }, _rideIndex, _stationNum, _isExit);
rideEntranceExitRemove.SetFlags(GetFlags());
auto result = GameActions::ExecuteNested(&rideEntranceExitRemove);
if (result->Error != GA_ERROR::OK)
{
return result;
}
}
auto z = ride->stations[_stationNum].Height * 8;
gCommandPosition.z = z;
if (!(GetFlags() & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_remove_litter(_loc.x, _loc.y, z);
wall_remove_at_z(_loc.x, _loc.y, z);
}
int8_t clear_z = (z / 8) + (_isExit ? 5 : 7);
auto cost = MONEY32_UNDEFINED;
if (!map_can_construct_with_clear_at(
_loc.x, _loc.y, z / 8, clear_z, &map_place_non_scenery_clear_func, { 0b1111, 0 }, GetFlags(), &cost,
CREATE_CROSSING_MODE_NONE))
{
return MakeResult(GA_ERROR::NO_CLEARANCE, errorTitle, gGameCommandErrorText, gCommonFormatArgs);
}
auto res = MakeResult();
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = tile_element_height(_loc.x, _loc.y);
res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
TileElement* tileElement = tile_element_insert(_loc.x / 32, _loc.y / 32, z / 8, 0b1111);
assert(tileElement != nullptr);
tileElement->SetType(TILE_ELEMENT_TYPE_ENTRANCE);
tileElement->SetDirection(_direction);
tileElement->clearance_height = clear_z;
tileElement->AsEntrance()->SetEntranceType(_isExit ? ENTRANCE_TYPE_RIDE_EXIT : ENTRANCE_TYPE_RIDE_ENTRANCE);
tileElement->AsEntrance()->SetStationIndex(_stationNum);
tileElement->AsEntrance()->SetRideIndex(_rideIndex);
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
tileElement->SetGhost(true);
}
if (_isExit)
{
ride_set_exit_location(
ride, _stationNum, { _loc.x / 32, _loc.y / 32, z / 8, (uint8_t)tileElement->GetDirection() });
}
else
{
ride_set_entrance_location(
ride, _stationNum, { _loc.x / 32, _loc.y / 32, z / 8, (uint8_t)tileElement->GetDirection() });
ride->stations[_stationNum].LastPeepInQueue = SPRITE_INDEX_NULL;
ride->stations[_stationNum].QueueLength = 0;
map_animation_create(MAP_ANIMATION_TYPE_RIDE_ENTRANCE, _loc.x, _loc.y, z / 8);
}
footpath_queue_chain_reset();
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
maze_entrance_hedge_removal(_loc.x, _loc.y, tileElement);
}
footpath_connect_edges(_loc.x, _loc.y, tileElement, GetFlags());
footpath_update_queue_chains();
map_invalidate_tile_full(_loc.x, _loc.y);
return res;
}
static GameActionResult::Ptr TrackPlaceQuery(const CoordsXYZ loc, const bool isExit)
{
auto errorTitle = isExit ? STR_CANT_BUILD_MOVE_EXIT_FOR_THIS_RIDE_ATTRACTION
: STR_CANT_BUILD_MOVE_ENTRANCE_FOR_THIS_RIDE_ATTRACTION;
if (!map_check_free_elements_and_reorganise(1))
{
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, errorTitle);
}
if (!gCheatsSandboxMode && !map_is_location_owned(loc.x, loc.y, loc.z))
{
return MakeResult(GA_ERROR::NOT_OWNED, errorTitle);
}
int16_t baseZ = loc.z / 8;
int16_t clearZ = baseZ + (isExit ? 5 : 7);
auto cost = MONEY32_UNDEFINED;
if (!map_can_construct_with_clear_at(
loc.x, loc.y, baseZ, clearZ, &map_place_non_scenery_clear_func, { 0b1111, 0 }, 0, &cost,
CREATE_CROSSING_MODE_NONE))
{
return MakeResult(GA_ERROR::NO_CLEARANCE, errorTitle, gGameCommandErrorText, gCommonFormatArgs);
}
if (gMapGroundFlags & ELEMENT_IS_UNDERWATER)
{
return MakeResult(GA_ERROR::DISALLOWED, errorTitle, STR_RIDE_CANT_BUILD_THIS_UNDERWATER);
}
if (baseZ > MaxRideEntranceOrExitHeight)
{
return MakeResult(GA_ERROR::DISALLOWED, errorTitle, STR_TOO_HIGH);
}
auto res = MakeResult();
res->Position.x = loc.x + 16;
res->Position.y = loc.y + 16;
res->Position.z = tile_element_height(loc.x, loc.y);
res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
return res;
}
};

View File

@ -331,9 +331,9 @@ template<> struct DataSerializerTraits<CoordsXY>
}
static void decode(IStream* stream, CoordsXY& coords)
{
auto x = ByteSwapBE(stream->ReadValue<int16_t>());
auto y = ByteSwapBE(stream->ReadValue<int16_t>());
coords = CoordsXY(x, y);
auto x = ByteSwapBE(stream->ReadValue<int32_t>());
auto y = ByteSwapBE(stream->ReadValue<int32_t>());
coords = CoordsXY{ x, y };
}
static void log(IStream* stream, const CoordsXY& coords)
{

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

View File

@ -1166,8 +1166,6 @@ rct_vehicle* ride_get_broken_vehicle(Ride* ride);
void window_ride_construction_do_station_check();
void window_ride_construction_do_entrance_exit_check();
void game_command_callback_place_ride_entrance_or_exit(
int32_t eax, int32_t ebx, int32_t ecx, int32_t edx, int32_t esi, int32_t edi, int32_t ebp);
void ride_delete(Ride* ride);
void ride_renew(Ride* ride);

View File

@ -13,6 +13,7 @@
#include "../Game.h"
#include "../OpenRCT2.h"
#include "../actions/LargeSceneryRemoveAction.hpp"
#include "../actions/RideEntranceExitPlaceAction.hpp"
#include "../actions/RideSetSetting.hpp"
#include "../actions/RideSetVehiclesAction.hpp"
#include "../actions/SmallSceneryPlaceAction.hpp"
@ -1217,9 +1218,8 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y,
if (_trackDesignPlaceOperation == PTD_OPERATION_1)
{
cost = game_do_command(
mapCoord.x, 0 | rotation << 8, mapCoord.y, (z / 16) & 0xFF,
GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT, -1, 0);
auto res = RideEntranceExitPlaceAction::TrackPlaceQuery({ mapCoord.x, mapCoord.y, z }, false);
cost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED;
}
else
{
@ -1232,9 +1232,10 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y,
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5
| GAME_COMMAND_FLAG_GHOST;
}
cost = game_do_command(
mapCoord.x, flags | rotation << 8, mapCoord.y, ride->id, GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT,
0, 0);
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(mapCoord, rotation, ride->id, 0, false);
rideEntranceExitPlaceAction.SetFlags(flags);
auto res = GameActions::ExecuteNested(&rideEntranceExitPlaceAction);
cost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED;
}
if (cost != MONEY32_UNDEFINED)
{
@ -1251,9 +1252,8 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y,
if (_trackDesignPlaceOperation == PTD_OPERATION_1)
{
cost = game_do_command(
mapCoord.x, 0 | rotation << 8, mapCoord.y, ((z / 16) & 0xFF) | (1 << 8),
GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT, -1, 0);
auto res = RideEntranceExitPlaceAction::TrackPlaceQuery({ mapCoord.x, mapCoord.y, z }, true);
cost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED;
}
else
{
@ -1266,9 +1266,10 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y,
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5
| GAME_COMMAND_FLAG_GHOST;
}
cost = game_do_command(
mapCoord.x, flags | rotation << 8, mapCoord.y, ride->id | (1 << 8),
GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT, 0, 0);
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(mapCoord, rotation, ride->id, 0, true);
rideEntranceExitPlaceAction.SetFlags(flags);
auto res = GameActions::ExecuteNested(&rideEntranceExitPlaceAction);
cost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED;
}
if (cost != MONEY32_UNDEFINED)
{
@ -1573,10 +1574,10 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in
case PTD_OPERATION_GET_COST:
{
rotation = (rotation + entrance->direction) & 3;
uint8_t isExit = 0;
bool isExit = false;
if (entrance->direction & (1 << 7))
{
isExit = 1;
isExit = true;
}
if (_trackDesignPlaceOperation != PTD_OPERATION_1)
@ -1601,29 +1602,33 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in
}
int32_t stationIndex = tile_element->AsTrack()->GetStationIndex();
uint8_t bl = 1;
uint8_t flags = GAME_COMMAND_FLAG_APPLY;
if (_trackDesignPlaceOperation == PTD_OPERATION_GET_COST)
{
bl = 41;
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5;
}
if (_trackDesignPlaceOperation == PTD_OPERATION_4)
{
bl = 105;
flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5
| GAME_COMMAND_FLAG_GHOST;
}
if (_trackDesignPlaceOperation == PTD_OPERATION_1)
{
bl = 0;
flags = 0;
}
gGameCommandErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE;
money32 cost = game_do_command(
x, bl | (rotation << 8), y, ride->id | (isExit << 8), GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT,
stationIndex, 0);
_trackDesignPlaceCost += cost;
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(
{ x, y }, rotation, ride->id, stationIndex, isExit);
rideEntranceExitPlaceAction.SetFlags(flags);
auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&rideEntranceExitPlaceAction)
: GameActions::QueryNested(&rideEntranceExitPlaceAction);
if (cost == MONEY32_UNDEFINED)
_trackDesignPlaceCost += res->Cost;
if (res->Error != GA_ERROR::OK)
{
_trackDesignPlaceCost = cost;
_trackDesignPlaceCost = MONEY32_UNDEFINED;
return false;
}
_trackDesignPlaceStateEntranceExitPlaced = true;
@ -1635,19 +1640,16 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in
z = (entrance->z == (int8_t)(uint8_t)0x80) ? -1 : entrance->z;
z *= 8;
z += gTrackPreviewOrigin.z;
z >>= 4;
gGameCommandErrorTitle = STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE;
money32 cost = game_do_command(
x, 0 | (rotation << 8), y, z | (isExit << 8), GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT, -1, 0);
if (cost == MONEY32_UNDEFINED)
auto res = RideEntranceExitPlaceAction::TrackPlaceQuery({ x, y, z }, false);
if (res->Error != GA_ERROR::OK)
{
_trackDesignPlaceCost = cost;
_trackDesignPlaceCost = MONEY32_UNDEFINED;
return false;
}
else
{
_trackDesignPlaceCost += cost;
_trackDesignPlaceCost += res->Cost;
_trackDesignPlaceStateEntranceExitPlaced = true;
}
}

View File

@ -108,30 +108,6 @@ uint8_t _rideConstructionState2;
bool _stationConstructed;
bool _deferClose;
void game_command_callback_place_ride_entrance_or_exit(
[[maybe_unused]] int32_t eax, [[maybe_unused]] int32_t ebx, [[maybe_unused]] int32_t ecx, [[maybe_unused]] int32_t edx,
[[maybe_unused]] int32_t esi, [[maybe_unused]] int32_t edi, [[maybe_unused]] int32_t ebp)
{
audio_play_sound_at_location(SOUND_PLACE_ITEM, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z);
Ride* ride = get_ride(gRideEntranceExitPlaceRideIndex);
if (ride_are_all_possible_entrances_and_exits_built(ride))
{
tool_cancel();
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_NO_TRACK))
{
window_close_by_class(WC_RIDE_CONSTRUCTION);
}
}
else
{
gRideEntranceExitPlaceType ^= 1;
gCurrentToolWidget.widget_index = (gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_ENTRANCE)
? WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE
: WC_RIDE_CONSTRUCTION__WIDX_EXIT;
}
}
/**
*
* rct2: 0x006CA162

View File

@ -12,6 +12,7 @@
#include "../Cheats.h"
#include "../Game.h"
#include "../OpenRCT2.h"
#include "../actions/RideEntranceExitPlaceAction.hpp"
#include "../actions/RideEntranceExitRemoveAction.hpp"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
@ -87,238 +88,15 @@ static money32 ParkEntranceRemove(int16_t x, int16_t y, uint8_t z, uint8_t flags
return 0;
}
static money32 RideEntranceExitPlace(
int16_t x, int16_t y, int16_t z, uint8_t direction, uint8_t flags, ride_id_t rideIndex, uint8_t stationNum, bool isExit)
{
// Remember when in unknown station num mode rideIndex is unknown and z is set
// When in known station num mode rideIndex is known and z is unknown
money32 cost = 0;
gCommandPosition.x = x;
gCommandPosition.y = y;
if (!map_check_free_elements_and_reorganise(1))
{
return MONEY32_UNDEFINED;
}
if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) && game_is_paused() && !gCheatsBuildInPauseMode)
{
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
if (stationNum == 0xFF)
{
z *= 16;
if (flags & GAME_COMMAND_FLAG_APPLY)
{
return MONEY32_UNDEFINED;
}
if (!gCheatsSandboxMode && !map_is_location_owned(x, y, z))
{
return MONEY32_UNDEFINED;
}
int16_t clear_z = z / 8 + (isExit ? 5 : 7);
if (!map_can_construct_with_clear_at(
x, y, z / 8, clear_z, &map_place_non_scenery_clear_func, { 0b1111, 0 }, flags, &cost,
CREATE_CROSSING_MODE_NONE))
{
return MONEY32_UNDEFINED;
}
if (gMapGroundFlags & ELEMENT_IS_UNDERWATER)
{
gGameCommandErrorText = STR_RIDE_CANT_BUILD_THIS_UNDERWATER;
return MONEY32_UNDEFINED;
}
if (z / 8 > 244)
{
gGameCommandErrorText = STR_TOO_HIGH;
return MONEY32_UNDEFINED;
}
}
else
{
if (rideIndex >= MAX_RIDES)
{
log_warning("Invalid game command for ride %u", rideIndex);
return MONEY32_UNDEFINED;
}
Ride* ride = get_ride(rideIndex);
if (ride->type == RIDE_TYPE_NULL)
{
log_warning("Invalid game command for ride %u", rideIndex);
return MONEY32_UNDEFINED;
}
if (ride->status != RIDE_STATUS_CLOSED)
{
gGameCommandErrorText = STR_MUST_BE_CLOSED_FIRST;
return MONEY32_UNDEFINED;
}
if (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK)
{
gGameCommandErrorText = STR_NOT_ALLOWED_TO_MODIFY_STATION;
return MONEY32_UNDEFINED;
}
ride_clear_for_construction(ride);
ride_remove_peeps(ride);
bool requiresRemove = false;
LocationXY16 removeCoord = { 0, 0 };
if (isExit)
{
const auto exit = ride_get_exit_location(ride, stationNum);
if (!exit.isNull())
{
if (flags & GAME_COMMAND_FLAG_GHOST)
{
gGameCommandErrorText = 0;
return MONEY32_UNDEFINED;
}
removeCoord.x = exit.x * 32;
removeCoord.y = exit.y * 32;
requiresRemove = true;
}
}
else
{
const auto entrance = ride_get_entrance_location(ride, stationNum);
if (!entrance.isNull())
{
if (flags & GAME_COMMAND_FLAG_GHOST)
{
gGameCommandErrorText = 0;
return MONEY32_UNDEFINED;
}
removeCoord.x = entrance.x * 32;
removeCoord.y = entrance.y * 32;
requiresRemove = true;
}
}
if (requiresRemove)
{
auto rideEntranceExitRemove = RideEntranceExitRemoveAction(
{ removeCoord.x, removeCoord.y }, rideIndex, stationNum, isExit);
rideEntranceExitRemove.SetFlags(flags);
auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&rideEntranceExitRemove)
: GameActions::QueryNested(&rideEntranceExitRemove);
if (res->Error != GA_ERROR::OK)
{
return MONEY32_UNDEFINED;
}
}
z = ride->stations[stationNum].Height * 8;
gCommandPosition.z = z;
if ((flags & GAME_COMMAND_FLAG_APPLY) && !(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED)
&& !(flags & GAME_COMMAND_FLAG_GHOST))
{
footpath_remove_litter(x, y, z);
wall_remove_at_z(x, y, z);
}
if (!gCheatsSandboxMode && !map_is_location_owned(x, y, z))
{
return MONEY32_UNDEFINED;
}
int8_t clear_z = (z / 8) + (isExit ? 5 : 7);
if (!map_can_construct_with_clear_at(
x, y, z / 8, clear_z, &map_place_non_scenery_clear_func, { 0b1111, 0 }, flags, &cost,
CREATE_CROSSING_MODE_NONE))
{
return MONEY32_UNDEFINED;
}
if (gMapGroundFlags & ELEMENT_IS_UNDERWATER)
{
gGameCommandErrorText = STR_RIDE_CANT_BUILD_THIS_UNDERWATER;
return MONEY32_UNDEFINED;
}
if (z / 8 > 244)
{
gGameCommandErrorText = STR_TOO_HIGH;
return MONEY32_UNDEFINED;
}
if (flags & GAME_COMMAND_FLAG_APPLY)
{
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* tileElement = tile_element_insert(x / 32, y / 32, z / 8, 0xF);
assert(tileElement != nullptr);
tileElement->SetType(TILE_ELEMENT_TYPE_ENTRANCE);
tileElement->SetDirection(direction);
tileElement->clearance_height = clear_z;
tileElement->AsEntrance()->SetEntranceType(isExit ? ENTRANCE_TYPE_RIDE_EXIT : ENTRANCE_TYPE_RIDE_ENTRANCE);
tileElement->AsEntrance()->SetStationIndex(stationNum);
tileElement->AsEntrance()->SetRideIndex(rideIndex);
if (flags & GAME_COMMAND_FLAG_GHOST)
{
tileElement->SetGhost(true);
}
if (isExit)
{
ride_set_exit_location(ride, stationNum, { x / 32, y / 32, z / 8, (uint8_t)tileElement->GetDirection() });
}
else
{
ride_set_entrance_location(ride, stationNum, { x / 32, y / 32, z / 8, (uint8_t)tileElement->GetDirection() });
ride->stations[stationNum].LastPeepInQueue = SPRITE_INDEX_NULL;
ride->stations[stationNum].QueueLength = 0;
map_animation_create(MAP_ANIMATION_TYPE_RIDE_ENTRANCE, x, y, z / 8);
}
footpath_queue_chain_reset();
if (!(flags & GAME_COMMAND_FLAG_GHOST))
{
maze_entrance_hedge_removal(x, y, tileElement);
}
footpath_connect_edges(x, y, tileElement, flags);
footpath_update_queue_chains();
map_invalidate_tile_full(x, y);
}
}
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
return cost;
}
static money32 RideEntranceExitPlaceGhost(
ride_id_t rideIndex, int16_t x, int16_t y, uint8_t direction, uint8_t placeType, uint8_t stationNum)
{
return game_do_command(
x,
(GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_GHOST)
| (direction << 8),
y, rideIndex | (placeType << 8), GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT, stationNum, 0);
auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(
{ x, y }, direction, rideIndex, stationNum, placeType == ENTRANCE_TYPE_RIDE_EXIT);
rideEntranceExitPlaceAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST);
auto res = GameActions::Execute(&rideEntranceExitPlaceAction);
return res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED;
}
/**
@ -410,19 +188,6 @@ money32 ride_entrance_exit_place_ghost(
return result;
}
/**
*
* rct2: 0x006660A8
*/
void game_command_place_ride_entrance_or_exit(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi,
[[maybe_unused]] int32_t* ebp)
{
*ebx = RideEntranceExitPlace(
*eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*ebx >> 8) & 0xFF, *ebx & 0xFF, *edx & 0xFF, *edi & 0xFF,
((*edx >> 8) & 0xFF) != 0);
}
/**
* Replaces the outer hedge walls for an entrance placement removal.
* rct2: 0x00666D6F

View File

@ -37,6 +37,8 @@ extern uint8_t gParkEntranceGhostDirection;
#define MAX_PARK_ENTRANCES 4
constexpr int32_t MaxRideEntranceOrExitHeight = 244;
extern std::vector<CoordsXYZD> gParkEntrances;
extern CoordsXYZD gRideEntranceExitGhostPosition;