Add new .park save format

Co-authored-by: Gymnasiast <Gymnasiast@users.noreply.github.com>
Co-authored-by: duncanspumpkin <duncanspumpkin@users.noreply.github.com>
Co-authored-by: ZehMatt <Zehmatt@users.noreply.github.com>
Co-authored-by: Broxzier <Broxzier@users.noreply.github.com>
This commit is contained in:
IntelOrca 2021-10-27 14:21:14 +02:00 committed by Gymnasiast
parent e9e8dceca7
commit 34128dc262
No known key found for this signature in database
GPG Key ID: DBFFF47AB2CA3EDD
112 changed files with 7281 additions and 4243 deletions

View File

@ -61,9 +61,9 @@ set(TITLE_SEQUENCE_VERSION "0.1.2c")
set(TITLE_SEQUENCE_URL "https://github.com/OpenRCT2/title-sequences/releases/download/v${TITLE_SEQUENCE_VERSION}/title-sequences.zip")
set(TITLE_SEQUENCE_SHA1 "304d13a126c15bf2c86ff13b81a2f2cc1856ac8d")
set(OBJECTS_VERSION "1.0.21")
set(OBJECTS_VERSION "1.2.2")
set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v${OBJECTS_VERSION}/objects.zip")
set(OBJECTS_SHA1 "c38af45d51a6e440386180feacf76c64720b6ac5")
set(OBJECTS_SHA1 "a808fd47e9bc35d105dc371428a55888e6a86860")
set(REPLAYS_VERSION "0.0.57")
set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v${REPLAYS_VERSION}/replays.zip")

View File

@ -2226,7 +2226,6 @@ STR_3174 :This object is required by another object
STR_3175 :This object is always required
STR_3176 :Unable to select this object
STR_3177 :Unable to de-select this object
STR_3178 :At least one path object must be selected
STR_3179 :At least one ride vehicle/attraction object must be selected
STR_3180 :Invalid selection of objects
STR_3181 :Object Selection - {STRINGID}
@ -2237,7 +2236,7 @@ STR_3185 :Small Scenery
STR_3186 :Large Scenery
STR_3187 :Walls/Fences
STR_3188 :Path Signs
STR_3189 :Footpaths
STR_3189 :Legacy footpaths
STR_3190 :Path Extras
STR_3191 :Scenery Groups
STR_3192 :Park Entrance
@ -2833,8 +2832,6 @@ STR_5560 :Sets the inspection time to Every 10 minutes on all rides
STR_5561 :Failed to load language
STR_5562 :WARNING!
STR_5563 :This feature is currently unstable, take extra caution.
STR_5564 :Insert Corrupt Element
STR_5565 :Inserts a corrupt map element at top of tile. This will hide any element above the corrupt element.
STR_5566 :Password:
STR_5567 :Advertise
STR_5568 :Password Required
@ -3532,7 +3529,6 @@ STR_6331 :Create ducks
STR_6332 :Remove ducks
STR_6333 :Increase scale factor
STR_6334 :Decrease scale factor
STR_6335 :Tile Inspector: Insert corrupt element
STR_6336 :Tile Inspector: Copy element
STR_6337 :Tile Inspector: Paste element
STR_6338 :Tile Inspector: Delete element

View File

@ -362,7 +362,9 @@ declare global {
"terrain_surface" |
"terrain_edge" |
"station" |
"music";
"music" |
"footpath_surface" |
"footpath_railings";
type HookType =
"interval.tick" | "interval.day" |
@ -623,6 +625,8 @@ declare global {
type: "footpath";
object: number;
surfaceObject: number;
railingsObject: number;
edges: number;
corners: number;
@ -693,6 +697,7 @@ declare global {
station: number;
sequence: number;
footpathObject: number;
footpathSurfaceObject: number;
}
interface LargeSceneryElement extends BaseTileElement {

255
docs/save-format.md Normal file
View File

@ -0,0 +1,255 @@
# File
All strings are null terminated UTF-8.
Array32:
length: uint32
element-size: uint32
data: blob
An element-size of 0 indicates varying. E.g. for strings.
string-table: []
lcode: string (E.g. "en-GB")
value: string
## File
Header:
magic: uint32
target-version: uint32
min-version: uint32
num-chunks: uint32
uncompressed-size: uint64
compression: uint32
sha1: byte[20]
Chunks:
id: uint32
offset: uint64
length: uint64
## Chunks
Authoring:
engine: string E.g. "openrct2 v0.1.2 (Linux)"
authors: string[]
notes: string
date-started: timestamp
date-modified: timestamp
Objects: object[]
type: uint16
id: string
version: string
Scenario:
category: uint32
name: string-table
park-name: string-table
details: string-table
objective-type: uint32
objective-year: uint16
objective-guests: uint32
objective-rating: uint16
objective-ride-excitement: uint16
objective-ride-length: uint16
objective-park-value: money32
objective-ride-profit: money32
objective-shop-profit: money32
completed-company-value: money32
completed-name: string
General:
ticks: uint32
elapsed-months: uint32
current-day: uint16
rand: uint32[2]
initial-cash: money32
guest-initial-cash: money16
guest-initial-hunger: uint8
guest-initial-thirst: uint8
guest-spawns: xyzd32
land-price: money32
construction-rights-price: money32
Interface:
main-viewport:
x: uint16
y: uint16
zoom: uint8
rotation: uint8
last-entrance-style: uint32
Climate:
climate: uint8
climate-update-timer: uint16
current:
weather: uint8
temperature: uint8
effect: uint8
gloom: uint8
rain-level: uint8
next:
(same as above)
Park:
name: string-id
cash: money32
loan: money32
loan-max: money32
interest: uint16
flags: uint64
entrance-fee: money32
rating: uint16
rating-casualty-penalty: uint16
current-expenditure: money32
current-profit: money32
total-admissions: uint32
income-from-admissions: money32
handyman-colour: uint8
mechanic-colour: uint8
security-colour: uint8
campaigns:
weeks-left: uint16
ride-index: uint32
research-funding: uint8
research-enabled: uint32
research-progress-stage: uint8
research-progress: uint16
research-last-item: uint32
research-next-item: uint32
rating-warning-days: uint16
peep-warning-throttle: uint8[16]
awards:
History:
rating-history: uint16[]
guests-history: uint32[]
cash-history: money32[]
weekly-profit-history: money32[]
park-value-history: money32[]
expenditure-history: money32[][]
Inventions:
Tiles:
map-size: xy32
tile-elements: tile-element[]
Things:
Rides:
Banners:
Animations:
Staff:
Strings: string[]
Editor:
step: uint8
Derived:
park-size: uint32
guests-in-park: uint32
guests-heading-for-park: uint32
company-value: money32
park-value: money32
## Tile Element
tile-element-base:
type: uint8
flags: uint8
base-z: uint8
clear-z: uint8
surface-element:
slope: uint8
water-height: uint8
ownership: uint8
grass-length: uint8
surface-object-id: uint8
edge-object-id: uint8
(spare: 6)
footpath-element:
object-id: uint16
edges: uint8
flags2: uint8
addition: uint16
addition-status: uint8
station-index: uint8
ride-index: uint32
(spare: -2)
track-element:
type: uint16 (straight, 25 deg up, brakes etc.)
sequence: uint8
style: uint8 (wooden, intamin, b&m etc.)
station-index: uint8
colour: uint8
flags: uint8 (station / on-ride)
(spare: 5)
track-element (maze):
type: uint16
maze-entry: uint16
(spare: 8)
entrance-element:
object-id: uint16
footpath-object-id: uint16
ride-index: uint32
station-index: uint8
type: uint8
(spare: 2)
scenery-element:
object-id: uint32
age: uint8
colours: uint8[2]
(spare: 5)
scenery-large-element:
object-id: uint32
colour: uint8[3]
wall-element:
object-id: uint32
colours: uint8[3]
animation: uint8
banner-index: uint32
(spare: 0)
banner-element:
object-id: uint16
index: uint32
flags: uint8
position: uint8
(spare: 4)
## Limits
Ride index: uint32
Banner index: uint32
Station index: uint8
Ride object index: uint16
Terrain (surface) object index: uint8
Terrain (edge) object index: uint8
Entrance object index: uint16 (can be for each type if necessary)
Footpath object index: uint16
Footpath addition object index: uint16
Scenery object index: uint32
Scenery (large) object index: uint32
Wall object index: uint32
Banner object index: uint16

View File

@ -46,8 +46,8 @@
<GtestSha1>058b9df80244c03f1633cb06e9f70471a29ebb8e</GtestSha1>
<TitleSequencesUrl>https://github.com/OpenRCT2/title-sequences/releases/download/v0.1.2c/title-sequences.zip</TitleSequencesUrl>
<TitleSequencesSha1>304d13a126c15bf2c86ff13b81a2f2cc1856ac8d</TitleSequencesSha1>
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.0.21/objects.zip</ObjectsUrl>
<ObjectsSha1>c38af45d51a6e440386180feacf76c64720b6ac5</ObjectsSha1>
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.2.2/objects.zip</ObjectsUrl>
<ObjectsSha1>a808fd47e9bc35d105dc371428a55888e6a86860</ObjectsSha1>
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.57/replays.zip</ReplaysUrl>
<ReplaysSha1>DF9C3B48755B19FDD4D0EC721007B98CD5B6F420</ReplaysSha1>
</PropertyGroup>

View File

@ -314,13 +314,15 @@
foreach (ZipArchiveEntry file in archive.Entries)
{
string fileName = Path.Combine(destinationDirectory, file.FullName);
if (file.Name == String.Empty)
string directory = Path.GetDirectoryName(fileName);
if (!Directory.Exists(directory))
{
string directory = Path.GetDirectoryName(fileName);
Directory.CreateDirectory(directory);
continue;
}
file.ExtractToFile(fileName, true);
if (file.Name != String.Empty)
{
file.ExtractToFile(fileName, true);
}
}
}
]]>

View File

@ -15,8 +15,8 @@ let
objects-src = pkgs.fetchFromGitHub {
owner = "OpenRCT2";
repo = "objects";
rev = "v1.0.21";
sha256 = "b081f885311f9afebc41d9dd4a68b7db4cf736eb815c04e307e1a426f08cfa35";
rev = "v1.2.2";
sha256 = "2c77023068831c68423eb51f96251d1a06f58253414e4563ca17407d654ed96b";
};
title-sequences-src = pkgs.fetchFromGitHub {

View File

@ -100,7 +100,7 @@ namespace OpenRCT2::Ui::ShortcutId
constexpr std::string_view WindowRideConstructionDemolish = "window.rideconstruction.demolish";
// Window / tile inspector
constexpr std::string_view WindowTileInspectorInsertCorrupt = "window.tileinspector.insert_corrupt";
constexpr std::string_view WindowTileInspectorToggleInvisibility = "window.tileinspector.toggle_invisibility";
constexpr std::string_view WindowTileInspectorCopy = "window.tileinspector.copy";
constexpr std::string_view WindowTileInspectorPaste = "window.tileinspector.paste";
constexpr std::string_view WindowTileInspectorRemove = "window.tileinspector.remove";

View File

@ -427,7 +427,7 @@ std::string_view ShortcutManager::GetLegacyShortcutId(size_t index)
ShortcutId::InterfaceSceneryPicker,
ShortcutId::InterfaceScaleIncrease,
ShortcutId::InterfaceScaleDecrease,
ShortcutId::WindowTileInspectorInsertCorrupt,
ShortcutId::WindowTileInspectorToggleInvisibility,
ShortcutId::WindowTileInspectorCopy,
ShortcutId::WindowTileInspectorPaste,
ShortcutId::WindowTileInspectorRemove,

View File

@ -533,9 +533,6 @@ static void ShortcutIncreaseElementHeight()
case WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_BANNER:
action = WC_TILE_INSPECTOR__WIDX_BANNER_SPINNER_HEIGHT_INCREASE;
break;
case WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_CORRUPT:
action = WC_TILE_INSPECTOR__WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE;
break;
case TileInspectorPage::Default:
break;
}
@ -577,9 +574,6 @@ static void ShortcutDecreaseElementHeight()
case WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_BANNER:
action = WC_TILE_INSPECTOR__WIDX_BANNER_SPINNER_HEIGHT_DECREASE;
break;
case WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_CORRUPT:
action = WC_TILE_INSPECTOR__WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE;
break;
case TileInspectorPage::Default:
break;
}
@ -857,7 +851,7 @@ void ShortcutManager::RegisterDefaultShortcuts()
RegisterShortcut(ShortcutId::WindowRideConstructionBuild, STR_SHORTCUT_CONSTRUCTION_BUILD_CURRENT, "NUMPAD 0", []() { ShortcutConstructionBuildCurrent(); });
RegisterShortcut(ShortcutId::WindowRideConstructionDemolish, STR_SHORTCUT_CONSTRUCTION_DEMOLISH_CURRENT, "NUMPAD -", []() { ShortcutConstructionDemolishCurrent(); });
RegisterShortcut(ShortcutId::WindowTileInspectorInsertCorrupt, STR_SHORTCUT_INSERT_CORRPUT_ELEMENT, []() { TileInspectorMouseUp(WC_TILE_INSPECTOR__WIDX_BUTTON_CORRUPT); });
RegisterShortcut(ShortcutId::WindowTileInspectorToggleInvisibility, STR_SHORTCUT_TOGGLE_INVISIBILITY, []() { TileInspectorMouseUp(WC_TILE_INSPECTOR__WIDX_BUTTON_TOGGLE_INVISIBILITY); });
RegisterShortcut(ShortcutId::WindowTileInspectorCopy, STR_SHORTCUT_COPY_ELEMENT, []() { TileInspectorMouseUp(WC_TILE_INSPECTOR__WIDX_BUTTON_COPY); });
RegisterShortcut(ShortcutId::WindowTileInspectorPaste, STR_SHORTCUT_PASTE_ELEMENT, []() { TileInspectorMouseUp(WC_TILE_INSPECTOR__WIDX_BUTTON_PASTE); });
RegisterShortcut(ShortcutId::WindowTileInspectorRemove, STR_SHORTCUT_REMOVE_ELEMENT, []() { TileInspectorMouseUp(WC_TILE_INSPECTOR__WIDX_BUTTON_REMOVE); });

View File

@ -86,7 +86,7 @@ static char _filter_string[MAX_PATH];
static constexpr const rct_string_id WINDOW_TITLE = STR_OBJECT_SELECTION;
static constexpr const int32_t WH = 400;
static constexpr const int32_t WW = 600;
static constexpr const int32_t WW = 755;
struct ObjectPageDesc
{
@ -101,19 +101,21 @@ static constexpr const ObjectPageDesc ObjectSelectionPages[] = {
{ STR_OBJECT_SELECTION_LARGE_SCENERY, SPR_TAB_SCENERY_URBAN, true },
{ STR_OBJECT_SELECTION_WALLS_FENCES, SPR_TAB_SCENERY_WALLS, true },
{ STR_OBJECT_SELECTION_PATH_SIGNS, SPR_TAB_SCENERY_SIGNAGE, true },
{ STR_OBJECT_SELECTION_FOOTPATHS, SPR_TAB_SCENERY_PATHS, false },
{ STR_OBJECT_SELECTION_FOOTPATHS, SPR_TAB_SCENERY_PATHS, true },
{ STR_OBJECT_SELECTION_PATH_EXTRAS, SPR_TAB_SCENERY_PATH_ITEMS, false },
{ STR_OBJECT_SELECTION_SCENERY_GROUPS, SPR_TAB_SCENERY_STATUES, false },
{ STR_OBJECT_SELECTION_PARK_ENTRANCE, SPR_TAB_PARK, false },
{ STR_OBJECT_SELECTION_WATER, SPR_TAB_WATER, false },
// Currently hidden until new save format arrives:
// { STR_OBJECT_SELECTION_TERRAIN_SURFACES, SPR_G2_TAB_LAND, true },
// { STR_OBJECT_SELECTION_TERRAIN_EDGES, SPR_G2_TAB_LAND, true },
// { STR_OBJECT_SELECTION_STATIONS, SPR_TAB_PARK, true },
// { STR_OBJECT_SELECTION_MUSIC, SPR_TAB_MUSIC_0, false },
// { STR_OBJECT_SELECTION_FOOTPATH_SURFACES, SPR_TAB_SCENERY_PATHS, false },
// { STR_OBJECT_SELECTION_FOOTPATH_RAILINGS, SPR_G2_PATH_RAILINGS_TAB, false },
// Dummy place holder for string objects
{ STR_NONE, static_cast<uint32_t>(SPR_NONE), false },
{ STR_OBJECT_SELECTION_TERRAIN_SURFACES, SPR_G2_TAB_LAND, true },
{ STR_OBJECT_SELECTION_TERRAIN_EDGES, SPR_G2_TAB_LAND, true },
{ STR_OBJECT_SELECTION_STATIONS, SPR_TAB_PARK, true },
{ STR_OBJECT_SELECTION_MUSIC, SPR_TAB_MUSIC_0, false },
{ STR_OBJECT_SELECTION_FOOTPATH_SURFACES, SPR_TAB_SCENERY_PATHS, false },
{ STR_OBJECT_SELECTION_FOOTPATH_RAILINGS, SPR_G2_PATH_RAILINGS_TAB, false },
};
#pragma region Widgets
@ -258,7 +260,6 @@ enum
struct list_item
{
const ObjectRepositoryItem* repositoryItem;
rct_object_entry* entry;
std::unique_ptr<rct_object_filters> filter;
uint8_t* flags;
};
@ -305,8 +306,7 @@ static void visible_list_refresh(rct_window* w)
{
uint8_t selectionFlags = _objectSelectionFlags[i];
const ObjectRepositoryItem* item = &items[i];
ObjectType objectType = item->ObjectEntry.GetType();
if (objectType == get_selected_object_type(w) && !(selectionFlags & OBJECT_SELECTION_FLAG_6) && filter_source(item)
if (item->Type == get_selected_object_type(w) && !(selectionFlags & OBJECT_SELECTION_FLAG_6) && filter_source(item)
&& filter_string(item) && filter_chunks(item) && filter_selected(selectionFlags))
{
auto filter = std::make_unique<rct_object_filters>();
@ -316,7 +316,6 @@ static void visible_list_refresh(rct_window* w)
list_item currentListItem;
currentListItem.repositoryItem = item;
currentListItem.entry = const_cast<rct_object_entry*>(&item->ObjectEntry);
currentListItem.filter = std::move(filter);
currentListItem.flags = &_objectSelectionFlags[i];
_listItems.push_back(std::move(currentListItem));
@ -403,7 +402,6 @@ rct_window* window_editor_object_selection_open()
window->selected_tab = 0;
window->selected_list_item = -1;
window->object_entry = nullptr;
window->min_width = WW;
window->min_height = WH;
window->max_width = 1200;
@ -478,7 +476,6 @@ static void window_editor_object_selection_mouseup(rct_window* w, rct_widgetinde
visible_list_refresh(w);
w->selected_list_item = -1;
w->object_entry = nullptr;
w->scrolls[0].v_top = 0;
w->Invalidate();
break;
@ -497,7 +494,6 @@ static void window_editor_object_selection_mouseup(rct_window* w, rct_widgetinde
visible_list_refresh(w);
w->selected_list_item = -1;
w->object_entry = nullptr;
w->scrolls[0].v_top = 0;
w->frame_no = 0;
w->Invalidate();
@ -766,15 +762,15 @@ static void window_editor_object_selection_scroll_mouseover(
_loadedObject = nullptr;
}
if (selectedObject == -1)
{
w->object_entry = nullptr;
}
else
if (selectedObject != -1)
{
auto listItem = &_listItems[selectedObject];
w->object_entry = listItem->entry;
_loadedObject = object_repository_load_object(listItem->entry);
auto& objRepository = OpenRCT2::GetContext()->GetObjectRepository();
_loadedObject = objRepository.LoadObject(listItem->repositoryItem);
if (_loadedObject != nullptr)
{
_loadedObject->Load();
}
}
w->Invalidate();
@ -857,7 +853,8 @@ static void window_editor_object_selection_invalidate(rct_window* w)
for (size_t i = 0; i < std::size(ObjectSelectionPages); i++)
{
auto& widget = w->widgets[WIDX_TAB_1 + i];
if (!advancedMode && ObjectSelectionPages[i].IsAdvanced)
if ((!advancedMode && ObjectSelectionPages[i].IsAdvanced)
|| ObjectSelectionPages[i].Image == static_cast<uint32_t>(SPR_NONE))
{
widget.type = WindowWidgetType::Empty;
}
@ -1214,8 +1211,9 @@ static void window_editor_object_selection_scrollpaint(rct_window* w, rct_drawpi
gfx_clear(dpi, paletteIndex);
screenCoords.y = 0;
for (const auto& listItem : _listItems)
for (size_t i = 0; i < _listItems.size(); i++)
{
const auto& listItem = _listItems[i];
if (screenCoords.y + SCROLLABLE_ROW_HEIGHT >= dpi->y && screenCoords.y <= dpi->y + dpi->height)
{
// Draw checkbox
@ -1224,7 +1222,7 @@ static void window_editor_object_selection_scrollpaint(rct_window* w, rct_drawpi
dpi, { { 2, screenCoords.y }, { 11, screenCoords.y + 10 } }, w->colours[1], INSET_RECT_F_E0);
// Highlight background
auto highlighted = listItem.entry == w->object_entry && !(*listItem.flags & OBJECT_SELECTION_FLAG_6);
auto highlighted = i == static_cast<size_t>(w->selected_list_item) && !(*listItem.flags & OBJECT_SELECTION_FLAG_6);
if (highlighted)
{
auto bottom = screenCoords.y + (SCROLLABLE_ROW_HEIGHT - 1);
@ -1296,7 +1294,6 @@ static void window_editor_object_set_page(rct_window* w, int32_t page)
w->selected_tab = page;
w->selected_list_item = -1;
w->object_entry = nullptr;
w->scrolls[0].v_top = 0;
w->frame_no = 0;
@ -1371,26 +1368,27 @@ static void window_editor_object_selection_manage_tracks()
*/
static void editor_load_selected_objects()
{
auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
int32_t numItems = static_cast<int32_t>(object_repository_get_items_count());
const ObjectRepositoryItem* items = object_repository_get_items();
for (int32_t i = 0; i < numItems; i++)
{
if (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED)
{
const ObjectRepositoryItem* item = &items[i];
const rct_object_entry* entry = &item->ObjectEntry;
const auto* loadedObject = object_manager_get_loaded_object(ObjectEntryDescriptor(*item));
const auto* item = &items[i];
auto descriptor = ObjectEntryDescriptor(*item);
const auto* loadedObject = objManager.GetLoadedObject(descriptor);
if (loadedObject == nullptr)
{
loadedObject = object_manager_load_object(entry);
loadedObject = objManager.LoadObject(descriptor);
if (loadedObject == nullptr)
{
log_error("Failed to load entry %.8s", entry->name);
log_error("Failed to load entry %s", std::string(descriptor.GetName()).c_str());
}
else if (!(gScreenFlags & SCREEN_FLAGS_EDITOR))
{
// Defaults selected items to researched (if in-game)
ObjectType objectType = entry->GetType();
auto objectType = loadedObject->GetObjectType();
auto entryIndex = object_manager_get_loaded_object_entry_index(loadedObject);
if (objectType == ObjectType::Ride)
{
@ -1493,7 +1491,7 @@ static bool filter_string(const ObjectRepositoryItem* item)
// Check if the searched string exists in the name, ride type, or filename
bool inName = nameUpper.find(filterUpper) != std::string::npos;
bool inRideType = (item->ObjectEntry.GetType() == ObjectType::Ride) && typeUpper.find(filterUpper) != std::string::npos;
bool inRideType = (item->Type == ObjectType::Ride) && typeUpper.find(filterUpper) != std::string::npos;
bool inPath = pathUpper.find(filterUpper) != std::string::npos;
return inName || inRideType || inPath;
@ -1536,7 +1534,7 @@ static bool filter_source(const ObjectRepositoryItem* item)
static bool filter_chunks(const ObjectRepositoryItem* item)
{
if (item->ObjectEntry.GetType() == ObjectType::Ride)
if (item->Type == ObjectType::Ride)
{
uint8_t rideType = 0;
for (int32_t i = 0; i < MAX_RIDE_TYPES_PER_RIDE_ENTRY; i++)
@ -1566,8 +1564,7 @@ static void filter_update_counts()
const ObjectRepositoryItem* item = &items[i];
if (filter_source(item) && filter_string(item) && filter_chunks(item) && filter_selected(selectionFlags[i]))
{
ObjectType objectType = item->ObjectEntry.GetType();
_filter_object_counts[EnumValue(objectType)]++;
_filter_object_counts[EnumValue(item->Type)]++;
}
}
}
@ -1605,8 +1602,5 @@ static std::string object_get_description(const Object* object)
static ObjectType get_selected_object_type(rct_window* w)
{
auto tab = w->selected_tab;
if (tab >= EnumValue(ObjectType::ScenarioText))
return static_cast<ObjectType>(tab + 1);
return static_cast<ObjectType>(tab);
}

View File

@ -387,7 +387,7 @@ rct_window* window_finances_open()
w->number = 0;
w->frame_no = 0;
research_update_uncompleted_types();
ResearchUpdateUncompletedTypes();
}
w->page = WINDOW_FINANCES_PAGE_SUMMARY;

View File

@ -31,7 +31,7 @@
#include <openrct2/world/Surface.h>
static constexpr const rct_string_id WINDOW_TITLE = STR_FOOTPATHS;
static constexpr const int32_t WH = 381;
static constexpr const int32_t WH = 421;
static constexpr const int32_t WW = 106;
static constexpr const uint16_t ARROW_PULSE_DURATION = 200;
@ -60,6 +60,7 @@ enum WINDOW_FOOTPATH_WIDGET_IDX
WIDX_TYPE_GROUP,
WIDX_FOOTPATH_TYPE,
WIDX_QUEUELINE_TYPE,
WIDX_RAILINGS_TYPE,
WIDX_DIRECTION_GROUP,
WIDX_DIRECTION_NW,
@ -83,29 +84,30 @@ static rct_widget window_footpath_widgets[] = {
WINDOW_SHIM(WINDOW_TITLE, WW, WH),
// Type group
MakeWidget({ 3, 17}, {100, 55}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_TYPE ),
MakeWidget({ 3, 17}, {100, 95}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_TYPE ),
MakeWidget({ 6, 30}, { 47, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_FOOTPATH_TIP ),
MakeWidget({53, 30}, { 47, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_QUEUE_LINE_PATH_TIP ),
MakeWidget({29, 69}, { 47, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_FOOTPATH_TIP ),
// Direction group
MakeWidget({ 3, 75}, {100, 77}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_DIRECTION ),
MakeWidget({53, 87}, { 45, 29}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_DIRECTION_NE, STR_DIRECTION_TIP ),
MakeWidget({53, 116}, { 45, 29}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_DIRECTION_SE, STR_DIRECTION_TIP ),
MakeWidget({ 8, 116}, { 45, 29}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_DIRECTION_SW, STR_DIRECTION_TIP ),
MakeWidget({ 8, 87}, { 45, 29}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_DIRECTION_NW, STR_DIRECTION_TIP ),
MakeWidget({ 3, 115}, {100, 77}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_DIRECTION ),
MakeWidget({53, 127}, { 45, 29}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_DIRECTION_NE, STR_DIRECTION_TIP ),
MakeWidget({53, 156}, { 45, 29}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_DIRECTION_SE, STR_DIRECTION_TIP ),
MakeWidget({ 8, 156}, { 45, 29}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_DIRECTION_SW, STR_DIRECTION_TIP ),
MakeWidget({ 8, 127}, { 45, 29}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_DIRECTION_NW, STR_DIRECTION_TIP ),
// Slope group
MakeWidget({ 3, 155}, {100, 41}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_SLOPE ),
MakeWidget({17, 167}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_RIDE_CONSTRUCTION_SLOPE_DOWN, STR_SLOPE_DOWN_TIP ),
MakeWidget({41, 167}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_RIDE_CONSTRUCTION_SLOPE_LEVEL, STR_LEVEL_TIP ),
MakeWidget({65, 167}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_RIDE_CONSTRUCTION_SLOPE_UP, STR_SLOPE_UP_TIP ),
MakeWidget({ 8, 202}, { 90, 90}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_CONSTRUCT_THE_SELECTED_FOOTPATH_SECTION_TIP),
MakeWidget({30, 295}, { 46, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_DEMOLISH_CURRENT_SECTION, STR_REMOVE_PREVIOUS_FOOTPATH_SECTION_TIP ),
MakeWidget({ 3, 195}, {100, 41}, WindowWidgetType::Groupbox, WindowColour::Primary , STR_SLOPE ),
MakeWidget({17, 207}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_RIDE_CONSTRUCTION_SLOPE_DOWN, STR_SLOPE_DOWN_TIP ),
MakeWidget({41, 207}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_RIDE_CONSTRUCTION_SLOPE_LEVEL, STR_LEVEL_TIP ),
MakeWidget({65, 207}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_RIDE_CONSTRUCTION_SLOPE_UP, STR_SLOPE_UP_TIP ),
MakeWidget({ 8, 242}, { 90, 90}, WindowWidgetType::FlatBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_CONSTRUCT_THE_SELECTED_FOOTPATH_SECTION_TIP),
MakeWidget({30, 335}, { 46, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_DEMOLISH_CURRENT_SECTION, STR_REMOVE_PREVIOUS_FOOTPATH_SECTION_TIP ),
// Mode group
MakeWidget({ 3, 321}, {100, 54}, WindowWidgetType::Groupbox, WindowColour::Primary ),
MakeWidget({13, 332}, { 36, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_FOOTPATH_LAND, STR_CONSTRUCT_FOOTPATH_ON_LAND_TIP ),
MakeWidget({57, 332}, { 36, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_FOOTPATH_BRIDGE, STR_CONSTRUCT_BRIDGE_OR_TUNNEL_FOOTPATH_TIP ),
MakeWidget({ 3, 361}, {100, 54}, WindowWidgetType::Groupbox, WindowColour::Primary ),
MakeWidget({13, 372}, { 36, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_FOOTPATH_LAND, STR_CONSTRUCT_FOOTPATH_ON_LAND_TIP ),
MakeWidget({57, 372}, { 36, 36}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_CONSTRUCTION_FOOTPATH_BRIDGE, STR_CONSTRUCT_BRIDGE_OR_TUNNEL_FOOTPATH_TIP ),
WIDGETS_END,
};
@ -172,6 +174,7 @@ static constexpr const uint8_t ConstructionPreviewImages[][4] = {
static void window_footpath_mousedown_direction(int32_t direction);
static void window_footpath_mousedown_slope(int32_t slope);
static void window_footpath_show_footpath_types_dialog(rct_window* w, rct_widget* widget, bool showQueues);
static void window_footpath_show_railings_types_dialog(rct_window* w, rct_widget* widget);
static void window_footpath_set_provisional_path_at_point(const ScreenCoordsXY& screenCoords);
static void window_footpath_set_selection_start_bridge_at_point(const ScreenCoordsXY& screenCoords);
static void window_footpath_place_path_at_point(const ScreenCoordsXY& screenCoords);
@ -204,9 +207,10 @@ rct_window* window_footpath_open()
window = WindowCreate(ScreenCoordsXY(0, 29), WW, WH, &window_footpath_events, WC_FOOTPATH, 0);
window->widgets = window_footpath_widgets;
window->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_FOOTPATH_TYPE) | (1ULL << WIDX_QUEUELINE_TYPE)
| (1ULL << WIDX_DIRECTION_NW) | (1ULL << WIDX_DIRECTION_NE) | (1ULL << WIDX_DIRECTION_SW) | (1ULL << WIDX_DIRECTION_SE)
| (1ULL << WIDX_SLOPEDOWN) | (1ULL << WIDX_LEVEL) | (1ULL << WIDX_SLOPEUP) | (1ULL << WIDX_CONSTRUCT)
| (1ULL << WIDX_REMOVE) | (1ULL << WIDX_CONSTRUCT_ON_LAND) | (1ULL << WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL);
| (1ULL << WIDX_RAILINGS_TYPE) | (1ULL << WIDX_DIRECTION_NW) | (1ULL << WIDX_DIRECTION_NE) | (1ULL << WIDX_DIRECTION_SW)
| (1ULL << WIDX_DIRECTION_SE) | (1ULL << WIDX_SLOPEDOWN) | (1ULL << WIDX_LEVEL) | (1ULL << WIDX_SLOPEUP)
| (1ULL << WIDX_CONSTRUCT) | (1ULL << WIDX_REMOVE) | (1ULL << WIDX_CONSTRUCT_ON_LAND)
| (1ULL << WIDX_CONSTRUCT_BRIDGE_OR_TUNNEL);
WindowInitScrollWidgets(window);
window_push_others_right(window);
@ -304,6 +308,9 @@ static void window_footpath_mousedown(rct_window* w, rct_widgetindex widgetIndex
case WIDX_QUEUELINE_TYPE:
window_footpath_show_footpath_types_dialog(w, widget, true);
break;
case WIDX_RAILINGS_TYPE:
window_footpath_show_railings_types_dialog(w, widget);
break;
case WIDX_DIRECTION_NW:
window_footpath_mousedown_direction(0);
break;
@ -364,6 +371,10 @@ static void window_footpath_dropdown(rct_window* w, rct_widgetindex widgetIndex,
gFootpathSelection.QueueSurface = entryIndex.second;
}
}
else if (widgetIndex == WIDX_RAILINGS_TYPE)
{
gFootpathSelection.Railings = entryIndex.second;
}
else
{
return;
@ -573,6 +584,16 @@ static void window_footpath_invalidate(rct_window* w)
window_footpath_widgets[WIDX_FOOTPATH_TYPE].image = pathImage;
window_footpath_widgets[WIDX_QUEUELINE_TYPE].image = queueImage;
// Set railing
auto railingsImage = static_cast<uint32_t>(SPR_NONE);
auto railingsEntry = GetPathRailingsEntry(gFootpathSelection.Railings);
if (railingsEntry != nullptr)
{
railingsImage = railingsEntry->PreviewImageId;
}
window_footpath_widgets[WIDX_RAILINGS_TYPE].image = railingsImage;
window_footpath_widgets[WIDX_RAILINGS_TYPE].type = WindowWidgetType::FlatBtn;
}
else
{
@ -592,6 +613,9 @@ static void window_footpath_invalidate(rct_window* w)
window_footpath_widgets[WIDX_FOOTPATH_TYPE].image = pathImage;
window_footpath_widgets[WIDX_QUEUELINE_TYPE].image = queueImage;
// Hide railing button
window_footpath_widgets[WIDX_RAILINGS_TYPE].type = WindowWidgetType::Empty;
}
}
@ -749,6 +773,39 @@ static void window_footpath_show_footpath_types_dialog(rct_window* w, rct_widget
gDropdownDefaultIndex = static_cast<int32_t>(*defaultIndex);
}
static void window_footpath_show_railings_types_dialog(rct_window* w, rct_widget* widget)
{
uint32_t numRailingsTypes = 0;
// If the game is in sandbox mode, also show paths that are normally restricted to the scenario editor
_dropdownEntries.clear();
std::optional<size_t> defaultIndex;
for (int32_t i = 0; i < MAX_FOOTPATH_RAILINGS_OBJECTS; i++)
{
const auto* railingsEntry = GetPathRailingsEntry(i);
if (railingsEntry == nullptr)
{
continue;
}
if (i == gFootpathSelection.Railings)
{
defaultIndex = numRailingsTypes;
}
gDropdownItemsFormat[numRailingsTypes] = STR_NONE;
gDropdownItemsArgs[numRailingsTypes] = railingsEntry->PreviewImageId;
_dropdownEntries.push_back({ ObjectType::FootpathRailings, i });
numRailingsTypes++;
}
auto itemsPerRow = DropdownGetAppropriateImageDropdownItemsPerRow(numRailingsTypes);
WindowDropdownShowImage(
w->windowPos.x + widget->left, w->windowPos.y + widget->top, widget->height() + 1, w->colours[1], 0, numRailingsTypes,
47, 36, itemsPerRow);
if (defaultIndex)
gDropdownDefaultIndex = static_cast<int32_t>(*defaultIndex);
}
/**
*
* rct2: 0x006A8111 0x006A8135 0x006A815C 0x006A8183

View File

@ -201,13 +201,13 @@ static const char* getFilterPatternByType(const int32_t type, const bool isSave)
switch (type & 0x0E)
{
case LOADSAVETYPE_GAME:
return isSave ? "*.sv6" : "*.sv6;*.sc6;*.sc4;*.sv4;*.sv7;*.sea;";
return isSave ? "*.park" : "*.park;*.sv6;*.sc6;*.sc4;*.sv4;*.sv7;*.sea";
case LOADSAVETYPE_LANDSCAPE:
return isSave ? "*.sc6" : "*.sc6;*.sv6;*.sc4;*.sv4;*.sv7;*.sea;";
return isSave ? "*.park" : "*.park;*.sc6;*.sv6;*.sc4;*.sv4;*.sv7;*.sea";
case LOADSAVETYPE_SCENARIO:
return "*.sc6";
return isSave ? "*.park" : "*.park;*.sc6;*.sc4";
case LOADSAVETYPE_TRACK:
return isSave ? "*.td6" : "*.td6;*.td4";
@ -1041,7 +1041,7 @@ static void window_loadsave_select(rct_window* w, const char* path)
case (LOADSAVETYPE_SAVE | LOADSAVETYPE_LANDSCAPE):
save_path(&gConfigGeneral.last_save_landscape_directory, pathBuffer);
safe_strcpy(gScenarioFileName, pathBuffer, sizeof(gScenarioFileName));
gScenarioFileName = std::string(String::ToStringView(pathBuffer, std::size(pathBuffer)));
if (scenario_save(pathBuffer, gConfigGeneral.save_plugin_data ? 3 : 2))
{
gCurrentLoadedPath = pathBuffer;
@ -1062,7 +1062,7 @@ static void window_loadsave_select(rct_window* w, const char* path)
int32_t parkFlagsBackup = gParkFlags;
gParkFlags &= ~PARK_FLAGS_SPRITES_INITIALISED;
gEditorStep = EditorStep::Invalid;
safe_strcpy(gScenarioFileName, pathBuffer, sizeof(gScenarioFileName));
gScenarioFileName = std::string(String::ToStringView(pathBuffer, std::size(pathBuffer)));
int32_t success = scenario_save(pathBuffer, gConfigGeneral.save_plugin_data ? 3 : 2);
gParkFlags = parkFlagsBackup;

View File

@ -1405,7 +1405,6 @@ static constexpr const uint16_t ElementTypeMaskColour[] = {
0xFFFF, // TILE_ELEMENT_TYPE_WALL
0x0000, // TILE_ELEMENT_TYPE_LARGE_SCENERY
0xFFFF, // TILE_ELEMENT_TYPE_BANNER
0x0000, // TILE_ELEMENT_TYPE_CORRUPT
};
static constexpr const uint16_t ElementTypeAddColour[] = {
@ -1417,7 +1416,6 @@ static constexpr const uint16_t ElementTypeAddColour[] = {
MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_WALL
MapColour(PALETTE_INDEX_99), // TILE_ELEMENT_TYPE_LARGE_SCENERY
MapColour(PALETTE_INDEX_0), // TILE_ELEMENT_TYPE_BANNER
MapColour(PALETTE_INDEX_68), // TILE_ELEMENT_TYPE_CORRUPT
};
static uint16_t map_window_get_pixel_colour_peep(const CoordsXY& c)
@ -1445,7 +1443,7 @@ static uint16_t map_window_get_pixel_colour_peep(const CoordsXY& c)
int32_t tileElementType = tileElement->GetType() >> 2;
if (tileElementType >= maxSupportedTileElementType)
{
tileElementType = TILE_ELEMENT_TYPE_CORRUPT >> 2;
tileElementType = TILE_ELEMENT_TYPE_SURFACE >> 2;
}
colour &= ElementTypeMaskColour[tileElementType];
colour |= ElementTypeAddColour[tileElementType];

View File

@ -193,7 +193,7 @@ rct_window* window_research_open()
w->page = 0;
w->frame_no = 0;
w->disabled_widgets = 0;
research_update_uncompleted_types();
ResearchUpdateUncompletedTypes();
}
w->page = 0;

View File

@ -19,6 +19,9 @@
#include <openrct2/core/Guard.hpp>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/localisation/StringIds.h>
#include <openrct2/object/FootpathObject.h>
#include <openrct2/object/FootpathRailingsObject.h>
#include <openrct2/object/FootpathSurfaceObject.h>
#include <openrct2/object/TerrainEdgeObject.h>
#include <openrct2/object/TerrainSurfaceObject.h>
#include <openrct2/ride/RideData.h>
@ -65,7 +68,7 @@ enum WINDOW_TILE_INSPECTOR_WIDGET_IDX
WIDX_SPINNER_Y,
WIDX_SPINNER_Y_INCREASE,
WIDX_SPINNER_Y_DECREASE,
WIDX_BUTTON_CORRUPT,
WIDX_BUTTON_INVISIBLE,
WIDX_BUTTON_REMOVE,
WIDX_BUTTON_MOVE_UP,
WIDX_BUTTON_MOVE_DOWN,
@ -77,6 +80,7 @@ enum WINDOW_TILE_INSPECTOR_WIDGET_IDX
WIDX_COLUMN_BASEHEIGHT,
WIDX_COLUMN_CLEARANCEHEIGHT,
WIDX_COLUMN_DIRECTION,
WIDX_COLUMN_INVISIBLE,
WIDX_COLUMN_GHOSTFLAG,
WIDX_COLUMN_LASTFLAG,
WIDX_GROUPBOX_DETAILS,
@ -162,12 +166,6 @@ enum WINDOW_TILE_INSPECTOR_WIDGET_IDX
WIDX_BANNER_CHECK_BLOCK_SE,
WIDX_BANNER_CHECK_BLOCK_SW,
WIDX_BANNER_CHECK_BLOCK_NW,
// Corrupt
WIDX_CORRUPT_SPINNER_HEIGHT = PAGE_WIDGETS,
WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE,
WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE,
WIDX_CORRUPT_BUTTON_CLAMP,
};
static constexpr const rct_string_id WINDOW_TITLE = STR_TILE_INSPECTOR_TITLE;
@ -188,14 +186,16 @@ constexpr auto ToolbarButtonOffsetX = ScreenSize{ -24, 0 };
// List's column offsets
constexpr auto TypeColumnXY = ScreenCoordsXY{ 3, 42 };
constexpr auto TypeColumnSize = ScreenSize{ 272, 14 };
constexpr auto TypeColumnSize = ScreenSize{ 257, 14 };
constexpr auto BaseHeightColumnXY = TypeColumnXY + ScreenSize{ TypeColumnSize.width, 0 };
constexpr auto BaseHeightColumnSize = ScreenSize{ 30, 14 };
constexpr auto ClearanceHeightColumnXY = BaseHeightColumnXY + ScreenCoordsXY{ BaseHeightColumnSize.width, 0 };
constexpr auto ClearanceHeightColumnSize = ScreenSize{ 30, 14 };
constexpr auto DirectionColumnXY = ClearanceHeightColumnXY + ScreenCoordsXY{ ClearanceHeightColumnSize.width, 0 };
constexpr auto DirectionColumnSize = ScreenSize{ 15, 14 };
constexpr auto GhostFlagColumnXY = DirectionColumnXY + ScreenCoordsXY{ DirectionColumnSize.width, 0 };
constexpr auto InvisibleFlagColumnXY = DirectionColumnXY + ScreenCoordsXY{ DirectionColumnSize.width, 0 };
constexpr auto InvisibleFlagColumnSize = ScreenSize{ 15, 14 };
constexpr auto GhostFlagColumnXY = InvisibleFlagColumnXY + ScreenCoordsXY{ InvisibleFlagColumnSize.width, 0 };
constexpr auto GhostFlagColumnSize = ScreenSize{ 15, 14 };
constexpr auto LastFlagColumnXY = GhostFlagColumnXY + ScreenCoordsXY{ GhostFlagColumnSize.width, 0 };
constexpr auto LastFlagColumnSize = ScreenSize{ 32, 14 };
@ -232,7 +232,7 @@ constexpr ScreenCoordsXY CheckboxGroupOffset(
MakeSpinnerWidgets({20, 23}, {51, 12}, WindowWidgetType::Spinner, WindowColour::Secondary), /* Spinner X (3 widgets) */ \
MakeSpinnerWidgets({90, 23}, {51, 12}, WindowWidgetType::Spinner, WindowColour::Secondary), /* Spinner Y (3 widgets) */ \
/* Top buttons */ \
MakeWidget(ToolbarButtonAnchor, ToolbarButtonSize, WindowWidgetType::FlatBtn , WindowColour::Secondary, SPR_MAP, STR_INSERT_CORRUPT_TIP), /* Insert corrupt button */ \
MakeWidget(ToolbarButtonAnchor, ToolbarButtonSize, WindowWidgetType::FlatBtn , WindowColour::Secondary, SPR_MAP, STR_TILE_INSPECTOR_TOGGLE_INVISIBILITY_TIP), /* Toggle invisibility button */ \
MakeWidget(ToolbarButtonAnchor + ToolbarButtonOffsetX * 1, ToolbarButtonSize, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_DEMOLISH, STR_REMOVE_SELECTED_ELEMENT_TIP ), /* Remove button */ \
MakeWidget(ToolbarButtonAnchor + ToolbarButtonOffsetX * 2, ToolbarButtonHalfSize, WindowWidgetType::Button, WindowColour::Secondary, STR_UP, STR_MOVE_SELECTED_ELEMENT_UP_TIP), /* Move up */ \
MakeWidget(ToolbarButtonAnchor + ToolbarButtonOffsetX * 2 + ScreenSize{0, 12}, ToolbarButtonHalfSize, WindowWidgetType::Button, WindowColour::Secondary, STR_DOWN, STR_MOVE_SELECTED_ELEMENT_DOWN_TIP), /* Move down */ \
@ -245,6 +245,7 @@ constexpr ScreenCoordsXY CheckboxGroupOffset(
MakeWidget(BaseHeightColumnXY, BaseHeightColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_BASE_HEIGHT_SHORT, STR_TILE_INSPECTOR_BASE_HEIGHT), /* Base height */ \
MakeWidget(ClearanceHeightColumnXY, ClearanceHeightColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_CLEARANGE_HEIGHT_SHORT, STR_TILE_INSPECTOR_CLEARANCE_HEIGHT), /* Clearance height */ \
MakeWidget(DirectionColumnXY, DirectionColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_DIRECTION_SHORT, STR_TILE_INSPECTOR_DIRECTION), /* Direction */ \
MakeWidget(InvisibleFlagColumnXY, InvisibleFlagColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_INVISIBLE_SHORT, STR_TILE_INSPECTOR_FLAG_INVISIBLE), /* Invisible flag */ \
MakeWidget(GhostFlagColumnXY, GhostFlagColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_FLAG_GHOST_SHORT, STR_TILE_INSPECTOR_FLAG_GHOST), /* Ghost flag */ \
MakeWidget(LastFlagColumnXY, LastFlagColumnSize, WindowWidgetType::TableHeader, WindowColour::Secondary, STR_TILE_INSPECTOR_FLAG_LAST_SHORT, STR_TILE_INSPECTOR_FLAG_LAST), /* Last of tile flag */ \
/* Group boxes */ \
@ -274,7 +275,7 @@ static rct_widget SurfaceWidgets[] = {
};
constexpr int32_t NumPathProperties = 5;
constexpr int32_t NumPathDetails = 2;
constexpr int32_t NumPathDetails = 3;
constexpr int32_t PathPropertiesHeight = 16 + NumPathProperties * 21;
constexpr int32_t PathDetailsHeight = 20 + NumPathDetails * 11;
static rct_widget PathWidgets[] = {
@ -374,17 +375,6 @@ static rct_widget BannerWidgets[] = {
WIDGETS_END,
};
constexpr int32_t NumCorruptProperties = 2;
constexpr int32_t NumCorruptDetails = 0;
constexpr int32_t CorruptPropertiesHeight = 16 + NumCorruptProperties * 21;
constexpr int32_t CorruptDetailsHeight = 20 + NumCorruptDetails * 11;
static rct_widget CorruptWidgets[] = {
MAIN_TILE_INSPECTOR_WIDGETS,
MakeSpinnerWidgets(PropertyRowCol({ 12, 0 }, 0, 1), PropertyButtonSize, WindowWidgetType::Spinner, WindowColour::Secondary), // WIDX_CORRUPT_SPINNER_HEIGHT
MakeWidget(PropertyRowCol({ 12, 0 }, 1, 0), PropertyButtonSize, WindowWidgetType::Button, WindowColour::Secondary,STR_TILE_INSPECTOR_CLAMP_TO_NEXT, STR_TILE_INSPECTOR_CLAMP_TO_NEXT_TIP ), // WIDX_CORRUPT_BUTTON_CLAMP
WIDGETS_END,
};
static rct_widget *PageWidgets[] = {
DefaultWidgets,
SurfaceWidgets,
@ -395,7 +385,6 @@ static rct_widget *PageWidgets[] = {
WallWidgets,
LargeSceneryWidgets,
BannerWidgets,
CorruptWidgets,
};
// clang-format on
@ -430,7 +419,6 @@ static constexpr TileInspectorGroupboxSettings PageGroupBoxSettings[] = {
MakeGroupboxSettings(WallDetailsHeight, WallPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_WALL_INFO),
MakeGroupboxSettings(LargeSceneryDetailsHeight, LargeSceneryPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_BANNER_INFO),
MakeGroupboxSettings(BannerDetailsHeight, BannerPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_BANNER_INFO),
MakeGroupboxSettings(CorruptDetailsHeight, CorruptPropertiesHeight, STR_TILE_INSPECTOR_GROUPBOX_CORRUPT_INFO),
};
static constexpr int32_t ViewportInteractionFlags = EnumsToFlags(
@ -487,16 +475,15 @@ static rct_window_event_list TileInspectorWindowEvents([](auto& events) {
// clang-format off
static uint64_t PageEnabledWidgets[] = {
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_SURFACE_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_SURFACE_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_SURFACE_BUTTON_REMOVE_FENCES) | (1ULL << WIDX_SURFACE_BUTTON_RESTORE_FENCES) | (1ULL << WIDX_SURFACE_CHECK_CORNER_N) | (1ULL << WIDX_SURFACE_CHECK_CORNER_E) | (1ULL << WIDX_SURFACE_CHECK_CORNER_S) | (1ULL << WIDX_SURFACE_CHECK_CORNER_W) | (1ULL << WIDX_SURFACE_CHECK_DIAGONAL),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_PATH_CHECK_SLOPED) | (1ULL << WIDX_PATH_CHECK_BROKEN) | (1ULL << WIDX_PATH_CHECK_EDGE_N) | (1ULL << WIDX_PATH_CHECK_EDGE_NE) | (1ULL << WIDX_PATH_CHECK_EDGE_E) | (1ULL << WIDX_PATH_CHECK_EDGE_SE) | (1ULL << WIDX_PATH_CHECK_EDGE_S) | (1ULL << WIDX_PATH_CHECK_EDGE_SW) | (1ULL << WIDX_PATH_CHECK_EDGE_W) | (1ULL << WIDX_PATH_CHECK_EDGE_NW),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_TRACK_CHECK_APPLY_TO_ALL) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_TRACK_CHECK_CHAIN_LIFT) | (1ULL << WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED) | (1ULL << WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_SCENERY_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_SCENERY_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_N) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_E) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_S) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_W) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_N) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_E) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_S) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_W),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_ENTRANCE_BUTTON_MAKE_USABLE),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_WALL_DROPDOWN_SLOPE) | (1ULL << WIDX_WALL_DROPDOWN_SLOPE_BUTTON) | (1ULL << WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE) | (1ULL << WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_BANNER_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_BANNER_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_BANNER_CHECK_BLOCK_NE) | (1ULL << WIDX_BANNER_CHECK_BLOCK_SE) | (1ULL << WIDX_BANNER_CHECK_BLOCK_SW) | (1ULL << WIDX_BANNER_CHECK_BLOCK_NW),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_CORRUPT_BUTTON_CLAMP),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_INVISIBLE),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_INVISIBLE) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_SURFACE_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_SURFACE_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_SURFACE_BUTTON_REMOVE_FENCES) | (1ULL << WIDX_SURFACE_BUTTON_RESTORE_FENCES) | (1ULL << WIDX_SURFACE_CHECK_CORNER_N) | (1ULL << WIDX_SURFACE_CHECK_CORNER_E) | (1ULL << WIDX_SURFACE_CHECK_CORNER_S) | (1ULL << WIDX_SURFACE_CHECK_CORNER_W) | (1ULL << WIDX_SURFACE_CHECK_DIAGONAL),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_INVISIBLE) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_PATH_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_PATH_CHECK_SLOPED) | (1ULL << WIDX_PATH_CHECK_BROKEN) | (1ULL << WIDX_PATH_CHECK_EDGE_N) | (1ULL << WIDX_PATH_CHECK_EDGE_NE) | (1ULL << WIDX_PATH_CHECK_EDGE_E) | (1ULL << WIDX_PATH_CHECK_EDGE_SE) | (1ULL << WIDX_PATH_CHECK_EDGE_S) | (1ULL << WIDX_PATH_CHECK_EDGE_SW) | (1ULL << WIDX_PATH_CHECK_EDGE_W) | (1ULL << WIDX_PATH_CHECK_EDGE_NW),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_INVISIBLE) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_TRACK_CHECK_APPLY_TO_ALL) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_TRACK_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_TRACK_CHECK_CHAIN_LIFT) | (1ULL << WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED) | (1ULL << WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_INVISIBLE) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_SCENERY_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_SCENERY_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_N) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_E) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_S) | (1ULL << WIDX_SCENERY_CHECK_QUARTER_W) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_N) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_E) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_S) | (1ULL << WIDX_SCENERY_CHECK_COLLISION_W),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_INVISIBLE) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_ENTRANCE_BUTTON_MAKE_USABLE),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_INVISIBLE) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_WALL_DROPDOWN_SLOPE) | (1ULL << WIDX_WALL_DROPDOWN_SLOPE_BUTTON) | (1ULL << WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE) | (1ULL << WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_INVISIBLE) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE),
(1ULL << WIDX_CLOSE) | (1ULL << WIDX_BUTTON_INVISIBLE) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY) | (1ULL << WIDX_BANNER_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_BANNER_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_BANNER_CHECK_BLOCK_NE) | (1ULL << WIDX_BANNER_CHECK_BLOCK_SE) | (1ULL << WIDX_BANNER_CHECK_BLOCK_SW) | (1ULL << WIDX_BANNER_CHECK_BLOCK_NW),
};
static uint64_t PageHoldDownWidgets[] = {
@ -509,11 +496,10 @@ static uint64_t PageHoldDownWidgets[] = {
(1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_WALL_SPINNER_HEIGHT_DECREASE) | (1ULL << WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE) | (1ULL << WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE),
(1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE),
(1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_BANNER_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_BANNER_SPINNER_HEIGHT_DECREASE),
(1ULL << WIDX_SPINNER_X_INCREASE) | (1ULL << WIDX_SPINNER_X_DECREASE) | (1ULL << WIDX_SPINNER_Y_INCREASE) | (1ULL << WIDX_SPINNER_Y_DECREASE) | (1ULL << WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE) | (1ULL << WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE),
};
static uint64_t PageDisabledWidgets[] = {
(1ULL << WIDX_BUTTON_CORRUPT) | (1ULL << WIDX_BUTTON_MOVE_UP) | (1ULL << WIDX_BUTTON_MOVE_DOWN) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY),
(1ULL << WIDX_BUTTON_INVISIBLE) | (1ULL << WIDX_BUTTON_MOVE_UP) | (1ULL << WIDX_BUTTON_MOVE_DOWN) | (1ULL << WIDX_BUTTON_REMOVE) | (1ULL << WIDX_BUTTON_ROTATE) | (1ULL << WIDX_BUTTON_COPY),
0,
0,
0,
@ -522,7 +508,6 @@ static uint64_t PageDisabledWidgets[] = {
0,
(1ULL << WIDX_BUTTON_ROTATE),
0,
(1ULL << WIDX_BUTTON_ROTATE),
};
// clang-format on
@ -607,13 +592,6 @@ static void window_tile_inspector_load_tile(rct_window* w, TileElement* elementT
w->Invalidate();
}
static void window_tile_inspector_insert_corrupt_element(int32_t elementIndex)
{
openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range");
auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyInsertCorrupt, elementIndex);
GameActions::Execute(&modifyTile);
}
static void window_tile_inspector_remove_element(int32_t elementIndex)
{
openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range");
@ -787,9 +765,10 @@ static void window_tile_inspector_banner_toggle_block(int32_t elementIndex, int3
GameActions::Execute(&modifyTile);
}
static void window_tile_inspector_clamp_corrupt(int32_t elementIndex)
static void WindowTileInspectorToggleInvisibility(int32_t elementIndex)
{
auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::CorruptClamp, elementIndex);
openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range");
auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyToggleInvisilibity, elementIndex);
GameActions::Execute(&modifyTile);
}
@ -806,8 +785,8 @@ static void window_tile_inspector_mouseup(rct_window* w, rct_widgetindex widgetI
tool_cancel();
window_close(w);
return;
case WIDX_BUTTON_CORRUPT:
window_tile_inspector_insert_corrupt_element(windowTileInspectorSelectedIndex);
case WIDX_BUTTON_INVISIBLE:
WindowTileInspectorToggleInvisibility(windowTileInspectorSelectedIndex);
break;
case WIDX_BUTTON_REMOVE:
{
@ -980,14 +959,6 @@ static void window_tile_inspector_mouseup(rct_window* w, rct_widgetindex widgetI
} // switch widget index
break;
case TILE_ELEMENT_TYPE_CORRUPT:
switch (widgetIndex)
{
case WIDX_CORRUPT_BUTTON_CLAMP:
window_tile_inspector_clamp_corrupt(windowTileInspectorSelectedIndex);
break;
} // switch widget index
break;
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
case TILE_ELEMENT_TYPE_WALL:
default:
@ -1186,16 +1157,6 @@ static void window_tile_inspector_mousedown(rct_window* w, rct_widgetindex widge
} // switch widget index
break;
case TILE_ELEMENT_TYPE_CORRUPT:
switch (widgetIndex)
{
case WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE:
window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, 1);
break;
case WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE:
window_tile_inspector_base_height_offset(windowTileInspectorSelectedIndex, -1);
break;
} // switch widget index
default:
break;
}
@ -1402,6 +1363,7 @@ static void window_tile_inspector_invalidate(rct_window* w)
auto type = element->GetType();
switch (type)
{
default:
case TILE_ELEMENT_TYPE_SURFACE:
page = TileInspectorPage::Surface;
break;
@ -1426,10 +1388,6 @@ static void window_tile_inspector_invalidate(rct_window* w)
case TILE_ELEMENT_TYPE_BANNER:
page = TileInspectorPage::Banner;
break;
case TILE_ELEMENT_TYPE_CORRUPT:
default:
page = TileInspectorPage::Corrupt;
break;
}
}
@ -1748,16 +1706,6 @@ static void window_tile_inspector_invalidate(rct_window* w)
w, WIDX_BANNER_CHECK_BLOCK_NW,
!(tileElement->AsBanner()->GetAllowedEdges() & (1 << ((3 - get_current_rotation()) & 3))));
break;
case TILE_ELEMENT_TYPE_CORRUPT:
w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3;
w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3;
w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4;
w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4;
w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4;
w->widgets[WIDX_CORRUPT_BUTTON_CLAMP].top = GBBT(propertiesAnchor, 1);
w->widgets[WIDX_CORRUPT_BUTTON_CLAMP].bottom = GBBB(propertiesAnchor, 1);
break;
default:
break; // Nothing.
}
@ -1874,10 +1822,38 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi)
case TILE_ELEMENT_TYPE_PATH:
{
// Details
// Path name
auto ft = Formatter();
ft.Add<rct_string_id>(tileElement->AsPath()->GetSurfaceDescriptor()->Name);
DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_PATH_NAME, ft, { w->colours[1] });
auto pathEl = tileElement->AsPath();
auto footpathObj = pathEl->GetLegacyPathEntry();
if (footpathObj == nullptr)
{
// Surface name
auto surfaceObj = pathEl->GetSurfaceEntry();
if (surfaceObj != nullptr)
{
auto ft = Formatter();
ft.Add<rct_string_id>(surfaceObj->NameStringId);
DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_FOOTPATH_SURFACE_NAME, ft, { COLOUR_WHITE });
}
// Railings name
auto railingsObj = pathEl->GetRailingsEntry();
if (railingsObj != nullptr)
{
auto ft = Formatter();
ft.Add<rct_string_id>(railingsObj->NameStringId);
DrawTextBasic(
dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_FOOTPATH_RAILINGS_NAME, ft,
{ COLOUR_WHITE });
}
}
else
{
// Legacy path name
auto footpathEntry = reinterpret_cast<const rct_footpath_entry*>(footpathObj->GetLegacyData());
auto ft = Formatter();
ft.Add<rct_string_id>(footpathEntry->string_idx);
DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_PATH_NAME, ft, { COLOUR_WHITE });
}
// Path addition
if (tileElement->AsPath()->HasAddition())
@ -1887,15 +1863,18 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi)
rct_string_id additionNameId = pathBitEntry != nullptr
? pathBitEntry->name
: static_cast<rct_string_id>(STR_UNKNOWN_OBJECT_TYPE);
ft = Formatter();
auto ft = Formatter();
ft.Add<rct_string_id>(additionNameId);
DrawTextBasic(
dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS, ft, { w->colours[1] });
dpi, screenCoords + ScreenCoordsXY{ 0, 2 * 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS, ft,
{ COLOUR_WHITE });
}
else
{
DrawTextBasic(
dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS_NONE, {},
{ w->colours[1] });
dpi, screenCoords + ScreenCoordsXY{ 0, 2 * 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS_NONE, {},
{ COLOUR_WHITE });
}
// Properties
// Raise / lower label
@ -1905,7 +1884,7 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi)
// Current base height
screenCoords.x = w->windowPos.x + w->widgets[WIDX_PATH_SPINNER_HEIGHT].left + 3;
ft = Formatter();
auto ft = Formatter();
ft.Add<int32_t>(tileElement->base_height);
DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
@ -2258,22 +2237,6 @@ static void window_tile_inspector_paint(rct_window* w, rct_drawpixelinfo* dpi)
DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BANNER_BLOCKED_PATHS, {}, { w->colours[1] });
break;
}
case TILE_ELEMENT_TYPE_CORRUPT:
{
// Properties
// Raise / lower label
screenCoords.y = w->windowPos.y + w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT].top;
DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] });
// Current base height
screenCoords.x = w->windowPos.x + w->widgets[WIDX_CORRUPT_SPINNER_HEIGHT].left + 3;
auto ft = Formatter();
ft.Add<int32_t>(tileElement->base_height);
DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] });
break;
}
default:
{
break;
@ -2365,14 +2328,13 @@ static void window_tile_inspector_scrollpaint(rct_window* w, rct_drawpixelinfo*
tileElement->AsBanner()->GetIndex());
typeName = buffer;
break;
case TILE_ELEMENT_TYPE_CORRUPT:
// fall-through
default:
snprintf(buffer, sizeof(buffer), "%s (%d)", language_get_string(STR_UNKNOWN_OBJECT_TYPE), type);
typeName = buffer;
}
const int32_t clearanceHeight = tileElement->clearance_height;
const bool invisible = tileElement->IsInvisible();
const bool ghost = tileElement->IsGhost();
const bool last = tileElement->IsLastForTile();
@ -2406,6 +2368,10 @@ static void window_tile_inspector_scrollpaint(rct_window* w, rct_drawpixelinfo*
ft = Formatter();
ft.Add<rct_string_id>(STR_STRING);
ft.Add<char*>(CheckBoxMarkString);
if (invisible)
{
DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ InvisibleFlagColumnXY.x, 0 }, stringFormat, ft);
}
if (ghost)
{
DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ GhostFlagColumnXY.x, 0 }, stringFormat, ft);

View File

@ -112,7 +112,7 @@ rct_window* window_title_menu_open()
static void window_title_menu_scenarioselect_callback(const utf8* path)
{
context_load_park_from_file(path);
OpenRCT2::GetContext()->LoadParkFromFile(path, false, true);
game_load_scripts();
}

View File

@ -530,7 +530,7 @@ static void window_top_toolbar_mousedown(rct_window* w, rct_widgetindex widgetIn
static void window_top_toolbar_scenarioselect_callback(const utf8* path)
{
window_close_by_class(WC_EDITOR_OBJECT_SELECTION);
context_load_park_from_file(path);
GetContext()->LoadParkFromFile(path, false, true);
}
/**

View File

@ -495,7 +495,6 @@ namespace OpenRCT2
#endif
}
gCurrentTicks = 0;
input_reset_place_obj_modifier();
viewport_init_all();
@ -573,7 +572,8 @@ namespace OpenRCT2
_drawingEngine = nullptr;
}
bool LoadParkFromFile(const std::string& path, bool loadTitleScreenOnFail) final override
bool LoadParkFromFile(
const std::string& path, bool loadTitleScreenOnFail = false, bool asScenario = false) final override
{
log_verbose("Context::LoadParkFromFile(%s)", path.c_str());
try
@ -582,7 +582,7 @@ namespace OpenRCT2
{
auto data = DecryptSea(fs::u8path(path));
auto ms = MemoryStream(data.data(), data.size(), MEMORY_ACCESS::READ);
if (!LoadParkFromStream(&ms, path, loadTitleScreenOnFail))
if (!LoadParkFromStream(&ms, path, loadTitleScreenOnFail, asScenario))
{
throw std::runtime_error(".sea file may have been renamed.");
}
@ -590,7 +590,7 @@ namespace OpenRCT2
}
auto fs = FileStream(path, FILE_MODE_OPEN);
if (!LoadParkFromStream(&fs, path, loadTitleScreenOnFail))
if (!LoadParkFromStream(&fs, path, loadTitleScreenOnFail, asScenario))
{
return false;
}
@ -609,7 +609,9 @@ namespace OpenRCT2
return false;
}
bool LoadParkFromStream(IStream* stream, const std::string& path, bool loadTitleScreenFirstOnFail) final override
bool LoadParkFromStream(
IStream* stream, const std::string& path, bool loadTitleScreenFirstOnFail = false,
bool asScenario = false) final override
{
try
{
@ -619,13 +621,17 @@ namespace OpenRCT2
throw std::runtime_error("Unable to detect file type");
}
if (info.Type != FILE_TYPE::SAVED_GAME && info.Type != FILE_TYPE::SCENARIO)
if (info.Type != FILE_TYPE::PARK && info.Type != FILE_TYPE::SAVED_GAME && info.Type != FILE_TYPE::SCENARIO)
{
throw std::runtime_error("Invalid file type.");
}
std::unique_ptr<IParkImporter> parkImporter;
if (info.Version <= FILE_TYPE_S4_CUTOFF)
if (info.Type == FILE_TYPE::PARK)
{
parkImporter = ParkImporter::CreateParkFile(*_objectRepository);
}
else if (info.Version <= FILE_TYPE_S4_CUTOFF)
{
// Save is an S4 (RCT1 format)
parkImporter = ParkImporter::CreateS4();
@ -656,7 +662,7 @@ namespace OpenRCT2
#ifndef DISABLE_NETWORK
bool sendMap = false;
#endif
if (info.Type == FILE_TYPE::SAVED_GAME)
if (!asScenario && (info.Type == FILE_TYPE::PARK || info.Type == FILE_TYPE::SAVED_GAME))
{
#ifndef DISABLE_NETWORK
if (_network.GetMode() == NETWORK_MODE_CLIENT)

View File

@ -141,9 +141,11 @@ namespace OpenRCT2
virtual bool Initialise() abstract;
virtual void InitialiseDrawingEngine() abstract;
virtual void DisposeDrawingEngine() abstract;
virtual bool LoadParkFromFile(const std::string& path, bool loadTitleScreenOnFail = false) abstract;
virtual bool LoadParkFromFile(
const std::string& path, bool loadTitleScreenOnFail = false, bool asScenario = false) abstract;
virtual bool LoadParkFromStream(
IStream* stream, const std::string& path, bool loadTitleScreenFirstOnFail = false) abstract;
IStream* stream, const std::string& path, bool loadTitleScreenFirstOnFail = false,
bool asScenario = false) abstract;
virtual void WriteLine(const std::string& s) abstract;
virtual void WriteErrorLine(const std::string& s) abstract;
virtual void Finish() abstract;

View File

@ -24,6 +24,7 @@
#include "localisation/Localisation.h"
#include "localisation/LocalisationService.h"
#include "management/NewsItem.h"
#include "object/DefaultObjects.h"
#include "object/ObjectManager.h"
#include "object/ObjectRepository.h"
#include "peep/Guest.h"
@ -60,6 +61,7 @@ namespace Editor
static bool LoadLandscapeFromSC4(const char* path);
static void FinaliseMainView();
static bool ReadS6(const char* path);
static bool ReadPark(const char* path);
static void ClearMapForEditing(bool fromSave);
static void object_list_load()
@ -76,7 +78,11 @@ namespace Editor
objectRepository.LoadOrConstruct(localisationService.GetCurrentLanguage());
// Reset loaded objects to just defaults
objectManager.LoadDefaultObjects();
// Load minimum required objects (like surface and edge)
for (const auto& entry : MinimumRequiredObjects)
{
objectManager.LoadObject(entry);
}
}
/**
@ -86,7 +92,6 @@ namespace Editor
void Load()
{
OpenRCT2::Audio::StopAll();
object_manager_unload_all_objects();
object_list_load();
OpenRCT2::GetContext()->GetGameState()->InitAll(150);
gScreenFlags = SCREEN_FLAGS_SCENARIO_EDITOR;
@ -232,6 +237,8 @@ namespace Editor
return LoadLandscapeFromSC4(path);
case FILE_EXTENSION_SV4:
return LoadLandscapeFromSV4(path);
case FILE_EXTENSION_PARK:
return ReadPark(path);
default:
return false;
}
@ -298,6 +305,32 @@ namespace Editor
return true;
}
static bool ReadPark(const char* path)
{
try
{
auto context = GetContext();
auto& objManager = context->GetObjectManager();
auto importer = ParkImporter::CreateParkFile(context->GetObjectRepository());
auto loadResult = importer->Load(path);
objManager.LoadObjects(loadResult.RequiredObjects);
importer->Import();
ClearMapForEditing(true);
gEditorStep = EditorStep::LandscapeEditor;
gScreenAge = 0;
gScreenFlags = SCREEN_FLAGS_SCENARIO_EDITOR;
viewport_init_all();
context_open_window_view(WV_EDITOR_MAIN);
FinaliseMainView();
return true;
}
catch (const std::exception&)
{
return false;
}
}
static void ClearMapForEditing(bool fromSave)
{
map_remove_all_rides();
@ -323,8 +356,6 @@ namespace Editor
gGuestChangeModifier = 0;
if (fromSave)
{
research_populate_list_random();
if (gParkFlags & PARK_FLAGS_NO_MONEY)
{
gParkFlags |= PARK_FLAGS_NO_MONEY_SCENARIO;
@ -454,9 +485,17 @@ namespace Editor
if (!isTrackDesignerManager)
{
if (!editor_check_object_group_at_least_one_selected(ObjectType::Paths))
if (!editor_check_object_group_at_least_one_surface_selected(false))
{
return { ObjectType::Paths, STR_AT_LEAST_ONE_PATH_OBJECT_MUST_BE_SELECTED };
return { ObjectType::FootpathSurface, STR_AT_LEAST_ONE_FOOTPATH_NON_QUEUE_SURFACE_OBJECT_MUST_BE_SELECTED };
}
if (!editor_check_object_group_at_least_one_surface_selected(true))
{
return { ObjectType::FootpathSurface, STR_AT_LEAST_ONE_FOOTPATH_QUEUE_SURFACE_OBJECT_MUST_BE_SELECTED };
}
if (!editor_check_object_group_at_least_one_selected(ObjectType::FootpathRailings))
{
return { ObjectType::FootpathRailings, STR_AT_LEAST_ONE_FOOTPATH_RAILING_OBJECT_MUST_BE_SELECTED };
}
}
@ -547,12 +586,16 @@ namespace Editor
void SetSelectedObject(ObjectType objectType, size_t index, uint32_t flags)
{
auto& list = _editorSelectedObjectFlags[EnumValue(objectType)];
if (list.size() <= index)
if (index != OBJECT_ENTRY_INDEX_NULL)
{
list.resize(index + 1);
assert(static_cast<int32_t>(objectType) < object_entry_group_counts[EnumValue(ObjectType::Paths)]);
auto& list = _editorSelectedObjectFlags[EnumValue(objectType)];
if (list.size() <= index)
{
list.resize(index + 1);
}
list[index] |= flags;
}
list[index] |= flags;
}
} // namespace Editor

View File

@ -135,45 +135,73 @@ void setup_in_use_selection_flags()
{
default:
case TILE_ELEMENT_TYPE_SURFACE:
{
auto surfaceEl = iter.element->AsSurface();
auto surfaceIndex = surfaceEl->GetSurfaceStyle();
auto edgeIndex = surfaceEl->GetEdgeStyle();
Editor::SetSelectedObject(ObjectType::TerrainSurface, surfaceIndex, OBJECT_SELECTION_FLAG_SELECTED);
Editor::SetSelectedObject(ObjectType::TerrainEdge, edgeIndex, OBJECT_SELECTION_FLAG_SELECTED);
break;
}
case TILE_ELEMENT_TYPE_TRACK:
break;
case TILE_ELEMENT_TYPE_PATH:
type = iter.element->AsPath()->GetLegacyPathEntryIndex();
assert(type < object_entry_group_counts[EnumValue(ObjectType::Paths)]);
Editor::SetSelectedObject(ObjectType::Paths, type, OBJECT_SELECTION_FLAG_SELECTED);
if (iter.element->AsPath()->HasAddition())
{
auto footpathEl = iter.element->AsPath();
auto legacyPathEntryIndex = footpathEl->GetLegacyPathEntryIndex();
if (legacyPathEntryIndex == OBJECT_ENTRY_INDEX_NULL)
{
uint8_t path_additions = iter.element->AsPath()->GetAdditionEntryIndex();
Editor::SetSelectedObject(ObjectType::PathBits, path_additions, OBJECT_SELECTION_FLAG_SELECTED);
auto surfaceEntryIndex = footpathEl->GetSurfaceEntryIndex();
auto railingEntryIndex = footpathEl->GetRailingsEntryIndex();
Editor::SetSelectedObject(ObjectType::FootpathSurface, surfaceEntryIndex, OBJECT_SELECTION_FLAG_SELECTED);
Editor::SetSelectedObject(ObjectType::FootpathRailings, railingEntryIndex, OBJECT_SELECTION_FLAG_SELECTED);
}
else
{
Editor::SetSelectedObject(ObjectType::Paths, legacyPathEntryIndex, OBJECT_SELECTION_FLAG_SELECTED);
}
if (footpathEl->HasAddition())
{
auto pathAdditionEntryIndex = footpathEl->GetAdditionEntryIndex();
Editor::SetSelectedObject(ObjectType::PathBits, pathAdditionEntryIndex, OBJECT_SELECTION_FLAG_SELECTED);
}
break;
}
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
type = iter.element->AsSmallScenery()->GetEntryIndex();
assert(type < object_entry_group_counts[EnumValue(ObjectType::SmallScenery)]);
Editor::SetSelectedObject(ObjectType::SmallScenery, type, OBJECT_SELECTION_FLAG_SELECTED);
break;
case TILE_ELEMENT_TYPE_ENTRANCE:
if (iter.element->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE)
break;
// Skip if not the middle part
if (iter.element->AsEntrance()->GetSequenceIndex() != 0)
{
auto parkEntranceEl = iter.element->AsEntrance();
if (parkEntranceEl->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE)
break;
Editor::SetSelectedObject(ObjectType::ParkEntrance, 0, OBJECT_SELECTION_FLAG_SELECTED);
type = iter.element->AsEntrance()->GetLegacyPathEntryIndex();
assert(type < object_entry_group_counts[EnumValue(ObjectType::Paths)]);
Editor::SetSelectedObject(ObjectType::Paths, type, OBJECT_SELECTION_FLAG_SELECTED);
// Skip if not the middle part
if (parkEntranceEl->GetSequenceIndex() != 0)
break;
auto legacyPathEntryIndex = parkEntranceEl->GetLegacyPathEntryIndex();
if (legacyPathEntryIndex == OBJECT_ENTRY_INDEX_NULL)
{
auto surfaceEntryIndex = parkEntranceEl->GetSurfaceEntryIndex();
Editor::SetSelectedObject(ObjectType::FootpathSurface, surfaceEntryIndex, OBJECT_SELECTION_FLAG_SELECTED);
}
else
{
Editor::SetSelectedObject(ObjectType::Paths, legacyPathEntryIndex, OBJECT_SELECTION_FLAG_SELECTED);
}
break;
}
case TILE_ELEMENT_TYPE_WALL:
type = iter.element->AsWall()->GetEntryIndex();
assert(type < object_entry_group_counts[EnumValue(ObjectType::Walls)]);
Editor::SetSelectedObject(ObjectType::Walls, type, OBJECT_SELECTION_FLAG_SELECTED);
break;
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
type = iter.element->AsLargeScenery()->GetEntryIndex();
assert(type < object_entry_group_counts[EnumValue(ObjectType::LargeScenery)]);
Editor::SetSelectedObject(ObjectType::LargeScenery, type, OBJECT_SELECTION_FLAG_SELECTED);
break;
case TILE_ELEMENT_TYPE_BANNER:
@ -182,7 +210,6 @@ void setup_in_use_selection_flags()
if (banner != nullptr)
{
type = banner->type;
assert(type < object_entry_group_counts[EnumValue(ObjectType::Banners)]);
Editor::SetSelectedObject(ObjectType::Banners, type, OBJECT_SELECTION_FLAG_SELECTED);
}
break;
@ -192,8 +219,9 @@ void setup_in_use_selection_flags()
for (auto& ride : GetRideManager())
{
ObjectEntryIndex type = ride.subtype;
Editor::SetSelectedObject(ObjectType::Ride, type, OBJECT_SELECTION_FLAG_SELECTED);
Editor::SetSelectedObject(ObjectType::Ride, ride.subtype, OBJECT_SELECTION_FLAG_SELECTED);
Editor::SetSelectedObject(ObjectType::Station, ride.entrance_style, OBJECT_SELECTION_FLAG_SELECTED);
Editor::SetSelectedObject(ObjectType::Music, ride.music, OBJECT_SELECTION_FLAG_SELECTED);
}
// Apply selected object status for hacked vehicles that may not have an associated ride
@ -214,19 +242,19 @@ void setup_in_use_selection_flags()
}
}
int32_t numObjects = static_cast<int32_t>(object_repository_get_items_count());
const ObjectRepositoryItem* items = object_repository_get_items();
for (int32_t i = 0; i < numObjects; i++)
auto numObjects = object_repository_get_items_count();
const auto* items = object_repository_get_items();
for (size_t i = 0; i < numObjects; i++)
{
uint8_t* selectionFlags = &_objectSelectionFlags[i];
const ObjectRepositoryItem* item = &items[i];
auto* selectionFlags = &_objectSelectionFlags[i];
const auto* item = &items[i];
*selectionFlags &= ~OBJECT_SELECTION_FLAG_IN_USE;
ObjectType entryType;
ObjectEntryIndex entryIndex;
if (find_object_in_entry_group(&item->ObjectEntry, &entryType, &entryIndex))
if (item->LoadedObject != nullptr)
{
auto flags = Editor::GetSelectedObjectFlags(entryType, entryIndex);
auto objectType = item->LoadedObject->GetObjectType();
auto entryIndex = objectMgr.GetLoadedObjectEntryIndex(item->LoadedObject.get());
auto flags = Editor::GetSelectedObjectFlags(objectType, entryIndex);
if (flags & OBJECT_SELECTION_FLAG_SELECTED)
{
*selectionFlags |= OBJECT_SELECTION_FLAG_IN_USE | OBJECT_SELECTION_FLAG_SELECTED;
@ -300,7 +328,7 @@ void editor_object_flags_free()
*
* rct2: 0x00685791
*/
static void remove_selected_objects_from_research(ObjectEntryDescriptor& descriptor)
static void remove_selected_objects_from_research(const ObjectEntryDescriptor& descriptor)
{
auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
auto obj = objManager.GetLoadedObject(descriptor);

View File

@ -9,6 +9,7 @@
#include "FileClassifier.h"
#include "ParkFile.h"
#include "core/Console.hpp"
#include "core/FileStream.h"
#include "core/Path.hpp"
@ -17,6 +18,7 @@
#include "scenario/Scenario.h"
#include "util/SawyerCoding.h"
static bool TryClassifyAsPark(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
static bool TryClassifyAsS6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
static bool TryClassifyAsS4(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
static bool TryClassifyAsTD4_TD6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result);
@ -41,6 +43,12 @@ bool TryClassifyFile(OpenRCT2::IStream* stream, ClassifiedFileInfo* result)
// between them is to decode it. Decoding however is currently not protected
// against invalid compression data for that decoding algorithm and will crash.
// Park detection
if (TryClassifyAsPark(stream, result))
{
return true;
}
// S6 detection
if (TryClassifyAsS6(stream, result))
{
@ -62,6 +70,29 @@ bool TryClassifyFile(OpenRCT2::IStream* stream, ClassifiedFileInfo* result)
return false;
}
static bool TryClassifyAsPark(OpenRCT2::IStream* stream, ClassifiedFileInfo* result)
{
bool success = false;
uint64_t originalPosition = stream->GetPosition();
try
{
auto magic = stream->ReadValue<uint32_t>();
if (magic == OpenRCT2::PARK_FILE_MAGIC)
{
result->Type = FILE_TYPE::PARK;
result->Version = 0;
success = true;
}
}
catch (const std::exception& e)
{
success = false;
log_verbose(e.what());
}
stream->SetPosition(originalPosition);
return success;
}
static bool TryClassifyAsS6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result)
{
bool success = false;
@ -183,5 +214,7 @@ uint32_t get_file_extension_type(const utf8* path)
return FILE_EXTENSION_SV6;
if (String::Equals(extension, ".td6", true))
return FILE_EXTENSION_TD6;
if (String::Equals(extension, ".park", true))
return FILE_EXTENSION_PARK;
return FILE_EXTENSION_UNKNOWN;
}

View File

@ -21,6 +21,7 @@ enum
FILE_EXTENSION_SC6,
FILE_EXTENSION_SV6,
FILE_EXTENSION_TD6,
FILE_EXTENSION_PARK,
};
#include <string>
@ -37,6 +38,7 @@ enum class FILE_TYPE
SAVED_GAME,
SCENARIO,
TRACK_DESIGN,
PARK,
};
struct ClassifiedFileInfo

View File

@ -501,7 +501,7 @@ void game_fix_save_vars()
}
}
research_fix();
ResearchFix();
// Fix banner list pointing to NULL map elements
banner_reset_broken_index();
@ -514,6 +514,8 @@ void game_fix_save_vars()
// Fix gParkEntrance locations for which the tile_element no longer exists
fix_park_entrance_locations();
staff_update_greyed_patrol_areas();
}
void game_load_init()
@ -824,7 +826,7 @@ void game_load_or_quit_no_save_prompt()
void start_silent_record()
{
std::string name = Path::Combine(
OpenRCT2::GetContext()->GetPlatformEnvironment()->GetDirectoryPath(OpenRCT2::DIRBASE::USER), "debug_replay.sv6r");
OpenRCT2::GetContext()->GetPlatformEnvironment()->GetDirectoryPath(OpenRCT2::DIRBASE::USER), "debug_replay.parkrep");
auto* replayManager = OpenRCT2::GetContext()->GetReplayManager();
if (replayManager->StartRecording(name, OpenRCT2::k_MaxReplayTicks, OpenRCT2::IReplayManager::RecordType::SILENT))
{

View File

@ -80,6 +80,9 @@ void GameState::InitAll(int32_t mapSize)
context_broadcast_intent(&intent);
load_palette();
CheatsReset();
ClearRestrictedScenery();
}
/**

4593
src/openrct2/ParkFile.cpp Normal file

File diff suppressed because it is too large Load Diff

22
src/openrct2/ParkFile.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <string_view>
#include <vector>
struct ObjectRepositoryItem;
namespace OpenRCT2
{
constexpr uint32_t PARK_FILE_MAGIC = 0x4B524150; // PARK
struct IStream;
} // namespace OpenRCT2
class ParkFileExporter
{
public:
std::vector<const ObjectRepositoryItem*> ExportObjectsList;
void Export(std::string_view path);
void Export(OpenRCT2::IStream& stream);
};

View File

@ -62,6 +62,7 @@ namespace ParkImporter
[[nodiscard]] std::unique_ptr<IParkImporter> Create(const std::string& hintPath);
[[nodiscard]] std::unique_ptr<IParkImporter> CreateS4();
[[nodiscard]] std::unique_ptr<IParkImporter> CreateS6(IObjectRepository& objectRepository);
[[nodiscard]] std::unique_ptr<IParkImporter> CreateParkFile(IObjectRepository& objectRepository);
bool ExtensionIsRCT1(const std::string& extension);
bool ExtensionIsScenario(const std::string& extension);

View File

@ -13,6 +13,7 @@
#include "Game.h"
#include "GameStateSnapshots.h"
#include "OpenRCT2.h"
#include "ParkFile.h"
#include "ParkImporter.h"
#include "PlatformEnvironment.h"
#include "actions/FootpathPlaceAction.h"
@ -28,7 +29,7 @@
#include "management/NewsItem.h"
#include "object/ObjectManager.h"
#include "object/ObjectRepository.h"
#include "rct2/S6Exporter.h"
#include "scenario/Scenario.h"
#include "world/EntityTweener.h"
#include "world/Park.h"
#include "world/Sprite.h"
@ -98,7 +99,7 @@ namespace OpenRCT2
class ReplayManager final : public IReplayManager
{
static constexpr uint16_t ReplayVersion = 4;
static constexpr uint16_t ReplayVersion = 10;
static constexpr uint32_t ReplayMagic = 0x5243524F; // ORCR.
static constexpr int ReplayCompressionLevel = 9;
static constexpr int NormalRecordingChecksumTicks = 1;
@ -246,10 +247,9 @@ namespace OpenRCT2
auto& objManager = context->GetObjectManager();
auto objects = objManager.GetPackableObjects();
auto s6exporter = std::make_unique<S6Exporter>();
s6exporter->ExportObjectsList = objects;
s6exporter->Export();
s6exporter->SaveGame(&replayData->parkData);
auto exporter = std::make_unique<ParkFileExporter>();
exporter->ExportObjectsList = objects;
exporter->Export(replayData->parkData);
replayData->timeRecorded = std::chrono::seconds(std::time(nullptr)).count();
@ -521,7 +521,7 @@ namespace OpenRCT2
auto context = GetContext();
auto& objManager = context->GetObjectManager();
auto importer = ParkImporter::CreateS6(context->GetObjectRepository());
auto importer = ParkImporter::CreateParkFile(context->GetObjectRepository());
auto loadResult = importer->LoadFromStream(&data.parkData, false);
objManager.LoadObjects(loadResult.RequiredObjects);
@ -534,12 +534,6 @@ namespace OpenRCT2
DataSerialiser parkParamsDs(false, data.parkParams);
SerialiseParkParameters(parkParamsDs);
// New cheats might not be serialised, make sure they are using their defaults.
CheatsReset();
DataSerialiser cheatDataDs(false, data.cheatData);
SerialiseCheats(cheatDataDs);
game_load_init();
fix_invalid_vehicle_sprite_sizes();
}
@ -609,9 +603,9 @@ namespace OpenRCT2
MemoryStream stream;
std::string fileName = file;
if (fileName.size() < 5 || fileName.substr(fileName.size() - 5) != ".sv6r")
if (fileName.size() < 5 || fileName.substr(fileName.size() - 5) != ".parkrep")
{
fileName += ".sv6r";
fileName += ".parkrep";
}
std::string outPath = GetContext()->GetPlatformEnvironment()->GetDirectoryPath(DIRBASE::USER, DIRID::REPLAY);

View File

@ -286,7 +286,13 @@ GameActions::Result::Ptr RideCreateAction::Execute() const
ride->income_per_hour = MONEY64_UNDEFINED;
ride->profit = MONEY64_UNDEFINED;
ride->connected_message_throttle = 0;
ride->entrance_style = 0;
ride->entrance_style = OBJECT_ENTRY_INDEX_NULL;
if (rtd.HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT))
{
ride->entrance_style = gLastEntranceStyle;
}
ride->num_block_brakes = 0;
ride->guests_favourite = 0;

View File

@ -111,13 +111,6 @@ GameActions::Result::Ptr StaffHireNewAction::QueryExecute(bool execute) const
}
}
auto numStaff = GetEntityListCount(EntityType::Staff);
if (numStaff == STAFF_MAX_COUNT)
{
// Too many staff members exist already.
return MakeResult(GameActions::Status::NoFreeElements, STR_CANT_HIRE_NEW_STAFF, STR_TOO_MANY_STAFF_IN_GAME);
}
Staff* newPeep = CreateEntity<Staff>();
if (newPeep == nullptr)
{

View File

@ -67,10 +67,10 @@ GameActions::Result::Ptr TileModifyAction::QueryExecute(bool isExecuting) const
res = TileInspector::SwapElementsAt(_loc, firstIndex, secondIndex, isExecuting);
break;
}
case TileModifyType::AnyInsertCorrupt:
case TileModifyType::AnyToggleInvisilibity:
{
const auto elementIndex = _value1;
res = TileInspector::InsertCorruptElementAt(_loc, elementIndex, isExecuting);
res = TileInspector::ToggleInvisibilityOfElementAt(_loc, elementIndex, isExecuting);
break;
}
case TileModifyType::AnyRotate:
@ -210,12 +210,6 @@ GameActions::Result::Ptr TileModifyAction::QueryExecute(bool isExecuting) const
res = TileInspector::BannerToggleBlockingEdge(_loc, elementIndex, edgeIndex, isExecuting);
break;
}
case TileModifyType::CorruptClamp:
{
const auto elementIndex = _value1;
res = TileInspector::CorruptClamp(_loc, elementIndex, isExecuting);
break;
}
default:
log_error("invalid instruction");
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);

View File

@ -15,7 +15,7 @@ enum class TileModifyType : uint8_t
{
AnyRemove,
AnySwap,
AnyInsertCorrupt,
AnyToggleInvisilibity,
AnyRotate,
AnyPaste,
AnySort,
@ -37,7 +37,6 @@ enum class TileModifyType : uint8_t
ScenerySetQuarterLocation,
ScenerySetQuarterCollision,
BannerToggleBlockingEdge,
CorruptClamp,
Count,
};

View File

@ -14,6 +14,7 @@
#include "../management/Research.h"
#include "../object/ObjectManager.h"
#include "../object/ObjectRepository.h"
#include "../rct12/RCT12.h"
#include "../ride/TrackDesign.h"
#include "RideCreateAction.h"
#include "RideDemolishAction.h"
@ -240,7 +241,12 @@ GameActions::Result::Ptr TrackDesignAction::Execute() const
ride->lifecycle_flags |= RIDE_LIFECYCLE_NOT_CUSTOM_DESIGN;
ride->colour_scheme_type = _td.colour_scheme;
ride->entrance_style = _td.entrance_style;
auto stationIdentifier = GetStationIdentifierFromStyle(_td.entrance_style);
ride->entrance_style = objManager.GetLoadedObjectEntryIndex(stationIdentifier);
if (ride->entrance_style == OBJECT_ENTRY_INDEX_NULL)
{
ride->entrance_style = gLastEntranceStyle;
}
for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++)
{

View File

@ -7,14 +7,17 @@
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "../Context.h"
#include "../FileClassifier.h"
#include "../OpenRCT2.h"
#include "../ParkFile.h"
#include "../ParkImporter.h"
#include "../common.h"
#include "../core/Console.hpp"
#include "../core/Path.hpp"
#include "../interface/Window.h"
#include "../rct2/S6Exporter.h"
#include "../object/ObjectManager.h"
#include "../scenario/Scenario.h"
#include "CommandLine.hpp"
#include <memory>
@ -55,9 +58,9 @@ exitcode_t CommandLine::HandleCommandConvert(CommandLineArgEnumerator* enumerato
uint32_t destinationFileType = get_file_extension_type(destinationPath);
// Validate target type
if (destinationFileType != FILE_EXTENSION_SC6 && destinationFileType != FILE_EXTENSION_SV6)
if (destinationFileType != FILE_EXTENSION_PARK)
{
Console::Error::WriteLine("Only conversion to .SC6 or .SV4 is supported.");
Console::Error::WriteLine("Only conversion to .PARK is supported.");
return EXITCODE_FAIL;
}
@ -90,11 +93,18 @@ exitcode_t CommandLine::HandleCommandConvert(CommandLineArgEnumerator* enumerato
WriteConvertFromAndToMessage(sourceFileType, destinationFileType);
gOpenRCT2Headless = true;
auto context = OpenRCT2::CreateContext();
context->Initialise();
auto& objManager = context->GetObjectManager();
try
{
auto importer = ParkImporter::Create(sourcePath);
importer->Load(sourcePath);
auto loadResult = importer->Load(sourcePath);
objManager.LoadObjects(loadResult.RequiredObjects);
importer->Import();
}
catch (const std::exception& ex)
@ -111,21 +121,13 @@ exitcode_t CommandLine::HandleCommandConvert(CommandLineArgEnumerator* enumerato
try
{
auto exporter = std::make_unique<S6Exporter>();
auto exporter = std::make_unique<ParkFileExporter>();
// HACK remove the main window so it saves the park with the
// correct initial view
window_close_by_class(WC_MAIN_WINDOW);
exporter->Export();
if (destinationFileType == FILE_EXTENSION_SC6)
{
exporter->SaveScenario(destinationPath);
}
else
{
exporter->SaveGame(destinationPath);
}
exporter->Export(destinationPath);
}
catch (const std::exception& ex)
{
@ -157,6 +159,8 @@ static const utf8* GetFileTypeFriendlyName(uint32_t fileType)
return "RollerCoaster Tycoon 2 scenario";
case FILE_EXTENSION_SV6:
return "RollerCoaster Tycoon 2 saved game";
case FILE_EXTENSION_PARK:
return "OpenRCT2 park";
}
assert(false);

View File

@ -809,7 +809,7 @@ template<> struct DataSerializerTraits_t<PeepThought>
{
char msg[128] = {};
snprintf(
msg, sizeof(msg), "PeepThought(type = %d, item = %d, freshness = %d, freshtimeout = %d)",
msg, sizeof(msg), "PeepThought(type = %d, item = %u, freshness = %d, freshtimeout = %d)",
static_cast<int32_t>(val.type), val.item, val.freshness, val.fresh_timeout);
stream->Write(msg, strlen(msg));
}

View File

@ -15,6 +15,7 @@
#include <cstddef>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
namespace CODE_PAGE

View File

@ -1278,15 +1278,12 @@ static int32_t cc_show_limits(InteractiveConsole& console, [[maybe_unused]] cons
spriteCount += GetEntityListCount(EntityType(i));
}
int32_t staffCount = GetEntityListCount(EntityType::Staff);
auto bannerCount = GetNumBanners();
console.WriteFormatLine("Sprites: %d/%d", spriteCount, MAX_ENTITIES);
console.WriteFormatLine("Map Elements: %zu/%d", tileElementCount, MAX_TILE_ELEMENTS);
console.WriteFormatLine("Banners: %d/%zu", bannerCount, MAX_BANNERS);
console.WriteFormatLine("Rides: %d/%d", rideCount, MAX_RIDES);
console.WriteFormatLine("Staff: %d/%d", staffCount, STAFF_MAX_COUNT);
console.WriteFormatLine("Images: %zu/%zu", ImageListGetUsedCount(), ImageListGetMaximum());
return 0;
}
@ -1430,9 +1427,9 @@ static int32_t cc_replay_startrecord(InteractiveConsole& console, const argument
std::string name = argv[0];
if (!String::EndsWith(name, ".sv6r", true))
if (!String::EndsWith(name, ".parkrep", true))
{
name += ".sv6r";
name += ".parkrep";
}
std::string outPath = OpenRCT2::GetContext()->GetPlatformEnvironment()->GetDirectoryPath(
OpenRCT2::DIRBASE::USER, OpenRCT2::DIRID::REPLAY);
@ -1573,9 +1570,9 @@ static int32_t cc_replay_normalise(InteractiveConsole& console, const arguments_
std::string inputFile = argv[0];
std::string outputFile = argv[1];
if (!String::EndsWith(outputFile, ".sv6r", true))
if (!String::EndsWith(outputFile, ".parkrep", true))
{
outputFile += ".sv6r";
outputFile += ".parkrep";
}
std::string outPath = OpenRCT2::GetContext()->GetPlatformEnvironment()->GetDirectoryPath(
OpenRCT2::DIRBASE::USER, OpenRCT2::DIRID::REPLAY);

View File

@ -531,7 +531,7 @@ enum
#define WC_EDITOR_OBJECT_SELECTION__WIDX_TAB_1 21
#define WC_STAFF__WIDX_PICKUP 9
#define WC_TILE_INSPECTOR__WIDX_BUTTON_ROTATE 14
#define WC_TILE_INSPECTOR__WIDX_BUTTON_CORRUPT 10
#define WC_TILE_INSPECTOR__WIDX_BUTTON_TOGGLE_INVISIBILITY 10
#define WC_TILE_INSPECTOR__WIDX_BUTTON_COPY 17
#define WC_TILE_INSPECTOR__WIDX_BUTTON_PASTE 16
#define WC_TILE_INSPECTOR__WIDX_BUTTON_REMOVE 11
@ -542,32 +542,29 @@ enum
#define WC_TILE_INSPECTOR__WIDX_SPINNER_Y_INCREASE 8
#define WC_TILE_INSPECTOR__WIDX_SPINNER_Y_DECREASE 9
#define WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_SURFACE TileInspectorPage::Surface
#define WC_TILE_INSPECTOR__WIDX_SURFACE_SPINNER_HEIGHT_INCREASE 27
#define WC_TILE_INSPECTOR__WIDX_SURFACE_SPINNER_HEIGHT_DECREASE 28
#define WC_TILE_INSPECTOR__WIDX_SURFACE_SPINNER_HEIGHT_INCREASE 28
#define WC_TILE_INSPECTOR__WIDX_SURFACE_SPINNER_HEIGHT_DECREASE 29
#define WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_PATH TileInspectorPage::Path
#define WC_TILE_INSPECTOR__WIDX_PATH_SPINNER_HEIGHT_INCREASE 27
#define WC_TILE_INSPECTOR__WIDX_PATH_SPINNER_HEIGHT_DECREASE 28
#define WC_TILE_INSPECTOR__WIDX_PATH_SPINNER_HEIGHT_INCREASE 28
#define WC_TILE_INSPECTOR__WIDX_PATH_SPINNER_HEIGHT_DECREASE 29
#define WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_TRACK TileInspectorPage::Track
#define WC_TILE_INSPECTOR__WIDX_TRACK_SPINNER_HEIGHT_INCREASE 28
#define WC_TILE_INSPECTOR__WIDX_TRACK_SPINNER_HEIGHT_DECREASE 29
#define WC_TILE_INSPECTOR__WIDX_TRACK_SPINNER_HEIGHT_INCREASE 29
#define WC_TILE_INSPECTOR__WIDX_TRACK_SPINNER_HEIGHT_DECREASE 30
#define WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_SCENERY TileInspectorPage::Scenery
#define WC_TILE_INSPECTOR__WIDX_SCENERY_SPINNER_HEIGHT_INCREASE 27
#define WC_TILE_INSPECTOR__WIDX_SCENERY_SPINNER_HEIGHT_DECREASE 28
#define WC_TILE_INSPECTOR__WIDX_SCENERY_SPINNER_HEIGHT_INCREASE 28
#define WC_TILE_INSPECTOR__WIDX_SCENERY_SPINNER_HEIGHT_DECREASE 29
#define WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_ENTRANCE TileInspectorPage::Entrance
#define WC_TILE_INSPECTOR__WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE 27
#define WC_TILE_INSPECTOR__WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE 28
#define WC_TILE_INSPECTOR__WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE 28
#define WC_TILE_INSPECTOR__WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE 29
#define WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_WALL TileInspectorPage::Wall
#define WC_TILE_INSPECTOR__WIDX_WALL_SPINNER_HEIGHT_INCREASE 27
#define WC_TILE_INSPECTOR__WIDX_WALL_SPINNER_HEIGHT_DECREASE 28
#define WC_TILE_INSPECTOR__WIDX_WALL_SPINNER_HEIGHT_INCREASE 28
#define WC_TILE_INSPECTOR__WIDX_WALL_SPINNER_HEIGHT_DECREASE 29
#define WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_LARGE_SCENERY TileInspectorPage::LargeScenery
#define WC_TILE_INSPECTOR__WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE 27
#define WC_TILE_INSPECTOR__WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE 28
#define WC_TILE_INSPECTOR__WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE 28
#define WC_TILE_INSPECTOR__WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE 29
#define WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_BANNER TileInspectorPage::Banner
#define WC_TILE_INSPECTOR__WIDX_BANNER_SPINNER_HEIGHT_INCREASE 27
#define WC_TILE_INSPECTOR__WIDX_BANNER_SPINNER_HEIGHT_DECREASE 28
#define WC_TILE_INSPECTOR__TILE_INSPECTOR_PAGE_CORRUPT TileInspectorPage::Corrupt
#define WC_TILE_INSPECTOR__WIDX_CORRUPT_SPINNER_HEIGHT_INCREASE 27
#define WC_TILE_INSPECTOR__WIDX_CORRUPT_SPINNER_HEIGHT_DECREASE 28
#define WC_TILE_INSPECTOR__WIDX_BANNER_SPINNER_HEIGHT_INCREASE 28
#define WC_TILE_INSPECTOR__WIDX_BANNER_SPINNER_HEIGHT_DECREASE 29
enum class PromptMode : uint8_t
{

View File

@ -92,7 +92,6 @@ struct rct_window
uint32_t highlighted_item;
uint16_t ride_colour;
ResearchItem* research_item;
rct_object_entry* object_entry;
const scenario_index_entry* highlighted_scenario;
uint16_t var_496;
};

View File

@ -288,6 +288,7 @@
<ClInclude Include="paint\tile_element\Paint.Surface.h" />
<ClInclude Include="paint\tile_element\Paint.TileElement.h" />
<ClInclude Include="paint\VirtualFloor.h" />
<ClInclude Include="ParkFile.h" />
<ClInclude Include="ParkImporter.h" />
<ClInclude Include="peep\Guest.h" />
<ClInclude Include="peep\GuestPathfinding.h" />
@ -306,7 +307,6 @@
<ClInclude Include="rct1\RCT1.h" />
<ClInclude Include="rct1\Tables.h" />
<ClInclude Include="rct2\RCT2.h" />
<ClInclude Include="rct2\S6Exporter.h" />
<ClInclude Include="rct2\T6Exporter.h" />
<ClInclude Include="ReplayManager.h" />
<ClInclude Include="ride\CableLift.h" />
@ -752,6 +752,7 @@
<ClCompile Include="paint\tile_element\Paint.TileElement.cpp" />
<ClCompile Include="paint\tile_element\Paint.Wall.cpp" />
<ClCompile Include="paint\VirtualFloor.cpp" />
<ClCompile Include="ParkFile.cpp" />
<ClCompile Include="ParkImporter.cpp" />
<ClCompile Include="peep\Guest.cpp" />
<ClCompile Include="peep\GuestPathfinding.cpp" />
@ -779,7 +780,6 @@
<ClCompile Include="rct1\T4Importer.cpp" />
<ClCompile Include="rct1\Tables.cpp" />
<ClCompile Include="rct2\RCT2.cpp" />
<ClCompile Include="rct2\S6Exporter.cpp" />
<ClCompile Include="rct2\S6Importer.cpp" />
<ClCompile Include="rct2\SeaDecrypt.cpp" />
<ClCompile Include="rct2\T6Exporter.cpp" />

View File

@ -23,13 +23,13 @@
using namespace OpenRCT2;
using namespace OpenRCT2::Localisation;
static constexpr rct_string_id NONSTEX_BASE_STRING_ID = 3463;
static constexpr uint16_t MAX_OBJECT_CACHED_STRINGS = 2048;
static constexpr uint16_t BASE_OBJECT_STRING_ID = 0x2000;
static constexpr uint16_t MAX_OBJECT_CACHED_STRINGS = 0x5000 - BASE_OBJECT_STRING_ID;
LocalisationService::LocalisationService(const std::shared_ptr<IPlatformEnvironment>& env)
: _env(env)
{
for (rct_string_id stringId = NONSTEX_BASE_STRING_ID + MAX_OBJECT_CACHED_STRINGS; stringId >= NONSTEX_BASE_STRING_ID;
for (rct_string_id stringId = BASE_OBJECT_STRING_ID + MAX_OBJECT_CACHED_STRINGS; stringId >= BASE_OBJECT_STRING_ID;
stringId--)
{
_availableObjectStringIds.push(stringId);
@ -48,6 +48,16 @@ const char* LocalisationService::GetString(rct_string_id id) const
{
result = "";
}
else if (id >= BASE_OBJECT_STRING_ID && id < BASE_OBJECT_STRING_ID + MAX_OBJECT_CACHED_STRINGS)
{
size_t index = id - BASE_OBJECT_STRING_ID;
if (index < _objectStrings.size())
{
return _objectStrings[index].c_str();
}
result = "(unallocated string)";
}
else if (id != STR_NONE)
{
if (_languageCurrent != nullptr)
@ -129,9 +139,21 @@ rct_string_id LocalisationService::GetObjectOverrideStringId(std::string_view le
rct_string_id LocalisationService::AllocateObjectString(const std::string& target)
{
if (_availableObjectStringIds.empty())
{
return STR_EMPTY;
}
auto stringId = _availableObjectStringIds.top();
_availableObjectStringIds.pop();
_languageCurrent->SetString(stringId, target);
size_t index = stringId - BASE_OBJECT_STRING_ID;
if (index >= _objectStrings.size())
{
_objectStrings.resize(index + 1);
}
_objectStrings[index] = target;
return stringId;
}
@ -139,9 +161,10 @@ void LocalisationService::FreeObjectString(rct_string_id stringId)
{
if (stringId != STR_EMPTY)
{
if (_languageCurrent != nullptr)
size_t index = stringId - BASE_OBJECT_STRING_ID;
if (index < _objectStrings.size())
{
_languageCurrent->RemoveString(stringId);
_objectStrings[index] = {};
}
_availableObjectStringIds.push(stringId);
}

View File

@ -16,6 +16,7 @@
#include <string>
#include <string_view>
#include <tuple>
#include <vector>
struct ILanguagePack;
struct IObjectManager;
@ -36,6 +37,7 @@ namespace OpenRCT2::Localisation
std::unique_ptr<ILanguagePack> _languageFallback;
std::unique_ptr<ILanguagePack> _languageCurrent;
std::stack<rct_string_id> _availableObjectStringIds;
std::vector<std::string> _objectStrings;
public:
int32_t GetCurrentLanguage() const

View File

@ -2347,7 +2347,6 @@ enum : uint16_t
STR_OBJECT_SELECTION_ERR_ALWAYS_REQUIRED = 3175,
STR_UNABLE_TO_SELECT_THIS_OBJECT = 3176,
STR_UNABLE_TO_DE_SELECT_THIS_OBJECT = 3177,
STR_AT_LEAST_ONE_PATH_OBJECT_MUST_BE_SELECTED = 3178,
STR_AT_LEAST_ONE_RIDE_OBJECT_MUST_BE_SELECTED = 3179,
STR_INVALID_SELECTION_OF_OBJECTS = 3180,
STR_OBJECT_SELECTION = 3181,
@ -2964,8 +2963,7 @@ enum : uint16_t
STR_LANGUAGE_LOAD_FAILED = 5561,
STR_WARNING_IN_CAPS = 5562,
STR_THIS_FEATURE_IS_CURRENTLY_UNSTABLE = 5563,
STR_INSERT_CORRUPT = 5564, // Unused
STR_INSERT_CORRUPT_TIP = 5565,
STR_PASSWORD = 5566,
STR_ADVERTISE = 5567,
STR_PASSWORD_REQUIRED = 5568,
@ -3288,7 +3286,6 @@ enum : uint16_t
STR_TILE_INSPECTOR_GROUPBOX_WALL_INFO = 5929,
STR_TILE_INSPECTOR_GROUPBOX_LARGE_SCENERY_INFO = 5930,
STR_TILE_INSPECTOR_GROUPBOX_BANNER_INFO = 5931,
STR_TILE_INSPECTOR_GROUPBOX_CORRUPT_INFO = 5932,
STR_TILE_INSPECTOR_GROUPBOX_PROPERTIES = 5933,
STR_TILE_INSPECTOR_SURFACE_TERAIN = 5934,
STR_TILE_INSPECTOR_SURFACE_EDGE = 5935,
@ -3753,7 +3750,6 @@ enum : uint16_t
STR_SHORTCUT_SCALE_UP = 6333,
STR_SHORTCUT_SCALE_DOWN = 6334,
STR_SHORTCUT_INSERT_CORRPUT_ELEMENT = 6335,
STR_SHORTCUT_COPY_ELEMENT = 6336,
STR_SHORTCUT_PASTE_ELEMENT = 6337,
STR_SHORTCUT_REMOVE_ELEMENT = 6338,
@ -3896,6 +3892,16 @@ enum : uint16_t
STR_VIEWPORT_TRANSPARENT_WATER = 6440,
STR_AT_LEAST_ONE_FOOTPATH_NON_QUEUE_SURFACE_OBJECT_MUST_BE_SELECTED = 6441,
STR_AT_LEAST_ONE_FOOTPATH_QUEUE_SURFACE_OBJECT_MUST_BE_SELECTED = 6442,
STR_AT_LEAST_ONE_FOOTPATH_RAILING_OBJECT_MUST_BE_SELECTED = 6443,
STR_OBJECT_SELECTION_FOOTPATH_SURFACES = 6444,
STR_OBJECT_SELECTION_FOOTPATH_RAILINGS = 6445,
STR_TILE_INSPECTOR_FOOTPATH_SURFACE_NAME = 6446,
STR_TILE_INSPECTOR_FOOTPATH_RAILINGS_NAME = 6447,
STR_UNSUPPORTED_OBJECT_FORMAT = 6448,
STR_MUSIC_OBJECT_TRACK_HEADER = 6449,
STR_MUSIC_OBJECT_TRACK_LIST_ITEM = 6450,
STR_MUSIC_OBJECT_TRACK_LIST_ITEM_WITH_COMPOSER = 6451,
@ -3913,8 +3919,6 @@ enum : uint16_t
STR_FOLLOW_SUBJECT_TIP = 6458,
STR_UNSUPPORTED_OBJECT_FORMAT = 6459,
STR_TILE_INSPECTOR_DIRECTION_SHORT = 6460,
STR_TILE_INSPECTOR_DIRECTION = 6461,

View File

@ -76,7 +76,7 @@ void research_reset_items()
*
* rct2: 0x00684BAE
*/
void research_update_uncompleted_types()
void ResearchUpdateUncompletedTypes()
{
int32_t uncompletedResearchTypes = 0;
@ -354,7 +354,7 @@ void research_update()
gResearchProgress = 0;
gResearchProgressStage = RESEARCH_STAGE_INITIAL_RESEARCH;
research_calculate_expected_date();
research_update_uncompleted_types();
ResearchUpdateUncompletedTypes();
research_invalidate_related_windows();
break;
case RESEARCH_STAGE_FINISHED_ALL:
@ -393,6 +393,12 @@ void research_reset_current_item()
*/
static void research_insert_unresearched(ResearchItem&& item)
{
// First check to make sure that entry is not already accounted for
if (item.Exists())
{
return;
}
gResearchItemsUninvented.push_back(std::move(item));
}
@ -724,18 +730,17 @@ void research_remove_flags()
}
}
void research_fix()
static void ResearchRemoveNullItems(std::vector<ResearchItem>& items)
{
// Fix invalid research items
for (auto it = gResearchItemsInvented.begin(); it != gResearchItemsInvented.end();)
for (auto it = items.begin(); it != items.end();)
{
auto& researchItem = *it;
if (researchItem.type == Research::EntryType::Ride)
{
rct_ride_entry* rideEntry = get_ride_entry(researchItem.entryIndex);
const auto* rideEntry = get_ride_entry(researchItem.entryIndex);
if (rideEntry == nullptr)
{
it = gResearchItemsInvented.erase(it);
it = items.erase(it);
}
else
{
@ -744,10 +749,10 @@ void research_fix()
}
else
{
rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem.entryIndex);
const auto* sceneryGroupEntry = get_scenery_group_entry(researchItem.entryIndex);
if (sceneryGroupEntry == nullptr)
{
it = gResearchItemsInvented.erase(it);
it = items.erase(it);
}
else
{
@ -755,74 +760,96 @@ void research_fix()
}
}
}
for (auto it = gResearchItemsUninvented.begin(); it != gResearchItemsUninvented.end();)
}
static void research_mark_item_as_researched(const ResearchItem& item)
{
if (item.type == Research::EntryType::Ride)
{
auto& researchItem = *it;
if (researchItem.type == Research::EntryType::Ride)
const auto* rideEntry = get_ride_entry(item.entryIndex);
if (rideEntry != nullptr)
{
rct_ride_entry* rideEntry = get_ride_entry(researchItem.entryIndex);
if (rideEntry == nullptr)
ride_entry_set_invented(item.entryIndex);
for (auto rideType : rideEntry->ride_type)
{
it = gResearchItemsUninvented.erase(it);
}
else
{
it++;
}
}
else
{
rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem.entryIndex);
if (sceneryGroupEntry == nullptr)
{
it = gResearchItemsUninvented.erase(it);
}
else
{
it++;
}
}
}
research_update_uncompleted_types();
if (gResearchUncompletedCategories == 0)
gResearchProgressStage = RESEARCH_STAGE_FINISHED_ALL;
// Sometimes ride entries are not in the research table.
// If all research is done, simply insert all of them as researched.
// For good measure, also include scenery groups.
if (gResearchProgressStage == RESEARCH_STAGE_FINISHED_ALL)
{
for (ObjectEntryIndex i = 0; i < MAX_RIDE_OBJECTS; i++)
{
const rct_ride_entry* rideEntry = get_ride_entry(i);
if (rideEntry != nullptr)
{
research_insert_ride_entry(i, true);
ride_entry_set_invented(i);
for (uint8_t j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++)
if (rideType != RIDE_TYPE_NULL)
{
uint32_t rideType = rideEntry->ride_type[j];
if (rideType != RIDE_TYPE_NULL)
{
ride_type_set_invented(rideEntry->ride_type[j]);
}
ride_type_set_invented(rideType);
}
}
}
for (uint8_t i = 0; i < MAX_SCENERY_GROUP_OBJECTS; i++)
}
else if (item.type == Research::EntryType::Scenery)
{
const auto sgEntry = get_scenery_group_entry(item.entryIndex);
if (sgEntry != nullptr)
{
const rct_scenery_group_entry* groupEntry = get_scenery_group_entry(i);
if (groupEntry != nullptr)
research_insert_scenery_group_entry(i, true);
for (auto i = 0; i < sgEntry->entry_count; i++)
{
auto sceneryEntryIndex = sgEntry->scenery_entries[i];
scenery_set_invented(sceneryEntryIndex);
}
}
}
}
static void ResearchRebuildInventedTables()
{
set_every_ride_type_not_invented();
set_every_ride_entry_invented();
set_every_ride_entry_not_invented();
set_all_scenery_items_not_invented();
for (const auto& item : gResearchItemsInvented)
{
// Ignore item, if the research of it is in progress
if (gResearchProgressStage == RESEARCH_STAGE_DESIGNING || gResearchProgressStage == RESEARCH_STAGE_COMPLETING_DESIGN)
{
if (item == gResearchNextItem)
{
continue;
}
}
research_mark_item_as_researched(item);
}
}
static void ResearchAddAllMissingItems(bool isResearched)
{
for (ObjectEntryIndex i = 0; i < MAX_RIDE_OBJECTS; i++)
{
const auto* rideEntry = get_ride_entry(i);
if (rideEntry != nullptr)
{
research_insert_ride_entry(i, isResearched);
}
}
for (ObjectEntryIndex i = 0; i < MAX_SCENERY_GROUP_OBJECTS; i++)
{
const auto* groupEntry = get_scenery_group_entry(i);
if (groupEntry != nullptr)
{
research_insert_scenery_group_entry(i, isResearched);
}
}
}
void ResearchFix()
{
// Remove null entries from the research list
ResearchRemoveNullItems(gResearchItemsInvented);
ResearchRemoveNullItems(gResearchItemsUninvented);
// Add missing entries to the research list
// If research is complete, mark all the missing items as available
ResearchAddAllMissingItems(gResearchProgressStage == RESEARCH_STAGE_FINISHED_ALL);
// Now rebuild all the tables that say whether a ride or scenery item is invented
ResearchRebuildInventedTables();
ResearchUpdateUncompletedTypes();
}
void research_items_make_all_unresearched()
{
gResearchItemsUninvented.insert(

View File

@ -169,7 +169,7 @@ extern uint8_t gResearchUncompletedCategories;
extern bool gSilentResearch;
void research_reset_items();
void research_update_uncompleted_types();
void ResearchUpdateUncompletedTypes();
void research_update();
void research_reset_current_item();
void research_populate_list_random();
@ -199,7 +199,7 @@ void set_every_ride_type_not_invented();
void set_every_ride_entry_invented();
void set_every_ride_entry_not_invented();
void research_remove_flags();
void research_fix();
void ResearchFix();
void research_items_make_all_unresearched();
void research_items_make_all_researched();

View File

@ -13,6 +13,7 @@
#include "../Game.h"
#include "../GameStateSnapshots.h"
#include "../OpenRCT2.h"
#include "../ParkFile.h"
#include "../PlatformEnvironment.h"
#include "../actions/LoadOrQuitAction.h"
#include "../actions/NetworkModifyGroupAction.h"
@ -21,6 +22,7 @@
#include "../core/Json.hpp"
#include "../localisation/Formatting.h"
#include "../platform/Platform2.h"
#include "../scenario/Scenario.h"
#include "../scripting/ScriptEngine.h"
#include "../ui/UiContext.h"
#include "../ui/WindowManager.h"
@ -70,7 +72,6 @@ static constexpr uint32_t MaxPacketsPerUpdate = 100;
# include "../localisation/Localisation.h"
# include "../object/ObjectManager.h"
# include "../object/ObjectRepository.h"
# include "../rct2/S6Exporter.h"
# include "../scenario/Scenario.h"
# include "../util/Util.h"
# include "../world/Park.h"
@ -1219,15 +1220,25 @@ void NetworkBase::Client_Send_AUTH(
_serverConnection->QueuePacket(std::move(packet));
}
void NetworkBase::Client_Send_MAPREQUEST(const std::vector<std::string>& objects)
void NetworkBase::Client_Send_MAPREQUEST(const std::vector<ObjectEntryDescriptor>& objects)
{
log_verbose("client requests %u objects", uint32_t(objects.size()));
NetworkPacket packet(NetworkCommand::MapRequest);
packet << static_cast<uint32_t>(objects.size());
for (const auto& object : objects)
{
log_verbose("client requests object %s", object.c_str());
packet.Write(reinterpret_cast<const uint8_t*>(object.c_str()), 8);
std::string name(object.GetName());
log_verbose("client requests object %s", name.c_str());
if (object.Generation == ObjectGeneration::DAT)
{
packet << static_cast<uint8_t>(0);
packet.Write(&object.Entry, sizeof(rct_object_entry));
}
else
{
packet << static_cast<uint8_t>(1);
packet.WriteString(name);
}
}
_serverConnection->QueuePacket(std::move(packet));
}
@ -1261,9 +1272,20 @@ void NetworkBase::Server_Send_OBJECTS_LIST(
NetworkPacket packet(NetworkCommand::ObjectsList);
packet << static_cast<uint32_t>(i) << static_cast<uint32_t>(objects.size());
log_verbose("Object %.8s (checksum %x)", object->ObjectEntry.name, object->ObjectEntry.checksum);
packet.Write(reinterpret_cast<const uint8_t*>(object->ObjectEntry.name), 8);
packet << object->ObjectEntry.checksum << object->ObjectEntry.flags;
if (object->Identifier.empty())
{
// DAT
log_verbose("Object %.8s (checksum %x)", object->ObjectEntry.name, object->ObjectEntry.checksum);
packet << static_cast<uint8_t>(0);
packet.Write(&object->ObjectEntry, sizeof(rct_object_entry));
}
else
{
// JSON
log_verbose("Object %s", object->Identifier.c_str());
packet << static_cast<uint8_t>(1);
packet.WriteString(object->Identifier);
}
connection.QueuePacket(std::move(packet));
}
@ -1401,37 +1423,18 @@ void NetworkBase::Server_Send_MAP(NetworkConnection* connection)
std::vector<uint8_t> NetworkBase::save_for_network(const std::vector<const ObjectRepositoryItem*>& objects) const
{
std::vector<uint8_t> header;
bool RLEState = gUseRLE;
gUseRLE = false;
std::vector<uint8_t> result;
auto ms = OpenRCT2::MemoryStream();
if (!SaveMap(&ms, objects))
if (SaveMap(&ms, objects))
{
log_warning("Failed to export map.");
return header;
}
gUseRLE = RLEState;
const void* data = ms.GetData();
int32_t size = ms.GetLength();
auto compressed = util_zlib_deflate(static_cast<const uint8_t*>(data), size);
if (compressed.has_value())
{
std::string headerString = "open2_sv6_zlib";
header.resize(headerString.size() + 1 + compressed->size());
std::memcpy(&header[0], headerString.c_str(), headerString.size() + 1);
std::memcpy(&header[headerString.size() + 1], compressed->data(), compressed->size());
log_verbose("Sending map of size %u bytes, compressed to %u bytes", size, headerString.size() + 1 + compressed->size());
result.resize(ms.GetLength());
std::memcpy(result.data(), ms.GetData(), result.size());
}
else
{
log_warning("Failed to compress the data, falling back to non-compressed sv6.");
header.resize(size);
std::memcpy(header.data(), data, size);
log_warning("Failed to export map.");
}
return header;
return result;
}
void NetworkBase::Client_Send_CHAT(const char* text)
@ -2339,26 +2342,45 @@ void NetworkBase::Client_Handle_OBJECTS_LIST(NetworkConnection& connection, Netw
intent.putExtra(INTENT_EXTRA_CALLBACK, []() -> void { ::GetContext()->GetNetwork().Close(); });
context_open_intent(&intent);
char objectName[12]{};
std::memcpy(objectName, packet.Read(8), 8);
uint8_t objectType{};
packet >> objectType;
uint32_t checksum = 0;
uint32_t flags = 0;
packet >> checksum >> flags;
const auto* object = repo.FindObjectLegacy(objectName);
// This could potentially request the object if checksums don't match, but since client
// won't replace its version with server-provided one, we don't do that.
if (object == nullptr)
if (objectType == 0)
{
log_verbose("Requesting object %s with checksum %x from server", objectName, checksum);
_missingObjects.emplace_back(objectName);
// DAT
auto entry = reinterpret_cast<const rct_object_entry*>(packet.Read(sizeof(rct_object_entry)));
if (entry != nullptr)
{
const auto* object = repo.FindObject(entry);
if (object == nullptr)
{
auto objectName = std::string(entry->GetName());
log_verbose("Requesting object %s with checksum %x from server", objectName.c_str(), entry->checksum);
_missingObjects.push_back(ObjectEntryDescriptor(*entry));
}
else if (object->ObjectEntry.checksum != entry->checksum || object->ObjectEntry.flags != entry->flags)
{
auto objectName = std::string(entry->GetName());
log_warning(
"Object %s has different checksum/flags (%x/%x) than server (%x/%x).", objectName.c_str(),
object->ObjectEntry.checksum, object->ObjectEntry.flags, entry->checksum, entry->flags);
}
}
}
else if (object->ObjectEntry.checksum != checksum || object->ObjectEntry.flags != flags)
else
{
log_warning(
"Object %s has different checksum/flags (%x/%x) than server (%x/%x).", objectName, object->ObjectEntry.checksum,
object->ObjectEntry.flags, checksum, flags);
// JSON
auto identifier = packet.ReadString();
if (!identifier.empty())
{
const auto* object = repo.FindObject(identifier);
if (object == nullptr)
{
auto objectName = std::string(identifier);
log_verbose("Requesting object %s from server", objectName.c_str());
_missingObjects.push_back(ObjectEntryDescriptor(objectName));
}
}
}
}
@ -2490,13 +2512,28 @@ void NetworkBase::Server_Handle_MAPREQUEST(NetworkConnection& connection, Networ
return;
}
// This is required, as packet does not have null terminator
std::string s(name, name + 8);
log_verbose("Client requested object %s", s.c_str());
const ObjectRepositoryItem* item = repo.FindObjectLegacy(s.c_str());
uint8_t generation{};
packet >> generation;
std::string objectName;
const ObjectRepositoryItem* item{};
if (generation == static_cast<uint8_t>(ObjectGeneration::DAT))
{
const auto* entry = reinterpret_cast<const rct_object_entry*>(packet.Read(sizeof(rct_object_entry)));
objectName = std::string(entry->GetName());
log_verbose("Client requested object %s", objectName.c_str());
item = repo.FindObject(entry);
}
else
{
objectName = std::string(packet.ReadString());
log_verbose("Client requested object %s", objectName.c_str());
item = repo.FindObject(objectName);
}
if (item == nullptr)
{
log_warning("Client tried getting non-existent object %s from us.", s.c_str());
log_warning("Client tried getting non-existent object %s from us.", objectName.c_str());
}
else
{
@ -2504,7 +2541,7 @@ void NetworkBase::Server_Handle_MAPREQUEST(NetworkConnection& connection, Networ
}
}
const char* player_name = static_cast<const char*>(connection.Player->Name.c_str());
auto player_name = connection.Player->Name.c_str();
Server_Send_MAP(&connection);
Server_Send_EVENT_PLAYER_JOINED(player_name);
Server_Send_GROUPLIST(connection);
@ -2673,25 +2710,6 @@ void NetworkBase::Client_Handle_MAP([[maybe_unused]] NetworkConnection& connecti
bool has_to_free = false;
uint8_t* data = &chunk_buffer[0];
size_t data_size = size;
// zlib-compressed
if (strcmp("open2_sv6_zlib", reinterpret_cast<char*>(&chunk_buffer[0])) == 0)
{
log_verbose("Received zlib-compressed sv6 map");
has_to_free = true;
size_t header_len = strlen("open2_sv6_zlib") + 1;
data = util_zlib_inflate(&chunk_buffer[header_len], size - header_len, &data_size);
if (data == nullptr)
{
log_warning("Failed to decompress data sent from server.");
Close();
return;
}
}
else
{
log_verbose("Assuming received map is in plain sv6 format");
}
auto ms = MemoryStream(data, data_size);
if (LoadMap(&ms))
{
@ -2734,7 +2752,7 @@ bool NetworkBase::LoadMap(IStream* stream)
{
auto& context = GetContext();
auto& objManager = context.GetObjectManager();
auto importer = ParkImporter::CreateS6(context.GetObjectRepository());
auto importer = ParkImporter::CreateParkFile(context.GetObjectRepository());
auto loadResult = importer->LoadFromStream(stream, false);
objManager.LoadObjects(loadResult.RequiredObjects);
importer->Import();
@ -2742,43 +2760,12 @@ bool NetworkBase::LoadMap(IStream* stream)
EntityTweener::Get().Reset();
AutoCreateMapAnimations();
// Read checksum
[[maybe_unused]] uint32_t checksum = stream->ReadValue<uint32_t>();
// Read other data not in normal save files
gGamePaused = stream->ReadValue<uint32_t>();
_guestGenerationProbability = stream->ReadValue<uint32_t>();
_suggestedGuestMaximum = stream->ReadValue<uint32_t>();
gCheatsAllowTrackPlaceInvalidHeights = stream->ReadValue<uint8_t>() != 0;
gCheatsEnableAllDrawableTrackPieces = stream->ReadValue<uint8_t>() != 0;
gCheatsSandboxMode = stream->ReadValue<uint8_t>() != 0;
gCheatsDisableClearanceChecks = stream->ReadValue<uint8_t>() != 0;
gCheatsDisableSupportLimits = stream->ReadValue<uint8_t>() != 0;
gCheatsDisableTrainLengthLimit = stream->ReadValue<uint8_t>() != 0;
gCheatsEnableChainLiftOnAllTrack = stream->ReadValue<uint8_t>() != 0;
gCheatsShowAllOperatingModes = stream->ReadValue<uint8_t>() != 0;
gCheatsShowVehiclesFromOtherTrackTypes = stream->ReadValue<uint8_t>() != 0;
gCheatsUnlockOperatingLimits = stream->ReadValue<uint8_t>() != 0;
gCheatsDisableBrakesFailure = stream->ReadValue<uint8_t>() != 0;
gCheatsDisableAllBreakdowns = stream->ReadValue<uint8_t>() != 0;
gCheatsBuildInPauseMode = stream->ReadValue<uint8_t>() != 0;
gCheatsIgnoreRideIntensity = stream->ReadValue<uint8_t>() != 0;
gCheatsDisableVandalism = stream->ReadValue<uint8_t>() != 0;
gCheatsDisableLittering = stream->ReadValue<uint8_t>() != 0;
gCheatsNeverendingMarketing = stream->ReadValue<uint8_t>() != 0;
gCheatsFreezeWeather = stream->ReadValue<uint8_t>() != 0;
gCheatsDisablePlantAging = stream->ReadValue<uint8_t>() != 0;
gCheatsAllowArbitraryRideTypeChanges = stream->ReadValue<uint8_t>() != 0;
gCheatsDisableRideValueAging = stream->ReadValue<uint8_t>() != 0;
gConfigGeneral.show_real_names_of_guests = stream->ReadValue<uint8_t>() != 0;
gCheatsIgnoreResearchStatus = stream->ReadValue<uint8_t>() != 0;
gAllowEarlyCompletionInNetworkPlay = stream->ReadValue<uint8_t>() != 0;
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
result = true;
}
catch (const std::exception&)
catch (const std::exception& e)
{
Console::Error::WriteLine("Unable to read map from server: %s", e.what());
}
return result;
}
@ -2789,44 +2776,14 @@ bool NetworkBase::SaveMap(IStream* stream, const std::vector<const ObjectReposit
viewport_set_saved_view();
try
{
auto s6exporter = std::make_unique<S6Exporter>();
s6exporter->ExportObjectsList = objects;
s6exporter->Export();
s6exporter->SaveGame(stream);
// Write other data not in normal save files
stream->WriteValue<uint32_t>(gGamePaused);
stream->WriteValue<uint32_t>(_guestGenerationProbability);
stream->WriteValue<uint32_t>(_suggestedGuestMaximum);
stream->WriteValue<uint8_t>(gCheatsAllowTrackPlaceInvalidHeights);
stream->WriteValue<uint8_t>(gCheatsEnableAllDrawableTrackPieces);
stream->WriteValue<uint8_t>(gCheatsSandboxMode);
stream->WriteValue<uint8_t>(gCheatsDisableClearanceChecks);
stream->WriteValue<uint8_t>(gCheatsDisableSupportLimits);
stream->WriteValue<uint8_t>(gCheatsDisableTrainLengthLimit);
stream->WriteValue<uint8_t>(gCheatsEnableChainLiftOnAllTrack);
stream->WriteValue<uint8_t>(gCheatsShowAllOperatingModes);
stream->WriteValue<uint8_t>(gCheatsShowVehiclesFromOtherTrackTypes);
stream->WriteValue<uint8_t>(gCheatsUnlockOperatingLimits);
stream->WriteValue<uint8_t>(gCheatsDisableBrakesFailure);
stream->WriteValue<uint8_t>(gCheatsDisableAllBreakdowns);
stream->WriteValue<uint8_t>(gCheatsBuildInPauseMode);
stream->WriteValue<uint8_t>(gCheatsIgnoreRideIntensity);
stream->WriteValue<uint8_t>(gCheatsDisableVandalism);
stream->WriteValue<uint8_t>(gCheatsDisableLittering);
stream->WriteValue<uint8_t>(gCheatsNeverendingMarketing);
stream->WriteValue<uint8_t>(gCheatsFreezeWeather);
stream->WriteValue<uint8_t>(gCheatsDisablePlantAging);
stream->WriteValue<uint8_t>(gCheatsAllowArbitraryRideTypeChanges);
stream->WriteValue<uint8_t>(gCheatsDisableRideValueAging);
stream->WriteValue<uint8_t>(gConfigGeneral.show_real_names_of_guests);
stream->WriteValue<uint8_t>(gCheatsIgnoreResearchStatus);
stream->WriteValue<uint8_t>(gConfigGeneral.allow_early_completion);
auto exporter = std::make_unique<ParkFileExporter>();
exporter->ExportObjectsList = objects;
exporter->Export(*stream);
result = true;
}
catch (const std::exception&)
catch (const std::exception& e)
{
Console::Error::WriteLine("Unable to serialise map: %s", e.what());
}
return result;
}

View File

@ -2,6 +2,7 @@
#include "../System.hpp"
#include "../actions/GameAction.h"
#include "../object/Object.h"
#include "NetworkConnection.h"
#include "NetworkGroup.h"
#include "NetworkPlayer.h"
@ -45,7 +46,7 @@ public: // Common
std::string BeginLog(const std::string& directory, const std::string& midName, const std::string& filenameFormat);
void AppendLog(std::ostream& fs, std::string_view s);
void BeginChatLog();
void AppendChatLog(std::string_view text);
void AppendChatLog(std::string_view s);
void CloseChatLog();
NetworkStats_t GetStats() const;
json_t GetServerInfoAsJson() const;
@ -138,7 +139,7 @@ public: // Client
void Client_Send_GAME_ACTION(const GameAction* action);
void Client_Send_PING();
void Client_Send_GAMEINFO();
void Client_Send_MAPREQUEST(const std::vector<std::string>& objects);
void Client_Send_MAPREQUEST(const std::vector<ObjectEntryDescriptor>& objects);
void Client_Send_HEARTBEAT(NetworkConnection& connection) const;
// Handlers.
@ -218,7 +219,7 @@ private: // Client Data
std::map<uint32_t, PlayerListUpdate> _pendingPlayerLists;
std::multimap<uint32_t, NetworkPlayer> _pendingPlayerInfo;
std::map<uint32_t, ServerTickData_t> _serverTickData;
std::vector<std::string> _missingObjects;
std::vector<ObjectEntryDescriptor> _missingObjects;
std::string _host;
std::string _chatLogPath;
std::string _chatLogFilenameFormat = "%Y%m%d-%H%M%S.txt";

View File

@ -11,60 +11,176 @@
#include "Object.h"
// clang-format off
const std::string_view MinimumRequiredObjects[] = { "rct2.terrain_surface.grass", "rct2.terrain_edge.rock" };
const std::string_view DefaultSelectedObjects[] = {
// An initial default selection
"rct2.scgtrees", // Scenery: Trees
"rct2.scgshrub", // Scenery: Shrubs and Ornaments
"rct2.scggardn", // Scenery: Gardens
"rct2.scgfence", // Scenery: Fences and Walls
"rct2.scgwalls", // Scenery: Walls and Roofs
"rct2.scgpathx", // Scenery: Signs and Items for Footpaths
"rct2.tarmac", // Footpath: Tarmac
"rct2.twist1", // Ride: Twist
"rct2.ptct1", // Ride: Wooden Roller Coaster (Wooden Roller Coaster Trains)
"rct2.zldb", // Ride: Junior Roller Coaster (Ladybird Trains)
"rct2.lfb1", // Ride: Log Flume
"rct2.vcr", // Ride: Vintage Cars
"rct2.mgr1", // Ride: Merry-Go-Round
"rct2.tlt1", // Ride: Restroom
"rct2.atm1", // Ride: Cash Machine
"rct2.faid1", // Ride: First Aid Room
"rct2.infok", // Ride: Information Kiosk
"rct2.drnks", // Ride: Drinks Stall
"rct2.cndyf", // Ride: Candyfloss Stall
"rct2.burgb", // Ride: Burger Bar
"rct2.balln", // Ride: Balloon Stall
"rct2.arrt1", // Ride: Corkscrew Roller Coaster
"rct2.rboat", // Ride: Rowing Boats
"rct2.pkent1", // Park Entrance: Traditional Park Entrance
"rct2.wtrcyan", // Water: Natural Water
"rct2.tarmacb", // Footpath: Brown Tarmac Footpath
"rct2.pathspce", // Footpath: Space Style Footpath
"rct2.pathdirt", // Footpath: Dirt Footpath
"rct2.pathcrzy", // Footpath: Crazy Paving Footpath
"rct2.pathash", // Footpath: Ash Footpath
"rct2.scenery_group.scgtrees", // Scenery: Trees
"rct2.scenery_group.scgshrub", // Scenery: Shrubs and Ornaments
"rct2.scenery_group.scggardn", // Scenery: Gardens
"rct2.scenery_group.scgfence", // Scenery: Fences and Walls
"rct2.scenery_group.scgwalls", // Scenery: Walls and Roofs
"rct2.scenery_group.scgpathx", // Scenery: Signs and Items for Footpaths
"rct2.ride.twist1", // Ride: Twist
"rct2.ride.ptct1", // Ride: Wooden Roller Coaster (Wooden Roller Coaster Trains)
"rct2.ride.zldb", // Ride: Junior Roller Coaster (Ladybird Trains)
"rct2.ride.lfb1", // Ride: Log Flume
"rct2.ride.vcr", // Ride: Vintage Cars
"rct2.ride.mgr1", // Ride: Merry-Go-Round
"rct2.ride.tlt1", // Ride: Restroom
"rct2.ride.atm1", // Ride: Cash Machine
"rct2.ride.faid1", // Ride: First Aid Room
"rct2.ride.infok", // Ride: Information Kiosk
"rct2.ride.drnks", // Ride: Drinks Stall
"rct2.ride.cndyf", // Ride: Candyfloss Stall
"rct2.ride.burgb", // Ride: Burger Bar
"rct2.ride.balln", // Ride: Balloon Stall
"rct2.ride.arrt1", // Ride: Corkscrew Roller Coaster
"rct2.ride.rboat", // Ride: Rowing Boats
"rct2.park_entrance.pkent1", // Park Entrance: Traditional Park Entrance
"rct2.water.wtrcyan", // Water: Natural Water
// The following are for all random map generation features to work out the box
"rct2.scgjungl", // Jungle Theming
"rct2.scgsnow", // Snow and Ice Theming
"rct2.scgwater", // Water Feature Theming
"rct2.scenery_group.scgjungl", // Jungle Theming
"rct2.scenery_group.scgsnow", // Snow and Ice Theming
"rct2.scenery_group.scgwater", // Water Feature Theming
// Surfaces
"rct2.terrain_surface.grass",
"rct2.terrain_surface.sand",
"rct2.terrain_surface.dirt",
"rct2.terrain_surface.rock",
"rct2.terrain_surface.martian",
"rct2.terrain_surface.chequerboard",
"rct2.terrain_surface.grass_clumps",
"rct2.terrain_surface.ice",
"rct2.terrain_surface.grid_red",
"rct2.terrain_surface.grid_yellow",
"rct2.terrain_surface.grid_purple",
"rct2.terrain_surface.grid_green",
"rct2.terrain_surface.sand_red",
"rct2.terrain_surface.sand_brown",
// Edges
"rct2.terrain_edge.rock",
"rct2.terrain_edge.wood_red",
"rct2.terrain_edge.wood_black",
"rct2.terrain_edge.ice",
// Stations
"rct2.station.plain",
"rct2.station.wooden",
"rct2.station.canvas_tent",
"rct2.station.castle_grey",
"rct2.station.castle_brown",
"rct2.station.jungle",
"rct2.station.log",
"rct2.station.classical",
"rct2.station.abstract",
"rct2.station.snow",
"rct2.station.pagoda",
"rct2.station.space",
// Music
"rct2.music.dodgems",
"rct2.music.fairground",
"rct2.music.roman",
"rct2.music.oriental",
"rct2.music.martian",
"rct2.music.jungle",
"rct2.music.egyptian",
"rct2.music.toyland",
"rct2.music.space",
"rct2.music.horror",
"rct2.music.techno",
"rct2.music.gentle",
"rct2.music.summer",
"rct2.music.water",
"rct2.music.wildwest",
"rct2.music.jurassic",
"rct2.music.rock1",
"rct2.music.ragtime",
"rct2.music.fantasy",
"rct2.music.rock2",
"rct2.music.ice",
"rct2.music.snow",
"rct2.music.medieval",
"rct2.music.urban",
"rct2.music.organ",
"rct2.music.mechanical",
"rct2.music.modern",
"rct2.music.pirate",
"rct2.music.rock3",
"rct2.music.candy",
// Footpath surfaces
"rct2.footpath_surface.tarmac",
"rct2.footpath_surface.tarmac_brown",
"rct2.footpath_surface.tarmac_red",
"rct2.footpath_surface.dirt",
"rct2.footpath_surface.crazy_paving",
"rct2.footpath_surface.ash",
"rct2.footpath_surface.queue_blue",
"rct2.footpath_surface.queue_green",
"rct2.footpath_surface.queue_red",
"rct2.footpath_surface.queue_yellow",
// Footpath railings
"rct2.footpath_railings.bamboo_black",
"rct2.footpath_railings.bamboo_brown",
"rct2.footpath_railings.concrete",
"rct2.footpath_railings.concrete_green",
"rct2.footpath_railings.space",
"rct2.footpath_railings.wood",
};
const std::string_view DesignerSelectedObjects[] = {
// An initial default selection + all standard footpaths
"rct2.scgtrees", // Scenery: Trees
"rct2.scgshrub", // Scenery: Shrubs and Ornaments
"rct2.scggardn", // Scenery: Gardens
"rct2.scgfence", // Scenery: Fences and Walls
"rct2.scgwalls", // Scenery: Walls and Roofs
"rct2.scgpathx", // Scenery: Signs and Items for Footpaths
"rct2.wtrcyan", // Water: Natural Water
"rct2.pkent1", // Park Entrance: Traditional Park Entrance
"rct2.tarmac", // Footpath: Tarmac
"rct2.tarmacg", // Footpath: Green Tarmac Footpath
"rct2.tarmacb", // Footpath: Brown Tarmac Footpath
"rct2.pathspce", // Footpath: Space Style Footpath
"rct2.pathcrzy", // Footpath: Crazy Paving Footpath
"rct2.pathdirt", // Footpath: Dirt Footpath
"rct2.pathash", // Footpath: Ash Footpath
// An initial default selection + all standard footpaths + all standard stations
"rct2.scenery_group.scgtrees", // Scenery: Trees
"rct2.scenery_group.scgshrub", // Scenery: Shrubs and Ornaments
"rct2.scenery_group.scggardn", // Scenery: Gardens
"rct2.scenery_group.scgfence", // Scenery: Fences and Walls
"rct2.scenery_group.scgwalls", // Scenery: Walls and Roofs
"rct2.scenery_group.scgpathx", // Scenery: Signs and Items for Footpaths
"rct2.water.wtrcyan", // Water: Natural Water
"rct2.park_entrance.pkent1", // Park Entrance: Traditional Park Entrance
"rct2.terrain_surface.grass",
"rct2.terrain_edge.rock",
// Footpath surfaces
"rct2.footpath_surface.tarmac",
"rct2.footpath_surface.tarmac_brown",
"rct2.footpath_surface.tarmac_red",
"rct2.footpath_surface.dirt",
"rct2.footpath_surface.crazy_paving",
"rct2.footpath_surface.ash",
"rct2.footpath_surface.queue_blue",
"rct2.footpath_surface.queue_green",
"rct2.footpath_surface.queue_red",
"rct2.footpath_surface.queue_yellow",
// Footpath railings
"rct2.footpath_railings.bamboo_black",
"rct2.footpath_railings.bamboo_brown",
"rct2.footpath_railings.concrete",
"rct2.footpath_railings.concrete_green",
"rct2.footpath_railings.space",
"rct2.footpath_railings.wood",
// Stations
"rct2.station.plain",
"rct2.station.wooden",
"rct2.station.canvas_tent",
"rct2.station.castle_grey",
"rct2.station.castle_brown",
"rct2.station.jungle",
"rct2.station.log",
"rct2.station.classical",
"rct2.station.abstract",
"rct2.station.snow",
"rct2.station.pagoda",
"rct2.station.space",
};
// clang-format on

View File

@ -11,5 +11,6 @@
#include "Object.h"
extern const std::string_view DefaultSelectedObjects[33];
extern const std::string_view DesignerSelectedObjects[15];
extern const std::string_view MinimumRequiredObjects[2];
extern const std::string_view DefaultSelectedObjects[103];
extern const std::string_view DesignerSelectedObjects[38];

View File

@ -26,6 +26,11 @@ public:
return &_legacyType;
}
const void* GetLegacyData() const
{
return &_legacyType;
}
const PathSurfaceDescriptor& GetPathSurfaceDescriptor() const
{
return _pathSurfaceDescriptor;

View File

@ -88,6 +88,7 @@ void FootpathRailingsObject::ReadJson(IReadObjectContext* context, json_t& root)
{
{ "hasSupportImages", RAILING_ENTRY_FLAG_HAS_SUPPORT_BASE_SPRITE },
{ "hasElevatedPathImages", RAILING_ENTRY_FLAG_DRAW_PATH_OVER_SUPPORTS },
{ "noQueueBanner", RAILING_ENTRY_FLAG_NO_QUEUE_BANNER },
});
}

View File

@ -92,10 +92,8 @@ bool ObjectEntryDescriptor::operator==(const ObjectEntryDescriptor& rhs) const
{
return Entry == rhs.Entry;
}
else
{
return Type == rhs.Type && Identifier == rhs.Identifier;
}
return Type == rhs.Type && Identifier == rhs.Identifier;
}
bool ObjectEntryDescriptor::operator!=(const ObjectEntryDescriptor& rhs) const
@ -159,12 +157,12 @@ std::string Object::GetString(int32_t language, ObjectStringID index) const
ObjectEntryDescriptor Object::GetScgWallsHeader() const
{
return ObjectEntryDescriptor("rct2.scgwalls");
return ObjectEntryDescriptor("rct2.scenery_group.scgwalls");
}
ObjectEntryDescriptor Object::GetScgPathXHeader() const
{
return ObjectEntryDescriptor("rct2.scgpathx");
return ObjectEntryDescriptor("rct2.scenery_group.scgpathx");
}
rct_object_entry Object::CreateHeader(const char name[DAT_NAME_LENGTH + 1], uint32_t flags, uint32_t checksum)

View File

@ -385,7 +385,6 @@ extern int32_t object_entry_group_counts[];
extern int32_t object_entry_group_encoding[];
int32_t object_calculate_checksum(const rct_object_entry* entry, const void* data, size_t dataLength);
bool find_object_in_entry_group(const rct_object_entry* entry, ObjectType* entry_type, ObjectEntryIndex* entryIndex);
void object_create_identifier_name(char* string_buffer, size_t size, const rct_object_entry* object);
const rct_object_entry* object_list_find(rct_object_entry* entry);

View File

@ -11,18 +11,20 @@
#include <cstdint>
constexpr const uint16_t MAX_RIDE_OBJECTS = 128;
constexpr const uint16_t MAX_SMALL_SCENERY_OBJECTS = 252;
constexpr const uint16_t MAX_LARGE_SCENERY_OBJECTS = 128;
constexpr const uint16_t MAX_WALL_SCENERY_OBJECTS = 128;
constexpr const uint16_t MAX_BANNER_OBJECTS = 32;
constexpr const uint16_t MAX_PATH_OBJECTS = 16;
constexpr const uint16_t MAX_PATH_ADDITION_OBJECTS = 15;
constexpr const uint16_t MAX_SCENERY_GROUP_OBJECTS = 19;
// Maximums based on number of values that can be represented in bit group.
// Subtract 1 to reserve the NULL entry identifier.
constexpr const uint16_t MAX_RIDE_OBJECTS = 2047;
constexpr const uint16_t MAX_SMALL_SCENERY_OBJECTS = 2047;
constexpr const uint16_t MAX_LARGE_SCENERY_OBJECTS = 2047;
constexpr const uint16_t MAX_WALL_SCENERY_OBJECTS = 2047;
constexpr const uint16_t MAX_BANNER_OBJECTS = 255;
constexpr const uint16_t MAX_PATH_OBJECTS = 255;
constexpr const uint16_t MAX_PATH_ADDITION_OBJECTS = 255;
constexpr const uint16_t MAX_SCENERY_GROUP_OBJECTS = 255;
constexpr const uint16_t MAX_PARK_ENTRANCE_OBJECTS = 1;
constexpr const uint16_t MAX_WATER_OBJECTS = 1;
constexpr const uint16_t MAX_SCENARIO_TEXT_OBJECTS = 1;
constexpr const uint16_t MAX_TERRAIN_SURFACE_OBJECTS = 18;
constexpr const uint16_t MAX_SCENARIO_TEXT_OBJECTS = 0;
constexpr const uint16_t MAX_TERRAIN_SURFACE_OBJECTS = 255;
constexpr const uint16_t MAX_TERRAIN_EDGE_OBJECTS = 255;
constexpr const uint16_t MAX_STATION_OBJECTS = 255;
constexpr const uint16_t MAX_MUSIC_OBJECTS = 255;

View File

@ -199,39 +199,6 @@ void object_create_identifier_name(char* string_buffer, size_t size, const rct_o
snprintf(string_buffer, size, "%.8s/%4X%4X", object->name, object->flags, object->checksum);
}
/**
*
* rct2: 0x006A9DA2
* bl = entry_index
* ecx = entry_type
*/
bool find_object_in_entry_group(const rct_object_entry* entry, ObjectType* entry_type, ObjectEntryIndex* entryIndex)
{
ObjectType objectType = entry->GetType();
if (objectType >= ObjectType::Count)
{
return false;
}
auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager();
auto maxObjects = object_entry_group_counts[EnumValue(objectType)];
for (int32_t i = 0; i < maxObjects; i++)
{
auto loadedObj = objectMgr.GetLoadedObject(objectType, i);
if (loadedObj != nullptr)
{
auto thisEntry = object_entry_get_object(objectType, i)->GetObjectEntry();
if (thisEntry == *entry)
{
*entry_type = objectType;
*entryIndex = i;
return true;
}
}
}
return false;
}
void get_type_entry_index(size_t index, ObjectType* outObjectType, ObjectEntryIndex* outEntryIndex)
{
uint8_t objectType = EnumValue(ObjectType::Ride);

View File

@ -78,7 +78,10 @@ public:
if (index >= static_cast<size_t>(object_entry_group_counts[EnumValue(objectType)]))
{
#ifdef DEBUG
log_warning("Object index %u exceeds maximum for type %d.", index, objectType);
if (index != OBJECT_ENTRY_INDEX_NULL)
{
log_warning("Object index %u exceeds maximum for type %d.", index, objectType);
}
#endif
return nullptr;
}
@ -127,6 +130,24 @@ public:
return result;
}
ObjectList GetLoadedObjects() override
{
ObjectList objectList;
for (auto objectType = ObjectType::Ride; objectType < ObjectType::Count; objectType++)
{
auto maxObjectsOfType = static_cast<ObjectEntryIndex>(object_entry_group_counts[EnumValue(objectType)]);
for (ObjectEntryIndex i = 0; i < maxObjectsOfType; i++)
{
auto obj = GetLoadedObject(objectType, i);
if (obj != nullptr)
{
objectList.SetObject(i, obj->GetDescriptor());
}
}
}
return objectList;
}
Object* LoadObject(std::string_view identifier) override
{
const ObjectRepositoryItem* ori = _objectRepository.FindObject(identifier);
@ -153,9 +174,6 @@ public:
// Load the required objects
LoadObjects(requiredObjects);
// Load defaults.
LoadDefaultObjects();
// Update indices.
UpdateSceneryGroupIndexes();
ResetTypeToRideEntryIndexMap();
@ -219,10 +237,8 @@ public:
size_t numObjects = _objectRepository.GetNumObjects();
for (size_t i = 0; i < numObjects; i++)
{
// TODO: remove ObjectGeneration::DAT check when the NSF is here
const ObjectRepositoryItem* item = &_objectRepository.GetObjects()[i];
if (item->LoadedObject != nullptr && IsObjectCustom(item) && item->LoadedObject->GetLegacyData() != nullptr
&& item->LoadedObject->GetGeneration() == ObjectGeneration::DAT)
if (item->LoadedObject != nullptr && IsObjectCustom(item) && item->LoadedObject->GetLegacyData() != nullptr)
{
objects.push_back(item);
}
@ -230,99 +246,6 @@ public:
return objects;
}
void LoadDefaultObjects() override
{
// We currently will load new object types here that apply to all
// loaded RCT1 and RCT2 save files.
// Surfaces
LoadObject("rct2.surface.grass");
LoadObject("rct2.surface.sand");
LoadObject("rct2.surface.dirt");
LoadObject("rct2.surface.rock");
LoadObject("rct2.surface.martian");
LoadObject("rct2.surface.chequerboard");
LoadObject("rct2.surface.grassclumps");
LoadObject("rct2.surface.ice");
LoadObject("rct2.surface.gridred");
LoadObject("rct2.surface.gridyellow");
LoadObject("rct2.surface.gridpurple");
LoadObject("rct2.surface.gridgreen");
LoadObject("rct2.surface.sandred");
LoadObject("rct2.surface.sandbrown");
LoadObject("rct1.aa.surface.roofred");
LoadObject("rct1.ll.surface.roofgrey");
LoadObject("rct1.ll.surface.rust");
LoadObject("rct1.ll.surface.wood");
// Edges
LoadObject("rct2.edge.rock");
LoadObject("rct2.edge.woodred");
LoadObject("rct2.edge.woodblack");
LoadObject("rct2.edge.ice");
LoadObject("rct1.edge.brick");
LoadObject("rct1.edge.iron");
LoadObject("rct1.aa.edge.grey");
LoadObject("rct1.aa.edge.yellow");
LoadObject("rct1.aa.edge.red");
LoadObject("rct1.ll.edge.purple");
LoadObject("rct1.ll.edge.green");
LoadObject("rct1.ll.edge.stonebrown");
LoadObject("rct1.ll.edge.stonegrey");
LoadObject("rct1.ll.edge.skyscrapera");
LoadObject("rct1.ll.edge.skyscraperb");
// Stations
LoadObject("rct2.station.plain");
LoadObject("rct2.station.wooden");
LoadObject("rct2.station.canvastent");
LoadObject("rct2.station.castlegrey");
LoadObject("rct2.station.castlebrown");
LoadObject("rct2.station.jungle");
LoadObject("rct2.station.log");
LoadObject("rct2.station.classical");
LoadObject("rct2.station.abstract");
LoadObject("rct2.station.snow");
LoadObject("rct2.station.pagoda");
LoadObject("rct2.station.space");
LoadObject("openrct2.station.noentrance");
// Music
auto baseIndex = GetIndexFromTypeEntry(ObjectType::Music, 0);
LoadObject(baseIndex + MUSIC_STYLE_DODGEMS_BEAT, "rct2.music.dodgems");
LoadObject(baseIndex + MUSIC_STYLE_FAIRGROUND_ORGAN, "rct2.music.fairground");
LoadObject(baseIndex + MUSIC_STYLE_ROMAN_FANFARE, "rct2.music.roman");
LoadObject(baseIndex + MUSIC_STYLE_ORIENTAL, "rct2.music.oriental");
LoadObject(baseIndex + MUSIC_STYLE_MARTIAN, "rct2.music.martian");
LoadObject(baseIndex + MUSIC_STYLE_JUNGLE_DRUMS, "rct2.music.jungle");
LoadObject(baseIndex + MUSIC_STYLE_EGYPTIAN, "rct2.music.egyptian");
LoadObject(baseIndex + MUSIC_STYLE_TOYLAND, "rct2.music.toyland");
LoadObject(baseIndex + MUSIC_STYLE_SPACE, "rct2.music.space");
LoadObject(baseIndex + MUSIC_STYLE_HORROR, "rct2.music.horror");
LoadObject(baseIndex + MUSIC_STYLE_TECHNO, "rct2.music.techno");
LoadObject(baseIndex + MUSIC_STYLE_GENTLE, "rct2.music.gentle");
LoadObject(baseIndex + MUSIC_STYLE_SUMMER, "rct2.music.summer");
LoadObject(baseIndex + MUSIC_STYLE_WATER, "rct2.music.water");
LoadObject(baseIndex + MUSIC_STYLE_WILD_WEST, "rct2.music.wildwest");
LoadObject(baseIndex + MUSIC_STYLE_JURASSIC, "rct2.music.jurassic");
LoadObject(baseIndex + MUSIC_STYLE_ROCK, "rct2.music.rock1");
LoadObject(baseIndex + MUSIC_STYLE_RAGTIME, "rct2.music.ragtime");
LoadObject(baseIndex + MUSIC_STYLE_FANTASY, "rct2.music.fantasy");
LoadObject(baseIndex + MUSIC_STYLE_ROCK_STYLE_2, "rct2.music.rock2");
LoadObject(baseIndex + MUSIC_STYLE_ICE, "rct2.music.ice");
LoadObject(baseIndex + MUSIC_STYLE_SNOW, "rct2.music.snow");
LoadObject(baseIndex + MUSIC_STYLE_CUSTOM_MUSIC_1, "rct2.music.custom1");
LoadObject(baseIndex + MUSIC_STYLE_CUSTOM_MUSIC_2, "rct2.music.custom2");
LoadObject(baseIndex + MUSIC_STYLE_MEDIEVAL, "rct2.music.medieval");
LoadObject(baseIndex + MUSIC_STYLE_URBAN, "rct2.music.urban");
LoadObject(baseIndex + MUSIC_STYLE_ORGAN, "rct2.music.organ");
LoadObject(baseIndex + MUSIC_STYLE_MECHANICAL, "rct2.music.mechanical");
LoadObject(baseIndex + MUSIC_STYLE_MODERN, "rct2.music.modern");
LoadObject(baseIndex + MUSIC_STYLE_PIRATES, "rct2.music.pirate");
LoadObject(baseIndex + MUSIC_STYLE_ROCK_STYLE_3, "rct2.music.rock3");
LoadObject(baseIndex + MUSIC_STYLE_CANDY_STYLE, "rct2.music.candy");
}
static rct_string_id GetObjectSourceGameString(const ObjectSourceGame sourceGame)
{
switch (sourceGame)

View File

@ -31,12 +31,12 @@ struct IObjectManager
virtual ObjectEntryIndex GetLoadedObjectEntryIndex(std::string_view identifier) abstract;
virtual ObjectEntryIndex GetLoadedObjectEntryIndex(const ObjectEntryDescriptor& descriptor) abstract;
virtual ObjectEntryIndex GetLoadedObjectEntryIndex(const Object* object) abstract;
virtual ObjectList GetLoadedObjects() abstract;
virtual Object* LoadObject(std::string_view identifier) abstract;
virtual Object* LoadObject(const rct_object_entry* entry) abstract;
virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor) abstract;
virtual void LoadObjects(const ObjectList& entries) abstract;
virtual void LoadDefaultObjects() abstract;
virtual void UnloadObjects(const std::vector<ObjectEntryDescriptor>& entries) abstract;
virtual void UnloadAll() abstract;

View File

@ -612,10 +612,8 @@ private:
// Convert to UTF-8 filename
return String::Convert(normalisedName, CODE_PAGE::CP_1252, CODE_PAGE::CP_UTF8);
}
else
{
return std::string(name);
}
return std::string(name);
}
void WritePackedObject(OpenRCT2::IStream* stream, const rct_object_entry* entry)

View File

@ -207,7 +207,7 @@ void SmallSceneryObject::PerformFixes()
ObjectEntryDescriptor SmallSceneryObject::GetScgPiratHeader() const
{
return ObjectEntryDescriptor("rct2.scgpirat");
return ObjectEntryDescriptor("rct2.scenery_group.scgpirat");
}
ObjectEntryDescriptor SmallSceneryObject::GetScgMineHeader() const
@ -217,7 +217,7 @@ ObjectEntryDescriptor SmallSceneryObject::GetScgMineHeader() const
ObjectEntryDescriptor SmallSceneryObject::GetScgAbstrHeader() const
{
return ObjectEntryDescriptor("rct2.scgabstr");
return ObjectEntryDescriptor("rct2.scenery_group.scgabstr");
}
void SmallSceneryObject::ReadJson(IReadObjectContext* context, json_t& root)

View File

@ -95,6 +95,7 @@ void StationObject::ReadJson(IReadObjectContext* context, json_t& root)
{ "hasSecondaryColour", STATION_OBJECT_FLAGS::HAS_SECONDARY_COLOUR },
{ "isTransparent", STATION_OBJECT_FLAGS::IS_TRANSPARENT },
{ "noPlatforms", STATION_OBJECT_FLAGS::NO_PLATFORMS },
{ "hasShelter", STATION_OBJECT_FLAGS::HAS_SHELTER },
});
}

View File

@ -17,6 +17,7 @@ namespace STATION_OBJECT_FLAGS
const uint32_t HAS_SECONDARY_COLOUR = 1 << 1;
const uint32_t IS_TRANSPARENT = 1 << 2;
const uint32_t NO_PLATFORMS = 1 << 3;
const uint32_t HAS_SHELTER = (1 << 4);
} // namespace STATION_OBJECT_FLAGS
class StationObject final : public Object

View File

@ -410,7 +410,7 @@ static void sub_6A4101(
}
}
if (!pathElement.HasQueueBanner())
if (!pathElement.HasQueueBanner() || (pathPaintInfo.RailingFlags & RAILING_ENTRY_FLAG_NO_QUEUE_BANNER))
{
return;
}
@ -487,7 +487,7 @@ static void sub_6A4101(
uint32_t drawnCorners = 0;
// If the path is not drawn over the supports, then no corner sprites will be drawn (making double-width paths
// look like connected series of intersections).
if (pathElement.ShouldDrawPathOverSupports())
if (pathPaintInfo.RailingFlags & RAILING_ENTRY_FLAG_DRAW_PATH_OVER_SUPPORTS)
{
drawnCorners = (connectedEdges & FOOTPATH_PROPERTIES_EDGES_CORNERS_MASK) >> 4;
}
@ -661,7 +661,7 @@ static void sub_6A4101(
* @param pathElement (esp[0])
* @param connectedEdges (bp) (relative to the camera's rotation)
* @param height (dx)
* @param railingsDescriptor (0x00F3EF6C)
* @param pathPaintInfo (0x00F3EF6C)
* @param imageFlags (0x00F3EF70)
* @param sceneryImageFlags (0x00F3EF74)
*/
@ -1083,7 +1083,8 @@ void path_paint_box_support(
session, image_id | imageFlags, { 0, 0, height }, { boundBoxSize, 0 },
{ boundBoxOffset, height + boundingBoxZOffset });
if (!pathElement.IsQueue() && !pathElement.ShouldDrawPathOverSupports())
// TODO: Revert this when path import works correctly.
if (!pathElement.IsQueue() && !(pathPaintInfo.RailingFlags & RAILING_ENTRY_FLAG_DRAW_PATH_OVER_SUPPORTS))
{
// don't draw
}
@ -1223,7 +1224,8 @@ void path_paint_pole_support(
session, bridgeImage | imageFlags, { 0, 0, height }, { boundBoxSize, 0 },
{ boundBoxOffset, height + boundingBoxZOffset });
if (pathElement.IsQueue() || pathElement.ShouldDrawPathOverSupports())
// TODO: Revert this when path import works correctly.
if (pathElement.IsQueue() || (pathPaintInfo.RailingFlags & RAILING_ENTRY_FLAG_DRAW_PATH_OVER_SUPPORTS))
{
PaintAddImageAsChild(
session, imageId | imageFlags, 0, 0, boundBoxSize.x, boundBoxSize.y, 0, height, boundBoxOffset.x,

View File

@ -216,6 +216,11 @@ static void sub_68B3FB(paint_session* session, int32_t x, int32_t y)
int32_t previousBaseZ = 0;
do
{
if (tile_element->IsInvisible())
{
continue;
}
// Only paint tile_elements below the clip height.
if ((session->ViewFlags & VIEWPORT_FLAG_CLIP_VIEW) && (tile_element->GetBaseZ() > gClipHeight * COORDS_Z_STEP))
continue;
@ -233,6 +238,11 @@ static void sub_68B3FB(paint_session* session, int32_t x, int32_t y)
const TileElement* tile_element_sub_iterator = tile_element;
while (!(tile_element_sub_iterator++)->IsLastForTile())
{
if (tile_element->IsInvisible())
{
continue;
}
if (tile_element_sub_iterator->GetBaseZ() != tile_element->GetBaseZ())
{
break;
@ -245,15 +255,6 @@ static void sub_68B3FB(paint_session* session, int32_t x, int32_t y)
case TILE_ELEMENT_TYPE_TRACK:
session->TrackElementOnSameHeight = tile_element_sub_iterator;
break;
case TILE_ELEMENT_TYPE_CORRUPT:
// To preserve regular behaviour, make an element hidden by
// corruption also invisible to this method.
if (tile_element->IsLastForTile())
{
break;
}
tile_element_sub_iterator++;
break;
}
}
}
@ -287,16 +288,6 @@ static void sub_68B3FB(paint_session* session, int32_t x, int32_t y)
case TILE_ELEMENT_TYPE_BANNER:
PaintBanner(session, direction, baseZ, *(tile_element->AsBanner()));
break;
// A corrupt element inserted by OpenRCT2 itself, which skips the drawing of the next element only.
case TILE_ELEMENT_TYPE_CORRUPT:
if (tile_element->IsLastForTile())
return;
tile_element++;
break;
default:
// An undefined map element is most likely a corrupt element inserted by 8 cars' MOM feature to skip drawing of
// all elements after it.
return;
}
session->MapPosition = mapPosition;
} while (!(tile_element++)->IsLastForTile());

View File

@ -1475,7 +1475,7 @@ bool Guest::DecideAndBuyItem(Ride* ride, ShopItem shopItem, money32 price)
if (HasItem(shopItem))
{
InsertNewThought(PeepThoughtType::AlreadyGot, EnumValue(shopItem));
InsertNewThought(PeepThoughtType::AlreadyGot, shopItem);
return false;
}
@ -1484,7 +1484,7 @@ bool Guest::DecideAndBuyItem(Ride* ride, ShopItem shopItem, money32 price)
int32_t food = bitscanforward(GetFoodOrDrinkFlags());
if (food != -1)
{
InsertNewThought(PeepThoughtType::HaventFinished, food);
InsertNewThought(PeepThoughtType::HaventFinished, static_cast<ShopItem>(food));
return false;
}
@ -1533,7 +1533,7 @@ bool Guest::DecideAndBuyItem(Ride* ride, ShopItem shopItem, money32 price)
}
if (price > CashInPocket)
{
InsertNewThought(PeepThoughtType::CantAffordItem, EnumValue(shopItem));
InsertNewThought(PeepThoughtType::CantAffordItem, shopItem);
return false;
}
}
@ -2326,17 +2326,11 @@ bool Guest::HasRidden(const Ride* ride) const
void Guest::SetHasRiddenRideType(int32_t rideType)
{
// This is needed to avoid desyncs. TODO: remove once the new save format is introduced.
rideType = OpenRCT2RideTypeToRCT2RideType(rideType);
OpenRCT2::RideUse::GetTypeHistory().Add(sprite_index, rideType);
}
bool Guest::HasRiddenRideType(int32_t rideType) const
{
// This is needed to avoid desyncs. TODO: remove once the new save format is introduced.
rideType = OpenRCT2RideTypeToRCT2RideType(rideType);
return OpenRCT2::RideUse::GetTypeHistory().Contains(sprite_index, rideType);
}
@ -2543,7 +2537,7 @@ bool Guest::FindVehicleToEnter(Ride* ride, std::vector<uint8_t>& car_array)
{
chosen_train = ride->stations[CurrentRideStation].TrainAtStation;
}
if (chosen_train == RideStation::NO_TRAIN || chosen_train >= MAX_VEHICLES_PER_RIDE)
if (chosen_train >= MAX_VEHICLES_PER_RIDE)
{
return false;
}

View File

@ -15,7 +15,6 @@
class DataSerialiser;
#define STAFF_MAX_COUNT 200
// The number of elements in the gStaffPatrolAreas array per staff member. Every bit in the array represents a 4x4 square.
// Right now, it's a 32-bit array like in RCT2. 32 * 128 = 4096 bits, which is also the number of 4x4 squares on a 256x256 map.
constexpr size_t STAFF_PATROL_AREA_BLOCKS_PER_LINE = MAXIMUM_MAP_SIZE_TECHNICAL / 4;

View File

@ -27,6 +27,7 @@
# include "../Context.h"
# include "../Game.h"
# include "../OpenRCT2.h"
# include "../ParkFile.h"
# include "../PlatformEnvironment.h"
# include "../Version.h"
# include "../config/Config.h"
@ -37,7 +38,6 @@
# include "../interface/Screenshot.h"
# include "../localisation/Language.h"
# include "../object/ObjectManager.h"
# include "../rct2/S6Exporter.h"
# include "../scenario/Scenario.h"
# include "../util/SawyerCoding.h"
# include "../util/Util.h"
@ -119,13 +119,11 @@ static bool OnCrash(
wchar_t dumpFilePath[MAX_PATH];
wchar_t saveFilePath[MAX_PATH];
wchar_t configFilePath[MAX_PATH];
wchar_t saveFilePathGZIP[MAX_PATH];
wchar_t recordFilePathNew[MAX_PATH];
swprintf_s(dumpFilePath, std::size(dumpFilePath), L"%s\\%s.dmp", dumpPath, miniDumpId);
swprintf_s(saveFilePath, std::size(saveFilePath), L"%s\\%s.sv6", dumpPath, miniDumpId);
swprintf_s(saveFilePath, std::size(saveFilePath), L"%s\\%s.park", dumpPath, miniDumpId);
swprintf_s(configFilePath, std::size(configFilePath), L"%s\\%s.ini", dumpPath, miniDumpId);
swprintf_s(saveFilePathGZIP, std::size(saveFilePathGZIP), L"%s\\%s.sv6.gz", dumpPath, miniDumpId);
swprintf_s(recordFilePathNew, std::size(recordFilePathNew), L"%s\\%s.sv6r", dumpPath, miniDumpId);
swprintf_s(recordFilePathNew, std::size(recordFilePathNew), L"%s\\%s.parkrep", dumpPath, miniDumpId);
wchar_t dumpFilePathNew[MAX_PATH];
swprintf_s(
@ -177,7 +175,7 @@ static bool OnCrash(
auto saveFilePathUTF8 = String::ToUtf8(saveFilePath);
try
{
auto exporter = std::make_unique<S6Exporter>();
auto exporter = std::make_unique<ParkFileExporter>();
// Make sure the save is using the current viewport settings.
viewport_set_saved_view();
@ -190,8 +188,7 @@ static bool OnCrash(
auto& objManager = ctx->GetObjectManager();
exporter->ExportObjectsList = objManager.GetPackableObjects();
exporter->Export();
exporter->SaveGame(saveFilePathUTF8.c_str());
exporter->Export(saveFilePathUTF8.c_str());
savedGameDumped = true;
}
catch (const std::exception&)
@ -201,19 +198,7 @@ static bool OnCrash(
// Compress the save
if (savedGameDumped)
{
FILE* input = _wfopen(saveFilePath, L"rb");
FILE* dest = _wfopen(saveFilePathGZIP, L"wb");
if (util_gzip_compress(input, dest))
{
uploadFiles[L"attachment_park.sv6.gz"] = saveFilePathGZIP;
}
else
{
uploadFiles[L"attachment_park.sv6"] = saveFilePath;
}
fclose(input);
fclose(dest);
uploadFiles[L"attachment_park.park"] = saveFilePath;
}
auto configFilePathUTF8 = String::ToUtf8(configFilePath);
@ -231,11 +216,11 @@ static bool OnCrash(
if (with_record)
{
auto sv6rPathW = String::ToWideChar(gSilentRecordingName);
bool record_copied = CopyFileW(sv6rPathW.c_str(), recordFilePathNew, true);
auto parkReplayPathW = String::ToWideChar(gSilentRecordingName);
bool record_copied = CopyFileW(parkReplayPathW.c_str(), recordFilePathNew, true);
if (record_copied)
{
uploadFiles[L"attachment_replay.sv6r"] = recordFilePathNew;
uploadFiles[L"attachment_replay.parkrep"] = recordFilePathNew;
}
else
{
@ -299,7 +284,6 @@ static bool OnCrash(
if (savedGameDumped)
{
files[numFiles++] = ILCreateFromPathW(saveFilePath);
files[numFiles++] = ILCreateFromPathW(saveFilePathGZIP);
}
if (with_record)
{

View File

@ -81,7 +81,7 @@ namespace RCT1
class EntryList
{
private:
std::vector<const char*> _entries;
std::vector<std::string> _entries;
public:
size_t GetCount() const
@ -89,20 +89,22 @@ namespace RCT1
return _entries.size();
}
const std::vector<const char*>& GetEntries() const
const std::vector<std::string>& GetEntries() const
{
return _entries;
}
ObjectEntryIndex GetOrAddEntry(const char* entryName)
ObjectEntryIndex GetOrAddEntry(std::string_view identifier)
{
auto entryIndex = Collections::IndexOf(_entries, entryName, true);
if (entryIndex == SIZE_MAX)
for (size_t i = 0; i < _entries.size(); i++)
{
entryIndex = _entries.size();
_entries.push_back(entryName);
if (_entries[i] == identifier)
{
return static_cast<ObjectEntryIndex>(i);
}
}
return static_cast<ObjectEntryIndex>(entryIndex);
_entries.emplace_back(identifier);
return static_cast<ObjectEntryIndex>(_entries.size() - 1);
}
void AddRange(std::initializer_list<const char*> initializerList)
@ -132,6 +134,10 @@ namespace RCT1
EntryList _pathAdditionEntries;
EntryList _sceneryGroupEntries;
EntryList _waterEntry;
EntryList _terrainSurfaceEntries;
EntryList _terrainEdgeEntries;
EntryList _footpathSurfaceEntries;
EntryList _footpathRailingsEntries;
// Lookup tables for converting from RCT1 hard coded types to the new dynamic object entries
ObjectEntryIndex _rideTypeToRideEntryMap[EnumValue(RideType::Count)]{};
@ -142,6 +148,10 @@ namespace RCT1
ObjectEntryIndex _pathTypeToEntryMap[24]{};
ObjectEntryIndex _pathAdditionTypeToEntryMap[16]{};
ObjectEntryIndex _sceneryThemeTypeToEntryMap[24]{};
ObjectEntryIndex _terrainSurfaceTypeToEntryMap[16]{};
ObjectEntryIndex _terrainEdgeTypeToEntryMap[16]{};
ObjectEntryIndex _footpathSurfaceTypeToEntryMap[32]{};
ObjectEntryIndex _footpathRailingsTypeToEntryMap[4]{};
// Research
std::bitset<MAX_RIDE_OBJECTS> _researchRideEntryUsed{};
@ -199,7 +209,6 @@ namespace RCT1
Initialise();
CreateAvailableObjectMappings();
LoadObjects();
ImportRides();
ImportRideMeasurements();
@ -222,6 +231,10 @@ namespace RCT1
map_count_remaining_land_rights();
research_determine_first_of_type();
CheatsReset();
ClearRestrictedScenery();
RestrictAllMiscScenery();
}
bool GetDetails(scenario_index_entry* dst) override
@ -343,11 +356,10 @@ namespace RCT1
uint16_t mapSize = _s4.map_size == 0 ? RCT1_MAX_MAP_SIZE : _s4.map_size;
String::Set(gScenarioFileName, sizeof(gScenarioFileName), GetRCT1ScenarioName().c_str());
gScenarioFileName = GetRCT1ScenarioName();
// Do map initialisation, same kind of stuff done when loading scenario editor
auto context = OpenRCT2::GetContext();
context->GetObjectManager().UnloadAll();
context->GetGameState()->InitAll(mapSize);
gEditorStep = EditorStep::ObjectSelection;
gParkFlags |= PARK_FLAGS_SHOW_REAL_GUEST_NAMES;
@ -375,6 +387,14 @@ namespace RCT1
std::fill(std::begin(_pathTypeToEntryMap), std::end(_pathTypeToEntryMap), OBJECT_ENTRY_INDEX_NULL);
std::fill(std::begin(_pathAdditionTypeToEntryMap), std::end(_pathAdditionTypeToEntryMap), OBJECT_ENTRY_INDEX_NULL);
std::fill(std::begin(_sceneryThemeTypeToEntryMap), std::end(_sceneryThemeTypeToEntryMap), OBJECT_ENTRY_INDEX_NULL);
std::fill(
std::begin(_terrainSurfaceTypeToEntryMap), std::end(_terrainSurfaceTypeToEntryMap), OBJECT_ENTRY_INDEX_NULL);
std::fill(std::begin(_terrainEdgeTypeToEntryMap), std::end(_terrainEdgeTypeToEntryMap), OBJECT_ENTRY_INDEX_NULL);
std::fill(
std::begin(_footpathSurfaceTypeToEntryMap), std::end(_footpathSurfaceTypeToEntryMap), OBJECT_ENTRY_INDEX_NULL);
std::fill(
std::begin(_footpathRailingsTypeToEntryMap), std::end(_footpathRailingsTypeToEntryMap),
OBJECT_ENTRY_INDEX_NULL);
}
/**
@ -395,25 +415,42 @@ namespace RCT1
{
// Add default scenery groups
_sceneryGroupEntries.AddRange({
"SCGTREES",
"SCGSHRUB",
"SCGGARDN",
"SCGFENCE",
"SCGWALLS",
"SCGPATHX",
"rct2.scenery_group.scgtrees",
"rct2.scenery_group.scgshrub",
"rct2.scenery_group.scggardn",
"rct2.scenery_group.scgfence",
"rct2.scenery_group.scgwalls",
"rct2.scenery_group.scgpathx",
});
// Add default footpaths
_pathEntries.AddRange({
"TARMAC ",
"TARMACG ",
"TARMACB ",
"PATHCRZY",
"PATHSPCE",
"PATHDIRT",
"PATHASH ",
"ROAD ",
});
_footpathSurfaceEntries.AddRange(
{ "rct1.footpath_surface.tarmac", "rct1.footpath_surface.dirt", "rct1.footpath_surface.crazy_paving",
"rct1.footpath_surface.tiles_brown", "rct1aa.footpath_surface.ash", "rct1aa.footpath_surface.tarmac_green",
"rct1aa.footpath_surface.tarmac_brown", "rct1aa.footpath_surface.tiles_grey",
"rct1aa.footpath_surface.tarmac_red", "rct1ll.footpath_surface.tiles_green",
"rct1ll.footpath_surface.tiles_red", "rct1.footpath_surface.queue_blue", "rct1aa.footpath_surface.queue_red",
"rct1aa.footpath_surface.queue_yellow", "rct1aa.footpath_surface.queue_green" });
_footpathRailingsEntries.AddRange({ "rct2.footpath_railings.wood", "rct1ll.footpath_railings.space",
"rct1ll.footpath_railings.bamboo", "rct2.footpath_railings.concrete" });
// Add default surfaces
_terrainSurfaceEntries.AddRange(
{ "rct2.terrain_surface.grass", "rct2.terrain_surface.sand", "rct2.terrain_surface.dirt",
"rct2.terrain_surface.rock", "rct2.terrain_surface.martian", "rct2.terrain_surface.chequerboard",
"rct2.terrain_surface.grass_clumps", "rct2.terrain_surface.ice", "rct2.terrain_surface.grid_red",
"rct2.terrain_surface.grid_yellow", "rct2.terrain_surface.grid_purple", "rct2.terrain_surface.grid_green",
"rct2.terrain_surface.sand_red", "rct2.terrain_surface.sand_brown", "rct1aa.terrain_surface.roof_red",
"rct1ll.terrain_surface.roof_grey", "rct1ll.terrain_surface.rust", "rct1ll.terrain_surface.wood" });
// Add default edges
_terrainEdgeEntries.AddRange({ "rct2.terrain_edge.rock", "rct2.terrain_edge.wood_red",
"rct2.terrain_edge.wood_black", "rct2.terrain_edge.ice", "rct1.terrain_edge.brick",
"rct1.terrain_edge.iron", "rct1aa.terrain_edge.grey", "rct1aa.terrain_edge.yellow",
"rct1aa.terrain_edge.red", "rct1ll.terrain_edge.purple", "rct1ll.terrain_edge.green",
"rct1ll.terrain_edge.stone_brown", "rct1ll.terrain_edge.stone_grey",
"rct1ll.terrain_edge.skyscraper_a", "rct1ll.terrain_edge.skyscraper_b" });
}
void AddAvailableEntriesFromResearchList()
@ -469,13 +506,28 @@ namespace RCT1
{
switch (tileElement->GetType())
{
case TILE_ELEMENT_TYPE_SURFACE:
{
auto surfaceEl = tileElement->AsSurface();
auto surfaceStyle = surfaceEl->GetSurfaceStyle();
auto edgeStyle = surfaceEl->GetEdgeStyle();
AddEntryForTerrainSurface(surfaceStyle);
AddEntryForTerrainEdge(edgeStyle);
break;
}
case TILE_ELEMENT_TYPE_PATH:
{
uint8_t pathType = tileElement->AsPath()->GetRCT1PathType();
uint8_t pathAdditionsType = tileElement->AsPath()->GetAddition();
uint8_t footpathRailingsType = RCT1_PATH_SUPPORT_TYPE_TRUSS;
if (_gameVersion == FILE_VERSION_RCT1_LL)
{
footpathRailingsType = tileElement->AsPath()->GetRCT1SupportType();
}
AddEntryForPath(pathType);
AddEntryForPathAddition(pathAdditionsType);
AddEntryForPathSurface(pathType);
AddEntryForFootpathRailings(footpathRailingsType);
break;
}
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
@ -528,14 +580,14 @@ namespace RCT1
if (sceneryTheme != 0 && _sceneryThemeTypeToEntryMap[sceneryTheme] == OBJECT_ENTRY_INDEX_NULL)
continue;
std::vector<const char*> objects = RCT1::GetSceneryObjects(sceneryTheme);
for (const char* objectName : objects)
auto objects = RCT1::GetSceneryObjects(sceneryTheme);
for (auto objectName : objects)
{
auto& objectRepository = OpenRCT2::GetContext()->GetObjectRepository();
auto foundObject = objectRepository.FindObjectLegacy(objectName);
auto foundObject = objectRepository.FindObject(objectName);
if (foundObject != nullptr)
{
ObjectType objectType = foundObject->ObjectEntry.GetType();
auto objectType = foundObject->Type;
switch (objectType)
{
case ObjectType::SmallScenery:
@ -559,14 +611,17 @@ namespace RCT1
break;
}
}
else
{
log_error("Cannot find object %s", objectName);
}
}
}
}
void AddEntryForWater()
{
const char* entryName;
std::string_view entryName;
if (_gameVersion < FILE_VERSION_RCT1_LL)
{
entryName = RCT1::GetWaterObject(RCT1_WATER_CYAN);
@ -575,7 +630,6 @@ namespace RCT1
{
entryName = RCT1::GetWaterObject(_s4.water_colour);
}
_waterEntry.GetOrAddEntry(entryName);
}
@ -585,8 +639,8 @@ namespace RCT1
if (_rideTypeToRideEntryMap[EnumValue(rideType)] == OBJECT_ENTRY_INDEX_NULL)
{
const char* entryName = RCT1::GetRideTypeObject(rideType);
if (!String::Equals(entryName, " "))
auto entryName = RCT1::GetRideTypeObject(rideType);
if (!entryName.empty())
{
auto entryIndex = _rideEntries.GetOrAddEntry(entryName);
_rideTypeToRideEntryMap[EnumValue(rideType)] = entryIndex;
@ -600,8 +654,8 @@ namespace RCT1
if (_vehicleTypeToRideEntryMap[vehicleType] == OBJECT_ENTRY_INDEX_NULL)
{
const char* entryName = RCT1::GetVehicleObject(vehicleType);
if (!String::Equals(entryName, " "))
auto entryName = RCT1::GetVehicleObject(vehicleType);
if (!entryName.empty())
{
auto entryIndex = _rideEntries.GetOrAddEntry(entryName);
_vehicleTypeToRideEntryMap[vehicleType] = entryIndex;
@ -617,7 +671,7 @@ namespace RCT1
assert(smallSceneryType < std::size(_smallSceneryTypeToEntryMap));
if (_smallSceneryTypeToEntryMap[smallSceneryType] == OBJECT_ENTRY_INDEX_NULL)
{
const char* entryName = RCT1::GetSmallSceneryObject(smallSceneryType);
auto entryName = RCT1::GetSmallSceneryObject(smallSceneryType);
auto entryIndex = _smallSceneryEntries.GetOrAddEntry(entryName);
_smallSceneryTypeToEntryMap[smallSceneryType] = entryIndex;
@ -629,7 +683,7 @@ namespace RCT1
assert(largeSceneryType < std::size(_largeSceneryTypeToEntryMap));
if (_largeSceneryTypeToEntryMap[largeSceneryType] == OBJECT_ENTRY_INDEX_NULL)
{
const char* entryName = RCT1::GetLargeSceneryObject(largeSceneryType);
auto entryName = RCT1::GetLargeSceneryObject(largeSceneryType);
auto entryIndex = _largeSceneryEntries.GetOrAddEntry(entryName);
_largeSceneryTypeToEntryMap[largeSceneryType] = entryIndex;
@ -641,23 +695,23 @@ namespace RCT1
assert(wallType < std::size(_wallTypeToEntryMap));
if (_wallTypeToEntryMap[wallType] == OBJECT_ENTRY_INDEX_NULL)
{
const char* entryName = RCT1::GetWallObject(wallType);
auto entryName = RCT1::GetWallObject(wallType);
auto entryIndex = _wallEntries.GetOrAddEntry(entryName);
_wallTypeToEntryMap[wallType] = entryIndex;
}
}
void AddEntryForPath(ObjectEntryIndex pathType)
void AddEntryForPathSurface(ObjectEntryIndex pathType)
{
assert(pathType < std::size(_pathTypeToEntryMap));
if (_pathTypeToEntryMap[pathType] == OBJECT_ENTRY_INDEX_NULL)
assert(pathType < std::size(_footpathSurfaceTypeToEntryMap));
if (_footpathSurfaceTypeToEntryMap[pathType] == OBJECT_ENTRY_INDEX_NULL)
{
const char* entryName = RCT1::GetPathObject(pathType);
if (!String::Equals(entryName, " "))
auto identifier = RCT1::GetPathSurfaceObject(pathType);
if (!identifier.empty())
{
auto entryIndex = _pathEntries.GetOrAddEntry(entryName);
_pathTypeToEntryMap[pathType] = entryIndex;
auto entryIndex = _footpathSurfaceEntries.GetOrAddEntry(identifier);
_footpathSurfaceTypeToEntryMap[pathType] = entryIndex;
}
}
}
@ -672,7 +726,7 @@ namespace RCT1
uint8_t normalisedPathAdditionType = RCT1::NormalisePathAddition(pathAdditionType);
if (_pathAdditionTypeToEntryMap[normalisedPathAdditionType] == OBJECT_ENTRY_INDEX_NULL)
{
const char* entryName = RCT1::GetPathAddtionObject(normalisedPathAdditionType);
auto entryName = RCT1::GetPathAddtionObject(normalisedPathAdditionType);
auto entryIndex = _pathAdditionEntries.GetOrAddEntry(entryName);
_pathAdditionTypeToEntryMap[normalisedPathAdditionType] = entryIndex;
@ -691,7 +745,7 @@ namespace RCT1
}
else
{
const char* entryName = RCT1::GetSceneryGroupObject(sceneryThemeType);
auto entryName = RCT1::GetSceneryGroupObject(sceneryThemeType);
if (_sceneryGroupEntries.GetCount() >= MAX_SCENERY_GROUP_OBJECTS)
{
Console::WriteLine("Warning: More than %d (max scenery groups) in RCT1 park.", MAX_SCENERY_GROUP_OBJECTS);
@ -705,6 +759,48 @@ namespace RCT1
}
}
void AddEntryForTerrainSurface(ObjectEntryIndex terrainSurfaceType)
{
assert(terrainSurfaceType < std::size(_terrainSurfaceTypeToEntryMap));
if (_terrainSurfaceTypeToEntryMap[terrainSurfaceType] == OBJECT_ENTRY_INDEX_NULL)
{
auto identifier = RCT1::GetTerrainSurfaceObject(terrainSurfaceType);
if (!identifier.empty())
{
auto entryIndex = _terrainSurfaceEntries.GetOrAddEntry(identifier);
_terrainSurfaceTypeToEntryMap[terrainSurfaceType] = entryIndex;
}
}
}
void AddEntryForTerrainEdge(ObjectEntryIndex terrainEdgeType)
{
assert(terrainEdgeType < std::size(_terrainEdgeTypeToEntryMap));
if (_terrainEdgeTypeToEntryMap[terrainEdgeType] == OBJECT_ENTRY_INDEX_NULL)
{
auto identifier = RCT1::GetTerrainEdgeObject(terrainEdgeType);
if (!identifier.empty())
{
auto entryIndex = _terrainEdgeEntries.GetOrAddEntry(identifier);
_terrainEdgeTypeToEntryMap[terrainEdgeType] = entryIndex;
}
}
}
void AddEntryForFootpathRailings(ObjectEntryIndex railingsType)
{
assert(railingsType < std::size(_footpathRailingsTypeToEntryMap));
if (_footpathRailingsTypeToEntryMap[railingsType] == OBJECT_ENTRY_INDEX_NULL)
{
auto identifier = RCT1::GetFootpathRailingsObject(railingsType);
if (!identifier.empty())
{
auto entryIndex = _footpathRailingsEntries.GetOrAddEntry(identifier);
_footpathRailingsTypeToEntryMap[railingsType] = entryIndex;
}
}
}
void ImportRides()
{
for (int32_t i = 0; i < RCT12_MAX_RIDES_IN_PARK; i++)
@ -1020,8 +1116,20 @@ namespace RCT1
dst->track_colour[i].additional = RCT1::GetColour(src->track_colour_additional[i]);
dst->track_colour[i].supports = RCT1::GetColour(src->track_colour_supports[i]);
}
}
dst->entrance_style = OBJECT_ENTRY_INDEX_NULL;
if (dst->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT))
{
// Entrance styles were introduced with AA. They correspond directly with those in RCT2.
dst->entrance_style = src->entrance_style;
if (_gameVersion == FILE_VERSION_RCT1)
{
dst->entrance_style = 0; // plain entrance
}
else
{
dst->entrance_style = src->entrance_style;
}
}
if (_gameVersion < FILE_VERSION_RCT1_LL && dst->type == RIDE_TYPE_MERRY_GO_ROUND)
@ -1130,7 +1238,6 @@ namespace RCT1
{
ImportEntity(_s4.sprites[i].unknown);
}
FixImportStaff();
}
void SetVehicleColours(::Vehicle* dst, const RCT1::Vehicle* src)
@ -1375,83 +1482,24 @@ namespace RCT1
}
}
void LoadObjects()
void AppendRequiredObjects(ObjectList& objectList, ObjectType objectType, const EntryList& entryList)
{
auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
objectManager.LoadDefaultObjects();
LoadObjects(ObjectType::Ride, _rideEntries);
LoadObjects(ObjectType::SmallScenery, _smallSceneryEntries);
LoadObjects(ObjectType::LargeScenery, _largeSceneryEntries);
LoadObjects(ObjectType::Walls, _wallEntries);
LoadObjects(ObjectType::Paths, _pathEntries);
LoadObjects(ObjectType::PathBits, _pathAdditionEntries);
LoadObjects(ObjectType::SceneryGroup, _sceneryGroupEntries);
LoadObjects(
ObjectType::Banners,
std::vector<const char*>({
"BN1 ",
"BN2 ",
"BN3 ",
"BN4 ",
"BN5 ",
"BN6 ",
"BN7 ",
"BN8 ",
"BN9 ",
}));
LoadObjects(ObjectType::ParkEntrance, std::vector<const char*>({ "PKENT1 " }));
LoadObjects(ObjectType::Water, _waterEntry);
AppendRequiredObjects(objectList, objectType, entryList.GetEntries());
}
void LoadObjects(ObjectType objectType, const EntryList& entries)
void AppendRequiredObjects(ObjectList& objectList, ObjectType objectType, const std::vector<std::string>& objectNames)
{
LoadObjects(objectType, entries.GetEntries());
}
void LoadObjects(ObjectType objectType, const std::vector<const char*>& entries)
{
auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
uint32_t entryIndex = 0;
for (const char* objectName : entries)
for (const auto& objectName : objectNames)
{
rct_object_entry entry;
entry.flags = 0x00008000 + EnumValue(objectType);
std::copy_n(objectName, 8, entry.name);
entry.checksum = 0;
Object* object = objectManager.LoadObject(&entry);
if (object == nullptr && objectType != ObjectType::SceneryGroup)
{
log_error("Failed to load %s.", objectName);
throw std::runtime_error("Failed to load object.");
}
entryIndex++;
}
}
void AppendRequiredObjects(std::vector<rct_object_entry>& entries, ObjectType objectType, const EntryList& entryList)
{
AppendRequiredObjects(entries, objectType, entryList.GetEntries());
}
void AppendRequiredObjects(
std::vector<rct_object_entry>& entries, ObjectType objectType, const std::vector<const char*>& objectNames)
{
for (const auto objectName : objectNames)
{
rct_object_entry entry{};
entry.flags = ((static_cast<uint8_t>(ObjectSourceGame::RCT2) << 4) & 0xF0) | (EnumValue(objectType) & 0x0F);
entry.SetName(objectName);
entries.push_back(entry);
auto descriptor = ObjectEntryDescriptor(objectName);
descriptor.Type = objectType;
objectList.Add(descriptor);
}
}
ObjectList GetRequiredObjects()
{
std::vector<rct_object_entry> result;
ObjectList result;
AppendRequiredObjects(result, ObjectType::Ride, _rideEntries);
AppendRequiredObjects(result, ObjectType::SmallScenery, _smallSceneryEntries);
AppendRequiredObjects(result, ObjectType::LargeScenery, _largeSceneryEntries);
@ -1461,27 +1509,25 @@ namespace RCT1
AppendRequiredObjects(result, ObjectType::SceneryGroup, _sceneryGroupEntries);
AppendRequiredObjects(
result, ObjectType::Banners,
std::vector<const char*>({
"BN1 ",
"BN2 ",
"BN3 ",
"BN4 ",
"BN5 ",
"BN6 ",
"BN7 ",
"BN8 ",
"BN9 ",
std::vector<std::string>({
"rct2.footpath_banner.bn1",
"rct2.footpath_banner.bn2",
"rct2.footpath_banner.bn3",
"rct2.footpath_banner.bn4",
"rct2.footpath_banner.bn5",
"rct2.footpath_banner.bn6",
"rct2.footpath_banner.bn7",
"rct2.footpath_banner.bn8",
"rct2.footpath_banner.bn9",
}));
AppendRequiredObjects(result, ObjectType::ParkEntrance, std::vector<const char*>({ "PKENT1 " }));
AppendRequiredObjects(result, ObjectType::ParkEntrance, std::vector<std::string>({ "rct2.park_entrance.pkent1" }));
AppendRequiredObjects(result, ObjectType::Water, _waterEntry);
ObjectList objectList;
for (rct_object_entry entry : result)
{
objectList.Add(ObjectEntryDescriptor(entry));
}
return objectList;
AppendRequiredObjects(result, ObjectType::TerrainSurface, _terrainSurfaceEntries);
AppendRequiredObjects(result, ObjectType::TerrainEdge, _terrainEdgeEntries);
AppendRequiredObjects(result, ObjectType::FootpathSurface, _footpathSurfaceEntries);
AppendRequiredObjects(result, ObjectType::FootpathRailings, _footpathRailingsEntries);
RCT12AddDefaultObjects(result);
return result;
}
void ImportTileElements()
@ -1555,9 +1601,12 @@ namespace RCT1
auto dst2 = dst->AsSurface();
auto src2 = src->AsSurface();
auto surfaceStyle = _terrainSurfaceTypeToEntryMap[src2->GetSurfaceStyle()];
auto edgeStyle = _terrainEdgeTypeToEntryMap[src2->GetEdgeStyle()];
dst2->SetSlope(src2->GetSlope());
dst2->SetSurfaceStyle(RCT1::GetTerrain(src2->GetSurfaceStyle()));
dst2->SetEdgeStyle(RCT1::GetTerrainEdge(src2->GetEdgeStyle()));
dst2->SetSurfaceStyle(surfaceStyle);
dst2->SetEdgeStyle(edgeStyle);
dst2->SetGrassLength(src2->GetGrassLength());
dst2->SetOwnership(src2->GetOwnership());
dst2->SetParkFences(src2->GetParkFences());
@ -1586,7 +1635,7 @@ namespace RCT1
// Type
uint8_t pathType = src2->GetRCT1PathType();
auto entryIndex = _pathTypeToEntryMap[pathType];
auto entryIndex = _footpathSurfaceTypeToEntryMap[pathType];
dst2->SetDirection(0);
dst2->SetIsBroken(false);
@ -1598,7 +1647,14 @@ namespace RCT1
{
dst2->SetIsQueue(true);
}
// TODO: Set railings type
uint8_t railingsType = RCT1_PATH_SUPPORT_TYPE_TRUSS;
if (_gameVersion == FILE_VERSION_RCT1_LL)
{
railingsType = src2->GetRCT1SupportType();
}
auto railingsEntryIndex = _footpathRailingsTypeToEntryMap[railingsType];
dst2->SetRailingsEntryIndex(railingsEntryIndex);
// Additions
ObjectEntryIndex additionType = dst2->GetAddition();
@ -1713,8 +1769,8 @@ namespace RCT1
{
pathType = RCT1_FOOTPATH_TYPE_TARMAC_GREY;
}
auto entryIndex = _pathTypeToEntryMap[pathType];
dst2->SetLegacyPathEntryIndex(entryIndex & 0x7F);
auto entryIndex = _footpathSurfaceTypeToEntryMap[pathType];
dst2->SetSurfaceEntryIndex(entryIndex);
}
return 1;
@ -2112,9 +2168,13 @@ namespace RCT1
}
// Number of guests history
for (size_t i = 0; i < 32; i++)
std::fill(std::begin(gGuestsInParkHistory), std::end(gGuestsInParkHistory), std::numeric_limits<uint32_t>::max());
for (size_t i = 0; i < std::size(_s4.guests_in_park_history); i++)
{
gGuestsInParkHistory[i] = _s4.guests_in_park_history[i];
if (_s4.guests_in_park_history[i] != std::numeric_limits<uint8_t>::max())
{
gGuestsInParkHistory[i] = _s4.guests_in_park_history[i] * 20;
}
}
// News items
@ -2343,7 +2403,10 @@ namespace RCT1
void ImportBanner(Banner* dst, const RCT12Banner* src)
{
auto id = dst->id;
*dst = {};
dst->id = id;
dst->type = RCTEntryIndexToOpenRCT2EntryIndex(src->type);
dst->flags = 0;

View File

@ -144,19 +144,16 @@ namespace RCT1
}
// Convert RCT1 vehicle type to RCT2 vehicle type. Initialise with a string consisting of 8 spaces.
rct_object_entry vehicleObject = { 0x80, " " };
std::string_view vehicleObject;
if (td4Base.type == RideType::HedgeMaze)
{
const char* vehObjName = RCT1::GetRideTypeObject(td4Base.type);
assert(vehObjName != nullptr);
std::memcpy(vehicleObject.name, vehObjName, std::min(String::SizeOf(vehObjName), static_cast<size_t>(8)));
vehicleObject = RCT1::GetRideTypeObject(td4Base.type);
}
else
{
const char* vehObjName = RCT1::GetVehicleObject(td4Base.vehicle_type);
assert(vehObjName != nullptr);
std::memcpy(vehicleObject.name, vehObjName, std::min(String::SizeOf(vehObjName), static_cast<size_t>(8)));
vehicleObject = RCT1::GetVehicleObject(td4Base.vehicle_type);
}
assert(!vehicleObject.empty());
td->vehicle_object = ObjectEntryDescriptor(vehicleObject);
td->vehicle_type = td4Base.vehicle_type;

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,6 @@ namespace RCT1
colour_t GetColour(colour_t colour);
PeepSpriteType GetPeepSpriteType(uint8_t rct1SpriteType);
ObjectEntryIndex GetTerrain(uint8_t terrain);
ObjectEntryIndex GetTerrainEdge(uint8_t terrainEdge);
uint8_t GetRideType(RideType rideType, uint8_t vehicleType);
VehicleColourSchemeCopyDescriptor GetColourSchemeCopyDescriptor(uint8_t vehicleType);
@ -35,15 +33,18 @@ namespace RCT1
uint8_t NormalisePathAddition(uint8_t pathAdditionType);
uint8_t GetVehicleSubEntryIndex(uint8_t vehicleSubEntry);
const char* GetRideTypeObject(RideType rideType);
const char* GetVehicleObject(uint8_t vehicleType);
const char* GetSmallSceneryObject(uint8_t smallSceneryType);
const char* GetLargeSceneryObject(uint8_t largeSceneryType);
const char* GetWallObject(uint8_t wallType);
const char* GetPathObject(uint8_t pathType);
const char* GetPathAddtionObject(uint8_t pathAdditionType);
const char* GetSceneryGroupObject(uint8_t sceneryGroupType);
const char* GetWaterObject(uint8_t waterType);
std::string_view GetRideTypeObject(RideType rideType);
std::string_view GetVehicleObject(uint8_t vehicleType);
std::string_view GetSmallSceneryObject(uint8_t smallSceneryType);
std::string_view GetLargeSceneryObject(uint8_t largeSceneryType);
std::string_view GetWallObject(uint8_t wallType);
std::string_view GetPathSurfaceObject(uint8_t pathType);
std::string_view GetPathAddtionObject(uint8_t pathAdditionType);
std::string_view GetFootpathRailingsObject(uint8_t footpathRailingsType);
std::string_view GetSceneryGroupObject(uint8_t sceneryGroupType);
std::string_view GetWaterObject(uint8_t waterType);
std::string_view GetTerrainSurfaceObject(uint8_t terrain);
std::string_view GetTerrainEdgeObject(uint8_t terrainEdge);
const std::vector<const char*> GetSceneryObjects(uint8_t sceneryType);
} // namespace RCT1

View File

@ -12,6 +12,7 @@
#include "../core/String.hpp"
#include "../localisation/Formatting.h"
#include "../localisation/Localisation.h"
#include "../object/ObjectList.h"
#include "../ride/Track.h"
#include "../scenario/Scenario.h"
#include "../world/Banner.h"
@ -1409,6 +1410,64 @@ std::optional<uint8_t> GetStyleFromMusicIdentifier(std::string_view identifier)
return std::nullopt;
}
void SetDefaultRCT2TerrainObjects(ObjectList& objectList)
{
// Surfaces
objectList.SetObject(ObjectType::TerrainSurface, 0, "rct2.terrain_surface.grass");
objectList.SetObject(ObjectType::TerrainSurface, 1, "rct2.terrain_surface.sand");
objectList.SetObject(ObjectType::TerrainSurface, 2, "rct2.terrain_surface.dirt");
objectList.SetObject(ObjectType::TerrainSurface, 3, "rct2.terrain_surface.rock");
objectList.SetObject(ObjectType::TerrainSurface, 4, "rct2.terrain_surface.martian");
objectList.SetObject(ObjectType::TerrainSurface, 5, "rct2.terrain_surface.chequerboard");
objectList.SetObject(ObjectType::TerrainSurface, 6, "rct2.terrain_surface.grass_clumps");
objectList.SetObject(ObjectType::TerrainSurface, 7, "rct2.terrain_surface.ice");
objectList.SetObject(ObjectType::TerrainSurface, 8, "rct2.terrain_surface.grid_red");
objectList.SetObject(ObjectType::TerrainSurface, 9, "rct2.terrain_surface.grid_yellow");
objectList.SetObject(ObjectType::TerrainSurface, 10, "rct2.terrain_surface.grid_purple");
objectList.SetObject(ObjectType::TerrainSurface, 11, "rct2.terrain_surface.grid_green");
objectList.SetObject(ObjectType::TerrainSurface, 12, "rct2.terrain_surface.sand_red");
objectList.SetObject(ObjectType::TerrainSurface, 13, "rct2.terrain_surface.sand_brown");
objectList.SetObject(ObjectType::TerrainSurface, 14, "rct1aa.terrain_surface.roof_red");
objectList.SetObject(ObjectType::TerrainSurface, 15, "rct1ll.terrain_surface.roof_grey");
objectList.SetObject(ObjectType::TerrainSurface, 16, "rct1ll.terrain_surface.rust");
objectList.SetObject(ObjectType::TerrainSurface, 17, "rct1ll.terrain_surface.wood");
// Edges
objectList.SetObject(ObjectType::TerrainEdge, 0, "rct2.terrain_edge.rock");
objectList.SetObject(ObjectType::TerrainEdge, 1, "rct2.terrain_edge.wood_red");
objectList.SetObject(ObjectType::TerrainEdge, 2, "rct2.terrain_edge.wood_black");
objectList.SetObject(ObjectType::TerrainEdge, 3, "rct2.terrain_edge.ice");
objectList.SetObject(ObjectType::TerrainEdge, 4, "rct1.terrain_edge.brick");
objectList.SetObject(ObjectType::TerrainEdge, 5, "rct1.terrain_edge.iron");
objectList.SetObject(ObjectType::TerrainEdge, 6, "rct1aa.terrain_edge.grey");
objectList.SetObject(ObjectType::TerrainEdge, 7, "rct1aa.terrain_edge.yellow");
objectList.SetObject(ObjectType::TerrainEdge, 8, "rct1aa.terrain_edge.red");
objectList.SetObject(ObjectType::TerrainEdge, 9, "rct1ll.terrain_edge.purple");
objectList.SetObject(ObjectType::TerrainEdge, 10, "rct1ll.terrain_edge.green");
objectList.SetObject(ObjectType::TerrainEdge, 11, "rct1ll.terrain_edge.stone_brown");
objectList.SetObject(ObjectType::TerrainEdge, 12, "rct1ll.terrain_edge.stone_grey");
objectList.SetObject(ObjectType::TerrainEdge, 13, "rct1ll.terrain_edge.skyscraper_a");
objectList.SetObject(ObjectType::TerrainEdge, 14, "rct1ll.terrain_edge.skyscraper_b");
}
void RCT12AddDefaultObjects(ObjectList& objectList)
{
// Stations
for (size_t i = 0; i < std::size(_stationStyles); i++)
{
objectList.SetObject(ObjectType::Station, static_cast<ObjectEntryIndex>(i), _stationStyles[i]);
}
// Music
for (size_t i = 0; i < std::size(_musicStyles); i++)
{
if (!_musicStyles[i].empty())
{
objectList.SetObject(ObjectType::Music, static_cast<ObjectEntryIndex>(i), _musicStyles[i]);
}
}
}
money64 RCT12CompletedCompanyValueToOpenRCT2(money32 origValue)
{
if (origValue == RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE)

View File

@ -944,6 +944,8 @@ track_type_t RCT12FlatTrackTypeToOpenRCT2(RCT12TrackType origTrackType);
RCT12TrackType OpenRCT2FlatTrackTypeToRCT12(track_type_t origTrackType);
std::string_view GetStationIdentifierFromStyle(uint8_t style);
std::optional<uint8_t> GetStyleFromMusicIdentifier(std::string_view identifier);
void SetDefaultRCT2TerrainObjects(ObjectList& objectList);
void RCT12AddDefaultObjects(ObjectList& objectList);
static constexpr money32 RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE = 0x80000001;

View File

@ -178,7 +178,7 @@ static FootpathMapping _footpathMappings[] = {
{ "FUTRPATH", "rct2tt.footpath_surface.circuitboard", "rct2tt.footpath_surface.queue_circuitboard",
"rct2tt.footpath_railings.circuitboard" },
{ "FUTRPAT2", "rct2tt.footpath_surface.circuitboard", "rct2tt.footpath_surface.queue_circuitboard",
"openrct2.footpath_railings.invisible" },
"rct2tt.footpath_railings.circuitboard_invisible" },
{ "JURRPATH", "rct2tt.footpath_surface.rocky", "rct2.footpath_surface.queue_yellow", "rct2tt.footpath_railings.rocky" },
{ "MEDIPATH", "rct2tt.footpath_surface.medieval", "rct2.footpath_surface.queue_yellow",
"rct2tt.footpath_railings.medieval" },

View File

@ -17,6 +17,7 @@
#include "../ride/VehicleColour.h"
#include "../world/EntityList.h"
#include <tuple>
#include <vector>
constexpr const uint8_t RCT2_MAX_STAFF = 200;

File diff suppressed because it is too large Load Diff

View File

@ -1,84 +0,0 @@
/*****************************************************************************
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#pragma once
#include "../common.h"
#include "../object/ObjectList.h"
#include "../scenario/Scenario.h"
#include <optional>
#include <string>
#include <string_view>
#include <vector>
namespace OpenRCT2
{
struct IStream;
}
struct Litter;
struct ObjectRepositoryItem;
struct RCT12SpriteBase;
struct EntityBase;
struct Peep;
union rct_sprite;
/**
* Class to export RollerCoaster Tycoon 2 scenarios (*.SC6) and saved games (*.SV6).
*/
class S6Exporter final
{
public:
bool RemoveTracklessRides;
std::vector<const ObjectRepositoryItem*> ExportObjectsList;
S6Exporter();
void SaveGame(const utf8* path);
void SaveGame(OpenRCT2::IStream* stream);
void SaveScenario(const utf8* path);
void SaveScenario(OpenRCT2::IStream* stream);
void Export();
void ExportParkName();
void ExportRides();
void ExportRide(rct2_ride* dst, const Ride* src);
void ExportEntities();
template<typename RCT12_T, typename OpenRCT2_T> void ExportEntity(RCT12_T* dst, const OpenRCT2_T* src);
void ExportEntityCommonProperties(RCT12SpriteBase* dst, const EntityBase* src);
void ExportEntityPeep(RCT2SpritePeep* dst, const Peep* src);
private:
rct_s6_data _s6{};
std::vector<std::string> _userStrings;
void Save(OpenRCT2::IStream* stream, bool isScenario);
static uint32_t GetLoanHash(money32 initialCash, money32 bankLoan, uint32_t maxBankLoan);
void ExportResearchedRideTypes();
void ExportResearchedRideEntries();
void ExportResearchedSceneryItems();
void ExportResearchList();
void ExportMarketingCampaigns();
void ExportPeepSpawns();
void ExportRideRatingsCalcData();
void ExportRideMeasurements();
void ExportRideMeasurement(RCT12RideMeasurement& dst, const RideMeasurement& src);
void ExportBanners();
void ExportBanner(RCT12Banner& dst, const Banner& src);
void ExportMapAnimations();
void ExportTileElements();
void ExportTileElement(RCT12TileElement* dst, const TileElement* src);
std::optional<uint16_t> AllocateUserString(std::string_view value);
void ExportUserStrings();
void RebuildEntityLinks();
void RebuildEntitySpatialLocation(const TileCoordsXY& loc);
void ExportStaffPatrolAreas();
};

View File

@ -88,6 +88,9 @@ private:
uint8_t _gameVersion = 0;
bool _isSV7 = false;
std::bitset<RCT12_MAX_RIDES_IN_PARK> _isFlatRide{};
ObjectEntryIndex _pathToSurfaceMap[16];
ObjectEntryIndex _pathToQueueSurfaceMap[16];
ObjectEntryIndex _pathToRailingMap[16];
public:
S6Importer(IObjectRepository& objectRepository)
@ -254,8 +257,6 @@ public:
gGuestChangeModifier = _s6.guest_count_change_modifier;
gResearchFundingLevel = _s6.current_research_level;
// pad_01357400
ImportResearchedRideTypes();
ImportResearchedRideEntries();
// _s6.researched_track_types_a
// _s6.researched_track_types_b
@ -276,8 +277,6 @@ public:
gStaffMechanicColour = _s6.mechanic_colour;
gStaffSecurityColour = _s6.security_colour;
ImportResearchedSceneryItems();
gParkRating = _s6.park_rating;
auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark();
@ -401,14 +400,13 @@ public:
if (_s6.header.type == S6_TYPE_SCENARIO)
{
// _s6.scenario_filename is wrong for some RCT2 expansion scenarios, so we use the real filename
String::Set(gScenarioFileName, sizeof(gScenarioFileName), Path::GetFileName(_s6Path));
gScenarioFileName = String::ToStd(Path::GetFileName(_s6Path));
}
else
{
// For savegames the filename can be arbitrary, so we have no choice but to rely on the name provided
String::Set(gScenarioFileName, sizeof(gScenarioFileName), _s6.scenario_filename);
gScenarioFileName = std::string(String::ToStringView(_s6.scenario_filename, std::size(_s6.scenario_filename)));
}
std::memcpy(gScenarioExpansionPacks, _s6.saved_expansion_pack_names, sizeof(_s6.saved_expansion_pack_names));
gCurrentRealTimeTicks = 0;
ImportRides();
@ -485,6 +483,9 @@ public:
research_determine_first_of_type();
staff_update_greyed_patrol_areas();
CheatsReset();
ClearRestrictedScenery();
}
void FixLandOwnership() const
@ -871,13 +872,14 @@ public:
}
dst->music = musicStyle;
auto entranceStyle = src->entrance_style;
// In SV7, "plain" entrances are invisible.
if (_isSV7 && entranceStyle == RCT12_STATION_STYLE_PLAIN)
auto entranceStyle = OBJECT_ENTRY_INDEX_NULL;
if (!_isSV7 && GetRideTypeDescriptor(dst->type).HasFlag(RIDE_TYPE_FLAG_HAS_ENTRANCE_EXIT))
{
entranceStyle = RCT12_STATION_STYLE_INVISIBLE;
entranceStyle = src->entrance_style;
}
dst->entrance_style = entranceStyle;
dst->vehicle_change_timeout = src->vehicle_change_timeout;
dst->num_block_brakes = src->num_block_brakes;
dst->lift_hill_speed = src->lift_hill_speed;
@ -958,61 +960,6 @@ public:
}
}
void ImportResearchedRideTypes()
{
set_every_ride_type_not_invented();
for (int32_t rideType = 0; rideType < RCT2_RIDE_TYPE_COUNT; rideType++)
{
int32_t quadIndex = rideType >> 5;
int32_t bitIndex = rideType & 0x1F;
bool invented = (_s6.researched_ride_types[quadIndex] & (1UL << bitIndex));
if (invented)
ride_type_set_invented(rideType);
}
}
void ImportResearchedRideEntries()
{
set_every_ride_entry_not_invented();
for (int32_t rideEntryIndex = 0; rideEntryIndex < MAX_RIDE_OBJECTS; rideEntryIndex++)
{
int32_t quadIndex = rideEntryIndex >> 5;
int32_t bitIndex = rideEntryIndex & 0x1F;
bool invented = (_s6.researched_ride_entries[quadIndex] & (1UL << bitIndex));
if (invented)
ride_entry_set_invented(rideEntryIndex);
}
}
void ImportResearchedSceneryItems()
{
set_all_scenery_items_not_invented();
for (uint16_t sceneryEntryIndex = 0; sceneryEntryIndex < RCT2_MAX_RESEARCHED_SCENERY_ITEMS; sceneryEntryIndex++)
{
int32_t quadIndex = sceneryEntryIndex >> 5;
int32_t bitIndex = sceneryEntryIndex & 0x1F;
bool invented = (_s6.researched_scenery_items[quadIndex] & (1UL << bitIndex));
if (invented)
{
ScenerySelection scenerySelection = { static_cast<uint8_t>((sceneryEntryIndex >> 8) & 0xFF),
static_cast<uint16_t>(sceneryEntryIndex & 0xFF) };
// SV6 has room for 8 types of scenery, and sometimes scenery of non-existing types 5 and 6 is marked as
// "invented".
if (scenerySelection.SceneryType < SCENERY_TYPE_COUNT)
{
scenery_set_invented(scenerySelection);
}
}
}
}
void ImportResearchList()
{
bool invented = true;
@ -1037,7 +984,10 @@ public:
void ImportBanner(Banner* dst, const RCT12Banner* src)
{
auto id = dst->id;
*dst = {};
dst->id = id;
dst->type = RCTEntryIndexToOpenRCT2EntryIndex(src->type);
dst->flags = src->flags;
@ -1137,10 +1087,15 @@ public:
auto tilePointerIndex = TilePointerIndex<RCT12TileElement>(RCT2_MAXIMUM_MAP_SIZE_TECHNICAL, _s6.tile_elements);
std::vector<TileElement> tileElements;
bool nextElementInvisible = false;
bool restOfTileInvisible = false;
for (TileCoordsXY coords = { 0, 0 }; coords.y < MAXIMUM_MAP_SIZE_TECHNICAL; coords.y++)
{
for (coords.x = 0; coords.x < MAXIMUM_MAP_SIZE_TECHNICAL; coords.x++)
{
nextElementInvisible = false;
restOfTileInvisible = false;
if (coords.x >= RCT2_MAXIMUM_MAP_SIZE_TECHNICAL || coords.y >= RCT2_MAXIMUM_MAP_SIZE_TECHNICAL)
{
auto& dstElement = tileElements.emplace_back();
@ -1161,22 +1116,30 @@ public:
do
{
auto& dstElement = tileElements.emplace_back();
if (srcElement->base_height == RCT12_MAX_ELEMENT_HEIGHT)
{
std::memcpy(&dstElement, srcElement, sizeof(*srcElement));
continue;
}
else
auto tileElementType = static_cast<RCT12TileElementType>(srcElement->GetType());
if (tileElementType == RCT12TileElementType::Corrupt)
{
auto tileElementType = static_cast<RCT12TileElementType>(srcElement->GetType());
// Todo: replace with setting invisibility bit
if (tileElementType == RCT12TileElementType::Corrupt
|| tileElementType == RCT12TileElementType::EightCarsCorrupt14
|| tileElementType == RCT12TileElementType::EightCarsCorrupt15)
std::memcpy(&dstElement, srcElement, sizeof(*srcElement));
else
ImportTileElement(&dstElement, srcElement);
// One property of corrupt elements was to hide tops of tower tracks, and to avoid the next element from
// being hidden, multiple consecutive corrupt elements were sometimes used. This would essentially
// toggle the flag, so we inverse nextElementInvisible here instead of always setting it to true.
nextElementInvisible = !nextElementInvisible;
continue;
}
if (tileElementType == RCT12TileElementType::EightCarsCorrupt14
|| tileElementType == RCT12TileElementType::EightCarsCorrupt15)
{
restOfTileInvisible = true;
continue;
}
auto& dstElement = tileElements.emplace_back();
ImportTileElement(&dstElement, srcElement, nextElementInvisible || restOfTileInvisible);
nextElementInvisible = false;
} while (!(srcElement++)->IsLastForTile());
// Set last element flag in case the original last element was never added
@ -1189,7 +1152,7 @@ public:
SetTileElements(std::move(tileElements));
}
void ImportTileElement(TileElement* dst, const RCT12TileElement* src)
void ImportTileElement(TileElement* dst, const RCT12TileElement* src, bool invisible)
{
// Todo: allow for changing definition of OpenRCT2 tile element types - replace with a map
uint8_t tileElementType = src->GetType();
@ -1202,6 +1165,7 @@ public:
dst->SetOccupiedQuadrants(src->GetOccupiedQuadrants());
dst->SetGhost(src->IsGhost());
dst->SetLastForTile(src->IsLastForTile());
dst->SetInvisible(invisible);
switch (tileElementType)
{
@ -1226,7 +1190,21 @@ public:
auto dst2 = dst->AsPath();
auto src2 = src->AsPath();
dst2->SetLegacyPathEntryIndex(src2->GetEntryIndex());
auto pathEntryIndex = src2->GetEntryIndex();
auto surfaceEntry = src2->IsQueue() ? _pathToQueueSurfaceMap[pathEntryIndex]
: _pathToSurfaceMap[pathEntryIndex];
if (surfaceEntry == OBJECT_ENTRY_INDEX_NULL)
{
// Legacy footpath object
dst2->SetLegacyPathEntryIndex(pathEntryIndex);
}
else
{
// Surface / railing
dst2->SetSurfaceEntryIndex(surfaceEntry);
dst2->SetRailingsEntryIndex(_pathToRailingMap[pathEntryIndex]);
}
dst2->SetQueueBannerDirection(src2->GetQueueBannerDirection());
dst2->SetSloped(src2->IsSloped());
dst2->SetSlopeDirection(src2->GetSlopeDirection());
@ -1317,8 +1295,26 @@ public:
dst2->SetRideIndex(RCT12RideIdToOpenRCT2RideId(src2->GetRideIndex()));
dst2->SetStationIndex(src2->GetStationIndex());
dst2->SetSequenceIndex(src2->GetSequenceIndex());
dst2->SetLegacyPathEntryIndex(src2->GetPathType());
if (src2->GetSequenceIndex() == 0)
{
auto pathEntryIndex = src2->GetPathType();
auto surfaceEntry = _pathToSurfaceMap[pathEntryIndex];
if (surfaceEntry == OBJECT_ENTRY_INDEX_NULL)
{
// Legacy footpath object
dst2->SetLegacyPathEntryIndex(pathEntryIndex);
}
else
{
// Surface
dst2->SetSurfaceEntryIndex(surfaceEntry);
}
}
else
{
dst2->SetSurfaceEntryIndex(OBJECT_ENTRY_INDEX_NULL);
}
break;
}
case TILE_ELEMENT_TYPE_WALL:
@ -1655,56 +1651,69 @@ public:
return justText.data();
}
template<size_t TInternalLimit, typename T>
static void AddRequiredObjects(std::vector<rct_object_entry>& required, const T& list)
{
rct_object_entry nullEntry = {};
std::memset(&nullEntry, 0xFF, sizeof(nullEntry));
for (const auto& entry : list)
{
required.push_back(entry);
}
// NOTE: The segment of this object type needs to be filled to the internal limit
// the object manager currently expects this.
for (size_t i = std::size(list); i < TInternalLimit; i++)
{
required.push_back(nullEntry);
}
}
ObjectList GetRequiredObjects()
{
std::vector<rct_object_entry> result;
AddRequiredObjects<MAX_RIDE_OBJECTS>(result, _s6.RideObjects);
AddRequiredObjects<MAX_SMALL_SCENERY_OBJECTS>(result, _s6.SceneryObjects);
AddRequiredObjects<MAX_LARGE_SCENERY_OBJECTS>(result, _s6.LargeSceneryObjects);
AddRequiredObjects<MAX_WALL_SCENERY_OBJECTS>(result, _s6.WallSceneryObjects);
AddRequiredObjects<MAX_BANNER_OBJECTS>(result, _s6.BannerObjects);
AddRequiredObjects<MAX_PATH_OBJECTS>(result, _s6.PathObjects);
AddRequiredObjects<MAX_PATH_ADDITION_OBJECTS>(result, _s6.PathAdditionObjects);
AddRequiredObjects<MAX_SCENERY_GROUP_OBJECTS>(result, _s6.SceneryGroupObjects);
AddRequiredObjects<MAX_PARK_ENTRANCE_OBJECTS>(result, _s6.ParkEntranceObjects);
AddRequiredObjects<MAX_WATER_OBJECTS>(result, _s6.WaterObjects);
AddRequiredObjects<MAX_SCENARIO_TEXT_OBJECTS>(result, _s6.ScenarioTextObjects);
std::fill(std::begin(_pathToSurfaceMap), std::end(_pathToSurfaceMap), OBJECT_ENTRY_INDEX_NULL);
std::fill(std::begin(_pathToQueueSurfaceMap), std::end(_pathToQueueSurfaceMap), OBJECT_ENTRY_INDEX_NULL);
std::fill(std::begin(_pathToRailingMap), std::end(_pathToRailingMap), OBJECT_ENTRY_INDEX_NULL);
ObjectList objectList;
for (size_t i = 0; i < result.size(); i++)
int objectIt = 0;
ObjectEntryIndex surfaceCount = 0;
ObjectEntryIndex railingCount = 0;
for (int16_t objectType = EnumValue(ObjectType::Ride); objectType <= EnumValue(ObjectType::Water); objectType++)
{
ObjectType objectType;
ObjectEntryIndex entryIndex;
get_type_entry_index(i, &objectType, &entryIndex);
auto desc = ObjectEntryDescriptor(result[i]);
if (desc.HasValue())
for (int16_t i = 0; i < rct2_object_entry_group_counts[objectType]; i++, objectIt++)
{
assert(desc.GetType() == objectType);
objectList.SetObject(entryIndex, desc);
auto entry = ObjectEntryDescriptor(_s6.Objects[objectIt]);
if (entry.HasValue())
{
if (objectType == EnumValue(ObjectType::Paths))
{
auto footpathMapping = GetFootpathSurfaceId(entry);
if (footpathMapping == nullptr)
{
// Unsupported footpath
objectList.SetObject(i, entry);
}
else
{
// We have surface objects for this footpath
auto surfaceIndex = objectList.Find(ObjectType::FootpathSurface, footpathMapping->NormalSurface);
if (surfaceIndex == OBJECT_ENTRY_INDEX_NULL)
{
objectList.SetObject(ObjectType::FootpathSurface, surfaceCount, footpathMapping->NormalSurface);
surfaceIndex = surfaceCount++;
}
_pathToSurfaceMap[i] = surfaceIndex;
surfaceIndex = objectList.Find(ObjectType::FootpathSurface, footpathMapping->QueueSurface);
if (surfaceIndex == OBJECT_ENTRY_INDEX_NULL)
{
objectList.SetObject(ObjectType::FootpathSurface, surfaceCount, footpathMapping->QueueSurface);
surfaceIndex = surfaceCount++;
}
_pathToQueueSurfaceMap[i] = surfaceIndex;
auto railingIndex = objectList.Find(ObjectType::FootpathRailings, footpathMapping->Railing);
if (railingIndex == OBJECT_ENTRY_INDEX_NULL)
{
objectList.SetObject(ObjectType::FootpathRailings, railingCount, footpathMapping->Railing);
railingIndex = railingCount++;
}
_pathToRailingMap[i] = railingIndex;
}
}
else
{
objectList.SetObject(i, entry);
}
}
}
}
SetDefaultRCT2TerrainObjects(objectList);
RCT12AddDefaultObjects(objectList);
return objectList;
}
};

View File

@ -1026,8 +1026,12 @@ void Ride::Update()
// Various things include news messages
if (lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_DUE_INSPECTION))
if (((gCurrentTicks >> 1) & 255) == static_cast<uint32_t>(id))
{
// Breakdown updates are distributed, only one ride can update the breakdown status per tick.
const auto updatingRideId = (gCurrentTicks / 2) % MAX_RIDES;
if (static_cast<ride_id_t>(updatingRideId) == id)
ride_breakdown_status_update(this);
}
ride_inspection_update(this);
@ -1274,6 +1278,7 @@ static void ride_breakdown_update(Ride* ride)
{
if (gCurrentTicks & 255)
return;
if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER)
return;
@ -5413,12 +5418,7 @@ bool ride_has_adjacent_station(Ride* ride)
bool ride_has_station_shelter(Ride* ride)
{
auto stationObj = ride_get_station_object(ride);
if (network_get_mode() != NETWORK_MODE_NONE)
{
// The server might run in headless mode so no images will be loaded, only check for stations.
return stationObj != nullptr;
}
return stationObj != nullptr && stationObj->BaseImageId != 0;
return stationObj != nullptr && (stationObj->Flags & STATION_OBJECT_FLAGS::HAS_SHELTER);
}
bool ride_has_ratings(const Ride* ride)
@ -5789,3 +5789,37 @@ void Ride::UpdateRideTypeForAllPieces()
}
}
}
std::vector<ride_id_t> GetTracklessRides()
{
// Iterate map and build list of seen ride IDs
std::vector<bool> seen;
seen.resize(256);
tile_element_iterator it;
tile_element_iterator_begin(&it);
while (tile_element_iterator_next(&it))
{
auto trackEl = it.element->AsTrack();
if (trackEl != nullptr && !trackEl->IsGhost())
{
auto rideId = static_cast<size_t>(trackEl->GetRideIndex());
if (rideId >= seen.size())
{
seen.resize(rideId + 1);
}
seen[rideId] = true;
}
}
// Get all rides that did not get seen during map iteration
const auto& rideManager = GetRideManager();
std::vector<ride_id_t> result;
for (const auto& ride : rideManager)
{
if (seen.size() <= static_cast<size_t>(ride.id) || !seen[static_cast<size_t>(ride.id)])
{
result.push_back(ride.id);
}
}
return result;
}

View File

@ -35,7 +35,7 @@ struct Vehicle;
// The max number of different types of vehicle.
// Examples of vehicles here are the locomotive, tender and carriage of the Miniature Railway.
#define MAX_VEHICLES_PER_RIDE_ENTRY 4
constexpr const uint8_t MAX_VEHICLES_PER_RIDE = 31;
constexpr const uint8_t MAX_VEHICLES_PER_RIDE = 255; // Note: that 255 represents No Train (null) hence why this is not 256
constexpr const uint8_t MAX_CIRCUITS_PER_RIDE = 20;
constexpr const uint8_t MAX_CARS_PER_TRAIN = 255;
constexpr const uint8_t MAX_VEHICLE_COLOURS = std::max(MAX_CARS_PER_TRAIN, MAX_VEHICLES_PER_RIDE);
@ -43,8 +43,9 @@ constexpr const uint8_t MAX_VEHICLE_COLOURS = std::max(MAX_CARS_PER_TRAIN, MAX_V
#define MAX_CATEGORIES_PER_RIDE 2
#define DOWNTIME_HISTORY_SIZE 8
#define CUSTOMER_HISTORY_SIZE 10
#define MAX_STATIONS 4
#define MAX_RIDES 255
#define MAX_CARS_PER_TRAIN 255
#define MAX_STATIONS 255
constexpr const uint16_t MAX_RIDES = 1000;
#define RIDE_TYPE_NULL 255
#define RIDE_ADJACENCY_CHECK_DISTANCE 5
@ -217,7 +218,7 @@ struct Ride
ObjectEntryIndex subtype;
RideMode mode;
uint8_t colour_scheme_type;
VehicleColour vehicle_colours[MAX_VEHICLE_COLOURS];
VehicleColour vehicle_colours[MAX_VEHICLES_PER_RIDE + 1];
// 0 = closed, 1 = open, 2 = test
RideStatus status;
std::string custom_name;
@ -515,8 +516,9 @@ enum
RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK = 1 << 15,
RIDE_LIFECYCLE_CABLE_LIFT_HILL_COMPONENT_USED = 1 << 16,
RIDE_LIFECYCLE_CABLE_LIFT = 1 << 17,
RIDE_LIFECYCLE_NOT_CUSTOM_DESIGN = 1 << 18, // Used for the Award for Best Custom-designed Rides
RIDE_LIFECYCLE_SIX_FLAGS_DEPRECATED = 1 << 19 // Not used anymore
RIDE_LIFECYCLE_NOT_CUSTOM_DESIGN = 1 << 18, // Used for the Award for Best Custom-designed Rides
RIDE_LIFECYCLE_SIX_FLAGS_DEPRECATED = 1 << 19, // Not used anymore
RIDE_LIFECYCLE_FIXED_RATINGS = 1 << 20, // When set, the ratings will not be updated (useful for hacked rides).
};
// Constants used by the ride_type->flags property at 0x008
@ -1249,3 +1251,7 @@ void ride_action_modify(Ride* ride, int32_t modifyType, int32_t flags);
void determine_ride_entrance_and_exit_locations();
void ride_clear_leftover_entrances(Ride* ride);
std::vector<ride_id_t> GetTracklessRides();
void ride_remove_vehicles(Ride* ride);

View File

@ -166,7 +166,7 @@ static void ride_ratings_update_state_0(RideRatingUpdateState& state)
}
auto ride = get_ride(currentRide);
if (ride != nullptr && ride->status != RideStatus::Closed)
if (ride != nullptr && ride->status != RideStatus::Closed && !(ride->lifecycle_flags & RIDE_LIFECYCLE_FIXED_RATINGS))
{
state.State = RIDE_RATINGS_STATE_INITIALISE;
}

View File

@ -688,25 +688,6 @@ static ObjectEntryIndex TrackDesignGetDefaultRailingIndex()
return OBJECT_ENTRY_INDEX_NULL;
}
static ObjectEntryIndex TrackDesignGetDefaultPathIndex(bool isQueue)
{
for (ObjectEntryIndex i = 0; i < MAX_PATH_OBJECTS; i++)
{
auto legacyPathEntry = GetLegacyFootpathEntry(i);
if (legacyPathEntry != nullptr)
{
const auto& surfaceDescriptor = isQueue ? legacyPathEntry->GetQueueSurfaceDescriptor()
: legacyPathEntry->GetPathSurfaceDescriptor();
if (surfaceDescriptor.IsEditorOnly())
{
continue;
}
return i;
}
}
return OBJECT_ENTRY_INDEX_NULL;
}
static std::optional<TrackSceneryEntry> TrackDesignPlaceSceneryElementGetEntry(const TrackDesignSceneryElement& scenery)
{
TrackSceneryEntry result;
@ -742,14 +723,7 @@ static std::optional<TrackSceneryEntry> TrackDesignPlaceSceneryElementGetEntry(c
if (result.SecondaryIndex == OBJECT_ENTRY_INDEX_NULL)
result.SecondaryIndex = TrackDesignGetDefaultRailingIndex();
// NOTE: This block can be deleted in the NSF branch.
if (result.Index == OBJECT_ENTRY_INDEX_NULL)
{
result.Type = ObjectType::Paths;
result.Index = TrackDesignGetDefaultPathIndex(scenery.IsQueue());
}
if (result.Index == OBJECT_ENTRY_INDEX_NULL)
if (result.Index == OBJECT_ENTRY_INDEX_NULL || result.SecondaryIndex == OBJECT_ENTRY_INDEX_NULL)
{
_trackDesignPlaceStateSceneryUnavailable = true;
return {};

View File

@ -44,7 +44,6 @@ struct TrackDesignEntranceElement
bool isExit;
};
/* Track Scenery entry size: 0x16 */
struct TrackDesignSceneryElement
{
ObjectEntryDescriptor scenery_object;

View File

@ -25,7 +25,7 @@ constexpr const RideTypeDescriptor DrinkStallRTD =
SET_FIELD(TrackPaintFunction, get_track_paint_function_shop),
SET_FIELD(Flags, RIDE_TYPE_FLAG_HAS_SINGLE_PIECE_STATION | RIDE_TYPE_FLAG_CANNOT_HAVE_GAPS | RIDE_TYPE_FLAG_NO_TEST_MODE | RIDE_TYPE_FLAG_NO_VEHICLES |
RIDE_TYPE_FLAG_HAS_NO_TRACK | RIDE_TYPE_FLAG_IS_SHOP | RIDE_TYPE_FLAG_TRACK_NO_WALLS | RIDE_TYPE_FLAG_FLAT_RIDE |
RIDE_TYPE_FLAG_SELLS_DRINKS | RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY),
RIDE_TYPE_FLAG_SELLS_DRINKS | RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY | RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN),
SET_FIELD(RideModes, EnumsToFlags(RideMode::ShopStall)),
SET_FIELD(DefaultMode, RideMode::ShopStall),
SET_FIELD(OperatingSettings, { 0, 0, 0, 0, 0, 0 }),

View File

@ -25,7 +25,7 @@ constexpr const RideTypeDescriptor FoodStallRTD =
SET_FIELD(TrackPaintFunction, get_track_paint_function_shop),
SET_FIELD(Flags, RIDE_TYPE_FLAG_HAS_SINGLE_PIECE_STATION | RIDE_TYPE_FLAG_CANNOT_HAVE_GAPS | RIDE_TYPE_FLAG_NO_TEST_MODE | RIDE_TYPE_FLAG_NO_VEHICLES |
RIDE_TYPE_FLAG_HAS_NO_TRACK | RIDE_TYPE_FLAG_IS_SHOP | RIDE_TYPE_FLAG_TRACK_NO_WALLS | RIDE_TYPE_FLAG_FLAT_RIDE |
RIDE_TYPE_FLAG_SELLS_FOOD | RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY),
RIDE_TYPE_FLAG_SELLS_FOOD | RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY | RIDE_TYPE_FLAG_HAS_TRACK_COLOUR_MAIN),
SET_FIELD(RideModes, EnumsToFlags(RideMode::ShopStall)),
SET_FIELD(DefaultMode, RideMode::ShopStall),
SET_FIELD(OperatingSettings, { 0, 0, 0, 0, 0, 0 }),

View File

@ -31,6 +31,7 @@
#include "../network/network.h"
#include "../object/Object.h"
#include "../object/ObjectList.h"
#include "../object/ObjectManager.h"
#include "../peep/Guest.h"
#include "../peep/Staff.h"
#include "../platform/platform.h"
@ -64,7 +65,6 @@ std::string gScenarioName;
std::string gScenarioDetails;
std::string gScenarioCompletedBy;
std::string gScenarioSavePath;
char gScenarioExpansionPacks[3256];
bool gFirstTimeSaving = true;
uint16_t gSavedAge;
uint32_t gLastAutoSaveUpdate = 0;
@ -78,7 +78,7 @@ uint16_t gScenarioParkRatingWarningDays;
money64 gScenarioCompletedCompanyValue;
money64 gScenarioCompanyValueRecord;
char gScenarioFileName[MAX_PATH];
std::string gScenarioFileName;
static void scenario_objective_check();
@ -154,7 +154,15 @@ void scenario_begin()
park_calculate_size();
map_count_remaining_land_rights();
Staff::ResetStats();
gLastEntranceStyle = 0;
auto& objManager = GetContext()->GetObjectManager();
gLastEntranceStyle = objManager.GetLoadedObjectEntryIndex("rct2.station.plain");
if (gLastEntranceStyle == OBJECT_ENTRY_INDEX_NULL)
{
// Fall back to first entrance object
gLastEntranceStyle = 0;
}
gMarketingCampaigns.clear();
gParkRatingCasualtyPenalty = 0;
@ -199,7 +207,7 @@ void scenario_success()
gScenarioCompletedCompanyValue = companyValue;
peep_applause();
if (scenario_repository_try_record_highscore(gScenarioFileName, companyValue, nullptr))
if (scenario_repository_try_record_highscore(gScenarioFileName.c_str(), companyValue, nullptr))
{
// Allow name entry
gParkFlags |= PARK_FLAGS_SCENARIO_COMPLETE_NAME_INPUT;
@ -214,7 +222,7 @@ void scenario_success()
*/
void scenario_success_submit_name(const char* name)
{
if (scenario_repository_try_record_highscore(gScenarioFileName, gScenarioCompanyValueRecord, name))
if (scenario_repository_try_record_highscore(gScenarioFileName.c_str(), gScenarioCompanyValueRecord, name))
{
gScenarioCompletedBy = name;
}

View File

@ -162,12 +162,11 @@ extern std::string gScenarioName;
extern std::string gScenarioDetails;
extern std::string gScenarioCompletedBy;
extern std::string gScenarioSavePath;
extern char gScenarioExpansionPacks[3256];
extern bool gFirstTimeSaving;
extern uint16_t gSavedAge;
extern uint32_t gLastAutoSaveUpdate;
extern char gScenarioFileName[260];
extern std::string gScenarioFileName;
void load_from_sc6(const char* path);
void scenario_begin();

View File

@ -126,7 +126,7 @@ class ScenarioFileIndex final : public FileIndex<scenario_index_entry>
private:
static constexpr uint32_t MAGIC_NUMBER = 0x58444953; // SIDX
static constexpr uint16_t VERSION = 5;
static constexpr auto PATTERN = "*.sc4;*.sc6;*.sea";
static constexpr auto PATTERN = "*.sc4;*.sc6;*.sea;*.park";
public:
explicit ScenarioFileIndex(const IPlatformEnvironment& env)
@ -197,6 +197,28 @@ private:
try
{
std::string extension = Path::GetExtension(path);
if (String::Equals(extension, ".park", true))
{
// OpenRCT2 park
bool result = false;
try
{
auto& objRepository = OpenRCT2::GetContext()->GetObjectRepository();
auto importer = ParkImporter::CreateParkFile(objRepository);
importer->LoadScenario(path.c_str(), true);
if (importer->GetDetails(entry))
{
String::Set(entry->path, sizeof(entry->path), path.c_str());
entry->timestamp = timestamp;
result = true;
}
}
catch (const std::exception&)
{
}
return result;
}
if (String::Equals(extension, ".sc4", true))
{
// RCT1 scenario

View File

@ -415,6 +415,19 @@ namespace OpenRCT2::Scripting
return dukCoords.Take();
}
template<> ObjectEntryIndex inline FromDuk(const DukValue& d)
{
if (d.type() == DukValue::Type::NUMBER)
{
auto value = d.as_int();
if (value >= 0 && value <= std::numeric_limits<ObjectEntryIndex>::max())
{
return static_cast<ObjectEntryIndex>(value);
}
}
return OBJECT_ENTRY_INDEX_NULL;
}
} // namespace OpenRCT2::Scripting
#endif

View File

@ -224,7 +224,7 @@ namespace OpenRCT2::Scripting
void filename_set(const std::string& value)
{
ThrowIfGameStateNotMutable();
String::Set(gScenarioFileName, std::size(gScenarioFileName), value.c_str());
gScenarioFileName = value;
}
std::shared_ptr<ScScenarioObjective> objective_get() const

View File

@ -54,8 +54,6 @@ namespace OpenRCT2::Scripting
return "large_scenery";
case TILE_ELEMENT_TYPE_BANNER:
return "banner";
case TILE_ELEMENT_TYPE_CORRUPT:
return "openrct2_corrupt_deprecated";
default:
return "unknown";
}
@ -81,9 +79,7 @@ namespace OpenRCT2::Scripting
_element->type = TILE_ELEMENT_TYPE_BANNER;
else
{
if (value == "openrct2_corrupt_deprecated")
std::puts(
"Creation of new corrupt elements is deprecated. To hide elements, use the 'hidden' property instead.");
std::puts("Element type not recognised!");
return;
}
@ -746,43 +742,46 @@ namespace OpenRCT2::Scripting
}
return DukValue::take_from_stack(ctx);
}
void ScTileElement::object_set(uint32_t value)
void ScTileElement::object_set(const DukValue& value)
{
ThrowIfGameStateNotMutable();
auto index = FromDuk<ObjectEntryIndex>(value);
switch (_element->GetType())
{
case TILE_ELEMENT_TYPE_PATH:
{
auto el = _element->AsPath();
el->SetLegacyPathEntryIndex(value & 0xFF);
el->SetLegacyPathEntryIndex(index);
Invalidate();
break;
}
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
{
auto el = _element->AsSmallScenery();
el->SetEntryIndex(value & 0xFF);
el->SetEntryIndex(index);
Invalidate();
break;
}
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
{
auto el = _element->AsLargeScenery();
el->SetEntryIndex(value);
el->SetEntryIndex(index);
Invalidate();
break;
}
case TILE_ELEMENT_TYPE_WALL:
{
auto el = _element->AsWall();
el->SetEntryIndex(value & 0xFFFF);
el->SetEntryIndex(index);
Invalidate();
break;
}
case TILE_ELEMENT_TYPE_ENTRANCE:
{
auto el = _element->AsEntrance();
el->SetEntranceType(value & 0xFF);
el->SetEntranceType(index);
Invalidate();
break;
}
@ -791,77 +790,13 @@ namespace OpenRCT2::Scripting
bool ScTileElement::isHidden_get() const
{
// TODO: Simply return the 'hidden' field once corrupt elements are superseded.
const TileElement* element = map_get_first_element_at(_coords);
bool previousElementWasUsefulCorrupt = false;
do
{
if (element == _element)
return previousElementWasUsefulCorrupt;
if (element->GetType() == TILE_ELEMENT_TYPE_CORRUPT)
previousElementWasUsefulCorrupt = !previousElementWasUsefulCorrupt;
else
previousElementWasUsefulCorrupt = false;
} while (!(element++)->IsLastForTile());
Guard::Assert(false);
return false;
return _element->IsInvisible();
}
void ScTileElement::isHidden_set(bool hide)
{
// TODO: Simply update the 'hidden' field once corrupt elements are superseded.
ThrowIfGameStateNotMutable();
const bool isHidden = isHidden_get();
if (hide == isHidden)
return;
if (hide)
{
// Get index of our current element (has to be done now before inserting the corrupt element)
const auto elementIndex = _element - map_get_first_element_at(_coords);
// Insert corrupt element at the end of the list for this tile
// Note: Z = MAX_ELEMENT_HEIGHT to guarantee this
TileElement* insertedElement = tile_element_insert(
{ _coords, MAX_ELEMENT_HEIGHT * COORDS_Z_STEP }, 0, TileElementType::Corrupt);
if (insertedElement == nullptr)
{
// TODO: Show error
return;
}
// Since inserting a new element may move the tile elements in memory, we have to update the local pointer
_element = map_get_first_element_at(_coords) + elementIndex;
// Move the corrupt element down in the list until it's right under our element
while (insertedElement > _element)
{
std::swap<TileElement>(*insertedElement, *(insertedElement - 1));
insertedElement--;
// Un-swap the last-for-tile flag
if (insertedElement->IsLastForTile())
{
insertedElement->SetLastForTile(false);
(insertedElement + 1)->SetLastForTile(true);
}
}
// Now the corrupt element took the hidden element's place, increment it by one
_element++;
// Update base and clearance heights of inserted corrupt element to match the element to hide
insertedElement->base_height = insertedElement->clearance_height = _element->base_height;
}
else
{
TileElement* const elementToRemove = _element - 1;
Guard::Assert(elementToRemove->GetType() == TILE_ELEMENT_TYPE_CORRUPT);
tile_element_remove(elementToRemove);
_element--;
}
_element->SetInvisible(hide);
Invalidate();
}
@ -1294,6 +1229,74 @@ namespace OpenRCT2::Scripting
}
}
DukValue ScTileElement::surfaceObject_get() const
{
auto ctx = GetContext()->GetScriptEngine().GetContext();
if (_element->GetType() == TILE_ELEMENT_TYPE_PATH)
{
auto el = _element->AsPath();
auto index = el->GetSurfaceEntryIndex();
if (index != OBJECT_ENTRY_INDEX_NULL)
{
duk_push_int(ctx, index);
}
else
{
duk_push_null(ctx);
}
}
else
{
duk_push_null(ctx);
}
return DukValue::take_from_stack(ctx);
}
void ScTileElement::surfaceObject_set(const DukValue& value)
{
ThrowIfGameStateNotMutable();
if (_element->GetType() == TILE_ELEMENT_TYPE_PATH)
{
auto el = _element->AsPath();
el->SetSurfaceEntryIndex(FromDuk<ObjectEntryIndex>(value));
Invalidate();
}
}
DukValue ScTileElement::railingsObject_get() const
{
auto ctx = GetContext()->GetScriptEngine().GetContext();
if (_element->GetType() == TILE_ELEMENT_TYPE_PATH)
{
auto el = _element->AsPath();
auto index = el->GetRailingsEntryIndex();
if (index != OBJECT_ENTRY_INDEX_NULL)
{
duk_push_int(ctx, index);
}
else
{
duk_push_null(ctx);
}
}
else
{
duk_push_null(ctx);
}
return DukValue::take_from_stack(ctx);
}
void ScTileElement::railingsObject_set(const DukValue& value)
{
ThrowIfGameStateNotMutable();
if (_element->GetType() == TILE_ELEMENT_TYPE_PATH)
{
auto el = _element->AsPath();
el->SetRailingsEntryIndex(FromDuk<ObjectEntryIndex>(value));
Invalidate();
}
}
DukValue ScTileElement::addition_get() const
{
auto ctx = GetContext()->GetScriptEngine().GetContext();
@ -1395,18 +1398,64 @@ namespace OpenRCT2::Scripting
auto ctx = GetContext()->GetScriptEngine().GetContext();
auto el = _element->AsEntrance();
if (el != nullptr)
duk_push_int(ctx, el->GetLegacyPathEntryIndex());
{
auto index = el->GetLegacyPathEntryIndex();
if (index != OBJECT_ENTRY_INDEX_NULL)
{
duk_push_int(ctx, index);
}
else
{
duk_push_null(ctx);
}
}
else
{
duk_push_null(ctx);
}
return DukValue::take_from_stack(ctx);
}
void ScTileElement::footpathObject_set(uint8_t value)
void ScTileElement::footpathObject_set(const DukValue& value)
{
ThrowIfGameStateNotMutable();
auto el = _element->AsEntrance();
if (el != nullptr)
{
el->SetLegacyPathEntryIndex(value);
el->SetLegacyPathEntryIndex(FromDuk<ObjectEntryIndex>(value));
Invalidate();
}
}
DukValue ScTileElement::footpathSurfaceObject_get() const
{
auto ctx = GetContext()->GetScriptEngine().GetContext();
auto el = _element->AsEntrance();
if (el != nullptr)
{
auto index = el->GetSurfaceEntryIndex();
if (index != OBJECT_ENTRY_INDEX_NULL)
{
duk_push_int(ctx, index);
}
else
{
duk_push_null(ctx);
}
}
else
{
duk_push_null(ctx);
}
return DukValue::take_from_stack(ctx);
}
void ScTileElement::footpathSurfaceObject_set(const DukValue& value)
{
ThrowIfGameStateNotMutable();
auto el = _element->AsEntrance();
if (el != nullptr)
{
el->SetSurfaceEntryIndex(FromDuk<ObjectEntryIndex>(value));
Invalidate();
}
}
@ -1524,12 +1573,15 @@ namespace OpenRCT2::Scripting
dukglue_register_property(ctx, &ScTileElement::isQueue_get, &ScTileElement::isQueue_set, "isQueue");
dukglue_register_property(
ctx, &ScTileElement::queueBannerDirection_get, &ScTileElement::queueBannerDirection_set, "queueBannerDirection");
dukglue_register_property(ctx, &ScTileElement::queueBannerDirection_get, &ScTileElement::edges_set, "test");
dukglue_register_property(
ctx, &ScTileElement::isBlockedByVehicle_get, &ScTileElement::isBlockedByVehicle_set, "isBlockedByVehicle");
dukglue_register_property(ctx, &ScTileElement::isWide_get, &ScTileElement::isWide_set, "isWide");
dukglue_register_property(ctx, &ScTileElement::surfaceObject_get, &ScTileElement::surfaceObject_set, "surfaceObject");
dukglue_register_property(
ctx, &ScTileElement::railingsObject_get, &ScTileElement::railingsObject_set, "railingsObject");
dukglue_register_property(ctx, &ScTileElement::addition_get, &ScTileElement::addition_set, "addition");
dukglue_register_property(
ctx, &ScTileElement::additionStatus_get, &ScTileElement::additionStatus_set, "additionStatus");
@ -1560,6 +1612,8 @@ namespace OpenRCT2::Scripting
// Entrance only
dukglue_register_property(
ctx, &ScTileElement::footpathObject_get, &ScTileElement::footpathObject_set, "footpathObject");
dukglue_register_property(
ctx, &ScTileElement::footpathSurfaceObject_get, &ScTileElement::footpathSurfaceObject_set, "footpathSurfaceObject");
}
} // namespace OpenRCT2::Scripting

View File

@ -112,7 +112,7 @@ namespace OpenRCT2::Scripting
void hasCableLift_set(bool value);
DukValue object_get() const;
void object_set(uint32_t value);
void object_set(const DukValue& value);
bool isHidden_get() const;
void isHidden_set(bool hide);
@ -172,6 +172,12 @@ namespace OpenRCT2::Scripting
DukValue addition_get() const;
void addition_set(const DukValue& value);
DukValue surfaceObject_get() const;
void surfaceObject_set(const DukValue& value);
DukValue railingsObject_get() const;
void railingsObject_set(const DukValue& value);
DukValue additionStatus_get() const;
void additionStatus_set(uint8_t value);
@ -182,7 +188,10 @@ namespace OpenRCT2::Scripting
void isAdditionGhost_set(bool value);
DukValue footpathObject_get() const;
void footpathObject_set(uint8_t value);
void footpathObject_set(const DukValue& value);
DukValue footpathSurfaceObject_get() const;
void footpathSurfaceObject_set(const DukValue& value);
DukValue direction_get() const;
void direction_set(uint8_t value);

View File

@ -23,7 +23,6 @@ enum class TileInspectorPage : int16_t
Wall,
LargeScenery,
Banner,
Corrupt
};
extern TileCoordsXY windowTileInspectorTile;

View File

@ -22,7 +22,7 @@ struct WallElement;
using BannerIndex = uint16_t;
constexpr ObjectEntryIndex BANNER_NULL = OBJECT_ENTRY_INDEX_NULL;
constexpr size_t MAX_BANNERS = 250;
constexpr size_t MAX_BANNERS = 8192;
constexpr BannerIndex BANNER_INDEX_NULL = static_cast<BannerIndex>(-1);
constexpr uint8_t SCROLLING_MODE_NONE = 255;

View File

@ -11,7 +11,7 @@
#include "EntityBase.h"
constexpr uint16_t MAX_ENTITIES = 10000;
constexpr uint16_t MAX_ENTITIES = 65535;
EntityBase* try_get_sprite(size_t spriteIndex);
EntityBase* get_sprite(size_t sprite_idx);

View File

@ -217,6 +217,23 @@ void fix_park_entrance_locations(void)
gParkEntrances.end());
}
void UpdateParkEntranceLocations()
{
gParkEntrances.clear();
tile_element_iterator it;
tile_element_iterator_begin(&it);
while (tile_element_iterator_next(&it))
{
auto entranceElement = it.element->AsEntrance();
if (entranceElement != nullptr && entranceElement->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE
&& entranceElement->GetSequenceIndex() == 0 && !entranceElement->IsGhost())
{
auto entrance = TileCoordsXYZD(it.x, it.y, it.element->base_height, it.element->GetDirection()).ToCoordsXYZD();
gParkEntrances.push_back(entrance);
}
}
}
uint8_t EntranceElement::GetStationIndex() const
{
return StationIndex;

View File

@ -40,7 +40,7 @@ constexpr const uint8_t RideExitHeight = 5 * COORDS_Z_STEP;
extern bool gParkEntranceGhostExists;
extern CoordsXYZD gParkEntranceGhostPosition;
#define MAX_PARK_ENTRANCES 4
#define MAX_PARK_ENTRANCES 256
constexpr int32_t MaxRideEntranceOrExitHeight = 244 * COORDS_Z_STEP;
@ -60,3 +60,4 @@ void maze_entrance_hedge_replacement(const CoordsXYE& entrance);
void maze_entrance_hedge_removal(const CoordsXYE& entrance);
void fix_park_entrance_locations();
void UpdateParkEntranceLocations();

View File

@ -29,8 +29,6 @@ constexpr auto FootpathMinHeight = 2 * COORDS_Z_STEP;
constexpr auto PATH_HEIGHT_STEP = 2 * COORDS_Z_STEP;
constexpr auto PATH_CLEARANCE = 4 * COORDS_Z_STEP;
class FootpathObject;
enum class RailingEntrySupportType : uint8_t
{
Box = 0,
@ -176,6 +174,7 @@ enum
{
RAILING_ENTRY_FLAG_HAS_SUPPORT_BASE_SPRITE = (1 << 0),
RAILING_ENTRY_FLAG_DRAW_PATH_OVER_SUPPORTS = (1 << 1), // When elevated
RAILING_ENTRY_FLAG_NO_QUEUE_BANNER = (1 << 2),
};
enum

Some files were not shown because too many files have changed in this diff Show More