mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-20 13:33:02 +01:00
733 lines
23 KiB
C
733 lines
23 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 "../game.h"
|
|
#include "../localisation/localisation.h"
|
|
#include "../util/util.h"
|
|
#include "footpath.h"
|
|
#include "map.h"
|
|
#include "scenery.h"
|
|
#include "../cheats.h"
|
|
#include "../config.h"
|
|
|
|
void footpath_interrupt_peeps(int x, int y, int z);
|
|
|
|
enum {
|
|
FOOTPATH_CONSTRUCTION_FLAG_ALLOW_DURING_PAUSED = 1 << 3
|
|
};
|
|
|
|
const rct_xy16 word_981D6C[] = {
|
|
{ -1, 0 },
|
|
{ 0, 1 },
|
|
{ 1, 0 },
|
|
{ 0, -1 }
|
|
};
|
|
|
|
/**
|
|
*
|
|
* 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(0x001358830, uint16) - 32) {
|
|
direction++;
|
|
if (x != RCT2_GLOBAL(0x001358830, 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 & (1 << 6))) {
|
|
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 & (1 << 7)))
|
|
sub_6A6C66(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)
|
|
{
|
|
rct_map_element *mapElement;
|
|
int bl, zHigh;
|
|
|
|
if (!sub_68B044())
|
|
return MONEY32_UNDEFINED;
|
|
|
|
if ((flags & GAME_COMMAND_FLAG_APPLY) && !(flags & (FOOTPATH_CONSTRUCTION_FLAG_ALLOW_DURING_PAUSED | (1 << 6))))
|
|
footpath_remove_litter(x, y, RCT2_GLOBAL(0x009DEA62, 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 (!map_can_construct_with_clear_at(x, y, z, zHigh, (void*)0x006A6733, bl))
|
|
return MONEY32_UNDEFINED;
|
|
|
|
RCT2_GLOBAL(0x00F3EFA4, uint8) = RCT2_GLOBAL(0x00F1AD60, uint8);
|
|
if (RCT2_GLOBAL(0x00F1AD60, uint8) & 4) {
|
|
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 = RCT2_GLOBAL(0x00F3EF88, uint8);
|
|
mapElement->properties.path.addition_status = 255;
|
|
mapElement->flags &= ~MAP_ELEMENT_FLAG_BROKEN;
|
|
if (flags & (1 << 6))
|
|
mapElement->flags |= 1 << 4;
|
|
|
|
RCT2_GLOBAL(0x00F3EFF4, uint32) = 0x00F3EFF8;
|
|
|
|
if (!(flags & (1 << 7)))
|
|
sub_6A6AA7(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)
|
|
{
|
|
if ((mapElement->properties.path.type >> 4) != (type & 0x0F) || (mapElement->type & 1) != (type >> 7)) {
|
|
RCT2_GLOBAL(0x00F3EFD9, money32) += MONEY(6, 00);
|
|
} else if (RCT2_GLOBAL(0x00F3EF88, uint16) != 0) {
|
|
if (
|
|
!(flags & (1 << 6)) &&
|
|
(mapElement->properties.path.additions & 0x0F) == RCT2_GLOBAL(0x00F3EF88, uint16) &&
|
|
!(mapElement->flags & MAP_ELEMENT_FLAG_BROKEN)
|
|
) {
|
|
if (flags & (1 << 4))
|
|
return MONEY32_UNDEFINED;
|
|
|
|
return RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY ? 0 : RCT2_GLOBAL(0x00F3EFD9, money32);
|
|
}
|
|
|
|
if (RCT2_GLOBAL(0x00F3EF88, uint16) != 0) {
|
|
rct_scenery_entry* scenery_entry = g_pathBitSceneryEntries[RCT2_GLOBAL(0x00F3EF88, uint16) - 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) && (mapElement->type & 1)) {
|
|
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) && !(mapElement->type & 1)) {
|
|
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 & (1 << 4))
|
|
return MONEY32_UNDEFINED;
|
|
|
|
if (flags & (1 << 6)) {
|
|
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 (
|
|
(
|
|
RCT2_GLOBAL(0x00F3EF88, uint16) != 0 &&
|
|
!(flags & (1 << 6))
|
|
) ||
|
|
(
|
|
RCT2_GLOBAL(0x00F3EF88, uint16) == 0 ||
|
|
(mapElement->properties.path.additions & 0x80)
|
|
)
|
|
) {
|
|
mapElement->properties.path.additions &= ~0x80;
|
|
}
|
|
|
|
mapElement->properties.path.additions = (mapElement->properties.path.additions & 0xF0) | RCT2_GLOBAL(0x00F3EF88, uint8);
|
|
mapElement->flags &= ~0x20;
|
|
if (RCT2_GLOBAL(0x00F3EF88, uint16) != 0) {
|
|
rct_scenery_entry* scenery_entry = g_pathBitSceneryEntries[RCT2_GLOBAL(0x00F3EF88, uint16) - 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 & (1 << 4))
|
|
return MONEY32_UNDEFINED;
|
|
|
|
if (flags & GAME_COMMAND_FLAG_APPLY) {
|
|
RCT2_GLOBAL(0x00F3EFF4, uint32) = 0x00F3EFF8;
|
|
|
|
if (!(flags & (1 << 7)))
|
|
sub_6A6AA7(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) | RCT2_GLOBAL(0x00F3EF88, uint8);
|
|
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 path_bit_type)
|
|
{
|
|
rct_map_element *mapElement;
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = 12;
|
|
RCT2_GLOBAL(0x009DEA5E, uint16) = x + 16;
|
|
RCT2_GLOBAL(0x009DEA60, uint16) = y + 16;
|
|
RCT2_GLOBAL(0x009DEA62, uint16) = z * 8;
|
|
|
|
if (!(flags & FOOTPATH_CONSTRUCTION_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;
|
|
RCT2_GLOBAL(0x00F3EF88, uint16) = path_bit_type; // di
|
|
|
|
if (x >= RCT2_GLOBAL(0x01358830, uint16) || y >= RCT2_GLOBAL(0x01358830, 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) || gSandboxMode) && !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;
|
|
}
|
|
|
|
mapElement = map_get_footpath_element_slope((x / 32), (y / 32), z, slope);
|
|
return mapElement == NULL ?
|
|
footpath_element_insert(type, x, y, z, slope, flags) :
|
|
footpath_element_update(x, y, mapElement, type, flags);
|
|
}
|
|
|
|
/* 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) = 12;
|
|
RCT2_GLOBAL(0x009DEA5E, uint16) = x + 16;
|
|
RCT2_GLOBAL(0x009DEA60, uint16) = y + 16;
|
|
RCT2_GLOBAL(0x009DEA62, uint16) = z * 8;
|
|
|
|
if (!(flags & FOOTPATH_CONSTRUCTION_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) || gSandboxMode) && !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);
|
|
sub_6A6AA7(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)
|
|
{
|
|
if (*ebx & (1 << 5))
|
|
RCT2_CALLFUNC_X(0x006A61DE, eax, ebx, ecx, edx, esi, edi, ebp);
|
|
else
|
|
*ebx = footpath_place_real((*edx >> 8) & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*ebx >> 8) & 0xFF, *ebx & 0xFF, *edi & 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, (1 << 6) | (1 << 5) | (1 << 4) | FOOTPATH_CONSTRUCTION_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),
|
|
(1 << 0) | FOOTPATH_CONSTRUCTION_FLAG_ALLOW_DURING_PAUSED | (1 << 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, 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 ebp = (*mapElement)->properties.entrance.type << 4;
|
|
int bl = (*mapElement)->properties.entrance.index & 0xF;
|
|
if (RCT2_GLOBAL(0x0097B974 + ebp + bl, uint16) & 0xF) {
|
|
int bx = bitscanforward(RCT2_GLOBAL(0x0097B974 + ebp + bl, uint16));
|
|
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 ebp = (*mapElement)->properties.entrance.type << 4;
|
|
int bl = (*mapElement)->properties.entrance.index & 0xF; // Seems to be always 0?
|
|
// The table at 0x0097B974 is only 48 bytes big
|
|
if (RCT2_GLOBAL(0x0097B974 + ebp + bl, uint16) & 0xF) {
|
|
int bx = bitscanforward(RCT2_GLOBAL(0x0097B974 + ebp + bl, uint16));
|
|
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) {
|
|
sub_6EC60B((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 == 0) {
|
|
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: 0x006A6C66
|
|
*/
|
|
void sub_6A6C66(int x, int y, rct_map_element *mapElement, int flags)
|
|
{
|
|
RCT2_CALLPROC_X(0x006A6C66, x, flags, y, 0, (int)mapElement, 0, 0);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006A742F
|
|
*/
|
|
void sub_6A742F(int rideIndex, int entranceIndex, int x, int y, rct_map_element *mapElement, int direction)
|
|
{
|
|
RCT2_CALLPROC_X(0x006A742F, x, direction, y, (entranceIndex << 8) | rideIndex, (int)mapElement, 0, 0);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* 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;
|
|
sub_6A742F(rideIndex, i, x << 5, y << 5, mapElement, direction);
|
|
} while (!map_element_is_last_for_tile(mapElement++));
|
|
}
|
|
}
|
|
}
|