diff --git a/resources/g2/loader/loader_hybrid_supports.png b/resources/g2/loader/loader_hybrid_supports.png
new file mode 100644
index 0000000000..8860ca12dd
Binary files /dev/null and b/resources/g2/loader/loader_hybrid_supports.png differ
diff --git a/resources/g2/loader/loader_hybrid_track.png b/resources/g2/loader/loader_hybrid_track.png
new file mode 100644
index 0000000000..326fd4b5ea
Binary files /dev/null and b/resources/g2/loader/loader_hybrid_track.png differ
diff --git a/resources/g2/loader/loader_hybrid_vehicle.png b/resources/g2/loader/loader_hybrid_vehicle.png
new file mode 100644
index 0000000000..914bf2447a
Binary files /dev/null and b/resources/g2/loader/loader_hybrid_vehicle.png differ
diff --git a/resources/g2/loader/loader_steel_supports.png b/resources/g2/loader/loader_steel_supports.png
new file mode 100644
index 0000000000..9dc4c99874
Binary files /dev/null and b/resources/g2/loader/loader_steel_supports.png differ
diff --git a/resources/g2/loader/loader_steel_track.png b/resources/g2/loader/loader_steel_track.png
new file mode 100644
index 0000000000..d0c604426b
Binary files /dev/null and b/resources/g2/loader/loader_steel_track.png differ
diff --git a/resources/g2/loader/loader_steel_vehicle.png b/resources/g2/loader/loader_steel_vehicle.png
new file mode 100644
index 0000000000..410b84f422
Binary files /dev/null and b/resources/g2/loader/loader_steel_vehicle.png differ
diff --git a/resources/g2/loader/loader_wooden_supports.png b/resources/g2/loader/loader_wooden_supports.png
new file mode 100644
index 0000000000..bf324c1b1e
Binary files /dev/null and b/resources/g2/loader/loader_wooden_supports.png differ
diff --git a/resources/g2/loader/loader_wooden_track.png b/resources/g2/loader/loader_wooden_track.png
new file mode 100644
index 0000000000..adafe2432a
Binary files /dev/null and b/resources/g2/loader/loader_wooden_track.png differ
diff --git a/resources/g2/loader/loader_wooden_vehicle.png b/resources/g2/loader/loader_wooden_vehicle.png
new file mode 100644
index 0000000000..5267ea5eb0
Binary files /dev/null and b/resources/g2/loader/loader_wooden_vehicle.png differ
diff --git a/resources/g2/sprites.json b/resources/g2/sprites.json
index d2f4bd51f3..862b804f48 100644
--- a/resources/g2/sprites.json
+++ b/resources/g2/sprites.json
@@ -311,6 +311,39 @@
{
"path": "icons/colour_invisible_pressed.png"
},
+ {
+ "path": "loader/loader_hybrid_supports.png",
+ "y": 10
+ },
+ {
+ "path": "loader/loader_hybrid_track.png",
+ "y": 15
+ },
+ {
+ "path": "loader/loader_hybrid_vehicle.png"
+ },
+ {
+ "path": "loader/loader_steel_supports.png",
+ "y": 27
+ },
+ {
+ "path": "loader/loader_steel_track.png",
+ "y": 16
+ },
+ {
+ "path": "loader/loader_steel_vehicle.png"
+ },
+ {
+ "path": "loader/loader_wooden_supports.png",
+ "y": 3
+ },
+ {
+ "path": "loader/loader_wooden_track.png",
+ "y": 3
+ },
+ {
+ "path": "loader/loader_wooden_vehicle.png"
+ },
{
"path": "palette_map/palette_map_dark_olive_dark.png",
"palette": "keep",
@@ -24912,12 +24945,12 @@
"palette": "keep"
},
{
- "path": "icons/medium-curve-left.png",
+ "path": "icons/medium-curve-left.png",
"x": 2,
"y": 6
},
{
- "path": "icons/medium-curve-right.png",
+ "path": "icons/medium-curve-right.png",
"x": 1,
"y": 6
}
diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp
index 34f8835da2..4b6aece012 100644
--- a/src/openrct2-ui/WindowManager.cpp
+++ b/src/openrct2-ui/WindowManager.cpp
@@ -269,6 +269,11 @@ public:
return nullptr;
}
+ case WindowClass::ProgressWindow:
+ {
+ std::string message = intent->GetStringExtra(INTENT_EXTRA_MESSAGE);
+ return ProgressWindowOpen(message);
+ }
case WindowClass::Ride:
{
const auto rideId = RideId::FromUnderlying(intent->GetSIntExtra(INTENT_EXTRA_RIDE_ID));
diff --git a/src/openrct2-ui/libopenrct2ui.vcxproj b/src/openrct2-ui/libopenrct2ui.vcxproj
index 23fc5adcb4..9c6aeaafc4 100644
--- a/src/openrct2-ui/libopenrct2ui.vcxproj
+++ b/src/openrct2-ui/libopenrct2ui.vcxproj
@@ -193,6 +193,7 @@
+
diff --git a/src/openrct2-ui/windows/ProgressWindow.cpp b/src/openrct2-ui/windows/ProgressWindow.cpp
new file mode 100644
index 0000000000..c803f193a0
--- /dev/null
+++ b/src/openrct2-ui/windows/ProgressWindow.cpp
@@ -0,0 +1,192 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2024 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.
+ *****************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace OpenRCT2::Ui::Windows
+{
+ enum ProgressWindowWidgetIdx
+ {
+ WIDX_BACKGROUND,
+ WIDX_TITLE,
+ WIDX_CLOSE,
+ };
+
+ static constexpr int32_t kWindowWidth = 400;
+ static constexpr int32_t kWindowHeight = 90;
+
+ // clang-format off
+ static Widget kProgressWindowWidgets[] = {
+ MakeWidget({ 0, 0}, { kWindowWidth, kWindowHeight}, WindowWidgetType::Frame, WindowColour::Primary ), // panel / background
+ MakeWidget({ 1, 1}, {kWindowWidth - 3, 14}, WindowWidgetType::Caption, WindowColour::Primary, STR_STRINGID, STR_WINDOW_TITLE_TIP), // title bar
+ MakeWidget({kWindowWidth - 12, 2}, { 11, 12}, WindowWidgetType::CloseBox, WindowColour::Primary, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP), // close x button
+ kWidgetsEnd,
+ };
+
+ struct LoaderVehicleStyle
+ {
+ ImageId supports;
+ ImageId track;
+ ImageId vehicle;
+ };
+
+ static std::array kVehicleStyles = { {
+ {
+ ImageId{SPR_G2_LOADER_HYBRID_SUPPORTS, COLOUR_LIGHT_ORANGE, COLOUR_DARK_BROWN },
+ ImageId{SPR_G2_LOADER_HYBRID_TRACK, COLOUR_LIGHT_ORANGE, COLOUR_LIGHT_ORANGE },
+ ImageId{SPR_G2_LOADER_HYBRID_VEHICLE, COLOUR_OLIVE_GREEN, COLOUR_OLIVE_GREEN, COLOUR_BRIGHT_RED }
+ },
+ {
+ ImageId{SPR_G2_LOADER_STEEL_SUPPORTS, COLOUR_LIGHT_BROWN, COLOUR_BLACK },
+ ImageId{SPR_G2_LOADER_STEEL_TRACK, COLOUR_LIGHT_BROWN, COLOUR_LIGHT_BROWN },
+ ImageId{SPR_G2_LOADER_STEEL_VEHICLE, COLOUR_LIGHT_BLUE, COLOUR_WHITE, COLOUR_LIGHT_BLUE }
+ },
+ {
+ ImageId{SPR_G2_LOADER_WOODEN_SUPPORTS, COLOUR_BLACK, COLOUR_WHITE },
+ ImageId{SPR_G2_LOADER_WOODEN_TRACK, COLOUR_BORDEAUX_RED, COLOUR_BLACK },
+ ImageId{SPR_G2_LOADER_WOODEN_VEHICLE, COLOUR_BRIGHT_RED, COLOUR_BRIGHT_RED, COLOUR_SATURATED_BROWN}
+ },
+ } };
+
+ static uint8_t nextStyle = 0;
+
+ // clang-format on
+ class ProgressWindow final : public Window
+ {
+ private:
+ std::string _captionTemplate;
+ std::string _currentCaption;
+ uint8_t style;
+
+ public:
+ void OnOpen() override
+ {
+ widgets = kProgressWindowWidgets;
+ WindowInitScrollWidgets(*this);
+
+ widgets[WIDX_CLOSE].type = WindowWidgetType::Empty;
+
+ frame_no = 0;
+ min_width = kWindowWidth;
+ min_height = kWindowHeight;
+ max_width = min_width;
+ max_height = min_height;
+
+ style = nextStyle++ % std::size(kVehicleStyles);
+ }
+
+ void OnUpdate() override
+ {
+ frame_no += 3;
+ if (frame_no > width)
+ {
+ frame_no = 0;
+ style++;
+ if (style >= 3)
+ style = 0;
+ }
+
+ InvalidateWidget(WIDX_BACKGROUND);
+ }
+
+ void OnPrepareDraw() override
+ {
+ ResizeFrame();
+ }
+
+ void PrepareCaption()
+ {
+ std::stringstream caption;
+ caption << _captionTemplate;
+ caption << " (" << frame_no << " / " << width << ")";
+
+ _currentCaption = caption.str();
+
+ // Set window title
+ auto ft = Formatter::Common();
+ ft.Add(STR_STRING);
+ ft.Add(_currentCaption.c_str());
+ }
+
+ void OnDraw(DrawPixelInfo& dpi) override
+ {
+ PrepareCaption();
+ WindowDrawWidgets(*this, dpi);
+
+ auto& widget = widgets[WIDX_TITLE];
+ auto screenCoords = windowPos + ScreenCoordsXY{ widget.left, widget.bottom + 1 };
+
+ DrawPixelInfo clipDPI;
+ if (!ClipDrawPixelInfo(clipDPI, dpi, screenCoords, width - 3, height - widget.bottom - 3))
+ return;
+
+ auto& variant = kVehicleStyles[style];
+
+ // Draw supports sprite -- twice, to fill the window
+ auto trackCoords = ScreenCoordsXY{ widget.left, widget.bottom + 1 };
+ GfxDrawSprite(clipDPI, variant.supports, trackCoords);
+ GfxDrawSprite(clipDPI, variant.supports, trackCoords + ScreenCoordsXY(256, 0));
+
+ // Draw track sprite -- twice, to fill the window
+ GfxDrawSprite(clipDPI, variant.track, trackCoords);
+ GfxDrawSprite(clipDPI, variant.track, trackCoords + ScreenCoordsXY(256, 0));
+
+ if (_totalCount == 0)
+ return;
+
+ // Draw vehicle to indicate progress
+ auto* vehicle = GfxGetG1Element(variant.vehicle);
+ auto position = (-vehicle->width + 2) + (frame_no % width);
+ GfxDrawSprite(clipDPI, variant.vehicle, ScreenCoordsXY(position, widget.bottom + 1));
+ }
+
+ void SetCaptionTemplate(const std::string& text)
+ {
+ _captionTemplate = text;
+ Invalidate();
+ }
+ };
+
+ WindowBase* ProgressWindowOpen(const std::string& text)
+ {
+ ProgressWindow* window;
+ if ((window = static_cast(WindowFindByClass(WindowClass::ProgressWindow))) != nullptr)
+ {
+ WindowBringToFront(*window);
+ }
+ else
+ {
+ window = WindowCreate(
+ WindowClass::ProgressWindow, kWindowWidth, kWindowHeight,
+ WF_10 | WF_TRANSPARENT | WF_CENTRE_SCREEN | WF_STICK_TO_FRONT);
+ }
+
+ window->SetCaptionTemplate(text);
+ return window;
+ }
+
+ // force close
+ void ProgressWindowClose()
+ {
+ auto window = WindowFindByClass(WindowClass::ProgressWindow);
+ if (window == nullptr)
+ {
+ return;
+ }
+ auto progressWindow = static_cast(window);
+ progressWindow->Close();
+ }
+} // namespace OpenRCT2::Ui::Windows
diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h
index e5ffb282a2..624f34cbc4 100644
--- a/src/openrct2-ui/windows/Window.h
+++ b/src/openrct2-ui/windows/Window.h
@@ -167,6 +167,9 @@ namespace OpenRCT2::Ui::Windows
WindowBase* NetworkStatusOpenPassword();
void WindowNetworkStatusClose();
+ WindowBase* ProgressWindowOpen(const std::string& text);
+ void ProgressWindowClose();
+
void WindowTextInputKey(WindowBase* w, uint32_t keycode);
void WindowTextInputOpen(
WindowBase* call_w, WidgetIndex call_widget, StringId title, StringId description, const Formatter& descriptionArgs,
diff --git a/src/openrct2/interface/WindowClasses.h b/src/openrct2/interface/WindowClasses.h
index c0bcdbed0d..30f4e81f1e 100644
--- a/src/openrct2/interface/WindowClasses.h
+++ b/src/openrct2/interface/WindowClasses.h
@@ -88,6 +88,7 @@ enum class WindowClass : uint8_t
Transparency = 134,
AssetPacks = 135,
ResetShortcutKeysPrompt = 136,
+ ProgressWindow = 137,
// Only used for colour schemes
Staff = 220,
diff --git a/src/openrct2/scenes/title/TitleScene.cpp b/src/openrct2/scenes/title/TitleScene.cpp
index 5fafb6819d..c9dc43f685 100644
--- a/src/openrct2/scenes/title/TitleScene.cpp
+++ b/src/openrct2/scenes/title/TitleScene.cpp
@@ -29,6 +29,7 @@
#include "../../scenario/ScenarioRepository.h"
#include "../../ui/UiContext.h"
#include "../../util/Util.h"
+#include "../../windows/Intent.h"
#include "TitleSequence.h"
#include "TitleSequenceManager.h"
#include "TitleSequencePlayer.h"
@@ -205,6 +206,11 @@ void TitleScene::CreateWindows()
ContextOpenWindow(WindowClass::TitleOptions);
ContextOpenWindow(WindowClass::TitleLogo);
WindowResizeGui(ContextGetWidth(), ContextGetHeight());
+
+ auto intent = Intent(WindowClass::ProgressWindow);
+ intent.PutExtra(INTENT_EXTRA_MESSAGE, "Important stuff is loading");
+ ContextOpenIntent(&intent);
+
_hideVersionInfo = false;
}
diff --git a/src/openrct2/sprites.h b/src/openrct2/sprites.h
index 235fe5d510..eb66c77299 100644
--- a/src/openrct2/sprites.h
+++ b/src/openrct2/sprites.h
@@ -976,6 +976,18 @@ enum
SPR_G2_ICON_PALETTE_INVISIBLE,
SPR_G2_ICON_PALETTE_INVISIBLE_PRESSED,
+ // G2 Loading progress
+
+ SPR_G2_LOADER_HYBRID_SUPPORTS,
+ SPR_G2_LOADER_HYBRID_TRACK,
+ SPR_G2_LOADER_HYBRID_VEHICLE,
+ SPR_G2_LOADER_STEEL_SUPPORTS,
+ SPR_G2_LOADER_STEEL_TRACK,
+ SPR_G2_LOADER_STEEL_VEHICLE,
+ SPR_G2_LOADER_WOODEN_SUPPORTS,
+ SPR_G2_LOADER_WOODEN_TRACK,
+ SPR_G2_LOADER_WOODEN_VEHICLE,
+
// G2 Palette maps
SPR_G2_PALETTE_BEGIN,