#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;
	}
}
