mirror of https://github.com/OpenRCT2/OpenRCT2.git
1774 lines
52 KiB
C
1774 lines
52 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 <SDL_keycode.h>
|
|
#include "addresses.h"
|
|
#include "audio/audio.h"
|
|
#include "config.h"
|
|
#include "cursors.h"
|
|
#include "game.h"
|
|
#include "input.h"
|
|
#include "interface/chat.h"
|
|
#include "interface/console.h"
|
|
#include "interface/keyboard_shortcut.h"
|
|
#include "interface/viewport.h"
|
|
#include "interface/widget.h"
|
|
#include "interface/window.h"
|
|
#include "localisation/localisation.h"
|
|
#include "platform/platform.h"
|
|
#include "ride/ride_data.h"
|
|
#include "scenario.h"
|
|
#include "tutorial.h"
|
|
#include "windows/tooltip.h"
|
|
#include "windows/dropdown.h"
|
|
#include "world/banner.h"
|
|
#include "world/map.h"
|
|
#include "world/sprite.h"
|
|
#include "world/scenery.h"
|
|
#include "openrct2.h"
|
|
|
|
static int _dragX, _dragY;
|
|
static rct_windowclass _dragWindowClass;
|
|
static rct_windownumber _dragWindowNumber;
|
|
static int _dragWidgetIndex, _dragScrollIndex;
|
|
static int _originalWindowWidth, _originalWindowHeight;
|
|
|
|
typedef struct {
|
|
uint32 x, y;
|
|
uint32 state; //1 = LeftDown 2 = LeftUp 3 = RightDown 4 = RightUp
|
|
} rct_mouse_data;
|
|
|
|
rct_mouse_data* mouse_buffer = RCT2_ADDRESS(RCT2_ADDRESS_INPUT_QUEUE, rct_mouse_data);
|
|
|
|
static void game_get_next_input(int *x, int *y, int *state);
|
|
static void input_widget_over(int x, int y, rct_window *w, int widgetIndex);
|
|
static void input_widget_over_change_check(rct_windowclass windowClass, rct_windownumber windowNumber, int widgetIndex);
|
|
static void input_widget_over_flatbutton_invalidate();
|
|
void process_mouse_over(int x, int y);
|
|
void process_mouse_tool(int x, int y);
|
|
void invalidate_scroll();
|
|
static rct_mouse_data* get_mouse_input();
|
|
void map_element_right_click(int type, rct_map_element *mapElement, int x, int y);
|
|
int sub_6EDE88(int x, int y, rct_map_element **mapElement, int *outX, int *outY);
|
|
int get_next_key();
|
|
static void game_handle_input_mouse(int x, int y, int state);
|
|
void game_handle_edge_scroll();
|
|
void game_handle_key_scroll();
|
|
static void input_widget_left(int x, int y, rct_window *w, int widgetIndex);
|
|
void input_state_widget_pressed(int x, int y, int state, int widgetIndex, rct_window* w, rct_widget* widget);
|
|
void sub_6ED990(char cursor_id);
|
|
static void input_window_position_continue(rct_window *w, int lastX, int lastY, int newX, int newY);
|
|
static void input_window_position_end(rct_window *w, int x, int y);
|
|
static void input_window_resize_begin(rct_window *w, int widgetIndex, int x, int y);
|
|
static void input_window_resize_continue(rct_window *w, int x, int y);
|
|
static void input_window_resize_end();
|
|
static void input_viewport_drag_begin(rct_window *w, int x, int y);
|
|
static void input_viewport_drag_continue();
|
|
static void input_viewport_drag_end();
|
|
static void input_scroll_begin();
|
|
static void input_scroll_continue(rct_window *w, int widgetIndex, int state, int x, int y);
|
|
static void input_scroll_end();
|
|
static void input_scroll_part_update_hthumb(rct_window *w, int widgetIndex, int x, int scroll_id);
|
|
static void input_scroll_part_update_hleft(rct_window *w, int widgetIndex, int scroll_id);
|
|
static void input_scroll_part_update_hright(rct_window *w, int widgetIndex, int scroll_id);
|
|
static void input_scroll_part_update_vthumb(rct_window *w, int widgetIndex, int y, int scroll_id);
|
|
static void input_scroll_part_update_vtop(rct_window *w, int widgetIndex, int scroll_id);
|
|
static void input_scroll_part_update_vbottom(rct_window *w, int widgetIndex, int scroll_id);
|
|
static void input_update_tooltip(rct_window *w, int widgetIndex, int x, int y);
|
|
static void update_cursor_position();
|
|
static void sub_6EA2AA(rct_window *w, int widgetIndex, int x, int y, int edi);
|
|
|
|
#pragma region Mouse input
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006EA627
|
|
*/
|
|
void game_handle_input()
|
|
{
|
|
rct_window *w;
|
|
int x, y, state;
|
|
|
|
if (RCT2_GLOBAL(0x009DEA64, uint16) & 2) {
|
|
RCT2_GLOBAL(0x009DEA64, uint16) &= ~2;
|
|
game_do_command(0, 1, 0, 0, GAME_COMMAND_LOAD_OR_QUIT, 2, 0);
|
|
}
|
|
|
|
if (RCT2_GLOBAL(0x009ABDF2, uint8) != 0) {
|
|
for (w = g_window_list; w < RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*); w++)
|
|
window_event_unknown_07_call(w);
|
|
|
|
sub_6EA73F();
|
|
update_cursor_position();
|
|
|
|
for (;;) {
|
|
game_get_next_input(&x, &y, &state);
|
|
if (state == 0)
|
|
break;
|
|
|
|
game_handle_input_mouse(x, y, state & 0xFF);
|
|
}
|
|
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_5) {
|
|
game_handle_input_mouse(x, y, state);
|
|
}
|
|
else if (x != 0x80000000) {
|
|
x = clamp(0, x, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) - 1);
|
|
y = clamp(0, y, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) - 1);
|
|
|
|
game_handle_input_mouse(x, y, state);
|
|
process_mouse_over(x, y);
|
|
process_mouse_tool(x, y);
|
|
}
|
|
}
|
|
|
|
for (w = g_window_list; w < RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*); w++)
|
|
window_event_unknown_08_call(w);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E83C7
|
|
*/
|
|
static void game_get_next_input(int *x, int *y, int *state)
|
|
{
|
|
rct_mouse_data* eax = get_mouse_input();
|
|
if (eax == NULL) {
|
|
*x = gCursorState.x;
|
|
*y = gCursorState.y;
|
|
*state = 0;
|
|
return;
|
|
}
|
|
|
|
*x = eax->x;
|
|
*y = eax->y;
|
|
*state = eax->state;
|
|
|
|
// NOTE this function lacks tutorial logic
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00407074
|
|
*/
|
|
static rct_mouse_data* get_mouse_input()
|
|
{
|
|
uint32 read_index = RCT2_GLOBAL(RCT2_ADDRESS_MOUSE_READ_INDEX, uint32);
|
|
|
|
// check if that location has been written to yet
|
|
if (read_index == RCT2_GLOBAL(RCT2_ADDRESS_MOUSE_WRITE_INDEX, uint32))
|
|
return NULL;
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_MOUSE_READ_INDEX, uint32) = (read_index + 1) % 64;
|
|
return &mouse_buffer[read_index];
|
|
}
|
|
|
|
/* rct2: 0x006E957F
|
|
*/
|
|
static void input_scroll_drag_begin(int x, int y, rct_window* w, rct_widget* widget, int widgetIndex) {
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_SCROLL_RIGHT;
|
|
_dragX = x;
|
|
_dragY = y;
|
|
_dragWindowClass = w->classification;
|
|
_dragWindowNumber = w->number;
|
|
_dragWidgetIndex = widgetIndex;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_DRAG_START, sint16) = 0;
|
|
|
|
_dragScrollIndex = window_get_scroll_data_index(w, widgetIndex);
|
|
platform_hide_cursor();
|
|
}
|
|
|
|
/* Based on (heavily changed) rct2: 0x006E9E0E & 0x006E9ED0 */
|
|
static void input_scroll_drag_continue(int x, int y, rct_window* w) {
|
|
uint8 widgetIndex = _dragWidgetIndex;
|
|
uint8 scrollIndex = _dragScrollIndex;
|
|
|
|
rct_widget* widget = &w->widgets[widgetIndex];
|
|
rct_scroll* scroll = &w->scrolls[scrollIndex];
|
|
|
|
int dx, dy;
|
|
dx = x - _dragX;
|
|
dy = y - _dragY;
|
|
|
|
if (scroll->flags & HSCROLLBAR_VISIBLE) {
|
|
sint16 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 + dx), size);
|
|
}
|
|
|
|
if (scroll->flags & VSCROLLBAR_VISIBLE) {
|
|
sint16 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 + dy), size);
|
|
}
|
|
|
|
widget_scroll_update_thumbs(w, widgetIndex);
|
|
window_invalidate_by_number(w->classification, w->number);
|
|
platform_set_cursor_position(_dragX, _dragY);
|
|
}
|
|
|
|
/* rct2: 0x006E8ACB*/
|
|
static void input_scroll_right(int x, int y, int state) {
|
|
rct_window* w = window_find_by_number(
|
|
_dragWindowClass,
|
|
_dragWindowNumber
|
|
);
|
|
|
|
if (w == NULL) {
|
|
platform_show_cursor();
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_RESET;
|
|
return;
|
|
}
|
|
|
|
switch (state) {
|
|
case 0:
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_DRAG_START, sint16) += RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_LAST_UPDATE, sint16);
|
|
if (x == 0 && y == 0)
|
|
return;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_DRAG_START, sint16) = 1000;
|
|
|
|
input_scroll_drag_continue(x, y, w);
|
|
break;
|
|
case 4:
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_RESET;
|
|
platform_show_cursor();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E8655
|
|
*/
|
|
static void game_handle_input_mouse(int x, int y, int state)
|
|
{
|
|
rct_window *w;
|
|
rct_widget *widget;
|
|
int widgetIndex;
|
|
|
|
// Get window and widget under cursor position
|
|
w = window_find_from_point(x, y);
|
|
widgetIndex = w == NULL ? -1 : window_find_widget_from_point(w, x, y);
|
|
widget = widgetIndex == -1 ? 0 : &w->widgets[widgetIndex];
|
|
|
|
switch (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8)) {
|
|
case INPUT_STATE_RESET:
|
|
window_tooltip_reset(x, y);
|
|
// fall-through
|
|
case INPUT_STATE_NORMAL:
|
|
switch (state) {
|
|
case 0:
|
|
input_widget_over(x, y, w, widgetIndex);
|
|
break;
|
|
case 1:
|
|
input_widget_left(x, y, w, widgetIndex);
|
|
break;
|
|
case 3:
|
|
window_close_by_class(WC_TOOLTIP);
|
|
|
|
if (w != NULL)
|
|
w = window_bring_to_front(w);
|
|
|
|
if (widgetIndex == -1)
|
|
break;
|
|
|
|
if (widget->type == WWT_VIEWPORT) {
|
|
if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & (SCREEN_FLAGS_TRACK_MANAGER | SCREEN_FLAGS_TITLE_DEMO)))
|
|
input_viewport_drag_begin(w, x, y);
|
|
}
|
|
else if (widget->type == WWT_SCROLL) {
|
|
input_scroll_drag_begin(x, y, w, widget, widgetIndex);
|
|
}
|
|
break;
|
|
}
|
|
|
|
break;
|
|
case INPUT_STATE_WIDGET_PRESSED:
|
|
input_state_widget_pressed(x, y, state, widgetIndex, w, widget);
|
|
break;
|
|
case INPUT_STATE_POSITIONING_WINDOW:
|
|
w = window_find_by_number(_dragWindowClass, _dragWindowNumber);
|
|
if (w == NULL) {
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_RESET;
|
|
}
|
|
else {
|
|
input_window_position_continue(w, _dragX, _dragY, x, y);
|
|
if (state == 2)
|
|
input_window_position_end(w, x, y);
|
|
}
|
|
break;
|
|
case INPUT_STATE_VIEWPORT_RIGHT:
|
|
if (state == 0) {
|
|
input_viewport_drag_continue();
|
|
}
|
|
else if (state == 4) {
|
|
input_viewport_drag_end();
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_DRAG_START, sint16) < 500) {
|
|
// If the user pressed the right mouse button for less than 500 ticks, interpret as right click
|
|
viewport_interaction_right_click(x, y);
|
|
}
|
|
}
|
|
break;
|
|
case INPUT_STATE_DROPDOWN_ACTIVE:
|
|
input_state_widget_pressed(x, y, state, widgetIndex, w, widget);
|
|
break;
|
|
case INPUT_STATE_VIEWPORT_LEFT:
|
|
w = window_find_by_number(
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_WINDOWCLASS, rct_windowclass),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_WINDOWNUMBER, rct_windownumber)
|
|
);
|
|
if (!w){
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 0;
|
|
break;
|
|
}
|
|
|
|
if (state == 0){
|
|
if (!w->viewport){
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 0;
|
|
break;
|
|
}
|
|
|
|
if (w->classification != RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_WINDOWCLASS, rct_windowclass) ||
|
|
w->number != RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_WINDOWNUMBER, rct_windownumber) ||
|
|
!(RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE)) break;
|
|
|
|
w = window_find_by_number(
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber)
|
|
);
|
|
if (w == NULL)
|
|
break;
|
|
|
|
window_event_tool_drag_call(w, RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WIDGETINDEX, uint32), x, y);
|
|
}
|
|
else if (state == 2){
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 0;
|
|
if (RCT2_GLOBAL(0x9DE52E, rct_windownumber) != w->number)break;
|
|
if ((RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE)){
|
|
w = window_find_by_number(
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber)
|
|
);
|
|
if (w == NULL)
|
|
break;
|
|
window_event_tool_up_call(w, RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WIDGETINDEX, uint16), x, y);
|
|
}
|
|
else{
|
|
if ((RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_4))
|
|
break;
|
|
|
|
viewport_interaction_left_click(x, y);
|
|
}
|
|
}
|
|
break;
|
|
case INPUT_STATE_SCROLL_LEFT:
|
|
if (state == 0)
|
|
input_scroll_continue(w, widgetIndex, state, x, y);
|
|
else if (state == 2)
|
|
input_scroll_end();
|
|
break;
|
|
case INPUT_STATE_RESIZING:
|
|
w = window_find_by_number(_dragWindowClass, _dragWindowNumber);
|
|
if (w == NULL) {
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_RESET;
|
|
}
|
|
else {
|
|
if (state == 2)
|
|
input_window_resize_end();
|
|
|
|
if (state == 0 || state == 2)
|
|
input_window_resize_continue(w, x, y);
|
|
}
|
|
break;
|
|
case INPUT_STATE_SCROLL_RIGHT:
|
|
input_scroll_right(x, y, state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#pragma region Window positioning / resizing
|
|
|
|
void input_window_position_begin(rct_window *w, int widgetIndex, int x, int y)
|
|
{
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_POSITIONING_WINDOW;
|
|
_dragX = x - w->x;
|
|
_dragY = y - w->y;
|
|
_dragWindowClass = w->classification;
|
|
_dragWindowNumber = w->number;
|
|
_dragWidgetIndex = widgetIndex;
|
|
}
|
|
|
|
static void input_window_position_continue(rct_window *w, int wdx, int wdy, int x, int y)
|
|
{
|
|
int snapProximity;
|
|
|
|
snapProximity = w->flags & WF_NO_SNAPPING ? 0 : gConfigGeneral.window_snap_proximity;
|
|
window_move_and_snap(w, x - wdx, y - wdy, snapProximity);
|
|
}
|
|
|
|
static void input_window_position_end(rct_window *w, int x, int y)
|
|
{
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_NORMAL;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint8) = 0;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_CLASS, rct_windowclass) = _dragWindowClass;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_NUMBER, rct_windownumber) = _dragWindowNumber;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WIDGET_INDEX, uint16) = _dragWidgetIndex;
|
|
window_event_moved_call(w, x, y);
|
|
}
|
|
|
|
static void input_window_resize_begin(rct_window *w, int widgetIndex, int x, int y)
|
|
{
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_RESIZING;
|
|
_dragX = x;
|
|
_dragY = y;
|
|
_dragWindowClass = w->classification;
|
|
_dragWindowNumber = w->number;
|
|
_dragWidgetIndex = widgetIndex;
|
|
_originalWindowWidth = w->width;
|
|
_originalWindowHeight = w->height;
|
|
}
|
|
|
|
static void input_window_resize_continue(rct_window *w, int x, int y)
|
|
{
|
|
int dx, dy, targetWidth, targetHeight;
|
|
|
|
if (y < RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) - 2) {
|
|
dx = x - _dragX;
|
|
dy = y - _dragY;
|
|
targetWidth = _originalWindowWidth + dx;
|
|
targetHeight = _originalWindowHeight + dy;
|
|
|
|
window_resize(
|
|
w,
|
|
targetWidth - w->width,
|
|
targetHeight - w->height
|
|
);
|
|
}
|
|
}
|
|
|
|
static void input_window_resize_end()
|
|
{
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_NORMAL;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint8) = 0;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_CLASS, rct_windowclass) = _dragWindowClass;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_NUMBER, rct_windownumber) = _dragWindowNumber;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WIDGET_INDEX, uint16) = _dragWidgetIndex;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Viewport dragging
|
|
|
|
static void input_viewport_drag_begin(rct_window *w, int x, int y)
|
|
{
|
|
w->flags &= ~WF_SCROLLING_TO_LOCATION;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_VIEWPORT_RIGHT;
|
|
_dragWindowClass = w->classification;
|
|
_dragWindowNumber = w->number;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_DRAG_START, sint16) = 0;
|
|
platform_get_cursor_position(&_dragX, &_dragY);
|
|
platform_hide_cursor();
|
|
|
|
// RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_5;
|
|
}
|
|
|
|
static void input_viewport_drag_continue()
|
|
{
|
|
int dx, dy, newDragX, newDragY;
|
|
rct_window *w;
|
|
rct_viewport *viewport;
|
|
|
|
platform_get_cursor_position(&newDragX, &newDragY);
|
|
|
|
dx = newDragX - _dragX;
|
|
dy = newDragY - _dragY;
|
|
w = window_find_by_number(_dragWindowClass, _dragWindowNumber);
|
|
assert(w != NULL);
|
|
|
|
viewport = w->viewport;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_DRAG_START, sint16) += RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_LAST_UPDATE, sint16);
|
|
if (viewport == NULL) {
|
|
platform_show_cursor();
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_RESET;
|
|
} else if (dx != 0 || dy != 0) {
|
|
if (!(w->flags & WF_NO_SCROLLING)) {
|
|
// User dragged a scrollable viewport
|
|
|
|
// If the drag time is less than 500 the "drag" is usually interpreted as a right click.
|
|
// As the user moved the mouse, don't interpret it as right click in any case.
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_DRAG_START, sint16) = 1000;
|
|
|
|
dx <<= viewport->zoom + 1;
|
|
dy <<= viewport->zoom + 1;
|
|
if (gConfigGeneral.invert_viewport_drag){
|
|
w->saved_view_x -= dx;
|
|
w->saved_view_y -= dy;
|
|
} else {
|
|
w->saved_view_x += dx;
|
|
w->saved_view_y += dy;
|
|
}
|
|
}
|
|
}
|
|
|
|
platform_set_cursor_position(_dragX, _dragY);
|
|
}
|
|
|
|
static void input_viewport_drag_end()
|
|
{
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_RESET;
|
|
platform_show_cursor();
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Scroll bars
|
|
|
|
static void input_scroll_begin(rct_window *w, int widgetIndex, int x, int y)
|
|
{
|
|
rct_widget *widget;
|
|
|
|
widget = &w->widgets[widgetIndex];
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_SCROLL_LEFT;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WIDGETINDEX, uint16) = widgetIndex;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWCLASS, rct_windowclass) = w->classification;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWNUMBER, rct_windownumber) = w->number;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_X, uint16) = x;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_Y, uint16) = y;
|
|
|
|
int eax, ebx, scroll_area, scroll_id;
|
|
scroll_id = 0; // safety
|
|
widget_scroll_get_part(w, widget, x, y, &eax, &ebx, &scroll_area, &scroll_id);
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SCROLL_AREA, uint16) = scroll_area;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SCROLL_ID, uint32) = scroll_id * sizeof(rct_scroll);
|
|
window_event_unknown_15_call(w, scroll_id, scroll_area);
|
|
if (scroll_area == SCROLL_PART_VIEW){
|
|
window_event_scroll_mousedown_call(w, scroll_id, eax, ebx);
|
|
return;
|
|
}
|
|
|
|
rct_widget* widg = &w->widgets[widgetIndex];
|
|
rct_scroll* scroll = &w->scrolls[scroll_id];
|
|
|
|
int widget_width = widg->right - widg->left - 1;
|
|
if (scroll->flags & VSCROLLBAR_VISIBLE)
|
|
widget_width -= 11;
|
|
widget_width = max(scroll->h_right - widget_width, 0);
|
|
|
|
int widget_height = widg->bottom - widg->top - 1;
|
|
if (scroll->flags & HSCROLLBAR_VISIBLE)
|
|
widget_height -= 11;
|
|
widget_height = max(scroll->v_bottom - widget_height, 0);
|
|
|
|
switch (scroll_area) {
|
|
case SCROLL_PART_HSCROLLBAR_LEFT:
|
|
scroll->h_left = max(scroll->h_left - 3, 0);
|
|
break;
|
|
case SCROLL_PART_HSCROLLBAR_RIGHT:
|
|
scroll->h_left = min(scroll->h_left + 3, widget_width);
|
|
break;
|
|
case SCROLL_PART_HSCROLLBAR_LEFT_TROUGH:
|
|
scroll->h_left = max(scroll->h_left - widget_width , 0);
|
|
break;
|
|
case SCROLL_PART_HSCROLLBAR_RIGHT_TROUGH:
|
|
scroll->h_left = min(scroll->h_left + widget_width, widget_width);
|
|
break;
|
|
case SCROLL_PART_VSCROLLBAR_TOP:
|
|
scroll->v_top = max(scroll->v_top - 3, 0);
|
|
break;
|
|
case SCROLL_PART_VSCROLLBAR_BOTTOM:
|
|
scroll->v_top = min(scroll->v_top + 3, widget_height);
|
|
break;
|
|
case SCROLL_PART_VSCROLLBAR_TOP_TROUGH:
|
|
scroll->v_top = max(scroll->v_top - widget_height, 0);
|
|
break;
|
|
case SCROLL_PART_VSCROLLBAR_BOTTOM_TROUGH:
|
|
scroll->v_top = min(scroll->v_top + widget_height, widget_height);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
widget_scroll_update_thumbs(w, widgetIndex);
|
|
window_invalidate_by_number(widgetIndex, w->classification);
|
|
}
|
|
|
|
static void input_scroll_continue(rct_window *w, int widgetIndex, int state, int x, int y)
|
|
{
|
|
rct_widget *widget;
|
|
int scroll_part, scroll_id;
|
|
int x2, y2;
|
|
|
|
assert(w != NULL);
|
|
|
|
widget = &w->widgets[widgetIndex];
|
|
if (widgetIndex != RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WIDGETINDEX, uint32)){
|
|
invalidate_scroll();
|
|
return;
|
|
}
|
|
if (w->classification != RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWCLASS, uint8)){
|
|
invalidate_scroll();
|
|
return;
|
|
}
|
|
if (w->number != RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWNUMBER, uint16)){
|
|
invalidate_scroll();
|
|
return;
|
|
}
|
|
|
|
widget_scroll_get_part(w, widget, x, y, &x2, &y2, &scroll_part, &scroll_id);
|
|
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SCROLL_AREA, uint16) == SCROLL_PART_HSCROLLBAR_THUMB){
|
|
int temp_x = x;
|
|
x -= RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_X, uint16);
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_X, uint16) = temp_x;
|
|
input_scroll_part_update_hthumb(w, widgetIndex, x, scroll_id);
|
|
return;
|
|
}
|
|
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SCROLL_AREA, uint16) == SCROLL_PART_VSCROLLBAR_THUMB){
|
|
int temp_y = y;
|
|
y -= RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_Y, uint16);
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_Y, uint16) = temp_y;
|
|
input_scroll_part_update_vthumb(w, widgetIndex, y, scroll_id);
|
|
return;
|
|
}
|
|
|
|
x = x2;
|
|
y = y2;
|
|
|
|
if (scroll_part != RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SCROLL_AREA, uint16)){
|
|
invalidate_scroll();
|
|
return;
|
|
}
|
|
|
|
switch (scroll_part){
|
|
case SCROLL_PART_VIEW:
|
|
window_event_scroll_mousedrag_call(w, scroll_id, x, y);
|
|
break;
|
|
case SCROLL_PART_HSCROLLBAR_LEFT:
|
|
input_scroll_part_update_hleft(w, widgetIndex, scroll_id);
|
|
break;
|
|
case SCROLL_PART_HSCROLLBAR_RIGHT:
|
|
input_scroll_part_update_hright(w, widgetIndex, scroll_id);
|
|
break;
|
|
case SCROLL_PART_HSCROLLBAR_LEFT_TROUGH:
|
|
case SCROLL_PART_HSCROLLBAR_RIGHT_TROUGH:
|
|
return;
|
|
break;
|
|
case SCROLL_PART_HSCROLLBAR_THUMB:
|
|
case SCROLL_PART_VSCROLLBAR_TOP:
|
|
input_scroll_part_update_vtop(w, widgetIndex, scroll_id);
|
|
break;
|
|
case SCROLL_PART_VSCROLLBAR_BOTTOM:
|
|
input_scroll_part_update_vbottom(w, widgetIndex, scroll_id);
|
|
break;
|
|
case SCROLL_PART_VSCROLLBAR_TOP_TROUGH:
|
|
case SCROLL_PART_VSCROLLBAR_BOTTOM_TROUGH:
|
|
return;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void input_scroll_end()
|
|
{
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_RESET;
|
|
invalidate_scroll();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct: 0x006E98F2
|
|
*/
|
|
static void input_scroll_part_update_hthumb(rct_window *w, int widgetIndex, int x, int scroll_id)
|
|
{
|
|
rct_widget *widget = &w->widgets[widgetIndex];
|
|
int newLeft;
|
|
|
|
if (window_find_by_number(w->classification, w->number)) {
|
|
newLeft = w->scrolls[scroll_id].h_right;
|
|
newLeft *= x;
|
|
x = widget->right - widget->left - 21;
|
|
if (w->scrolls[scroll_id].flags & VSCROLLBAR_VISIBLE)
|
|
x -= 11;
|
|
newLeft /= x;
|
|
x = newLeft;
|
|
w->scrolls[scroll_id].flags |= HSCROLLBAR_THUMB_PRESSED;
|
|
newLeft = w->scrolls[scroll_id].h_left;
|
|
newLeft += x;
|
|
if (newLeft < 0)
|
|
newLeft = 0;
|
|
x = widget->right - widget->left - 1;
|
|
if (w->scrolls[scroll_id].flags & VSCROLLBAR_VISIBLE)
|
|
x -= 11;
|
|
x *= -1;
|
|
x += w->scrolls[scroll_id].h_right;
|
|
if (x < 0)
|
|
x = 0;
|
|
if (newLeft > x)
|
|
newLeft = x;
|
|
w->scrolls[scroll_id].h_left = newLeft;
|
|
widget_scroll_update_thumbs(w, widgetIndex);
|
|
widget_invalidate_by_number(w->classification, w->number, widgetIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct: 0x006E99A9
|
|
*/
|
|
static void input_scroll_part_update_vthumb(rct_window *w, int widgetIndex, int y, int scroll_id)
|
|
{
|
|
assert(w != NULL);
|
|
rct_widget *widget = &w->widgets[widgetIndex];
|
|
int newTop;
|
|
|
|
if (window_find_by_number(w->classification, w->number)) {
|
|
newTop = w->scrolls[scroll_id].v_bottom;
|
|
newTop *= y;
|
|
y = widget->bottom - widget->top - 21;
|
|
if (w->scrolls[scroll_id].flags & HSCROLLBAR_VISIBLE)
|
|
y -= 11;
|
|
newTop /= y;
|
|
y = newTop;
|
|
w->scrolls[scroll_id].flags |= VSCROLLBAR_THUMB_PRESSED;
|
|
newTop = w->scrolls[scroll_id].v_top;
|
|
newTop += y;
|
|
if (newTop < 0)
|
|
newTop = 0;
|
|
y = widget->bottom - widget->top - 1;
|
|
if (w->scrolls[scroll_id].flags & HSCROLLBAR_VISIBLE)
|
|
y -= 11;
|
|
y *= -1;
|
|
y += w->scrolls[scroll_id].v_bottom;
|
|
if (y < 0)
|
|
y = 0;
|
|
if (newTop > y)
|
|
newTop = y;
|
|
w->scrolls[scroll_id].v_top = newTop;
|
|
widget_scroll_update_thumbs(w, widgetIndex);
|
|
widget_invalidate_by_number(w->classification, w->number, widgetIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct: 0x006E9A60
|
|
*/
|
|
static void input_scroll_part_update_hleft(rct_window *w, int widgetIndex, int scroll_id)
|
|
{
|
|
assert(w != NULL);
|
|
if (window_find_by_number(w->classification, w->number)) {
|
|
w->scrolls[scroll_id].flags |= HSCROLLBAR_LEFT_PRESSED;
|
|
if (w->scrolls[scroll_id].h_left < 0)
|
|
w->scrolls[scroll_id].h_left = 0;
|
|
else if (w->scrolls[scroll_id].h_left >= 3)
|
|
w->scrolls[scroll_id].h_left -= 3;
|
|
widget_scroll_update_thumbs(w, widgetIndex);
|
|
widget_invalidate_by_number(w->classification, w->number, widgetIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct: 0x006E9ABF
|
|
*/
|
|
static void input_scroll_part_update_hright(rct_window *w, int widgetIndex, int scroll_id)
|
|
{
|
|
assert(w != NULL);
|
|
rct_widget *widget = &w->widgets[widgetIndex];
|
|
if (window_find_by_number(w->classification, w->number)) {
|
|
w->scrolls[scroll_id].flags |= HSCROLLBAR_RIGHT_PRESSED;
|
|
w->scrolls[scroll_id].h_left += 3;
|
|
int newLeft = widget->right - widget->left - 1;
|
|
if (w->scrolls[scroll_id].flags & VSCROLLBAR_VISIBLE)
|
|
newLeft -= 11;
|
|
newLeft *= -1;
|
|
newLeft += w->scrolls[scroll_id].h_right;
|
|
if (newLeft < 0)
|
|
newLeft = 0;
|
|
if (w->scrolls[scroll_id].h_left > newLeft)
|
|
w->scrolls[scroll_id].h_left = newLeft;
|
|
widget_scroll_update_thumbs(w, widgetIndex);
|
|
widget_invalidate_by_number(w->classification, w->number, widgetIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct: 0x006E9C37
|
|
*/
|
|
static void input_scroll_part_update_vtop(rct_window *w, int widgetIndex, int scroll_id)
|
|
{
|
|
assert(w != NULL);
|
|
if (window_find_by_number(w->classification, w->number)) {
|
|
w->scrolls[scroll_id].flags |= VSCROLLBAR_UP_PRESSED;
|
|
if (w->scrolls[scroll_id].v_top < 0)
|
|
w->scrolls[scroll_id].v_top = 0;
|
|
else if (w->scrolls[scroll_id].v_top >= 3)
|
|
w->scrolls[scroll_id].v_top -= 3;
|
|
widget_scroll_update_thumbs(w, widgetIndex);
|
|
widget_invalidate_by_number(w->classification, w->number, widgetIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct: 0x006E9C96
|
|
*/
|
|
static void input_scroll_part_update_vbottom(rct_window *w, int widgetIndex, int scroll_id)
|
|
{
|
|
assert(w != NULL);
|
|
rct_widget *widget = &w->widgets[widgetIndex];
|
|
if (window_find_by_number(w->classification, w->number)) {
|
|
w->scrolls[scroll_id].flags |= VSCROLLBAR_DOWN_PRESSED;
|
|
w->scrolls[scroll_id].v_top += 3;
|
|
int newTop = widget->bottom - widget->top - 1;
|
|
if (w->scrolls[scroll_id].flags & HSCROLLBAR_VISIBLE)
|
|
newTop -= 11;
|
|
newTop *= -1;
|
|
newTop += w->scrolls[scroll_id].v_bottom;
|
|
if (newTop < 0)
|
|
newTop = 0;
|
|
if (w->scrolls[scroll_id].v_top > newTop)
|
|
w->scrolls[scroll_id].v_top = newTop;
|
|
widget_scroll_update_thumbs(w, widgetIndex);
|
|
widget_invalidate_by_number(w->classification, w->number, widgetIndex);
|
|
}
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Widgets
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E9253
|
|
*/
|
|
static void input_widget_over(int x, int y, rct_window *w, int widgetIndex)
|
|
{
|
|
rct_windowclass windowClass = 255;
|
|
rct_windownumber windowNumber = 0;
|
|
rct_widget *widget = NULL;
|
|
|
|
if (w != NULL) {
|
|
windowClass = w->classification;
|
|
windowNumber = w->number;
|
|
widget = &w->widgets[widgetIndex];
|
|
}
|
|
|
|
input_widget_over_change_check(windowClass, windowNumber, widgetIndex);
|
|
|
|
if (w != NULL && widgetIndex != -1 && widget->type == WWT_SCROLL) {
|
|
int eax, ebx, ecx, edx;
|
|
widget_scroll_get_part(w, widget, x, y, &eax, &ebx, &ecx, &edx);
|
|
|
|
if (ecx < 0)
|
|
input_update_tooltip(w, widgetIndex, x, y);
|
|
else if (ecx == 0) {
|
|
window_event_scroll_mouseover_call(w, edx, eax, ebx);
|
|
input_update_tooltip(w, widgetIndex, x, y);
|
|
}
|
|
}
|
|
else {
|
|
input_update_tooltip(w, widgetIndex, x, y);
|
|
}
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint16) = 0;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_X, sint16) = x;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_Y, sint16) = y;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E9269
|
|
*/
|
|
static void input_widget_over_change_check(rct_windowclass windowClass, rct_windownumber windowNumber, int widgetIndex)
|
|
{
|
|
// Prevents invalid widgets being clicked source of bug is elsewhere
|
|
if (widgetIndex == -1)
|
|
return;
|
|
|
|
// Check if the widget that the cursor was over, has changed
|
|
if (
|
|
windowClass != RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWCLASS, rct_windowclass) ||
|
|
windowNumber != RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWNUMBER, rct_windownumber) ||
|
|
widgetIndex != RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WIDGETINDEX, uint16)
|
|
) {
|
|
// Invalidate last widget cursor was on if widget is a flat button
|
|
input_widget_over_flatbutton_invalidate();
|
|
|
|
// Set new cursor over widget
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWCLASS, rct_windowclass) = windowClass;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWNUMBER, rct_windownumber) = windowNumber;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WIDGETINDEX, uint16) = widgetIndex;
|
|
|
|
// Invalidate new widget cursor is on if widget is a flat button
|
|
if (windowClass != 255)
|
|
input_widget_over_flatbutton_invalidate();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used to invalidate flat button widgets when the mouse leaves and enters them. This should be generalised so that all widgets
|
|
* can use this in the future.
|
|
*/
|
|
static void input_widget_over_flatbutton_invalidate()
|
|
{
|
|
rct_window *w = window_find_by_number(
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWCLASS, rct_windowclass),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWNUMBER, rct_windownumber)
|
|
);
|
|
if (w == NULL)
|
|
return;
|
|
|
|
window_event_invalidate_call(w);
|
|
if (w->widgets[RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WIDGETINDEX, rct_windownumber)].type == WWT_FLATBTN) {
|
|
widget_invalidate_by_number(
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWCLASS, rct_windowclass),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWNUMBER, rct_windownumber),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WIDGETINDEX, rct_windownumber)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E95F9
|
|
*/
|
|
static void input_widget_left(int x, int y, rct_window *w, int widgetIndex)
|
|
{
|
|
rct_windowclass windowClass = 255;
|
|
rct_windownumber windowNumber = 0;
|
|
rct_widget *widget;
|
|
|
|
if (w != NULL) {
|
|
windowClass = w->classification;
|
|
windowNumber = w->number;
|
|
}
|
|
|
|
window_close_by_class(WC_ERROR);
|
|
window_close_by_class(WC_TOOLTIP);
|
|
|
|
// Window might have changed position in the list, therefore find it again
|
|
w = window_find_by_number(windowClass, windowNumber);
|
|
if (w == NULL)
|
|
return;
|
|
|
|
w = window_bring_to_front(w);
|
|
if (widgetIndex == -1)
|
|
return;
|
|
|
|
if (windowClass != gCurrentTextBox.window.classification ||
|
|
windowNumber != gCurrentTextBox.window.number ||
|
|
widgetIndex != gCurrentTextBox.widget_index) {
|
|
window_cancel_textbox();
|
|
}
|
|
|
|
widget = &w->widgets[widgetIndex];
|
|
|
|
switch (widget->type) {
|
|
case WWT_FRAME:
|
|
case WWT_RESIZE:
|
|
if (window_can_resize(w) && (x >= w->x + w->width - 19 && y >= w->y + w->height - 19))
|
|
input_window_resize_begin(w, widgetIndex, x, y);
|
|
break;
|
|
case WWT_VIEWPORT:
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_VIEWPORT_LEFT;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_LAST_X, uint16) = x;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_LAST_Y, uint16) = y;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_WINDOWCLASS, rct_windowclass) = windowClass;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_WINDOWNUMBER, rct_windownumber) = windowNumber;
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE) {
|
|
w = window_find_by_number(
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber)
|
|
);
|
|
if (w != NULL) {
|
|
window_event_tool_down_call(w, RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WIDGETINDEX, uint32), x, y);
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_4;
|
|
}
|
|
}
|
|
break;
|
|
case WWT_CAPTION:
|
|
input_window_position_begin(w, widgetIndex, x, y);
|
|
break;
|
|
case WWT_SCROLL:
|
|
input_scroll_begin(w, widgetIndex, x, y);
|
|
break;
|
|
default:
|
|
if (widget_is_enabled(w, widgetIndex) && !widget_is_disabled(w, widgetIndex)) {
|
|
audio_play_sound_panned(SOUND_CLICK_1, w->x + (widget->left + widget->right) / 2, 0, 0, 0);
|
|
|
|
// Set new cursor down widget
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWCLASS, rct_windowclass) = windowClass;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWNUMBER, rct_windownumber) = windowNumber;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WIDGETINDEX, uint16) = widgetIndex;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_WIDGET_PRESSED;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = INPUT_STATE_WIDGET_PRESSED;
|
|
RCT2_GLOBAL(0x009DE528, uint16) = 1;
|
|
|
|
widget_invalidate_by_number(windowClass, windowNumber, widgetIndex);
|
|
window_event_mouse_down_call(w, widgetIndex);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006ED833
|
|
*/
|
|
void process_mouse_over(int x, int y)
|
|
{
|
|
rct_window* window;
|
|
rct_window* subWindow;
|
|
|
|
int widgetId;
|
|
int cursorId;
|
|
int ebx, esi, edi, ebp;
|
|
|
|
cursorId = CURSOR_ARROW;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS, sint16) = -1;
|
|
window = window_find_from_point(x, y);
|
|
|
|
if (window != NULL) {
|
|
widgetId = window_find_widget_from_point(window, x, y);
|
|
RCT2_GLOBAL(0x1420046, sint16) = (widgetId & 0xFFFF);
|
|
if (widgetId != -1) {
|
|
switch (window->widgets[widgetId].type){
|
|
|
|
case WWT_VIEWPORT:
|
|
if ((RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, int) & INPUT_FLAG_TOOL_ACTIVE) == 0) {
|
|
if (viewport_interaction_left_over(x, y)) {
|
|
sub_6ED990(CURSOR_HAND_POINT);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
cursorId = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TOOL, uint8);
|
|
subWindow = window_find_by_number(
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber)
|
|
);
|
|
ebp = (int)subWindow;
|
|
if (subWindow == NULL)
|
|
break;
|
|
|
|
ebx = ebx & 0xFFFFFF00;
|
|
edi = cursorId;
|
|
esi = (int)subWindow;
|
|
// Not sure what this is for, no windows actually implement a handler
|
|
// RCT2_CALLFUNC_X(subWindow->event_handlers[WE_UNKNOWN_0E], &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
|
|
cursorId = edi;
|
|
if ((ebx & 0xFF) != 0)
|
|
{
|
|
sub_6ED990(cursorId);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case WWT_FRAME:
|
|
case WWT_RESIZE:
|
|
if (!(window->flags & 0x100))
|
|
break;
|
|
|
|
if (window->min_width == window->max_width && window->min_height == window->max_height)
|
|
break;
|
|
|
|
if (x < window->x + window->width - 0x13)
|
|
break;
|
|
|
|
if (y < window->y + window->height - 0x13)
|
|
break;
|
|
|
|
cursorId = CURSOR_DIAGONAL_ARROWS;
|
|
break;
|
|
|
|
case WWT_SCROLL:
|
|
RCT2_GLOBAL(0x9DE558, uint16) = x;
|
|
RCT2_GLOBAL(0x9DE55A, uint16) = y;
|
|
int output_scroll_area, scroll_id;
|
|
int scroll_x, scroll_y;
|
|
widget_scroll_get_part(window, &window->widgets[widgetId], x, y, &scroll_x, &scroll_y, &output_scroll_area, &scroll_id);
|
|
cursorId = scroll_id;
|
|
if (output_scroll_area != SCROLL_PART_VIEW)
|
|
{
|
|
cursorId = CURSOR_ARROW;
|
|
break;
|
|
}
|
|
// Same as default but with scroll_x/y
|
|
cursorId = window_event_cursor_call(window, widgetId, scroll_x, scroll_y);
|
|
if (cursorId == -1)
|
|
cursorId = CURSOR_ARROW;
|
|
break;
|
|
default:
|
|
cursorId = window_event_cursor_call(window, widgetId, x, y);
|
|
if (cursorId == -1)
|
|
cursorId = CURSOR_ARROW;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
viewport_interaction_right_over(x, y);
|
|
sub_6ED990(cursorId);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006ED801
|
|
*/
|
|
void process_mouse_tool(int x, int y)
|
|
{
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE)
|
|
{
|
|
rct_window* w = window_find_by_number(
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, uint8),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, uint16)
|
|
);
|
|
|
|
if (!w)
|
|
tool_cancel();
|
|
else
|
|
window_event_tool_update_call(w, RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WIDGETINDEX, uint32), x, y);
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E8DA7
|
|
*/
|
|
void input_state_widget_pressed(int x, int y, int state, int widgetIndex, rct_window *w, rct_widget *widget)
|
|
{
|
|
RCT2_GLOBAL(0x1420054, uint16) = x;
|
|
RCT2_GLOBAL(0x1420056, uint16) = y;
|
|
|
|
rct_windowclass cursor_w_class;
|
|
rct_windownumber cursor_w_number;
|
|
cursor_w_class = RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWCLASS, rct_windowclass);
|
|
cursor_w_number = RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWNUMBER, rct_windownumber);
|
|
int cursor_widgetIndex = RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WIDGETINDEX, uint32);
|
|
|
|
rct_window *cursor_w = window_find_by_number(cursor_w_class, cursor_w_number);
|
|
if (cursor_w == NULL) {
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 0;
|
|
return;
|
|
}
|
|
|
|
switch (state){
|
|
case 0:
|
|
if (!w || cursor_w_class != w->classification || cursor_w_number != w->number || widgetIndex != cursor_widgetIndex)
|
|
break;
|
|
|
|
if (w->disabled_widgets & (1ULL << widgetIndex))
|
|
break;
|
|
|
|
if (RCT2_GLOBAL(0x009DE528, uint16) != 0)
|
|
RCT2_GLOBAL(0x009DE528, uint16)++;
|
|
|
|
if (w->hold_down_widgets & (1ULL << widgetIndex) &&
|
|
RCT2_GLOBAL(0x009DE528, uint16) >= 16 &&
|
|
!(RCT2_GLOBAL(0x009DE528, uint16) & 3)
|
|
) {
|
|
window_event_mouse_down_call(w, widgetIndex);
|
|
}
|
|
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_WIDGET_PRESSED) return;
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_WIDGET_PRESSED;
|
|
widget_invalidate_by_number(cursor_w_class, cursor_w_number, widgetIndex);
|
|
return;
|
|
case 3:
|
|
case 2:
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) == 5) {
|
|
if (w) {
|
|
int dropdown_index = 0;
|
|
|
|
if (w->classification == WC_DROPDOWN) {
|
|
dropdown_index = dropdown_index_from_point(x, y, w);
|
|
if (dropdown_index == -1) {
|
|
goto dropdown_cleanup;
|
|
}
|
|
|
|
if (dropdown_index < 64 && gDropdownItemsDisabled & (1ULL << dropdown_index)) {
|
|
goto dropdown_cleanup;
|
|
}
|
|
|
|
if (gDropdownItemsFormat[dropdown_index] == 0) {
|
|
goto dropdown_cleanup;
|
|
}
|
|
}
|
|
else {
|
|
if (cursor_w_class != w->classification || cursor_w_number != w->number || widgetIndex != cursor_widgetIndex)
|
|
goto dropdown_cleanup;
|
|
dropdown_index = -1;
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_DROPDOWN_STAY_OPEN){
|
|
if (!(RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_DROPDOWN_MOUSE_UP)){
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_DROPDOWN_MOUSE_UP;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
window_close_by_class(WC_DROPDOWN);
|
|
cursor_w = window_find_by_number(cursor_w_class, cursor_w_number);
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_WIDGET_PRESSED) {
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= 0xFFFE;
|
|
widget_invalidate_by_number(cursor_w_class, cursor_w_number, cursor_widgetIndex);
|
|
}
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 1;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint16) = 0;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WIDGET_INDEX, uint16) = cursor_widgetIndex;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_CLASS, rct_windowclass) = cursor_w_class;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_NUMBER, rct_windownumber) = cursor_w_number;
|
|
window_event_dropdown_call(cursor_w, cursor_widgetIndex, dropdown_index);
|
|
}
|
|
dropdown_cleanup:
|
|
window_close_by_class(WC_DROPDOWN);
|
|
}
|
|
if (state == 3) return;
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 1;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint16) = 0;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WIDGET_INDEX, uint16) = cursor_widgetIndex;
|
|
|
|
if (!w)
|
|
break;
|
|
|
|
if (!widget)
|
|
break;
|
|
|
|
int mid_point_x = (widget->left + widget->right) / 2 + w->x;
|
|
audio_play_sound_panned(5, mid_point_x, 0, 0, 0);
|
|
if (cursor_w_class != w->classification || cursor_w_number != w->number || widgetIndex != cursor_widgetIndex)
|
|
break;
|
|
|
|
if (w->disabled_widgets & (1ULL << widgetIndex))
|
|
break;
|
|
|
|
widget_invalidate_by_number(cursor_w_class, cursor_w_number, widgetIndex);
|
|
window_event_mouse_up_call(w, widgetIndex);
|
|
default:
|
|
return;
|
|
}
|
|
|
|
RCT2_GLOBAL(0x9DE528, uint16) = 0;
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) != 5){
|
|
// Hold down widget and drag outside of area??
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_WIDGET_PRESSED){
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= 0xFFFE;
|
|
widget_invalidate_by_number(cursor_w_class, cursor_w_number, cursor_widgetIndex);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!w) return;
|
|
|
|
if (w->classification == WC_DROPDOWN){
|
|
int dropdown_index = dropdown_index_from_point(x, y, w);
|
|
|
|
if (dropdown_index == -1) return;
|
|
|
|
if (gDropdownIsColour && gDropdownLastColourHover != dropdown_index) {
|
|
gDropdownLastColourHover = dropdown_index;
|
|
window_tooltip_close();
|
|
window_tooltip_show(STR_COLOUR_NAMES_START + dropdown_index, x, y);
|
|
}
|
|
|
|
if (dropdown_index < 64 && gDropdownItemsDisabled & (1ULL << dropdown_index)) {
|
|
return;
|
|
}
|
|
|
|
if (gDropdownItemsFormat[dropdown_index] == 0) {
|
|
return;
|
|
}
|
|
|
|
gDropdownHighlightedIndex = dropdown_index;
|
|
window_invalidate_by_class(WC_DROPDOWN);
|
|
} else {
|
|
gDropdownLastColourHover = -1;
|
|
window_tooltip_close();
|
|
}
|
|
}
|
|
|
|
static void input_update_tooltip(rct_window *w, int widgetIndex, int x, int y)
|
|
{
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_CLASS, rct_windowclass) == 255) {
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_NOT_SHOWN_TICKS, uint16) < 500 ||
|
|
(RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_X, sint16) == x &&
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_Y, sint16) == y)
|
|
) {
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint16) = RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_LAST_UPDATE, uint16);
|
|
|
|
int bp = 2000;
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_NOT_SHOWN_TICKS, uint16) >= 1)
|
|
bp = 0;
|
|
if (bp > RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint16)) {
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_NOT_SHOWN_TICKS, sint16)++;
|
|
return;
|
|
}
|
|
|
|
window_tooltip_open(w, widgetIndex, x, y);
|
|
}
|
|
}
|
|
else {
|
|
if (((w != NULL) &&
|
|
(RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_CLASS, rct_windowclass) != w->classification ||
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_NUMBER, rct_windownumber) != w->number)) ||
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WIDGET_INDEX, uint16) != widgetIndex
|
|
) {
|
|
window_tooltip_close();
|
|
}
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint16) += RCT2_GLOBAL(RCT2_ADDRESS_TICKS_SINCE_LAST_UPDATE, uint16);
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint16) < 8000)
|
|
return;
|
|
window_close_by_class(WC_TOOLTIP);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Keyboard input
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E3B43
|
|
*/
|
|
void title_handle_keyboard_input()
|
|
{
|
|
rct_window *w;
|
|
int key;
|
|
|
|
if (gOpenRCT2Headless) {
|
|
return;
|
|
}
|
|
|
|
if (!gConsoleOpen) {
|
|
// Handle modifier keys and key scrolling
|
|
RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) = 0;
|
|
if (RCT2_GLOBAL(0x009E2B64, uint32) != 1) {
|
|
if (gKeysState[SDL_SCANCODE_LSHIFT] || gKeysState[SDL_SCANCODE_RSHIFT])
|
|
RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) |= 1;
|
|
if (gKeysState[SDL_SCANCODE_LCTRL] || gKeysState[SDL_SCANCODE_RCTRL])
|
|
RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) |= 2;
|
|
if (gKeysState[SDL_SCANCODE_LALT] || gKeysState[SDL_SCANCODE_RALT])
|
|
RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) |= 4;
|
|
}
|
|
}
|
|
|
|
while ((key = get_next_key()) != 0) {
|
|
if (key == 255)
|
|
continue;
|
|
|
|
// Reserve backtick for console
|
|
if (key == SDL_SCANCODE_GRAVE) {
|
|
if (gConfigGeneral.debugging_tools || gConsoleOpen) {
|
|
window_cancel_textbox();
|
|
console_toggle();
|
|
}
|
|
continue;
|
|
} else if (gConsoleOpen) {
|
|
console_input(key);
|
|
continue;
|
|
}
|
|
|
|
key |= RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) << 8;
|
|
|
|
w = window_find_by_class(WC_CHANGE_KEYBOARD_SHORTCUT);
|
|
if (w != NULL) {
|
|
keyboard_shortcut_set(key);
|
|
}
|
|
else if (key == gShortcutKeys[SHORTCUT_SCREENSHOT]) {
|
|
keyboard_shortcut_handle_command(SHORTCUT_SCREENSHOT);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E3B43
|
|
*/
|
|
void game_handle_keyboard_input()
|
|
{
|
|
rct_window *w;
|
|
int key;
|
|
|
|
if (!gConsoleOpen) {
|
|
// Handle mouse scrolling
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_ON_TUTORIAL, uint8) == 0)
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_EDGE_SCROLLING, uint8) != 0)
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) == 1)
|
|
if (!(RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) & 3))
|
|
game_handle_edge_scroll();
|
|
|
|
// Handle modifier keys and key scrolling
|
|
RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) = 0;
|
|
if (RCT2_GLOBAL(0x009E2B64, uint32) != 1) {
|
|
if (gKeysState[SDL_SCANCODE_LSHIFT] || gKeysState[SDL_SCANCODE_RSHIFT])
|
|
RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) |= 1;
|
|
if (gKeysState[SDL_SCANCODE_LCTRL] || gKeysState[SDL_SCANCODE_RCTRL])
|
|
RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) |= 2;
|
|
if (gKeysState[SDL_SCANCODE_LALT] || gKeysState[SDL_SCANCODE_RALT])
|
|
RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) |= 4;
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_ON_TUTORIAL, uint8) == 0)
|
|
game_handle_key_scroll();
|
|
}
|
|
}
|
|
|
|
|
|
// Handle key input
|
|
while (!gOpenRCT2Headless && (key = get_next_key()) != 0) {
|
|
if (key == 255)
|
|
continue;
|
|
|
|
// Reserve backtick for console
|
|
if (key == SDL_SCANCODE_GRAVE) {
|
|
if (gConfigGeneral.debugging_tools || gConsoleOpen) {
|
|
window_cancel_textbox();
|
|
console_toggle();
|
|
}
|
|
continue;
|
|
} else if (gConsoleOpen) {
|
|
console_input(key);
|
|
continue;
|
|
} else if (gChatOpen) {
|
|
chat_input(key);
|
|
continue;
|
|
}
|
|
|
|
key |= RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) << 8;
|
|
|
|
w = window_find_by_class(WC_CHANGE_KEYBOARD_SHORTCUT);
|
|
if (w != NULL) {
|
|
keyboard_shortcut_set(key);
|
|
}
|
|
else if (RCT2_GLOBAL(RCT2_ADDRESS_ON_TUTORIAL, uint8) == 1) {
|
|
tutorial_stop();
|
|
}
|
|
else {
|
|
w = window_find_by_class(WC_TEXTINPUT);
|
|
if (w != NULL) {
|
|
window_text_input_key(w, key);
|
|
} else if (!gUsingWidgetTextBox) {
|
|
keyboard_shortcut_handle(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_ON_TUTORIAL, uint8) == 0)
|
|
return;
|
|
|
|
// Tutorial and the modifier key
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_ON_TUTORIAL, uint8) == 1) {
|
|
int eax, ebx, ecx, edx, esi, edi, ebp;
|
|
RCT2_CALLFUNC_X(0x0066EEB4, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
|
|
eax &= 0xFF;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) = eax;
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) & 4) {
|
|
window_tooltip_close();
|
|
if ((w = window_get_main()) != NULL) {
|
|
RCT2_CALLPROC_X(0x006EA2AA, 0, 0, 0, 0, (int)w, RCT2_GLOBAL(0x009DEA72, uint16), 0);
|
|
RCT2_GLOBAL(0x009DEA72, uint16)++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (!(RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) & 4)) {
|
|
window_tooltip_close();
|
|
if ((w = window_get_main()) != NULL) {
|
|
sub_6EA2AA(w, 0, 0, 0, RCT2_GLOBAL(0x009DEA72, uint16));
|
|
RCT2_GLOBAL(0x009DEA72, uint16)++;
|
|
}
|
|
}
|
|
|
|
// Write tutorial input
|
|
RCT2_CALLPROC_X(0x0066EEE1, RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8), 0, 0, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
static void sub_6EA2AA(rct_window *w, int widgetIndex, int x, int y, int edi)
|
|
{
|
|
RCT2_CALLPROC_X(0x006EA2AA, 0, 0, 0, 0, (int)w, RCT2_GLOBAL(0x009DEA72, uint16), 0);
|
|
return;
|
|
|
|
rct_window *tooltipWindow;
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_CLASS, rct_windowclass) = w->classification;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_NUMBER, rct_windownumber) = w->number;
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WIDGET_INDEX, uint16) = widgetIndex;
|
|
|
|
rct_string_id stringId = window_event_tooltip_call(w, widgetIndex);
|
|
if (stringId == (rct_string_id)STR_NONE)
|
|
return;
|
|
|
|
tooltipWindow = window_find_by_class(WC_TOOLTIP);
|
|
if (tooltipWindow == NULL)
|
|
return;
|
|
|
|
char *buffer = (char*)RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER;
|
|
|
|
RCT2_GLOBAL(0x0142006C, uint32) = edi;
|
|
format_string(buffer, edi, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS);
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 224;
|
|
int width = gfx_get_string_width_new_lined(buffer);
|
|
width = min(width, 196);
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 224;
|
|
|
|
int numLines, fontHeight;
|
|
gfx_wrap_string(buffer, width + 1, &numLines, &fontHeight);
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TEXT_HEIGHT, uint16) = numLines;
|
|
tooltipWindow->widgets[0].right = width + 3;
|
|
tooltipWindow->widgets[0].bottom = ((numLines + 1) * 10) + 4;
|
|
|
|
char *tooltipBuffer = (char*)RCT2_ADDRESS_TOOLTIP_TEXT_BUFFER;
|
|
memcpy(tooltipBuffer, buffer, 512);
|
|
|
|
window_tooltip_open(w, widgetIndex, x, y);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00406CD2
|
|
*/
|
|
int get_next_key()
|
|
{
|
|
int i;
|
|
for (i = 0; i < 221; i++) {
|
|
if (gKeysPressed[i]) {
|
|
gKeysPressed[i] = 0;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006ED990
|
|
*/
|
|
void sub_6ED990(char cursor_id){
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) == INPUT_STATE_RESIZING)
|
|
{
|
|
cursor_id = CURSOR_DIAGONAL_ARROWS; //resize icon
|
|
}
|
|
|
|
if (cursor_id == RCT2_GLOBAL(RCT2_ADDRESS_CURENT_CURSOR, uint8))
|
|
{
|
|
return;
|
|
}
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURENT_CURSOR, uint8) = cursor_id;
|
|
RCT2_GLOBAL(0x14241BC, uint32) = 2;
|
|
platform_set_cursor(cursor_id);
|
|
RCT2_GLOBAL(0x14241BC, uint32) = 0;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E876D
|
|
*/
|
|
void invalidate_scroll()
|
|
{
|
|
rct_window* wind = window_find_by_number(
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWCLASS, uint8),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWNUMBER, uint16)
|
|
);
|
|
if (wind == NULL) return;
|
|
|
|
int scroll_id = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SCROLL_ID, uint32);
|
|
//Reset to basic scroll
|
|
wind->scrolls[scroll_id / sizeof(rct_scroll)].flags &= 0xFF11;
|
|
|
|
window_invalidate_by_number(
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWCLASS, uint8),
|
|
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWNUMBER, uint16)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* rct2: 0x00406C96
|
|
*/
|
|
void store_mouse_input(int state)
|
|
{
|
|
uint32 write_index = RCT2_GLOBAL(RCT2_ADDRESS_MOUSE_WRITE_INDEX, uint32);
|
|
uint32 next_write_index = (write_index + 1) % 64;
|
|
|
|
// check if the queue is full
|
|
if (next_write_index == RCT2_GLOBAL(RCT2_ADDRESS_MOUSE_READ_INDEX, uint32))
|
|
return;
|
|
|
|
rct_mouse_data* item = &mouse_buffer[write_index];
|
|
item->x = RCT2_GLOBAL(0x01424318, uint32);
|
|
item->y = RCT2_GLOBAL(0x0142431C, uint32);
|
|
item->state = state;
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_MOUSE_WRITE_INDEX, uint32) = next_write_index;
|
|
}
|
|
|
|
void game_handle_edge_scroll()
|
|
{
|
|
rct_window *mainWindow;
|
|
int scrollX, scrollY;
|
|
|
|
mainWindow = window_get_main();
|
|
if (mainWindow == NULL)
|
|
return;
|
|
if ((mainWindow->flags & WF_NO_SCROLLING) || (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 9))
|
|
return;
|
|
if (mainWindow->viewport == NULL)
|
|
return;
|
|
|
|
uint32 window_flags = SDL_GetWindowFlags(gWindow);
|
|
if ((window_flags & SDL_WINDOW_INPUT_FOCUS) == 0)
|
|
return;
|
|
|
|
scrollX = 0;
|
|
scrollY = 0;
|
|
|
|
// Scroll left / right
|
|
if (gCursorState.x == 0)
|
|
scrollX = -1;
|
|
else if (gCursorState.x == RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) - 1)
|
|
scrollX = 1;
|
|
|
|
// Scroll up / down
|
|
if (gCursorState.y == 0)
|
|
scrollY = -1;
|
|
else if (gCursorState.y == RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) - 1)
|
|
scrollY = 1;
|
|
|
|
// Scroll viewport
|
|
if (scrollX != 0) {
|
|
mainWindow->saved_view_x += scrollX * (12 << mainWindow->viewport->zoom);
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_VIEWPORT_SCROLLING;
|
|
}
|
|
if (scrollY != 0) {
|
|
mainWindow->saved_view_y += scrollY * (12 << mainWindow->viewport->zoom);
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_VIEWPORT_SCROLLING;
|
|
}
|
|
}
|
|
|
|
void game_handle_key_scroll()
|
|
{
|
|
rct_window *mainWindow;
|
|
int scrollX, scrollY;
|
|
|
|
mainWindow = window_get_main();
|
|
if (mainWindow == NULL)
|
|
return;
|
|
if ((mainWindow->flags & WF_NO_SCROLLING) || (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 9))
|
|
return;
|
|
if (mainWindow->viewport == NULL)
|
|
return;
|
|
|
|
rct_window *textWindow;
|
|
|
|
textWindow = window_find_by_class(WC_TEXTINPUT);
|
|
if (textWindow || gUsingWidgetTextBox) return;
|
|
|
|
scrollX = 0;
|
|
scrollY = 0;
|
|
|
|
for (int shortcutId = SHORTCUT_SCROLL_MAP_UP; shortcutId <= SHORTCUT_SCROLL_MAP_RIGHT; shortcutId++) {
|
|
const int SHIFT = 0x100;
|
|
const int CTRL = 0x200;
|
|
const int ALT = 0x400;
|
|
|
|
uint16 shortcutKey = gShortcutKeys[shortcutId];
|
|
uint8 scancode = shortcutKey & 0xFF;
|
|
|
|
if (shortcutKey == 0xFFFF) continue;
|
|
if (!gKeysState[scancode]) continue;
|
|
|
|
if (shortcutKey & SHIFT) {
|
|
if (!gKeysState[SDL_SCANCODE_LSHIFT] && !gKeysState[SDL_SCANCODE_RSHIFT]) continue;
|
|
}
|
|
if (shortcutKey & CTRL) {
|
|
if (!gKeysState[SDL_SCANCODE_LCTRL] && !gKeysState[SDL_SCANCODE_RCTRL]) continue;
|
|
}
|
|
if (shortcutKey & ALT) {
|
|
if (!gKeysState[SDL_SCANCODE_LALT] && !gKeysState[SDL_SCANCODE_RALT]) continue;
|
|
}
|
|
|
|
switch (shortcutId) {
|
|
case SHORTCUT_SCROLL_MAP_UP:
|
|
scrollY = -1;
|
|
break;
|
|
case SHORTCUT_SCROLL_MAP_LEFT:
|
|
scrollX = -1;
|
|
break;
|
|
case SHORTCUT_SCROLL_MAP_DOWN:
|
|
scrollY = 1;
|
|
break;
|
|
case SHORTCUT_SCROLL_MAP_RIGHT:
|
|
scrollX = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Scroll viewport
|
|
if (scrollX != 0) {
|
|
mainWindow->saved_view_x += scrollX * (12 << mainWindow->viewport->zoom);
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_VIEWPORT_SCROLLING;
|
|
}
|
|
if (scrollY != 0) {
|
|
mainWindow->saved_view_y += scrollY * (12 << mainWindow->viewport->zoom);
|
|
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_VIEWPORT_SCROLLING;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* rct2: 0x006E8346
|
|
*/
|
|
static void update_cursor_position()
|
|
{
|
|
switch (RCT2_GLOBAL(RCT2_ADDRESS_ON_TUTORIAL, uint8)) {
|
|
case 0:
|
|
// RCT2_GLOBAL(0x0142004C, sint32) = RCT2_GLOBAL(0x0142406C, sint32);
|
|
// RCT2_GLOBAL(0x01420050, sint32) = RCT2_GLOBAL(0x01424070, sint32);
|
|
break;
|
|
case 1:
|
|
// read tutorial cursor position
|
|
break;
|
|
case 2:
|
|
// write tutorial cursor position
|
|
break;
|
|
}
|
|
}
|