From 8772cdd7aa5319c7a97fc917a9d20f9268227aa5 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Tue, 19 May 2015 03:53:37 +0100 Subject: [PATCH] add in game console --- projects/openrct2.vcxproj | 2 + projects/openrct2.vcxproj.filters | 7 +- projects/openrct2.vcxproj.user | 3 +- src/cmdline.c | 3 +- src/input.c | 47 ++-- src/interface/console.c | 373 ++++++++++++++++++++++++++++++ src/interface/console.h | 21 ++ src/localisation/localisation.c | 5 + src/localisation/localisation.h | 2 +- src/platform/platform.h | 1 + src/platform/shared.c | 4 +- src/rct2.c | 6 +- src/windows/top_toolbar.c | 4 +- 13 files changed, 451 insertions(+), 27 deletions(-) create mode 100644 src/interface/console.c create mode 100644 src/interface/console.h diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj index ca7c8e0c09..0e3320b342 100644 --- a/projects/openrct2.vcxproj +++ b/projects/openrct2.vcxproj @@ -35,6 +35,7 @@ + @@ -162,6 +163,7 @@ + diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters index 0d112d516f..c39a466b58 100644 --- a/projects/openrct2.vcxproj.filters +++ b/projects/openrct2.vcxproj.filters @@ -369,7 +369,6 @@ Source - Source\Windows @@ -439,6 +438,9 @@ Source\Windows + + Source\Interface + @@ -642,5 +644,8 @@ Source\World + + Source\Interface + \ No newline at end of file diff --git a/projects/openrct2.vcxproj.user b/projects/openrct2.vcxproj.user index 6377c9eb5e..dacd4e50f3 100644 --- a/projects/openrct2.vcxproj.user +++ b/projects/openrct2.vcxproj.user @@ -4,7 +4,6 @@ $(TargetDir) WindowsLocalDebugger $(TargetDir)\openrct2.exe - - + "C:\Program Files (x86)\Infogrames\RollerCoaster Tycoon 2\Scenarios\Lucky Lake.SC6" \ No newline at end of file diff --git a/src/cmdline.c b/src/cmdline.c index 62fc461592..6779415e3c 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -78,7 +78,8 @@ int cmdline_run(const char **argv, int argc) if (argc != 0) { gExitCode = cmdline_call_action(argv, argc); - return 0; + if (gExitCode != 0) + return 0; } print_launch_information(); diff --git a/src/input.c b/src/input.c index fce9a72caa..2fd69bc2b8 100644 --- a/src/input.c +++ b/src/input.c @@ -25,6 +25,7 @@ #include "cursors.h" #include "game.h" #include "input.h" +#include "interface/console.h" #include "interface/keyboard_shortcut.h" #include "interface/viewport.h" #include "interface/widget.h" @@ -1140,24 +1141,26 @@ void game_handle_keyboard_input() rct_window *w; int key; - // 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 (!gConsoleOpen) { + // Handle mouse scrolling if (RCT2_GLOBAL(RCT2_ADDRESS_ON_TUTORIAL, uint8) == 0) - game_handle_key_scroll(); + 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(); + } } @@ -1166,6 +1169,16 @@ void game_handle_keyboard_input() if (key == 255) continue; + // Reserve backtick for console + if (key == SDL_SCANCODE_GRAVE) { + if (gConfigGeneral.debugging_tools || gConsoleOpen) + 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); diff --git a/src/interface/console.c b/src/interface/console.c new file mode 100644 index 0000000000..cfbcae57a1 --- /dev/null +++ b/src/interface/console.c @@ -0,0 +1,373 @@ +#include +#include "../addresses.h" +#include "../drawing/drawing.h" +#include "../localisation/localisation.h" +#include "../platform/platform.h" +#include "console.h" +#include "window.h" + +#define CONSOLE_BUFFER_SIZE 1024 +#define CONSOLE_HISTORY_SIZE 64 +#define CONSOLE_INPUT_SIZE 256 + +bool gConsoleOpen = false; + +static bool _consoleInitialised = false; +static int _consoleLeft, _consoleTop, _consoleRight, _consoleBottom; +static int _lastMainViewportX, _lastMainViewportY; +static char _consoleBuffer[CONSOLE_BUFFER_SIZE] = { 0 }; +static char *_consoleBufferPointer = _consoleBuffer; +static char *_consoleViewBufferStart = _consoleBuffer; +static char _consoleCurrentLine[CONSOLE_INPUT_SIZE]; +static char *_consoleCurrentLinePointer = _consoleCurrentLine; +static int _consoleCaretTicks; + +static char _consoleHistory[CONSOLE_HISTORY_SIZE][CONSOLE_INPUT_SIZE]; +static int _consoleHistoryIndex = 0; +static int _consoleHistoryCount = 0; + +static void console_invalidate(); +static void console_write_prompt(); +static void console_update_scroll(); +static void console_clear_input(); +static void console_history_add(const char *src); +static void console_write_all_commands(); + +void console_open() +{ + gConsoleOpen = true; + console_update_scroll(); + platform_start_text_input(_consoleCurrentLine, sizeof(_consoleCurrentLine)); +} + +void console_close() +{ + gConsoleOpen = false; + console_invalidate(); + platform_stop_text_input(); +} + +void console_toggle() +{ + if (gConsoleOpen) + console_close(); + else + console_open(); +} + +void console_init() +{ + _consoleInitialised = true; + console_writeline(OPENRCT2_NAME " " OPENRCT2_VERSION); + console_writeline(""); + console_write_prompt(); +} + +void console_update() +{ + if (!_consoleInitialised) + console_init(); + + _consoleLeft = 0; + _consoleTop = 0; + _consoleRight = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16); + _consoleBottom = 300; + + if (gConsoleOpen) { + console_invalidate(); + + // When scrolling the map, the console pixels get copied... therefore invalidate the screen + rct_window *mainWindow = window_get_main(); + if (mainWindow != NULL) { + rct_viewport *mainViewport = mainWindow->viewport; + if (mainViewport != NULL) { + if (_lastMainViewportX != mainViewport->view_x || _lastMainViewportY != mainViewport->view_y) { + _lastMainViewportX = mainViewport->view_x; + _lastMainViewportY = mainViewport->view_y; + + gfx_invalidate_screen(); + } + } + } + + // Remove unwated characters in console input + unsigned char *ch = (unsigned char*)_consoleCurrentLine; + while (*ch != 0) { + if (*ch < 32 || *ch > 126) + *ch = ' '; + ch++; + } + } + + // Flash the caret + _consoleCaretTicks = (_consoleCaretTicks + 1) % 30; +} + +void console_draw(rct_drawpixelinfo *dpi) +{ + if (!gConsoleOpen) + return; + + // Set font + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 224; + + // Background + gfx_fill_rect(dpi, _consoleLeft, _consoleTop, _consoleRight, _consoleBottom, 0); + + int x = _consoleLeft + 4; + int y = _consoleTop + 4; + + // Draw previous lines + char lineBuffer[1 + 256]; + char *ch = _consoleViewBufferStart; + while (*ch != 0) { + // Find line break or null terminator + char *nextLine = ch; + while (*nextLine != 0 && *nextLine != '\n') { + nextLine++; + } + + int lineLength = min(sizeof(lineBuffer) - 1, nextLine - ch); + strncpy(lineBuffer + 1, ch, lineLength); + lineBuffer[0] = FORMAT_GREEN; + lineBuffer[1 + lineLength] = 0; + + gfx_draw_string(dpi, lineBuffer, 255, x, y); + + x = gLastDrawStringX; + + if (*nextLine == '\n') { + ch = nextLine + 1; + x = _consoleLeft + 4; + y += 10; + } else { + break; + } + } + + // Draw current line + gfx_draw_string(dpi, _consoleCurrentLine, 255, x, y); + + // Draw caret + if (_consoleCaretTicks >= 15) { + memcpy(lineBuffer, _consoleCurrentLine, gTextInputCursorPosition); + lineBuffer[gTextInputCursorPosition] = 0; + int caretX = x + gfx_get_string_width(lineBuffer); + int caretY = y + 10; + + gfx_fill_rect(dpi, caretX, caretY, caretX + 6, caretY + 1, FORMAT_GREEN); + } + +} + +void console_input(int c) +{ + switch (c) { + case SDL_SCANCODE_ESCAPE: + console_clear_input(); + break; + case SDL_SCANCODE_RETURN: + if (_consoleCurrentLine[0] != 0) { + console_history_add(_consoleCurrentLine); + console_execute(_consoleCurrentLine); + console_write_prompt(); + console_clear_input(); + } + break; + case SDL_SCANCODE_UP: + if (_consoleHistoryIndex > 0) { + _consoleHistoryIndex--; + memcpy(_consoleCurrentLine, _consoleHistory[_consoleHistoryIndex], 256); + } + gTextInputCursorPosition = strlen(_consoleCurrentLine); + gTextInputLength = gTextInputCursorPosition; + break; + case SDL_SCANCODE_DOWN: + if (_consoleHistoryIndex < _consoleHistoryCount - 1) { + _consoleHistoryIndex++; + memcpy(_consoleCurrentLine, _consoleHistory[_consoleHistoryIndex], 256); + gTextInputCursorPosition = strlen(_consoleCurrentLine); + gTextInputLength = gTextInputCursorPosition; + } else { + _consoleHistoryIndex = _consoleHistoryCount; + console_clear_input(); + } + break; + } +} + +static void console_invalidate() +{ + gfx_set_dirty_blocks(_consoleLeft, _consoleTop, _consoleRight, _consoleBottom); +} + +static void console_write_prompt() +{ + console_write("OpenRCT2 > "); +} + +void console_write(const char *src) +{ + int charactersRemainingInBuffer = CONSOLE_BUFFER_SIZE - (_consoleBufferPointer - _consoleBuffer) - 1; + int charactersToWrite = strlen(src); + int bufferShift = charactersToWrite - charactersRemainingInBuffer; + if (charactersToWrite > charactersRemainingInBuffer) { + memmove(_consoleBuffer, _consoleBuffer + bufferShift, CONSOLE_BUFFER_SIZE - bufferShift); + _consoleBufferPointer -= bufferShift; + } + strcpy(_consoleBufferPointer, src); + _consoleBufferPointer += charactersToWrite; + console_update_scroll(); +} + +void console_writeline(const char *src) +{ + console_write(src); + console_write("\n"); +} + +static void console_update_scroll() +{ + int lines = 0; + int maxLines = ((_consoleBottom - _consoleTop) / 10) - 1; + char *ch = strchr(_consoleBuffer, 0); + while (ch > _consoleBuffer && lines < maxLines) { + ch--; + if (*ch == '\n') + lines++; + } + + if (*ch == '\n') + ch++; + _consoleViewBufferStart = ch; +} + +void console_clear() +{ + _consoleBuffer[0] = 0; + _consoleBufferPointer = _consoleBuffer; +} + +static void console_clear_input() +{ + _consoleCurrentLine[0] = 0; + gTextInputCursorPosition = 0; + gTextInputLength = 0; +} + +static void console_history_add(const char *src) +{ + if (_consoleHistoryCount >= CONSOLE_HISTORY_SIZE) { + for (int i = 0; i < _consoleHistoryCount - 1; i++) + memcpy(_consoleHistory[i], _consoleHistory[i + 1], CONSOLE_INPUT_SIZE); + _consoleHistoryCount--; + } + memcpy(_consoleHistory[_consoleHistoryCount++], src, CONSOLE_INPUT_SIZE); + _consoleHistoryIndex = _consoleHistoryCount; +} + +static int cc_clear(const char **argv, int argc) +{ + console_clear(); + return 0; +} + +static int cc_echo(const char **argv, int argc) +{ + if (argc > 0) + console_writeline(argv[0]); + return 0; +} + +static int cc_help(const char **argv, int argc) +{ + console_write_all_commands(); + return 0; +} + +typedef int (*console_command_func)(const char **argv, int argc); +typedef struct { + char *command; + console_command_func func; +} console_command; + +console_command console_command_table[] = { + { "clear", cc_clear }, + { "echo", cc_echo }, + { "help", cc_help } +}; + +static void console_write_all_commands() +{ + for (int i = 0; i < countof(console_command_table); i++) + console_writeline(console_command_table[i].command); +} + +void console_execute(const char *src) +{ + console_writeline(_consoleCurrentLine); + + int argc = 0; + int argvCapacity = 8; + char **argv = malloc(argvCapacity * sizeof(char*)); + const char *start = src; + const char *end; + bool inQuotes = false; + do { + while (*start == ' ') + start++; + + if (*start == '"') { + inQuotes = true; + start++; + } else { + inQuotes = false; + } + + end = start; + while (*end != 0) { + if (*end == ' ' && !inQuotes) + break; + if (*end == '"' && inQuotes) + break; + end++; + } + int length = end - start; + + if (length > 0) { + char *arg = malloc(length + 1); + memcpy(arg, start, length); + arg[length] = 0; + + if (argc >= argvCapacity) { + argvCapacity *= 2; + argv = realloc(argv, argvCapacity * sizeof(char*)); + } + argv[argc] = arg; + argc++; + } + + start = end; + } while (*end != 0); + + if (argc == 0) + return; + + bool validCommand = false; + for (int i = 0; i < countof(console_command_table); i++) { + if (strcmp(argv[0], console_command_table[i].command) == 0) { + console_command_table[i].func(argv + 1, argc - 1); + validCommand = true; + break; + } + } + + for (int i = 0; i < argc; i++) + free(argv[i]); + free(argv); + + if (!validCommand) { + char output[] = { FORMAT_RED, "Unknown command. Type help to list available commands." }; + console_writeline(output); + } +} \ No newline at end of file diff --git a/src/interface/console.h b/src/interface/console.h new file mode 100644 index 0000000000..2ac2e1c4d7 --- /dev/null +++ b/src/interface/console.h @@ -0,0 +1,21 @@ +#ifndef _CONSOLE_H_ +#define _CONSOLE_H_ + +#include "../common.h" + +extern bool gConsoleOpen; + +void console_open(); +void console_close(); +void console_toggle(); + +void console_update(); +void console_draw(rct_drawpixelinfo *dpi); + +void console_input(int c); +void console_write(const char *src); +void console_writeline(const char *src); +void console_execute(const char *src); +void console_clear(); + +#endif \ No newline at end of file diff --git a/src/localisation/localisation.c b/src/localisation/localisation.c index c898158984..68a43a52d9 100644 --- a/src/localisation/localisation.c +++ b/src/localisation/localisation.c @@ -638,6 +638,11 @@ void format_string(char *dest, rct_string_id format, void *args) format_string_part(&dest, format, (char**)&args); } +void format_string_raw(char *dest, char *src, void *args) +{ + format_string_part_from_raw(&dest, src, &args); +} + /** * rct2: 0x006E37F7 * error (eax) diff --git a/src/localisation/localisation.h b/src/localisation/localisation.h index cea3d1fba4..e220b85a7a 100644 --- a/src/localisation/localisation.h +++ b/src/localisation/localisation.h @@ -27,7 +27,7 @@ #include "string_ids.h" void format_string(char *dest, rct_string_id format, void *args); -void format_string_part_from_raw(char **dest, const char *src, char **args); +void format_string_raw(char *dest, char *src, void *args); void generate_string_file(); void error_string_quit(int error, rct_string_id format); int get_string_length(char* buffer); diff --git a/src/platform/platform.h b/src/platform/platform.h index 98bd6b0041..e851468819 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -61,6 +61,7 @@ extern const unsigned char *gKeysState; extern unsigned char *gKeysPressed; extern unsigned int gLastKeyPressed; extern int gTextInputCursorPosition; +extern int gTextInputLength; extern int gResolutionsAllowAnyAspectRatio; extern int gNumResolutions; diff --git a/src/platform/shared.c b/src/platform/shared.c index d5e2010966..a722a65609 100644 --- a/src/platform/shared.c +++ b/src/platform/shared.c @@ -460,8 +460,10 @@ void platform_process_messages() memmove(gTextInput + gTextInputCursorPosition + 1, gTextInput + gTextInputCursorPosition, gTextInputMaxLength - gTextInputCursorPosition - 1); gTextInput[gTextInputCursorPosition] = new_char; gTextInputLength++; + } else { + gTextInput[gTextInputLength++] = new_char; + gTextInput[gTextInputLength++] = 0; } - else gTextInput[gTextInputLength++] = new_char; gTextInputCursorPosition++; } diff --git a/src/rct2.c b/src/rct2.c index 01fdc105b0..f7e2ec74e5 100644 --- a/src/rct2.c +++ b/src/rct2.c @@ -28,6 +28,7 @@ #include "drawing/drawing.h" #include "editor.h" #include "game.h" +#include "interface/console.h" #include "interface/viewport.h" #include "intro.h" #include "localisation/date.h" @@ -72,7 +73,7 @@ int rct2_init() RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) = 0; RCT2_GLOBAL(0x009AC310, char*) = RCT2_GLOBAL(RCT2_ADDRESS_CMDLINE, char*); get_system_time(); - srand(time(0)); + srand((unsigned int)time(0)); RCT2_GLOBAL(0x009DEA69, short) = RCT2_GLOBAL(RCT2_ADDRESS_OS_TIME_DAY, short); RCT2_GLOBAL(0x009DEA6B, short) = RCT2_GLOBAL(RCT2_ADDRESS_OS_TIME_MONTH, short); if (!rct2_init_directories()) @@ -346,6 +347,9 @@ void rct2_update_2() title_update(); else game_update(); + + console_update(); + console_draw(RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo)); } void rct2_endupdate() diff --git a/src/windows/top_toolbar.c b/src/windows/top_toolbar.c index 12641d8fb1..1128961e02 100644 --- a/src/windows/top_toolbar.c +++ b/src/windows/top_toolbar.c @@ -649,9 +649,7 @@ static void window_top_toolbar_paint() (gGameSpeed >= 5 ? FORMAT_YELLOW : FORMAT_GREEN), 175, (gGameSpeed >= 2 ? 175 : '\0'), (gGameSpeed >= 3 ? 175 : '\0'), (gGameSpeed >= 4 ? 175 : '\0'), '\0' }; - char* buffer; - buffer = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char); - format_string_part_from_raw(&buffer, &speedStr, NULL); + format_string_raw(RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char), speedStr, NULL); gfx_draw_string(dpi, speedStr, 0, x + 5, y + 14); }