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);
}