mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-21 22:13:07 +01:00
Merge branch 'develop' into HEAD
This commit is contained in:
@@ -27,6 +27,11 @@ SwapFramebuffer::SwapFramebuffer(int32_t width, int32_t height)
|
||||
glClearBufferfv(GL_DEPTH, 0, depthValueTransparent);
|
||||
}
|
||||
|
||||
SwapFramebuffer::~SwapFramebuffer()
|
||||
{
|
||||
glDeleteTextures(1, &_backDepth);
|
||||
}
|
||||
|
||||
void SwapFramebuffer::ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex)
|
||||
{
|
||||
_mixFramebuffer.Bind();
|
||||
|
||||
@@ -33,6 +33,7 @@ private:
|
||||
|
||||
public:
|
||||
SwapFramebuffer(int32_t width, int32_t height);
|
||||
~SwapFramebuffer();
|
||||
|
||||
const OpenGLFramebuffer& GetFinalFramebuffer() const
|
||||
{
|
||||
|
||||
@@ -753,7 +753,10 @@ static void window_editor_object_selection_scroll_mouseover(
|
||||
w->selected_list_item = selectedObject;
|
||||
|
||||
if (_loadedObject != nullptr)
|
||||
{
|
||||
_loadedObject->Unload();
|
||||
_loadedObject = nullptr;
|
||||
}
|
||||
|
||||
if (selectedObject != -1)
|
||||
{
|
||||
|
||||
@@ -284,322 +284,294 @@ static rct_widget window_object_load_error_widgets[] = {
|
||||
#endif
|
||||
WIDGETS_END,
|
||||
};
|
||||
|
||||
static void window_object_load_error_close(rct_window *w);
|
||||
static void window_object_load_error_update(rct_window *w);
|
||||
static void window_object_load_error_mouseup(rct_window *w, rct_widgetindex widgetIndex);
|
||||
static void window_object_load_error_scrollgetsize(rct_window *w, int32_t scrollIndex, int32_t *width, int32_t *height);
|
||||
static void window_object_load_error_scrollmouseover(rct_window *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
|
||||
static void window_object_load_error_scrollmousedown(rct_window *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords);
|
||||
static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi);
|
||||
static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex);
|
||||
#ifndef DISABLE_HTTP
|
||||
static void window_object_load_error_download_all(rct_window* w);
|
||||
static void window_object_load_error_update_list(rct_window* w);
|
||||
#endif
|
||||
|
||||
static rct_window_event_list window_object_load_error_events([](auto& events)
|
||||
{
|
||||
events.close = &window_object_load_error_close;
|
||||
events.mouse_up = &window_object_load_error_mouseup;
|
||||
events.update = &window_object_load_error_update;
|
||||
events.get_scroll_size = &window_object_load_error_scrollgetsize;
|
||||
events.scroll_mousedown = &window_object_load_error_scrollmousedown;
|
||||
events.scroll_mouseover = &window_object_load_error_scrollmouseover;
|
||||
events.paint = &window_object_load_error_paint;
|
||||
events.scroll_paint = &window_object_load_error_scrollpaint;
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
static std::vector<ObjectEntryDescriptor> _invalid_entries;
|
||||
static int32_t highlighted_index = -1;
|
||||
static std::string file_path;
|
||||
#ifndef DISABLE_HTTP
|
||||
static ObjectDownloader _objDownloader;
|
||||
static bool _updatedListAfterDownload;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns an rct_string_id that represents an rct_object_entry's type.
|
||||
*
|
||||
* Could possibly be moved out of the window file if other
|
||||
* uses exist and a suitable location is found.
|
||||
*/
|
||||
static rct_string_id get_object_type_string(ObjectType type)
|
||||
static constexpr rct_string_id GetStringFromObjectType(const ObjectType type)
|
||||
{
|
||||
rct_string_id result;
|
||||
switch (type)
|
||||
{
|
||||
case ObjectType::Ride:
|
||||
result = STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS;
|
||||
break;
|
||||
return STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS;
|
||||
case ObjectType::SmallScenery:
|
||||
result = STR_OBJECT_SELECTION_SMALL_SCENERY;
|
||||
break;
|
||||
return STR_OBJECT_SELECTION_SMALL_SCENERY;
|
||||
case ObjectType::LargeScenery:
|
||||
result = STR_OBJECT_SELECTION_LARGE_SCENERY;
|
||||
break;
|
||||
return STR_OBJECT_SELECTION_LARGE_SCENERY;
|
||||
case ObjectType::Walls:
|
||||
result = STR_OBJECT_SELECTION_WALLS_FENCES;
|
||||
break;
|
||||
return STR_OBJECT_SELECTION_WALLS_FENCES;
|
||||
case ObjectType::Banners:
|
||||
result = STR_OBJECT_SELECTION_PATH_SIGNS;
|
||||
break;
|
||||
return STR_OBJECT_SELECTION_PATH_SIGNS;
|
||||
case ObjectType::Paths:
|
||||
result = STR_OBJECT_SELECTION_FOOTPATHS;
|
||||
break;
|
||||
return STR_OBJECT_SELECTION_FOOTPATHS;
|
||||
case ObjectType::PathBits:
|
||||
result = STR_OBJECT_SELECTION_PATH_EXTRAS;
|
||||
break;
|
||||
return STR_OBJECT_SELECTION_PATH_EXTRAS;
|
||||
case ObjectType::SceneryGroup:
|
||||
result = STR_OBJECT_SELECTION_SCENERY_GROUPS;
|
||||
break;
|
||||
return STR_OBJECT_SELECTION_SCENERY_GROUPS;
|
||||
case ObjectType::ParkEntrance:
|
||||
result = STR_OBJECT_SELECTION_PARK_ENTRANCE;
|
||||
break;
|
||||
return STR_OBJECT_SELECTION_PARK_ENTRANCE;
|
||||
case ObjectType::Water:
|
||||
result = STR_OBJECT_SELECTION_WATER;
|
||||
break;
|
||||
return STR_OBJECT_SELECTION_WATER;
|
||||
default:
|
||||
result = STR_UNKNOWN_OBJECT_TYPE;
|
||||
return STR_UNKNOWN_OBJECT_TYPE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a newline-separated string listing all object names.
|
||||
* Used for placing all names on the clipboard.
|
||||
*/
|
||||
static void copy_object_names_to_clipboard(rct_window* w)
|
||||
class ObjectLoadErrorWindow final : public rct_window
|
||||
{
|
||||
std::stringstream stream;
|
||||
for (uint16_t i = 0; i < w->no_list_items; i++)
|
||||
private:
|
||||
std::vector<ObjectEntryDescriptor> _invalidEntries;
|
||||
int32_t _highlightedIndex = -1;
|
||||
std::string _filePath;
|
||||
#ifndef DISABLE_HTTP
|
||||
ObjectDownloader _objDownloader;
|
||||
bool _updatedListAfterDownload{};
|
||||
|
||||
void DownloadAllObjects()
|
||||
{
|
||||
const auto& entry = _invalid_entries[i];
|
||||
stream << entry.GetName();
|
||||
stream << PLATFORM_NEWLINE;
|
||||
if (!_objDownloader.IsDownloading())
|
||||
{
|
||||
_updatedListAfterDownload = false;
|
||||
_objDownloader.Begin(_invalidEntries);
|
||||
}
|
||||
}
|
||||
|
||||
auto clip = stream.str();
|
||||
OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(clip.c_str());
|
||||
}
|
||||
void UpdateObjectList()
|
||||
{
|
||||
const auto entries = _objDownloader.GetDownloadedEntries();
|
||||
for (auto& de : entries)
|
||||
{
|
||||
_invalidEntries.erase(
|
||||
std::remove_if(
|
||||
_invalidEntries.begin(), _invalidEntries.end(),
|
||||
[de](const ObjectEntryDescriptor& e) { return de.GetName() == e.GetName(); }),
|
||||
_invalidEntries.end());
|
||||
}
|
||||
no_list_items = static_cast<uint16_t>(_invalidEntries.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns a newline-separated string listing all object names.
|
||||
* Used for placing all names on the clipboard.
|
||||
*/
|
||||
void CopyObjectNamesToClipboard()
|
||||
{
|
||||
std::stringstream stream;
|
||||
for (uint16_t i = 0; i < no_list_items; i++)
|
||||
{
|
||||
const auto& entry = _invalidEntries[i];
|
||||
stream << entry.GetName();
|
||||
stream << PLATFORM_NEWLINE;
|
||||
}
|
||||
|
||||
const auto clip = stream.str();
|
||||
OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(clip.c_str());
|
||||
}
|
||||
|
||||
void SelectObjectFromList(const int32_t index)
|
||||
{
|
||||
if (index < 0 || index > no_list_items)
|
||||
{
|
||||
selected_list_item = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
selected_list_item = index;
|
||||
}
|
||||
widget_invalidate(this, WIDX_SCROLL);
|
||||
}
|
||||
|
||||
public:
|
||||
void OnOpen() override
|
||||
{
|
||||
widgets = window_object_load_error_widgets;
|
||||
enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_COPY_CURRENT) | (1ULL << WIDX_COPY_ALL)
|
||||
| (1ULL << WIDX_DOWNLOAD_ALL);
|
||||
|
||||
WindowInitScrollWidgets(this);
|
||||
colours[0] = COLOUR_LIGHT_BLUE;
|
||||
colours[1] = COLOUR_LIGHT_BLUE;
|
||||
colours[2] = COLOUR_LIGHT_BLUE;
|
||||
}
|
||||
|
||||
void OnClose() override
|
||||
{
|
||||
_invalidEntries.clear();
|
||||
_invalidEntries.shrink_to_fit();
|
||||
}
|
||||
|
||||
void OnMouseUp(const rct_widgetindex widgetIndex) override
|
||||
{
|
||||
switch (widgetIndex)
|
||||
{
|
||||
case WIDX_CLOSE:
|
||||
window_close(this);
|
||||
return;
|
||||
case WIDX_COPY_CURRENT:
|
||||
if (selected_list_item > -1 && selected_list_item < no_list_items)
|
||||
{
|
||||
const auto name = std::string(_invalidEntries[selected_list_item].GetName());
|
||||
OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(name.c_str());
|
||||
}
|
||||
break;
|
||||
case WIDX_COPY_ALL:
|
||||
CopyObjectNamesToClipboard();
|
||||
break;
|
||||
#ifndef DISABLE_HTTP
|
||||
case WIDX_DOWNLOAD_ALL:
|
||||
DownloadAllObjects();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void OnUpdate() override
|
||||
{
|
||||
frame_no++;
|
||||
|
||||
// Check if the mouse is hovering over the list
|
||||
if (!WidgetIsHighlighted(this, WIDX_SCROLL))
|
||||
{
|
||||
_highlightedIndex = -1;
|
||||
widget_invalidate(this, WIDX_SCROLL);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
_objDownloader.Update();
|
||||
|
||||
// Remove downloaded objects from our invalid entry list
|
||||
if (_objDownloader.IsDownloading())
|
||||
{
|
||||
// Don't do this too often as it isn't particularly efficient
|
||||
if (frame_no % 64 == 0)
|
||||
{
|
||||
UpdateObjectList();
|
||||
}
|
||||
}
|
||||
else if (!_updatedListAfterDownload)
|
||||
{
|
||||
UpdateObjectList();
|
||||
_updatedListAfterDownload = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ScreenSize OnScrollGetSize(const int32_t scrollIndex) override
|
||||
{
|
||||
return ScreenSize(0, no_list_items * SCROLLABLE_ROW_HEIGHT);
|
||||
}
|
||||
|
||||
void OnScrollMouseDown(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
|
||||
{
|
||||
const auto selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT;
|
||||
SelectObjectFromList(selectedItem);
|
||||
}
|
||||
|
||||
void OnScrollMouseOver(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
|
||||
{
|
||||
// Highlight item that the cursor is over, or remove highlighting if none
|
||||
const auto selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT;
|
||||
if (selectedItem < 0 || selectedItem >= no_list_items)
|
||||
_highlightedIndex = -1;
|
||||
else
|
||||
_highlightedIndex = selectedItem;
|
||||
|
||||
widget_invalidate(this, WIDX_SCROLL);
|
||||
}
|
||||
|
||||
void OnDraw(rct_drawpixelinfo& dpi) override
|
||||
{
|
||||
WindowDrawWidgets(this, &dpi);
|
||||
|
||||
// Draw explanatory message
|
||||
auto ft = Formatter();
|
||||
ft.Add<rct_string_id>(STR_OBJECT_ERROR_WINDOW_EXPLANATION);
|
||||
DrawTextWrapped(&dpi, windowPos + ScreenCoordsXY{ 5, 18 }, WW - 10, STR_BLACK_STRING, ft);
|
||||
|
||||
// Draw file name
|
||||
ft = Formatter();
|
||||
ft.Add<rct_string_id>(STR_OBJECT_ERROR_WINDOW_FILE);
|
||||
ft.Add<utf8*>(_filePath.c_str());
|
||||
DrawTextEllipsised(&dpi, { windowPos.x + 5, windowPos.y + 43 }, WW - 5, STR_BLACK_STRING, ft);
|
||||
}
|
||||
|
||||
void OnScrollDraw(const int32_t scrollIndex, rct_drawpixelinfo& dpi) override
|
||||
{
|
||||
auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y };
|
||||
gfx_fill_rect(
|
||||
&dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light);
|
||||
const int32_t listWidth = widgets[WIDX_SCROLL].width();
|
||||
|
||||
for (int32_t i = 0; i < no_list_items; i++)
|
||||
{
|
||||
ScreenCoordsXY screenCoords;
|
||||
screenCoords.y = i * SCROLLABLE_ROW_HEIGHT;
|
||||
if (screenCoords.y > dpi.y + dpi.height)
|
||||
break;
|
||||
|
||||
if (screenCoords.y + SCROLLABLE_ROW_HEIGHT < dpi.y)
|
||||
continue;
|
||||
|
||||
const auto screenRect = ScreenRect{ { 0, screenCoords.y },
|
||||
{ listWidth, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } };
|
||||
// If hovering over item, change the color and fill the backdrop.
|
||||
if (i == selected_list_item)
|
||||
gfx_fill_rect(&dpi, screenRect, ColourMapA[colours[1]].darker);
|
||||
else if (i == _highlightedIndex)
|
||||
gfx_fill_rect(&dpi, screenRect, ColourMapA[colours[1]].mid_dark);
|
||||
else if ((i & 1) != 0) // odd / even check
|
||||
gfx_fill_rect(&dpi, screenRect, ColourMapA[colours[1]].light);
|
||||
|
||||
// Draw the actual object entry's name...
|
||||
screenCoords.x = NAME_COL_LEFT - 3;
|
||||
|
||||
const auto& entry = _invalidEntries[i];
|
||||
|
||||
auto name = entry.GetName();
|
||||
char buffer[256];
|
||||
String::Set(buffer, sizeof(buffer), name.data(), name.size());
|
||||
gfx_draw_string(&dpi, screenCoords, buffer, { COLOUR_DARK_GREEN });
|
||||
|
||||
if (entry.Generation == ObjectGeneration::DAT)
|
||||
{
|
||||
// ... source game ...
|
||||
const auto sourceStringId = object_manager_get_source_game_string(entry.Entry.GetSourceGame());
|
||||
DrawTextBasic(&dpi, { SOURCE_COL_LEFT - 3, screenCoords.y }, sourceStringId, {}, { COLOUR_DARK_GREEN });
|
||||
}
|
||||
|
||||
// ... and type
|
||||
const auto type = GetStringFromObjectType(entry.GetType());
|
||||
DrawTextBasic(&dpi, { TYPE_COL_LEFT - 3, screenCoords.y }, type, {}, { COLOUR_DARK_GREEN });
|
||||
}
|
||||
}
|
||||
|
||||
void Initialise(utf8* path, const size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects)
|
||||
{
|
||||
_invalidEntries = std::vector<ObjectEntryDescriptor>(missingObjects, missingObjects + numMissingObjects);
|
||||
|
||||
// Refresh list items and path
|
||||
no_list_items = static_cast<uint16_t>(numMissingObjects);
|
||||
_filePath = path;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects)
|
||||
{
|
||||
_invalid_entries = std::vector<ObjectEntryDescriptor>(missingObjects, missingObjects + numMissingObjects);
|
||||
|
||||
// Check if window is already open
|
||||
rct_window* window = window_bring_to_front_by_class(WC_OBJECT_LOAD_ERROR);
|
||||
auto* window = window_bring_to_front_by_class(WC_OBJECT_LOAD_ERROR);
|
||||
if (window == nullptr)
|
||||
{
|
||||
window = WindowCreateCentred(WW, WH, &window_object_load_error_events, WC_OBJECT_LOAD_ERROR, 0);
|
||||
|
||||
window->widgets = window_object_load_error_widgets;
|
||||
window->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_COPY_CURRENT) | (1ULL << WIDX_COPY_ALL)
|
||||
| (1ULL << WIDX_DOWNLOAD_ALL);
|
||||
|
||||
WindowInitScrollWidgets(window);
|
||||
window->colours[0] = COLOUR_LIGHT_BLUE;
|
||||
window->colours[1] = COLOUR_LIGHT_BLUE;
|
||||
window->colours[2] = COLOUR_LIGHT_BLUE;
|
||||
window = WindowCreate<ObjectLoadErrorWindow>(WC_OBJECT_LOAD_ERROR, WW, WH, 0);
|
||||
}
|
||||
|
||||
// Refresh list items and path
|
||||
window->no_list_items = static_cast<uint16_t>(numMissingObjects);
|
||||
file_path = path;
|
||||
static_cast<ObjectLoadErrorWindow*>(window)->Initialise(path, numMissingObjects, missingObjects);
|
||||
|
||||
window->Invalidate();
|
||||
return window;
|
||||
}
|
||||
|
||||
static void window_object_load_error_close(rct_window* w)
|
||||
{
|
||||
_invalid_entries.clear();
|
||||
_invalid_entries.shrink_to_fit();
|
||||
}
|
||||
|
||||
static void window_object_load_error_update(rct_window* w)
|
||||
{
|
||||
w->frame_no++;
|
||||
|
||||
// Check if the mouse is hovering over the list
|
||||
if (!WidgetIsHighlighted(w, WIDX_SCROLL))
|
||||
{
|
||||
highlighted_index = -1;
|
||||
widget_invalidate(w, WIDX_SCROLL);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
_objDownloader.Update();
|
||||
|
||||
// Remove downloaded objects from our invalid entry list
|
||||
if (_objDownloader.IsDownloading())
|
||||
{
|
||||
// Don't do this too often as it isn't particularly efficient
|
||||
if (w->frame_no % 64 == 0)
|
||||
{
|
||||
window_object_load_error_update_list(w);
|
||||
}
|
||||
}
|
||||
else if (!_updatedListAfterDownload)
|
||||
{
|
||||
window_object_load_error_update_list(w);
|
||||
_updatedListAfterDownload = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void window_object_load_error_mouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||||
{
|
||||
switch (widgetIndex)
|
||||
{
|
||||
case WIDX_CLOSE:
|
||||
window_close(w);
|
||||
break;
|
||||
case WIDX_COPY_CURRENT:
|
||||
if (w->selected_list_item > -1 && w->selected_list_item < w->no_list_items)
|
||||
{
|
||||
auto name = std::string(_invalid_entries[w->selected_list_item].GetName());
|
||||
OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(name.c_str());
|
||||
}
|
||||
break;
|
||||
case WIDX_COPY_ALL:
|
||||
copy_object_names_to_clipboard(w);
|
||||
break;
|
||||
#ifndef DISABLE_HTTP
|
||||
case WIDX_DOWNLOAD_ALL:
|
||||
window_object_load_error_download_all(w);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void window_object_load_error_scrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
|
||||
{
|
||||
// Highlight item that the cursor is over, or remove highlighting if none
|
||||
int32_t selected_item;
|
||||
selected_item = screenCoords.y / SCROLLABLE_ROW_HEIGHT;
|
||||
if (selected_item < 0 || selected_item >= w->no_list_items)
|
||||
highlighted_index = -1;
|
||||
else
|
||||
highlighted_index = selected_item;
|
||||
|
||||
widget_invalidate(w, WIDX_SCROLL);
|
||||
}
|
||||
|
||||
static void window_object_load_error_select_element_from_list(rct_window* w, int32_t index)
|
||||
{
|
||||
if (index < 0 || index > w->no_list_items)
|
||||
{
|
||||
w->selected_list_item = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
w->selected_list_item = index;
|
||||
}
|
||||
widget_invalidate(w, WIDX_SCROLL);
|
||||
}
|
||||
|
||||
static void window_object_load_error_scrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords)
|
||||
{
|
||||
int32_t selected_item;
|
||||
selected_item = screenCoords.y / SCROLLABLE_ROW_HEIGHT;
|
||||
window_object_load_error_select_element_from_list(w, selected_item);
|
||||
}
|
||||
|
||||
static void window_object_load_error_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height)
|
||||
{
|
||||
*height = w->no_list_items * SCROLLABLE_ROW_HEIGHT;
|
||||
}
|
||||
|
||||
static void window_object_load_error_paint(rct_window* w, rct_drawpixelinfo* dpi)
|
||||
{
|
||||
WindowDrawWidgets(w, dpi);
|
||||
|
||||
// Draw explanatory message
|
||||
auto ft = Formatter();
|
||||
ft.Add<rct_string_id>(STR_OBJECT_ERROR_WINDOW_EXPLANATION);
|
||||
DrawTextWrapped(dpi, w->windowPos + ScreenCoordsXY{ 5, 18 }, WW - 10, STR_BLACK_STRING, ft);
|
||||
|
||||
// Draw file name
|
||||
ft = Formatter();
|
||||
ft.Add<rct_string_id>(STR_OBJECT_ERROR_WINDOW_FILE);
|
||||
ft.Add<utf8*>(file_path.c_str());
|
||||
DrawTextEllipsised(dpi, { w->windowPos.x + 5, w->windowPos.y + 43 }, WW - 5, STR_BLACK_STRING, ft);
|
||||
}
|
||||
|
||||
static void window_object_load_error_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex)
|
||||
{
|
||||
auto dpiCoords = ScreenCoordsXY{ dpi->x, dpi->y };
|
||||
gfx_fill_rect(
|
||||
dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi->width - 1, dpi->height - 1 } }, ColourMapA[w->colours[1]].mid_light);
|
||||
const int32_t list_width = w->widgets[WIDX_SCROLL].width();
|
||||
|
||||
for (int32_t i = 0; i < w->no_list_items; i++)
|
||||
{
|
||||
ScreenCoordsXY screenCoords;
|
||||
screenCoords.y = i * SCROLLABLE_ROW_HEIGHT;
|
||||
if (screenCoords.y > dpi->y + dpi->height)
|
||||
break;
|
||||
|
||||
if (screenCoords.y + SCROLLABLE_ROW_HEIGHT < dpi->y)
|
||||
continue;
|
||||
|
||||
auto screenRect = ScreenRect{ { 0, screenCoords.y }, { list_width, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } };
|
||||
// If hovering over item, change the color and fill the backdrop.
|
||||
if (i == w->selected_list_item)
|
||||
gfx_fill_rect(dpi, screenRect, ColourMapA[w->colours[1]].darker);
|
||||
else if (i == highlighted_index)
|
||||
gfx_fill_rect(dpi, screenRect, ColourMapA[w->colours[1]].mid_dark);
|
||||
else if ((i & 1) != 0) // odd / even check
|
||||
gfx_fill_rect(dpi, screenRect, ColourMapA[w->colours[1]].light);
|
||||
|
||||
// Draw the actual object entry's name...
|
||||
screenCoords.x = NAME_COL_LEFT - 3;
|
||||
|
||||
const auto& entry = _invalid_entries[i];
|
||||
|
||||
auto name = entry.GetName();
|
||||
char buffer[256];
|
||||
String::Set(buffer, sizeof(buffer), name.data(), name.size());
|
||||
gfx_draw_string(dpi, screenCoords, buffer, { COLOUR_DARK_GREEN });
|
||||
|
||||
if (entry.Generation == ObjectGeneration::DAT)
|
||||
{
|
||||
// ... source game ...
|
||||
rct_string_id sourceStringId = object_manager_get_source_game_string(entry.Entry.GetSourceGame());
|
||||
DrawTextBasic(dpi, { SOURCE_COL_LEFT - 3, screenCoords.y }, sourceStringId, {}, { COLOUR_DARK_GREEN });
|
||||
}
|
||||
|
||||
// ... and type
|
||||
rct_string_id type = get_object_type_string(entry.GetType());
|
||||
DrawTextBasic(dpi, { TYPE_COL_LEFT - 3, screenCoords.y }, type, {}, { COLOUR_DARK_GREEN });
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_HTTP
|
||||
|
||||
static void window_object_load_error_download_all(rct_window* w)
|
||||
{
|
||||
if (!_objDownloader.IsDownloading())
|
||||
{
|
||||
_updatedListAfterDownload = false;
|
||||
_objDownloader.Begin(_invalid_entries);
|
||||
}
|
||||
}
|
||||
|
||||
static void window_object_load_error_update_list(rct_window* w)
|
||||
{
|
||||
auto entries = _objDownloader.GetDownloadedEntries();
|
||||
for (auto& de : entries)
|
||||
{
|
||||
_invalid_entries.erase(
|
||||
std::remove_if(
|
||||
_invalid_entries.begin(), _invalid_entries.end(),
|
||||
[de](const ObjectEntryDescriptor& e) { return de.GetName() == e.GetName(); }),
|
||||
_invalid_entries.end());
|
||||
w->no_list_items = static_cast<uint16_t>(_invalid_entries.size());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -98,7 +98,7 @@ void LargeSceneryObject::Unload()
|
||||
gfx_object_free_images(_baseImageId, GetImageTable().GetCount());
|
||||
|
||||
_legacyType.name = 0;
|
||||
_legacyType.image = 0;
|
||||
_baseImageId = _legacyType.image = 0;
|
||||
}
|
||||
|
||||
void LargeSceneryObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int32_t height) const
|
||||
|
||||
@@ -46,6 +46,7 @@ void WaterObject::Unload()
|
||||
language_free_object_string(_legacyType.string_idx);
|
||||
|
||||
_legacyType.string_idx = 0;
|
||||
_legacyType.image_id = 0;
|
||||
}
|
||||
|
||||
void WaterObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int32_t height) const
|
||||
|
||||
@@ -170,3 +170,12 @@ void Painter::ReleaseSession(paint_session* session)
|
||||
session->PaintEntryChain.Clear();
|
||||
_freePaintSessions.push_back(session);
|
||||
}
|
||||
|
||||
Painter::~Painter()
|
||||
{
|
||||
for (auto&& session : _paintSessionPool)
|
||||
{
|
||||
ReleaseSession(session.get());
|
||||
}
|
||||
_paintSessionPool.clear();
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace OpenRCT2
|
||||
|
||||
paint_session* CreateSession(rct_drawpixelinfo* dpi, uint32_t viewFlags);
|
||||
void ReleaseSession(paint_session* session);
|
||||
~Painter();
|
||||
|
||||
private:
|
||||
void PaintReplayNotice(rct_drawpixelinfo* dpi, const char* text);
|
||||
|
||||
@@ -1202,43 +1202,6 @@ money32 set_operating_setting_nested(ride_id_t rideId, RideSetSetting setting, u
|
||||
return res->Error == GameActions::Status::Ok ? 0 : MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
// Finds the direction a ride entrance should face for an NxN station.
|
||||
// Direction is valid if queryLocation is directly adjacent to this station.
|
||||
static Direction GetRideEntranceDirection(const CoordsXY& queryLocation, CoordsXY entranceMin, CoordsXY entranceMax)
|
||||
{
|
||||
entranceMin -= CoordsXY(32, 32);
|
||||
entranceMax += CoordsXY(32, 32);
|
||||
if (queryLocation.x == entranceMin.x)
|
||||
{
|
||||
if (queryLocation.y > entranceMin.y && queryLocation.y < entranceMax.y)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (queryLocation.y == entranceMax.y)
|
||||
{
|
||||
if (queryLocation.x > entranceMin.x && queryLocation.x < entranceMax.x)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (queryLocation.x == entranceMax.x)
|
||||
{
|
||||
if (queryLocation.y > entranceMin.y && queryLocation.y < entranceMax.y)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
if (queryLocation.y == entranceMin.y)
|
||||
{
|
||||
if (queryLocation.x > entranceMin.x && queryLocation.x < entranceMax.x)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
return INVALID_DIRECTION;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006CCF70
|
||||
@@ -1253,18 +1216,19 @@ CoordsXYZD ride_get_entrance_or_exit_position_from_screen_position(const ScreenC
|
||||
{
|
||||
if (info.Element->GetType() == TILE_ELEMENT_TYPE_TRACK)
|
||||
{
|
||||
if (info.Element->AsTrack()->GetRideIndex() == gRideEntranceExitPlaceRideIndex)
|
||||
const auto* trackElement = info.Element->AsTrack();
|
||||
if (trackElement->GetRideIndex() == gRideEntranceExitPlaceRideIndex)
|
||||
{
|
||||
const auto& ted = GetTrackElementDescriptor(info.Element->AsTrack()->GetTrackType());
|
||||
const auto& ted = GetTrackElementDescriptor(trackElement->GetTrackType());
|
||||
if (ted.SequenceProperties[0] & TRACK_SEQUENCE_FLAG_ORIGIN)
|
||||
{
|
||||
if (info.Element->AsTrack()->GetTrackType() == TrackElemType::Maze)
|
||||
if (trackElement->GetTrackType() == TrackElemType::Maze)
|
||||
{
|
||||
gRideEntranceExitPlaceStationIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gRideEntranceExitPlaceStationIndex = info.Element->AsTrack()->GetStationIndex();
|
||||
gRideEntranceExitPlaceStationIndex = trackElement->GetStationIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1302,132 +1266,67 @@ CoordsXYZD ride_get_entrance_or_exit_position_from_screen_position(const ScreenC
|
||||
return entranceExitCoords;
|
||||
}
|
||||
|
||||
// flat rides
|
||||
if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_SINGLE_PIECE_STATION))
|
||||
// find the quadrant the mouse is hovering over - that's the direction to start searching for a station TileElement
|
||||
Direction startDirection = 0;
|
||||
auto mapX = (coordsAtHeight->x & 0x1F) - 16;
|
||||
auto mapY = (coordsAtHeight->y & 0x1F) - 16;
|
||||
if (std::abs(mapX) < std::abs(mapY))
|
||||
{
|
||||
// find the quadrant the mouse is hovering over - that's the direction to start searching for a station TileElement
|
||||
Direction startDirection = 0;
|
||||
auto mapX = (coordsAtHeight->x & 0x1F) - 16;
|
||||
auto mapY = (coordsAtHeight->y & 0x1F) - 16;
|
||||
if (std::abs(mapX) < std::abs(mapY))
|
||||
{
|
||||
startDirection = mapY < 0 ? 3 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
startDirection = mapX < 0 ? 0 : 2;
|
||||
}
|
||||
// check all 4 directions, starting with the mouse's quadrant
|
||||
for (uint8_t directionIncrement = 0; directionIncrement < 4; directionIncrement++)
|
||||
{
|
||||
entranceExitCoords.direction = (startDirection + directionIncrement) & 3;
|
||||
// search for TrackElement one tile over, shifted in the search direction
|
||||
auto nextLocation = entranceExitCoords;
|
||||
nextLocation += CoordsDirectionDelta[entranceExitCoords.direction];
|
||||
if (map_is_location_valid(nextLocation))
|
||||
{
|
||||
// iterate over every element in the tile until we find what we want
|
||||
auto tileElement = map_get_first_element_at(nextLocation);
|
||||
if (tileElement == nullptr)
|
||||
continue;
|
||||
do
|
||||
{
|
||||
if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
|
||||
continue;
|
||||
if (tileElement->GetBaseZ() != stationBaseZ)
|
||||
continue;
|
||||
if (tileElement->AsTrack()->GetRideIndex() != gRideEntranceExitPlaceRideIndex)
|
||||
continue;
|
||||
if (tileElement->AsTrack()->GetTrackType() == TrackElemType::Maze)
|
||||
{
|
||||
// if it's a maze, it can place the entrance and exit immediately
|
||||
entranceExitCoords.direction = direction_reverse(entranceExitCoords.direction);
|
||||
gRideEntranceExitPlaceDirection = entranceExitCoords.direction;
|
||||
return entranceExitCoords;
|
||||
}
|
||||
if (tileElement->AsTrack()->GetStationIndex() != gRideEntranceExitPlaceStationIndex)
|
||||
continue;
|
||||
// if it's not a maze, the sequence properties for the TrackElement must be found to determine if an
|
||||
// entrance can be placed on that side
|
||||
|
||||
// get the ride entrance's side relative to the TrackElement
|
||||
Direction direction = (direction_reverse(entranceExitCoords.direction) - tileElement->GetDirection()) & 3;
|
||||
const auto& ted = GetTrackElementDescriptor(tileElement->AsTrack()->GetTrackType());
|
||||
if (ted.SequenceProperties[tileElement->AsTrack()->GetSequenceIndex()] & (1 << direction))
|
||||
{
|
||||
// if that side of the TrackElement supports stations, the ride entrance is valid and faces away from
|
||||
// the station
|
||||
entranceExitCoords.direction = direction_reverse(entranceExitCoords.direction);
|
||||
gRideEntranceExitPlaceDirection = entranceExitCoords.direction;
|
||||
return entranceExitCoords;
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
}
|
||||
}
|
||||
gRideEntranceExitPlaceDirection = INVALID_DIRECTION;
|
||||
startDirection = mapY < 0 ? 3 : 1;
|
||||
}
|
||||
// tracked rides excluding tower rides
|
||||
else
|
||||
{
|
||||
// create a 2-point rectangle. The rectangle will have a width or height of 0 depending on station direction
|
||||
CoordsXY entranceMin = { stationStart };
|
||||
CoordsXY entranceMax = { stationStart };
|
||||
|
||||
// get the beginning of the station TrackElement
|
||||
auto tileElement = ride_get_station_start_track_element(ride, gRideEntranceExitPlaceStationIndex);
|
||||
if (tileElement == nullptr)
|
||||
startDirection = mapX < 0 ? 0 : 2;
|
||||
}
|
||||
// check all 4 directions, starting with the mouse's quadrant
|
||||
for (uint8_t directionIncrement = 0; directionIncrement < 4; directionIncrement++)
|
||||
{
|
||||
entranceExitCoords.direction = (startDirection + directionIncrement) & 3;
|
||||
// search for TrackElement one tile over, shifted in the search direction
|
||||
auto nextLocation = entranceExitCoords;
|
||||
nextLocation += CoordsDirectionDelta[entranceExitCoords.direction];
|
||||
if (map_is_location_valid(nextLocation))
|
||||
{
|
||||
entranceExitCoords.SetNull();
|
||||
return entranceExitCoords;
|
||||
}
|
||||
auto stationDirection = tileElement->GetDirection();
|
||||
entranceExitCoords.direction = stationDirection;
|
||||
|
||||
auto next = entranceMax;
|
||||
// find additional station TrackElements and extend the line of valid entrance locations
|
||||
while (true)
|
||||
{
|
||||
entranceMax = next;
|
||||
next -= CoordsDirectionDelta[stationDirection];
|
||||
tileElement = map_get_first_element_at(next);
|
||||
// iterate over every element in the tile until we find what we want
|
||||
auto* tileElement = map_get_first_element_at(nextLocation);
|
||||
if (tileElement == nullptr)
|
||||
break;
|
||||
bool goToNextTile = false;
|
||||
|
||||
continue;
|
||||
do
|
||||
{
|
||||
if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
|
||||
continue;
|
||||
if (tileElement->AsTrack()->GetRideIndex() != gRideEntranceExitPlaceRideIndex)
|
||||
if (tileElement->GetBaseZ() != stationBaseZ)
|
||||
continue;
|
||||
if (tileElement->AsTrack()->GetStationIndex() != gRideEntranceExitPlaceStationIndex)
|
||||
auto* trackElement = tileElement->AsTrack();
|
||||
if (trackElement->GetRideIndex() != gRideEntranceExitPlaceRideIndex)
|
||||
continue;
|
||||
|
||||
if (tileElement->AsTrack()->IsStation())
|
||||
if (trackElement->GetTrackType() == TrackElemType::Maze)
|
||||
{
|
||||
goToNextTile = true;
|
||||
// if it's a maze, it can place the entrance and exit immediately
|
||||
entranceExitCoords.direction = direction_reverse(entranceExitCoords.direction);
|
||||
gRideEntranceExitPlaceDirection = entranceExitCoords.direction;
|
||||
return entranceExitCoords;
|
||||
}
|
||||
} while (!goToNextTile && !(tileElement++)->IsLastForTile());
|
||||
// if it's not a maze, the sequence properties for the TrackElement must be found to determine if an
|
||||
// entrance can be placed on that side
|
||||
|
||||
if (!goToNextTile)
|
||||
break;
|
||||
}
|
||||
// swap variables for valid ranges
|
||||
if (entranceMin.x > entranceMax.x)
|
||||
std::swap(entranceMin.x, entranceMax.x);
|
||||
if (entranceMin.y > entranceMax.y)
|
||||
std::swap(entranceMin.y, entranceMax.y);
|
||||
gRideEntranceExitPlaceStationIndex = trackElement->GetStationIndex();
|
||||
|
||||
// get the station direction from its position
|
||||
auto direction = GetRideEntranceDirection(entranceExitCoords, entranceMin, entranceMax);
|
||||
|
||||
if (direction != INVALID_DIRECTION && direction != stationDirection && direction != direction_reverse(stationDirection))
|
||||
{
|
||||
entranceExitCoords.direction = direction;
|
||||
gRideEntranceExitPlaceDirection = entranceExitCoords.direction;
|
||||
return entranceExitCoords;
|
||||
// get the ride entrance's side relative to the TrackElement
|
||||
Direction direction = (direction_reverse(entranceExitCoords.direction) - tileElement->GetDirection()) & 3;
|
||||
const auto& ted = GetTrackElementDescriptor(trackElement->GetTrackType());
|
||||
if (ted.SequenceProperties[trackElement->GetSequenceIndex()] & (1 << direction))
|
||||
{
|
||||
// if that side of the TrackElement supports stations, the ride entrance is valid and faces away from
|
||||
// the station
|
||||
entranceExitCoords.direction = direction_reverse(entranceExitCoords.direction);
|
||||
gRideEntranceExitPlaceDirection = entranceExitCoords.direction;
|
||||
return entranceExitCoords;
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
}
|
||||
}
|
||||
gRideEntranceExitPlaceDirection = INVALID_DIRECTION;
|
||||
return entranceExitCoords;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user