/***************************************************************************** * Copyright (c) 2014 Ted John * 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 "../input.h" #include "../interface/widget.h" #include "../interface/window.h" #include "../localisation/localisation.h" #include "../scenario.h" #include "../sprites.h" #include "dropdown.h" int gAppropriateImageDropdownItemsPerRow[] = { 1, 1, 1, 1, 2, 2, 3, 3, 4, 3, 5, 4, 4, 5, 5, 5, 4, 5, 6, 5, 5, 7, 4, 5, 6, 5, 6, 6, 6, 6, 6, 8, 8, 0 }; enum { WIDX_BACKGROUND, }; static rct_widget window_dropdown_widgets[] = { { WWT_IMGBTN, 0, 0, 0, 0, 0, -1, STR_NONE }, { WIDGETS_END }, }; int _dropdown_num_columns; int _dropdown_num_rows; int _dropdown_item_width; int _dropdown_item_height; int _dropdown_highlighted_index; int gDropdownNumItems; uint16 gDropdownItemsFormat[64]; sint64 gDropdownItemsArgs[64]; // Replaces 0x009DED38 uint32 gDropdownItemsChecked; static void window_dropdown_emptysub() { } static void window_dropdown_paint(); static void* window_dropdown_events[] = { window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_emptysub, window_dropdown_paint, window_dropdown_emptysub }; /** * Shows a text dropdown menu. * rct2: 0x006ECFB9 * * @param x (cx) * @param y (dx) * @param extray (di) * @param flags (bh) * @param num_items (bx) * @param colour (al) */ void window_dropdown_show_text(int x, int y, int extray, uint8 colour, uint8 flags, int num_items) { int i, string_width, max_string_width; char buffer[256]; // Calculate the longest string width max_string_width = 0; for (i = 0; i < num_items; i++) { format_string(buffer, gDropdownItemsFormat[i], (void*)(&gDropdownItemsArgs[i])); RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, sint16) = 224; string_width = gfx_get_string_width(buffer); max_string_width = max(string_width, max_string_width); } window_dropdown_show_text_custom_width(x, y, extray, colour, flags, num_items, max_string_width + 3); } /** * Shows a text dropdown menu. * rct2: 0x006ECFB9, although 0x006ECE50 is real version * * @param x (cx) * @param y (dx) * @param extray (di) * @param flags (bh) * @param num_items (bx) * @param colour (al) */ void window_dropdown_show_text_custom_width(int x, int y, int extray, uint8 colour, uint8 flags, int num_items, int width) { rct_window* w; // Copy the formats and arguments until all use of it is decompiled memcpy((void*)0x009DEBA4, gDropdownItemsFormat, 40 * 2); memcpy((void*)0x009DEBF4, gDropdownItemsArgs, 40 * 8); RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= ~(INPUT_FLAG_DROPDOWN_STAY_OPEN | INPUT_FLAG_DROPDOWN_MOUSE_UP); if (flags & DROPDOWN_FLAG_STAY_OPEN) RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_DROPDOWN_STAY_OPEN; window_dropdown_close(); _dropdown_num_columns = 1; _dropdown_item_width = width; _dropdown_item_height = 10; if (flags & 0x40) _dropdown_item_height = flags & 0x3F; // Set the widgets gDropdownNumItems = num_items; _dropdown_num_rows = num_items; width = _dropdown_item_width * _dropdown_num_columns + 3; int height = _dropdown_item_height * _dropdown_num_rows + 3; if (x + width > RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16)) x = max(0, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16) - width); if (y + height > RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16)) y = max(0, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16) - height); window_dropdown_widgets[WIDX_BACKGROUND].bottom = _dropdown_item_height * num_items + 3; window_dropdown_widgets[WIDX_BACKGROUND].right = _dropdown_item_width + 3; // Create the window w = window_create( x, y + extray, window_dropdown_widgets[WIDX_BACKGROUND].right + 1, window_dropdown_widgets[WIDX_BACKGROUND].bottom + 1, (uint32*)window_dropdown_events, WC_DROPDOWN, 0x02 ); w->widgets = window_dropdown_widgets; if (colour & 0x80) w->flags |= WF_TRANSPARENT; w->colours[0] = colour; // Input state _dropdown_highlighted_index = -1; RCT2_GLOBAL(0x009DED34, sint32) = 0; gDropdownItemsChecked = 0; RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, sint8) = INPUT_STATE_DROPDOWN_ACTIVE; // Copy the following properties until all use of it is decompiled RCT2_GLOBAL(0x009DEBA0, sint16) = gDropdownNumItems; RCT2_GLOBAL(0x009DED44, sint32) = _dropdown_num_columns; RCT2_GLOBAL(0x009DED48, sint32) = _dropdown_num_rows; RCT2_GLOBAL(0x009DED40, sint32) = _dropdown_item_width; RCT2_GLOBAL(0x009DED3C, sint32) = _dropdown_item_height; RCT2_GLOBAL(0x009DEBA2, sint16) = _dropdown_highlighted_index; } /** * Shows an image dropdown menu. * rct2: 0x006ECFB9 * * @param x (cx) * @param y (dx) * @param extray (di) * @param flags (bh) * @param numItems (bx) * @param colour (al) * @param itemWidth (bp) * @param itemHeight (ah) * @param numColumns (bl) */ void window_dropdown_show_image(int x, int y, int extray, uint8 colour, uint8 flags, int numItems, int itemWidth, int itemHeight, int numColumns) { int width, height; rct_window* w; // Copy the formats and arguments until all use of it is decompiled memcpy((void*)0x009DEBA4, gDropdownItemsFormat, 40 * 2); memcpy((void*)0x009DEBF4, gDropdownItemsArgs, 40 * 8); RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= ~(INPUT_FLAG_DROPDOWN_STAY_OPEN | INPUT_FLAG_DROPDOWN_MOUSE_UP); if (flags & DROPDOWN_FLAG_STAY_OPEN) RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_DROPDOWN_STAY_OPEN; // Close existing dropdown window_dropdown_close(); // Set and calculate num items, rows and columns _dropdown_item_width = itemWidth; _dropdown_item_height = itemHeight; gDropdownNumItems = numItems; _dropdown_num_columns = numColumns; _dropdown_num_rows = gDropdownNumItems / _dropdown_num_columns; if (gDropdownNumItems % _dropdown_num_columns != 0) _dropdown_num_rows++; // Calculate position and size width = _dropdown_item_width * _dropdown_num_columns + 3; height = _dropdown_item_height * _dropdown_num_rows + 3; if (x + width > RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16)) x = max(0, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16) - width); if (y + height > RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16)) y = max(0, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16) - height); window_dropdown_widgets[WIDX_BACKGROUND].right = width; window_dropdown_widgets[WIDX_BACKGROUND].bottom = height; // Create the window w = window_create( x, y + extray, window_dropdown_widgets[WIDX_BACKGROUND].right + 1, window_dropdown_widgets[WIDX_BACKGROUND].bottom + 1, (uint32*)window_dropdown_events, WC_DROPDOWN, WF_STICK_TO_FRONT ); w->widgets = window_dropdown_widgets; if (colour & 0x80) w->flags |= WF_TRANSPARENT; w->colours[0] = colour; // Input state _dropdown_highlighted_index = -1; RCT2_GLOBAL(0x009DED34, sint32) = 0; gDropdownItemsChecked = 0; RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, sint8) = INPUT_STATE_DROPDOWN_ACTIVE; // Copy the following properties until all use of it is decompiled RCT2_GLOBAL(0x009DEBA0, sint16) = gDropdownNumItems; RCT2_GLOBAL(0x009DED44, sint32) = _dropdown_num_columns; RCT2_GLOBAL(0x009DED48, sint32) = _dropdown_num_rows; RCT2_GLOBAL(0x009DED40, sint32) = _dropdown_item_width; RCT2_GLOBAL(0x009DED3C, sint32) = _dropdown_item_height; RCT2_GLOBAL(0x009DEBA2, sint16) = _dropdown_highlighted_index; } void window_dropdown_close() { window_close_by_class(WC_DROPDOWN); } static void window_dropdown_paint() { rct_window *w; rct_drawpixelinfo *dpi; window_paint_get_registers(w, dpi); window_draw_widgets(w, dpi); _dropdown_highlighted_index = RCT2_GLOBAL(0x009DEBA2, sint16); { int i, cell_x, cell_y, l, t, r, b, item, image, colour; for (i = 0; i < gDropdownNumItems; i++) { cell_x = i % _dropdown_num_columns; cell_y = i / _dropdown_num_columns; if (gDropdownItemsFormat[i] == DROPDOWN_SEPARATOR) { l = w->x + 2 + (cell_x * _dropdown_item_width); t = w->y + 2 + (cell_y * _dropdown_item_height); r = l + _dropdown_item_width - 1; t += (_dropdown_item_height / 2); b = t; if (w->colours[0] & 0x80) { gfx_fill_rect(dpi, l, t, r, b, (RCT2_ADDRESS(0x009DEDF4, uint8)[w->colours[0]] | 0x02000000) + 1); gfx_fill_rect(dpi, l, t + 1, r, b + 1, (RCT2_ADDRESS(0x009DEDF4, uint8)[w->colours[0]] | 0x02000000) + 2); } else { gfx_fill_rect(dpi, l, t, r, b, *((char*)(0x00141FC47 + (w->colours[0] * 8)))); gfx_fill_rect(dpi, l, t + 1, r, b + 1, *((char*)(0x00141FC4B + (w->colours[0] * 8)))); } } else { // if (i == _dropdown_highlighted_index) { l = w->x + 2 + (cell_x * _dropdown_item_width); t = w->y + 2 + (cell_y * _dropdown_item_height); r = l + _dropdown_item_width - 1; b = t + _dropdown_item_height - 1; gfx_fill_rect(dpi, l, t, r, b, 0x2000000 | 0x2F); } item = gDropdownItemsFormat[i]; if (item == (uint16)-1 || item == (uint16)-2) { // Image item image = *((uint32*)&gDropdownItemsArgs[i]); if (item == (uint16)-2 && _dropdown_highlighted_index == i) image++; gfx_draw_sprite( dpi, image, w->x + 2 + (cell_x * _dropdown_item_width), w->y + 2 + (cell_y * _dropdown_item_height), 0 ); } else { // Text item if (i < 32) if (gDropdownItemsChecked & (1 << i)) item++; // Calculate colour colour = w->colours[0] & 0x7F; if (i == _dropdown_highlighted_index) colour = 2; if (RCT2_GLOBAL(0x009DED34, uint32) & (1 << i)) if (i < 32) colour = (w->colours[0] & 0x7F) | 0x40; // Draw item string gfx_draw_string_left_clipped( dpi, item, (void*)(&gDropdownItemsArgs[i]), colour, w->x + 2 + (cell_x * _dropdown_item_width), w->y + 1 + (cell_y * _dropdown_item_height), w->width - 5 ); } } } } } /* New function based on 6e914e * returns -1 if index is invalid */ int dropdown_index_from_point(int x, int y, rct_window* w){ int top = y - w->y - 2; if (top < 0) return -1; int left = x - w->x; if (left >= w->width) return -1; left -= 2; if (left < 0) return -1; // _dropdown_item_width int column_no = left / RCT2_GLOBAL(0x009DED40, sint32); // _dropdown_no_columns if (column_no >= RCT2_GLOBAL(0x009DED44, sint32)) return -1; // _dropdown_item_height int row_no = top / RCT2_GLOBAL(0x9DED3C, uint8); // _dropdown_no_rows if (row_no >= RCT2_GLOBAL(0x009DED48, sint32)) return -1; // _dropdown_no_columns int dropdown_index = row_no * RCT2_GLOBAL(0x009DED44, sint32) + column_no; // _dropdown_no_items if (dropdown_index >= RCT2_GLOBAL(0x009DEBA0, sint16)) return -1; return dropdown_index; } void window_dropdown_show_colour(rct_window *w, rct_widget *widget, uint8 dropdownColour, uint8 selectedColour) { window_dropdown_show_colour_available(w, widget, dropdownColour, selectedColour, 0xFFFFFFFF); } /** * rct2: 0x006ED43D * al: dropdown colour * ah: selected colour * esi: window * edi: widget * ebp: unknown */ void window_dropdown_show_colour_available(rct_window *w, rct_widget *widget, uint8 dropdownColour, uint8 selectedColour, uint32 availableColours) { int i, numItems; // Count number of available colours numItems = 0; for (i = 0; i < 32; i++) if (availableColours & (1 << i)) numItems++; // Set items for (i = 0; i < 32; i++) { if (availableColours & (1 << i)) { if (selectedColour == i) RCT2_GLOBAL(0x009DEBA2, sint16) = i; gDropdownItemsFormat[i] = 0xFFFE; gDropdownItemsArgs[i] = ((uint64)i << 32) | (0x20000000 | (i << 19) | 5059); } } // Show dropdown window_dropdown_show_image( w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, dropdownColour, DROPDOWN_FLAG_STAY_OPEN, numItems, 12, 12, gAppropriateImageDropdownItemsPerRow[numItems] ); }