1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-16 19:43:06 +01:00
Files
OpenRCT2/src/world/footpath.c
duncanspumpkin 1f08f092dd Fix #2273
This was also allowing bins to be paid for placing in a 4 connected tile
2015-11-10 17:23:38 +00:00

1733 lines
54 KiB
C

/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "../addresses.h"
#include "../cheats.h"
#include "../config.h"
#include "../game.h"
#include "../localisation/localisation.h"
#include "../management/finance.h"
#include "../util/util.h"
#include "footpath.h"
#include "map.h"
#include "map_animation.h"
#include "scenery.h"
void footpath_interrupt_peeps(int x, int y, int z);
void sub_6A7642(int x, int y, rct_map_element *mapElement);
void sub_6A76E9(int rideIndex);
const rct_xy16 word_981D6C[4] = {
{ -1, 0 },
{ 0, 1 },
{ 1, 0 },
{ 0, -1 }
};
// rct2: 0x0097B974
static const uint16 EntranceDirections[] = {
(4 ), 0, 0, 0, 0, 0, 0, 0, // ENTRANCE_TYPE_RIDE_ENTRANCE,
(4 ), 0, 0, 0, 0, 0, 0, 0, // ENTRANCE_TYPE_RIDE_EXIT,
(4 | 1), 0, 0, 0, 0, 0, 0, 0, // ENTRANCE_TYPE_PARK_ENTRANCE
};
static int entrance_get_directions(rct_map_element *mapElement)
{
uint8 entranceType = mapElement->properties.entrance.type;
uint8 sequence = mapElement->properties.entrance.index & 0x0F;
return EntranceDirections[(entranceType * 8) + sequence];
}
static bool entrance_has_direction(rct_map_element *mapElement, int direction)
{
return entrance_get_directions(mapElement) & (1 << direction);
}
/**
*
* rct2: 0x006A65AD
*/
static void automatically_set_peep_spawn(int x, int y, int z)
{
rct2_peep_spawn *peepSpawn = (rct2_peep_spawn*)RCT2_ADDRESS_PEEP_SPAWNS;
int direction = 0;
if (x != 32) {
direction++;
if (y != RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE_UNITS, uint16) - 32) {
direction++;
if (x != RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE_UNITS, uint16) - 32) {
direction++;
if (y != 32)
return;
}
}
}
peepSpawn->x = x + (word_981D6C[direction].x * 15) + 16;
peepSpawn->y = y + (word_981D6C[direction].y * 15) + 16;
peepSpawn->direction = direction;
peepSpawn->z = z;
}
rct_map_element *map_get_footpath_element(int x, int y, int z)
{
rct_map_element *mapElement;
mapElement = map_get_first_element_at(x, y);
do {
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH && mapElement->base_height == z)
return mapElement;
} while (!map_element_is_last_for_tile(mapElement++));
return NULL;
}
rct_map_element *map_get_footpath_element_slope(int x, int y, int z, int slope)
{
rct_map_element *mapElement;
mapElement = map_get_first_element_at(x, y);
do {
if (
map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH &&
mapElement->base_height == z &&
(mapElement->properties.path.type & 7) == slope
) {
return mapElement;
}
} while (!map_element_is_last_for_tile(mapElement++));
return NULL;
}
static void loc_6A6620(int flags, int x, int y, rct_map_element *mapElement)
{
int direction, z;
if ((mapElement->properties.path.type & 4) && !(flags & GAME_COMMAND_FLAG_GHOST)) {
direction = mapElement->properties.path.type & 3;
z = mapElement->base_height;
map_remove_intersecting_walls(x, y, z, z + 6, direction ^ 2);
map_remove_intersecting_walls(x, y, z, z + 6, direction);
mapElement = map_get_footpath_element(x / 32, y / 32, z);
}
if (!(flags & GAME_COMMAND_FLAG_7))
footpath_connect_edges(x, y, mapElement, flags);
sub_6A759F();
map_invalidate_tile_full(x, y);
}
static money32 footpath_element_insert(int type, int x, int y, int z, int slope, int flags, uint8 pathItemType)
{
rct_map_element *mapElement;
int bl, zHigh;
if (!sub_68B044())
return MONEY32_UNDEFINED;
if ((flags & GAME_COMMAND_FLAG_APPLY) && !(flags & (GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST)))
footpath_remove_litter(x, y, RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Z, uint16));
// loc_6A649D:
RCT2_GLOBAL(0x00F3EFD9, money32) += MONEY(12, 00);
bl = 15;
zHigh = z + 4;
if (slope & 4) {
bl = RCT2_ADDRESS(0x0098D7EC, uint8)[slope & 3];
zHigh += 2;
}
RCT2_GLOBAL(0x00F3EF84, uint16) = x;
RCT2_GLOBAL(0x00F3EF86, uint16) = y;
// Ugh, hack until 0x006A6733 is written
// 0x006A6733 expects the flags to be at (*0xF3EF7C) + 8
RCT2_GLOBAL(0x00F3EF7C, uint32) = (uint32)(&flags - 2);
if (!gCheatsDisableClearanceChecks && !map_can_construct_with_clear_at(x, y, z, zHigh, (void*)0x006A6733, bl))
return MONEY32_UNDEFINED;
RCT2_GLOBAL(0x00F3EFA4, uint8) = RCT2_GLOBAL(RCT2_ADDRESS_ELEMENT_LOCATION_COMPARED_TO_GROUND_AND_WATER, uint8);
if (!gCheatsDisableClearanceChecks && (RCT2_GLOBAL(RCT2_ADDRESS_ELEMENT_LOCATION_COMPARED_TO_GROUND_AND_WATER, uint8) & ELEMENT_IS_UNDERWATER)) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_CANT_BUILD_THIS_UNDERWATER;
return MONEY32_UNDEFINED;
}
mapElement = map_get_surface_element_at((x / 32), (y / 32));
int supportHeight = z - mapElement->base_height;
RCT2_GLOBAL(0x00F3EFD9, money32) += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / 2) * MONEY(5, 00);
if (flags & GAME_COMMAND_FLAG_APPLY) {
mapElement = map_element_insert(x / 32, y / 32, z, 0x0F);
mapElement->type = MAP_ELEMENT_TYPE_PATH;
mapElement->clearance_height = z + 4 + (slope & 4 ? 2 : 0);
mapElement->properties.path.type = (type << 4) | (slope & 7);
mapElement->type |= type >> 7;
mapElement->properties.path.additions = pathItemType;
mapElement->properties.path.addition_status = 255;
mapElement->flags &= ~MAP_ELEMENT_FLAG_BROKEN;
if (flags & (1 << 6))
mapElement->flags |= MAP_ELEMENT_FLAG_GHOST;
RCT2_GLOBAL(0x00F3EFF4, uint32) = 0x00F3EFF8;
if (!(flags & (1 << 7)))
footpath_remove_edges_at(x, y, mapElement);
if ((RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) && !(flags & (1 << 6)))
automatically_set_peep_spawn(x, y, mapElement->base_height / 2);
loc_6A6620(flags, x, y, mapElement);
}
return RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY ? 0 : RCT2_GLOBAL(0x00F3EFD9, money32);
}
static money32 footpath_element_update(int x, int y, rct_map_element *mapElement, int type, int flags, uint8 pathItemType)
{
if ((mapElement->properties.path.type >> 4) != (type & 0x0F) || (mapElement->type & 1) != (type >> 7)) {
RCT2_GLOBAL(0x00F3EFD9, money32) += MONEY(6, 00);
} else if (pathItemType != 0) {
if (
!(flags & GAME_COMMAND_FLAG_GHOST) &&
(mapElement->properties.path.additions & 0x0F) == pathItemType &&
!(mapElement->flags & MAP_ELEMENT_FLAG_BROKEN)
) {
if (flags & GAME_COMMAND_FLAG_4)
return MONEY32_UNDEFINED;
return RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY ? 0 : RCT2_GLOBAL(0x00F3EFD9, money32);
}
if (pathItemType != 0) {
rct_scenery_entry* scenery_entry = g_pathBitSceneryEntries[pathItemType - 1];
uint16 unk6 = scenery_entry->path_bit.var_06;
if ((unk6 & 0x80) && (mapElement->properties.path.type & 4)) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_CANT_BUILD_THIS_ON_SLOPED_FOOTPATH;
return MONEY32_UNDEFINED;
}
if ((unk6 & 0x40) && footpath_element_is_queue(mapElement)) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_CANNOT_PLACE_THESE_ON_QUEUE_LINE_AREA;
return MONEY32_UNDEFINED;
}
if (!(unk6 & 0x30) && (mapElement->properties.path.edges & 0x0F) == 0x0F) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_NONE;
return MONEY32_UNDEFINED;
}
if ((unk6 & 0x100) && !footpath_element_is_queue(mapElement)) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_CAN_ONLY_PLACE_THESE_ON_QUEUE_AREA;
return MONEY32_UNDEFINED;
}
RCT2_GLOBAL(0x00F3EFD9, money32) += scenery_entry->path_bit.price;
}
if (flags & GAME_COMMAND_FLAG_4)
return MONEY32_UNDEFINED;
if (flags & GAME_COMMAND_FLAG_GHOST) {
if (mapElement->properties.path.additions & 0x0F) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_NONE;
return MONEY32_UNDEFINED;
}
if (flags & GAME_COMMAND_FLAG_APPLY)
mapElement->properties.path.additions |= 0x80;
}
if (!(flags & GAME_COMMAND_FLAG_APPLY))
return RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY ? 0 : RCT2_GLOBAL(0x00F3EFD9, money32);
if (
(pathItemType != 0 && !(flags & GAME_COMMAND_FLAG_GHOST)) ||
(pathItemType == 0 && (mapElement->properties.path.additions & 0x80))
) {
mapElement->properties.path.additions &= ~0x80;
}
mapElement->properties.path.additions = (mapElement->properties.path.additions & 0xF0) | pathItemType;
mapElement->flags &= ~MAP_ELEMENT_FLAG_BROKEN;
if (pathItemType != 0) {
rct_scenery_entry* scenery_entry = g_pathBitSceneryEntries[pathItemType - 1];
uint16 unk6 = scenery_entry->path_bit.var_06;
if (unk6 & 1)
mapElement->properties.path.addition_status = 255;
}
map_invalidate_tile_full(x, y);
return RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY ? 0 : RCT2_GLOBAL(0x00F3EFD9, money32);
}
if (flags & GAME_COMMAND_FLAG_4)
return MONEY32_UNDEFINED;
if (flags & GAME_COMMAND_FLAG_APPLY) {
RCT2_GLOBAL(0x00F3EFF4, uint32) = 0x00F3EFF8;
if (!(flags & GAME_COMMAND_FLAG_7))
footpath_remove_edges_at(x, y, mapElement);
mapElement->properties.path.type = (mapElement->properties.path.type & 0x0F) | (type << 4);
mapElement->type = (mapElement->type & 0xFE) | (type >> 7);
mapElement->properties.path.additions = (mapElement->properties.path.additions & 0xF0) | pathItemType;
mapElement->flags &= ~MAP_ELEMENT_FLAG_BROKEN;
loc_6A6620(flags, x, y, mapElement);
}
return RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY ? 0 : RCT2_GLOBAL(0x00F3EFD9, money32);
}
static money32 footpath_place_real(int type, int x, int y, int z, int slope, int flags, uint8 pathItemType)
{
rct_map_element *mapElement;
RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = RCT_EXPENDITURE_TYPE_LANDSCAPING * 4;
RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_X, uint16) = x + 16;
RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Y, uint16) = y + 16;
RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Z, uint16) = z * 8;
if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) && RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) != 0 && !gConfigCheat.build_in_pause_mode) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
if (flags & GAME_COMMAND_FLAG_APPLY)
footpath_interrupt_peeps(x, y, z * 8);
RCT2_GLOBAL(0x00F3EFD9, money32) = 0;
RCT2_GLOBAL(0x00F3EFA4, uint8) = 0;
if (x >= RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE_UNITS, uint16) || y >= RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE_UNITS, uint16)) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_OFF_EDGE_OF_MAP;
return MONEY32_UNDEFINED;
}
if (!((RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(x, y, z * 8))
return MONEY32_UNDEFINED;
if (slope & 8) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_LAND_SLOPE_UNSUITABLE;
return MONEY32_UNDEFINED;
}
if (z < 2) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_TOO_LOW;
return MONEY32_UNDEFINED;
}
if (z > 248) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_TOO_HIGH;
return MONEY32_UNDEFINED;
}
// Force ride construction to recheck area
RCT2_GLOBAL(0x00F440B0, uint8) |= 8;
footpath_provisional_remove();
mapElement = map_get_footpath_element_slope((x / 32), (y / 32), z, slope);
if (mapElement == NULL) {
return footpath_element_insert(type, x, y, z, slope, flags, pathItemType);
} else {
return footpath_element_update(x, y, mapElement, type, flags, pathItemType);
}
}
/* rct2: 0x006BA23E */
void remove_banners_at_element(int x, int y, rct_map_element* mapElement){
while (!map_element_is_last_for_tile(mapElement++)){
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH)return;
else if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_BANNER)continue;
game_do_command(x, 1, y, mapElement->base_height | mapElement->properties.banner.position << 8, GAME_COMMAND_REMOVE_BANNER, 0, 0);
mapElement--;
}
}
money32 footpath_remove_real(int x, int y, int z, int flags)
{
rct_map_element *mapElement;
RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = RCT_EXPENDITURE_TYPE_LANDSCAPING * 4;
RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_X, uint16) = x + 16;
RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Y, uint16) = y + 16;
RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Z, uint16) = z * 8;
if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) && RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) != 0 && !gConfigCheat.build_in_pause_mode) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
if (flags & GAME_COMMAND_FLAG_APPLY) {
footpath_interrupt_peeps(x, y, z * 8);
footpath_remove_litter(x, y, z * 8);
}
if (!((RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(x, y, z * 8))
return MONEY32_UNDEFINED;
mapElement = map_get_footpath_element(x / 32, y / 32, z);
if (mapElement != NULL && (flags & GAME_COMMAND_FLAG_APPLY)) {
RCT2_GLOBAL(0x00F3EFF4, uint32) = 0x00F3EFF8;
remove_banners_at_element(x, y, mapElement);
footpath_remove_edges_at(x, y, mapElement);
map_invalidate_tile_full(x, y);
map_element_remove(mapElement);
sub_6A759F();
}
return (flags & (1 << 5)) || (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY) ? 0 : -MONEY(10,00);
}
/**
*
* rct2: 0x006A61DE
*/
void game_command_place_footpath(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp)
{
*ebx = footpath_place_real(
(*edx >> 8) & 0xFF,
*eax & 0xFFFF,
*ecx & 0xFFFF,
*edx & 0xFF,
(*ebx >> 8) & 0xFF,
*ebx & 0xFF,
*edi & 0xFF
);
}
static money32 footpath_place_from_track(int type, int x, int y, int z, int slope, int edges, int flags)
{
rct_map_element *mapElement;
RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = RCT_EXPENDITURE_TYPE_LANDSCAPING * 4;
RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_X, uint16) = x + 16;
RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Y, uint16) = y + 16;
RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Z, uint16) = z * 8;
if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) && RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) != 0 && !gConfigCheat.build_in_pause_mode) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
if (flags & GAME_COMMAND_FLAG_APPLY)
footpath_interrupt_peeps(x, y, z * 8);
RCT2_GLOBAL(0x00F3EFD9, money32) = 0;
RCT2_GLOBAL(0x00F3EFA4, uint8) = 0;
if (!map_is_location_owned(x, y, z * 8) && !gCheatsSandboxMode) {
return MONEY32_UNDEFINED;
}
if (!((RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(x, y, z * 8))
return MONEY32_UNDEFINED;
if (z < 2) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_TOO_LOW;
return MONEY32_UNDEFINED;
}
if (z > 248) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_TOO_HIGH;
return MONEY32_UNDEFINED;
}
if (flags & GAME_COMMAND_FLAG_APPLY) {
if (!(flags & (GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST))) {
footpath_remove_litter(x, y, z * 8);
}
}
RCT2_GLOBAL(0x00F3EFD9, money32) += 120;
uint8 bl = 15;
int zHigh = z + 4;
if (slope & 4) {
bl = RCT2_ADDRESS(0x0098D7EC, uint8)[slope & 3];
zHigh += 2;
}
RCT2_GLOBAL(0x00F3EF84, uint16) = x;
RCT2_GLOBAL(0x00F3EF86, uint16) = y;
// Ugh, hack until 0x006A6733 is written
// 0x006A6733 expects the flags to be at (*0xF3EF7C) + 8
RCT2_GLOBAL(0x00F3EF7C, uint32) = (uint32)(&flags - 2);
if (!gCheatsDisableClearanceChecks && !map_can_construct_with_clear_at(x, y, z, zHigh, (void*)0x006A6733, bl))
return MONEY32_UNDEFINED;
RCT2_GLOBAL(0x00F3EFA4, uint8) = RCT2_GLOBAL(RCT2_ADDRESS_ELEMENT_LOCATION_COMPARED_TO_GROUND_AND_WATER, uint8);
if (!gCheatsDisableClearanceChecks && (RCT2_GLOBAL(RCT2_ADDRESS_ELEMENT_LOCATION_COMPARED_TO_GROUND_AND_WATER, uint8) & ELEMENT_IS_UNDERWATER)) {
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_CANT_BUILD_THIS_UNDERWATER;
return MONEY32_UNDEFINED;
}
mapElement = map_get_surface_element_at((x / 32), (y / 32));
int supportHeight = z - mapElement->base_height;
RCT2_GLOBAL(0x00F3EFD9, money32) += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / 2) * MONEY(5, 00);
if (flags & GAME_COMMAND_FLAG_APPLY) {
mapElement = map_element_insert(x / 32, y / 32, z, 0x0F);
mapElement->type = MAP_ELEMENT_TYPE_PATH;
mapElement->clearance_height = z + 4 + (slope & 4 ? 2 : 0);
mapElement->properties.path.type = (type << 4) | (slope & 7);
mapElement->type |= type >> 7;
mapElement->properties.path.additions = 0;
mapElement->properties.path.addition_status = 255;
mapElement->properties.path.edges = edges & 0xF;
mapElement->flags &= ~MAP_ELEMENT_FLAG_BROKEN;
if (flags & (1 << 6))
mapElement->flags |= MAP_ELEMENT_FLAG_GHOST;
map_invalidate_tile_full(x, y);
}
return RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY ? 0 : RCT2_GLOBAL(0x00F3EFD9, money32);
}
/*
* rct2: 0x006A68AE
*/
void game_command_place_footpath_from_track(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp)
{
*ebx = footpath_place_from_track(
(*edx >> 8) & 0xFF,
*eax & 0xFFFF,
*ecx & 0xFFFF,
*edx & 0xFF,
((*ebx >> 13) & 0x3) | ((*ebx >> 10) & 0x4),
(*ebx >> 8) & 0xF,
*ebx & 0xFF
);
}
/**
*
* rct2: 0x006A67C0
*/
void game_command_remove_footpath(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp)
{
*ebx = footpath_remove_real((*eax & 0xFFFF), (*ecx & 0xFFFF), (*edx & 0xFF), (*ebx & 0xFF));
}
money32 footpath_place(int type, int x, int y, int z, int slope, int flags)
{
return game_do_command(x, (slope << 8) | flags, y, (type << 8) | z, GAME_COMMAND_PLACE_PATH, 0, 0);
}
void footpath_remove(int x, int y, int z, int flags)
{
game_do_command(x, flags, y, z, GAME_COMMAND_REMOVE_PATH, 0, 0);
}
/**
*
* rct2: 0x006A76FF
*/
money32 footpath_provisional_set(int type, int x, int y, int z, int slope)
{
money32 cost;
footpath_provisional_remove();
cost = footpath_place(type, x, y, z, slope, GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_4 | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_APPLY);
if (cost != MONEY32_UNDEFINED) {
RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_X, uint16) = x;
RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_Y, uint16) = y;
RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_Z, uint8) = z & 0xFF;
RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_FLAGS, uint8) |= (1 << 1);
viewport_set_visibility(RCT2_GLOBAL(0x00F3EFA4, uint8) & 2 ? 1 : 3);
}
return cost;
}
/**
*
* rct2: 0x006A77FF
*/
void footpath_provisional_remove()
{
if (RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_FLAGS, uint8) & (1 << 1)) {
RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_FLAGS, uint8) &= ~(1 << 1);
footpath_remove(
RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_X, uint16),
RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_Y, uint16),
RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_Z, uint16),
GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5
);
}
}
/**
*
* rct2: 0x006A7831
*/
void footpath_provisional_update()
{
if (RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_FLAGS, uint8) & PROVISIONAL_PATH_FLAG_SHOW_ARROW) {
RCT2_GLOBAL(RCT2_ADDRESS_PROVISIONAL_PATH_FLAGS, uint8) &= ~PROVISIONAL_PATH_FLAG_SHOW_ARROW;
RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, uint8) &= ~(1 << 2);
map_invalidate_tile_full(
RCT2_GLOBAL(RCT2_ADDRESS_CONSTRUCT_PATH_FROM_X, uint16),
RCT2_GLOBAL(RCT2_ADDRESS_CONSTRUCT_PATH_FROM_Y, uint16)
);
}
footpath_provisional_remove();
}
/**
* Determines the location of the footpath at which we point with the cursor. If no footpath is underneath the cursor,
* then return the location of the ground tile. Besides the location it also computes the direction of the yellow arrow
* when we are going to build a footpath bridge/tunnel.
* rct2: 0x00689726
* In:
* screenX: eax
* screenY: ebx
* Out:
* x: ax
* y: bx
* direction: ecx
* mapElement: edx
*/
void footpath_get_coordinates_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement)
{
int z = 0, interactionType;
rct_map_element *myMapElement;
rct_viewport *viewport;
rct_xy16 map_pos = { 0 };
get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_FOOTPATH, &map_pos.x, &map_pos.y, &interactionType, &myMapElement, &viewport);
if (interactionType != VIEWPORT_INTERACTION_ITEM_FOOTPATH || !(viewport->flags & (VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_HIDE_VERTICAL))) {
get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_FOOTPATH & VIEWPORT_INTERACTION_MASK_TERRAIN, &map_pos.x, &map_pos.y, &interactionType, &myMapElement, &viewport);
if (interactionType == VIEWPORT_INTERACTION_ITEM_NONE) {
if (x != NULL) *x = (sint16)0x8000;
return;
}
}
RCT2_GLOBAL(0x00F1AD3E, uint8) = interactionType;
RCT2_GLOBAL(0x00F1AD30, rct_map_element*) = myMapElement;
if (interactionType == VIEWPORT_INTERACTION_ITEM_FOOTPATH) {
z = myMapElement->base_height * 8;
if (myMapElement->properties.path.type & (1 << 2))
z += 8;
}
RCT2_GLOBAL(0x00F1AD3C, uint16) = z;
RCT2_GLOBAL(0x00F1AD34, sint16) = map_pos.x;
RCT2_GLOBAL(0x00F1AD36, sint16) = map_pos.y;
RCT2_GLOBAL(0x00F1AD38, sint16) = map_pos.x + 31;
RCT2_GLOBAL(0x00F1AD3A, sint16) = map_pos.y + 31;
map_pos.x += 16;
map_pos.y += 16;
rct_xy16 start_vp_pos = screen_coord_to_viewport_coord(viewport, screenX, screenY);
for (int i = 0; i < 5; i++) {
if (RCT2_GLOBAL(0x00F1AD3E, uint8) != 6) {
z = map_element_height(map_pos.x, map_pos.y);
} else {
z = RCT2_GLOBAL(0x00F1AD3C, uint16);
}
map_pos = viewport_coord_to_map_coord(start_vp_pos.x, start_vp_pos.y, z);
map_pos.x = clamp(RCT2_GLOBAL(0x00F1AD34, sint16), map_pos.x, RCT2_GLOBAL(0x00F1AD38, sint16));
map_pos.y = clamp(RCT2_GLOBAL(0x00F1AD36, sint16), map_pos.y, RCT2_GLOBAL(0x00F1AD3A, sint16));
}
// Determine to which edge the cursor is closest
uint32 myDirection;
int mod_x = map_pos.x & 0x1F, mod_y = map_pos.y & 0x1F;
if (mod_x < mod_y) {
if (mod_x + mod_y < 32) {
myDirection = 0;
} else {
myDirection = 1;
}
} else {
if (mod_x + mod_y < 32) {
myDirection = 3;
} else {
myDirection = 2;
}
}
if (x != NULL) *x = map_pos.x & ~0x1F;
if (y != NULL) *y = map_pos.y & ~0x1F;
if (direction != NULL) *direction = myDirection;
if (mapElement != NULL) *mapElement = myMapElement;
// We should get the rct_map_element from 0x00F1AD30 here, but we set it earlier to our myMapElement anyway.
}
/**
*
* rct2: 0x0068A0C9
* screenX: eax
* screenY: ebx
* x: ax
* y: bx
* direction: cl
* mapElement: edx
*/
void footpath_bridge_get_info_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement)
{
// First check if we point at an entrance or exit. In that case, we would want the path coming from the entrance/exit.
int interactionType;
rct_viewport *viewport;
rct_xy16 map_pos = { 0 };
get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_RIDE, &map_pos.x, &map_pos.y, &interactionType, mapElement, &viewport);
*x = map_pos.x;
*y = map_pos.y;
if (interactionType == VIEWPORT_INTERACTION_ITEM_RIDE
&& viewport->flags & (VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_HIDE_VERTICAL)
&& map_element_get_type(*mapElement) == MAP_ELEMENT_TYPE_ENTRANCE
) {
int directions = entrance_get_directions(*mapElement);
if (directions & 0x0F) {
int bx = bitscanforward(directions);
bx += (*mapElement)->type;
bx &= 3;
if (direction != NULL) *direction = bx;
return;
}
}
get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_RIDE & VIEWPORT_INTERACTION_MASK_FOOTPATH & VIEWPORT_INTERACTION_MASK_TERRAIN, &map_pos.x, &map_pos.y, &interactionType, mapElement, &viewport);
*x = map_pos.x;
*y = map_pos.y;
if (interactionType == VIEWPORT_INTERACTION_ITEM_RIDE && map_element_get_type(*mapElement) == MAP_ELEMENT_TYPE_ENTRANCE) {
int directions = entrance_get_directions(*mapElement);
if (directions & 0x0F) {
int bx = bitscanforward(directions);
bx += (*mapElement)->type; // First two bits seem to contain the direction of entrance/exit
bx &= 3;
if (direction != NULL) *direction = bx;
return;
}
}
// We point at something else
footpath_get_coordinates_from_pos(screenX, screenY, x, y, direction, mapElement);
}
/**
*
* rct2: 0x00673883
*/
void footpath_remove_litter(int x, int y, int z)
{
int index;
uint16 spriteIndex, nextSpriteIndex;
rct_litter *sprite;
index = (x & 0x1FE0) << 3 | (y >> 5);
spriteIndex = RCT2_ADDRESS(0x00F1EF60, uint16)[index];
while (spriteIndex != SPRITE_INDEX_NULL) {
sprite = &g_sprite_list[spriteIndex].litter;
nextSpriteIndex = sprite->next_in_quadrant;
if (sprite->linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_LITTER) {
int distanceZ = abs(sprite->z - z);
if (distanceZ <= 32) {
invalidate_sprite_0((rct_sprite*)sprite);
sprite_remove((rct_sprite*)sprite);
}
}
spriteIndex = nextSpriteIndex;
}
}
/**
*
* rct2: 0x0069A48B
*/
void footpath_interrupt_peeps(int x, int y, int z)
{
int index;
uint16 spriteIndex, nextSpriteIndex;
rct_peep *peep;
index = (x & 0x1FE0) << 3 | (y >> 5);
spriteIndex = RCT2_ADDRESS(0x00F1EF60, uint16)[index];
while (spriteIndex != SPRITE_INDEX_NULL) {
peep = &g_sprite_list[spriteIndex].peep;
nextSpriteIndex = peep->next_in_quadrant;
if (peep->linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_PEEP) {
if (peep->state == PEEP_STATE_SITTING || peep->state == PEEP_STATE_WATCHING) {
if (peep->z == z) {
peep_decrement_num_riders(peep);
peep->state = PEEP_STATE_WALKING;
peep_window_state_update(peep);
peep->destination_x = (peep->x & 0xFFE0) + 16;
peep->destination_y = (peep->y & 0xFFE0) + 16;
peep->destination_tolerence = 5;
sub_693B58(peep);
}
}
}
spriteIndex = nextSpriteIndex;
}
}
/* rct2: 0x006E59DC */
bool fence_in_the_way(int x, int y, int z0, int z1, int direction)
{
rct_map_element *mapElement;
mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_FENCE)
continue;
if (mapElement->flags & MAP_ELEMENT_FLAG_GHOST)
continue;
if (z0 >= mapElement->clearance_height)
continue;
if (z1 <= mapElement->base_height)
continue;
if ((mapElement->type & MAP_ELEMENT_DIRECTION_MASK) != direction)
continue;
return true;
} while (!map_element_is_last_for_tile(mapElement++));
return false;
}
bool map_is_edge(int x, int y)
{
return (
x < 32 ||
y < 32 ||
x >= RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE_UNITS, uint16) ||
y >= RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE_UNITS, uint16)
);
}
static rct_map_element *footpath_connect_corners_get_neighbour(int x, int y, int z, int requireEdges)
{
rct_map_element *mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH)
continue;
if (footpath_element_is_queue(mapElement))
continue;
if (mapElement->base_height != z)
continue;
if (!(mapElement->properties.path.edges & requireEdges))
continue;
return mapElement;
} while (!map_element_is_last_for_tile(mapElement++));
return NULL;
}
/**
* Sets the corner edges of four path tiles.
* The function will search for a path in the direction given, then check clockwise to see if it there is a path and again until
* it reaches the initial path. In other words, checks if there are four paths together so that it can set the inner corners of
* each one.
*
* rct2: 0x006A70EB
*/
static void footpath_connect_corners(int initialX, int initialY, rct_map_element *initialMapElement)
{
rct_map_element *mapElement[4];
if (footpath_element_is_queue(initialMapElement))
return;
if (footpath_element_is_sloped(initialMapElement))
return;
mapElement[0] = initialMapElement;
int z = initialMapElement->base_height;
for (int initialDirection = 0; initialDirection < 4; initialDirection++) {
int x = initialX;
int y = initialY;
int direction = initialDirection;
x += TileDirectionDelta[direction].x;
y += TileDirectionDelta[direction].y;
mapElement[1] = footpath_connect_corners_get_neighbour(x, y, z, (1 << (direction ^ 2)));
if (mapElement[1] == NULL)
continue;
direction = (direction + 1) & 3;
x += TileDirectionDelta[direction].x;
y += TileDirectionDelta[direction].y;
mapElement[2] = footpath_connect_corners_get_neighbour(x, y, z, (1 << (direction ^ 2)));
if (mapElement[2] == NULL)
continue;
direction = (direction + 1) & 3;
x += TileDirectionDelta[direction].x;
y += TileDirectionDelta[direction].y;
mapElement[3] = footpath_connect_corners_get_neighbour(x, y, z, (1 << (direction ^ 2)) | (1 << (((direction ^ 2) - 1) & 3)));
if (mapElement[3] == NULL)
continue;
direction = (direction + 1) & 3;
mapElement[3]->properties.path.edges |= (1 << (direction + 4));
map_invalidate_element(x, y, mapElement[3]);
direction = (direction - 1) & 3;
mapElement[2]->properties.path.edges |= (1 << (direction + 4));
map_invalidate_element(x, y, mapElement[2]);
direction = (direction - 1) & 3;
mapElement[1]->properties.path.edges |= (1 << (direction + 4));
map_invalidate_element(x, y, mapElement[1]);
direction = (direction - 1) & 3;
mapElement[0]->properties.path.edges |= (1 << (direction + 4));
map_invalidate_element(x, y, mapElement[0]);
}
}
typedef struct {
uint8 order;
uint8 direction;
uint8 ride_index;
} rct_neighbour;
typedef struct {
rct_neighbour items[8];
int count;
} rct_neighbour_list;
static int rct_neighbour_compare(const void *a, const void *b)
{
uint8 va = ((rct_neighbour*)a)->order;
uint8 vb = ((rct_neighbour*)b)->order;
if (va < vb) return 1;
else if (va > vb) return -1;
else {
uint8 da = ((rct_neighbour*)a)->direction;
uint8 db = ((rct_neighbour*)b)->direction;
if (va < vb) return -1;
else if (va > vb) return 1;
else return 0;
}
}
static void neighbour_list_init(rct_neighbour_list *neighbourList)
{
neighbourList->count = 0;
}
static void neighbour_list_push(rct_neighbour_list *neighbourList, int order, int direction, uint8 rideIndex)
{
neighbourList->items[neighbourList->count].order = order;
neighbourList->items[neighbourList->count].direction = direction;
neighbourList->items[neighbourList->count].ride_index = rideIndex;
neighbourList->count++;
}
static bool neighbour_list_pop(rct_neighbour_list *neighbourList, rct_neighbour *outNeighbour)
{
if (neighbourList->count == 0)
return false;
*outNeighbour = neighbourList->items[0];
for (int i = 0; i < neighbourList->count - 1; i++)
neighbourList->items[i] = neighbourList->items[i + 1];
neighbourList->count--;
return true;
}
static void neighbour_list_remove(rct_neighbour_list *neighbourList, int index)
{
int itemsRemaining = neighbourList->count - index - 1;
if (itemsRemaining > 0) {
memmove(&neighbourList->items[index], &neighbourList->items[index + 1], sizeof(rct_neighbour) * itemsRemaining);
}
neighbourList->count--;
}
static void neighbour_list_sort(rct_neighbour_list *neighbourList)
{
qsort(neighbourList->items, neighbourList->count, sizeof(rct_neighbour), rct_neighbour_compare);
}
static rct_map_element *footpath_get_element(int x, int y, int z0, int z1, int direction)
{
rct_map_element *mapElement;
int slope;
mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH)
continue;
if (z1 == mapElement->base_height) {
if (footpath_element_is_sloped(mapElement)) {
slope = footpath_element_get_slope_direction(mapElement);
if (slope != direction)
break;
}
return mapElement;
}
if (z0 == mapElement->base_height) {
if (!footpath_element_is_sloped(mapElement))
break;
slope = footpath_element_get_slope_direction(mapElement) ^ 2;
if (slope != direction)
break;
return mapElement;
}
} while (!map_element_is_last_for_tile(mapElement++));
return NULL;
}
static bool sub_footpath_disconnect_queue_from_path(int x, int y, rct_map_element *mapElement, int action, int direction) {
if (((mapElement->properties.path.edges & (1 << direction)) == 0) ^ (action < 0))
return false;
if ((action < 0) && fence_in_the_way(x, y, mapElement->base_height, mapElement->clearance_height, direction))
return false;
int x1 = x + TileDirectionDelta[direction].x;
int y1 = y + TileDirectionDelta[direction].y;
int z = mapElement->base_height;
rct_map_element *otherMapElement = footpath_get_element(x1, y1, z - 2, z, direction);
if (otherMapElement != NULL && !footpath_element_is_queue(otherMapElement)) {
mapElement->properties.path.type &= 0xFC;
if (action > 0) {
mapElement->properties.path.edges &= ~(1 << direction);
otherMapElement->properties.path.edges &= ~(1 << ((direction + 2) & 3));
if (action >= 2) mapElement->properties.path.type |= direction;
}
else if (action < 0) {
mapElement->properties.path.edges |= (1 << direction);
otherMapElement->properties.path.edges |= (1 << ((direction + 2) & 3));
}
if (action != 0) map_invalidate_tile_full(x1, y1);
return true;
}
return false;
}
static bool footpath_disconnect_queue_from_path(int x, int y, rct_map_element *mapElement, int action) {
if (!footpath_element_is_queue(mapElement)) return false;
if (footpath_element_is_sloped(mapElement)) return false;
uint8 c = RCT2_ADDRESS(0x0098D7F0, uint8)[mapElement->properties.path.edges & 0x0F];
if ((action < 0) ? (c >= 2) : (c < 2)) return false;
if (action < 0) {
if (sub_footpath_disconnect_queue_from_path(x, y, mapElement, action, mapElement->properties.path.type & 3))
return true;
}
for (int direction = 0; direction < 4; direction++) {
if ((action < 0) && (direction == (mapElement->properties.path.type & 3))) continue;
if (sub_footpath_disconnect_queue_from_path(x, y, mapElement, action, direction))
return true;
}
return false;
}
static bool footpath_is_queue_connected_to_path(int x, int y, rct_map_element *mapElement, int direction)
{
if (!footpath_element_is_queue(mapElement)) return false;
if (!(mapElement->properties.path.edges & (1 << direction))) return false;
x += TileDirectionDelta[direction].x;
y += TileDirectionDelta[direction].y;
mapElement = map_get_path_element_at(x / 32, y / 32, mapElement->base_height);
if (mapElement == NULL) return false;
if (footpath_element_is_queue(mapElement)) return false;
if (mapElement->properties.path.edges & ((1 << direction) ^ 2)) return true;
return false;
}
/**
*
* rct2: 0x006A6D7E
*/
static void loc_6A6D7E(
int initialX, int initialY, int z, int direction, rct_map_element *initialMapElement,
int flags, bool query, rct_neighbour_list *neighbourList
) {
int x = initialX + TileDirectionDelta[direction].x;
int y = initialY + TileDirectionDelta[direction].y;
if (((RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && map_is_edge(x, y)) {
if (query) {
neighbour_list_push(neighbourList, 7, direction, 255);
}
} else {
rct_map_element *mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
switch (map_element_get_type(mapElement)) {
case MAP_ELEMENT_TYPE_PATH:
if (z == mapElement->base_height) {
if (footpath_element_is_sloped(mapElement) && footpath_element_get_slope_direction(mapElement) != direction) {
return;
} else {
goto loc_6A6F1F;
}
}
if (z - 2 == mapElement->base_height) {
if (!footpath_element_is_sloped(mapElement) || footpath_element_get_slope_direction(mapElement) != (direction ^ 2)) {
return;
}
goto loc_6A6F1F;
}
break;
case MAP_ELEMENT_TYPE_TRACK:
if (z == mapElement->base_height) {
rct_ride *ride = GET_RIDE(mapElement->properties.track.ride_index);
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) {
continue;
}
uint16 di = (mapElement->properties.track.sequence & 0x0F) | (mapElement->properties.track.type << 4);
if (!(RCT2_ADDRESS(0x0099CA64, uint8)[di] & (1 << 5))) {
return;
}
uint16 dx = ((direction - mapElement->type) & 3) ^ 2;
if (!(RCT2_ADDRESS(0x0099CA64, uint16)[di / 2] & (1 << dx))) {
return;
}
if (query) {
neighbour_list_push(neighbourList, 1, direction, mapElement->properties.track.ride_index);
}
goto loc_6A6FD2;
}
break;
case MAP_ELEMENT_TYPE_ENTRANCE:
if (z == mapElement->base_height) {
if (entrance_has_direction(mapElement, ((direction - mapElement->type) & 3) ^ 2)) {
if (query) {
neighbour_list_push(neighbourList, 8, direction, mapElement->properties.entrance.ride_index);
} else {
if (mapElement->properties.entrance.type != ENTRANCE_TYPE_PARK_ENTRANCE) {
sub_6A76E9(mapElement->properties.entrance.ride_index);
}
}
goto loc_6A6FD2;
}
}
break;
}
} while (!map_element_is_last_for_tile(mapElement++));
return;
loc_6A6F1F:
if (query) {
if (fence_in_the_way(x, y, mapElement->base_height, mapElement->clearance_height, direction ^ 2)) {
return;
}
if (footpath_element_is_queue(mapElement)) {
if (RCT2_ADDRESS(0x0098D7F0, uint8)[mapElement->properties.path.edges & 0x0F] < 2) {
neighbour_list_push(neighbourList, 4, direction, mapElement->properties.path.ride_index);
} else {
if (map_element_get_type(initialMapElement) == MAP_ELEMENT_TYPE_PATH &&
footpath_element_is_queue(initialMapElement)) {
if (footpath_disconnect_queue_from_path(x, y, mapElement, 0)) {
neighbour_list_push(neighbourList, 3, direction, mapElement->properties.path.ride_index);
}
}
}
} else {
neighbour_list_push(neighbourList, 2, direction, 255);
}
} else {
footpath_disconnect_queue_from_path(x, y, mapElement, 1 + ((flags >> 6) & 1));
mapElement->properties.path.edges |= (1 << (direction ^ 2));
if (footpath_element_is_queue(mapElement)) {
sub_6A76E9(mapElement->properties.path.ride_index);
}
}
if (!(flags & 0x48)) {
footpath_interrupt_peeps(x, y, mapElement->base_height * 8);
}
map_invalidate_element(x, y, mapElement);
}
loc_6A6FD2:
if (map_element_get_type(initialMapElement) == MAP_ELEMENT_TYPE_PATH) {
if (!query) {
initialMapElement->properties.path.edges |= (1 << direction);
map_invalidate_element(initialX, initialY, initialMapElement);
}
}
}
static void loc_6A6C85(
int x, int y, int direction, rct_map_element *mapElement,
int flags, bool query, rct_neighbour_list *neighbourList
) {
if (query && fence_in_the_way(x, y, mapElement->base_height, mapElement->clearance_height, direction))
return;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_ENTRANCE) {
if (!entrance_has_direction(mapElement, (direction - mapElement->type) & 3)) {
return;
}
}
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_TRACK) {
rct_ride *ride = GET_RIDE(mapElement->properties.track.ride_index);
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) {
return;
}
uint16 di = (mapElement->properties.track.sequence & 0x0F) | (mapElement->properties.track.type << 4);
if (!(RCT2_ADDRESS(0x0099CA64, uint8)[di] & (1 << 5))) {
return;
}
uint16 dx = (direction - mapElement->type) & 3;
if (!(RCT2_ADDRESS(0x0099CA64, uint16)[di / 2] & (1 << dx))) {
return;
}
}
int z = mapElement->base_height;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH) {
if (footpath_element_is_sloped(mapElement)) {
if ((footpath_element_get_slope_direction(mapElement) - direction) & 1) {
return;
}
if (footpath_element_get_slope_direction(mapElement) == direction) {
z += 2;
}
}
}
loc_6A6D7E(x, y, z, direction, mapElement, flags, query, neighbourList);
}
/**
*
* rct2: 0x006A6C66
*/
void footpath_connect_edges(int x, int y, rct_map_element *mapElement, int flags)
{
rct_neighbour_list neighbourList;
rct_neighbour neighbour;
sub_6A759F();
neighbour_list_init(&neighbourList);
sub_6A7642(x, y, mapElement);
for (int direction = 0; direction < 4; direction++) {
loc_6A6C85(x, y, direction, mapElement, flags, true, &neighbourList);
}
neighbour_list_sort(&neighbourList);
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH && footpath_element_is_queue(mapElement)) {
int rideIndex = -1;
for (int i = 0; i < neighbourList.count; i++) {
if (neighbourList.items[i].ride_index != 255) {
if (rideIndex == -1) {
rideIndex = neighbourList.items[i].ride_index;
} else if (rideIndex != neighbourList.items[i].ride_index) {
neighbour_list_remove(&neighbourList, i);
}
}
}
neighbourList.count = min(neighbourList.count, 2);
}
while (neighbour_list_pop(&neighbourList, &neighbour)) {
loc_6A6C85(x, y, neighbour.direction, mapElement, flags, false, NULL);
}
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH) {
footpath_connect_corners(x, y, mapElement);
}
}
/**
*
* rct2: 0x006A742F
*/
void footpath_chain_ride_queue(int rideIndex, int entranceIndex, int x, int y, rct_map_element *mapElement, int direction)
{
rct_map_element *lastPathElement, *lastQueuePathElement;
int lastPathX, lastPathY, lastPathDirection;
lastPathElement = NULL;
lastQueuePathElement = NULL;
int z = mapElement->base_height;
for (;;) {
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH) {
lastPathElement = mapElement;
lastPathX = x;
lastPathY = y;
lastPathDirection = direction;
if (footpath_element_is_sloped(mapElement)) {
if (footpath_element_get_slope_direction(mapElement) == direction) {
z += 2;
}
}
}
x += TileDirectionDelta[direction].x;
y += TileDirectionDelta[direction].y;
mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (lastQueuePathElement == mapElement)
continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH)
continue;
if (mapElement->base_height == z) {
if (footpath_element_is_sloped(mapElement)) {
if (footpath_element_get_slope_direction(mapElement) != direction)
break;
}
goto foundNextPath;
}
if (mapElement->base_height == z - 2) {
if (!footpath_element_is_sloped(mapElement))
break;
if ((footpath_element_get_slope_direction(mapElement) ^ 2) != direction)
break;
z -= 2;
goto foundNextPath;
}
} while (!map_element_is_last_for_tile(mapElement++));
break;
foundNextPath:
if (footpath_element_is_queue(mapElement)) {
mapElement->properties.path.type &= ~(1 << 3);
mapElement->properties.path.edges |= (1 << (direction ^ 2));
mapElement->properties.path.ride_index = rideIndex;
mapElement->properties.path.additions &= 0x8F;
mapElement->properties.path.additions |= (entranceIndex & 7) << 4;
if (lastQueuePathElement == NULL) {
lastQueuePathElement = mapElement;
}
if (mapElement->properties.path.edges & (1 << direction))
continue;
direction = (direction + 1) & 3;
if (mapElement->properties.path.edges & (1 << direction))
continue;
direction ^= 2;
if (mapElement->properties.path.edges & (1 << direction))
continue;
}
break;
}
if (rideIndex != 255 && lastPathElement != NULL) {
if (footpath_element_is_queue(lastPathElement)) {
lastPathElement->properties.path.type |= (1 << 3);
lastPathElement->type &= 0x3F;
lastPathElement->type |= lastPathDirection << 6;
map_animation_create(
MAP_ANIMATION_TYPE_QUEUE_BANNER,
lastPathX,
lastPathY,
lastPathElement->base_height
);
}
}
}
/**
*
* rct2: 0x006A759F
*/
void sub_6A759F()
{
uint8 *esi;
int i, x, y, z, direction, rideIndex;
rct_ride *ride;
rct_map_element *mapElement;
for (esi = (uint8*)0x00F3EFF8; esi < RCT2_GLOBAL(0x00F3EFF4, uint8*); esi++) {
rideIndex = *esi;
ride = GET_RIDE(rideIndex);
if (ride->type == RIDE_TYPE_NULL)
continue;
for (i = 0; i < 4; i++) {
if (ride->entrances[i] == 0xFFFF)
continue;
x = ride->entrances[i] & 0xFF;
y = ride->entrances[i] >> 8;
z = ride->station_heights[i];
mapElement = map_get_first_element_at(x, y);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_ENTRANCE)
continue;
if (mapElement->base_height != z)
continue;
if (mapElement->properties.entrance.type != ENTRANCE_TYPE_RIDE_ENTRANCE)
continue;
direction = (mapElement->type & 3) ^ 2;
footpath_chain_ride_queue(rideIndex, i, x << 5, y << 5, mapElement, direction);
} while (!map_element_is_last_for_tile(mapElement++));
}
}
}
/**
*
* rct2: 0x0069ADBD
*/
static void footpath_unown(int x, int y, rct_map_element *pathElement)
{
int ownershipUnk = 0;
int z = pathElement->base_height;
rct_map_element *surfaceElement = map_get_surface_element_at(x >> 5, y >> 5);
if (surfaceElement->base_height != z) {
z -= 2;
if (surfaceElement->base_height != z) {
ownershipUnk = (surfaceElement->properties.surface.ownership & 0xCF) >> 4;
}
}
map_buy_land_rights(x, y, x, y, 6, 1);
}
bool get_next_direction(int edges, int *direction)
{
int index = bitscanforward(edges);
if (index == -1)
return false;
*direction = index;
return true;
}
/**
*
* rct2: 0x0069AC1A
*/
int footpath_is_connected_to_map_edge_recurse(
int x, int y, int z, int direction, int flags,
int level, int distanceFromJunction, int junctionTolerance
) {
rct_map_element *mapElement;
int edges, slopeDirection;
x += TileDirectionDelta[direction].x;
y += TileDirectionDelta[direction].y;
if (++level > 250)
return FOOTPATH_SEARCH_TOO_COMPLEX;
// Check if we are at edge of map
if (x < 32 || y < 32)
return FOOTPATH_SEARCH_SUCCESS;
if (x >= RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE_UNITS, uint16) || y >= RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE_UNITS, uint16))
return FOOTPATH_SEARCH_SUCCESS;
mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH)
continue;
if (
footpath_element_is_sloped(mapElement) &&
(slopeDirection = footpath_element_get_slope_direction(mapElement)) != direction
) {
if ((slopeDirection ^ 2) != direction) continue;
if (mapElement->base_height + 2 != z) continue;
} else if (mapElement->base_height != z) {
continue;
}
if (mapElement->type & RCT2_GLOBAL(0x00F1AEE0, uint8)) continue;
if (flags & 0x20) {
footpath_unown(x, y, mapElement);
}
edges = mapElement->properties.path.edges & 0x0F;
direction ^= 2;
if (!(flags & 0x80)) {
if (mapElement[1].type == MAP_ELEMENT_TYPE_BANNER) {
for (int i = 1; i < 4; i++) {
if (map_element_is_last_for_tile(&mapElement[i - 1])) break;
if (mapElement[i].type != MAP_ELEMENT_TYPE_BANNER) break;
edges &= mapElement[i].properties.banner.flags;
}
}
if (mapElement[2].type == MAP_ELEMENT_TYPE_BANNER && mapElement[1].type != MAP_ELEMENT_TYPE_PATH) {
for (int i = 1; i < 6; i++) {
if (map_element_is_last_for_tile(&mapElement[i - 1])) break;
if (mapElement[i].type != MAP_ELEMENT_TYPE_BANNER) break;
edges &= mapElement[i].properties.banner.flags;
}
}
}
goto searchFromFootpath;
} while (!map_element_is_last_for_tile(mapElement++));
return level == 1 ? FOOTPATH_SEARCH_NOT_FOUND : FOOTPATH_SEARCH_INCOMPLETE;
searchFromFootpath:
// Exclude direction we came from
z = mapElement->base_height;
edges &= ~(1 << direction);
// Find next direction to go
if (!get_next_direction(edges, &direction)) {
return FOOTPATH_SEARCH_INCOMPLETE;
}
edges &= ~(1 << direction);
if (edges == 0) {
// Only possible direction to go
if (footpath_element_is_sloped(mapElement) && footpath_element_get_slope_direction(mapElement) == direction) {
z += 2;
}
return footpath_is_connected_to_map_edge_recurse(
x, y, z, direction, flags,
level, distanceFromJunction + 1, junctionTolerance
);
} else {
// We have reached a junction
if (distanceFromJunction != 0) {
junctionTolerance--;
}
junctionTolerance--;
if (junctionTolerance < 0) {
return FOOTPATH_SEARCH_TOO_COMPLEX;
}
do {
edges &= ~(1 << direction);
if (footpath_element_is_sloped(mapElement) && footpath_element_get_slope_direction(mapElement) == direction) {
z += 2;
}
int result = footpath_is_connected_to_map_edge_recurse(x, y, z, direction, flags, level, 0, junctionTolerance);
if (result == FOOTPATH_SEARCH_SUCCESS) {
return result;
}
} while (get_next_direction(edges, &direction));
return FOOTPATH_SEARCH_INCOMPLETE;
}
}
int footpath_is_connected_to_map_edge(int x, int y, int z, int direction, int flags)
{
RCT2_GLOBAL(0x00F1AEE0, uint8) = 1;
return footpath_is_connected_to_map_edge_recurse(x, y, z, direction, flags, 0, 0, 16);
}
bool footpath_element_is_sloped(rct_map_element *mapElement)
{
return mapElement->properties.path.type & 4;
}
int footpath_element_get_slope_direction(rct_map_element *mapElement)
{
return mapElement->properties.path.type & 3;
}
bool footpath_element_is_queue(rct_map_element *mapElement)
{
return mapElement->type & 1;
}
bool footpath_element_is_wide(rct_map_element *mapElement)
{
return mapElement->type & 2;
}
/**
*
* rct2: 0x006A76E9
*/
void sub_6A76E9(int rideIndex)
{
if (rideIndex == 255)
return;
*(RCT2_GLOBAL(0x00F3EFF4, uint8*)) = rideIndex;
RCT2_GLOBAL(0x00F3EFF4, uint8*)++;
}
/**
*
* rct2: 0x006A7642
*/
void sub_6A7642(int x, int y, rct_map_element *mapElement)
{
int elementType = map_element_get_type(mapElement);
switch (elementType) {
case MAP_ELEMENT_TYPE_PATH:
if (footpath_element_is_queue(mapElement)) {
sub_6A76E9(mapElement->properties.path.ride_index);
for (int direction = 0; direction < 4; direction++) {
if (mapElement->properties.path.edges & (1 << direction)) {
footpath_chain_ride_queue(255, 0, x, y, mapElement, direction);
}
}
mapElement->properties.path.ride_index = 255;
}
break;
case MAP_ELEMENT_TYPE_ENTRANCE:
if (mapElement->properties.entrance.type == ENTRANCE_TYPE_RIDE_ENTRANCE) {
sub_6A76E9(mapElement->properties.entrance.ride_index);
footpath_chain_ride_queue(255, 0, x, y, mapElement, (mapElement->type & MAP_ELEMENT_DIRECTION_MASK) ^ 2);
}
break;
}
}
/**
*
* rct2: 0x006A6B7F
*/
static void footpath_remove_edges_towards_here(int x, int y, int z, int direction, rct_map_element *mapElement, bool isQueue)
{
int d;
if (footpath_element_is_queue(mapElement)) {
sub_6A76E9(mapElement->properties.path.ride_index);
}
d = direction ^ 2;
mapElement->properties.path.edges &= ~(1 << d);
d = ((d - 1) & 3) + 4;
mapElement->properties.path.edges &= ~(1 << d);
d = (((d - 4) + 1) & 3) + 4;
mapElement->properties.path.edges &= ~(1 << d);
map_invalidate_tile(x, y, mapElement->base_height, mapElement->clearance_height);
if (isQueue) footpath_disconnect_queue_from_path(x, y, mapElement, -1);
direction = (direction + 1) & 3;
x += TileDirectionDelta[direction].x;
y += TileDirectionDelta[direction].y;
mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH)
continue;
if (mapElement->base_height != z)
continue;
if (footpath_element_is_sloped(mapElement))
break;
d = ((direction + 1) & 3) + 4;
mapElement->properties.path.edges &= ~(1 << d);
map_invalidate_tile(x, y, mapElement->base_height, mapElement->clearance_height);
break;
} while (!map_element_is_last_for_tile(mapElement++));
}
/**
*
* rct2: 0x006A6B14
*/
static void footpath_remove_edges_towards(int x, int y, int z0, int z1, int direction, bool isQueue)
{
rct_map_element *mapElement;
int slope;
mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH)
continue;
if (z1 == mapElement->base_height) {
if (footpath_element_is_sloped(mapElement)) {
slope = footpath_element_get_slope_direction(mapElement);
if (slope != direction)
break;
}
footpath_remove_edges_towards_here(x, y, z1, direction, mapElement, isQueue);
break;
}
if (z0 == mapElement->base_height) {
if (!footpath_element_is_sloped(mapElement))
break;
slope = footpath_element_get_slope_direction(mapElement) ^ 2;
if (slope != direction)
break;
footpath_remove_edges_towards_here(x, y, z1, direction, mapElement, isQueue);
break;
}
} while (!map_element_is_last_for_tile(mapElement++));
}
/**
*
* rct2: 0x006A6AA7
* @param x x-coordinate in units (not tiles)
* @param y y-coordinate in units (not tiles)
*/
void footpath_remove_edges_at(int x, int y, rct_map_element *mapElement)
{
rct_ride *ride;
int z0, z1, slope;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_TRACK) {
int rideIndex = mapElement->properties.track.ride_index;
ride = GET_RIDE(rideIndex);
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE))
return;
}
sub_6A7642(x, y, mapElement);
for (int direction = 0; direction < 4; direction++) {
z1 = mapElement->base_height;
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH) {
if (footpath_element_is_sloped(mapElement)) {
slope = footpath_element_get_slope_direction(mapElement);
if ((slope - direction) & 1)
continue;
z1 += slope == direction ? 2 : 0;
}
}
z0 = z1 - 2;
footpath_remove_edges_towards(x + TileDirectionDelta[direction].x, y + TileDirectionDelta[direction].y,
z0, z1, direction, footpath_element_is_queue(mapElement));
}
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH)
mapElement->properties.path.edges = 0;
}