2017-01-15 13:53:19 +01:00
|
|
|
/*****************************************************************************
|
2019-03-17 08:16:15 +01:00
|
|
|
* Copyright (c) 2014-2019 OpenRCT2 developers
|
2018-01-17 13:00:24 +01:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
2018-01-17 13:00:24 +01:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
2018-01-17 13:00:24 +01:00
|
|
|
*****************************************************************************/
|
2017-01-15 13:53:19 +01:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
#include "TileInspector.h"
|
|
|
|
|
2017-11-23 00:42:12 +01:00
|
|
|
#include "../Context.h"
|
2017-11-30 18:17:06 +01:00
|
|
|
#include "../Game.h"
|
2019-05-15 22:15:23 +02:00
|
|
|
#include "../actions/GameAction.h"
|
2019-05-18 10:23:33 +02:00
|
|
|
#include "../common.h"
|
2018-01-17 13:00:24 +01:00
|
|
|
#include "../core/Guard.hpp"
|
2018-01-06 01:05:16 +01:00
|
|
|
#include "../interface/Window.h"
|
2018-02-21 16:51:07 +01:00
|
|
|
#include "../localisation/Localisation.h"
|
2018-06-22 23:17:03 +02:00
|
|
|
#include "../ride/Station.h"
|
2017-10-17 13:51:47 +02:00
|
|
|
#include "../ride/Track.h"
|
2017-11-23 00:42:12 +01:00
|
|
|
#include "../windows/Intent.h"
|
2017-01-15 13:53:19 +01:00
|
|
|
#include "../windows/tile_inspector.h"
|
2018-06-22 23:17:03 +02:00
|
|
|
#include "Banner.h"
|
2018-01-11 10:59:26 +01:00
|
|
|
#include "Footpath.h"
|
2018-02-21 16:51:07 +01:00
|
|
|
#include "LargeScenery.h"
|
2018-01-03 14:56:08 +01:00
|
|
|
#include "Map.h"
|
2018-06-22 23:17:03 +02:00
|
|
|
#include "Park.h"
|
2018-02-21 16:51:07 +01:00
|
|
|
#include "Scenery.h"
|
2018-05-01 16:33:16 +02:00
|
|
|
#include "Surface.h"
|
2017-01-15 13:53:19 +01:00
|
|
|
|
2018-06-11 01:09:00 +02:00
|
|
|
using namespace OpenRCT2;
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t windowTileInspectorTileX;
|
|
|
|
uint32_t windowTileInspectorTileY;
|
|
|
|
int32_t windowTileInspectorElementCount = 0;
|
|
|
|
int32_t windowTileInspectorSelectedIndex;
|
2017-11-23 00:42:12 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
static bool map_swap_elements_at(CoordsXY loc, int16_t first, int16_t second)
|
2017-01-15 13:53:19 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const firstElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, first);
|
|
|
|
TileElement* const secondElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, second);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-11-20 11:37:15 +01:00
|
|
|
if (firstElement == nullptr)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
log_error("First element is out of range for the tile");
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-20 11:37:15 +01:00
|
|
|
if (secondElement == nullptr)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
log_error("Second element is out of range for the tile");
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-20 11:37:15 +01:00
|
|
|
if (firstElement == secondElement)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
log_error("Can't swap the element with itself");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Swap their memory
|
2018-11-01 13:53:50 +01:00
|
|
|
TileElement temp = *firstElement;
|
2018-06-22 23:17:03 +02:00
|
|
|
*firstElement = *secondElement;
|
|
|
|
*secondElement = temp;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Swap the 'last map element for tile' flag if either one of them was last
|
2018-05-24 11:44:53 +02:00
|
|
|
if ((firstElement)->IsLastForTile() || (secondElement)->IsLastForTile())
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2017-10-31 12:57:40 +01:00
|
|
|
firstElement->flags ^= TILE_ELEMENT_FLAG_LAST_TILE;
|
|
|
|
secondElement->flags ^= TILE_ELEMENT_FLAG_LAST_TILE;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2017-01-15 13:53:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts a corrupt element under a given element on a given tile
|
2018-06-06 22:36:04 +02:00
|
|
|
* @param x The x coordinate of the tile
|
|
|
|
* @param y The y coordinate of the tile
|
|
|
|
* @param elementIndex The nth element on this tile
|
2017-01-15 13:53:19 +01:00
|
|
|
* Returns 0 on success, MONEY_UNDEFINED otherwise.
|
|
|
|
*/
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_insert_corrupt_at(CoordsXY loc, int16_t elementIndex, bool isExecuting)
|
2017-01-15 13:53:19 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Make sure there is enough space for the new element
|
|
|
|
if (!map_check_free_elements_and_reorganise(1))
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::NO_FREE_ELEMENTS, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
// Create new corrupt element
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* corruptElement = tile_element_insert(
|
|
|
|
loc.x / 32, loc.y / 32, -1, 0); // Ugly hack: -1 guarantees this to be placed first
|
2017-11-20 11:37:15 +01:00
|
|
|
if (corruptElement == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
log_warning("Failed to insert corrupt element.");
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-09-17 15:18:07 +02:00
|
|
|
corruptElement->SetType(TILE_ELEMENT_TYPE_CORRUPT);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Set the base height to be the same as the selected element
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const selectedElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex + 1);
|
2017-11-20 11:37:15 +01:00
|
|
|
if (!selectedElement)
|
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
corruptElement->base_height = corruptElement->clearance_height = selectedElement->base_height;
|
|
|
|
|
|
|
|
// Move the corrupt element up until the selected list item is reached
|
|
|
|
// this way it's placed under the selected element, even when there are multiple elements with the same base height
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int16_t i = 0; i < elementIndex; i++)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
if (!map_swap_elements_at(loc, i, i + 1))
|
2017-11-20 11:37:15 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// don't return error here, we've already inserted an element
|
|
|
|
// and moved it as far as we could, the only sensible thing left
|
|
|
|
// to do is to invalidate the window.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Update the tile inspector's list for everyone who has the tile selected
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
windowTileInspectorElementCount++;
|
|
|
|
|
|
|
|
// Keep other elements (that are not being hidden) selected
|
2018-06-11 01:09:00 +02:00
|
|
|
if (windowTileInspectorSelectedIndex > elementIndex)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-06-11 01:09:00 +02:00
|
|
|
windowTileInspectorSelectedIndex++;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Nothing went wrong
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-15 13:53:19 +01:00
|
|
|
}
|
2017-01-23 17:08:16 +01:00
|
|
|
|
2017-01-23 17:46:36 +01:00
|
|
|
/**
|
2018-01-17 13:00:24 +01:00
|
|
|
* Forcefully removes an element for a given tile
|
2018-06-06 22:36:04 +02:00
|
|
|
* @param x The x coordinate of the tile
|
|
|
|
* @param y The y coordinate of the tile
|
|
|
|
* @param elementIndex The nth element on this tile
|
2018-01-17 13:00:24 +01:00
|
|
|
*/
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_remove_element_at(CoordsXY loc, int16_t elementIndex, bool isExecuting)
|
2017-01-23 17:08:16 +01:00
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
// Forcefully remove the element
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-11-20 11:37:15 +01:00
|
|
|
if (!tileElement)
|
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2017-10-31 14:03:45 +01:00
|
|
|
tile_element_remove(tileElement);
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Update the window
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
windowTileInspectorElementCount--;
|
|
|
|
|
2018-06-11 01:09:00 +02:00
|
|
|
if (windowTileInspectorSelectedIndex > elementIndex)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-06-11 01:09:00 +02:00
|
|
|
windowTileInspectorSelectedIndex--;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-06-11 01:09:00 +02:00
|
|
|
else if (windowTileInspectorSelectedIndex == elementIndex)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-06-11 01:09:00 +02:00
|
|
|
windowTileInspectorSelectedIndex = -1;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-23 17:08:16 +01:00
|
|
|
}
|
2017-01-23 21:04:28 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_swap_elements_at(CoordsXY loc, int16_t first, int16_t second, bool isExecuting)
|
2017-01-23 21:04:28 +01:00
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
if (!map_swap_elements_at(loc, first, second))
|
2017-11-20 11:37:15 +01:00
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Update the window
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
// If one of them was selected, update selected list item
|
2018-06-11 01:09:00 +02:00
|
|
|
if (windowTileInspectorSelectedIndex == first)
|
|
|
|
windowTileInspectorSelectedIndex = second;
|
|
|
|
else if (windowTileInspectorSelectedIndex == second)
|
|
|
|
windowTileInspectorSelectedIndex = first;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-23 21:04:28 +01:00
|
|
|
}
|
2017-01-23 23:38:02 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_rotate_element_at(CoordsXY loc, int32_t elementIndex, bool isExecuting)
|
2017-01-23 23:38:02 +01:00
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t newRotation, pathEdges, pathCorners;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-11-20 11:37:15 +01:00
|
|
|
if (!tileElement)
|
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-05-02 19:27:04 +02:00
|
|
|
switch (tileElement->GetType())
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-06-22 23:17:03 +02:00
|
|
|
case TILE_ELEMENT_TYPE_PATH:
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-10-04 15:07:48 +02:00
|
|
|
newRotation = (tileElement->AsPath()->GetSlopeDirection() + 1) & TILE_ELEMENT_DIRECTION_MASK;
|
|
|
|
tileElement->AsPath()->SetSlopeDirection(newRotation);
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
2018-10-05 13:11:44 +02:00
|
|
|
pathEdges = tileElement->AsPath()->GetEdges();
|
2018-10-05 13:33:46 +02:00
|
|
|
pathCorners = tileElement->AsPath()->GetCorners();
|
|
|
|
tileElement->AsPath()->SetEdges((pathEdges << 1) | (pathEdges >> 3));
|
|
|
|
tileElement->AsPath()->SetCorners((pathCorners << 1) | (pathCorners >> 3));
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
|
|
|
case TILE_ELEMENT_TYPE_ENTRANCE:
|
2018-03-07 19:10:50 +01:00
|
|
|
{
|
2018-06-22 23:17:03 +02:00
|
|
|
// Update element rotation
|
2018-09-14 11:14:19 +02:00
|
|
|
newRotation = tileElement->GetDirectionWithOffset(1);
|
2018-09-17 15:18:07 +02:00
|
|
|
tileElement->SetDirection(newRotation);
|
2018-06-22 23:17:03 +02:00
|
|
|
|
|
|
|
// Update ride's known entrance/exit rotation
|
2018-09-26 12:30:27 +02:00
|
|
|
Ride* ride = get_ride(tileElement->AsEntrance()->GetRideIndex());
|
2018-09-26 12:54:44 +02:00
|
|
|
uint8_t stationIndex = tileElement->AsEntrance()->GetStationIndex();
|
2018-06-22 23:17:03 +02:00
|
|
|
auto entrance = ride_get_entrance_location(ride, stationIndex);
|
|
|
|
auto exit = ride_get_exit_location(ride, stationIndex);
|
2018-09-26 12:13:44 +02:00
|
|
|
uint8_t entranceType = tileElement->AsEntrance()->GetEntranceType();
|
2018-06-22 23:17:03 +02:00
|
|
|
uint8_t z = tileElement->base_height;
|
|
|
|
|
|
|
|
// Make sure this is the correct entrance or exit
|
2019-05-18 10:23:33 +02:00
|
|
|
if (entranceType == ENTRANCE_TYPE_RIDE_ENTRANCE && entrance.x == loc.x / 32 && entrance.y == loc.y / 32
|
|
|
|
&& entrance.z == z)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
ride_set_entrance_location(ride, stationIndex, { entrance.x, entrance.y, entrance.z, newRotation });
|
|
|
|
}
|
2019-05-18 10:23:33 +02:00
|
|
|
else if (entranceType == ENTRANCE_TYPE_RIDE_EXIT && exit.x == loc.x / 32 && exit.y == loc.y / 32 && exit.z == z)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
ride_set_exit_location(ride, stationIndex, { exit.x, exit.y, exit.z, newRotation });
|
|
|
|
}
|
|
|
|
break;
|
2018-03-07 19:10:50 +01:00
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
case TILE_ELEMENT_TYPE_TRACK:
|
|
|
|
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
|
|
|
|
case TILE_ELEMENT_TYPE_WALL:
|
2018-09-14 11:14:19 +02:00
|
|
|
newRotation = tileElement->GetDirectionWithOffset(1);
|
2018-09-17 15:18:07 +02:00
|
|
|
tileElement->SetDirection(newRotation);
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
|
|
|
case TILE_ELEMENT_TYPE_BANNER:
|
2018-03-07 19:10:50 +01:00
|
|
|
{
|
2018-09-26 14:52:16 +02:00
|
|
|
uint8_t unblockedEdges = tileElement->AsBanner()->GetAllowedEdges();
|
2018-06-22 23:17:03 +02:00
|
|
|
unblockedEdges = (unblockedEdges << 1 | unblockedEdges >> 3) & 0xF;
|
2018-09-26 14:52:16 +02:00
|
|
|
tileElement->AsBanner()->SetAllowedEdges(unblockedEdges);
|
|
|
|
tileElement->AsBanner()->SetPosition((tileElement->AsBanner()->GetPosition() + 1) & 3);
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
2018-03-07 19:10:50 +01:00
|
|
|
}
|
2018-06-09 17:16:04 +02:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
if ((uint32_t)(loc.x / 32) == windowTileInspectorTileX && (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate_by_class(WC_TILE_INSPECTOR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-24 00:16:43 +01:00
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_paste_element_at(CoordsXY loc, TileElement element, bool isExecuting)
|
2017-01-24 00:16:43 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Make sure there is enough space for the new element
|
|
|
|
if (!map_check_free_elements_and_reorganise(1))
|
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::NO_FREE_ELEMENTS, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-02-21 16:51:07 +01:00
|
|
|
// Check if the element to be pasted refers to a banner index
|
2018-06-09 13:51:45 +02:00
|
|
|
BannerIndex bannerIndex = tile_element_get_banner_index(&element);
|
2018-03-10 00:57:45 +01:00
|
|
|
if (bannerIndex != BANNER_INDEX_NULL)
|
2018-02-21 16:51:07 +01:00
|
|
|
{
|
|
|
|
// The element to be pasted refers to a banner index - make a copy of it
|
2019-05-15 22:15:23 +02:00
|
|
|
BannerIndex newBannerIndex = create_new_banner(GAME_COMMAND_FLAG_APPLY);
|
2018-06-11 14:39:55 +02:00
|
|
|
if (newBannerIndex == BANNER_INDEX_NULL)
|
2018-02-21 16:51:07 +01:00
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2018-02-21 16:51:07 +01:00
|
|
|
}
|
2019-07-26 18:24:19 +02:00
|
|
|
auto& newBanner = *GetBanner(newBannerIndex);
|
|
|
|
newBanner = *GetBanner(bannerIndex);
|
2019-07-25 23:31:49 +02:00
|
|
|
newBanner.position = TileCoordsXY(loc);
|
2018-02-21 16:51:07 +01:00
|
|
|
|
|
|
|
// Use the new banner index
|
2018-03-09 19:16:43 +01:00
|
|
|
tile_element_set_banner_index(&element, newBannerIndex);
|
2018-02-21 16:51:07 +01:00
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const pastedElement = tile_element_insert(loc.x / 32, loc.y / 32, element.base_height, 0);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-05-24 11:44:53 +02:00
|
|
|
bool lastForTile = pastedElement->IsLastForTile();
|
2018-06-22 23:17:03 +02:00
|
|
|
*pastedElement = element;
|
2017-10-31 12:57:40 +01:00
|
|
|
pastedElement->flags &= ~TILE_ELEMENT_FLAG_LAST_TILE;
|
2017-06-06 23:24:18 +02:00
|
|
|
if (lastForTile)
|
|
|
|
{
|
2017-10-31 12:57:40 +01:00
|
|
|
pastedElement->flags |= TILE_ELEMENT_FLAG_LAST_TILE;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
windowTileInspectorElementCount++;
|
|
|
|
|
|
|
|
// Select new element if there was none selected already
|
2019-05-18 10:23:33 +02:00
|
|
|
int16_t newIndex = (int16_t)(pastedElement - map_get_first_element_at(loc.x / 32, loc.y / 32));
|
2018-06-11 01:09:00 +02:00
|
|
|
if (windowTileInspectorSelectedIndex == -1)
|
|
|
|
windowTileInspectorSelectedIndex = newIndex;
|
|
|
|
else if (windowTileInspectorSelectedIndex >= newIndex)
|
|
|
|
windowTileInspectorSelectedIndex++;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-23 23:38:02 +01:00
|
|
|
}
|
2017-01-25 13:42:04 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_sort_elements_at(CoordsXY loc, bool isExecuting)
|
2017-01-25 13:42:04 +01:00
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
const TileElement* const firstElement = map_get_first_element_at(loc.x / 32, loc.y / 32);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Count elements on tile
|
2018-06-22 23:17:03 +02:00
|
|
|
int32_t numElement = 0;
|
2018-11-01 13:53:50 +01:00
|
|
|
const TileElement* elementIterator = firstElement;
|
2017-06-06 23:24:18 +02:00
|
|
|
do
|
|
|
|
{
|
|
|
|
numElement++;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(elementIterator++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Bubble sort
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t loopStart = 1; loopStart < numElement; loopStart++)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-06-22 23:17:03 +02:00
|
|
|
int32_t currentId = loopStart;
|
2018-11-01 13:53:50 +01:00
|
|
|
const TileElement* currentElement = firstElement + currentId;
|
|
|
|
const TileElement* otherElement = currentElement - 1;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-01-17 13:00:24 +01:00
|
|
|
// While current element's base height is lower, or (when their baseheight is the same) the other map element's
|
|
|
|
// clearance height is lower...
|
2018-06-22 23:17:03 +02:00
|
|
|
while (currentId > 0
|
|
|
|
&& (otherElement->base_height > currentElement->base_height
|
|
|
|
|| (otherElement->base_height == currentElement->base_height
|
|
|
|
&& otherElement->clearance_height > currentElement->clearance_height)))
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
if (!map_swap_elements_at(loc, currentId - 1, currentId))
|
2017-11-20 11:37:15 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// don't return error here, we've already ran some actions
|
|
|
|
// and moved things as far as we could, the only sensible
|
|
|
|
// thing left to do is to invalidate the window.
|
|
|
|
loopStart = numElement;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
currentId--;
|
|
|
|
currentElement--;
|
|
|
|
otherElement--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Deselect tile for clients who had it selected
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-06-11 01:09:00 +02:00
|
|
|
windowTileInspectorSelectedIndex = -1;
|
2017-06-06 23:24:18 +02:00
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-25 13:42:04 +01:00
|
|
|
}
|
2017-01-25 14:22:33 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_any_base_height_offset(
|
|
|
|
CoordsXY loc, int16_t elementIndex, int8_t heightOffset, bool isExecuting)
|
2017-01-25 14:22:33 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2018-01-17 13:00:24 +01:00
|
|
|
if (tileElement == nullptr)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2018-01-17 13:00:24 +01:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
int16_t newBaseHeight = (int16_t)tileElement->base_height + heightOffset;
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t newClearanceHeight = (int16_t)tileElement->clearance_height + heightOffset;
|
2017-06-06 23:24:18 +02:00
|
|
|
if (newBaseHeight < 0 || newBaseHeight > 0xff || newClearanceHeight < 0 || newClearanceHeight > 0xff)
|
|
|
|
{
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-05-02 19:27:04 +02:00
|
|
|
if (tileElement->GetType() == TILE_ELEMENT_TYPE_ENTRANCE)
|
2018-03-07 19:10:50 +01:00
|
|
|
{
|
2018-09-26 12:13:44 +02:00
|
|
|
uint8_t entranceType = tileElement->AsEntrance()->GetEntranceType();
|
2018-03-07 19:10:50 +01:00
|
|
|
if (entranceType != ENTRANCE_TYPE_PARK_ENTRANCE)
|
|
|
|
{
|
|
|
|
// Update the ride's known entrance or exit height
|
2018-09-26 12:30:27 +02:00
|
|
|
Ride* ride = get_ride(tileElement->AsEntrance()->GetRideIndex());
|
2018-09-26 12:54:44 +02:00
|
|
|
uint8_t entranceIndex = tileElement->AsEntrance()->GetStationIndex();
|
2018-06-22 23:17:03 +02:00
|
|
|
auto entrance = ride_get_entrance_location(ride, entranceIndex);
|
|
|
|
auto exit = ride_get_exit_location(ride, entranceIndex);
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t z = tileElement->base_height;
|
2018-03-07 19:10:50 +01:00
|
|
|
|
|
|
|
// Make sure this is the correct entrance or exit
|
2019-05-18 10:23:33 +02:00
|
|
|
if (entranceType == ENTRANCE_TYPE_RIDE_ENTRANCE && entrance.x == loc.x / 32 && entrance.y == loc.y / 32
|
|
|
|
&& entrance.z == z)
|
2018-03-07 19:10:50 +01:00
|
|
|
ride_set_entrance_location(
|
|
|
|
ride, entranceIndex, { entrance.x, entrance.y, z + heightOffset, entrance.direction });
|
2019-05-18 10:23:33 +02:00
|
|
|
else if (entranceType == ENTRANCE_TYPE_RIDE_EXIT && exit.x == loc.x / 32 && exit.y == loc.y / 32 && exit.z == z)
|
2018-06-22 23:17:03 +02:00
|
|
|
ride_set_exit_location(ride, entranceIndex, { exit.x, exit.y, z + heightOffset, exit.direction });
|
2018-03-07 19:10:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement->base_height += heightOffset;
|
|
|
|
tileElement->clearance_height += heightOffset;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-25 14:22:33 +01:00
|
|
|
}
|
2017-01-27 13:30:09 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_surface_show_park_fences(CoordsXY loc, bool showFences, bool isExecuting)
|
2017-01-27 13:30:09 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const surfaceelement = map_get_surface_element_at(loc);
|
2017-01-27 13:30:09 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
// No surface element on tile
|
2017-11-20 11:37:15 +01:00
|
|
|
if (surfaceelement == nullptr)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-01-27 13:30:09 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
if (!showFences)
|
2018-09-14 14:54:12 +02:00
|
|
|
surfaceelement->AsSurface()->SetParkFences(0);
|
2017-06-06 23:24:18 +02:00
|
|
|
else
|
2019-05-18 10:23:33 +02:00
|
|
|
update_park_fences(loc);
|
2017-01-27 13:30:09 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-01-27 14:19:23 +01:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
2017-01-27 13:30:09 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-27 13:30:09 +01:00
|
|
|
}
|
2017-01-27 14:02:17 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_surface_toggle_corner(CoordsXY loc, int32_t cornerIndex, bool isExecuting)
|
2017-01-27 14:02:17 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const surfaceElement = map_get_surface_element_at(loc);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// No surface element on tile
|
2017-11-20 11:37:15 +01:00
|
|
|
if (surfaceElement == nullptr)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-09-14 14:54:12 +02:00
|
|
|
const uint8_t originalSlope = surfaceElement->AsSurface()->GetSlope();
|
2018-06-22 23:17:03 +02:00
|
|
|
const bool diagonal = (originalSlope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) >> 4;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-09-16 13:21:17 +02:00
|
|
|
uint8_t newSlope = surfaceElement->AsSurface()->GetSlope() ^ (1 << cornerIndex);
|
|
|
|
surfaceElement->AsSurface()->SetSlope(newSlope);
|
2018-09-14 14:54:12 +02:00
|
|
|
if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
surfaceElement->clearance_height = surfaceElement->base_height + 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
surfaceElement->clearance_height = surfaceElement->base_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
// All corners are raised
|
2018-09-14 14:54:12 +02:00
|
|
|
if ((surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) == TILE_ELEMENT_SLOPE_ALL_CORNERS_UP)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-09-14 14:54:12 +02:00
|
|
|
uint8_t slope = TILE_ELEMENT_SLOPE_FLAT;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
if (diagonal)
|
|
|
|
{
|
2017-11-18 12:58:27 +01:00
|
|
|
switch (originalSlope & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-06-22 23:17:03 +02:00
|
|
|
case TILE_ELEMENT_SLOPE_S_CORNER_DN:
|
2018-09-14 14:54:12 +02:00
|
|
|
slope |= TILE_ELEMENT_SLOPE_N_CORNER_UP;
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
|
|
|
case TILE_ELEMENT_SLOPE_W_CORNER_DN:
|
2018-09-14 14:54:12 +02:00
|
|
|
slope |= TILE_ELEMENT_SLOPE_E_CORNER_UP;
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
|
|
|
case TILE_ELEMENT_SLOPE_N_CORNER_DN:
|
2018-09-14 14:54:12 +02:00
|
|
|
slope |= TILE_ELEMENT_SLOPE_S_CORNER_UP;
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
|
|
|
case TILE_ELEMENT_SLOPE_E_CORNER_DN:
|
2018-09-14 14:54:12 +02:00
|
|
|
slope |= TILE_ELEMENT_SLOPE_W_CORNER_UP;
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2018-09-14 14:54:12 +02:00
|
|
|
surfaceElement->AsSurface()->SetSlope(slope);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Update base and clearance heights
|
|
|
|
surfaceElement->base_height += 2;
|
|
|
|
surfaceElement->clearance_height = surfaceElement->base_height + (diagonal ? 2 : 0);
|
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-27 14:19:23 +01:00
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_surface_toggle_diagonal(CoordsXY loc, bool isExecuting)
|
2017-01-27 14:19:23 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const surfaceElement = map_get_surface_element_at(loc);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// No surface element on tile
|
2017-11-20 11:37:15 +01:00
|
|
|
if (surfaceElement == nullptr)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-09-16 13:21:17 +02:00
|
|
|
uint8_t newSlope = surfaceElement->AsSurface()->GetSlope() ^ TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT;
|
|
|
|
surfaceElement->AsSurface()->SetSlope(newSlope);
|
2018-09-14 14:54:12 +02:00
|
|
|
if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
surfaceElement->clearance_height = surfaceElement->base_height + 4;
|
|
|
|
}
|
2018-09-14 14:54:12 +02:00
|
|
|
else if (surfaceElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
surfaceElement->clearance_height = surfaceElement->base_height + 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
surfaceElement->clearance_height = surfaceElement->base_height;
|
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-27 14:02:17 +01:00
|
|
|
}
|
2017-01-27 19:12:25 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_path_set_sloped(CoordsXY loc, int32_t elementIndex, bool sloped, bool isExecuting)
|
2017-02-06 14:51:45 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const pathElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (pathElement == nullptr || pathElement->GetType() != TILE_ELEMENT_TYPE_PATH)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-10-04 15:40:55 +02:00
|
|
|
pathElement->AsPath()->SetSloped(sloped);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-02-06 14:51:45 +01:00
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_path_set_broken(CoordsXY loc, int32_t elementIndex, bool broken, bool isExecuting)
|
2019-05-14 21:38:31 +02:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const pathElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2019-05-14 21:38:31 +02:00
|
|
|
|
|
|
|
if (pathElement == nullptr || pathElement->GetType() != TILE_ELEMENT_TYPE_PATH)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2019-05-14 21:38:31 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2019-05-14 21:38:31 +02:00
|
|
|
{
|
|
|
|
pathElement->AsPath()->SetIsBroken(broken);
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2019-05-14 21:38:31 +02:00
|
|
|
|
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2019-05-14 21:38:31 +02:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2019-05-14 21:38:31 +02:00
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_path_toggle_edge(CoordsXY loc, int32_t elementIndex, int32_t edgeIndex, bool isExecuting)
|
2017-01-27 19:12:25 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const pathElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-02-12 23:32:00 +01:00
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (pathElement == nullptr || pathElement->GetType() != TILE_ELEMENT_TYPE_PATH)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-01-27 19:12:25 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-10-05 13:33:46 +02:00
|
|
|
uint8_t newEdges = pathElement->AsPath()->GetEdgesAndCorners() ^ (1 << edgeIndex);
|
|
|
|
pathElement->AsPath()->SetEdgesAndCorners(newEdges);
|
2017-01-27 19:12:25 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-01-27 19:12:25 +01:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
2017-01-27 19:12:25 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-01-27 19:12:25 +01:00
|
|
|
}
|
2017-02-02 20:06:27 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_entrance_make_usable(CoordsXY loc, int32_t elementIndex, bool isExecuting)
|
2018-01-13 15:44:10 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const entranceElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2018-01-13 15:44:10 +01:00
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (entranceElement == nullptr || entranceElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2018-01-17 13:00:24 +01:00
|
|
|
|
2018-09-26 12:30:27 +02:00
|
|
|
Ride* ride = get_ride(entranceElement->AsEntrance()->GetRideIndex());
|
2018-01-17 13:00:24 +01:00
|
|
|
|
|
|
|
if (ride == nullptr)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2018-01-13 15:44:10 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2018-01-13 15:44:10 +01:00
|
|
|
{
|
2018-09-26 12:54:44 +02:00
|
|
|
uint8_t stationIndex = entranceElement->AsEntrance()->GetStationIndex();
|
2018-01-13 15:44:10 +01:00
|
|
|
|
2018-09-26 12:13:44 +02:00
|
|
|
switch (entranceElement->AsEntrance()->GetEntranceType())
|
2018-01-13 15:44:10 +01:00
|
|
|
{
|
2018-06-22 23:17:03 +02:00
|
|
|
case ENTRANCE_TYPE_RIDE_ENTRANCE:
|
|
|
|
ride_set_entrance_location(
|
2019-05-18 10:23:33 +02:00
|
|
|
ride, stationIndex,
|
|
|
|
{ loc.x / 32, loc.y / 32, entranceElement->base_height, (uint8_t)entranceElement->GetDirection() });
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
|
|
|
case ENTRANCE_TYPE_RIDE_EXIT:
|
|
|
|
ride_set_exit_location(
|
2019-05-18 10:23:33 +02:00
|
|
|
ride, stationIndex,
|
|
|
|
{ loc.x / 32, loc.y / 32, entranceElement->base_height, (uint8_t)entranceElement->GetDirection() });
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
2018-01-13 15:44:10 +01:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2018-01-13 15:44:10 +01:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2018-01-13 15:44:10 +01:00
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_wall_set_slope(CoordsXY loc, int32_t elementIndex, int32_t slopeValue, bool isExecuting)
|
2017-02-02 20:06:27 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const wallElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-02-02 20:06:27 +01:00
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (wallElement == nullptr || wallElement->GetType() != TILE_ELEMENT_TYPE_WALL)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-02-02 20:06:27 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
// Set new slope value
|
2018-09-17 15:18:07 +02:00
|
|
|
wallElement->AsWall()->SetSlope(slopeValue);
|
2017-02-02 20:06:27 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2017-02-02 20:06:27 +01:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
2017-02-02 20:06:27 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-02-02 20:06:27 +01:00
|
|
|
}
|
2017-02-07 01:06:50 +01:00
|
|
|
|
|
|
|
// Changes the height of all track elements that belong to the same track piece
|
|
|
|
// Broxzier: Copied from track_remove and stripped of unneeded code, but I think this should be smaller
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_track_base_height_offset(
|
|
|
|
CoordsXY loc, int32_t elementIndex, int8_t offset, bool isExecuting)
|
2017-02-07 01:06:50 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const trackElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
if (offset == 0)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (trackElement == nullptr || trackElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-09-17 21:54:36 +02:00
|
|
|
uint8_t type = trackElement->AsTrack()->GetTrackType();
|
2019-05-18 10:23:33 +02:00
|
|
|
int16_t originX = loc.x;
|
|
|
|
int16_t originY = loc.y;
|
2018-06-22 23:17:03 +02:00
|
|
|
int16_t originZ = trackElement->base_height * 8;
|
2018-09-14 11:14:19 +02:00
|
|
|
uint8_t rotation = trackElement->GetDirection();
|
2019-01-12 11:11:55 +01:00
|
|
|
ride_id_t rideIndex = trackElement->AsTrack()->GetRideIndex();
|
2018-06-22 23:17:03 +02:00
|
|
|
Ride* ride = get_ride(rideIndex);
|
|
|
|
const rct_preview_track* trackBlock = get_track_def_from_ride(ride, type);
|
2018-09-17 22:10:15 +02:00
|
|
|
trackBlock += trackElement->AsTrack()->GetSequenceIndex();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-09-14 11:14:19 +02:00
|
|
|
uint8_t originDirection = trackElement->GetDirection();
|
2019-05-25 15:53:11 +02:00
|
|
|
CoordsXY offsets = { trackBlock->x, trackBlock->y };
|
|
|
|
CoordsXY coords = { originX, originY };
|
|
|
|
coords += offsets.Rotate(direction_reverse(originDirection));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-25 15:53:11 +02:00
|
|
|
originX = (int16_t)coords.x;
|
|
|
|
originY = (int16_t)coords.y;
|
2017-06-06 23:24:18 +02:00
|
|
|
originZ -= trackBlock->z;
|
|
|
|
|
|
|
|
trackBlock = get_track_def_from_ride(ride, type);
|
|
|
|
for (; trackBlock->index != 255; trackBlock++)
|
|
|
|
{
|
2019-05-25 15:53:11 +02:00
|
|
|
CoordsXY elem = { originX, originY };
|
|
|
|
int16_t elemZ = originZ;
|
|
|
|
offsets.x = trackBlock->x;
|
|
|
|
offsets.y = trackBlock->y;
|
|
|
|
elem += offsets.Rotate(originDirection);
|
2017-06-06 23:24:18 +02:00
|
|
|
elemZ += trackBlock->z;
|
|
|
|
|
2019-05-25 15:53:11 +02:00
|
|
|
map_invalidate_tile_full(elem.x, elem.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
bool found = false;
|
2019-05-25 15:53:11 +02:00
|
|
|
TileElement* tileElement = map_get_first_element_at(elem.x >> 5, elem.y >> 5);
|
2017-06-06 23:24:18 +02:00
|
|
|
do
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
if (tileElement->base_height != elemZ / 8)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-09-17 22:10:15 +02:00
|
|
|
if (tileElement->GetDirection() != rotation)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-09-17 22:10:15 +02:00
|
|
|
if (tileElement->AsTrack()->GetSequenceIndex() != trackBlock->index)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-09-17 21:54:36 +02:00
|
|
|
if (tileElement->AsTrack()->GetTrackType() != type)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
break;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
log_error("Track map element part not found!");
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2018-01-17 13:00:24 +01:00
|
|
|
// track_remove returns here on failure, not sure when this would ever be hit. Only thing I can think of is for when
|
|
|
|
// you decrease the map size.
|
2019-05-25 15:53:11 +02:00
|
|
|
openrct2_assert(map_get_surface_element_at(elem) != nullptr, "No surface at %d,%d", elem.x >> 5, elem.y >> 5);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Keep?
|
2019-01-18 11:46:18 +01:00
|
|
|
// invalidate_test_results(ride);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement->base_height += offset;
|
|
|
|
tileElement->clearance_height += offset;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Only invalidate when one of the affected tiles is selected
|
|
|
|
window_invalidate_by_class(WC_TILE_INSPECTOR);
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-02-07 01:06:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sets chainlift, optionally for an entire track block
|
|
|
|
// Broxzier: Basically a copy of the above function, with just two different lines... should probably be combined somehow
|
2019-05-15 22:15:23 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_track_set_chain(
|
2019-05-18 10:23:33 +02:00
|
|
|
CoordsXY loc, int32_t elementIndex, bool entireTrackBlock, bool setChain, bool isExecuting)
|
2017-02-07 01:06:50 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const trackElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (trackElement == nullptr || trackElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
if (!entireTrackBlock)
|
|
|
|
{
|
|
|
|
// Set chain for only the selected piece
|
2018-09-18 13:01:09 +02:00
|
|
|
if (trackElement->AsTrack()->HasChain() != setChain)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-09-18 13:01:09 +02:00
|
|
|
trackElement->AsTrack()->SetHasChain(setChain);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-17 21:54:36 +02:00
|
|
|
uint8_t type = trackElement->AsTrack()->GetTrackType();
|
2019-05-18 10:23:33 +02:00
|
|
|
int16_t originX = loc.x;
|
|
|
|
int16_t originY = loc.y;
|
2018-06-22 23:17:03 +02:00
|
|
|
int16_t originZ = trackElement->base_height * 8;
|
2018-09-14 11:14:19 +02:00
|
|
|
uint8_t rotation = trackElement->GetDirection();
|
2019-01-12 11:11:55 +01:00
|
|
|
ride_id_t rideIndex = trackElement->AsTrack()->GetRideIndex();
|
2018-06-22 23:17:03 +02:00
|
|
|
Ride* ride = get_ride(rideIndex);
|
|
|
|
const rct_preview_track* trackBlock = get_track_def_from_ride(ride, type);
|
2018-09-17 22:10:15 +02:00
|
|
|
trackBlock += trackElement->AsTrack()->GetSequenceIndex();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-09-14 11:14:19 +02:00
|
|
|
uint8_t originDirection = trackElement->GetDirection();
|
2019-05-25 15:53:11 +02:00
|
|
|
CoordsXY offsets = { trackBlock->x, trackBlock->y };
|
|
|
|
CoordsXY coords = { originX, originY };
|
|
|
|
coords += offsets.Rotate(direction_reverse(originDirection));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-25 15:53:11 +02:00
|
|
|
originX = (int16_t)coords.x;
|
|
|
|
originY = (int16_t)coords.y;
|
2017-06-06 23:24:18 +02:00
|
|
|
originZ -= trackBlock->z;
|
|
|
|
|
|
|
|
trackBlock = get_track_def_from_ride(ride, type);
|
|
|
|
for (; trackBlock->index != 255; trackBlock++)
|
|
|
|
{
|
2019-05-25 15:53:11 +02:00
|
|
|
CoordsXY elem = { originX, originY };
|
|
|
|
int16_t elemZ = originZ;
|
|
|
|
offsets.x = trackBlock->x;
|
|
|
|
offsets.y = trackBlock->y;
|
|
|
|
elem += offsets.Rotate(originDirection);
|
2017-06-06 23:24:18 +02:00
|
|
|
elemZ += trackBlock->z;
|
|
|
|
|
2019-05-25 15:53:11 +02:00
|
|
|
map_invalidate_tile_full(elem.x, elem.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
bool found = false;
|
2019-05-25 15:53:11 +02:00
|
|
|
TileElement* tileElement = map_get_first_element_at(elem.x >> 5, elem.y >> 5);
|
2017-06-06 23:24:18 +02:00
|
|
|
do
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
if (tileElement->base_height != elemZ / 8)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-09-17 22:10:15 +02:00
|
|
|
if (tileElement->GetDirection() != rotation)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-09-17 22:10:15 +02:00
|
|
|
if (tileElement->AsTrack()->GetSequenceIndex() != trackBlock->index)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-09-17 21:54:36 +02:00
|
|
|
if (tileElement->AsTrack()->GetTrackType() != type)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
break;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
log_error("Track map element part not found!");
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2018-01-17 13:00:24 +01:00
|
|
|
// track_remove returns here on failure, not sure when this would ever be hit. Only thing I can think of is for when
|
|
|
|
// you decrease the map size.
|
2019-05-25 15:53:11 +02:00
|
|
|
openrct2_assert(map_get_surface_element_at(elem) != nullptr, "No surface at %d,%d", elem.x >> 5, elem.y >> 5);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Keep?
|
2019-01-18 11:46:18 +01:00
|
|
|
// invalidate_test_results(ride);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-09-18 13:01:09 +02:00
|
|
|
if (tileElement->AsTrack()->HasChain() != setChain)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-10-03 11:07:50 +02:00
|
|
|
tileElement->AsTrack()->SetHasChain(setChain);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Only invalidate when one of the affected tiles is selected
|
|
|
|
window_invalidate_by_class(WC_TILE_INSPECTOR);
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-02-07 01:06:50 +01:00
|
|
|
}
|
2017-02-07 15:07:31 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_track_set_block_brake(
|
|
|
|
CoordsXY loc, int32_t elementIndex, bool blockBrake, bool isExecuting)
|
2019-05-14 21:38:31 +02:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const trackElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2019-05-14 21:38:31 +02:00
|
|
|
|
|
|
|
if (trackElement == nullptr || trackElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2019-05-14 21:38:31 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2019-05-14 21:38:31 +02:00
|
|
|
{
|
|
|
|
trackElement->AsTrack()->SetBlockBrakeClosed(blockBrake);
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2019-05-14 21:38:31 +02:00
|
|
|
|
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2019-05-14 21:38:31 +02:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2019-05-14 21:38:31 +02:00
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_track_set_indestructible(
|
2019-05-18 10:23:33 +02:00
|
|
|
CoordsXY loc, int32_t elementIndex, bool isIndestructible, bool isExecuting)
|
2019-05-14 21:38:31 +02:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const trackElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2019-05-14 21:38:31 +02:00
|
|
|
|
|
|
|
if (trackElement == nullptr || trackElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2019-05-14 21:38:31 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2019-05-14 21:38:31 +02:00
|
|
|
{
|
|
|
|
trackElement->AsTrack()->SetIsIndestructible(isIndestructible);
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
2019-05-14 21:38:31 +02:00
|
|
|
|
|
|
|
rct_window* const tileInspectorWindow = window_find_by_class(WC_TILE_INSPECTOR);
|
2019-05-18 10:23:33 +02:00
|
|
|
if (tileInspectorWindow != nullptr && (uint32_t)(loc.x / 32) == windowTileInspectorTileX
|
|
|
|
&& (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2019-05-14 21:38:31 +02:00
|
|
|
{
|
|
|
|
window_invalidate(tileInspectorWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2019-05-14 21:38:31 +02:00
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_scenery_set_quarter_location(
|
2019-05-18 10:23:33 +02:00
|
|
|
CoordsXY loc, int32_t elementIndex, int32_t quarterIndex, bool isExecuting)
|
2017-02-07 15:07:31 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (tileElement == nullptr || tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
// Set quadrant index
|
2018-09-17 15:18:07 +02:00
|
|
|
tileElement->AsSmallScenery()->SetSceneryQuadrant(quarterIndex);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Update collision
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement->flags &= 0xF0;
|
|
|
|
tileElement->flags |= 1 << ((quarterIndex + 2) & 3);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
|
|
|
if ((uint32_t)(loc.x / 32) == windowTileInspectorTileX && (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate_by_class(WC_TILE_INSPECTOR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-02-07 15:07:31 +01:00
|
|
|
}
|
2017-02-07 15:25:42 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_scenery_set_quarter_collision(
|
2019-05-18 10:23:33 +02:00
|
|
|
CoordsXY loc, int32_t elementIndex, int32_t quarterIndex, bool isExecuting)
|
2017-02-07 15:25:42 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const tileElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-02-07 15:25:42 +01:00
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (tileElement == nullptr || tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-02-07 15:25:42 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement->flags ^= 1 << quarterIndex;
|
2017-02-07 15:25:42 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
map_invalidate_tile_full(loc.x, loc.y);
|
|
|
|
if ((uint32_t)(loc.x / 32) == windowTileInspectorTileX && (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate_by_class(WC_TILE_INSPECTOR);
|
|
|
|
}
|
|
|
|
}
|
2017-02-07 15:25:42 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-02-07 15:25:42 +01:00
|
|
|
}
|
2017-02-07 15:39:07 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_banner_toggle_blocking_edge(
|
|
|
|
CoordsXY loc, int32_t elementIndex, int32_t edgeIndex, bool isExecuting)
|
2017-02-07 21:42:39 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const bannerElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-02-07 21:42:39 +01:00
|
|
|
|
2018-09-27 22:51:56 +02:00
|
|
|
if (bannerElement == nullptr || bannerElement->GetType() != TILE_ELEMENT_TYPE_BANNER)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-02-07 21:42:39 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-09-27 22:51:56 +02:00
|
|
|
uint8_t edges = bannerElement->AsBanner()->GetAllowedEdges();
|
2018-09-26 14:52:16 +02:00
|
|
|
edges ^= (1 << edgeIndex);
|
2018-09-27 22:51:56 +02:00
|
|
|
bannerElement->AsBanner()->SetAllowedEdges(edges);
|
2017-02-07 21:42:39 +01:00
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
if ((uint32_t)(loc.x / 32) == windowTileInspectorTileX && (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate_by_class(WC_TILE_INSPECTOR);
|
|
|
|
}
|
|
|
|
}
|
2017-02-07 21:42:39 +01:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-02-07 21:42:39 +01:00
|
|
|
}
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
GameActionResult::Ptr tile_inspector_corrupt_clamp(CoordsXY loc, int32_t elementIndex, bool isExecuting)
|
2017-02-07 15:39:07 +01:00
|
|
|
{
|
2019-05-18 10:23:33 +02:00
|
|
|
TileElement* const corruptElement = map_get_nth_element_at(loc.x / 32, loc.y / 32, elementIndex);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-05-02 19:27:04 +02:00
|
|
|
if (corruptElement == nullptr || corruptElement->GetType() != TILE_ELEMENT_TYPE_CORRUPT)
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-05-24 11:44:53 +02:00
|
|
|
if (corruptElement->IsLastForTile())
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>(GA_ERROR::UNKNOWN, STR_NONE);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
if (isExecuting)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-11-01 13:53:50 +01:00
|
|
|
TileElement* const nextElement = corruptElement + 1;
|
2017-06-06 23:24:18 +02:00
|
|
|
corruptElement->base_height = corruptElement->clearance_height = nextElement->base_height;
|
|
|
|
|
2019-05-18 10:23:33 +02:00
|
|
|
if ((uint32_t)(loc.x / 32) == windowTileInspectorTileX && (uint32_t)(loc.y / 32) == windowTileInspectorTileY)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
window_invalidate_by_class(WC_TILE_INSPECTOR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-15 22:15:23 +02:00
|
|
|
return std::make_unique<GameActionResult>();
|
2017-02-07 15:39:07 +01:00
|
|
|
}
|