OpenRCT2/src/ride/track.c

1900 lines
75 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* 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
#include "../addresses.h"
#include "../audio/audio.h"
#include "../cheats.h"
#include "../config.h"
#include "../game.h"
#include "../interface/viewport.h"
#include "../localisation/localisation.h"
#include "../management/finance.h"
#include "../network/network.h"
#include "../platform/platform.h"
#include "../rct1.h"
#include "../util/sawyercoding.h"
#include "../util/util.h"
#include "../world/map_animation.h"
#include "../world/park.h"
#include "../world/scenery.h"
#include "../world/footpath.h"
#include "../windows/error.h"
#include "ride.h"
#include "ride_data.h"
#include "ride_ratings.h"
#include "track.h"
#include "track_data.h"
uint8 gTrackGroundFlags;
/**
*
* rct2: 0x00997C9D
*/
const rct_trackdefinition *gTrackDefinitions = (rct_trackdefinition*)0x00997C9D;
const rct_trackdefinition *gFlatRideTrackDefinitions = (rct_trackdefinition*)0x0099849D;
// TODO This table is incorrect or at least missing 69 elements. There should be 256 in total!
const rct_trackdefinition gTrackDefinitions_INCORRECT[] = {
// TYPE VANGLE END VANGLE START BANK END BANK START SPECIAL
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_END_STATION
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_BEGIN_STATION
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_MIDDLE_STATION
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_TO_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_UP_TO_60_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_UP_TO_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_UP_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_TO_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_DOWN_TO_60_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_DOWN_TO_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_DOWN_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_5_TILES
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_5_TILES
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_TO_LEFT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_TO_RIGHT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_BANK_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_BANK_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_BANKED_LEFT_QUARTER_TURN_5_TILES
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_BANKED_RIGHT_QUARTER_TURN_5_TILES
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_BANK_TO_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_BANK_TO_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_25, TRACK_BANK_LEFT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_UP_TO_LEFT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_25, TRACK_BANK_RIGHT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_UP_TO_RIGHT_BANK
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_BANK_TO_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_BANK_TO_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_25, TRACK_BANK_LEFT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_DOWN_TO_LEFT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_25, TRACK_BANK_RIGHT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_DOWN_TO_RIGHT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_BANK
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_5_TILES_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_5_TILES_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_5_TILES_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_5_TILES_25_DEG_DOWN
{ TRACK_S_BEND, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_S_BEND_LEFT
{ TRACK_S_BEND, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_S_BEND_RIGHT
{ TRACK_VERTICAL_LOOP, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_UNKNOWN_VERTICAL_LOOP }, // ELEM_LEFT_VERTICAL_LOOP
{ TRACK_VERTICAL_LOOP, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_UNKNOWN_VERTICAL_LOOP }, // ELEM_RIGHT_VERTICAL_LOOP
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_3_TILES
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_3_TILES
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_3_TILES_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_3_TILES_BANK
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_1_TILE
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_1_TILE
{ TRACK_TWIST, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_TWIST_DOWN_TO_UP
{ TRACK_TWIST, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_TWIST_DOWN_TO_UP
{ TRACK_TWIST, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_NONE }, // ELEM_LEFT_TWIST_UP_TO_DOWN
{ TRACK_TWIST, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_NONE }, // ELEM_RIGHT_TWIST_UP_TO_DOWN
{ TRACK_HALF_LOOP, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_25, TRACK_BANK_UPSIDE_DOWN, TRACK_BANK_NONE, TRACK_HALF_LOOP_UP }, // ELEM_HALF_LOOP_UP
{ TRACK_HALF_LOOP, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_HALF_LOOP_DOWN }, // ELEM_HALF_LOOP_DOWN
{ TRACK_CORKSCREW, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_CORKSCREW_UP
{ TRACK_CORKSCREW, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_CORKSCREW_UP
{ TRACK_CORKSCREW, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_CORKSCREW_DOWN }, // ELEM_LEFT_CORKSCREW_DOWN
{ TRACK_CORKSCREW, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_CORKSCREW_DOWN }, // ELEM_RIGHT_CORKSCREW_DOWN
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_TO_60_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_UP_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_TO_60_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_DOWN_TO_FLAT
{ TRACK_TOWER_BASE, TRACK_VANGLE_TOWER, TRACK_VANGLE_TOWER, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_TOWER_BASE
{ TRACK_TOWER_BASE, TRACK_VANGLE_TOWER, TRACK_VANGLE_TOWER, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_TOWER_SECTION
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_COVERED
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_UP_COVERED
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_UP_COVERED
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_TO_25_DEG_UP_COVERED
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_UP_TO_60_DEG_UP_COVERED
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_UP_TO_25_DEG_UP_COVERED
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_UP_TO_FLAT_COVERED
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_DOWN_COVERED
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_DOWN_COVERED
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_TO_25_DEG_DOWN_COVERED
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_DOWN_TO_60_DEG_DOWN_COVERED
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_DOWN_TO_25_DEG_DOWN_COVERED
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_25_DEG_DOWN_TO_FLAT_COVERED
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_5_TILES_COVERED
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_5_TILES_COVERED
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_S_BEND_LEFT_COVERED
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_S_BEND_RIGHT_COVERED
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_3_TILES_COVERED
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_3_TILES_COVERED
{ TRACK_HELIX_SMALL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_HALF_BANKED_HELIX_UP_SMALL
{ TRACK_HELIX_SMALL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_HALF_BANKED_HELIX_UP_SMALL
{ TRACK_HELIX_SMALL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_HALF_BANKED_HELIX_DOWN_SMALL
{ TRACK_HELIX_SMALL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_HALF_BANKED_HELIX_DOWN_SMALL
{ TRACK_HELIX_SMALL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_HALF_BANKED_HELIX_UP_LARGE
{ TRACK_HELIX_SMALL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_HALF_BANKED_HELIX_UP_LARGE
{ TRACK_HELIX_SMALL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_HALF_BANKED_HELIX_DOWN_LARGE
{ TRACK_HELIX_SMALL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_HALF_BANKED_HELIX_DOWN_LARGE
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_1_TILE_60_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_1_TILE_60_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_1_TILE_60_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_1_TILE_60_DEG_DOWN
{ TRACK_BRAKES, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_BRAKES
{ TRACK_ROTATION_CONTROL_TOGGLE,TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_ROTATION_CONTROL_TOGGLE
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP
{ TRACK_HELIX_LARGE, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_UP
{ TRACK_HELIX_LARGE, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_UP
{ TRACK_HELIX_LARGE, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_QUARTER_BANKED_HELIX_LARGE_DOWN
{ TRACK_HELIX_LARGE, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_DOWN
{ TRACK_HELIX_LARGE_UNBANKED, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_HELIX_LARGE_UP
{ TRACK_HELIX_LARGE_UNBANKED, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_HELIX_LARGE_UP
{ TRACK_HELIX_LARGE_UNBANKED, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_HELIX_LARGE_DOWN
{ TRACK_HELIX_LARGE_UNBANKED, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_HELIX_LARGE_DOWN
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_25, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_25_DEG_UP_LEFT_BANKED
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_25, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_25_DEG_UP_RIGHT_BANKED
{ TRACK_WATERFALL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_WATERFALL
{ TRACK_RAPIDS, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RAPIDS
{ TRACK_ON_RIDE_PHOTO, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_ON_RIDE_PHOTO
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_25, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_25_DEG_DOWN_LEFT_BANKED
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_25, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_25_DEG_DOWN_RIGHT_BANKED
{ TRACK_WATER_SPLASH, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_WATER_SPLASH
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_TO_60_DEG_UP_LONG_BASE
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_UP_TO_FLAT_LONG_BASE
{ TRACK_WHIRLPOOL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_WHIRLPOOL
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_FLAT_TO_60_DEG_DOWN_LONG_BASE
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_UP_TO_FLAT_LONG_BASE
{ TRACK_LIFT_HILL, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_CABLE_LIFT_HILL
{ TRACK_WHOA_BELLY, TRACK_VANGLE_WHOA_BELLY, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_REVERSE_WHOA_BELLY_SLOPE
{ TRACK_WHOA_BELLY, TRACK_VANGLE_WHOA_BELLY, TRACK_VANGLE_WHOA_BELLY, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_REVERSE_WHOA_BELLY_VERTICAL
{ TRACK_FLAT, TRACK_SLOPE_UP_90, TRACK_SLOPE_UP_90, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_90_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_DOWN_90, TRACK_SLOPE_DOWN_90, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_90_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_UP_90, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_UP_TO_90_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_DOWN_90, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_90_DEG_DOWN_TO_60_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_UP_90, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_90_DEG_UP_TO_60_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_DOWN_90, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_60_DEG_DOWN_TO_90_DEG_DOWN
{ TRACK_BRAKE_FOR_DROP, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_BRAKE_FOR_DROP
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_EIGHTH_TO_DIAG
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_EIGHTH_TO_DIAG
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_EIGHTH_TO_ORTHOGONAL
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_EIGHTH_TO_ORTHOGONAL
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_EIGHTH_BANK_TO_DIAG
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_EIGHTH_BANK_TO_DIAG
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_EIGHTH_BANK_TO_ORTHOGONAL
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_EIGHTH_BANK_TO_ORTHOGONAL
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_FLAT
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_60_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_FLAT_TO_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_25_DEG_UP_TO_60_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_60_DEG_UP_TO_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_25_DEG_UP_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_60_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_FLAT_TO_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_25_DEG_DOWN_TO_60_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_60_DEG_DOWN_TO_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_25_DEG_DOWN_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_UP_60, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_FLAT_TO_60_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_60_DEG_UP_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_DOWN_60, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_FLAT_TO_60_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_60, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_60_DEG_DOWN_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_FLAT_TO_LEFT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_FLAT_TO_RIGHT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_DIAG_LEFT_BANK_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_DIAG_RIGHT_BANK_TO_FLAT
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_DIAG_LEFT_BANK_TO_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_DIAG_RIGHT_BANK_TO_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_25, TRACK_BANK_LEFT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_25_DEG_UP_TO_LEFT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_25, TRACK_BANK_RIGHT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_25_DEG_UP_TO_RIGHT_BANK
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_DIAG_LEFT_BANK_TO_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_DIAG_RIGHT_BANK_TO_25_DEG_DOWN
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_25, TRACK_BANK_LEFT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_25_DEG_DOWN_TO_LEFT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_25, TRACK_BANK_RIGHT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_DIAG_25_DEG_DOWN_TO_RIGHT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_LEFT, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_DIAG_LEFT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_RIGHT, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_DIAG_RIGHT_BANK
{ TRACK_LOG_FLUME_REVERSER, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LOG_FLUME_REVERSER
{ TRACK_SPINNING_TUNNEL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_SPINNING_TUNNEL
{ TRACK_BARREL_ROLL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_BARREL_ROLL_UP_TO_DOWN
{ TRACK_BARREL_ROLL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_BARREL_ROLL_UP_TO_DOWN
{ TRACK_BARREL_ROLL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_NONE }, // ELEM_LEFT_BARREL_ROLL_DOWN_TO_UP
{ TRACK_BARREL_ROLL, TRACK_SLOPE_NONE, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_NONE }, // ELEM_RIGHT_BARREL_ROLL_DOWN_TO_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_LEFT, TRACK_NONE }, // ELEM_LEFT_BANK_TO_LEFT_QUARTER_TURN_3_TILES_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_UP_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_RIGHT, TRACK_NONE }, // ELEM_RIGHT_BANK_TO_RIGHT_QUARTER_TURN_3_TILES_25_DEG_UP
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_25, TRACK_BANK_LEFT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN_TO_LEFT_BANK
{ TRACK_FLAT, TRACK_SLOPE_NONE, TRACK_SLOPE_DOWN_25, TRACK_BANK_RIGHT, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_RIGHT_QUARTER_TURN_3_TILES_25_DEG_DOWN_TO_RIGHT_BANK
{ TRACK_POWERED_LIFT, TRACK_SLOPE_UP_25, TRACK_SLOPE_UP_25, TRACK_BANK_NONE, TRACK_BANK_NONE, TRACK_NONE }, // ELEM_POWERED_LIFT
{ TRACK_HALF_LOOP_LARGE, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_25, TRACK_BANK_UPSIDE_DOWN, TRACK_BANK_NONE, TRACK_HALF_LOOP_UP }, // ELEM_LEFT_LARGE_HALF_LOOP_UP
{ TRACK_HALF_LOOP_LARGE, TRACK_SLOPE_NONE, TRACK_SLOPE_UP_25, TRACK_BANK_UPSIDE_DOWN, TRACK_BANK_NONE, TRACK_HALF_LOOP_UP }, // ELEM_RIGHT_LARGE_HALF_LOOP_UP
{ TRACK_HALF_LOOP_LARGE, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_HALF_LOOP_DOWN }, // ELEM_RIGHT_LARGE_HALF_LOOP_DOWN
{ TRACK_HALF_LOOP_LARGE, TRACK_SLOPE_DOWN_25, TRACK_SLOPE_NONE, TRACK_BANK_NONE, TRACK_BANK_UPSIDE_DOWN, TRACK_HALF_LOOP_DOWN }, // ELEM_LEFT_LARGE_HALF_LOOP_DOWN
};
/**
* Helper method to determine if a connects to b by its bank and angle, not location.
*/
int track_is_connected_by_shape(rct_map_element *a, rct_map_element *b)
{
int trackType, aBank, aAngle, bBank, bAngle;
trackType = a->properties.track.type;
aBank = gTrackDefinitions[trackType].bank_end;
aAngle = gTrackDefinitions[trackType].vangle_end;
aBank = track_get_actual_bank(a, aBank);
trackType = b->properties.track.type;
bBank = gTrackDefinitions[trackType].bank_start;
bAngle = gTrackDefinitions[trackType].vangle_start;
bBank = track_get_actual_bank(b, bBank);
return aBank == bBank && aAngle == bAngle;
}
const rct_preview_track *get_track_def_from_ride(rct_ride *ride, int trackType)
{
return ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE) ?
FlatRideTrackBlocks[trackType] :
TrackBlocks[trackType];
}
const rct_track_coordinates *get_track_coord_from_ride(rct_ride *ride, int trackType){
return ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE) ?
&FlatTrackCoordinates[trackType] :
&TrackCoordinates[trackType];
}
const rct_preview_track *get_track_def_from_ride_index(int rideIndex, int trackType)
{
return get_track_def_from_ride(get_ride(rideIndex), trackType);
}
static rct_map_element *find_station_element(int x, int y, int z, int direction, int rideIndex)
{
rct_map_element *mapElement = map_get_first_element_at(x >> 5, y >> 5);
do {
if (z != mapElement->base_height) continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK) continue;
if (map_element_get_direction(mapElement) != direction) continue;
if (mapElement->properties.track.ride_index != rideIndex) continue;
if (!track_element_is_station(mapElement)) continue;
return mapElement;
} while (!map_element_is_last_for_tile(mapElement++));
return NULL;
}
static void ride_remove_station(rct_ride *ride, int x, int y, int z)
{
uint16 xy = (x >> 5) | ((y >> 5) << 8);
for (int i = 0; i < 4; i++) {
if (ride->station_starts[i] == xy && ride->station_heights[i] == z) {
ride->station_starts[i] = 0xFFFF;
ride->num_stations--;
break;
}
}
}
/**
*
* rct2: 0x006C4D89
*/
static bool track_add_station_element(int x, int y, int z, int direction, int rideIndex, int flags)
{
int stationX0 = x;
int stationY0 = y;
int stationX1 = x;
int stationY1 = y;
int stationLength = 1;
rct_ride *ride = get_ride(rideIndex);
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_3)) {
if (ride->num_stations >= 4) {
gGameCommandErrorText = STR_NO_MORE_STATIONS_ALLOWED_ON_THIS_RIDE;
return false;
}
if (flags & GAME_COMMAND_FLAG_APPLY) {
int stationIndex = -1;
for (int i = 0; i < 4; i++) {
if (ride->station_starts[i] == 0xFFFF) {
stationIndex = i;
break;
}
}
assert(stationIndex != -1);
ride->station_starts[stationIndex] = (x >> 5) | ((y >> 5) << 8);
ride->station_heights[stationIndex] = z;
ride->station_depart[stationIndex] = 1;
ride->station_length[stationIndex] = 0;
ride->num_stations++;
}
return true;
}
rct_map_element *stationElement;
// Search backwards for more station
x = stationX0;
y = stationY0;
do {
x -= TileDirectionDelta[direction].x;
y -= TileDirectionDelta[direction].y;
stationElement = find_station_element(x, y, z, direction, rideIndex);
if (stationElement != NULL) {
if (stationElement->properties.track.type == TRACK_ELEM_END_STATION) {
if (flags & GAME_COMMAND_FLAG_APPLY) {
ride_remove_station(ride, x, y, z);
}
}
stationX0 = x;
stationY0 = y;
stationLength++;
}
} while (stationElement != NULL);
// Search forwards for more station
x = stationX1;
y = stationY1;
do {
x += TileDirectionDelta[direction].x;
y += TileDirectionDelta[direction].y;
stationElement = find_station_element(x, y, z, direction, rideIndex);
if (stationElement != NULL) {
if (stationElement->properties.track.type == TRACK_ELEM_END_STATION) {
if (flags & GAME_COMMAND_FLAG_APPLY) {
ride_remove_station(ride, x, y, z);
}
}
stationX1 = x;
stationY1 = y;
stationLength++;
}
} while (stationElement != NULL);
if (stationX0 == stationX1 && stationY0 == stationY1 && ride->num_stations >= 4) {
gGameCommandErrorText = STR_NO_MORE_STATIONS_ALLOWED_ON_THIS_RIDE;
return false;
}
if (stationLength > MAX_STATION_PLATFORM_LENGTH) {
gGameCommandErrorText = STR_STATION_PLATFORM_TOO_LONG;
return false;
}
if (flags & GAME_COMMAND_FLAG_APPLY) {
x = stationX1;
y = stationY1;
bool finaliseStationDone;
do {
finaliseStationDone = true;
stationElement = find_station_element(x, y, z, direction, rideIndex);
if (stationElement != NULL) {
int targetTrackType;
if (x == stationX1 && y == stationY1) {
int stationIndex = -1;
for (int i = 0; i < 4; i++) {
if (ride->station_starts[i] == 0xFFFF) {
stationIndex = i;
break;
}
}
assert(stationIndex != -1);
uint16 xy = (x >> 5) | ((y >> 5) << 8);
ride->station_starts[stationIndex] = xy;
ride->station_heights[stationIndex] = z;
ride->station_depart[stationIndex] = 1;
ride->station_length[stationIndex] = stationLength;
ride->num_stations++;
targetTrackType = TRACK_ELEM_END_STATION;
} else if (x == stationX0 && y == stationY0) {
targetTrackType = TRACK_ELEM_BEGIN_STATION;
} else {
targetTrackType = TRACK_ELEM_MIDDLE_STATION;
}
stationElement->properties.track.type = targetTrackType;
map_invalidate_element(x, y, stationElement);
if (x != stationX0 || y != stationY0) {
x -= TileDirectionDelta[direction].x;
y -= TileDirectionDelta[direction].y;
finaliseStationDone = false;
}
}
} while (!finaliseStationDone);
}
return true;
}
/**
*
* rct2: 0x006C494B
*/
static bool track_remove_station_element(int x, int y, int z, int direction, int rideIndex, int flags)
{
int removeX = x;
int removeY = y;
int stationX0 = x;
int stationY0 = y;
int stationX1 = x;
int stationY1 = y;
int stationLength = 0;
int byte_F441D1 = -1;
rct_ride *ride = get_ride(rideIndex);
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_3)) {
rct_map_element *mapElement = map_get_track_element_at_with_direction_from_ride(x, y, z, direction, rideIndex);
if (mapElement != NULL) {
if (flags & GAME_COMMAND_FLAG_APPLY) {
ride_remove_station(ride, x, y, z);
}
}
return true;
}
rct_map_element *stationElement;
// Search backwards for more station
x = stationX0;
y = stationY0;
while ((stationElement = find_station_element(x, y, z, direction, rideIndex)) != NULL) {
if (stationElement->properties.track.type == TRACK_ELEM_END_STATION) {
if (flags & GAME_COMMAND_FLAG_APPLY) {
ride_remove_station(ride, x, y, z);
}
}
stationX0 = x;
stationY0 = y;
byte_F441D1++;
x -= TileDirectionDelta[direction].x;
y -= TileDirectionDelta[direction].y;
}
// Search forwards for more station
x = stationX1;
y = stationY1;
do {
x += TileDirectionDelta[direction].x;
y += TileDirectionDelta[direction].y;
stationElement = find_station_element(x, y, z, direction, rideIndex);
if (stationElement != NULL) {
if (stationElement->properties.track.type == TRACK_ELEM_END_STATION) {
if (flags & GAME_COMMAND_FLAG_APPLY) {
ride_remove_station(ride, x, y, z);
}
}
stationX1 = x;
stationY1 = y;
stationLength++;
}
} while (stationElement != NULL);
if (!(flags & GAME_COMMAND_FLAG_APPLY)) {
if ((removeX != stationX0 || removeY != stationY0) &&
(removeX != stationX1 || removeY != stationY1) &&
ride->num_stations >= 4
) {
gGameCommandErrorText = STR_NO_MORE_STATIONS_ALLOWED_ON_THIS_RIDE;
return false;
} else {
return true;
}
}
x = stationX1;
y = stationY1;
bool finaliseStationDone;
do {
finaliseStationDone = true;
if (x != removeX || y != removeY) {
stationElement = find_station_element(x, y, z, direction, rideIndex);
if (stationElement != NULL) {
int targetTrackType;
if (x == stationX1 && y == stationY1) {
loc_6C4BF5:;
int stationIndex = -1;
for (int i = 0; i < 4; i++) {
if (ride->station_starts[i] == 0xFFFF) {
stationIndex = i;
break;
}
}
assert(stationIndex != -1);
uint16 xy = (x >> 5) | ((y >> 5) << 8);
ride->station_starts[stationIndex] = xy;
ride->station_heights[stationIndex] = z;
ride->station_depart[stationIndex] = 1;
ride->station_length[stationIndex] = stationLength != 0 ? stationLength : byte_F441D1;
ride->num_stations++;
stationLength = 0;
targetTrackType = TRACK_ELEM_END_STATION;
} else {
if (x + TileDirectionDelta[direction].x == removeX &&
y + TileDirectionDelta[direction].y == removeY
) {
goto loc_6C4BF5;
} else {
if (x - TileDirectionDelta[direction].x == removeX &&
y - TileDirectionDelta[direction].y == removeY
) {
targetTrackType = TRACK_ELEM_BEGIN_STATION;
} else {
if (x == stationX0 && y == stationY0) {
targetTrackType = TRACK_ELEM_BEGIN_STATION;
} else {
targetTrackType = TRACK_ELEM_MIDDLE_STATION;
}
}
}
}
stationElement->properties.track.type = targetTrackType;
map_invalidate_element(x, y, stationElement);
}
}
if (x != stationX0 || y != stationY0) {
x -= TileDirectionDelta[direction].x;
y -= TileDirectionDelta[direction].y;
finaliseStationDone = false;
}
} while (!finaliseStationDone);
return true;
}
static money32 track_place(int rideIndex, int type, int originX, int originY, int originZ, int direction, int properties_1, int properties_2, int properties_3, int edx_flags, int flags)
{
rct_ride *ride = get_ride(rideIndex);
if (ride == NULL)
{
log_warning("Invalid ride for track placement, rideIndex = %d", rideIndex);
return MONEY32_UNDEFINED;
}
rct_ride_entry *rideEntry = get_ride_entry(ride->subtype);
if (rideEntry == (rct_ride_entry *)0xFFFFFFFF || rideEntry == NULL)
{
log_warning("Invalid ride type for track placement, rideIndex = %d", rideIndex);
return MONEY32_UNDEFINED;
}
rct_map_element *mapElement;
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
gCommandPosition.x = originX + 16;
gCommandPosition.y = originY + 16;
gCommandPosition.z = originZ;
sint16 trackpieceZ = originZ;
direction &= 3;
RCT2_GLOBAL(0x00F441D5, uint32) = properties_1;
RCT2_GLOBAL(0x00F441D9, uint32) = properties_2;
RCT2_GLOBAL(0x00F441DD, uint32) = properties_3;
gTrackGroundFlags = 0;
uint64 enabledTrackPieces = 0;
enabledTrackPieces |= rideEntry->enabledTrackPiecesB & gResearchedTrackTypesB[ride->type];
enabledTrackPieces <<= 32;
enabledTrackPieces |= rideEntry->enabledTrackPiecesA & gResearchedTrackTypesA[ride->type];
uint32 rideTypeFlags = RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + (ride->type * 8), uint32);
RCT2_GLOBAL(0x00F44068, uint32) = rideTypeFlags;
if ((ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) && type == 1) {
gGameCommandErrorText = STR_NOT_ALLOWED_TO_MODIFY_STATION;
return MONEY32_UNDEFINED;
}
if (!sub_68B044()) {
return MONEY32_UNDEFINED;
}
if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED)) {
if (game_is_paused() && !gCheatsBuildInPauseMode) {
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
}
if (rideTypeFlags & RIDE_TYPE_FLAG_FLAT_RIDE) {
RCT2_GLOBAL(0x00F44054, uint8*) = &RCT2_ADDRESS(0x0099AA94, uint8)[type * 16];
} else {
if (type == TRACK_ELEM_ON_RIDE_PHOTO) {
if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) {
gGameCommandErrorText = STR_ONLY_ONE_ON_RIDE_PHOTO_PER_RIDE;
return MONEY32_UNDEFINED;
}
} else if (type == TRACK_ELEM_CABLE_LIFT_HILL) {
if (ride->lifecycle_flags & RIDE_LIFECYCLE_16) {
gGameCommandErrorText = STR_ONLY_ONE_CABLE_LIFT_HILL_PER_RIDE;
return MONEY32_UNDEFINED;
}
}
if ((edx_flags & (1 << 0)) && !(enabledTrackPieces & (1ULL << TRACK_LIFT_HILL_STEEP))) {
if (RCT2_ADDRESS(0x0099423C, uint16)[type] & 0x400) {
gGameCommandErrorText = STR_TOO_STEEP_FOR_LIFT_HILL;
return MONEY32_UNDEFINED;
}
}
RCT2_GLOBAL(0x00F44054, uint8*) = &RCT2_ADDRESS(0x00999A94, uint8)[type * 16];
}
money32 cost = 0;
const rct_preview_track *trackBlock = get_track_def_from_ride(ride, type);
// First check if any of the track pieces are outside the park
for (; trackBlock->index != 0xFF; trackBlock++) {
int x, y, z, offsetX, offsetY;
switch (direction) {
case 0:
offsetX = trackBlock->x;
offsetY = trackBlock->y;
break;
case 1:
offsetX = trackBlock->y;
offsetY = -trackBlock->x;
break;
case 2:
offsetX = -trackBlock->x;
offsetY = -trackBlock->y;
break;
case 3:
offsetX = -trackBlock->y;
offsetY = trackBlock->x;
break;
}
x = originX + offsetX;
y = originY + offsetY;
z = originZ + trackBlock->z;
if (!map_is_location_owned(x, y, z) && !gCheatsSandboxMode) {
gGameCommandErrorText = STR_LAND_NOT_OWNED_BY_PARK;
return MONEY32_UNDEFINED;
}
}
uint16 *trackFlags = (rideTypeFlags & RIDE_TYPE_FLAG_FLAT_RIDE) ?
RCT2_ADDRESS(0x0099443C, uint16) :
RCT2_ADDRESS(0x0099423C, uint16);
if (trackFlags[type] & 0x100) {
if ((originZ & 0x0F) != 8) {
gGameCommandErrorText = 954;
return MONEY32_UNDEFINED;
}
} else {
if ((originZ & 0x0F) != 0) {
gGameCommandErrorText = 954;
return MONEY32_UNDEFINED;
}
}
// If that is not the case, then perform the remaining checks
trackBlock = get_track_def_from_ride(ride, type);
for (; trackBlock->index != 0xFF; trackBlock++, RCT2_GLOBAL(0x00F44054, uint8*)++) {
int x, y, z, offsetX, offsetY;
int bl = trackBlock->var_08;
int bh;
switch (direction) {
case 0:
offsetX = trackBlock->x;
offsetY = trackBlock->y;
break;
case 1:
offsetX = trackBlock->y;
offsetY = -trackBlock->x;
bl = rol8(bl, 1);
bh = bl;
bh = ror8(bh, 4);
bl &= 0xEE;
bh &= 0x11;
bl |= bh;
break;
case 2:
offsetX = -trackBlock->x;
offsetY = -trackBlock->y;
bl = rol8(bl, 2);
bh = bl;
bh = ror8(bh, 4);
bl &= 0xCC;
bh &= 0x33;
bl |= bh;
break;
case 3:
offsetX = -trackBlock->y;
offsetY = trackBlock->x;
bl = rol8(bl, 3);
bh = bl;
bh = ror8(bh, 4);
bl &= 0x88;
bh &= 0x77;
bl |= bh;
break;
}
x = originX + offsetX;
y = originY + offsetY;
z = originZ + trackBlock->z;
trackpieceZ = z;
if (z < 16) {
gGameCommandErrorText = STR_TOO_LOW;
return MONEY32_UNDEFINED;
}
int baseZ = (originZ + trackBlock->z) / 8;
int clearanceZ = trackBlock->var_07;
if (trackBlock->var_09 & (1 << 2) && RideData5[ride->type].clearance_height > 24) {
clearanceZ += 24;
}
else{
clearanceZ += RideData5[ride->type].clearance_height;
}
clearanceZ = (clearanceZ / 8) + baseZ;
if (clearanceZ >= 255) {
gGameCommandErrorText = STR_TOO_HIGH;
return MONEY32_UNDEFINED;
}
_currentTrackEndX = x;
_currentTrackEndY = y;
if (!gCheatsDisableClearanceChecks || flags & GAME_COMMAND_FLAG_GHOST){
if (!map_can_construct_with_clear_at(x, y, baseZ, clearanceZ, &map_place_non_scenery_clear_func, bl, flags, &cost))
return MONEY32_UNDEFINED;
}
//6c53dc
if ((flags & GAME_COMMAND_FLAG_APPLY) && !(flags & GAME_COMMAND_FLAG_GHOST)) {
footpath_remove_litter(x, y, z);
if (rideTypeFlags & RIDE_TYPE_FLAG_18) {
map_remove_walls_at(x, y, baseZ * 8, clearanceZ * 8);
} else {
// Remove walls in the directions this track intersects
uint8 intersectingDirections = *RCT2_GLOBAL(0x00F44054, uint8*);
intersectingDirections ^= 0x0F;
for (int i = 0; i < 4; i++) {
if (intersectingDirections & (1 << i)) {
map_remove_intersecting_walls(x, y, baseZ, clearanceZ, i);
}
}
}
}
bh = gMapGroundFlags & (ELEMENT_IS_1 | ELEMENT_IS_UNDERGROUND);
if (gTrackGroundFlags != 0 && (gTrackGroundFlags & bh) == 0) {
gGameCommandErrorText = STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND;
return MONEY32_UNDEFINED;
}
gTrackGroundFlags = bh;
if (rideTypeFlags & RIDE_TYPE_FLAG_FLAT_RIDE) {
if (RCT2_ADDRESS(0x0099443C, uint16)[type] & 0x200) {
if (gTrackGroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) {
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND;
return MONEY32_UNDEFINED;
}
}
}
else {
if (RCT2_ADDRESS(0x0099423C, uint16)[type] & 0x200) {
if (gTrackGroundFlags & TRACK_ELEMENT_LOCATION_IS_UNDERGROUND) {
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND;
return MONEY32_UNDEFINED;
}
}
}
if (rideTypeFlags & RIDE_TYPE_FLAG_FLAT_RIDE) {
if (RCT2_ADDRESS(0x0099443C, uint16)[type] & 1) {
if (!(gMapGroundFlags & ELEMENT_IS_UNDERWATER)) {
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_UNDERWATER;
return MONEY32_UNDEFINED;
}
}
}
else {
if (RCT2_ADDRESS(0x0099423C, uint16)[type] & 1) {
if (gMapGroundFlags & ELEMENT_IS_UNDERWATER) {
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_UNDERWATER;
return MONEY32_UNDEFINED;
}
}
}
if (gMapGroundFlags & ELEMENT_IS_UNDERWATER) {
gGameCommandErrorText = STR_RIDE_CANT_BUILD_THIS_UNDERWATER;
return MONEY32_UNDEFINED;
}
if ((rideTypeFlags & RIDE_TYPE_FLAG_6) && !(byte_9D8150 & 1)) {
mapElement = map_get_surface_element_at(x / 32, y / 32);
uint8 water_height = 2 * (mapElement->properties.surface.terrain & MAP_ELEMENT_WATER_HEIGHT_MASK);
if (water_height == 0) {
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ON_WATER;
return MONEY32_UNDEFINED;
}
if (water_height != baseZ) {
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ON_WATER;
return MONEY32_UNDEFINED;
}
water_height -= 2;
if (water_height == mapElement->base_height) {
bh = mapElement->properties.surface.slope & 0x0F;
if (bh == 7 || bh == 11 || bh == 13 || bh == 14) {
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ON_WATER;
return MONEY32_UNDEFINED;
}
}
}
int entranceDirections;
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) {
entranceDirections = RCT2_ADDRESS(0x0099CA64, uint8)[type * 16];
}
else {
entranceDirections = RCT2_ADDRESS(0x0099BA64, uint8)[type * 16];
}
if ((entranceDirections & 0x10) && trackBlock->index == 0) {
if (!track_add_station_element(x, y, baseZ, direction, rideIndex, 0)) {
return MONEY32_UNDEFINED;
}
}
//6c55be
if (entranceDirections & 0x20) {
entranceDirections &= 0x0F;
if (entranceDirections != 0) {
if (!(flags & GAME_COMMAND_FLAG_APPLY) && !(flags & GAME_COMMAND_FLAG_GHOST)) {
uint8 _bl = entranceDirections;
for (int dl = bitscanforward(_bl); dl != -1; dl = bitscanforward(_bl)){
_bl &= ~(1 << dl);
int temp_x = x, temp_y = y;
int temp_direction = (direction + dl) & 3;
temp_x += TileDirectionDelta[temp_direction].x;
temp_y += TileDirectionDelta[temp_direction].y;
temp_direction ^= (1 << 1);
map_remove_intersecting_walls(temp_x, temp_y, baseZ, clearanceZ, temp_direction & 3);
}
}
}
}
//6c5648 12 push
mapElement = map_get_surface_element_at(x / 32, y / 32);
if (!gCheatsDisableSupportLimits){
int ride_height = clearanceZ - mapElement->base_height;
if (ride_height >= 0) {
int maxHeight = rideEntry->max_height;
if (maxHeight == 0) {
maxHeight = RideData5[ride->type].max_height;
}
ride_height /= 2;
if (ride_height > maxHeight && !(byte_9D8150 & 1)) {
gGameCommandErrorText = STR_TOO_HIGH_FOR_SUPPORTS;
return MONEY32_UNDEFINED;
}
}
}
int support_height = baseZ - mapElement->base_height;
if (support_height < 0) {
support_height = 10;
}
cost += ((support_height / 2) * RideTrackCosts[ride->type].support_price) * 5;
//6c56d3
if (!(flags & GAME_COMMAND_FLAG_APPLY))
continue;
invalidate_test_results(rideIndex);
switch (type){
case TRACK_ELEM_ON_RIDE_PHOTO:
ride->lifecycle_flags |= RIDE_LIFECYCLE_ON_RIDE_PHOTO;
break;
case TRACK_ELEM_CABLE_LIFT_HILL:
if (trackBlock->index != 0)
break;
ride->lifecycle_flags |= RIDE_LIFECYCLE_16;
ride->cable_lift_x = x;
ride->cable_lift_y = y;
ride->cable_lift_z = baseZ;
break;
case 0xD8:
ride->num_block_brakes++;
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_OPERATING;
ride->mode = RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED;
if (ride->type == RIDE_TYPE_LIM_LAUNCHED_ROLLER_COASTER)
ride->mode = RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED;
break;
}
if (trackBlock->index == 0){
switch (type){
case TRACK_ELEM_25_DEG_UP_TO_FLAT:
case TRACK_ELEM_60_DEG_UP_TO_FLAT:
case TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT:
case TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT:
if (!(edx_flags & 1))
break;
//Fall Through
case TRACK_ELEM_CABLE_LIFT_HILL:
ride->num_block_brakes++;
break;
}
}
entranceDirections = 0;
if (ride->overall_view != 0xFFFF){
if (!(flags & GAME_COMMAND_FLAG_5)){
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) {
entranceDirections = RCT2_ADDRESS(0x0099CA64, uint8)[type * 16];
}
else {
entranceDirections = RCT2_ADDRESS(0x0099BA64, uint8)[type * 16];
}
}
}
if (entranceDirections & (1 << 4) || ride->overall_view == 0xFFFF){
ride->overall_view = (x >> 5) | (y << 3);
}
mapElement = map_element_insert(x / 32, y / 32, baseZ, bl & 0xF);
assert(mapElement != NULL);
mapElement->clearance_height = clearanceZ;
uint8 map_type = direction;
map_type |= MAP_ELEMENT_TYPE_TRACK;
if (edx_flags & 1){
map_type |= (1 << 7);
}
mapElement->type = map_type;
mapElement->properties.track.sequence = trackBlock->index;
mapElement->properties.track.ride_index = rideIndex;
mapElement->properties.track.type = type;
mapElement->properties.track.colour = 0;
if (flags & GAME_COMMAND_FLAG_GHOST){
mapElement->flags |= MAP_ELEMENT_FLAG_GHOST;
}
switch (type) {
case TRACK_ELEM_WATERFALL:
map_animation_create(MAP_ANIMATION_TYPE_TRACK_WATERFALL, x, y, mapElement->base_height);
break;
case TRACK_ELEM_RAPIDS:
map_animation_create(MAP_ANIMATION_TYPE_TRACK_RAPIDS, x, y, mapElement->base_height);
break;
case TRACK_ELEM_WHIRLPOOL:
map_animation_create(MAP_ANIMATION_TYPE_TRACK_WHIRLPOOL, x, y, mapElement->base_height);
break;
case TRACK_ELEM_SPINNING_TUNNEL:
map_animation_create(MAP_ANIMATION_TYPE_TRACK_SPINNINGTUNNEL, x, y, mapElement->base_height);
break;
}
if (type == TRACK_ELEM_BRAKES) {
mapElement->properties.track.sequence = (properties_1 >> 1) << 4;
}
else {
mapElement->properties.track.colour = properties_3 << 4;
}
uint8 colour = properties_2;
if (edx_flags & (1 << 1)){
colour |= (1 << 2);
}
mapElement->properties.track.colour |= colour;
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) {
entranceDirections = RCT2_ADDRESS(0x0099CA64, uint8)[type * 16];
}
else {
entranceDirections = RCT2_ADDRESS(0x0099BA64, uint8)[type * 16];
}
if (entranceDirections & (1 << 4)) {
if (trackBlock->index == 0) {
track_add_station_element(x, y, baseZ, direction, rideIndex, GAME_COMMAND_FLAG_APPLY);
}
sub_6CB945(rideIndex);
ride_update_max_vehicles(rideIndex);
}
if (rideTypeFlags & RIDE_TYPE_FLAG_6){
rct_map_element* surfaceElement = map_get_surface_element_at(x / 32, y / 32);
surfaceElement->type |= (1 << 6);
mapElement = surfaceElement;
}
if (!gCheatsDisableClearanceChecks || !(flags & (1 << 6))) {
footpath_connect_edges(x, y, mapElement, flags);
}
map_invalidate_tile_full(x, y);
}
if (gGameCommandNestLevel == 1) {
rct_xyz16 coord;
coord.x = originX + 16;
coord.y = originY + 16;
coord.z = trackpieceZ;
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
}
money32 price = RideTrackCosts[ride->type].track_price;
price *= (rideTypeFlags & RIDE_TYPE_FLAG_FLAT_RIDE) ?
RCT2_ADDRESS(0x0099DE34, money32)[type] :
RCT2_ADDRESS(0x0099DA34, money32)[type];
price >>= 16;
price = cost + ((price / 2) * 10);
if (gParkFlags & PARK_FLAGS_NO_MONEY) {
return 0;
}
else {
return price;
}
}
/**
*
* rct2: 0x006C511D
*/
void game_command_place_track(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp)
{
*ebx = track_place(
*edx & 0xFF,
(*edx >> 8) & 0xFF,
(sint16)(*eax & 0xFFFF),
(sint16)(*ecx & 0xFFFF),
(sint16)(*edi & 0xFFFF),
(*ebx >> 8) & 0xFF,
(*edi >> 16) & 0xFF,
(*edi >> 24) & 0x0F,
(*edi >> 28) & 0x0F,
(*edx >> 16),
*ebx & 0xFF
);
}
money32 track_remove(uint8 type, uint8 sequence, sint16 originX, sint16 originY, sint16 originZ, uint8 rotation, uint8 flags){
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
gCommandPosition.x = originX + 16;
gCommandPosition.y = originY + 16;
gCommandPosition.z = originZ;
sint16 trackpieceZ = originZ;
RCT2_GLOBAL(0x00F440E1, uint8) = sequence;
switch (type){
case TRACK_ELEM_BEGIN_STATION:
case TRACK_ELEM_MIDDLE_STATION:
type = TRACK_ELEM_END_STATION;
break;
}
if (!(flags & (1 << 3)) && game_is_paused() && !gCheatsBuildInPauseMode){
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
uint8 found = 0;
rct_map_element* mapElement = map_get_first_element_at(originX / 32, originY / 32);
if (mapElement == NULL)
{
log_warning("Invalid coordinates for track removal. x = %d, y = %d", originX, originY);
return MONEY32_UNDEFINED;
}
do{
if (mapElement->base_height * 8 != originZ)
continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK)
continue;
if ((mapElement->type & MAP_ELEMENT_DIRECTION_MASK) != rotation)
continue;
if ((mapElement->properties.track.sequence & 0xF) != sequence)
continue;
// Probably should add a check for ghost here as well!
uint8 track_type = mapElement->properties.track.type;
switch (track_type){
case TRACK_ELEM_BEGIN_STATION:
case TRACK_ELEM_MIDDLE_STATION:
track_type = TRACK_ELEM_END_STATION;
break;
}
if (track_type != type)
continue;
found = 1;
break;
} while (!map_element_is_last_for_tile(mapElement++));
if (!found){
return MONEY32_UNDEFINED;
}
if (mapElement->flags & (1 << 6)){
gGameCommandErrorText = STR_YOU_ARE_NOT_ALLOWED_TO_REMOVE_THIS_SECTION;
return MONEY32_UNDEFINED;
}
uint8 rideIndex = mapElement->properties.track.ride_index;
type = mapElement->properties.track.type;
RCT2_GLOBAL(0x00F44139, uint8) = type;
RCT2_GLOBAL(0x00F44138, uint8) = rideIndex;
RCT2_GLOBAL(0x00F4414C, uint8) = mapElement->type;
rct_ride* ride = get_ride(rideIndex);
const rct_preview_track* trackBlock = get_track_def_from_ride(ride, type);
trackBlock += mapElement->properties.track.sequence & 0xF;
uint8 originDirection = mapElement->type & MAP_ELEMENT_DIRECTION_MASK;
switch (originDirection){
case 0:
originX -= trackBlock->x;
originY -= trackBlock->y;
break;
case 1:
originX -= trackBlock->y;
originY += trackBlock->x;
break;
case 2:
originX += trackBlock->x;
originY += trackBlock->y;
break;
case 3:
originX += trackBlock->y;
originY -= trackBlock->x;
break;
}
originZ -= trackBlock->z;
money32 cost = 0;
trackBlock = get_track_def_from_ride(ride, type);
for (; trackBlock->index != 255; trackBlock++){
sint16 x = originX, y = originY, z = originZ;
switch (originDirection){
case 0:
x += trackBlock->x;
y += trackBlock->y;
break;
case 1:
x += trackBlock->y;
y -= trackBlock->x;
break;
case 2:
x -= trackBlock->x;
y -= trackBlock->y;
break;
case 3:
x -= trackBlock->y;
y += trackBlock->x;
break;
}
z += trackBlock->z;
map_invalidate_tile_full(x, y);
RCT2_GLOBAL(0x00F441C4, sint16) = x;
RCT2_GLOBAL(0x00F441C6, sint16) = y;
trackpieceZ = z;
found = 0;
mapElement = map_get_first_element_at(x / 32, y / 32);
do{
if (mapElement->base_height != z / 8)
continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK)
continue;
if ((mapElement->type & MAP_ELEMENT_DIRECTION_MASK) != rotation)
continue;
if ((mapElement->properties.track.sequence & 0xF) != trackBlock->index)
continue;
if (mapElement->properties.track.type != type)
continue;
found = 1;
break;
} while (!map_element_is_last_for_tile(mapElement++));
if (!found){
log_error("Track map element part not found!");
return MONEY32_UNDEFINED;
}
int entranceDirections;
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) {
entranceDirections = RCT2_ADDRESS(0x0099CA64, uint8)[type * 16];
}
else {
entranceDirections = RCT2_ADDRESS(0x0099BA64, uint8)[type * 16];
}
if (entranceDirections & (1 << 4) && ((mapElement->properties.track.sequence & 0xF) == 0)){
if (!track_remove_station_element(x, y, z / 8, rotation, rideIndex, 0)) {
return MONEY32_UNDEFINED;
}
}
rct_map_element* surfaceElement = map_get_surface_element_at(x / 32, y / 32);
if (surfaceElement == NULL){
return MONEY32_UNDEFINED;
}
sint8 support_height = mapElement->base_height - surfaceElement->base_height;
if (support_height < 0){
support_height = 10;
}
cost += (support_height / 2) * RideTrackCosts[ride->type].support_price;
if (!(flags & GAME_COMMAND_FLAG_APPLY))
continue;
if (entranceDirections & (1 << 4) && ((mapElement->properties.track.sequence & 0xF) == 0)){
if (!track_remove_station_element(x, y, z / 8, rotation, rideIndex, GAME_COMMAND_FLAG_APPLY)) {
return MONEY32_UNDEFINED;
}
}
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_6)){
surfaceElement->type &= ~(1 << 6);
}
invalidate_test_results(rideIndex);
footpath_queue_chain_reset();
if (!gCheatsDisableClearanceChecks || !(mapElement->flags & MAP_ELEMENT_FLAG_GHOST)) {
footpath_remove_edges_at(x, y, mapElement);
}
map_element_remove(mapElement);
sub_6CB945(rideIndex);
if (!(flags & (1 << 6))){
ride_update_max_vehicles(rideIndex);
}
}
if (flags & GAME_COMMAND_FLAG_APPLY){
switch (type){
case TRACK_ELEM_ON_RIDE_PHOTO:
ride->lifecycle_flags &= ~RIDE_LIFECYCLE_ON_RIDE_PHOTO;
break;
case TRACK_ELEM_CABLE_LIFT_HILL:
ride->lifecycle_flags &= ~RIDE_LIFECYCLE_16;
break;
case 216:
ride->num_block_brakes--;
if (ride->num_block_brakes == 0){
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_OPERATING;
ride->mode = RIDE_MODE_CONTINUOUS_CIRCUIT;
if (ride->type == RIDE_TYPE_LIM_LAUNCHED_ROLLER_COASTER){
ride->mode = RIDE_MODE_POWERED_LAUNCH;
}
}
break;
}
switch (type){
case TRACK_ELEM_25_DEG_UP_TO_FLAT:
case TRACK_ELEM_60_DEG_UP_TO_FLAT:
case TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT:
case TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT:
if (!(RCT2_GLOBAL(0x00F4414C, uint8) & (1 << 7)))
break;
// Fall through
case TRACK_ELEM_CABLE_LIFT_HILL:
ride->num_block_brakes--;
break;
}
}
money32 price = RideTrackCosts[ride->type].track_price;
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_FLAT_RIDE)) {
price *= RCT2_ADDRESS(0x0099DE34, money32)[type];
}
else {
price *= RCT2_ADDRESS(0x0099DA34, money32)[type];
}
price >>= 16;
price = (price + cost) / 2;
if (ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED)
price *= -7;
else
price *= -10;
if (gGameCommandNestLevel == 1) {
rct_xyz16 coord;
coord.x = originX + 16;
coord.y = originY + 16;
coord.z = trackpieceZ;
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
}
if (gParkFlags & PARK_FLAGS_NO_MONEY)
return 0;
else
return price;
}
/**
*
* rct2: 0x006C5B69
*/
void game_command_remove_track(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp)
{
*ebx = track_remove(
*edx & 0xFF,
(*edx >> 8) & 0xFF,
*eax & 0xFFFF,
*ecx & 0xFFFF,
*edi & 0xFFFF,
(*ebx >> 8) & 0xFF,
*ebx & 0xFF
);
return;
}
uint8 maze_element_get_segment_bit(uint16 x, uint16 y) {
uint8 minorX = x & 0x1F; // 0 or 16
uint8 minorY = y & 0x1F; // 0 or 16
if (minorX == 0 && minorY == 0) {
return 3;
}
if (minorY == 16 && minorX == 16) {
return 11;
}
if (minorY == 0) {
return 15;
}
return 7;
}
money32 set_maze_track(uint16 x, uint8 flags, uint8 direction, uint16 y, uint8 rideIndex, uint8 mode, uint16 z) {
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
gCommandPosition.x = x + 8;
gCommandPosition.y = y + 8;
gCommandPosition.z = z;
RCT2_GLOBAL(0xF4413E, money32) = 0;
if (!sub_68B044()) {
return MONEY32_UNDEFINED;
}
if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) && !gCheatsBuildInPauseMode && game_is_paused()) {
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
if ((z & 0xF) != 0) {
// Can't construct this here…
gGameCommandErrorText = 954;
return MONEY32_UNDEFINED;
}
if ((flags & GAME_COMMAND_FLAG_APPLY) != 0) {
if (!(flags & GAME_COMMAND_FLAG_GHOST) && !(flags & GAME_COMMAND_FLAG_2)) {
footpath_remove_litter(x, y, z);
map_remove_walls_at(floor2(x, 32), floor2(y, 32), z, z + 32);
}
}
if (!map_is_location_owned(floor2(x, 32), floor2(y, 32), z) && !gCheatsSandboxMode) {
return MONEY32_UNDEFINED;
}
rct_map_element *mapElement = map_get_surface_element_at(x / 32, y / 32);
if (mapElement == NULL) {
return MONEY32_UNDEFINED;
}
uint8 baseHeight = z >> 3;
uint8 clearanceHeight = (z + 32) >> 3;
sint8 heightDifference = baseHeight - mapElement->base_height;
if (heightDifference >= 0 && !gCheatsDisableSupportLimits) {
heightDifference = heightDifference >> 1;
if (heightDifference > RideData5[RIDE_TYPE_MAZE].max_height) {
gGameCommandErrorText = STR_TOO_HIGH_FOR_SUPPORTS;
return MONEY32_UNDEFINED;
}
}
mapElement = map_get_track_element_at_of_type_from_ride(x, y, baseHeight, 0x65, rideIndex);
if (mapElement == NULL) {
if (mode != 0) {
gGameCommandErrorText = 0;
return MONEY32_UNDEFINED;
}
if (!map_can_construct_at(floor2(x, 32), floor2(y, 32), baseHeight, clearanceHeight, 0x0F)) {
return MONEY32_UNDEFINED;
}
if (gMapGroundFlags & ELEMENT_IS_UNDERWATER) {
gGameCommandErrorText = STR_RIDE_CANT_BUILD_THIS_UNDERWATER;
return MONEY32_UNDEFINED;
}
if (gMapGroundFlags & ELEMENT_IS_UNDERGROUND) {
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND;
return MONEY32_UNDEFINED;
}
rct_ride *ride = get_ride(rideIndex);
money32 price = (((RideTrackCosts[ride->type].track_price * RCT2_GLOBAL(0x0099DBC8, money32)) >> 16));
RCT2_GLOBAL(0x00F4413E, money32) = price / 2 * 10;
if (!(flags & GAME_COMMAND_FLAG_APPLY)) {
if (gParkFlags & PARK_FLAGS_NO_MONEY) {
return 0;
}
return RCT2_GLOBAL(0xF4413E, money32);
}
uint16 flooredX = floor2(x, 32);
uint16 flooredY = floor2(y, 32);
mapElement = map_element_insert(x / 32, y / 32, baseHeight, 0xF);
assert(mapElement != NULL);
mapElement->clearance_height = clearanceHeight;
mapElement->type = MAP_ELEMENT_TYPE_TRACK;
mapElement->properties.track.type = 0x65;
mapElement->properties.track.ride_index = rideIndex;
mapElement->properties.track.maze_entry = 0xFFFF;
if (flags & GAME_COMMAND_FLAG_GHOST) {
mapElement->flags |= MAP_ELEMENT_FLAG_GHOST;
}
map_invalidate_tile_full(flooredX, flooredY);
ride->maze_tiles++;
ride->station_heights[0] = mapElement->base_height;
ride->station_starts[0] = 0;
if (direction == 4) {
if (!(flags & GAME_COMMAND_FLAG_GHOST)) {
ride->overall_view = (flooredX >> 5) | (flooredY << 3);
}
}
}
if (!(flags & GAME_COMMAND_FLAG_APPLY)) {
if (gParkFlags & PARK_FLAGS_NO_MONEY) {
return 0;
}
return RCT2_GLOBAL(0xF4413E, money32);
}
if (mode == 0) {
// Build mode
uint8 segmentOffset = maze_element_get_segment_bit(x, y);
mapElement->properties.track.maze_entry &= ~(1 << segmentOffset);
if (direction != 4) {
segmentOffset = RCT2_GLOBAL(0x993ce9 + (direction + segmentOffset), uint8);
mapElement->properties.track.maze_entry &= ~(1 << segmentOffset);
uint8 temp_edx = RCT2_GLOBAL(0x993cfc + segmentOffset, uint8);
if (temp_edx != 0xFF) {
uint16 previousElementX = floor2(x, 32) - TileDirectionDelta[direction].x;
uint16 previousElementY = floor2(y, 32) - TileDirectionDelta[direction].y;
rct_map_element *previousMapElement = map_get_track_element_at_of_type_from_ride(previousElementX, previousElementY, baseHeight, 0x65, rideIndex);
if (previousMapElement != NULL) {
previousMapElement->properties.track.maze_entry &= ~(1 << temp_edx);
} else {
mapElement->properties.track.maze_entry |= (1 << segmentOffset);
}
}
}
} else if (mode == 1) {
// Move mode
} else {
// Fill-in mode
if (direction != 4) {
uint16 previousSegmentX = x - TileDirectionDelta[direction].x / 2;
uint16 previousSegmentY = y - TileDirectionDelta[direction].y / 2;
mapElement = map_get_track_element_at_of_type_from_ride(previousSegmentX, previousSegmentY, baseHeight, 0x65, rideIndex);
map_invalidate_tile_full(floor2(previousSegmentX, 32), floor2(previousSegmentY, 32));
if (mapElement == NULL) {
log_error("No surface found");
return MONEY32_UNDEFINED;
}
uint32 segmentBit = maze_element_get_segment_bit(previousSegmentX, previousSegmentY);
mapElement->properties.track.maze_entry |= (1 << segmentBit);
segmentBit--;
mapElement->properties.track.maze_entry |= (1 << segmentBit);
segmentBit = (segmentBit - 4) & 0x0F;
mapElement->properties.track.maze_entry |= (1 << segmentBit);
segmentBit = (segmentBit + 3) & 0x0F;
do {
mapElement->properties.track.maze_entry |= (1 << segmentBit);
uint32 direction1 = RCT2_GLOBAL(0x00993D0C + segmentBit, uint8_t);
uint16 nextElementX = floor2(previousSegmentX, 32) + TileDirectionDelta[direction1].x;
uint16 nextElementY = floor2(previousSegmentY, 32) + TileDirectionDelta[direction1].y;
rct_map_element *tmp_mapElement = map_get_track_element_at_of_type_from_ride(nextElementX, nextElementY, baseHeight, 0x65, rideIndex);
if (tmp_mapElement != NULL) {
uint8 edx11 = RCT2_GLOBAL(0x993CFC + segmentBit, uint8);
tmp_mapElement->properties.track.maze_entry |= 1 << (edx11);
}
segmentBit--;
} while ((segmentBit & 0x3) != 0x3);
}
}
map_invalidate_tile(floor2(x, 32), floor2(y, 32), mapElement->base_height * 8, mapElement->clearance_height * 8);
if ((mapElement->properties.track.maze_entry & 0x8888) == 0x8888) {
map_element_remove(mapElement);
sub_6CB945(rideIndex);
get_ride(rideIndex)->maze_tiles--;
}
if (gParkFlags & PARK_FLAGS_NO_MONEY) {
return 0;
}
return RCT2_GLOBAL(0xF4413E, money32);
}
/**
*
* rct2: 0x006CD8CE
*/
void game_command_set_maze_track(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp)
{
uint16 x = (*eax & 0xFFFF); // AX
uint8 flags = (*ebx & 0xFF); // BL
uint8 direction = ((*ebx & 0xFF00) >> 8); // BH
uint16 y = (*ecx & 0xFFFF); // CX
uint8 rideIndex = (*edx & 0xFF); // DL
uint8 mode = ((*edx & 0xFF00) >> 8); // DH
uint16 z = (*edi & 0xFFFF); // DI
*ebx = set_maze_track(x, flags, direction, y, rideIndex, mode, z);
}
/**
*
* rct2: 0x006C5AE9
*/
void game_command_set_brakes_speed(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp)
{
rct_map_element *mapElement;
int x, y, z, trackType, brakesSpeed;
x = (*eax & 0xFFFF);
y = (*ecx & 0xFFFF);
z = (*edi & 0xFFFF);
trackType = (*edx & 0xFF);
brakesSpeed = ((*ebx >> 8) & 0xFF);
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
gCommandPosition.x = x + 16;
gCommandPosition.y = y + 16;
gCommandPosition.z = z;
if (*ebx & GAME_COMMAND_FLAG_APPLY) {
*ebx = 0;
return;
}
mapElement = map_get_first_element_at(x >> 5, y >> 5);
if (mapElement == NULL)
{
log_warning("Invalid game command for setting brakes speed. x = %d, y = %d", x, y);
*ebx = MONEY32_UNDEFINED;
return;
}
do {
if (mapElement->base_height * 8 != z)
continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK)
continue;
if (mapElement->properties.track.type != trackType)
continue;
mapElement->properties.track.sequence =
(mapElement->properties.track.sequence & 0x0F) |
((brakesSpeed >> 1) << 4);
break;
} while (!map_element_is_last_for_tile(mapElement++));
*ebx = 0;
}
void track_circuit_iterator_begin(track_circuit_iterator *it, rct_xy_element first)
{
it->last = first;
it->first = NULL;
it->firstIteration = true;
it->looped = false;
}
bool track_circuit_iterator_previous(track_circuit_iterator *it)
{
track_begin_end trackBeginEnd;
if (it->first == NULL) {
if (!track_block_get_previous(it->last.x, it->last.y, it->last.element, &trackBeginEnd))
return false;
it->current.x = trackBeginEnd.begin_x;
it->current.y = trackBeginEnd.begin_y;
it->current.element = trackBeginEnd.begin_element;
it->currentZ = trackBeginEnd.begin_z;
it->currentDirection = trackBeginEnd.begin_direction;
it->first = it->current.element;
return true;
} else {
if (!it->firstIteration && it->first == it->current.element) {
it->looped = true;
return false;
}
it->firstIteration = false;
it->last = it->current;
if (track_block_get_previous(it->last.x, it->last.y, it->last.element, &trackBeginEnd)) {
it->current.x = trackBeginEnd.end_x;
it->current.y = trackBeginEnd.end_y;
it->current.element = trackBeginEnd.begin_element;
it->currentZ = trackBeginEnd.begin_z;
it->currentDirection = trackBeginEnd.begin_direction;
return true;
} else {
return false;
}
}
}
bool track_circuit_iterator_next(track_circuit_iterator *it)
{
if (it->first == NULL) {
if (!track_block_get_next(&it->last, &it->current, &it->currentZ, &it->currentDirection))
return false;
it->first = it->current.element;
return true;
} else {
if (!it->firstIteration && it->first == it->current.element) {
it->looped = true;
return false;
}
it->firstIteration = false;
it->last = it->current;
return track_block_get_next(&it->last, &it->current, &it->currentZ, &it->currentDirection);
}
}
void track_get_back(rct_xy_element *input, rct_xy_element *output)
{
rct_xy_element lastTrack;
track_begin_end currentTrack;
bool result;
lastTrack = *input;
do {
result = track_block_get_previous(lastTrack.x, lastTrack.y, lastTrack.element, &currentTrack);
if (result) {
lastTrack.x = currentTrack.begin_x;
lastTrack.y = currentTrack.begin_y;
lastTrack.element = currentTrack.begin_element;
}
} while (result);
*output = lastTrack;
}
void track_get_front(rct_xy_element *input, rct_xy_element *output)
{
rct_xy_element lastTrack, currentTrack;
int z, direction;
bool result;
lastTrack = *input;
do {
result = track_block_get_next(&lastTrack, &currentTrack, &z, &direction);
if (result) {
lastTrack = currentTrack;
}
} while (result);
*output = lastTrack;
}
bool track_element_is_lift_hill(rct_map_element *trackElement)
{
return trackElement->type & 0x80;
}
/**
* Checks if a track element is recognised as the beginning of a block.
* A beginning of a block can be the end of a station, the end of a lift hill,
* or a block brake.
*/
bool track_element_is_block_start(rct_map_element *trackElement)
{
switch (trackElement->properties.track.type) {
case TRACK_ELEM_END_STATION:
case TRACK_ELEM_CABLE_LIFT_HILL:
case 216:
return true;
case TRACK_ELEM_25_DEG_UP_TO_FLAT:
case TRACK_ELEM_60_DEG_UP_TO_FLAT:
case TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT:
case TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT:
if (track_element_is_lift_hill(trackElement)) {
return true;
}
break;
}
return false;
}
bool track_element_is_cable_lift(rct_map_element *trackElement) {
return trackElement->properties.track.colour & TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT;
}
void track_element_set_cable_lift(rct_map_element *trackElement) {
trackElement->properties.track.colour |= TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT;
}
void track_element_clear_cable_lift(rct_map_element *trackElement) {
trackElement->properties.track.colour &= ~TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT;
}
int track_get_actual_bank(rct_map_element *mapElement, int bank)
{
rct_ride *ride = get_ride(mapElement->properties.track.ride_index);
int trackColour = mapElement->properties.track.colour;
return track_get_actual_bank_2(ride->type, trackColour, bank);
}
int track_get_actual_bank_2(int rideType, int trackColour, int bank)
{
if (RideData4[rideType].flags & RIDE_TYPE_FLAG4_3) {
if (trackColour & 4) {
if (bank == TRACK_BANK_NONE) {
bank = TRACK_BANK_UPSIDE_DOWN;
} else if (bank == TRACK_BANK_UPSIDE_DOWN) {
bank = TRACK_BANK_NONE;
}
}
}
return bank;
}
int track_get_actual_bank_3(rct_vehicle *vehicle, rct_map_element *mapElement)
{
uint8 colourThingToXor = (vehicle->update_flags >> 9) & 0xFF;
int trackType = mapElement->properties.track.type;
int rideType = get_ride(mapElement->properties.track.ride_index)->type;
int trackColour = mapElement->properties.track.colour ^ colourThingToXor;
int bankStart = gTrackDefinitions[trackType].bank_start;
return track_get_actual_bank_2(rideType, trackColour, bankStart);
}
bool track_element_is_station(rct_map_element *trackElement)
{
switch (trackElement->properties.track.type) {
case TRACK_ELEM_END_STATION:
case TRACK_ELEM_BEGIN_STATION:
case TRACK_ELEM_MIDDLE_STATION:
return true;
default:
return false;
}
}
bool track_element_is_covered(int trackElementType)
{
switch (trackElementType) {
case TRACK_ELEM_FLAT_COVERED:
case TRACK_ELEM_25_DEG_UP_COVERED:
case TRACK_ELEM_60_DEG_UP_COVERED:
case TRACK_ELEM_FLAT_TO_25_DEG_UP_COVERED:
case TRACK_ELEM_25_DEG_UP_TO_60_DEG_UP_COVERED:
case TRACK_ELEM_60_DEG_UP_TO_25_DEG_UP_COVERED:
case TRACK_ELEM_25_DEG_UP_TO_FLAT_COVERED:
case TRACK_ELEM_25_DEG_DOWN_COVERED:
case TRACK_ELEM_60_DEG_DOWN_COVERED:
case TRACK_ELEM_FLAT_TO_25_DEG_DOWN_COVERED:
case TRACK_ELEM_25_DEG_DOWN_TO_60_DEG_DOWN_COVERED:
case TRACK_ELEM_60_DEG_DOWN_TO_25_DEG_DOWN_COVERED:
case TRACK_ELEM_25_DEG_DOWN_TO_FLAT_COVERED:
case TRACK_ELEM_LEFT_QUARTER_TURN_5_TILES_COVERED:
case TRACK_ELEM_RIGHT_QUARTER_TURN_5_TILES_COVERED:
case TRACK_ELEM_S_BEND_LEFT_COVERED:
case TRACK_ELEM_S_BEND_RIGHT_COVERED:
case TRACK_ELEM_LEFT_QUARTER_TURN_3_TILES_COVERED:
case TRACK_ELEM_RIGHT_QUARTER_TURN_3_TILES_COVERED:
return true;
default:
return false;
}
}