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-09-18 17:05:28 +02:00
|
|
|
#include "entrance.h"
|
|
|
|
#include "footpath.h"
|
|
|
|
#include "map.h"
|
|
|
|
#include "park.h"
|
|
|
|
#include "../cheats.h"
|
|
|
|
#include "../game.h"
|
|
|
|
#include "../localisation/string_ids.h"
|
|
|
|
#include "../management/finance.h"
|
|
|
|
#include "../ride/track.h"
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2017-04-24 21:46:58 +02:00
|
|
|
bool gParkEntranceGhostExists = false;
|
|
|
|
rct_xyz16 gParkEntranceGhostPosition = { 0, 0, 0 };
|
|
|
|
uint8 gParkEntranceGhostDirection = 0;
|
2017-02-25 14:51:30 +01:00
|
|
|
rct_xyzd16 gParkEntrances[MAX_PARK_ENTRANCES];
|
|
|
|
|
2017-03-04 11:18:31 +01:00
|
|
|
rct_xyzd16 gRideEntranceExitGhostPosition;
|
|
|
|
uint8 gRideEntranceExitGhostStationIndex;
|
|
|
|
|
2017-02-25 14:51:30 +01:00
|
|
|
static void ParkEntranceRemoveSegment(sint32 x, sint32 y, sint32 z)
|
|
|
|
{
|
2017-03-01 22:19:15 +01:00
|
|
|
rct_map_element *mapElement;
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2017-03-01 22:19:15 +01:00
|
|
|
mapElement = map_get_park_entrance_element_at(x, y, z, true);
|
2017-08-15 10:07:44 +02:00
|
|
|
if (mapElement == nullptr)
|
2017-03-01 22:19:15 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-02-25 14:51:30 +01:00
|
|
|
|
2017-03-01 22:19:15 +01:00
|
|
|
map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8);
|
|
|
|
map_element_remove(mapElement);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
gParkEntrances[entranceIndex].x = MAP_LOCATION_NULL;
|
|
|
|
direction = (gParkEntrances[entranceIndex].direction - 1) & 3;
|
|
|
|
|
|
|
|
// Centre (sign)
|
|
|
|
ParkEntranceRemoveSegment(x, y, z * 2);
|
|
|
|
|
|
|
|
// Left post
|
|
|
|
ParkEntranceRemoveSegment(
|
|
|
|
x + TileDirectionDelta[direction].x,
|
|
|
|
y + TileDirectionDelta[direction].y,
|
|
|
|
z * 2
|
|
|
|
);
|
|
|
|
|
|
|
|
// Right post
|
|
|
|
ParkEntranceRemoveSegment(
|
|
|
|
x - TileDirectionDelta[direction].x,
|
|
|
|
y - TileDirectionDelta[direction].y,
|
|
|
|
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-03-09 19:48:09 +01:00
|
|
|
!map_can_construct_with_clear_at(x, y, z / 8, clear_z, &map_place_non_scenery_clear_func, 0xF, flags, &cost))
|
|
|
|
{
|
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;
|
|
|
|
rct_xy16 removeCoord = { 0, 0 };
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (isExit)
|
|
|
|
{
|
2017-07-17 20:04:51 +02:00
|
|
|
if (ride->exits[stationNum].xy != RCT_XY8_UNDEFINED)
|
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;
|
|
|
|
}
|
|
|
|
|
2017-07-17 20:04:51 +02:00
|
|
|
removeCoord.x = ride->exits[stationNum].x * 32;
|
|
|
|
removeCoord.y = ride->exits[stationNum].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
|
|
|
}
|
2017-07-17 18:10:38 +02:00
|
|
|
else if (ride->entrances[stationNum].xy != RCT_XY8_UNDEFINED)
|
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;
|
|
|
|
}
|
|
|
|
|
2017-07-17 18:10:38 +02:00
|
|
|
removeCoord.x = ride->entrances[stationNum].x * 32;
|
|
|
|
removeCoord.y = ride->entrances[stationNum].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
|
|
|
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-03-09 19:48:09 +01:00
|
|
|
!map_can_construct_with_clear_at(x, y, z / 8, clear_z, &map_place_non_scenery_clear_func, 0xF, flags, &cost))
|
|
|
|
{
|
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-03-04 11:18:31 +01:00
|
|
|
rct_xyz16 coord;
|
|
|
|
coord.x = x + 16;
|
|
|
|
coord.y = y + 16;
|
|
|
|
coord.z = map_element_height(coord.x, coord.y);
|
|
|
|
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
|
|
|
|
|
|
|
|
rct_map_element* mapElement = map_element_insert(x / 32, y / 32, z / 8, 0xF);
|
2017-08-15 10:07:44 +02:00
|
|
|
assert(mapElement != nullptr);
|
2017-03-04 11:18:31 +01:00
|
|
|
mapElement->clearance_height = clear_z;
|
2017-03-09 19:48:09 +01:00
|
|
|
mapElement->properties.entrance.type = isExit;
|
|
|
|
mapElement->properties.entrance.index = stationNum << 4;
|
2017-03-04 11:18:31 +01:00
|
|
|
mapElement->properties.entrance.ride_index = rideIndex;
|
|
|
|
mapElement->type = MAP_ELEMENT_TYPE_ENTRANCE | direction;
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (flags & GAME_COMMAND_FLAG_GHOST)
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
mapElement->flags |= MAP_ELEMENT_FLAG_GHOST;
|
|
|
|
}
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (isExit)
|
|
|
|
{
|
2017-07-17 20:04:51 +02:00
|
|
|
ride->exits[stationNum].x = x / 32;
|
|
|
|
ride->exits[stationNum].y = y / 32;
|
2017-03-09 19:48:09 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-07-17 18:10:38 +02:00
|
|
|
ride->entrances[stationNum].x = x / 32;
|
|
|
|
ride->entrances[stationNum].y = y / 32;
|
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-03-04 11:18:31 +01:00
|
|
|
maze_entrance_hedge_removal(x, y, mapElement);
|
|
|
|
}
|
2017-03-09 19:48:09 +01:00
|
|
|
|
2017-03-04 11:18:31 +01:00
|
|
|
footpath_connect_edges(x, y, mapElement, 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-03-04 11:18:31 +01:00
|
|
|
rct_map_element* mapElement = map_get_first_element_at(x / 32, y / 32);
|
2017-08-15 10:07:44 +02:00
|
|
|
if (mapElement == 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
|
|
|
|
{
|
2017-03-04 11:18:31 +01:00
|
|
|
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_ENTRANCE)
|
|
|
|
continue;
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (mapElement->base_height != ride->station_heights[stationNum])
|
2017-03-04 11:18:31 +01:00
|
|
|
continue;
|
|
|
|
|
2017-03-09 20:18:24 +01:00
|
|
|
if (flags & GAME_COMMAND_FLAG_5 && !(mapElement->flags & MAP_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
|
|
|
}
|
|
|
|
while (!map_element_is_last_for_tile(mapElement++));
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
rct_xyz16 coord;
|
|
|
|
coord.x = x + 16;
|
|
|
|
coord.y = y + 16;
|
|
|
|
coord.z = map_element_height(coord.x, coord.y);
|
|
|
|
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
|
|
|
|
|
|
|
|
footpath_queue_chain_reset();
|
|
|
|
maze_entrance_hedge_replacement(x, y, mapElement);
|
|
|
|
footpath_remove_edges_at(x, y, mapElement);
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
bool isExit = mapElement->properties.entrance.type == ENTRANCE_TYPE_RIDE_EXIT;
|
2017-03-04 11:18:31 +01:00
|
|
|
|
|
|
|
map_element_remove(mapElement);
|
|
|
|
|
2017-03-09 19:48:09 +01:00
|
|
|
if (isExit)
|
|
|
|
{
|
2017-07-17 20:04:51 +02:00
|
|
|
ride->exits[stationNum].xy = RCT_XY8_UNDEFINED;
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
2017-03-09 19:48:09 +01:00
|
|
|
else
|
|
|
|
{
|
2017-07-17 18:10:38 +02:00
|
|
|
ride->entrances[stationNum].xy = RCT_XY8_UNDEFINED;
|
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
|
|
|
|
|
|
|
extern "C"
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x00666A63
|
2017-06-06 23:24:18 +02:00
|
|
|
*/
|
|
|
|
void game_command_remove_park_entrance(sint32 *eax,
|
|
|
|
sint32 *ebx,
|
|
|
|
sint32 *ecx,
|
|
|
|
sint32 *edx,
|
|
|
|
sint32 *esi,
|
|
|
|
sint32 *edi,
|
2017-03-01 22:19:15 +01:00
|
|
|
sint32 *ebp)
|
2017-02-25 14:51:30 +01:00
|
|
|
{
|
|
|
|
*ebx = ParkEntranceRemove(
|
|
|
|
*eax & 0xFFFF,
|
|
|
|
*ecx & 0xFFFF,
|
|
|
|
*edx & 0xFF,
|
|
|
|
*ebx & 0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x00666F9E
|
|
|
|
*/
|
2017-03-01 22:19:15 +01:00
|
|
|
void park_entrance_remove_ghost()
|
2017-02-25 14:51:30 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
if (gParkEntranceGhostExists)
|
2017-03-01 22:19:15 +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
|
|
|
}
|
|
|
|
|
2017-03-09 20:18:24 +01:00
|
|
|
sint32 park_entrance_get_index(sint32 x, sint32 y, sint32 z)
|
2017-02-25 14:51:30 +01:00
|
|
|
{
|
2017-03-01 22:19:15 +01:00
|
|
|
sint32 i;
|
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
for (i = 0; i < MAX_PARK_ENTRANCES; i++)
|
2017-03-01 22:19:15 +01:00
|
|
|
{
|
|
|
|
if (x == gParkEntrances[i].x &&
|
|
|
|
y == gParkEntrances[i].y &&
|
2017-06-06 23:24:18 +02:00
|
|
|
z == gParkEntrances[i].z)
|
2017-03-01 22:19:15 +01:00
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
2017-02-25 14:51:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void reset_park_entrance()
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
for (sint32 i = 0; i < MAX_PARK_ENTRANCES; i++)
|
2017-03-01 22:19:15 +01:00
|
|
|
{
|
2017-02-25 14:51:30 +01:00
|
|
|
gParkEntrances[i].x = MAP_LOCATION_NULL;
|
|
|
|
}
|
|
|
|
}
|
2017-03-04 11:18:31 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-03-04 11:18:31 +01:00
|
|
|
void ride_entrance_exit_place_provisional_ghost()
|
|
|
|
{
|
|
|
|
if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT) {
|
2017-06-06 23:24:18 +02:00
|
|
|
RideEntranceExitPlaceGhost(_currentRideIndex,
|
|
|
|
gRideEntranceExitGhostPosition.x,
|
|
|
|
gRideEntranceExitGhostPosition.y,
|
|
|
|
gRideEntranceExitGhostPosition.direction,
|
|
|
|
gRideEntranceExitPlaceType,
|
2017-03-04 11:18:31 +01:00
|
|
|
gRideEntranceExitGhostStationIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006CA28C
|
|
|
|
*/
|
2017-06-06 23:24:18 +02:00
|
|
|
money32 ride_entrance_exit_place_ghost(sint32 rideIndex,
|
|
|
|
sint32 x,
|
|
|
|
sint32 y,
|
|
|
|
sint32 direction,
|
|
|
|
sint32 placeType,
|
2017-03-09 19:48:09 +01:00
|
|
|
sint32 stationNum)
|
2017-03-04 11:18:31 +01:00
|
|
|
{
|
2017-06-02 01:35:47 +02:00
|
|
|
ride_construction_remove_ghosts();
|
2017-03-09 20:18:24 +01:00
|
|
|
money32 result = RideEntranceExitPlaceGhost(rideIndex, x, y, direction, placeType, stationNum);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
if (result != MONEY32_UNDEFINED)
|
2017-03-09 19:48:09 +01:00
|
|
|
{
|
|
|
|
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT;
|
|
|
|
gRideEntranceExitGhostPosition.x = x;
|
|
|
|
gRideEntranceExitGhostPosition.y = y;
|
|
|
|
gRideEntranceExitGhostPosition.direction = direction;
|
|
|
|
gRideEntranceExitGhostStationIndex = stationNum & 0xFF;
|
|
|
|
}
|
|
|
|
return result;
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006660A8
|
|
|
|
*/
|
2017-03-09 19:48:09 +01:00
|
|
|
void game_command_place_ride_entrance_or_exit(sint32 *eax,
|
|
|
|
sint32 *ebx,
|
|
|
|
sint32 *ecx,
|
|
|
|
sint32 *edx,
|
|
|
|
sint32 *esi,
|
|
|
|
sint32 *edi,
|
|
|
|
sint32 *ebp)
|
|
|
|
{
|
2017-03-09 20:18:24 +01:00
|
|
|
*ebx = RideEntranceExitPlace(
|
2017-03-04 11:18:31 +01:00
|
|
|
*eax & 0xFFFF,
|
|
|
|
*ecx & 0xFFFF,
|
|
|
|
*edx & 0xFF,
|
|
|
|
(*ebx >> 8) & 0xFF,
|
|
|
|
*ebx & 0xFF,
|
|
|
|
*edx & 0xFF,
|
|
|
|
*edi & 0xFF,
|
2017-03-09 21:03:23 +01:00
|
|
|
((*edx >> 8) & 0xFF) != 0
|
2017-03-04 11:18:31 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x0066640B
|
|
|
|
*/
|
2017-03-09 19:48:09 +01:00
|
|
|
void game_command_remove_ride_entrance_or_exit(sint32 *eax,
|
|
|
|
sint32 *ebx,
|
|
|
|
sint32 *ecx,
|
|
|
|
sint32 *edx,
|
|
|
|
sint32 *esi,
|
|
|
|
sint32 *edi,
|
|
|
|
sint32 *ebp)
|
|
|
|
{
|
2017-03-09 20:18:24 +01:00
|
|
|
*ebx = RideEntranceExitRemove(
|
2017-03-04 11:18:31 +01:00
|
|
|
*eax & 0xFFFF,
|
|
|
|
*ecx & 0xFFFF,
|
|
|
|
*edx & 0xFF,
|
|
|
|
*edi & 0xFF,
|
|
|
|
*ebx & 0xFF
|
2017-03-09 19:48:09 +01:00
|
|
|
);
|
2017-03-04 11:18:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces the outer hedge walls for an entrance placement removal.
|
|
|
|
* rct2: 0x00666D6F
|
|
|
|
*/
|
|
|
|
void maze_entrance_hedge_replacement(sint32 x, sint32 y, rct_map_element *mapElement)
|
|
|
|
{
|
2017-07-27 11:59:41 +02:00
|
|
|
sint32 direction = map_element_get_direction(mapElement);
|
2017-03-04 11:18:31 +01:00
|
|
|
x += TileDirectionDelta[direction].x;
|
|
|
|
y += TileDirectionDelta[direction].y;
|
|
|
|
sint32 z = mapElement->base_height;
|
|
|
|
sint32 rideIndex = mapElement->properties.track.ride_index;
|
|
|
|
|
|
|
|
mapElement = map_get_first_element_at(x >> 5, y >> 5);
|
|
|
|
do {
|
|
|
|
if (mapElement->type != MAP_ELEMENT_TYPE_TRACK) continue;
|
|
|
|
if (mapElement->properties.track.ride_index != rideIndex) continue;
|
|
|
|
if (mapElement->base_height != z) continue;
|
|
|
|
if (mapElement->properties.track.type != 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
|
|
|
|
mapElement->properties.track.maze_entry |= (1 << ((mazeSection + 9) & 0x0F));
|
|
|
|
// Add the bottom outer wall
|
|
|
|
mapElement->properties.track.maze_entry |= (1 << ((mazeSection + 12) & 0x0F));
|
|
|
|
|
|
|
|
map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8);
|
|
|
|
return;
|
|
|
|
} while (!map_element_is_last_for_tile(mapElement++));
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-03-04 11:18:31 +01:00
|
|
|
/**
|
|
|
|
* Removes the hedge walls for an entrance placement.
|
|
|
|
* rct2: 0x00666CBE
|
|
|
|
*/
|
|
|
|
void maze_entrance_hedge_removal(sint32 x, sint32 y, rct_map_element *mapElement)
|
|
|
|
{
|
2017-07-27 11:59:41 +02:00
|
|
|
sint32 direction = map_element_get_direction(mapElement);
|
2017-03-04 11:18:31 +01:00
|
|
|
x += TileDirectionDelta[direction].x;
|
|
|
|
y += TileDirectionDelta[direction].y;
|
|
|
|
sint32 z = mapElement->base_height;
|
|
|
|
sint32 rideIndex = mapElement->properties.track.ride_index;
|
|
|
|
|
|
|
|
mapElement = map_get_first_element_at(x >> 5, y >> 5);
|
|
|
|
do {
|
|
|
|
if (mapElement->type != MAP_ELEMENT_TYPE_TRACK) continue;
|
|
|
|
if (mapElement->properties.track.ride_index != rideIndex) continue;
|
|
|
|
if (mapElement->base_height != z) continue;
|
|
|
|
if (mapElement->properties.track.type != 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
|
|
|
|
mapElement->properties.track.maze_entry &= ~(1 << ((mazeSection + 9) & 0x0F));
|
|
|
|
// Remove the bottom outer wall
|
|
|
|
mapElement->properties.track.maze_entry &= ~(1 << ((mazeSection + 12) & 0x0F));
|
|
|
|
// Remove the intersecting wall
|
|
|
|
mapElement->properties.track.maze_entry &= ~(1 << ((mazeSection + 10) & 0x0F));
|
|
|
|
// Remove the top hedge section
|
|
|
|
mapElement->properties.track.maze_entry &= ~(1 << ((mazeSection + 11) & 0x0F));
|
|
|
|
// Remove the bottom hedge section
|
|
|
|
mapElement->properties.track.maze_entry &= ~(1 << ((mazeSection + 15) & 0x0F));
|
|
|
|
|
|
|
|
map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8);
|
|
|
|
return;
|
|
|
|
} while (!map_element_is_last_for_tile(mapElement++));
|
|
|
|
}
|
2017-04-24 21:46:58 +02:00
|
|
|
}
|