2017-04-24 21:46:58 +02:00
|
|
|
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
2017-02-25 14:51:30 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
|
|
|
*
|
|
|
|
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
|
|
|
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
|
|
|
*
|
|
|
|
* OpenRCT2 is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* A full copy of the GNU General Public License can be found in licence.txt
|
|
|
|
*****************************************************************************/
|
|
|
|
#pragma endregion
|
|
|
|
|
2017-03-04 11:18:31 +01:00
|
|
|
#include "../network/network.h"
|
2017-07-28 20:44:13 +02:00
|
|
|
#include "../OpenRCT2.h"
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2017-12-14 10:34:12 +01:00
|
|
|
#include "Entrance.h"
|
2018-01-11 10:59:26 +01:00
|
|
|
#include "Footpath.h"
|
2018-01-03 14:56:08 +01:00
|
|
|
#include "Map.h"
|
2018-03-19 23:28:40 +01:00
|
|
|
#include "MapAnimation.h"
|
2017-12-31 21:40:00 +01:00
|
|
|
#include "Park.h"
|
2018-03-19 23:34:37 +01:00
|
|
|
#include "Sprite.h"
|
2017-12-13 13:02:24 +01:00
|
|
|
#include "../Cheats.h"
|
2017-11-30 18:17:06 +01:00
|
|
|
#include "../Game.h"
|
2018-01-06 18:32:25 +01:00
|
|
|
#include "../localisation/StringIds.h"
|
2017-10-06 22:37:06 +02:00
|
|
|
#include "../management/Finance.h"
|
2017-10-17 13:51:47 +02:00
|
|
|
#include "../ride/Track.h"
|
2018-03-07 19:10:50 +01:00
|
|
|
#include "../ride/Station.h"
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2017-04-24 21:46:58 +02:00
|
|
|
bool gParkEntranceGhostExists = false;
|
2017-10-13 22:23:07 +02:00
|
|
|
LocationXYZ16 gParkEntranceGhostPosition = { 0, 0, 0 };
|
2017-04-24 21:46:58 +02:00
|
|
|
uint8 gParkEntranceGhostDirection = 0;
|
2018-02-16 09:50:40 +01:00
|
|
|
CoordsXYZD gParkEntrances[MAX_PARK_ENTRANCES];
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2018-02-16 09:50:40 +01:00
|
|
|
CoordsXYZD gRideEntranceExitGhostPosition;
|
2017-03-04 11:18:31 +01:00
|
|
|
uint8 gRideEntranceExitGhostStationIndex;
|
|
|
|
|
2017-02-25 14:51:30 +01:00
|
|
|
static void ParkEntranceRemoveSegment(sint32 x, sint32 y, sint32 z)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
rct_tile_element *tileElement;
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement = map_get_park_entrance_element_at(x, y, z, true);
|
|
|
|
if (tileElement == nullptr)
|
2017-03-01 22:19:15 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
map_invalidate_tile(x, y, tileElement->base_height * 8, tileElement->clearance_height * 8);
|
|
|
|
tile_element_remove(tileElement);
|
2017-03-01 22:19:15 +01:00
|
|
|
update_park_fences(x, y);
|
2017-02-25 14:51:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static money32 ParkEntranceRemove(sint16 x, sint16 y, uint8 z, uint8 flags)
|
|
|
|
{
|
2017-03-01 22:19:15 +01:00
|
|
|
sint32 entranceIndex, direction;
|
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode)
|
2017-03-01 22:19:15 +01:00
|
|
|
{
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
|
|
|
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE;
|
|
|
|
gCommandPosition.x = x;
|
|
|
|
gCommandPosition.y = y;
|
|
|
|
gCommandPosition.z = z * 16;
|
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
if (!(flags & GAME_COMMAND_FLAG_APPLY))
|
2017-03-01 22:19:15 +01:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-09 20:18:24 +01:00
|
|
|
entranceIndex = park_entrance_get_index(x, y, z * 16);
|
2017-06-06 23:24:18 +02:00
|
|
|
if (entranceIndex == -1)
|
2017-03-01 22:19:15 +01:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-13 22:23:07 +02:00
|
|
|
gParkEntrances[entranceIndex].x = LOCATION_NULL;
|
2017-03-01 22:19:15 +01:00
|
|
|
direction = (gParkEntrances[entranceIndex].direction - 1) & 3;
|
|
|
|
|
|
|
|
// Centre (sign)
|
|
|
|
ParkEntranceRemoveSegment(x, y, z * 2);
|
|
|
|
|
|
|
|
// Left post
|
|
|
|
ParkEntranceRemoveSegment(
|
2018-04-18 20:34:34 +02:00
|
|
|
x + CoordsDirectionDelta[direction].x,
|
|
|
|
y + CoordsDirectionDelta[direction].y,
|
2017-03-01 22:19:15 +01:00
|
|
|
z * 2
|
|
|
|
);
|
|
|
|
|
|
|
|
// Right post
|
|
|
|
ParkEntranceRemoveSegment(
|
2018-04-18 20:34:34 +02:00
|
|
|
x - CoordsDirectionDelta[direction].x,
|
|
|
|
y - CoordsDirectionDelta[direction].y,
|
2017-03-01 22:19:15 +01:00
|
|
|
z * 2
|
|
|
|
);
|
|
|
|
|
|
|
|
return 0;
|
2017-02-25 14:51:30 +01:00
|
|
|
}
|
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
static money32 RideEntranceExitPlace(sint16 x,
|
|
|
|
sint16 y,
|
|
|
|
sint16 z,
|
|
|
|
uint8 direction,
|
|
|
|
uint8 flags,
|
|
|
|
uint8 rideIndex,
|
|
|
|
uint8 stationNum,
|
2017-03-09 19:48:09 +01:00
|
|
|
bool isExit)
|
2017-03-04 11:18:31 +01:00
|
|
|
{
|
2017-05-15 09:28:43 +02:00
|
|
|
// Remember when in unknown station num mode rideIndex is unknown and z is set
|
2017-03-04 11:18:31 +01:00
|
|
|
// When in known station num mode rideIndex is known and z is unknown
|
|
|
|
|
|
|
|
money32 cost = 0;
|
|
|
|
gCommandPosition.x = x;
|
|
|
|
gCommandPosition.y = y;
|
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
if (!map_check_free_elements_and_reorganise(1))
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) && game_is_paused() && !gCheatsBuildInPauseMode)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
if (stationNum == 0xFF)
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
z *= 16;
|
|
|
|
if (flags & GAME_COMMAND_FLAG_APPLY)
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
return MONEY32_UNDEFINED;
|
2017-03-09 19:48:09 +01:00
|
|
|
}
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (!gCheatsSandboxMode && !map_is_location_owned(x, y, z))
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
sint16 clear_z = z / 8 + (isExit ? 5 : 7);
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
if (!gCheatsDisableClearanceChecks &&
|
2017-12-20 18:20:31 +01:00
|
|
|
!map_can_construct_with_clear_at(x, y, z / 8, clear_z, &map_place_non_scenery_clear_func, 0xF, flags, &cost, CREATE_CROSSING_MODE_NONE))
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
if (gMapGroundFlags & ELEMENT_IS_UNDERWATER)
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = STR_RIDE_CANT_BUILD_THIS_UNDERWATER;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 20:18:24 +01:00
|
|
|
if (z / 8 > 244)
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = STR_TOO_HIGH;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
2017-03-09 19:48:09 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
if (rideIndex >= MAX_RIDES)
|
|
|
|
{
|
|
|
|
log_warning("Invalid game command for ride %u", rideIndex);
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
2017-03-09 19:48:09 +01:00
|
|
|
|
2017-09-12 11:16:57 +02:00
|
|
|
Ride* ride = get_ride(rideIndex);
|
2017-03-04 11:18:31 +01:00
|
|
|
if (ride->type == RIDE_TYPE_NULL)
|
|
|
|
{
|
|
|
|
log_warning("Invalid game command for ride %u", rideIndex);
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
2017-03-09 19:48:09 +01:00
|
|
|
|
|
|
|
if (ride->status != RIDE_STATUS_CLOSED)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = STR_MUST_BE_CLOSED_FIRST;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = STR_NOT_ALLOWED_TO_MODIFY_STATION;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
|
|
|
ride_clear_for_construction(rideIndex);
|
|
|
|
ride_remove_peeps(rideIndex);
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
bool requiresRemove = false;
|
2017-10-13 22:23:07 +02:00
|
|
|
LocationXY16 removeCoord = { 0, 0 };
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (isExit)
|
|
|
|
{
|
2018-03-07 19:10:50 +01:00
|
|
|
const auto exit = ride_get_exit_location(rideIndex, stationNum);
|
|
|
|
if (!exit.isNull())
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
|
|
|
if (flags & GAME_COMMAND_FLAG_GHOST)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = 0;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2018-03-07 19:10:50 +01:00
|
|
|
removeCoord.x = exit.x * 32;
|
|
|
|
removeCoord.y = exit.y * 32;
|
2017-03-09 19:48:09 +01:00
|
|
|
requiresRemove = true;
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
2017-03-09 19:48:09 +01:00
|
|
|
}
|
2018-03-07 19:10:50 +01:00
|
|
|
else
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2018-03-07 19:10:50 +01:00
|
|
|
const auto entrance = ride_get_entrance_location(rideIndex, stationNum);
|
|
|
|
if (!entrance.isNull())
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2018-03-07 19:10:50 +01:00
|
|
|
if (flags & GAME_COMMAND_FLAG_GHOST)
|
|
|
|
{
|
|
|
|
gGameCommandErrorText = 0;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2018-03-07 19:10:50 +01:00
|
|
|
removeCoord.x = entrance.x * 32;
|
|
|
|
removeCoord.y = entrance.y * 32;
|
|
|
|
requiresRemove = true;
|
|
|
|
}
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (requiresRemove)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
money32 success = game_do_command(
|
2017-03-09 19:48:09 +01:00
|
|
|
removeCoord.x,
|
2017-03-04 11:18:31 +01:00
|
|
|
flags,
|
2017-03-09 19:48:09 +01:00
|
|
|
removeCoord.y,
|
2017-03-04 11:18:31 +01:00
|
|
|
rideIndex,
|
|
|
|
GAME_COMMAND_REMOVE_RIDE_ENTRANCE_OR_EXIT,
|
2017-03-09 19:48:09 +01:00
|
|
|
stationNum,
|
2017-03-04 11:18:31 +01:00
|
|
|
0
|
|
|
|
);
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (success == MONEY32_UNDEFINED)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
z = ride->station_heights[stationNum] * 8;
|
2017-03-04 11:18:31 +01:00
|
|
|
gCommandPosition.z = z;
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if ((flags & GAME_COMMAND_FLAG_APPLY) &&
|
2017-03-04 11:18:31 +01:00
|
|
|
!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) &&
|
2017-03-09 19:48:09 +01:00
|
|
|
!(flags & GAME_COMMAND_FLAG_GHOST))
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
footpath_remove_litter(x, y, z);
|
|
|
|
wall_remove_at_z(x, y, z);
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (!gCheatsSandboxMode && !map_is_location_owned(x, y, z))
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
sint8 clear_z = (z / 8) + (isExit ? 5 : 7);
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
if (!gCheatsDisableClearanceChecks &&
|
2017-12-20 18:20:31 +01:00
|
|
|
!map_can_construct_with_clear_at(x, y, z / 8, clear_z, &map_place_non_scenery_clear_func, 0xF, flags, &cost, CREATE_CROSSING_MODE_NONE))
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (gMapGroundFlags & ELEMENT_IS_UNDERWATER)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = STR_RIDE_CANT_BUILD_THIS_UNDERWATER;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (z / 8 > 244)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = STR_TOO_HIGH;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (flags & GAME_COMMAND_FLAG_APPLY)
|
|
|
|
{
|
2017-10-13 22:23:07 +02:00
|
|
|
LocationXYZ16 coord;
|
2017-03-04 11:18:31 +01:00
|
|
|
coord.x = x + 16;
|
|
|
|
coord.y = y + 16;
|
2017-10-31 12:57:40 +01:00
|
|
|
coord.z = tile_element_height(coord.x, coord.y);
|
2017-03-04 11:18:31 +01:00
|
|
|
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
|
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
rct_tile_element* tileElement = tile_element_insert(x / 32, y / 32, z / 8, 0xF);
|
|
|
|
assert(tileElement != nullptr);
|
2018-05-02 13:27:53 +02:00
|
|
|
tileElement->SetType(TILE_ELEMENT_TYPE_ENTRANCE);
|
|
|
|
tileElement->SetDirection(direction);
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement->clearance_height = clear_z;
|
|
|
|
tileElement->properties.entrance.type = isExit;
|
|
|
|
tileElement->properties.entrance.index = stationNum << 4;
|
|
|
|
tileElement->properties.entrance.ride_index = rideIndex;
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (flags & GAME_COMMAND_FLAG_GHOST)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement->flags |= TILE_ELEMENT_FLAG_GHOST;
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (isExit)
|
|
|
|
{
|
2018-03-07 19:10:50 +01:00
|
|
|
ride_set_exit_location(ride, stationNum, { x / 32, y / 32, z / 8, (uint8)tile_element_get_direction(tileElement)});
|
2017-03-09 19:48:09 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-03-07 19:10:50 +01:00
|
|
|
ride_set_entrance_location(ride, stationNum, { x / 32, y / 32, z / 8, (uint8)tile_element_get_direction(tileElement)});
|
2017-03-09 19:48:09 +01:00
|
|
|
ride->last_peep_in_queue[stationNum] = SPRITE_INDEX_NULL;
|
|
|
|
ride->queue_length[stationNum] = 0;
|
2017-03-04 11:18:31 +01:00
|
|
|
|
|
|
|
map_animation_create(MAP_ANIMATION_TYPE_RIDE_ENTRANCE, x, y, z / 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
footpath_queue_chain_reset();
|
2017-03-09 19:48:09 +01:00
|
|
|
|
|
|
|
if (!(flags & GAME_COMMAND_FLAG_GHOST))
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
maze_entrance_hedge_removal(x, y, tileElement);
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
2017-03-09 19:48:09 +01:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
footpath_connect_edges(x, y, tileElement, flags);
|
2017-06-02 01:29:50 +02:00
|
|
|
footpath_update_queue_chains();
|
2017-03-04 11:18:31 +01:00
|
|
|
|
|
|
|
map_invalidate_tile_full(x, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
|
|
|
|
return cost;
|
|
|
|
}
|
|
|
|
|
2017-03-09 20:18:24 +01:00
|
|
|
static money32 RideEntranceExitRemove(sint16 x, sint16 y, uint8 rideIndex, uint8 stationNum, uint8 flags)
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
if (rideIndex >= MAX_RIDES)
|
|
|
|
{
|
|
|
|
log_warning("Invalid game command for ride %u", rideIndex);
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
2017-03-09 19:48:09 +01:00
|
|
|
|
2017-09-12 11:16:57 +02:00
|
|
|
Ride* ride = get_ride(rideIndex);
|
2017-03-04 11:18:31 +01:00
|
|
|
if (ride->type == RIDE_TYPE_NULL)
|
|
|
|
{
|
2017-04-30 06:39:24 +02:00
|
|
|
log_warning("Invalid ride id %u for entrance/exit removal", rideIndex);
|
2017-03-04 11:18:31 +01:00
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (!(flags & GAME_COMMAND_FLAG_GHOST))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) &&
|
|
|
|
game_is_paused() &&
|
2017-03-09 19:48:09 +01:00
|
|
|
!gCheatsBuildInPauseMode)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (ride->status != RIDE_STATUS_CLOSED)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = STR_MUST_BE_CLOSED_FIRST;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
gGameCommandErrorText = STR_NOT_ALLOWED_TO_MODIFY_STATION;
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (flags & GAME_COMMAND_FLAG_APPLY)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
ride_clear_for_construction(rideIndex);
|
|
|
|
ride_remove_peeps(rideIndex);
|
|
|
|
invalidate_test_results(rideIndex);
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
bool found = false;
|
2017-10-31 14:03:45 +01:00
|
|
|
rct_tile_element* tileElement = map_get_first_element_at(x / 32, y / 32);
|
|
|
|
if (tileElement == nullptr)
|
2017-03-04 11:18:31 +01:00
|
|
|
{
|
|
|
|
log_warning("Invalid coordinates for entrance/exit removal x = %d, y = %d", x, y);
|
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
2017-03-09 19:48:09 +01:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2018-05-02 19:27:04 +02:00
|
|
|
if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE)
|
2017-03-04 11:18:31 +01:00
|
|
|
continue;
|
|
|
|
|
2018-03-07 19:10:50 +01:00
|
|
|
if (tile_element_get_ride_index(tileElement) != rideIndex)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (tile_element_get_station(tileElement) != stationNum)
|
2017-03-04 11:18:31 +01:00
|
|
|
continue;
|
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
if (flags & GAME_COMMAND_FLAG_5 && !(tileElement->flags & TILE_ELEMENT_FLAG_GHOST))
|
2017-03-04 11:18:31 +01:00
|
|
|
continue;
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
found = true;
|
2017-03-04 11:18:31 +01:00
|
|
|
break;
|
2017-03-09 19:48:09 +01:00
|
|
|
}
|
2017-10-31 14:03:45 +01:00
|
|
|
while (!tile_element_is_last_for_tile(tileElement++));
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (!found)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
return MONEY32_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
2017-10-13 22:23:07 +02:00
|
|
|
LocationXYZ16 coord;
|
2017-03-04 11:18:31 +01:00
|
|
|
coord.x = x + 16;
|
|
|
|
coord.y = y + 16;
|
2017-10-31 12:57:40 +01:00
|
|
|
coord.z = tile_element_height(coord.x, coord.y);
|
2017-03-04 11:18:31 +01:00
|
|
|
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
|
|
|
|
|
|
|
|
footpath_queue_chain_reset();
|
2017-10-31 14:03:45 +01:00
|
|
|
maze_entrance_hedge_replacement(x, y, tileElement);
|
|
|
|
footpath_remove_edges_at(x, y, tileElement);
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
bool isExit = tileElement->properties.entrance.type == ENTRANCE_TYPE_RIDE_EXIT;
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
tile_element_remove(tileElement);
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (isExit)
|
|
|
|
{
|
2018-03-07 19:10:50 +01:00
|
|
|
ride_clear_exit_location(ride, stationNum);
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
2017-03-09 19:48:09 +01:00
|
|
|
else
|
|
|
|
{
|
2018-03-07 19:10:50 +01:00
|
|
|
ride_clear_entrance_location(ride, stationNum);
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
|
|
|
|
2017-06-02 01:29:50 +02:00
|
|
|
footpath_update_queue_chains();
|
2017-03-04 11:18:31 +01:00
|
|
|
|
|
|
|
map_invalidate_tile_full(x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-09 21:03:23 +01:00
|
|
|
static money32 RideEntranceExitPlaceGhost(uint8 rideIndex, sint16 x, sint16 y, uint8 direction, uint8 placeType, uint8 stationNum)
|
2017-03-04 11:18:31 +01:00
|
|
|
{
|
|
|
|
return game_do_command(
|
|
|
|
x,
|
2017-06-06 23:24:18 +02:00
|
|
|
(GAME_COMMAND_FLAG_APPLY |
|
|
|
|
GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED |
|
|
|
|
GAME_COMMAND_FLAG_5 |
|
|
|
|
GAME_COMMAND_FLAG_GHOST) |
|
2017-03-09 19:48:09 +01:00
|
|
|
(direction << 8),
|
2017-03-04 11:18:31 +01:00
|
|
|
y,
|
|
|
|
rideIndex | (placeType << 8),
|
|
|
|
GAME_COMMAND_PLACE_RIDE_ENTRANCE_OR_EXIT,
|
|
|
|
stationNum,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
}
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x00666A63
|
|
|
|
*/
|
2018-04-20 13:56:37 +02:00
|
|
|
void game_command_remove_park_entrance(
|
|
|
|
sint32 * eax,
|
|
|
|
sint32 * ebx,
|
|
|
|
sint32 * ecx,
|
|
|
|
sint32 * edx,
|
|
|
|
[[maybe_unused]] sint32 * esi,
|
|
|
|
[[maybe_unused]] sint32 * edi,
|
|
|
|
[[maybe_unused]] sint32 * ebp)
|
2017-02-25 14:51:30 +01:00
|
|
|
{
|
2018-02-01 18:49:14 +01:00
|
|
|
*ebx = ParkEntranceRemove(
|
|
|
|
*eax & 0xFFFF,
|
|
|
|
*ecx & 0xFFFF,
|
|
|
|
*edx & 0xFF,
|
|
|
|
*ebx & 0xFF);
|
|
|
|
}
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x00666F9E
|
|
|
|
*/
|
|
|
|
void park_entrance_remove_ghost()
|
|
|
|
{
|
|
|
|
if (gParkEntranceGhostExists)
|
2017-02-25 14:51:30 +01:00
|
|
|
{
|
2018-02-01 18:49:14 +01:00
|
|
|
gParkEntranceGhostExists = false;
|
|
|
|
game_do_command(
|
|
|
|
gParkEntranceGhostPosition.x,
|
|
|
|
GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_APPLY,
|
|
|
|
gParkEntranceGhostPosition.y,
|
|
|
|
gParkEntranceGhostPosition.z,
|
|
|
|
GAME_COMMAND_REMOVE_PARK_ENTRANCE,
|
|
|
|
0,
|
|
|
|
0
|
|
|
|
);
|
2017-02-25 14:51:30 +01:00
|
|
|
}
|
2018-02-01 18:49:14 +01:00
|
|
|
}
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
sint32 park_entrance_get_index(sint32 x, sint32 y, sint32 z)
|
|
|
|
{
|
|
|
|
sint32 i;
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
for (i = 0; i < MAX_PARK_ENTRANCES; i++)
|
2017-02-25 14:51:30 +01:00
|
|
|
{
|
2018-02-01 18:49:14 +01:00
|
|
|
if (x == gParkEntrances[i].x &&
|
|
|
|
y == gParkEntrances[i].y &&
|
|
|
|
z == gParkEntrances[i].z)
|
2017-03-01 22:19:15 +01:00
|
|
|
{
|
2018-02-01 18:49:14 +01:00
|
|
|
return i;
|
2017-03-01 22:19:15 +01:00
|
|
|
}
|
2017-02-25 14:51:30 +01:00
|
|
|
}
|
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset_park_entrance()
|
|
|
|
{
|
|
|
|
for (auto &parkEntrance : gParkEntrances)
|
2017-02-25 14:51:30 +01:00
|
|
|
{
|
2018-02-01 18:49:14 +01:00
|
|
|
parkEntrance.x = LOCATION_NULL;
|
2017-02-25 14:51:30 +01:00
|
|
|
}
|
2018-02-01 18:49:14 +01:00
|
|
|
}
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
void ride_entrance_exit_place_provisional_ghost()
|
|
|
|
{
|
|
|
|
if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT) {
|
|
|
|
RideEntranceExitPlaceGhost(_currentRideIndex,
|
|
|
|
gRideEntranceExitGhostPosition.x,
|
|
|
|
gRideEntranceExitGhostPosition.y,
|
|
|
|
gRideEntranceExitGhostPosition.direction,
|
|
|
|
gRideEntranceExitPlaceType,
|
|
|
|
gRideEntranceExitGhostStationIndex);
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
2018-02-01 18:49:14 +01:00
|
|
|
}
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
void ride_entrance_exit_remove_ghost()
|
|
|
|
{
|
|
|
|
if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT) {
|
|
|
|
game_do_command(
|
|
|
|
gRideEntranceExitGhostPosition.x,
|
|
|
|
(GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_APPLY),
|
|
|
|
gRideEntranceExitGhostPosition.y,
|
|
|
|
_currentRideIndex,
|
|
|
|
GAME_COMMAND_REMOVE_RIDE_ENTRANCE_OR_EXIT,
|
|
|
|
gRideEntranceExitGhostStationIndex,
|
|
|
|
0
|
|
|
|
);
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
2018-02-01 18:49:14 +01:00
|
|
|
}
|
2017-03-04 11:18:31 +01:00
|
|
|
|
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006CA28C
|
|
|
|
*/
|
|
|
|
money32 ride_entrance_exit_place_ghost(sint32 rideIndex,
|
|
|
|
sint32 x,
|
|
|
|
sint32 y,
|
|
|
|
sint32 direction,
|
|
|
|
sint32 placeType,
|
|
|
|
sint32 stationNum)
|
|
|
|
{
|
|
|
|
ride_construction_remove_ghosts();
|
|
|
|
money32 result = RideEntranceExitPlaceGhost(rideIndex, x, y, direction, placeType, stationNum);
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
if (result != MONEY32_UNDEFINED)
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
2018-02-01 18:49:14 +01:00
|
|
|
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT;
|
|
|
|
gRideEntranceExitGhostPosition.x = x;
|
|
|
|
gRideEntranceExitGhostPosition.y = y;
|
|
|
|
gRideEntranceExitGhostPosition.direction = direction;
|
|
|
|
gRideEntranceExitGhostStationIndex = stationNum & 0xFF;
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
2018-02-01 18:49:14 +01:00
|
|
|
return result;
|
|
|
|
}
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006660A8
|
|
|
|
*/
|
2018-04-20 13:56:37 +02:00
|
|
|
void game_command_place_ride_entrance_or_exit(
|
|
|
|
sint32 * eax,
|
|
|
|
sint32 * ebx,
|
|
|
|
sint32 * ecx,
|
|
|
|
sint32 * edx,
|
|
|
|
[[maybe_unused]] sint32 * esi,
|
|
|
|
sint32 * edi,
|
|
|
|
[[maybe_unused]] sint32 * ebp)
|
2018-02-01 18:49:14 +01:00
|
|
|
{
|
|
|
|
*ebx = RideEntranceExitPlace(
|
|
|
|
*eax & 0xFFFF,
|
|
|
|
*ecx & 0xFFFF,
|
|
|
|
*edx & 0xFF,
|
|
|
|
(*ebx >> 8) & 0xFF,
|
|
|
|
*ebx & 0xFF,
|
|
|
|
*edx & 0xFF,
|
|
|
|
*edi & 0xFF,
|
|
|
|
((*edx >> 8) & 0xFF) != 0
|
2017-03-09 19:48:09 +01:00
|
|
|
);
|
2018-02-01 18:49:14 +01:00
|
|
|
}
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x0066640B
|
|
|
|
*/
|
2018-04-20 13:56:37 +02:00
|
|
|
void game_command_remove_ride_entrance_or_exit(
|
|
|
|
sint32 * eax,
|
|
|
|
sint32 * ebx,
|
|
|
|
sint32 * ecx,
|
|
|
|
sint32 * edx,
|
|
|
|
[[maybe_unused]] sint32 * esi,
|
|
|
|
sint32 * edi,
|
|
|
|
[[maybe_unused]] sint32 * ebp)
|
2018-02-01 18:49:14 +01:00
|
|
|
{
|
|
|
|
*ebx = RideEntranceExitRemove(
|
|
|
|
*eax & 0xFFFF,
|
|
|
|
*ecx & 0xFFFF,
|
|
|
|
*edx & 0xFF,
|
|
|
|
*edi & 0xFF,
|
|
|
|
*ebx & 0xFF
|
|
|
|
);
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
/**
|
|
|
|
* Replaces the outer hedge walls for an entrance placement removal.
|
|
|
|
* rct2: 0x00666D6F
|
|
|
|
*/
|
|
|
|
void maze_entrance_hedge_replacement(sint32 x, sint32 y, rct_tile_element *tileElement)
|
|
|
|
{
|
|
|
|
sint32 direction = tile_element_get_direction(tileElement);
|
2018-04-18 20:34:34 +02:00
|
|
|
x += CoordsDirectionDelta[direction].x;
|
|
|
|
y += CoordsDirectionDelta[direction].y;
|
2018-02-01 18:49:14 +01:00
|
|
|
sint32 z = tileElement->base_height;
|
|
|
|
sint32 rideIndex = track_element_get_ride_index(tileElement);
|
|
|
|
|
|
|
|
tileElement = map_get_first_element_at(x >> 5, y >> 5);
|
|
|
|
do {
|
2018-05-02 13:27:53 +02:00
|
|
|
if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) continue;
|
2018-02-01 18:49:14 +01:00
|
|
|
if (track_element_get_ride_index(tileElement) != rideIndex) continue;
|
|
|
|
if (tileElement->base_height != z) continue;
|
|
|
|
if (track_element_get_type(tileElement) != TRACK_ELEM_MAZE) continue;
|
|
|
|
|
|
|
|
// Each maze element is split into 4 sections with 4 different walls
|
|
|
|
uint8 mazeSection = direction * 4;
|
|
|
|
// Add the top outer wall
|
|
|
|
tileElement->properties.track.maze_entry |= (1 << ((mazeSection + 9) & 0x0F));
|
|
|
|
// Add the bottom outer wall
|
|
|
|
tileElement->properties.track.maze_entry |= (1 << ((mazeSection + 12) & 0x0F));
|
|
|
|
|
|
|
|
map_invalidate_tile(x, y, tileElement->base_height * 8, tileElement->clearance_height * 8);
|
|
|
|
return;
|
|
|
|
} while (!tile_element_is_last_for_tile(tileElement++));
|
|
|
|
}
|
2017-10-22 23:22:46 +02:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
/**
|
|
|
|
* Removes the hedge walls for an entrance placement.
|
|
|
|
* rct2: 0x00666CBE
|
|
|
|
*/
|
|
|
|
void maze_entrance_hedge_removal(sint32 x, sint32 y, rct_tile_element *tileElement)
|
|
|
|
{
|
|
|
|
sint32 direction = tile_element_get_direction(tileElement);
|
2018-04-18 20:34:34 +02:00
|
|
|
x += CoordsDirectionDelta[direction].x;
|
|
|
|
y += CoordsDirectionDelta[direction].y;
|
2018-02-01 18:49:14 +01:00
|
|
|
sint32 z = tileElement->base_height;
|
|
|
|
sint32 rideIndex = track_element_get_ride_index(tileElement);
|
|
|
|
|
|
|
|
tileElement = map_get_first_element_at(x >> 5, y >> 5);
|
|
|
|
do {
|
2018-05-02 13:27:53 +02:00
|
|
|
if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) continue;
|
2018-02-01 18:49:14 +01:00
|
|
|
if (track_element_get_ride_index(tileElement) != rideIndex) continue;
|
|
|
|
if (tileElement->base_height != z) continue;
|
|
|
|
if (track_element_get_type(tileElement) != TRACK_ELEM_MAZE) continue;
|
|
|
|
|
|
|
|
// Each maze element is split into 4 sections with 4 different walls
|
|
|
|
uint8 mazeSection = direction * 4;
|
|
|
|
// Remove the top outer wall
|
|
|
|
tileElement->properties.track.maze_entry &= ~(1 << ((mazeSection + 9) & 0x0F));
|
|
|
|
// Remove the bottom outer wall
|
|
|
|
tileElement->properties.track.maze_entry &= ~(1 << ((mazeSection + 12) & 0x0F));
|
|
|
|
// Remove the intersecting wall
|
|
|
|
tileElement->properties.track.maze_entry &= ~(1 << ((mazeSection + 10) & 0x0F));
|
|
|
|
// Remove the top hedge section
|
|
|
|
tileElement->properties.track.maze_entry &= ~(1 << ((mazeSection + 11) & 0x0F));
|
|
|
|
// Remove the bottom hedge section
|
|
|
|
tileElement->properties.track.maze_entry &= ~(1 << ((mazeSection + 15) & 0x0F));
|
|
|
|
|
|
|
|
map_invalidate_tile(x, y, tileElement->base_height * 8, tileElement->clearance_height * 8);
|
|
|
|
return;
|
|
|
|
} while (!tile_element_is_last_for_tile(tileElement++));
|
|
|
|
}
|
|
|
|
|
|
|
|
void fix_park_entrance_locations(void)
|
|
|
|
{
|
|
|
|
// Fix gParkEntrance locations for which the tile_element no longer exists
|
|
|
|
for (auto &entrance : gParkEntrances)
|
2017-10-22 23:22:46 +02:00
|
|
|
{
|
2018-02-01 18:49:14 +01:00
|
|
|
if (entrance.x == LOCATION_NULL)
|
|
|
|
continue;
|
2017-10-22 23:22:46 +02:00
|
|
|
|
2018-02-01 18:49:14 +01:00
|
|
|
if (map_get_park_entrance_element_at(
|
|
|
|
entrance.x,
|
|
|
|
entrance.y,
|
|
|
|
entrance.z >> 3, false) == nullptr)
|
|
|
|
{
|
|
|
|
entrance.x = LOCATION_NULL;
|
2017-10-22 23:22:46 +02:00
|
|
|
}
|
|
|
|
}
|
2017-04-24 21:46:58 +02:00
|
|
|
}
|
2018-02-01 18:49:14 +01:00
|
|
|
|