1 /*****************************************************************************
2 * Copyright (c) 2014-2020 OpenRCT2 developers
3 *
4 * For a complete list of all authors, please refer to contributors.md
5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6 *
7 * OpenRCT2 is licensed under the GNU General Public License version 3.
8 *****************************************************************************/
9
10 #include <SDL_clipboard.h>
11 #include <openrct2-ui/interface/Widget.h>
12 #include <openrct2-ui/windows/Window.h>
13 #include <openrct2/Context.h>
14 #include <openrct2/OpenRCT2.h>
15 #include <openrct2/Version.h>
16 #include <openrct2/drawing/Drawing.h>
17 #include <openrct2/localisation/Localisation.h>
18 #include <openrct2/localisation/LocalisationService.h>
19 #include <openrct2/sprites.h>
20 #include <openrct2/ui/UiContext.h>
21
22 static constexpr const int32_t WW = 400;
23 static constexpr const int32_t WH = 352;
24 static constexpr const rct_string_id WINDOW_TITLE = STR_ABOUT;
25 static constexpr const int32_t TABHEIGHT = 50;
26
27 // clang-format off
28 enum
29 {
30 WINDOW_ABOUT_PAGE_OPENRCT2,
31 WINDOW_ABOUT_PAGE_RCT2,
32 };
33
34 enum WINDOW_ABOUT_WIDGET_IDX {
35 WIDX_BACKGROUND,
36 WIDX_TITLE,
37 WIDX_CLOSE,
38 WIDX_PAGE_BACKGROUND,
39 WIDX_TAB_ABOUT_OPENRCT2,
40 WIDX_TAB_ABOUT_RCT2,
41
42 WIDX_PAGE_START,
43
44 // About OpenRCT2
45 WIDX_INTRO = WIDX_PAGE_START,
46 WIDX_OPENRCT2_LOGO,
47 WIDX_VERSION,
48 WIDX_COPY_BUILD_INFO,
49 WIDX_NEW_VERSION,
50 WIDX_CHANGELOG,
51 WIDX_JOIN_DISCORD,
52 WIDX_CONTRIBUTORS,
53 WIDX_COPYRIGHT,
54 };
55
56 #define WIDGETS_MAIN \
57 WINDOW_SHIM(WINDOW_TITLE, WW, WH), \
58 MakeWidget ({ 0, TABHEIGHT}, {WW, WH - TABHEIGHT}, WindowWidgetType::ImgBtn, WindowColour::Secondary ), /* page background */ \
59 MakeRemapWidget({ 3, 17}, {91, TABHEIGHT - 16}, WindowWidgetType::Tab, WindowColour::Secondary, SPR_TAB_LARGE), /* about OpenRCT2 button */ \
60 MakeRemapWidget({94, 17}, {91, TABHEIGHT - 16}, WindowWidgetType::Tab, WindowColour::Secondary, SPR_TAB_LARGE) /* about RCT2 button */
61
62 static rct_widget window_about_openrct2_widgets[] = {
63 WIDGETS_MAIN,
64 MakeWidget({10, 60}, {WW - 20, 20}, WindowWidgetType::LabelCentred, WindowColour::Secondary, STR_ABOUT_OPENRCT2_DESCRIPTION), // Introduction
65 MakeWidget({30, 90}, {128, 128}, WindowWidgetType::Placeholder, WindowColour::Secondary, STR_NONE), // OpenRCT2 Logo
66 MakeWidget({168, 100}, {173, 24}, WindowWidgetType::Placeholder, WindowColour::Secondary, STR_NONE), // Build version
67 MakeWidget({344, 100 }, {24, 24}, WindowWidgetType::ImgBtn, WindowColour::Secondary, SPR_G2_COPY, STR_COPY_BUILD_HASH ), // "Copy build info" button
68 MakeWidget({168, 115 + 24}, {200, 14}, WindowWidgetType::Placeholder, WindowColour::Secondary, STR_UPDATE_AVAILABLE ), // "new version" button
69 MakeWidget({168, 115 + 48}, {200, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_CHANGELOG_ELLIPSIS), // changelog button
70 MakeWidget({168, 115 + 72}, {200, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_JOIN_DISCORD ), // "join discord" button
71 MakeWidget({10, 250}, {WW - 20, 50}, WindowWidgetType::LabelCentred, WindowColour::Secondary, STR_ABOUT_OPENRCT2_DESCRIPTION_2), // Contributors
72 MakeWidget({10, 300}, {WW - 20, 50}, WindowWidgetType::LabelCentred, WindowColour::Secondary, STR_ABOUT_OPENRCT2_DESCRIPTION_3), // Copyright
73 WIDGETS_END,
74 };
75
76 static rct_widget window_about_rct2_widgets[] = {
77 WIDGETS_MAIN,
78 WIDGETS_END,
79 };
80
81 static rct_widget *window_about_page_widgets[] = {
82 window_about_openrct2_widgets,
83 window_about_rct2_widgets,
84 };
85
86 #define DEFAULT_ENABLED_WIDGETS \
87 (1ULL << WIDX_CLOSE) | (1ULL << WIDX_TAB_ABOUT_OPENRCT2) | (1ULL << WIDX_TAB_ABOUT_RCT2)
88
89 static uint64_t window_about_page_enabled_widgets[] = {
90 DEFAULT_ENABLED_WIDGETS | (1ULL << WIDX_COPY_BUILD_INFO) | (1ULL << WIDX_CHANGELOG) | (1ULL << WIDX_JOIN_DISCORD),
91 DEFAULT_ENABLED_WIDGETS,
92 };
93
94 static void window_about_openrct2_mouseup(rct_window *w, rct_widgetindex widgetIndex);
95 static void window_about_openrct2_paint(rct_window *w, rct_drawpixelinfo *dpi);
96 static void window_about_openrct2_invalidate(rct_window *w);
97
98 static void window_about_rct2_mouseup(rct_window *w, rct_widgetindex widgetIndex);
99 static void window_about_rct2_paint(rct_window *w, rct_drawpixelinfo *dpi);
100 static void window_about_openrct2_common_paint(rct_window *w, rct_drawpixelinfo *dpi);
101
102 static rct_window_event_list window_about_openrct2_events([](auto& events)
__anon6a7c5c820202(auto& events) 103 {
104 events.mouse_up = &window_about_openrct2_mouseup;
105 events.invalidate = &window_about_openrct2_invalidate;
106 events.paint = &window_about_openrct2_paint;
107 });
108
109 static rct_window_event_list window_about_rct2_events([](auto& events)
__anon6a7c5c820302(auto& events) 110 {
111 events.mouse_up = &window_about_rct2_mouseup;
112 events.paint = &window_about_rct2_paint;
113 });
114
115 static rct_window_event_list *window_about_page_events[] = {
116 &window_about_openrct2_events,
117 &window_about_rct2_events,
118 };
119 // clang-format on
120
121 static void window_about_set_page(rct_window* w, int32_t page);
122
123 /**
124 *
125 * rct2: 0x0066D2AC
126 */
window_about_open()127 rct_window* window_about_open()
128 {
129 rct_window* window;
130
131 // Check if window is already open
132 window = window_bring_to_front_by_class(WC_ABOUT);
133 if (window != nullptr)
134 return window;
135
136 window = WindowCreateCentred(WW, WH, window_about_page_events[WINDOW_ABOUT_PAGE_OPENRCT2], WC_ABOUT, 0);
137
138 window_about_set_page(window, WINDOW_ABOUT_PAGE_OPENRCT2);
139
140 WindowInitScrollWidgets(window);
141
142 return window;
143 }
144
145 #pragma region OpenRCT2
146
window_about_openrct2_mouseup(rct_window * w,rct_widgetindex widgetIndex)147 static void window_about_openrct2_mouseup(rct_window* w, rct_widgetindex widgetIndex)
148 {
149 switch (widgetIndex)
150 {
151 case WIDX_CLOSE:
152 window_close(w);
153 break;
154 case WIDX_TAB_ABOUT_OPENRCT2:
155 case WIDX_TAB_ABOUT_RCT2:
156 window_about_set_page(w, widgetIndex - WIDX_TAB_ABOUT_OPENRCT2);
157 break;
158 case WIDX_JOIN_DISCORD:
159 OpenRCT2::GetContext()->GetUiContext()->OpenURL("https://discord.gg/ZXZd8D8");
160 break;
161 case WIDX_CHANGELOG:
162 context_open_window(WC_CHANGELOG);
163 break;
164 case WIDX_NEW_VERSION:
165 context_open_window_view(WV_NEW_VERSION_INFO);
166 break;
167 case WIDX_COPY_BUILD_INFO:
168 SDL_SetClipboardText(gVersionInfoFull);
169 break;
170 }
171 }
172
window_about_openrct2_common_paint(rct_window * w,rct_drawpixelinfo * dpi)173 static void window_about_openrct2_common_paint(rct_window* w, rct_drawpixelinfo* dpi)
174 {
175 WindowDrawWidgets(w, dpi);
176
177 const auto& aboutOpenRCT2 = w->widgets[WIDX_TAB_ABOUT_OPENRCT2];
178 const auto& aboutRCT2 = w->widgets[WIDX_TAB_ABOUT_RCT2];
179
180 int32_t y = w->windowPos.y + aboutOpenRCT2.midY() - 3;
181 ScreenCoordsXY aboutOpenRCT2Coords(w->windowPos.x + aboutOpenRCT2.left + 45, y);
182 ScreenCoordsXY aboutRCT2Coords(w->windowPos.x + aboutRCT2.left + 45, y);
183
184 // Draw tab names
185 {
186 auto ft = Formatter();
187 ft.Add<rct_string_id>(STR_TITLE_SEQUENCE_OPENRCT2);
188 DrawTextWrapped(
189 dpi, aboutOpenRCT2Coords, 87, STR_WINDOW_COLOUR_2_STRINGID, ft, { COLOUR_AQUAMARINE, TextAlignment::CENTRE });
190 }
191 {
192 auto ft = Formatter();
193 ft.Add<rct_string_id>(STR_TITLE_SEQUENCE_RCT2);
194 DrawTextWrapped(
195 dpi, aboutRCT2Coords, 87, STR_WINDOW_COLOUR_2_STRINGID, ft, { COLOUR_AQUAMARINE, TextAlignment::CENTRE });
196 }
197 }
198
window_about_openrct2_paint(rct_window * w,rct_drawpixelinfo * dpi)199 static void window_about_openrct2_paint(rct_window* w, rct_drawpixelinfo* dpi)
200 {
201 window_about_openrct2_common_paint(w, dpi);
202
203 // Draw logo on placeholder widget
204 ScreenCoordsXY logoCoords = w->windowPos
205 + ScreenCoordsXY(w->widgets[WIDX_OPENRCT2_LOGO].left, w->widgets[WIDX_OPENRCT2_LOGO].top);
206 gfx_draw_sprite(dpi, ImageId(SPR_G2_LOGO), logoCoords);
207
208 // Version info
209 utf8 buffer[256];
210 utf8* ch = buffer;
211 openrct2_write_full_version_info(ch, sizeof(buffer) - (ch - buffer));
212 auto ft = Formatter();
213 ft.Add<const char*>(buffer);
214
215 auto const& versionPlaceholder = w->widgets[WIDX_VERSION];
216 auto width = versionPlaceholder.right - versionPlaceholder.left;
217 auto centreX = versionPlaceholder.left + width / 2;
218 auto centreY = (versionPlaceholder.top + versionPlaceholder.bottom - font_get_line_height(FontSpriteBase::MEDIUM)) / 2;
219 auto centrePos = w->windowPos + ScreenCoordsXY(centreX, centreY);
220 DrawTextWrapped(dpi, centrePos, width, STR_STRING, ft, { w->colours[1], TextAlignment::CENTRE });
221 }
222
window_about_openrct2_invalidate(rct_window * w)223 static void window_about_openrct2_invalidate(rct_window* w)
224 {
225 if (w->page == WINDOW_ABOUT_PAGE_OPENRCT2 && OpenRCT2::GetContext()->HasNewVersionInfo())
226 {
227 w->enabled_widgets |= (1ULL << WIDX_NEW_VERSION);
228 w->widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button;
229 window_about_openrct2_widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button;
230 }
231 }
232
233 #pragma endregion OpenRCT2
234
235 #pragma region RCT2
236
237 /**
238 *
239 * rct2: 0x0066D4D5
240 */
window_about_rct2_mouseup(rct_window * w,rct_widgetindex widgetIndex)241 static void window_about_rct2_mouseup(rct_window* w, rct_widgetindex widgetIndex)
242 {
243 switch (widgetIndex)
244 {
245 case WIDX_CLOSE:
246 window_close(w);
247 break;
248 case WIDX_TAB_ABOUT_OPENRCT2:
249 case WIDX_TAB_ABOUT_RCT2:
250 window_about_set_page(w, widgetIndex - WIDX_TAB_ABOUT_OPENRCT2);
251 break;
252 }
253 }
254
255 /**
256 *
257 * rct2: 0x0066D321
258 */
window_about_rct2_paint(rct_window * w,rct_drawpixelinfo * dpi)259 static void window_about_rct2_paint(rct_window* w, rct_drawpixelinfo* dpi)
260 {
261 window_about_openrct2_common_paint(w, dpi);
262
263 int32_t yPage = w->windowPos.y + w->widgets[WIDX_PAGE_BACKGROUND].top + 5;
264
265 auto screenCoords = ScreenCoordsXY{ w->windowPos.x + 200, yPage + 5 };
266
267 int32_t lineHeight = font_get_line_height(FontSpriteBase::MEDIUM);
268
269 // Credits
270 DrawTextBasic(dpi, screenCoords, STR_COPYRIGHT_CS, {}, { TextAlignment::CENTRE });
271 screenCoords.y += lineHeight + 74;
272 DrawTextBasic(dpi, screenCoords, STR_DESIGNED_AND_PROGRAMMED_BY_CS, {}, { TextAlignment::CENTRE });
273 screenCoords.y += lineHeight;
274 DrawTextBasic(dpi, screenCoords, STR_GRAPHICS_BY_SF, {}, { TextAlignment::CENTRE });
275 screenCoords.y += lineHeight;
276 DrawTextBasic(dpi, screenCoords, STR_SOUND_AND_MUSIC_BY_AB, {}, { TextAlignment::CENTRE });
277 screenCoords.y += lineHeight;
278 DrawTextBasic(dpi, screenCoords, STR_ADDITIONAL_SOUNDS_RECORDED_BY_DE, {}, { TextAlignment::CENTRE });
279 screenCoords.y += lineHeight + 3;
280 DrawTextBasic(dpi, screenCoords, STR_REPRESENTATION_BY_JL, {}, { TextAlignment::CENTRE });
281 screenCoords.y += 2 * lineHeight + 5;
282 DrawTextBasic(dpi, screenCoords, STR_THANKS_TO, {}, { TextAlignment::CENTRE });
283 screenCoords.y += lineHeight;
284 DrawTextBasic(dpi, screenCoords, STR_THANKS_TO_PEOPLE, {}, { TextAlignment::CENTRE });
285 screenCoords.y += 2 * lineHeight + 5;
286 DrawTextBasic(dpi, screenCoords, STR_LICENSED_TO_INFOGRAMES_INTERACTIVE_INC, {}, { TextAlignment::CENTRE });
287
288 // Images
289 gfx_draw_sprite(dpi, ImageId(SPR_CREDITS_CHRIS_SAWYER_SMALL), { w->windowPos.x + 92, yPage + 24 });
290
291 // Licence
292 }
293
294 #pragma endregion RCT2
295
window_about_set_page(rct_window * w,int32_t page)296 static void window_about_set_page(rct_window* w, int32_t page)
297 {
298 w->page = page;
299 w->frame_no = 0;
300 w->pressed_widgets = 0;
301 w->widgets = window_about_page_widgets[page];
302 w->enabled_widgets = window_about_page_enabled_widgets[page];
303 w->event_handlers = window_about_page_events[page];
304
305 w->pressed_widgets |= (page == WINDOW_ABOUT_PAGE_RCT2) ? (1ULL << WIDX_TAB_ABOUT_RCT2) : (1ULL << WIDX_TAB_ABOUT_OPENRCT2);
306
307 WindowInitScrollWidgets(w);
308 w->Invalidate();
309 }
310