2016-05-04 19:54:33 +02:00
|
|
|
/*****************************************************************************
|
2018-06-15 14:07:34 +02:00
|
|
|
* Copyright (c) 2014-2018 OpenRCT2 developers
|
2016-05-04 19:54:33 +02: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
|
2016-05-04 19:54:33 +02:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
2016-05-04 19:54:33 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
2017-11-20 11:13:55 +01:00
|
|
|
#include "../Context.h"
|
2017-11-30 18:17:06 +01:00
|
|
|
#include "../Game.h"
|
2018-06-22 23:14:18 +02:00
|
|
|
#include "../audio/audio.h"
|
|
|
|
#include "../config/Config.h"
|
|
|
|
#include "../interface/Viewport.h"
|
2018-01-06 18:32:25 +01:00
|
|
|
#include "../localisation/Localisation.h"
|
|
|
|
#include "../localisation/StringIds.h"
|
2018-02-13 15:20:55 +01:00
|
|
|
#include "../object/ObjectList.h"
|
2017-12-13 13:02:24 +01:00
|
|
|
#include "../util/SawyerCoding.h"
|
|
|
|
#include "../util/Util.h"
|
2017-10-19 10:01:05 +02:00
|
|
|
#include "../windows/Intent.h"
|
2018-01-11 10:59:26 +01:00
|
|
|
#include "../world/Footpath.h"
|
2017-11-20 11:13:55 +01:00
|
|
|
#include "../world/LargeScenery.h"
|
2018-01-11 10:59:26 +01:00
|
|
|
#include "../world/Scenery.h"
|
2017-11-20 11:13:55 +01:00
|
|
|
#include "../world/SmallScenery.h"
|
|
|
|
#include "../world/Wall.h"
|
2018-01-10 00:00:09 +01:00
|
|
|
#include "RideData.h"
|
2018-06-22 23:14:18 +02:00
|
|
|
#include "Station.h"
|
2017-10-17 13:51:47 +02:00
|
|
|
#include "Track.h"
|
2017-10-16 12:02:23 +02:00
|
|
|
#include "TrackData.h"
|
2017-09-01 13:38:21 +02:00
|
|
|
#include "TrackDesign.h"
|
2016-10-22 18:06:27 +02:00
|
|
|
#include "TrackDesignRepository.h"
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
#include <algorithm>
|
2018-02-13 15:20:55 +01:00
|
|
|
|
2017-10-31 12:57:40 +01:00
|
|
|
#define TRACK_MAX_SAVED_TILE_ELEMENTS 1500
|
2017-05-29 22:34:33 +02:00
|
|
|
#define TRACK_NEARBY_SCENERY_DISTANCE 1
|
2017-11-30 21:28:23 +01:00
|
|
|
#define TRACK_TD6_MAX_ELEMENTS 8192
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2016-09-10 19:03:25 +02:00
|
|
|
bool gTrackDesignSaveMode = false;
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t gTrackDesignSaveRideIndex = 255;
|
2016-09-10 19:03:25 +02:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
static size_t _trackSavedTileElementsCount;
|
2018-11-01 13:53:50 +01:00
|
|
|
static TileElement* _trackSavedTileElements[TRACK_MAX_SAVED_TILE_ELEMENTS];
|
2016-05-02 04:05:17 +02:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
static size_t _trackSavedTileElementsDescCount;
|
|
|
|
static rct_td6_scenery_element _trackSavedTileElementsDesc[TRACK_MAX_SAVED_TILE_ELEMENTS];
|
2016-05-02 18:49:38 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
static rct_track_td6* _trackDesign;
|
2018-06-20 17:28:51 +02:00
|
|
|
static uint8_t _trackSaveDirection;
|
2016-05-02 18:49:38 +02:00
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static bool track_design_save_should_select_scenery_around(int32_t rideIndex, TileElement* tileElement);
|
2018-06-20 17:28:51 +02:00
|
|
|
static void track_design_save_select_nearby_scenery_for_tile(int32_t rideIndex, int32_t cx, int32_t cy);
|
2018-11-01 13:53:50 +01:00
|
|
|
static bool track_design_save_add_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement);
|
|
|
|
static void track_design_save_remove_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement);
|
2018-06-22 23:14:18 +02:00
|
|
|
static bool track_design_save_copy_scenery_to_td6(rct_track_td6* td6);
|
|
|
|
static rct_track_td6* track_design_save_to_td6(uint8_t rideIndex);
|
|
|
|
static bool track_design_save_to_td6_for_maze(uint8_t rideIndex, rct_track_td6* td6);
|
|
|
|
static bool track_design_save_to_td6_for_tracked_ride(uint8_t rideIndex, rct_track_td6* td6);
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2016-05-02 04:05:17 +02:00
|
|
|
void track_design_save_init()
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
_trackSavedTileElementsCount = 0;
|
|
|
|
_trackSavedTileElementsDescCount = 0;
|
2017-10-15 15:53:16 +02:00
|
|
|
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memset(_trackSavedTileElements, 0, sizeof(_trackSavedTileElements));
|
|
|
|
std::memset(_trackSavedTileElementsDesc, 0, sizeof(_trackSavedTileElementsDesc));
|
2016-05-02 04:05:17 +02:00
|
|
|
}
|
|
|
|
|
2016-04-28 23:57:04 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D2B07
|
|
|
|
*/
|
2018-06-22 23:14:18 +02:00
|
|
|
void track_design_save_select_tile_element(
|
2018-11-01 13:53:50 +01:00
|
|
|
int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement, bool collect)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
if (track_design_save_contains_tile_element(tileElement))
|
|
|
|
{
|
|
|
|
if (!collect)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
track_design_save_remove_tile_element(interactionType, x, y, tileElement);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-06-22 23:14:18 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (collect)
|
|
|
|
{
|
|
|
|
if (!track_design_save_add_tile_element(interactionType, x, y, tileElement))
|
|
|
|
{
|
2017-09-06 16:09:35 +02:00
|
|
|
context_show_error(
|
2017-06-06 23:24:18 +02:00
|
|
|
STR_SAVE_TRACK_SCENERY_UNABLE_TO_SELECT_ADDITIONAL_ITEM_OF_SCENERY,
|
2018-06-22 23:14:18 +02:00
|
|
|
STR_SAVE_TRACK_SCENERY_TOO_MANY_ITEMS_SELECTED);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D303D
|
|
|
|
*/
|
2018-06-20 17:28:51 +02:00
|
|
|
void track_design_save_select_nearby_scenery(int32_t rideIndex)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-11-01 13:53:50 +01:00
|
|
|
TileElement* tileElement;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++)
|
|
|
|
{
|
|
|
|
for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement = map_get_first_element_at(x, y);
|
2018-06-22 23:14:18 +02:00
|
|
|
do
|
|
|
|
{
|
|
|
|
if (track_design_save_should_select_scenery_around(rideIndex, tileElement))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
track_design_save_select_nearby_scenery_for_tile(rideIndex, x, y);
|
|
|
|
break;
|
|
|
|
}
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
gfx_invalidate_screen();
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D3026
|
|
|
|
*/
|
|
|
|
void track_design_save_reset_scenery()
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
track_design_save_init();
|
|
|
|
gfx_invalidate_screen();
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
static void track_design_save_callback(int32_t result, [[maybe_unused]] const utf8* path)
|
2016-11-29 19:41:54 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
free(_trackDesign->track_elements);
|
|
|
|
free(_trackDesign->entrance_elements);
|
|
|
|
free(_trackDesign->scenery_elements);
|
|
|
|
free(_trackDesign);
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (result == MODAL_RESULT_OK)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
track_repository_scan();
|
|
|
|
}
|
|
|
|
gfx_invalidate_screen();
|
2016-07-09 01:51:56 +02:00
|
|
|
}
|
|
|
|
|
2016-04-28 23:57:04 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D2804, 0x006D264D
|
|
|
|
*/
|
2018-06-20 17:28:51 +02:00
|
|
|
bool track_design_save(uint8_t rideIndex)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2017-09-12 11:16:57 +02:00
|
|
|
Ride* ride = get_ride(rideIndex);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED))
|
|
|
|
{
|
2017-09-06 16:09:35 +02:00
|
|
|
context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText);
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (!ride_has_ratings(ride))
|
|
|
|
{
|
2017-09-06 16:09:35 +02:00
|
|
|
context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText);
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_trackDesign = track_design_save_to_td6(rideIndex);
|
2018-06-22 23:14:18 +02:00
|
|
|
if (_trackDesign == nullptr)
|
|
|
|
{
|
2017-09-06 16:09:35 +02:00
|
|
|
context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText);
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (gTrackDesignSaveMode)
|
|
|
|
{
|
|
|
|
if (!track_design_save_copy_scenery_to_td6(_trackDesign))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
free(_trackDesign->track_elements);
|
|
|
|
free(_trackDesign->entrance_elements);
|
|
|
|
free(_trackDesign);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-11 16:41:09 +02:00
|
|
|
utf8 track_name[256];
|
|
|
|
format_string(track_name, sizeof(track_name), ride->name, &ride->name_arguments);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-02-05 22:59:44 +01:00
|
|
|
auto intent = Intent(WC_LOADSAVE);
|
|
|
|
intent.putExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK);
|
2018-06-22 23:14:18 +02:00
|
|
|
intent.putExtra(INTENT_EXTRA_PATH, std::string{ track_name });
|
|
|
|
intent.putExtra(INTENT_EXTRA_CALLBACK, (void*)track_design_save_callback);
|
2018-02-05 22:59:44 +01:00
|
|
|
context_open_intent(&intent);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
return true;
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
bool track_design_save_contains_tile_element(const TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
for (size_t i = 0; i < _trackSavedTileElementsCount; i++)
|
|
|
|
{
|
|
|
|
if (_trackSavedTileElements[i] == tileElement)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static int32_t tile_element_get_total_element_count(TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t elementCount;
|
2018-06-22 23:14:18 +02:00
|
|
|
rct_scenery_entry* sceneryEntry;
|
|
|
|
rct_large_scenery_tile* tile;
|
|
|
|
|
|
|
|
switch (tileElement->GetType())
|
|
|
|
{
|
|
|
|
case TILE_ELEMENT_TYPE_PATH:
|
|
|
|
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
|
|
|
|
case TILE_ELEMENT_TYPE_WALL:
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
|
2018-09-14 12:12:22 +02:00
|
|
|
sceneryEntry = tileElement->AsLargeScenery()->GetEntry();
|
2018-06-22 23:14:18 +02:00
|
|
|
tile = sceneryEntry->large_scenery.tiles;
|
|
|
|
elementCount = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
tile++;
|
|
|
|
elementCount++;
|
|
|
|
} while (tile->x_offset != (int16_t)(uint16_t)0xFFFF);
|
|
|
|
return elementCount;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D2ED2
|
|
|
|
*/
|
2018-11-01 13:53:50 +01:00
|
|
|
static bool track_design_save_can_add_tile_element(TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
size_t newElementCount = tile_element_get_total_element_count(tileElement);
|
2018-06-22 23:14:18 +02:00
|
|
|
if (newElementCount == 0)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get number of spare elements left
|
2017-10-31 14:03:45 +01:00
|
|
|
size_t spareSavedElements = TRACK_MAX_SAVED_TILE_ELEMENTS - _trackSavedTileElementsCount;
|
2018-06-22 23:14:18 +02:00
|
|
|
if (newElementCount > spareSavedElements)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// No more spare saved elements left
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D2F4C
|
|
|
|
*/
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_push_tile_element(int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
if (_trackSavedTileElementsCount < TRACK_MAX_SAVED_TILE_ELEMENTS)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
_trackSavedTileElements[_trackSavedTileElementsCount++] = tileElement;
|
2017-06-06 23:24:18 +02:00
|
|
|
map_invalidate_tile_full(x, y);
|
|
|
|
}
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D2FA7
|
|
|
|
*/
|
2018-06-22 23:14:18 +02:00
|
|
|
static void track_design_save_push_tile_element_desc(
|
2018-07-21 13:51:54 +02:00
|
|
|
const rct_object_entry* entry, int32_t x, int32_t y, int32_t z, uint8_t flags, uint8_t primaryColour,
|
2018-06-22 23:14:18 +02:00
|
|
|
uint8_t secondaryColour)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
rct_td6_scenery_element* item = &_trackSavedTileElementsDesc[_trackSavedTileElementsDescCount++];
|
2017-06-06 23:24:18 +02:00
|
|
|
item->scenery_object = *entry;
|
|
|
|
item->x = x / 32;
|
|
|
|
item->y = y / 32;
|
|
|
|
item->z = z;
|
|
|
|
item->flags = flags;
|
|
|
|
item->primary_colour = primaryColour;
|
|
|
|
item->secondary_colour = secondaryColour;
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_add_scenery(int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-09-13 19:40:07 +02:00
|
|
|
SmallSceneryElement* sceneryElement = tileElement->AsSmallScenery();
|
2018-09-13 18:12:20 +02:00
|
|
|
int32_t entryType = sceneryElement->GetEntryIndex();
|
2018-02-11 21:30:48 +01:00
|
|
|
auto entry = object_entry_get_entry(OBJECT_TYPE_SMALL_SCENERY, entryType);
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t flags = 0;
|
2018-10-03 11:39:33 +02:00
|
|
|
flags |= tileElement->GetDirection();
|
|
|
|
flags |= tileElement->AsSmallScenery()->GetSceneryQuadrant() << 2;
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2018-09-13 18:12:20 +02:00
|
|
|
uint8_t primaryColour = sceneryElement->GetPrimaryColour();
|
|
|
|
uint8_t secondaryColour = sceneryElement->GetSecondaryColour();
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
track_design_save_push_tile_element(x, y, tileElement);
|
|
|
|
track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, primaryColour, secondaryColour);
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_add_large_scenery(int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
rct_large_scenery_tile *sceneryTiles, *tile;
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t x0, y0, z0, z;
|
|
|
|
int32_t direction, sequence;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-09-14 12:12:22 +02:00
|
|
|
int32_t entryType = tileElement->AsLargeScenery()->GetEntryIndex();
|
2018-02-11 20:02:51 +01:00
|
|
|
auto entry = object_entry_get_entry(OBJECT_TYPE_LARGE_SCENERY, entryType);
|
2017-06-06 23:24:18 +02:00
|
|
|
sceneryTiles = get_large_scenery_entry(entryType)->large_scenery.tiles;
|
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
z = tileElement->base_height;
|
2018-10-03 11:39:33 +02:00
|
|
|
direction = tileElement->GetDirection();
|
2018-09-14 12:12:22 +02:00
|
|
|
sequence = tileElement->AsLargeScenery()->GetSequenceIndex();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-07-25 22:49:20 +02:00
|
|
|
if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, nullptr))
|
2018-06-22 23:14:18 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate through each tile of the large scenery element
|
|
|
|
sequence = 0;
|
2018-06-22 23:14:18 +02:00
|
|
|
for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++)
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t offsetX = tile->x_offset;
|
|
|
|
int16_t offsetY = tile->y_offset;
|
2017-06-06 23:24:18 +02:00
|
|
|
rotate_map_coordinates(&offsetX, &offsetY, direction);
|
|
|
|
|
|
|
|
x = x0 + offsetX;
|
|
|
|
y = y0 + offsetY;
|
|
|
|
z = (z0 + tile->z_offset) / 8;
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement = map_get_large_scenery_segment(x, y, z, direction, sequence);
|
2018-01-29 17:06:01 +01:00
|
|
|
if (tileElement != nullptr)
|
2017-11-17 17:15:55 +01:00
|
|
|
{
|
|
|
|
if (sequence == 0)
|
|
|
|
{
|
2018-10-03 11:39:33 +02:00
|
|
|
uint8_t flags = tileElement->GetDirection();
|
2018-09-14 12:12:22 +02:00
|
|
|
uint8_t primaryColour = tileElement->AsLargeScenery()->GetPrimaryColour();
|
|
|
|
uint8_t secondaryColour = tileElement->AsLargeScenery()->GetSecondaryColour();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-10-31 12:57:40 +01:00
|
|
|
track_design_save_push_tile_element_desc(entry, x, y, z, flags, primaryColour, secondaryColour);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2017-10-31 14:03:45 +01:00
|
|
|
track_design_save_push_tile_element(x, y, tileElement);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_add_wall(int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-09-17 15:42:34 +02:00
|
|
|
int32_t entryType = tileElement->AsWall()->GetEntryIndex();
|
2018-02-11 20:02:51 +01:00
|
|
|
auto entry = object_entry_get_entry(OBJECT_TYPE_WALLS, entryType);
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t flags = 0;
|
2018-10-03 11:39:33 +02:00
|
|
|
flags |= tileElement->GetDirection();
|
2018-09-17 14:22:17 +02:00
|
|
|
flags |= tileElement->AsWall()->GetTertiaryColour() << 2;
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2018-09-17 14:22:17 +02:00
|
|
|
uint8_t secondaryColour = tileElement->AsWall()->GetSecondaryColour();
|
|
|
|
uint8_t primaryColour = tileElement->AsWall()->GetPrimaryColour();
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
track_design_save_push_tile_element(x, y, tileElement);
|
|
|
|
track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, primaryColour, secondaryColour);
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_add_footpath(int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2019-01-02 19:52:53 +01:00
|
|
|
int32_t entryType = tileElement->AsPath()->GetPathEntryIndex();
|
2018-02-11 20:02:51 +01:00
|
|
|
auto entry = object_entry_get_entry(OBJECT_TYPE_PATHS, entryType);
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t flags = 0;
|
2018-10-05 13:11:44 +02:00
|
|
|
flags |= tileElement->AsPath()->GetEdges();
|
2018-10-04 15:40:55 +02:00
|
|
|
flags |= (tileElement->AsPath()->GetSlopeDirection()) << 5;
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
|
|
|
flags |= 0b00010000;
|
2018-10-03 10:57:29 +02:00
|
|
|
if (tileElement->AsPath()->IsQueue())
|
|
|
|
flags |= 1 << 7;
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
track_design_save_push_tile_element(x, y, tileElement);
|
|
|
|
track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, 0, 0);
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D2B3C
|
|
|
|
*/
|
2018-11-01 13:53:50 +01:00
|
|
|
static bool track_design_save_add_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
if (!track_design_save_can_add_tile_element(tileElement))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
switch (interactionType)
|
|
|
|
{
|
|
|
|
case VIEWPORT_INTERACTION_ITEM_SCENERY:
|
|
|
|
track_design_save_add_scenery(x, y, tileElement);
|
|
|
|
return true;
|
|
|
|
case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY:
|
|
|
|
track_design_save_add_large_scenery(x, y, tileElement);
|
|
|
|
return true;
|
|
|
|
case VIEWPORT_INTERACTION_ITEM_WALL:
|
|
|
|
track_design_save_add_wall(x, y, tileElement);
|
|
|
|
return true;
|
|
|
|
case VIEWPORT_INTERACTION_ITEM_FOOTPATH:
|
|
|
|
track_design_save_add_footpath(x, y, tileElement);
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D2F78
|
|
|
|
*/
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_pop_tile_element(int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
map_invalidate_tile_full(x, y);
|
|
|
|
|
|
|
|
// Find index of map element to remove
|
|
|
|
size_t removeIndex = SIZE_MAX;
|
2018-06-22 23:14:18 +02:00
|
|
|
for (size_t i = 0; i < _trackSavedTileElementsCount; i++)
|
|
|
|
{
|
|
|
|
if (_trackSavedTileElements[i] == tileElement)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
removeIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove map element from list
|
2018-06-22 23:14:18 +02:00
|
|
|
if (removeIndex != SIZE_MAX)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
size_t remainingNumItems = _trackSavedTileElementsCount - removeIndex - 1;
|
2018-06-22 23:14:18 +02:00
|
|
|
if (remainingNumItems > 0)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
memmove(
|
2018-07-21 13:51:54 +02:00
|
|
|
&_trackSavedTileElements[removeIndex], &_trackSavedTileElements[removeIndex + 1],
|
2018-11-01 13:53:50 +01:00
|
|
|
remainingNumItems * sizeof(TileElement*));
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2017-10-31 14:03:45 +01:00
|
|
|
_trackSavedTileElementsCount--;
|
2018-01-29 17:06:01 +01:00
|
|
|
_trackSavedTileElements[_trackSavedTileElementsCount] = nullptr;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D2FDD
|
|
|
|
*/
|
2018-07-21 11:50:45 +02:00
|
|
|
static void track_design_save_pop_tile_element_desc(
|
|
|
|
const rct_object_entry* entry, int32_t x, int32_t y, int32_t z, uint8_t flags)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t removeIndex = SIZE_MAX;
|
2018-06-22 23:14:18 +02:00
|
|
|
for (size_t i = 0; i < _trackSavedTileElementsDescCount; i++)
|
|
|
|
{
|
|
|
|
rct_td6_scenery_element* item = &_trackSavedTileElementsDesc[i];
|
|
|
|
if (item->x != x / 32)
|
|
|
|
continue;
|
|
|
|
if (item->y != y / 32)
|
|
|
|
continue;
|
|
|
|
if (item->z != z)
|
|
|
|
continue;
|
|
|
|
if (item->flags != flags)
|
|
|
|
continue;
|
|
|
|
if (!object_entry_compare(&item->scenery_object, entry))
|
|
|
|
continue;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
removeIndex = i;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (removeIndex != SIZE_MAX)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
size_t remainingNumItems = _trackSavedTileElementsDescCount - removeIndex - 1;
|
2018-06-22 23:14:18 +02:00
|
|
|
if (remainingNumItems > 0)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
memmove(
|
2018-07-21 13:51:54 +02:00
|
|
|
&_trackSavedTileElementsDesc[removeIndex], &_trackSavedTileElementsDesc[removeIndex + 1],
|
2018-06-22 23:14:18 +02:00
|
|
|
remainingNumItems * sizeof(rct_td6_scenery_element));
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2017-10-31 14:03:45 +01:00
|
|
|
_trackSavedTileElementsDescCount--;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_remove_scenery(int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-09-13 17:02:38 +02:00
|
|
|
int32_t entryType = tileElement->AsSmallScenery()->GetEntryIndex();
|
2018-02-11 20:02:51 +01:00
|
|
|
auto entry = object_entry_get_entry(OBJECT_TYPE_SMALL_SCENERY, entryType);
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t flags = 0;
|
2018-10-03 11:39:33 +02:00
|
|
|
flags |= tileElement->GetDirection();
|
|
|
|
flags |= tileElement->AsSmallScenery()->GetSceneryQuadrant() << 2;
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
track_design_save_pop_tile_element(x, y, tileElement);
|
2018-04-20 13:56:37 +02:00
|
|
|
track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags);
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_remove_large_scenery(int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
rct_large_scenery_tile *sceneryTiles, *tile;
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t x0, y0, z0, z;
|
|
|
|
int32_t direction, sequence;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-09-14 12:12:22 +02:00
|
|
|
int32_t entryType = tileElement->AsLargeScenery()->GetEntryIndex();
|
2018-02-11 20:02:51 +01:00
|
|
|
auto entry = object_entry_get_entry(OBJECT_TYPE_LARGE_SCENERY, entryType);
|
2017-06-06 23:24:18 +02:00
|
|
|
sceneryTiles = get_large_scenery_entry(entryType)->large_scenery.tiles;
|
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
z = tileElement->base_height;
|
2018-10-03 11:39:33 +02:00
|
|
|
direction = tileElement->GetDirection();
|
2018-09-14 12:12:22 +02:00
|
|
|
sequence = tileElement->AsLargeScenery()->GetSequenceIndex();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-07-25 22:49:20 +02:00
|
|
|
if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, nullptr))
|
2018-06-22 23:14:18 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate through each tile of the large scenery element
|
|
|
|
sequence = 0;
|
2018-06-22 23:14:18 +02:00
|
|
|
for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++)
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t offsetX = tile->x_offset;
|
|
|
|
int16_t offsetY = tile->y_offset;
|
2017-06-06 23:24:18 +02:00
|
|
|
rotate_map_coordinates(&offsetX, &offsetY, direction);
|
|
|
|
|
|
|
|
x = x0 + offsetX;
|
|
|
|
y = y0 + offsetY;
|
|
|
|
z = (z0 + tile->z_offset) / 8;
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement = map_get_large_scenery_segment(x, y, z, direction, sequence);
|
2018-01-29 17:06:01 +01:00
|
|
|
if (tileElement != nullptr)
|
2017-11-17 17:15:55 +01:00
|
|
|
{
|
|
|
|
if (sequence == 0)
|
|
|
|
{
|
2018-10-03 11:39:33 +02:00
|
|
|
uint8_t flags = tileElement->GetDirection();
|
2018-04-20 13:56:37 +02:00
|
|
|
track_design_save_pop_tile_element_desc(entry, x, y, z, flags);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2017-10-31 14:03:45 +01:00
|
|
|
track_design_save_pop_tile_element(x, y, tileElement);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_remove_wall(int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-09-17 15:42:34 +02:00
|
|
|
int32_t entryType = tileElement->AsWall()->GetEntryIndex();
|
2018-02-11 20:02:51 +01:00
|
|
|
auto entry = object_entry_get_entry(OBJECT_TYPE_WALLS, entryType);
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t flags = 0;
|
2018-10-03 11:39:33 +02:00
|
|
|
flags |= tileElement->GetDirection();
|
2018-09-17 14:22:17 +02:00
|
|
|
flags |= tileElement->AsWall()->GetTertiaryColour() << 2;
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
track_design_save_pop_tile_element(x, y, tileElement);
|
2018-04-20 13:56:37 +02:00
|
|
|
track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags);
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_remove_footpath(int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2019-01-02 19:52:53 +01:00
|
|
|
int32_t entryType = tileElement->AsPath()->GetPathEntryIndex();
|
2018-02-11 20:02:51 +01:00
|
|
|
auto entry = object_entry_get_entry(OBJECT_TYPE_PATHS, entryType);
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t flags = 0;
|
2018-10-05 13:11:44 +02:00
|
|
|
flags |= tileElement->AsPath()->GetEdges();
|
2018-10-04 15:40:55 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
|
|
|
flags |= (1 << 4);
|
|
|
|
flags |= (tileElement->AsPath()->GetSlopeDirection()) << 5;
|
2018-10-03 10:57:29 +02:00
|
|
|
if (tileElement->AsPath()->IsQueue())
|
|
|
|
flags |= (1 << 7);
|
2016-04-28 23:57:04 +02:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
track_design_save_pop_tile_element(x, y, tileElement);
|
2018-04-20 13:56:37 +02:00
|
|
|
track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags);
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006D2B3C
|
|
|
|
*/
|
2018-11-01 13:53:50 +01:00
|
|
|
static void track_design_save_remove_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
switch (interactionType)
|
|
|
|
{
|
|
|
|
case VIEWPORT_INTERACTION_ITEM_SCENERY:
|
|
|
|
track_design_save_remove_scenery(x, y, tileElement);
|
|
|
|
break;
|
|
|
|
case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY:
|
|
|
|
track_design_save_remove_large_scenery(x, y, tileElement);
|
|
|
|
break;
|
|
|
|
case VIEWPORT_INTERACTION_ITEM_WALL:
|
|
|
|
track_design_save_remove_wall(x, y, tileElement);
|
|
|
|
break;
|
|
|
|
case VIEWPORT_INTERACTION_ITEM_FOOTPATH:
|
|
|
|
track_design_save_remove_footpath(x, y, tileElement);
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
static bool track_design_save_should_select_scenery_around(int32_t rideIndex, TileElement* tileElement)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
switch (tileElement->GetType())
|
|
|
|
{
|
|
|
|
case TILE_ELEMENT_TYPE_PATH:
|
2018-10-05 12:39:37 +02:00
|
|
|
if (tileElement->AsPath()->IsQueue() && tileElement->AsPath()->GetRideIndex() == rideIndex)
|
2018-06-22 23:14:18 +02:00
|
|
|
return true;
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
2018-06-22 23:14:18 +02:00
|
|
|
case TILE_ELEMENT_TYPE_TRACK:
|
2018-09-18 13:10:29 +02:00
|
|
|
if (tileElement->AsTrack()->GetRideIndex() == rideIndex)
|
2018-06-22 23:14:18 +02:00
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case TILE_ELEMENT_TYPE_ENTRANCE:
|
2018-09-26 12:13:44 +02:00
|
|
|
// FIXME: This will always break and return false!
|
|
|
|
if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE)
|
2018-06-22 23:14:18 +02:00
|
|
|
break;
|
2018-09-26 12:13:44 +02:00
|
|
|
if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT)
|
2018-06-22 23:14:18 +02:00
|
|
|
break;
|
2018-09-26 12:30:27 +02:00
|
|
|
if (tileElement->AsEntrance()->GetRideIndex() == rideIndex)
|
2018-06-22 23:14:18 +02:00
|
|
|
return true;
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
static void track_design_save_select_nearby_scenery_for_tile(int32_t rideIndex, int32_t cx, int32_t cy)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-11-01 13:53:50 +01:00
|
|
|
TileElement* tileElement;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
for (int32_t y = cy - TRACK_NEARBY_SCENERY_DISTANCE; y <= cy + TRACK_NEARBY_SCENERY_DISTANCE; y++)
|
|
|
|
{
|
|
|
|
for (int32_t x = cx - TRACK_NEARBY_SCENERY_DISTANCE; x <= cx + TRACK_NEARBY_SCENERY_DISTANCE; x++)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement = map_get_first_element_at(x, y);
|
2018-06-22 23:14:18 +02:00
|
|
|
do
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t interactionType = VIEWPORT_INTERACTION_ITEM_NONE;
|
2018-06-22 23:14:18 +02:00
|
|
|
switch (tileElement->GetType())
|
|
|
|
{
|
|
|
|
case TILE_ELEMENT_TYPE_PATH:
|
2018-10-03 10:57:29 +02:00
|
|
|
if (!tileElement->AsPath()->IsQueue())
|
2018-06-22 23:14:18 +02:00
|
|
|
interactionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH;
|
2018-10-05 12:39:37 +02:00
|
|
|
else if (tileElement->AsPath()->GetRideIndex() == rideIndex)
|
2018-06-22 23:14:18 +02:00
|
|
|
interactionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH;
|
|
|
|
break;
|
|
|
|
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
|
|
|
|
interactionType = VIEWPORT_INTERACTION_ITEM_SCENERY;
|
|
|
|
break;
|
|
|
|
case TILE_ELEMENT_TYPE_WALL:
|
|
|
|
interactionType = VIEWPORT_INTERACTION_ITEM_WALL;
|
|
|
|
break;
|
|
|
|
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
|
|
|
|
interactionType = VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY;
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (interactionType != VIEWPORT_INTERACTION_ITEM_NONE)
|
|
|
|
{
|
|
|
|
if (!track_design_save_contains_tile_element(tileElement))
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
track_design_save_add_tile_element(interactionType, x * 32, y * 32, tileElement);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Based on rct2: 0x006D2897 */
|
2018-06-22 23:14:18 +02:00
|
|
|
static bool track_design_save_copy_scenery_to_td6(rct_track_td6* td6)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Copy TD6 scenery elements to new memory and add end marker
|
2017-10-31 14:03:45 +01:00
|
|
|
size_t totalSceneryElementsSize = _trackSavedTileElementsDescCount * sizeof(rct_td6_scenery_element);
|
2018-06-22 23:14:18 +02:00
|
|
|
td6->scenery_elements = (rct_td6_scenery_element*)malloc(totalSceneryElementsSize + 1);
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(td6->scenery_elements, _trackSavedTileElementsDesc, totalSceneryElementsSize);
|
2018-06-20 17:28:51 +02:00
|
|
|
*((uint8_t*)&td6->scenery_elements[_trackSavedTileElementsDescCount]) = 0xFF;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Run an element loop
|
2018-06-22 23:14:18 +02:00
|
|
|
for (size_t i = 0; i < _trackSavedTileElementsDescCount; i++)
|
|
|
|
{
|
|
|
|
rct_td6_scenery_element* scenery = &td6->scenery_elements[i];
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
switch (object_entry_get_type(&scenery->scenery_object))
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
case OBJECT_TYPE_PATHS:
|
|
|
|
{
|
|
|
|
uint8_t slope = (scenery->flags & 0x60) >> 5;
|
|
|
|
slope -= _trackSaveDirection;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
scenery->flags &= 0x9F;
|
|
|
|
scenery->flags |= ((slope & 3) << 5);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
// Direction of connection on path
|
|
|
|
uint8_t direction = scenery->flags & 0xF;
|
|
|
|
// Rotate the direction by the track direction
|
|
|
|
direction = ((direction << 4) >> _trackSaveDirection);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
scenery->flags &= 0xF0;
|
|
|
|
scenery->flags |= (direction & 0xF) | (direction >> 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case OBJECT_TYPE_WALLS:
|
|
|
|
{
|
|
|
|
uint8_t direction = scenery->flags & 3;
|
|
|
|
direction -= _trackSaveDirection;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
scenery->flags &= 0xFC;
|
|
|
|
scenery->flags |= (direction & 3);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
uint8_t direction = scenery->flags & 3;
|
|
|
|
uint8_t quadrant = (scenery->flags & 0x0C) >> 2;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
direction -= _trackSaveDirection;
|
|
|
|
quadrant -= _trackSaveDirection;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
scenery->flags &= 0xF0;
|
|
|
|
scenery->flags |= (direction & 3) | ((quadrant & 3) << 2);
|
|
|
|
break;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t x = ((uint8_t)scenery->x) * 32 - gTrackPreviewOrigin.x;
|
|
|
|
int16_t y = ((uint8_t)scenery->y) * 32 - gTrackPreviewOrigin.y;
|
2017-06-06 23:24:18 +02:00
|
|
|
rotate_map_coordinates(&x, &y, (0 - _trackSaveDirection) & 3);
|
|
|
|
x /= 32;
|
|
|
|
y /= 32;
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (x > 127 || y > 127 || x < -126 || y < -126)
|
|
|
|
{
|
2017-09-06 16:09:35 +02:00
|
|
|
context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY);
|
2017-06-06 23:24:18 +02:00
|
|
|
SafeFree(td6->scenery_elements);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
scenery->x = (int8_t)x;
|
|
|
|
scenery->y = (int8_t)y;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t z = scenery->z * 8 - gTrackPreviewOrigin.z;
|
2017-06-06 23:24:18 +02:00
|
|
|
z /= 8;
|
2018-06-22 23:14:18 +02:00
|
|
|
if (z > 127 || z < -126)
|
|
|
|
{
|
2017-09-06 16:09:35 +02:00
|
|
|
context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY);
|
2017-06-06 23:24:18 +02:00
|
|
|
SafeFree(td6->scenery_elements);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
scenery->z = z;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006CE44F
|
|
|
|
*/
|
2018-06-22 23:14:18 +02:00
|
|
|
static rct_track_td6* track_design_save_to_td6(uint8_t rideIndex)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
rct_track_td6* td6 = (rct_track_td6*)calloc(1, sizeof(rct_track_td6));
|
|
|
|
Ride* ride = get_ride(rideIndex);
|
2017-06-06 23:24:18 +02:00
|
|
|
td6->type = ride->type;
|
2018-02-11 19:56:09 +01:00
|
|
|
auto object = object_entry_get_entry(OBJECT_TYPE_RIDE, ride->subtype);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Note we are only copying rct_object_entry in size and
|
|
|
|
// not the extended as we don't need the chunk size.
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(&td6->vehicle_object, object, sizeof(rct_object_entry));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
td6->ride_mode = ride->mode;
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
td6->version_and_colour_scheme = (ride->colour_scheme_type & 3) | (1 << 3); // Version .TD6
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t i = 0; i < RCT12_MAX_VEHICLES_PER_RIDE; i++)
|
2018-01-02 18:21:40 +01:00
|
|
|
{
|
2018-12-31 14:05:44 +01:00
|
|
|
td6->vehicle_colours[i].body_colour = ride->vehicle_colours[i].Body;
|
|
|
|
td6->vehicle_colours[i].trim_colour = ride->vehicle_colours[i].Trim;
|
|
|
|
td6->vehicle_additional_colour[i] = ride->vehicle_colours[i].Ternary;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++)
|
2018-01-02 18:21:40 +01:00
|
|
|
{
|
2019-01-01 21:33:59 +01:00
|
|
|
td6->track_spine_colour[i] = ride->track_colour[i].main;
|
|
|
|
td6->track_rail_colour[i] = ride->track_colour[i].additional;
|
|
|
|
td6->track_support_colour[i] = ride->track_colour[i].supports;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
td6->depart_flags = ride->depart_flags;
|
|
|
|
td6->number_of_trains = ride->num_vehicles;
|
|
|
|
td6->number_of_cars_per_train = ride->num_cars_per_train;
|
|
|
|
td6->min_waiting_time = ride->min_waiting_time;
|
|
|
|
td6->max_waiting_time = ride->max_waiting_time;
|
|
|
|
td6->operation_setting = ride->operation_option;
|
2018-06-22 23:14:18 +02:00
|
|
|
td6->lift_hill_speed_num_circuits = ride->lift_hill_speed | (ride->num_circuits << 5);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
td6->entrance_style = ride->entrance_style;
|
2018-06-20 17:28:51 +02:00
|
|
|
td6->max_speed = (int8_t)(ride->max_speed / 65536);
|
|
|
|
td6->average_speed = (int8_t)(ride->average_speed / 65536);
|
2017-06-06 23:24:18 +02:00
|
|
|
td6->ride_length = ride_get_total_length(ride) / 65536;
|
|
|
|
td6->max_positive_vertical_g = ride->max_positive_vertical_g / 32;
|
|
|
|
td6->max_negative_vertical_g = ride->max_negative_vertical_g / 32;
|
|
|
|
td6->max_lateral_g = ride->max_lateral_g / 32;
|
|
|
|
td6->inversions = ride->inversions;
|
|
|
|
td6->drops = ride->drops;
|
|
|
|
td6->highest_drop_height = ride->highest_drop_height;
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint16_t total_air_time = (ride->total_air_time * 123) / 1024;
|
2018-06-22 23:14:18 +02:00
|
|
|
if (total_air_time > 255)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
total_air_time = 0;
|
|
|
|
}
|
2018-06-20 17:28:51 +02:00
|
|
|
td6->total_air_time = (uint8_t)total_air_time;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
td6->excitement = ride->ratings.excitement / 10;
|
|
|
|
td6->intensity = ride->ratings.intensity / 10;
|
|
|
|
td6->nausea = ride->ratings.nausea / 10;
|
|
|
|
|
|
|
|
td6->upkeep_cost = ride->upkeep_cost;
|
|
|
|
td6->flags = 0;
|
|
|
|
td6->flags2 = 0;
|
|
|
|
|
|
|
|
bool result;
|
2018-06-22 23:14:18 +02:00
|
|
|
if (td6->type == RIDE_TYPE_MAZE)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
result = track_design_save_to_td6_for_maze(rideIndex, td6);
|
2018-06-22 23:14:18 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
result = track_design_save_to_td6_for_tracked_ride(rideIndex, td6);
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (!result)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
track_design_dispose(td6);
|
2018-01-29 17:06:01 +01:00
|
|
|
td6 = nullptr;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
return td6;
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006CEAAE
|
|
|
|
*/
|
2018-06-22 23:14:18 +02:00
|
|
|
static bool track_design_save_to_td6_for_maze(uint8_t rideIndex, rct_track_td6* td6)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-11-01 13:53:50 +01:00
|
|
|
TileElement* tileElement = nullptr;
|
2017-06-06 23:24:18 +02:00
|
|
|
bool mapFound = false;
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t startX = 0;
|
|
|
|
int16_t startY = 0;
|
2018-06-22 23:14:18 +02:00
|
|
|
for (startY = 0; startY < 8192; startY += 32)
|
|
|
|
{
|
|
|
|
for (startX = 0; startX < 8192; startX += 32)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement = map_get_first_element_at(startX >> 5, startY >> 5);
|
2018-06-22 23:14:18 +02:00
|
|
|
do
|
|
|
|
{
|
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-18 13:10:29 +02:00
|
|
|
if (tileElement->AsTrack()->GetRideIndex() == rideIndex)
|
2018-06-22 23:14:18 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
mapFound = true;
|
|
|
|
break;
|
|
|
|
}
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2018-06-22 23:14:18 +02:00
|
|
|
if (mapFound)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-06-22 23:14:18 +02:00
|
|
|
if (mapFound)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (mapFound == 0)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
gTrackPreviewOrigin = { startX, startY, (int16_t)(tileElement->base_height * 8) };
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
size_t numMazeElements = 0;
|
2018-06-22 23:14:18 +02:00
|
|
|
td6->maze_elements = (rct_td6_maze_element*)calloc(8192, sizeof(rct_td6_maze_element));
|
|
|
|
rct_td6_maze_element* maze = td6->maze_elements;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// x is defined here as we can start the search
|
|
|
|
// on tile start_x, start_y but then the next row
|
|
|
|
// must restart on 0
|
2018-06-22 23:14:18 +02:00
|
|
|
for (int16_t y = startY, x = startX; y < 8192; y += 32)
|
|
|
|
{
|
|
|
|
for (; x < 8192; x += 32)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement = map_get_first_element_at(x / 32, y / 32);
|
2018-06-22 23:14:18 +02:00
|
|
|
do
|
|
|
|
{
|
|
|
|
if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
|
|
|
|
continue;
|
2018-09-18 13:10:29 +02:00
|
|
|
if (tileElement->AsTrack()->GetRideIndex() != rideIndex)
|
2018-06-22 23:14:18 +02:00
|
|
|
continue;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-09-18 14:04:23 +02:00
|
|
|
maze->maze_entry = tileElement->AsTrack()->GetMazeEntry();
|
2017-06-06 23:24:18 +02:00
|
|
|
maze->x = (x - startX) / 32;
|
|
|
|
maze->y = (y - startY) / 32;
|
|
|
|
maze++;
|
|
|
|
numMazeElements++;
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (numMazeElements >= 2000)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY;
|
|
|
|
SafeFree(td6->maze_elements);
|
|
|
|
return false;
|
|
|
|
}
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
x = 0;
|
|
|
|
}
|
|
|
|
|
2018-03-07 19:10:50 +01:00
|
|
|
TileCoordsXYZD location = ride_get_entrance_location(rideIndex, 0);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-03-07 19:10:50 +01:00
|
|
|
if (location.isNull())
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY;
|
|
|
|
SafeFree(td6->maze_elements);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t x = location.x * 32;
|
|
|
|
int16_t y = location.y * 32;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement = map_get_first_element_at(location.x, location.y);
|
2018-06-22 23:14:18 +02:00
|
|
|
do
|
|
|
|
{
|
|
|
|
if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE)
|
|
|
|
continue;
|
2018-09-26 12:13:44 +02:00
|
|
|
if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE)
|
2018-06-22 23:14:18 +02:00
|
|
|
continue;
|
2018-09-26 12:30:27 +02:00
|
|
|
if (tileElement->AsEntrance()->GetRideIndex() == rideIndex)
|
2018-06-22 23:14:18 +02:00
|
|
|
break;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
// Add something that stops this from walking off the end
|
|
|
|
|
2018-09-14 11:14:19 +02:00
|
|
|
uint8_t entrance_direction = tileElement->GetDirection();
|
2017-07-28 00:18:03 +02:00
|
|
|
maze->direction = entrance_direction;
|
2017-06-06 23:24:18 +02:00
|
|
|
maze->type = 8;
|
2018-06-20 17:28:51 +02:00
|
|
|
maze->x = (int8_t)((x - startX) / 32);
|
|
|
|
maze->y = (int8_t)((y - startY) / 32);
|
2017-06-06 23:24:18 +02:00
|
|
|
maze++;
|
|
|
|
numMazeElements++;
|
|
|
|
|
2018-10-15 22:58:59 +02:00
|
|
|
location = ride_get_exit_location(rideIndex, 0);
|
2018-03-07 19:10:50 +01:00
|
|
|
if (location.isNull())
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY;
|
|
|
|
SafeFree(td6->maze_elements);
|
2018-03-07 19:10:50 +01:00
|
|
|
return false;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2017-07-17 20:04:51 +02:00
|
|
|
x = location.x * 32;
|
|
|
|
y = location.y * 32;
|
2017-10-31 14:03:45 +01:00
|
|
|
tileElement = map_get_first_element_at(location.x, location.y);
|
2018-06-22 23:14:18 +02:00
|
|
|
do
|
|
|
|
{
|
|
|
|
if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE)
|
|
|
|
continue;
|
2018-09-26 12:13:44 +02:00
|
|
|
if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT)
|
2018-06-22 23:14:18 +02:00
|
|
|
continue;
|
2018-09-26 12:30:27 +02:00
|
|
|
if (tileElement->AsEntrance()->GetRideIndex() == rideIndex)
|
2018-06-22 23:14:18 +02:00
|
|
|
break;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
// Add something that stops this from walking off the end
|
|
|
|
|
2018-09-14 11:14:19 +02:00
|
|
|
uint8_t exit_direction = tileElement->GetDirection();
|
2017-07-28 00:18:03 +02:00
|
|
|
maze->direction = exit_direction;
|
2017-06-06 23:24:18 +02:00
|
|
|
maze->type = 0x80;
|
2018-06-20 17:28:51 +02:00
|
|
|
maze->x = (int8_t)((x - startX) / 32);
|
|
|
|
maze->y = (int8_t)((y - startY) / 32);
|
2017-06-06 23:24:18 +02:00
|
|
|
maze++;
|
|
|
|
maze->all = 0;
|
|
|
|
maze++;
|
|
|
|
numMazeElements++;
|
|
|
|
|
|
|
|
// Write end marker
|
|
|
|
maze->all = 0;
|
|
|
|
maze++;
|
|
|
|
numMazeElements++;
|
|
|
|
|
|
|
|
// Trim memory
|
2018-06-22 23:14:18 +02:00
|
|
|
td6->maze_elements = (rct_td6_maze_element*)realloc(td6->maze_elements, numMazeElements * sizeof(rct_td6_maze_element));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Save global vars as they are still used by scenery
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t startZ = gTrackPreviewOrigin.z;
|
2017-07-24 18:43:57 +02:00
|
|
|
place_virtual_track(td6, PTD_OPERATION_DRAW_OUTLINES, true, 0, 4096, 4096, 0);
|
2018-01-18 23:59:30 +01:00
|
|
|
gTrackPreviewOrigin = { startX, startY, startZ };
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
|
|
|
|
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
|
|
|
|
gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN;
|
|
|
|
|
|
|
|
td6->space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1;
|
|
|
|
td6->space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1;
|
|
|
|
return true;
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006CE68D
|
|
|
|
*/
|
2018-06-22 23:14:18 +02:00
|
|
|
static bool track_design_save_to_td6_for_tracked_ride(uint8_t rideIndex, rct_track_td6* td6)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
Ride* ride = get_ride(rideIndex);
|
2018-02-15 17:42:53 +01:00
|
|
|
CoordsXYE trackElement;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (!ride_try_get_origin_element(rideIndex, &trackElement))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-04-03 16:39:05 +02:00
|
|
|
ride_get_start_of_track(&trackElement);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t z = trackElement.element->base_height * 8;
|
2018-09-17 21:54:36 +02:00
|
|
|
uint8_t track_type = trackElement.element->AsTrack()->GetTrackType();
|
2018-09-14 11:14:19 +02:00
|
|
|
uint8_t direction = trackElement.element->GetDirection();
|
2017-06-06 23:24:18 +02:00
|
|
|
_trackSaveDirection = direction;
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-17 21:54:36 +02:00
|
|
|
const rct_track_coordinates* trackCoordinates = &TrackCoordinates[trackElement.element->AsTrack()->GetTrackType()];
|
2017-06-06 23:24:18 +02:00
|
|
|
// Used in the following loop to know when we have
|
|
|
|
// completed all of the elements and are back at the
|
|
|
|
// start.
|
2018-11-01 13:53:50 +01:00
|
|
|
TileElement* initialMap = trackElement.element;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t start_x = trackElement.x;
|
|
|
|
int16_t start_y = trackElement.y;
|
|
|
|
int16_t start_z = z + trackCoordinates->z_begin;
|
2018-01-18 23:59:30 +01:00
|
|
|
gTrackPreviewOrigin = { start_x, start_y, start_z };
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
size_t numTrackElements = 0;
|
2018-06-22 23:14:18 +02:00
|
|
|
td6->track_elements = (rct_td6_track_element*)calloc(TRACK_TD6_MAX_ELEMENTS, sizeof(rct_td6_track_element));
|
|
|
|
rct_td6_track_element* track = td6->track_elements;
|
|
|
|
do
|
|
|
|
{
|
2018-09-17 21:54:36 +02:00
|
|
|
track->type = trackElement.element->AsTrack()->GetTrackType();
|
2018-06-22 23:14:18 +02:00
|
|
|
if (track->type == TRACK_ELEM_255)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
track->type = TRACK_ELEM_255_ALIAS;
|
|
|
|
}
|
|
|
|
|
2018-10-03 11:39:33 +02:00
|
|
|
uint8_t flags;
|
2017-10-04 17:14:53 +02:00
|
|
|
if (track_element_has_speed_setting(track->type))
|
|
|
|
{
|
2018-10-03 11:39:33 +02:00
|
|
|
flags = trackElement.element->AsTrack()->GetBrakeBoosterSpeed() >> 1;
|
2017-10-04 17:14:53 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-10-03 11:39:33 +02:00
|
|
|
flags = trackElement.element->AsTrack()->GetSeatRotation();
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2018-10-03 11:39:33 +02:00
|
|
|
if (trackElement.element->AsTrack()->HasChain())
|
|
|
|
flags |= (1 << 7);
|
2018-09-18 13:14:30 +02:00
|
|
|
flags |= trackElement.element->AsTrack()->GetColourScheme() << 4;
|
2018-06-22 23:14:18 +02:00
|
|
|
if (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_HAS_ALTERNATIVE_TRACK_TYPE
|
2018-09-18 13:37:44 +02:00
|
|
|
&& trackElement.element->AsTrack()->IsInverted())
|
2017-10-04 17:14:53 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
flags |= TRACK_ELEMENT_FLAG_INVERTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
track->flags = flags;
|
|
|
|
track++;
|
|
|
|
numTrackElements++;
|
|
|
|
|
2018-07-25 22:49:20 +02:00
|
|
|
if (!track_block_get_next(&trackElement, &trackElement, nullptr, nullptr))
|
2017-10-04 17:14:53 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
z = trackElement.element->base_height * 8;
|
2018-09-14 11:14:19 +02:00
|
|
|
direction = trackElement.element->GetDirection();
|
2018-09-17 21:54:36 +02:00
|
|
|
track_type = trackElement.element->AsTrack()->GetTrackType();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-10-04 17:14:53 +02:00
|
|
|
if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
}
|
2017-11-23 00:02:55 +01:00
|
|
|
|
2017-11-30 21:28:23 +01:00
|
|
|
if (TRACK_TD6_MAX_ELEMENTS == numTrackElements)
|
2017-11-23 00:02:55 +01:00
|
|
|
{
|
|
|
|
free(td6->track_elements);
|
|
|
|
gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY;
|
2017-11-30 21:28:23 +01:00
|
|
|
return false;
|
2017-11-23 00:02:55 +01:00
|
|
|
}
|
2018-06-22 23:14:18 +02:00
|
|
|
} while (trackElement.element != initialMap);
|
2017-10-04 17:14:53 +02:00
|
|
|
|
2018-07-21 11:50:45 +02:00
|
|
|
td6->track_elements = (rct_td6_track_element*)realloc(
|
|
|
|
td6->track_elements, numTrackElements * sizeof(rct_td6_track_element) + 1);
|
2018-06-20 17:28:51 +02:00
|
|
|
*((uint8_t*)&td6->track_elements[numTrackElements]) = 0xFF;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
size_t numEntranceElements = 0;
|
2018-06-22 23:14:18 +02:00
|
|
|
td6->entrance_elements = (rct_td6_entrance_element*)calloc(32, sizeof(rct_td6_entrance_element));
|
|
|
|
rct_td6_entrance_element* entrance = td6->entrance_elements;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// First entrances, second exits
|
2018-06-22 23:14:18 +02:00
|
|
|
for (int32_t i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
for (int32_t station_index = 0; station_index < RCT12_MAX_STATIONS_PER_RIDE; station_index++)
|
|
|
|
{
|
2019-01-01 02:14:19 +01:00
|
|
|
z = ride->stations[station_index].Height;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-03-07 19:10:50 +01:00
|
|
|
TileCoordsXYZD location;
|
2018-06-22 23:14:18 +02:00
|
|
|
if (i == 0)
|
|
|
|
{
|
2018-03-07 19:10:50 +01:00
|
|
|
location = ride_get_entrance_location(rideIndex, station_index);
|
2018-06-22 23:14:18 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-03-07 19:10:50 +01:00
|
|
|
location = ride_get_exit_location(rideIndex, station_index);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (location.isNull())
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t x = location.x * 32;
|
|
|
|
int16_t y = location.y * 32;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-11-01 13:53:50 +01:00
|
|
|
TileElement* tile_element = map_get_first_element_at(x >> 5, y >> 5);
|
2018-06-22 23:14:18 +02:00
|
|
|
do
|
|
|
|
{
|
|
|
|
if (tile_element->GetType() != TILE_ELEMENT_TYPE_ENTRANCE)
|
|
|
|
continue;
|
|
|
|
if (tile_element->base_height == z)
|
|
|
|
break;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tile_element++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
// Add something that stops this from walking off the end
|
|
|
|
|
2018-09-14 11:14:19 +02:00
|
|
|
uint8_t entrance_direction = tile_element->GetDirection();
|
2017-06-06 23:24:18 +02:00
|
|
|
entrance_direction -= _trackSaveDirection;
|
2017-10-31 12:57:40 +01:00
|
|
|
entrance_direction &= TILE_ELEMENT_DIRECTION_MASK;
|
2017-06-06 23:24:18 +02:00
|
|
|
entrance->direction = entrance_direction;
|
|
|
|
|
|
|
|
x -= gTrackPreviewOrigin.x;
|
|
|
|
y -= gTrackPreviewOrigin.y;
|
|
|
|
|
|
|
|
// Rotate entrance coordinates backwards to the correct direction
|
|
|
|
rotate_map_coordinates(&x, &y, (0 - _trackSaveDirection) & 3);
|
|
|
|
entrance->x = x;
|
|
|
|
entrance->y = y;
|
|
|
|
|
|
|
|
z *= 8;
|
|
|
|
z -= gTrackPreviewOrigin.z;
|
|
|
|
z /= 8;
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (z > 127 || z < -126)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
if (z == 0xFF)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
z = 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
entrance->z = z;
|
|
|
|
|
|
|
|
// If this is the exit version
|
2018-06-22 23:14:18 +02:00
|
|
|
if (i == 1)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
entrance->direction |= (1 << 7);
|
|
|
|
}
|
|
|
|
entrance++;
|
|
|
|
numEntranceElements++;
|
|
|
|
}
|
|
|
|
}
|
2018-06-22 23:14:18 +02:00
|
|
|
td6->entrance_elements = (rct_td6_entrance_element*)realloc(
|
|
|
|
td6->entrance_elements, numEntranceElements * sizeof(rct_td6_entrance_element) + 1);
|
2018-06-20 17:28:51 +02:00
|
|
|
*((uint8_t*)&td6->entrance_elements[numEntranceElements]) = 0xFF;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-07-24 18:43:57 +02:00
|
|
|
place_virtual_track(td6, PTD_OPERATION_DRAW_OUTLINES, true, 0, 4096, 4096, 0);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Resave global vars for scenery reasons.
|
2018-01-18 23:59:30 +01:00
|
|
|
gTrackPreviewOrigin = { start_x, start_y, start_z };
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
|
|
|
|
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
|
|
|
|
gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN;
|
|
|
|
|
|
|
|
td6->space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1;
|
|
|
|
td6->space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1;
|
|
|
|
return true;
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
static size_t track_design_get_maze_elements_count(rct_track_td6* td6)
|
2016-05-02 18:49:38 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t count = 0;
|
2018-06-22 23:14:18 +02:00
|
|
|
rct_td6_maze_element* mazeElement = td6->maze_elements;
|
|
|
|
while (mazeElement->all != 0)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
count++;
|
|
|
|
mazeElement++;
|
|
|
|
}
|
|
|
|
return count;
|
2016-05-02 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
static size_t track_design_get_track_elements_count(rct_track_td6* td6)
|
2016-05-02 18:49:38 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t count = 0;
|
2018-06-22 23:14:18 +02:00
|
|
|
rct_td6_track_element* trackElement = td6->track_elements;
|
|
|
|
while (trackElement->type != 0xFF)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
count++;
|
|
|
|
trackElement++;
|
|
|
|
}
|
|
|
|
return count;
|
2016-05-02 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
static size_t track_design_get_entrance_elements_count(rct_track_td6* td6)
|
2016-05-02 18:49:38 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t count = 0;
|
2018-06-22 23:14:18 +02:00
|
|
|
rct_td6_entrance_element* entranceElement = td6->entrance_elements;
|
|
|
|
while (entranceElement->z != -1)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
count++;
|
|
|
|
entranceElement++;
|
|
|
|
}
|
|
|
|
return count;
|
2016-05-02 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
static size_t track_design_get_scenery_elements_count(rct_track_td6* td6)
|
2016-05-02 18:49:38 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t count = 0;
|
2018-06-22 23:14:18 +02:00
|
|
|
rct_td6_scenery_element* sceneryElement = td6->scenery_elements;
|
|
|
|
if (sceneryElement != nullptr)
|
|
|
|
{
|
|
|
|
while (sceneryElement->scenery_object.end_flag != 0xFF)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
count++;
|
|
|
|
sceneryElement++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
2016-05-02 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
struct auto_buffer
|
|
|
|
{
|
|
|
|
void* ptr;
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t length;
|
|
|
|
size_t capacity;
|
2018-02-14 09:42:26 +01:00
|
|
|
};
|
2016-05-02 18:49:38 +02:00
|
|
|
|
2018-06-22 23:14:18 +02:00
|
|
|
static void auto_buffer_write(auto_buffer* buffer, const void* src, size_t len)
|
2016-05-02 18:49:38 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t remainingSpace = buffer->capacity - buffer->length;
|
2018-06-22 23:14:18 +02:00
|
|
|
if (remainingSpace < len)
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
2018-01-18 23:04:09 +01:00
|
|
|
buffer->capacity = std::max<size_t>(8, buffer->capacity * 2);
|
2017-06-06 23:24:18 +02:00
|
|
|
remainingSpace = buffer->capacity - buffer->length;
|
|
|
|
} while (remainingSpace < len);
|
|
|
|
|
|
|
|
buffer->ptr = realloc(buffer->ptr, buffer->capacity);
|
|
|
|
}
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy((void*)((uintptr_t)buffer->ptr + buffer->length), src, len);
|
2017-06-06 23:24:18 +02:00
|
|
|
buffer->length += len;
|
2016-05-02 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
2016-04-28 23:57:04 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006771DC but not really its branched from that
|
|
|
|
* quite far.
|
|
|
|
*/
|
2018-06-22 23:14:18 +02:00
|
|
|
bool track_design_save_to_file(const utf8* path)
|
2016-04-28 23:57:04 +02:00
|
|
|
{
|
2018-06-22 23:14:18 +02:00
|
|
|
rct_track_td6* td6 = _trackDesign;
|
2018-06-05 16:06:18 +02:00
|
|
|
const rct_td6_maze_element EndMarkerForMaze = {};
|
2018-06-20 17:28:51 +02:00
|
|
|
const uint8_t EndMarker = 0xFF;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
window_close_construction_windows();
|
|
|
|
|
|
|
|
// Create TD6 data buffer
|
2018-06-04 19:50:46 +02:00
|
|
|
auto_buffer td6Buffer = {};
|
2017-06-06 23:24:18 +02:00
|
|
|
auto_buffer_write(&td6Buffer, td6, 0xA3);
|
2018-06-22 23:14:18 +02:00
|
|
|
if (td6->type == RIDE_TYPE_MAZE)
|
|
|
|
{
|
|
|
|
auto_buffer_write(
|
|
|
|
&td6Buffer, td6->maze_elements, track_design_get_maze_elements_count(td6) * sizeof(rct_td6_maze_element));
|
2017-06-06 23:24:18 +02:00
|
|
|
auto_buffer_write(&td6Buffer, &EndMarkerForMaze, sizeof(EndMarkerForMaze));
|
2018-06-22 23:14:18 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto_buffer_write(
|
|
|
|
&td6Buffer, td6->track_elements, track_design_get_track_elements_count(td6) * sizeof(rct_td6_track_element));
|
2017-06-06 23:24:18 +02:00
|
|
|
auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker));
|
2018-06-22 23:14:18 +02:00
|
|
|
auto_buffer_write(
|
2018-07-21 13:51:54 +02:00
|
|
|
&td6Buffer, td6->entrance_elements,
|
2018-06-22 23:14:18 +02:00
|
|
|
track_design_get_entrance_elements_count(td6) * sizeof(rct_td6_entrance_element));
|
2017-06-06 23:24:18 +02:00
|
|
|
auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker));
|
|
|
|
}
|
2018-06-22 23:14:18 +02:00
|
|
|
auto_buffer_write(
|
|
|
|
&td6Buffer, td6->scenery_elements, track_design_get_scenery_elements_count(td6) * sizeof(rct_td6_scenery_element));
|
2017-06-06 23:24:18 +02:00
|
|
|
auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker));
|
|
|
|
|
|
|
|
// Encode TD6 data
|
2018-06-22 23:14:18 +02:00
|
|
|
uint8_t* encodedData = (uint8_t*)malloc(0x8000);
|
2018-01-29 17:06:01 +01:00
|
|
|
assert(td6Buffer.ptr != nullptr);
|
2018-06-20 17:28:51 +02:00
|
|
|
size_t encodedDataLength = sawyercoding_encode_td6((uint8_t*)td6Buffer.ptr, encodedData, td6Buffer.length);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
// Save encoded TD6 data to file
|
|
|
|
bool result;
|
|
|
|
log_verbose("saving track %s", path);
|
|
|
|
result = writeentirefile(path, encodedData, encodedDataLength);
|
2018-06-22 23:14:18 +02:00
|
|
|
if (!result)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
log_error("Failed to save %s", path);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(encodedData);
|
|
|
|
free(td6Buffer.ptr);
|
|
|
|
return result;
|
2016-04-28 23:57:04 +02:00
|
|
|
}
|