/***************************************************************************** * Copyright (c) 2014 Ted John, Peter Hill * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. * * This file is part of OpenRCT2. * * OpenRCT2 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . *****************************************************************************/ #include "../addresses.h" #include "../audio/audio.h" #include "../game.h" #include "../drawing/drawing.h" #include "../input.h" #include "../platform/platform.h" #include "../world/map.h" #include "../world/sprite.h" #include "widget.h" #include "window.h" #include "viewport.h" #define RCT2_FIRST_WINDOW (RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window)) #define RCT2_LAST_WINDOW (RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*) - 1) #define RCT2_NEW_WINDOW (RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*)) #define MAX_NUMBER_WINDOWS 11 rct_window* g_window_list = RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window); // converted from uint16 values at 0x009A41EC - 0x009A4230 // these are percentage coordinates of the viewport to center to, if a window is obscuring a location, the next is tried float window_scroll_locations[][2] = { 0.5f, 0.5f, 0.75f, 0.5f, 0.25f, 0.5f, 0.5f, 0.75f, 0.5f, 0.25f, 0.75f, 0.75f, 0.75f, 0.25f, 0.25f, 0.75f, 0.25f, 0.25f, 0.125f, 0.5f, 0.875f, 0.5f, 0.5f, 0.125f, 0.5f, 0.875f, 0.875f, 0.125f, 0.875f, 0.875f, 0.125f, 0.875f, 0.125f, 0.125f, }; static void window_all_wheel_input(); static int window_draw_split(rct_window *w, int left, int top, int right, int bottom); int window_get_widget_index(rct_window *w, rct_widget *widget) { rct_widget *widget2; int i = 0; for (widget2 = w->widgets; widget2->type != WWT_LAST; widget2++, i++) if (widget == widget2) return i; return -1; } int window_get_scroll_index(rct_window *w, int targetWidgetIndex) { int widgetIndex, scrollIndex; rct_widget *widget; if (w->widgets[targetWidgetIndex].type != WWT_SCROLL) return -1; scrollIndex = 0; widgetIndex = 0; for (widget = w->widgets; widget->type != WWT_LAST; widget++, widgetIndex++) { if (widgetIndex == targetWidgetIndex) break; if (widget->type == WWT_SCROLL) scrollIndex++; } return scrollIndex; } int window_get_scroll_index_from_widget(rct_window *w, rct_widget *widget) { int scrollIndex; rct_widget *widget2; if (widget->type != WWT_SCROLL) return -1; scrollIndex = 0; for (widget2 = w->widgets; widget2->type != WWT_LAST; widget2++) { if (widget2 == widget) break; if (widget2->type == WWT_SCROLL) scrollIndex++; } return scrollIndex; } rct_widget *window_get_scroll_widget(rct_window *w, int scrollIndex) { rct_widget *widget; for (widget = w->widgets; widget->type != WWT_LAST; widget++) { if (widget->type != WWT_SCROLL) continue; if (scrollIndex == 0) return widget; scrollIndex--; } return NULL; } static void RCT2_CALLPROC_WE_UPDATE(int address, rct_window* w) { #ifdef _MSC_VER __asm { push address push w mov esi, w call[esp + 4] add esp, 8 } #else __asm__ ( "\ push %[address]\n\ mov eax, %[w] \n\ push eax \n\ mov esi, %[w] \n\ call [esp+4] \n\ add esp, 8 \n\ " : [address] "+m" (address), [w] "+m" (w) : : "eax", "esi" ); #endif } /** * * rct2: 0x006ED7B0 */ void window_dispatch_update_all() { rct_window *w; RCT2_GLOBAL(0x01423604, sint32)++; RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_NOT_SHOWN_TICKS, sint16)++; for (w = RCT2_LAST_WINDOW; w >= g_window_list; w--) RCT2_CALLPROC_WE_UPDATE(w->event_handlers[WE_UPDATE], w); RCT2_CALLPROC_EBPSAFE(0x006EE411); // handle_text_input } /** * * rct2: 0x006E77A1 */ void window_update_all() { rct_window* w; RCT2_GLOBAL(0x009E3CD8, sint32)++; // if (RCT2_GLOBAL(0x009E3CD8, sint32) == 224 && RCT2_GLOBAL(0x009ABDF2, sint8) != 0) // RCT2_CALLPROC(0x004067E3); // ddwindow_move_to_top_corner if (RCT2_GLOBAL(0x009ABDF2, sint8) == 0) return; gfx_draw_all_dirty_blocks(); for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) if (w->viewport != NULL) viewport_update_position(w); // 1000 tick update RCT2_GLOBAL(0x009DEB7C, sint16) += RCT2_GLOBAL(0x009DE588, sint16); if (RCT2_GLOBAL(0x009DEB7C, sint16) >= 1000) { RCT2_GLOBAL(0x009DEB7C, sint16) = 0; for (w = RCT2_LAST_WINDOW; w >= g_window_list; w--) RCT2_CALLPROC_X(w->event_handlers[WE_UNKNOWN_07], 0, 0, 0, 0, (int) w, 0, 0); } // Border flash invalidation for (w = RCT2_LAST_WINDOW; w >= g_window_list; w--) { if (w->flags & WF_WHITE_BORDER_MASK) { w->flags -= WF_WHITE_BORDER_ONE; if (!(w->flags & WF_WHITE_BORDER_MASK)) window_invalidate(w); } } // RCT2_CALLPROC_X(0x006E7868, 0, 0, 0, 0, 0, RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo), 0); // process_mouse_wheel_input(); window_all_wheel_input(); } /** * * rct2: 0x006E78E3 */ static void window_scroll_wheel_input(rct_window *w, int scrollIndex, int wheel) { int widgetIndex, size; rct_scroll *scroll; rct_widget *widget; scroll = &w->scrolls[scrollIndex]; widget = window_get_scroll_widget(w, scrollIndex); widgetIndex = window_get_widget_index(w, widget); if (scroll->flags & VSCROLLBAR_VISIBLE) { size = widget->bottom - widget->top - 1; if (scroll->flags & HSCROLLBAR_VISIBLE) size -= 11; size = max(0, scroll->v_bottom - size); scroll->v_top = min(max(0, scroll->v_top + wheel), size); } else { size = widget->right - widget->left - 1; if (scroll->flags & VSCROLLBAR_VISIBLE) size -= 11; size = max(0, scroll->h_right - size); scroll->h_left = min(max(0, scroll->h_left + wheel), size); } widget_scroll_update_thumbs(w, widgetIndex); widget_invalidate(w, widgetIndex); } /** * * rct2: 0x006E793B */ static int window_wheel_input(rct_window *w, int wheel) { int i; rct_widget *widget; rct_scroll *scroll; i = 0; for (widget = w->widgets; widget->type != WWT_LAST; widget++) { if (widget->type != WWT_SCROLL) continue; // Originally always checked first scroll view, bug maybe? scroll = &w->scrolls[i]; if (scroll->flags & (HSCROLLBAR_VISIBLE | VSCROLLBAR_VISIBLE)) { window_scroll_wheel_input(w, i, wheel); return 1; } i++; } return 0; } /** * * rct2: 0x006E79FB */ static void window_viewport_wheel_input(rct_window *w, int wheel) { if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 9) return; if (wheel < 0) window_zoom_in(w); else if (wheel > 0) window_zoom_out(w); } /** * * rct2: 0x006E7868 */ static void window_all_wheel_input() { int raw, wheel, widgetIndex; rct_window *w; rct_widget *widget; rct_scroll *scroll; // Get wheel value raw = gCursorState.wheel; wheel = 0; while (1) { raw -= 120; if (raw < 0) break; wheel -= 17; } raw += 120; while (1) { raw += 120; if (raw > 0) break; wheel += 17; } raw -= 120; gCursorState.wheel = raw; if (wheel == 0) return; // Check window cursor is over if (!(RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_5)) { w = window_find_from_point(gCursorState.x, gCursorState.y); if (w != NULL) { // Check if main window if (w->classification == WC_MAIN_WINDOW || w->classification == WC_VIEWPORT) { window_viewport_wheel_input(w, wheel); return; } // Check scroll view, cursor is over widgetIndex = window_find_widget_from_point(w, gCursorState.x, gCursorState.y); if (widgetIndex != -1) { widget = &w->widgets[widgetIndex]; if (widget->type == WWT_SCROLL) { scroll = &w->scrolls[RCT2_GLOBAL(0x01420075, uint8)]; if (scroll->flags & (HSCROLLBAR_VISIBLE | VSCROLLBAR_VISIBLE)) { window_scroll_wheel_input(w, window_get_scroll_index(w, widgetIndex), wheel); return; } } // Check other scroll views on window if (window_wheel_input(w, wheel)) return; } } } // Check windows, front to back for (w = RCT2_LAST_WINDOW; w >= g_window_list; w--) if (window_wheel_input(w, wheel)) return; } /** * Opens a new window. * rct2: 0x006EACA4 * * @param x (ax) * @param y (eax >> 16) * @param width (bx) * @param height (ebx >> 16) * @param events (edx) * @param flags (ch) * @param class (cl) */ rct_window *window_create(int x, int y, int width, int height, uint32 *event_handlers, rct_windowclass cls, uint16 flags) { rct_window *w; // Check if there are any window slots left if (RCT2_NEW_WINDOW >= &(g_window_list[MAX_NUMBER_WINDOWS])) { // Close least recently used window for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) if (!(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT | WF_9))) break; window_close(w); } w = RCT2_NEW_WINDOW; // Flags if (flags & WF_STICK_TO_BACK) { for (; w >= g_window_list + 1; w--) { if ((w - 1)->flags & WF_STICK_TO_FRONT) continue; if ((w - 1)->flags & WF_STICK_TO_BACK) break; } } else if (!(flags & WF_STICK_TO_FRONT)) { for (; w >= g_window_list + 1; w--) { if (!((w - 1)->flags & WF_STICK_TO_FRONT)) break; } } // Move w to new window slot if (w != RCT2_NEW_WINDOW) *RCT2_NEW_WINDOW = *w; // Setup window w->classification = cls; w->var_4B8 = -1; w->var_4B9 = -1; w->flags = flags; // Play sounds and flash the window if (!(flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))){ w->flags |= WF_WHITE_BORDER_MASK; sound_play_panned(SOUND_WINDOW_OPEN, x + (width / 2), 0, 0, 0); } w->number = 0; w->x = x; w->y = y; w->width = width; w->height = height; w->viewport = NULL; w->event_handlers = event_handlers; w->enabled_widgets = 0; w->disabled_widgets = 0; w->pressed_widgets = 0; w->hold_down_widgets = 0; w->viewport_focus_coordinates.var_480 = 0; w->viewport_focus_coordinates.x = 0; w->viewport_focus_coordinates.y = 0; w->viewport_focus_coordinates.z = 0; w->viewport_focus_coordinates.rotation = 0; w->page = 0; w->var_48C = 0; w->frame_no = 0; w->list_information_type = 0; w->var_492 = 0; w->selected_tab = 0; w->var_4AE = 0; RCT2_NEW_WINDOW++; window_invalidate(w); return w; } /** * Opens a new window, supposedly automatically positioned * rct2: 0x006EA9B1 * * @param width (bx) * @param height (ebx >> 16) * @param events (edx) * @param flags (ch) * @param class (cl) */ rct_window *window_create_auto_pos(int width, int height, uint32 *event_handlers, rct_windowclass cls, uint16 flags) { int eax, ebx, ecx, edx, esi, edi, ebp; ebx = (height << 16) | width; ecx = (flags << 8) | cls; edx = (int)event_handlers; RCT2_CALLFUNC_X(0x006EA9B1, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); return (rct_window*)esi; } rct_window *window_create_centred(int width, int height, uint32 *event_handlers, rct_windowclass cls, uint16 flags) { int x, y; x = (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) - width) / 2; y = max(28, (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) - height) / 2); return window_create(x, y, width, height, event_handlers, cls, flags); } /** * Closes the specified window. * rct2: 0x006ECD4C * * @param window The window to close (esi). */ void window_close(rct_window* window) { int num_windows; if (window == NULL) return; // Make a copy of the window class and number incase // the window order is changed by the close event. rct_windowclass cls = window->classification; rct_windownumber number = window->number; // Call close event of window RCT2_CALLPROC_X(window->event_handlers[WE_CLOSE], 0, 0, 0, 0, (int)window, 0, 0); window = window_find_by_number(cls, number); if (window == NULL) return; // Remove viewport if (window->viewport != NULL) { window->viewport->width = 0; window->viewport = NULL; } // Invalidate the window (area) window_invalidate(window); // Remove window from list and reshift all windows RCT2_NEW_WINDOW--; num_windows = (RCT2_NEW_WINDOW - window); if (num_windows > 0) memmove(window, window + 1, num_windows * sizeof(rct_window)); viewport_update_pointers(); } /** * Closes all windows with the specified window class. * rct2: 0x006ECCF4 * @param cls (cl) with bit 15 set */ void window_close_by_class(rct_windowclass cls) { rct_window *w; for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) { if (w->classification == cls) { window_close(w); w = g_window_list - 1; } } } /** * Closes all windows with specified window class and number. * rct2: 0x006ECCF4 * @param cls (cl) without bit 15 set * @param number (dx) */ void window_close_by_number(rct_windowclass cls, rct_windownumber number) { rct_window* w; for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) { if (w->classification == cls && w->number == number) { window_close(w); w = g_window_list - 1; } } } /** * Finds the first window with the specified window class. * rct2: 0x006EA8A0 * @param cls (cl) with bit 15 set * @returns the window or NULL if no window was found. */ rct_window *window_find_by_class(rct_windowclass cls) { rct_window *w; for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) if (w->classification == cls) return w; return NULL; } /** * Finds the first window with the specified window class and number. * rct2: 0x006EA8A0 * @param cls (cl) without bit 15 set * @param number (dx) * @returns the window or NULL if no window was found. */ rct_window *window_find_by_number(rct_windowclass cls, rct_windownumber number) { rct_window *w; for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) if (w->classification == cls && w->number == number) return w; return NULL; } /** * Closes the top-most window * * rct2: 0x006E403C */ void window_close_top() { rct_window* w; window_close_by_class(WC_DROPDOWN); if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 2) if (RCT2_GLOBAL(0x0141F570, uint8) != 1) return; for (w = g_window_list; w < RCT2_LAST_WINDOW; w++) { if (!(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) { window_close(w); return; } } } /** * Closes all open windows * * rct2: 0x006EE927 */ void window_close_all() { rct_window* w; window_close_by_class(WC_DROPDOWN); for (w = g_window_list; w < RCT2_LAST_WINDOW; w++){ if (!(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) { window_close(w); w = g_window_list; } } } /** * * rct2: 0x006EA845 */ rct_window *window_find_from_point(int x, int y) { rct_window *w; rct_widget *widget; int widget_index; for (w = RCT2_LAST_WINDOW; w >= g_window_list; w--) { if (x < w->x || x >= w->x + w->width || y < w->y || y >= w->y + w->height) continue; if (w->flags & WF_5) { widget_index = window_find_widget_from_point(w, x, y); if (widget_index == -1) continue; widget = &w->widgets[widget_index]; } return w; } return NULL; } /** * * rct2: 0x006EA594 * x (ax) * y (bx) * returns widget_index (edx) * EDI NEEDS TO BE SET TO w->widgets[widget_index] AFTER */ int window_find_widget_from_point(rct_window *w, int x, int y) { rct_widget *widget; int i, widget_index; // Invalidate the window RCT2_CALLPROC_X(w->event_handlers[WE_INVALIDATE], 0, 0, 0, 0, (int)w, 0, 0); // Find the widget at point x, y widget_index = -1; RCT2_GLOBAL(0x01420074, uint8) = -1; for (i = 0;; i++) { widget = &w->widgets[i]; if (widget->type == WWT_LAST) { break; } else if (widget->type != WWT_EMPTY) { if (widget->type == WWT_SCROLL) RCT2_GLOBAL(0x01420074, uint8)++; if (x >= w->x + widget->left && x <= w->x + widget->right && y >= w->y + widget->top && y <= w->y + widget->bottom ) { widget_index = i; RCT2_GLOBAL(0x01420075, uint8) = RCT2_GLOBAL(0x01420074, uint8); } } } // Return next widget if a dropdown if (widget_index != -1) if (w->widgets[widget_index].type == WWT_DROPDOWN) widget_index++; // Return the widget index return widget_index; } /** * Invalidates the specified window. * rct2: 0x006EB13A * * @param window The window to invalidate (esi). */ void window_invalidate(rct_window *window) { if (window != NULL) gfx_set_dirty_blocks(window->x, window->y, window->x + window->width, window->y + window->height); } /** * Invalidates all windows with the specified window class. * rct2: 0x006EC3AC * @param cls (al) with bit 14 set */ void window_invalidate_by_class(rct_windowclass cls) { rct_window* w; for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) if (w->classification == cls) window_invalidate(w); } /** * Invalidates all windows with the specified window class and number. * rct2: 0x006EC3AC * @param (ah) widget index * @param cls (al) without bit 14 set * @param number (bx) */ void window_invalidate_by_number(rct_windowclass cls, rct_windownumber number) { rct_window* w; for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) if (w->classification == cls && w->number == number) window_invalidate(w); } /** * Invalidates the specified widget of a window. * rct2: 0x006EC402 */ void widget_invalidate(rct_window *w, int widgetIndex) { rct_widget* widget; widget = &w->widgets[widgetIndex]; if (widget->left == -2) return; gfx_set_dirty_blocks(w->x + widget->left, w->y + widget->top, w->x + widget->right + 1, w->y + widget->bottom + 1); } /** * Invalidates the specified widget of all windows that match the specified window class. */ void widget_invalidate_by_class(rct_windowclass cls, int widgetIndex) { rct_window* w; for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) if (w->classification == cls) widget_invalidate(w, widgetIndex); } /** * Invalidates the specified widget of all windows that match the specified window class and number. * rct2: 0x006EC3AC * @param (ah) widget index * @param cls (al) with bit 15 set * @param number (bx) */ void widget_invalidate_by_number(rct_windowclass cls, rct_windownumber number, int widgetIndex) { rct_window* w; for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) if (w->classification == cls && w->number == number) widget_invalidate(w, widgetIndex); } /** * Initialises scroll widgets to their virtual size. * rct2: 0x006EAEB8 * * @param window The window (esi). */ void window_init_scroll_widgets(rct_window *w) { rct_widget* widget; rct_scroll* scroll; int widget_index, scroll_index; int width, height; widget_index = 0; scroll_index = 0; for (widget = w->widgets; widget->type != WWT_LAST; widget++) { if (widget->type != WWT_SCROLL) { widget_index++; continue; } scroll = &w->scrolls[scroll_index]; scroll->flags = 0; window_get_scroll_size(w, scroll_index, &width, &height); scroll->h_left = 0; scroll->h_right = width + 1; scroll->v_top = 0; scroll->v_bottom = height + 1; if (widget->image & 0x01) scroll->flags |= HSCROLLBAR_VISIBLE; if (widget->image & 0x02) scroll->flags |= VSCROLLBAR_VISIBLE; widget_scroll_update_thumbs(w, widget_index); widget_index++; scroll_index++; } } /** * * rct2: 0x006EAE4E * * @param w The window (esi). */ void window_update_scroll_widgets(rct_window *w) { int widgetIndex, scrollIndex, width, height, scrollPositionChanged; rct_scroll *scroll; rct_widget *widget; widgetIndex = 0; scrollIndex = 0; for (widget = w->widgets; widget->type != WWT_LAST; widget++, widgetIndex++) { if (widget->type != WWT_SCROLL) continue; scroll = &w->scrolls[scrollIndex]; window_get_scroll_size(w, scrollIndex, &width, &height); width++; height++; scrollPositionChanged = 0; if ((widget->image & 1) && width != scroll->h_right) { scrollPositionChanged = 1; scroll->h_right = width; } if ((widget->image & 2) && height != scroll->v_bottom) { scrollPositionChanged = 1; scroll->v_bottom = height; } if (scrollPositionChanged) { widget_scroll_update_thumbs(w, widgetIndex); window_invalidate(w); } scrollIndex++; } } int window_get_scroll_size(rct_window *w, int scrollIndex, int *width, int *height) { rct_widget *widget = window_get_scroll_widget(w, scrollIndex); int widgetIndex = window_get_widget_index(w, widget); int eax = 0, ebx = scrollIndex * sizeof(rct_scroll), ecx = 0, edx = 0, esi = (int)w, edi = widgetIndex * sizeof(rct_widget), ebp = 0; RCT2_CALLFUNC_X(w->event_handlers[WE_SCROLL_GETSIZE], & eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); *width = ecx; *height = edx; return 1; } int window_get_scroll_data_index(rct_window *w, int widget_index) { int i, result; result = 0; for (i = 0; i < widget_index; i++) { if (w->widgets[i].type == WWT_SCROLL) result++; } return result; } /** * * rct2: 0x006ECDA4 */ rct_window *window_bring_to_front(rct_window *w) { int i; rct_window* v, t; if (w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)) return w; for (v = RCT2_LAST_WINDOW; v >= g_window_list; v--) if (!(v->flags & WF_STICK_TO_FRONT)) break; if (v >= g_window_list && w != v) { do { t = *w; *w = *(w + 1); *(w + 1) = t; w++; } while (w != v); window_invalidate(w); } if (w->x + w->width < 20) { i = 20 - w->x; w->x += i; if (w->viewport != NULL) w->viewport->x += i; window_invalidate(w); } return w; } rct_window *window_bring_to_front_by_class(rct_windowclass cls) { rct_window* w; w = window_find_by_class(cls); if (w != NULL) { w->flags |= WF_WHITE_BORDER_MASK; window_invalidate(w); w = window_bring_to_front(w); } return w; } /** * * rct2: 0x006ED78A * cls (cl) * number (dx) */ rct_window *window_bring_to_front_by_number(rct_windowclass cls, rct_windownumber number) { rct_window* w; w = window_find_by_number(cls, number); if (w != NULL) { w->flags |= WF_WHITE_BORDER_MASK; window_invalidate(w); w = window_bring_to_front(w); } return w; } /** * * rct2: 0x006EE65A */ void window_push_others_right(rct_window* window) { for (rct_window* w = g_window_list; w < RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*); w++) { if (w == window) continue; if (w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)) continue; if (w->x >= window->x + window->width) continue; if (w->x + w->width <= window->x) continue; if (w->y >= window->y + window->height) continue; if (w->y + w->height <= window->y) continue; window_invalidate(w); if (window->x + window->width + 13 >= RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16)) continue; uint16 push_amount = window->x + window->width - w->x + 3; w->x += push_amount; window_invalidate(w); if (w->viewport != NULL) w->viewport->x += push_amount; } } /** * * rct2: 0x006EE6EA */ void window_push_others_below(rct_window *w1) { int push_amount; rct_window* w2; // Enumerate through all other windows for (w2 = g_window_list; w2 < RCT2_NEW_WINDOW; w2++) { if (w1 == w2) continue; // ? if (w2->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)) continue; // Check if w2 intersects with w1 if (w2->x > (w1->x + w1->width) || w2->x + w2->width < w1->x) continue; if (w2->y > (w1->y + w1->height) || w2->y + w2->height < w1->y) continue; // Check if there is room to push it down if (w1->y + w1->height + 80 >= RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16)) continue; // Invalidate the window's current area window_invalidate(w2); push_amount = w1->y + w1->height - w2->y + 3; w2->y += push_amount; // Invalidate the window's new area window_invalidate(w2); // Update viewport position if necessary if (w2->viewport != NULL) w2->viewport->y += push_amount; } } /** * * rct2: 0x006EE2E4 */ rct_window *window_get_main() { rct_window* w; for (w = g_window_list; w < RCT2_NEW_WINDOW; w++) if (w->classification == WC_MAIN_WINDOW) return w; return NULL; } /** * Based on * rct2: 0x696ee9 & 0x66842F & 0x006AF3B3 * */ void window_scroll_to_viewport(rct_window *w) { int x, y, z; rct_window *mainWindow; // In original checked to make sure x and y were not -1 as well. if (w->viewport == NULL || w->viewport_focus_coordinates.y == -1) return; if (w->viewport_focus_sprite.type & VIEWPORT_FOCUS_TYPE_SPRITE) { rct_sprite *sprite = &(g_sprite_list[w->viewport_focus_sprite.sprite_id]); x = sprite->unknown.x; y = sprite->unknown.y; z = sprite->unknown.z; } else { x = w->viewport_focus_coordinates.x; y = w->viewport_focus_coordinates.y & VIEWPORT_FOCUS_Y_MASK; z = w->viewport_focus_coordinates.z; } mainWindow = window_get_main(); if (mainWindow != NULL) window_scroll_to_location(mainWindow, x, y, z); } /** * * rct2: 0x006E7C9C * @param w (esi) * @param x (eax) * @param y (ecx) * @param z (edx) */ void window_scroll_to_location(rct_window *w, int x, int y, int z) { if (w->viewport) { sint16 height = map_element_height(x, y); if (z < height - 16) { if (!(w->viewport->flags & 1 << 0)) { w->viewport->flags |= 1 << 0; window_invalidate(w); } } else { if (w->viewport->flags & 1 << 0) { w->viewport->flags &= ~(1 << 0); window_invalidate(w); } } sint16 sx; sint16 sy; switch (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8)) { case 0: sx = y - x; sy = ((x + y) / 2) - z; break; case 1: sx = -y - x; sy = ((-x + y) / 2) - z; break; case 2: sx = -y + x; sy = ((-x - y) / 2) - z; break; case 3: sx = y + x; sy = ((x - y) / 2) - z; break; } int i = 0; if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TITLE_DEMO)) { int found = 0; while (!found) { sint16 x2 = w->viewport->x + (sint16)(w->viewport->width * window_scroll_locations[i][0]); sint16 y2 = w->viewport->y + (sint16)(w->viewport->height * window_scroll_locations[i][1]); rct_window* w2 = w; while (1) { w2++; if (w2 >= RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*)) { found = 1; break; } sint16 x1 = w2->x - 10; sint16 y1 = w2->y - 10; if (x2 >= x1 && x2 <= w2->width + x1 + 20) { if (y2 >= y1 && y2 <= w2->height + y1 + 20) { // window is covering this area, try the next one i++; found = 0; break; } } } if (i >= countof(window_scroll_locations)) { i = 0; found = 1; } } } // rct2: 0x006E7C76 if (w->viewport_target_sprite == -1) { if (!(w->flags & WF_2)) { w->saved_view_x = sx - (sint16)(w->viewport->view_width * window_scroll_locations[i][0]); w->saved_view_y = sy - (sint16)(w->viewport->view_height * window_scroll_locations[i][1]); w->flags |= WF_SCROLLING_TO_LOCATION; } } } } /** * * rct2: 0x0068881A */ void window_rotate_camera(rct_window *w) { //RCT2_CALLPROC_X(0x0068881A, 0, 0, 0, 0, (int)w, 0, 0); rct_viewport *viewport = w->viewport; if (viewport == NULL) return; sint16 x = (viewport->width >> 1) + viewport->x; sint16 y = (viewport->height >> 1) + viewport->y; sint16 z; uint8 rot = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); int ecx, edx, esi, edi = (int)viewport, ebp; //has something to do with checking if middle of the viewport is obstructed RCT2_CALLFUNC_X(0x00688972, (int*)&x, (int*)&y, &ecx, &edx, &esi, &edi, &ebp); rct_viewport *other = (rct_viewport*)edi; // other != viewport probably triggers on viewports in ride or guest window? // x is 0x8000 if middle of viewport is obstructed by another window? if (x == (sint16)SPRITE_LOCATION_NULL || other != viewport){ x = (viewport->view_width >> 1) + viewport->view_x; y = (viewport->view_height >> 1) + viewport->view_y; sub_689174(&x, &y, &z, rot); } else { z = map_element_height(x, y); } RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint32) = (rot + 1) % 4; int new_x, new_y; center_2d_coordinates(x, y, z, &new_x, &new_y, viewport); w->saved_view_x = new_x; w->saved_view_y = new_y; viewport->view_x = new_x; viewport->view_y = new_y; window_invalidate(w); RCT2_CALLPROC_EBPSAFE(0x00688956); sub_69E9A7(); } /** * * rct2: 0x006887A6 */ void window_zoom_in(rct_window *w) { rct_viewport* v = w->viewport; // Prevent zooming more than possible. if (v->zoom <= 0) { return; } v->zoom--; v->view_width /= 2; v->view_height /= 2; w->saved_view_x += v->view_width >> 1; w->saved_view_y += v->view_height >> 1; window_invalidate(w); } /** * * rct2: 0x006887E0 */ void window_zoom_out(rct_window *w) { rct_viewport* v = w->viewport; // Prevent zooming more than possible. if (v->zoom >= 3) { return; } v->zoom++; int width = v->view_width; int height = v->view_height; v->view_width *= 2; v->view_height *= 2; w->saved_view_x -= width / 2; w->saved_view_y -= height >> 1; window_invalidate(w); } /** * * rct2: 0x006EE308 * DEPRECIATED please use the new text_input window. */ void window_show_textinput(rct_window *w, int widgetIndex, uint16 title, uint16 text, int value) { RCT2_CALLPROC_X(0x006EE308, title, text, value, widgetIndex, (int)w, 0, 0); } /** * Draws a window that is in the specified region. * rct2: 0x006E756C * left (ax) * top (bx) * right (dx) * bottom (bp) */ void window_draw(rct_window *w, int left, int top, int right, int bottom) { rct_window* v; rct_drawpixelinfo *dpi, copy; int overflow; // RCT2_CALLPROC_X(0x006E756C, left, top, 0, right, w, 0, bottom); // return; // Split window into only the regions that require drawing if (window_draw_split(w, left, top, right, bottom)) return; // Clamp region left = max(left, w->x); top = max(top, w->y); right = min(right, w->x + w->width); bottom = min(bottom, w->y + w->height); if (left >= right) return; if (top >= bottom) return; // Draw the window in this region for (v = w; v < RCT2_NEW_WINDOW; v++) { // Don't draw overlapping opaque windows, they won't have changed if (w != v && !(v->flags & WF_TRANSPARENT)) continue; copy = *RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_DPI, rct_drawpixelinfo); dpi = © // Clamp left to 0 overflow = left - dpi->x; if (overflow > 0) { dpi->x += overflow; dpi->width -= overflow; if (dpi->width <= 0) continue; dpi->pitch += overflow; dpi->bits += overflow; } // Clamp width to right overflow = dpi->x + dpi->width - right; if (overflow > 0) { dpi->width -= overflow; if (dpi->width <= 0) continue; dpi->pitch += overflow; } // Clamp top to 0 overflow = top - dpi->y; if (overflow > 0) { dpi->y += overflow; dpi->height -= overflow; if (dpi->height <= 0) continue; dpi->bits += (dpi->width + dpi->pitch) * overflow; } // Clamp height to bottom overflow = dpi->y + dpi->height - bottom; if (overflow > 0) { dpi->height -= overflow; if (dpi->height <= 0) continue; } RCT2_GLOBAL(0x01420070, sint32) = v->x; // Text colouring RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_WINDOW_COLOUR_1, uint8) = v->colours[0] & 0x7F; RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_WINDOW_COLOUR_2, uint8) = v->colours[1] & 0x7F; RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_WINDOW_COLOUR_3, uint8) = v->colours[2] & 0x7F; RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_WINDOW_COLOUR_4, uint8) = v->colours[3] & 0x7F; // Invalidate the window RCT2_CALLPROC_X(v->event_handlers[WE_INVALIDATE], 0, 0, 0, 0, (int)v, 0, 0); // Paint the window RCT2_CALLPROC_X(v->event_handlers[WE_PAINT], 0, 0, 0, 0, (int)v, (int)dpi, 0); } } /** * Splits a drawing of a window into regions that can be seen and are not hidden * by other opaque overlapping windows. */ static int window_draw_split(rct_window *w, int left, int top, int right, int bottom) { rct_window* topwindow; // Divide the draws up for only the visible regions of the window recursively for (topwindow = w + 1; topwindow < RCT2_NEW_WINDOW; topwindow++) { // Check if this window overlaps w if (topwindow->x >= right || topwindow->y >= bottom) continue; if (topwindow->x + topwindow->width <= left || topwindow->y + topwindow->height <= top) continue; if (topwindow->flags & WF_TRANSPARENT) continue; // A window overlaps w, split up the draw into two regions where the window starts to overlap if (topwindow->x > left) { // Split draw at topwindow.left window_draw(w, left, top, topwindow->x, bottom); window_draw(w, topwindow->x, top, right, bottom); } else if (topwindow->x + topwindow->width < right) { // Split draw at topwindow.right window_draw(w, left, top, topwindow->x + topwindow->width, bottom); window_draw(w, topwindow->x + topwindow->width, top, right, bottom); } else if (topwindow->y > top) { // Split draw at topwindow.top window_draw(w, left, top, right, topwindow->y); window_draw(w, left, topwindow->y, right, bottom); } else if (topwindow->y + topwindow->height < bottom) { // Split draw at topwindow.bottom window_draw(w, left, top, right, topwindow->y + topwindow->height); window_draw(w, left, topwindow->y + topwindow->height, right, bottom); } // Drawing for this region should be done now, exit return 1; } // No windows overlap return 0; } /** * * rct2: 0x006EB15C * * @param window (esi) * @param dpi (edi) */ void window_draw_widgets(rct_window *w, rct_drawpixelinfo *dpi) { rct_widget *widget; int widgetIndex; // RCT2_CALLPROC_X(0x006EB15C, 0, 0, 0, 0, w, dpi, 0); // return; if ((w->flags & WF_TRANSPARENT) && !(w->flags & WF_5)) gfx_fill_rect(dpi, w->x, w->y, w->x + w->width - 1, w->y + w->height - 1, 0x2000000 | 51); //todo: some code missing here? Between 006EB18C and 006EB260 widgetIndex = 0; for (widget = w->widgets; widget->type != WWT_LAST; widget++) { // Check if widget is outside the draw region if (w->x + widget->left < dpi->x + dpi->width && w->x + widget->right >= dpi->x) if (w->y + widget->top < dpi->y + dpi->height && w->y + widget->bottom >= dpi->y) widget_draw(dpi, w, widgetIndex); widgetIndex++; } //todo: something missing here too? Between 006EC32B and 006EC369 if (w->flags & WF_WHITE_BORDER_MASK) { gfx_fill_rect_inset(dpi, w->x, w->y, w->x + w->width - 1, w->y + w->height - 1, 2, 0x10); } } /** * * rct2: 0x00685BE1 * * @param dpi (edi) * @param w (esi) */ void window_draw_viewport(rct_drawpixelinfo *dpi, rct_window *w) { viewport_render(dpi, w->viewport, dpi->x, dpi->y, dpi->x + dpi->width, dpi->y + dpi->height); } void window_set_position(rct_window *w, int x, int y) { window_move_position(w, x - w->x, y - w->y); } void window_move_position(rct_window *w, int dx, int dy) { if (dx == 0 && dy == 0) return; // Invalidate old region window_invalidate(w); // Translate window and viewport w->x += dx; w->y += dy; if (w->viewport != NULL) { w->viewport->x += dx; w->viewport->y += dy; } // Invalidate new region window_invalidate(w); } void window_resize(rct_window *w, int dw, int dh) { int i; if (dw == 0 && dh == 0) return; // Invalidate old region window_invalidate(w); // Clamp new size to minimum and maximum w->width = clamp(w->min_width, w->width + dw, w->max_width); w->height = clamp(w->min_height, w->height + dh, w->max_height); RCT2_CALLPROC_X(w->event_handlers[WE_RESIZE], w->width, w->height, 0, 0, (int)w, 0, 0); RCT2_CALLPROC_X(w->event_handlers[WE_INVALIDATE], 0, 0, 0, 0, (int)w, 0, 0); // Update scroll widgets for (i = 0; i < 3; i++) { w->scrolls[i].h_right = -1; w->scrolls[i].v_bottom = -1; } window_update_scroll_widgets(w); // Invalidate new region window_invalidate(w); } void window_set_resize(rct_window *w, int minWidth, int minHeight, int maxWidth, int maxHeight) { w->min_width = minWidth; w->min_height = minHeight; w->max_width = maxWidth; w->max_height = maxHeight; // Clamp width and height to minimum and maximum if (w->width < minWidth) { w->width = minWidth; window_invalidate(w); } if (w->height < minHeight) { w->height = minHeight; window_invalidate(w); } if (w->width > maxWidth) { w->width = maxWidth; window_invalidate(w); } if (w->height > maxHeight) { w->height = maxHeight; window_invalidate(w); } } /** * * rct2: 0x006EE212 * * @param tool (al) * @param widgetIndex (dx) * @param w (esi) */ int tool_set(rct_window *w, int widgetIndex, int tool) { if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE) { if ( w->classification == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass) && w->number == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber) && widgetIndex == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WIDGETINDEX, uint16) ) { tool_cancel(); return 1; } else { tool_cancel(); } } RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_TOOL_ACTIVE; RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= ~INPUT_FLAG_6; RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TOOL, uint8) = tool; RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass) = w->classification; RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber) = w->number; RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WIDGETINDEX, uint16) = widgetIndex; return 0; } /** * * rct2: 0x006EE281 */ void tool_cancel() { rct_window *w; if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE) { RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= ~INPUT_FLAG_TOOL_ACTIVE; map_invalidate_selection_rect(); map_invalidate_map_selection_tiles(); // Reset map selection RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, uint16) = 0; if (RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WIDGETINDEX, sint16) >= 0) { // Invalidate tool widget widget_invalidate_by_number( RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass), RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber), RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WIDGETINDEX, uint16) ); // Abort tool event w = window_find_by_number( RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass), RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber) ); if (w != NULL) RCT2_CALLPROC_X(w->event_handlers[WE_TOOL_ABORT], 0, 0, 0, RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WIDGETINDEX, uint16), (int)w, 0, 0); } } } /** * * rct2: 0x0068F083 */ void window_guest_list_init_vars_a() { RCT2_GLOBAL(0x013B0E6C, uint32) = 1; RCT2_GLOBAL(0x00F1AF1C, uint32) = 0xFFFFFFFF; RCT2_GLOBAL(0x00F1EE02, uint32) = 0xFFFFFFFF; RCT2_GLOBAL(RCT2_ADDRESS_WINDOW_GUEST_LIST_SELECTED_FILTER, uint8) = 0xFF; } /** * * rct2: 0x0068F050 */ void window_guest_list_init_vars_b() { RCT2_GLOBAL(RCT2_ADDRESS_WINDOW_GUEST_LIST_SELECTED_TAB, uint8) = 0; RCT2_GLOBAL(RCT2_ADDRESS_WINDOW_GUEST_LIST_SELECTED_VIEW, uint8) = 0; RCT2_GLOBAL(0x00F1AF1C, uint32) = 0xFFFFFFFF; RCT2_GLOBAL(0x00F1EE02, uint32) = 0xFFFFFFFF; RCT2_GLOBAL(RCT2_ADDRESS_WINDOW_GUEST_LIST_SELECTED_FILTER, uint8) = 0xFF; RCT2_GLOBAL(0x00F1AF20, uint16) = 0; } void window_event_mouse_up_call(rct_window* w, int widgetIndex) { RCT2_CALLPROC_X(w->event_handlers[WE_MOUSE_UP], 0, 0, 0, widgetIndex, (int)w, (int)&(w->event_handlers[widgetIndex]), 0); } void window_event_resize_call(rct_window* w) { RCT2_CALLPROC_X(w->event_handlers[WE_RESIZE], 0, 0, 0, 0, (int)w, 0, 0); } void window_event_mouse_down_call(rct_window *w, int widgetIndex) { int address = w->event_handlers[WE_MOUSE_DOWN]; rct_widget *widget = &w->widgets[widgetIndex]; #ifdef _MSC_VER __asm { push ebp push address push widget push w push widgetIndex mov edi, widget mov edx, widgetIndex mov esi, w call[esp + 12] add esp, 16 pop ebp } #else __asm__("\ push ebp \n\ push %[address]\n\ mov edi, %[widget] \n\ mov eax, %[w] \n\ mov edx, %[widgetIndex] \n\ push edi \n\ push eax \n\ push edx \n\ mov esi, %[w] \n\ call [esp+12] \n\ add esp, 16 \n\ pop ebp \n\ " :[address] "+m" (address), [w] "+m" (w), [widget] "+m" (widget), [widgetIndex] "+m" (widgetIndex): : "eax", "esi", "edx", "edi" ); #endif } void window_event_invalidate_call(rct_window* w) { RCT2_CALLPROC_X(w->event_handlers[WE_INVALIDATE], 0, 0, 0, 0, (int)w, 0, 0); } /** * rct2: New function not from rct2 * Bubbles an item one position up in the window list. * This is done by swapping the two locations. */ void window_bubble_list_item(rct_window* w, int item_position){ char swap = w->list_item_positions[item_position]; w->list_item_positions[item_position] = w->list_item_positions[item_position + 1]; w->list_item_positions[item_position + 1] = swap; } /* rct2: 0x006ED710 * Called after a window resize to move windows if they * are going to be out of sight. */ void window_relocate_windows(int width, int height){ int new_location = 8; for (rct_window* w = g_window_list; w < RCT2_NEW_WINDOW; w++){ // Work out if the window requires moving if (w->x + 10 < width){ if (w->flags&(WF_STICK_TO_BACK | WF_STICK_TO_FRONT)){ if (w->y -22 < height)continue; } if (w->y + 10 < height)continue; } // Calculate the new locations int x = w->x; int y = w->y; w->x = new_location; w->y = new_location + 28; // Move the next new location so windows are not directly on top new_location += 8; // Adjust the viewport if required. if (w->viewport){ w->viewport->x -= x - w->x; w->viewport->y -= y - w->y; } } } /** * rct2: 0x0066B905 */ void window_resize_gui(int width, int height) { if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 0xE){ window_resize_gui_scenario_editor(width, height); return; } rct_window *mainWind = window_get_main(); if (mainWind != NULL) { rct_viewport* viewport = mainWind->viewport; mainWind->width = width; mainWind->height = height; RCT2_GLOBAL(0x9A9418, uint16) = width - 1; RCT2_GLOBAL(0x9A941C, uint16) = height - 1; viewport->width = width; viewport->height = height; viewport->view_width = width << viewport->zoom; viewport->view_height = height << viewport->zoom; if (mainWind->widgets != NULL && mainWind->widgets[0].type == WWT_VIEWPORT){ mainWind->widgets[0].right = width; mainWind->widgets[0].bottom = height; } } rct_window *topWind = window_find_by_class(WC_TOP_TOOLBAR); if (topWind != NULL) { topWind->width = max(640, width); } rct_window *bottomWind = window_find_by_class(WC_BOTTOM_TOOLBAR); if (bottomWind != NULL) { bottomWind->y = height - 32; bottomWind->width = max(640, width); RCT2_GLOBAL(0x9A95D0, uint16) = width - 1; RCT2_GLOBAL(0x9A95E0, uint16) = width - 3; RCT2_GLOBAL(0x9A95DE, uint16) = width - 118; RCT2_GLOBAL(0x9A95CE, uint16) = width - 120; RCT2_GLOBAL(0x9A9590, uint16) = width - 121; RCT2_GLOBAL(0x9A95A0, uint16) = width - 123; RCT2_GLOBAL(0x9A95C0, uint16) = width - 126; RCT2_GLOBAL(0x9A95BE, uint16) = width - 149; RCT2_GLOBAL(0x9A95EE, uint16) = width - 118; RCT2_GLOBAL(0x9A95F0, uint16) = width - 3; } rct_window *titleWind = window_find_by_class(WC_TITLE_MENU); if (titleWind != NULL) { titleWind->x = width / 2 - 164; titleWind->y = height - 142; } rct_window *exitWind = window_find_by_class(WC_TITLE_EXIT); if (exitWind != NULL) { exitWind->x = width - 40; exitWind->y = height - 64; } } /** * rct2: 0x0066F0DD */ void window_resize_gui_scenario_editor(int width, int height) { rct_window* mainWind = window_get_main(); if (mainWind) { rct_viewport* viewport = mainWind->viewport; mainWind->width = width; mainWind->height = height; RCT2_GLOBAL(0x9A9834, uint16) = width - 1; RCT2_GLOBAL(0x9A9838, uint16) = height - 1; viewport->width = width; viewport->height = height; viewport->view_width = width << viewport->zoom; viewport->view_height = height << viewport->zoom; if (mainWind->widgets != NULL && mainWind->widgets[0].type == WWT_VIEWPORT){ mainWind->widgets[0].right = width; mainWind->widgets[0].bottom = height; } } rct_window *topWind = window_find_by_class(WC_TOP_TOOLBAR); if (topWind != NULL) { topWind->width = max(640, width); } rct_window *bottomWind = window_find_by_class(WC_BOTTOM_TOOLBAR); if (bottomWind != NULL) { bottomWind->y = height - 32; bottomWind->width = max(640, width); RCT2_GLOBAL(0x9A997C, uint16) = bottomWind->width - 1; RCT2_GLOBAL(0x9A997A, uint16) = bottomWind->width - 200; RCT2_GLOBAL(0x9A998A, uint16) = bottomWind->width - 198; RCT2_GLOBAL(0x9A998C, uint16) = bottomWind->width - 3; } } /* Based on rct2: 0x6987ED and another version from window_park */ void window_align_tabs(rct_window *w, uint8 start_tab_id, uint8 end_tab_id) { int i, x = w->widgets[start_tab_id].left; int tab_width = w->widgets[start_tab_id].right - w->widgets[start_tab_id].left; for (i = start_tab_id; i <= end_tab_id; i++) { if (!(w->disabled_widgets & (1LL << i))) { w->widgets[i].left = x; w->widgets[i].right = x + tab_width; x += tab_width + 1; } } } /** * * rct2: 0x006CBCC3 */ void window_close_construction_windows() { window_close_by_class(WC_RIDE_CONSTRUCTION); window_close_by_class(WC_FOOTPATH); window_close_by_class(WC_TRACK_DESIGN_LIST); window_close_by_class(WC_TRACK_DESIGN_PLACE); } /** * * rct2: 0x006EA776 */ void window_invalidate_pressed_image_buttons(rct_window *w) { int widgetIndex; rct_widget *widget; widgetIndex = 0; for (widget = w->widgets; widget->type != WWT_LAST; widget++, widgetIndex++) { if (widget->type != WWT_5 && widget->type != WWT_IMGBTN) continue; if (widget_is_pressed(w, widgetIndex) || widget_is_active_tool(w, widgetIndex)) gfx_set_dirty_blocks(w->x, w->y, w->x + w->width, w->y + w->height); } } /** * * rct2: 0x006EA73F */ void sub_6EA73F() { rct_window *w; if (RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) != 0) RCT2_GLOBAL(0x01423604, uint32)++; for (w = RCT2_LAST_WINDOW; w >= g_window_list; w--) { window_update_scroll_widgets(w); window_invalidate_pressed_image_buttons(w); RCT2_CALLPROC_X(w->event_handlers[WE_RESIZE], 0, 0, 0, 0, (int)w, 0, 0); } } /** * Update zoom based volume attenuation for ride music and clear music list. * rct2: 0x006BC348 */ void window_update_viewport_ride_music() { rct_viewport *viewport; rct_window *w; gRideMusicParamsListEnd = &gRideMusicParamsList[0];//RCT2_GLOBAL(0x009AF42C, rct_ride_music_params*) = (rct_ride_music_params*)0x009AF430; RCT2_GLOBAL(0x00F438A4, rct_viewport*) = (rct_viewport*)-1; for (w = RCT2_LAST_WINDOW; w >= g_window_list; w--) { viewport = w->viewport; if (viewport == NULL || !(viewport->flags & VIEWPORT_FLAG_SOUND_ON)) continue; RCT2_GLOBAL(0x00F438A4, rct_viewport*) = viewport; RCT2_GLOBAL(0x00F438A8, rct_window*) = w; switch (viewport->zoom) { case 0: RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 0; break; case 1: RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 30; break; default: RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 60; break; } break; } } void window_snap_left(rct_window *w, int proximity) { int right, rightMost, wLeftProximity, wRightProximity, wBottom; rct_window *mainWindow, *w2; mainWindow = window_get_main(); wBottom = w->y + w->height; wLeftProximity = w->x - (proximity * 2); wRightProximity = w->x + (proximity * 2); rightMost = INT32_MIN; for (w2 = g_window_list; w2 < RCT2_NEW_WINDOW; w2++) { if (w2 == w || w2 == mainWindow) continue; right = w2->x + w2->width; if (wBottom < w2->y || w->y > w2->y + w2->height) continue; if (right < wLeftProximity || right > wRightProximity) continue; rightMost = max(rightMost, right); } if (0 >= wLeftProximity && 0 <= wRightProximity) rightMost = max(rightMost, 0); if (rightMost != INT32_MIN) w->x = rightMost; } void window_snap_top(rct_window *w, int proximity) { int bottom, bottomMost, wTopProximity, wBottomProximity, wRight; rct_window *mainWindow, *w2; mainWindow = window_get_main(); wRight = w->x + w->width; wTopProximity = w->y - (proximity * 2); wBottomProximity = w->y + (proximity * 2); bottomMost = INT32_MIN; for (w2 = g_window_list; w2 < RCT2_NEW_WINDOW; w2++) { if (w2 == w || w2 == mainWindow) continue; bottom = w2->y + w2->height; if (wRight < w2->x || w->x > w2->x + w2->width) continue; if (bottom < wTopProximity || bottom > wBottomProximity) continue; bottomMost = max(bottomMost, bottom); } if (0 >= wTopProximity && 0 <= wBottomProximity) bottomMost = max(bottomMost, 0); if (bottomMost != INT32_MIN) w->y = bottomMost; } void window_snap_right(rct_window *w, int proximity) { int leftMost, wLeftProximity, wRightProximity, wRight, wBottom, screenWidth; rct_window *mainWindow, *w2; mainWindow = window_get_main(); wRight = w->x + w->width; wBottom = w->y + w->height; wLeftProximity = wRight - (proximity * 2); wRightProximity = wRight + (proximity * 2); leftMost = INT32_MAX; for (w2 = g_window_list; w2 < RCT2_NEW_WINDOW; w2++) { if (w2 == w || w2 == mainWindow) continue; if (wBottom < w2->y || w->y > w2->y + w2->height) continue; if (w2->x < wLeftProximity || w2->x > wRightProximity) continue; leftMost = min(leftMost, w2->x); } screenWidth = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16); if (screenWidth >= wLeftProximity && screenWidth <= wRightProximity) leftMost = min(leftMost, screenWidth); if (leftMost != INT32_MAX) w->x = leftMost - w->width; } void window_snap_bottom(rct_window *w, int proximity) { int topMost, wTopProximity, wBottomProximity, wRight, wBottom, screenHeight; rct_window *mainWindow, *w2; mainWindow = window_get_main(); wRight = w->x + w->width; wBottom = w->y + w->height; wTopProximity = wBottom - (proximity * 2); wBottomProximity = wBottom + (proximity * 2); topMost = INT32_MAX; for (w2 = g_window_list; w2 < RCT2_NEW_WINDOW; w2++) { if (w2 == w || w2 == mainWindow) continue; if (wRight < w2->x || w->x > w2->x + w2->width) continue; if (w2->y < wTopProximity || w2->y > wBottomProximity) continue; topMost = min(topMost, w2->y); } screenHeight = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16); if (screenHeight >= wTopProximity && screenHeight <= wBottomProximity) topMost = min(topMost, screenHeight); if (topMost != INT32_MAX) w->y = topMost - w->height; } void window_move_and_snap(rct_window *w, int newWindowX, int newWindowY, int snapProximity) { int originalX = w->x; int originalY = w->y; newWindowY = clamp(29, newWindowY, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) - 34); if (snapProximity > 0) { w->x = newWindowX; w->y = newWindowY; window_snap_right(w, snapProximity); window_snap_bottom(w, snapProximity); window_snap_left(w, snapProximity); window_snap_top(w, snapProximity); if (w->x == originalX && w->y == originalY) return; newWindowX = w->x; newWindowY = w->y; w->x = originalX; w->y = originalY; } window_set_position(w, newWindowX, newWindowY); } int window_can_resize(rct_window *w) { return (w->flags & WF_RESIZABLE) && (w->min_width != w->max_width || w->min_height != w->max_height); }