1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-21 14:02:59 +01:00
Files
OpenRCT2/src/windows/dropdown.c
duncanspumpkin eaa44ab1e1 Fix #1854. Special track elements disable correctly.
Issue was caused by a change I asked to be done a month back. Dropdowns are awkward windows the global array for dropdown content should be populated before creating them but not anything else as that will get reset. In this case the disabled items were reset. The same happens for highlighted index if that is specified before window creation
2015-09-27 21:55:48 +01:00

439 lines
12 KiB
C

/*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#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;
uint32 *gDropdownItemsDisabled = RCT2_ADDRESS(0x009DED34, uint32);
bool gDropdownIsColour;
int gDropdownLastColourHover;
static void window_dropdown_paint(rct_window *w, rct_drawpixelinfo *dpi);
static rct_window_event_list window_dropdown_events = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
window_dropdown_paint,
NULL
};
/**
* 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, uint16))
x = max(0, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) - width);
if (y + height > RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16))
y = max(0, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) - 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,
&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;
*gDropdownItemsDisabled = 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;
gDropdownIsColour = false;
}
/**
* 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, uint16))
x = max(0, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) - width);
if (y + height > RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16))
y = max(0, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) - 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,
&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;
*gDropdownItemsDisabled = 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;
gDropdownIsColour = false;
}
void window_dropdown_close()
{
window_close_by_class(WC_DROPDOWN);
}
static void window_dropdown_paint(rct_window *w, rct_drawpixelinfo *dpi)
{
int cell_x, cell_y, l, t, r, b, item, image, colour;
window_draw_widgets(w, dpi);
_dropdown_highlighted_index = RCT2_GLOBAL(0x009DEBA2, sint16);
for (int 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 (*gDropdownItemsDisabled & (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]
);
gDropdownIsColour = true;
gDropdownLastColourHover = -1;
}