From 6ee8d9e0d2b32579a2900b69fcdb116776060149 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Sun, 22 Sep 2024 22:22:42 +0200 Subject: [PATCH 1/9] console: import logging; add word wrap --- include/libtrx/strings.h | 3 ++ meson.build | 1 + src/game/console/common.c | 65 +++++++++++++++++++++++++++++++++ src/strings.c | 77 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 src/game/console/common.c diff --git a/include/libtrx/strings.h b/include/libtrx/strings.h index fd15a83..3089d31 100644 --- a/include/libtrx/strings.h +++ b/include/libtrx/strings.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include bool String_EndsWith(const char *str, const char *suffix); @@ -13,3 +14,5 @@ bool String_IsEmpty(const char *value); bool String_ParseBool(const char *value, bool *target); bool String_ParseInteger(const char *value, int32_t *target); bool String_ParseDecimal(const char *value, float *target); + +char *String_WordWrap(const char *text, size_t line_length); diff --git a/meson.build b/meson.build index 01e40b8..75ba698 100644 --- a/meson.build +++ b/meson.build @@ -76,6 +76,7 @@ sources = [ 'src/game/console/cmd/pos.c', 'src/game/console/cmd/set_health.c', 'src/game/game_string.c', + 'src/game/console/common.c', 'src/game/items.c', 'src/gfx/2d/2d_renderer.c', 'src/gfx/2d/2d_surface.c', diff --git a/src/game/console/common.c b/src/game/console/common.c new file mode 100644 index 0000000..c05b4bf --- /dev/null +++ b/src/game/console/common.c @@ -0,0 +1,65 @@ +#include "game/console/common.h" + +#include "log.h" +#include "memory.h" +#include "strings.h" + +#include +#include +#include +#include + +extern int32_t Console_GetMaxLineLength(void); +extern void Console_LogImpl(const char *const text); + +static void M_LogMultiline(const char *text); +static void M_Log(const char *text); + +static void M_LogMultiline(const char *const text) +{ + assert(text != NULL); + char *wrapped_text = String_WordWrap(text, Console_GetMaxLineLength()); + + const char *start = wrapped_text; + while (true) { + const char *newline = strchr(start, '\n'); + if (newline == NULL) { + break; + } + char temp[newline - start + 1]; + strncpy(temp, start, newline - start); + temp[newline - start] = '\0'; + M_Log(temp); + start = newline + 1; + } + if (*start != '\0') { + M_Log(start); + } + Memory_FreePointer(&wrapped_text); +} + +static void M_Log(const char *text) +{ + assert(text != NULL); + Console_LogImpl(text); +} + +void Console_Log(const char *fmt, ...) +{ + assert(fmt != NULL); + + va_list va; + + va_start(va, fmt); + const size_t text_length = vsnprintf(NULL, 0, fmt, va); + char *text = Memory_Alloc(text_length + 1); + va_end(va); + + va_start(va, fmt); + vsnprintf(text, text_length + 1, fmt, va); + va_end(va); + + LOG_INFO("%s", text); + M_LogMultiline(text); + Memory_FreePointer(&text); +} diff --git a/src/strings.c b/src/strings.c index 819eefd..fc544bf 100644 --- a/src/strings.c +++ b/src/strings.c @@ -1,6 +1,8 @@ #include "strings.h" #include "log.h" +#include "memory.h" +#include "utils.h" #include #include @@ -155,3 +157,78 @@ bool String_ParseDecimal(const char *const value, float *const target) } return true; } + +char *String_WordWrap(const char *text, const size_t line_len) +{ + if (text == NULL || line_len == 0) { + return NULL; + } + + const size_t text_len = strlen(text); + char *const wrapped_text = Memory_Alloc(text_len + text_len / line_len + 2); + + size_t cur_line_len = 0; + char *dest = wrapped_text; + + while (*text != '\0') { + // Handle pre-existing newlines and leading spaces + if (*text == '\n' || (cur_line_len == 0 && isspace(*text))) { + if (*text == '\n') { + cur_line_len = 0; + } + *dest++ = *text++; + while (cur_line_len == 0 && isspace(*text) && *text != '\n') { + text++; + } + continue; + } + + // Find the length of the next word + const char *end = text; + while (*end != '\0' && !isspace(*end) && *end != '\n') { + end++; + } + const size_t word_len = end - text; + + if (cur_line_len + word_len > line_len) { + if (cur_line_len > 0) { + *dest++ = '\n'; + cur_line_len = 0; + continue; + } else { + for (size_t i = 0; i < word_len; i++) { + if (cur_line_len >= line_len) { + *dest++ = '\n'; + cur_line_len = 0; + } + *dest++ = *text++; + cur_line_len++; + } + continue; + } + } + + // Copy the word to the destination + for (size_t i = 0; i < word_len; i++) { + *dest++ = *text++; + cur_line_len++; + } + + // Copy any spaces (handle overflow within the loop) + while (*text != '\0' && isspace(*text) && *text != '\n') { + if (cur_line_len >= line_len) { + *dest++ = '\n'; + cur_line_len = 0; + while (isspace(*text) && *text != '\n') { + text++; + } + break; + } + *dest++ = *text++; + cur_line_len++; + } + } + + *dest = '\0'; + return wrapped_text; +} From 1faf0204d307da9f51ec408cb54861df137c65b0 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Sun, 22 Sep 2024 22:25:52 +0200 Subject: [PATCH 2/9] console/cmd: add /sfx --- include/libtrx/game/console/cmd/sfx.h | 5 ++ include/libtrx/game/game_string.def | 3 + include/libtrx/game/sound.h | 2 + include/libtrx/game/sound/common.h | 3 +- include/libtrx/game/sound/ids.h | 3 + meson.build | 1 + src/game/console/cmd/sfx.c | 85 +++++++++++++++++++++++++++ 7 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 include/libtrx/game/console/cmd/sfx.h create mode 100644 src/game/console/cmd/sfx.c diff --git a/include/libtrx/game/console/cmd/sfx.h b/include/libtrx/game/console/cmd/sfx.h new file mode 100644 index 0000000..5d8c92b --- /dev/null +++ b/include/libtrx/game/console/cmd/sfx.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../common.h" + +extern CONSOLE_COMMAND g_Console_Cmd_SFX; diff --git a/include/libtrx/game/game_string.def b/include/libtrx/game/game_string.def index 134012d..91ef726 100644 --- a/include/libtrx/game/game_string.def +++ b/include/libtrx/game/game_string.def @@ -15,4 +15,7 @@ GS_DEFINE(OSD_KILL_ALL_FAIL, "Uh-oh, there are no enemies left to kill...") GS_DEFINE(OSD_KILL, "Bye-bye!") GS_DEFINE(OSD_KILL_FAIL, "No enemy nearby...") GS_DEFINE(OSD_INVALID_OBJECT, "Invalid object") +GS_DEFINE(OSD_INVALID_SAMPLE, "Invalid sound: %d") GS_DEFINE(OSD_OBJECT_NOT_FOUND, "Object not found") +GS_DEFINE(OSD_SOUND_AVAILABLE_SAMPLES, "Available sounds: %s") +GS_DEFINE(OSD_SOUND_PLAYING_SAMPLE, "Playing sound %d") diff --git a/include/libtrx/game/sound.h b/include/libtrx/game/sound.h index 20fbf2e..173e870 100644 --- a/include/libtrx/game/sound.h +++ b/include/libtrx/game/sound.h @@ -1,2 +1,4 @@ +#pragma once + #include "sound/common.h" #include "sound/ids.h" diff --git a/include/libtrx/game/sound/common.h b/include/libtrx/game/sound/common.h index 87cb69e..e71afd4 100644 --- a/include/libtrx/game/sound/common.h +++ b/include/libtrx/game/sound/common.h @@ -11,4 +11,5 @@ typedef enum SOUND_PLAY_MODE { } SOUND_PLAY_MODE; // clang-format on -bool Sound_Effect(int32_t sfx_num, const XYZ_32 *pos, uint32_t flags); +bool Sound_Effect(SOUND_EFFECT_ID sfx_num, const XYZ_32 *pos, uint32_t flags); +bool Sound_IsAvailable(SOUND_EFFECT_ID sfx_num); diff --git a/include/libtrx/game/sound/ids.h b/include/libtrx/game/sound/ids.h index bd58360..81e69eb 100644 --- a/include/libtrx/game/sound/ids.h +++ b/include/libtrx/game/sound/ids.h @@ -3,6 +3,7 @@ #if TR_VERSION == 1 // clang-format off typedef enum SOUND_EFFECT_ID { + SFX_INVALID = -1, SFX_LARA_FEET = 0, SFX_LARA_CLIMB2 = 1, SFX_LARA_NO = 2, @@ -202,12 +203,14 @@ typedef enum SOUND_EFFECT_ID { SFX_SKATEKID_SPEECH = 204, SFX_LARA_SETUP = 205, SFX_EXPLOSION_CHEAT = SFX_ATLANTEAN_EXPLODE, + SFX_NUMBER_OF = 256, // clang-format on } SOUND_EFFECT_ID; #elif TR_VERSION == 2 typedef enum { // clang-format off + SFX_INVALID = -1, SFX_LARA_FEET = 0, SFX_LARA_CLIMB2 = 1, SFX_LARA_NO = 2, diff --git a/meson.build b/meson.build index 75ba698..1bea252 100644 --- a/meson.build +++ b/meson.build @@ -75,6 +75,7 @@ sources = [ 'src/game/console/cmd/kill.c', 'src/game/console/cmd/pos.c', 'src/game/console/cmd/set_health.c', + 'src/game/console/cmd/sfx.c', 'src/game/game_string.c', 'src/game/console/common.c', 'src/game/items.c', diff --git a/src/game/console/cmd/sfx.c b/src/game/console/cmd/sfx.c new file mode 100644 index 0000000..97562a5 --- /dev/null +++ b/src/game/console/cmd/sfx.c @@ -0,0 +1,85 @@ +#include "game/console/cmd/sfx.h" + +#include "game/console/common.h" +#include "game/game_string.h" +#include "game/sound.h" +#include "memory.h" +#include "strings.h" + +#include +#include +#include + +static char *M_CreateRangeString(void); +static COMMAND_RESULT M_Entrypoint(const char *const args); + +static char *M_CreateRangeString(void) +{ + size_t buffer_size = 64; + char *result = Memory_Alloc(buffer_size); + + int32_t prev = -1; + int32_t start = -1; + for (int32_t i = 0; i <= SFX_NUMBER_OF; i++) { + const bool valid = Sound_IsAvailable(i); + + if (valid && start == -1) { + start = i; + } + if (!valid && start != -1) { + char temp[32]; + if (start == prev) { + sprintf(temp, "%d, ", prev); + } else { + sprintf(temp, "%d-%d, ", start, prev); + } + + const int32_t len = strlen(temp); + if (strlen(result) + len >= buffer_size) { + buffer_size *= 2; + result = Memory_Realloc(result, buffer_size); + } + + strcat(result, temp); + start = -1; + } + + if (valid) { + prev = i; + } + } + + // Remove the trailing comma and space + result[strlen(result) - 2] = '\0'; + + return result; +} + +static COMMAND_RESULT M_Entrypoint(const char *const args) +{ + if (String_IsEmpty(args)) { + char *ranges = M_CreateRangeString(); + Console_Log(GS(OSD_SOUND_AVAILABLE_SAMPLES), ranges); + Memory_FreePointer(&ranges); + return CR_SUCCESS; + } + + int32_t sfx_id; + if (!String_ParseInteger(args, &sfx_id)) { + return CR_BAD_INVOCATION; + } + + if (!Sound_IsAvailable(sfx_id)) { + Console_Log(GS(OSD_INVALID_SAMPLE), sfx_id); + return CR_FAILURE; + } + + Console_Log(GS(OSD_SOUND_PLAYING_SAMPLE), sfx_id); + Sound_Effect(sfx_id, NULL, SPM_ALWAYS); + return CR_SUCCESS; +} + +CONSOLE_COMMAND g_Console_Cmd_SFX = { + .prefix = "sfx", + .proc = M_Entrypoint, +}; From 671659e0e600739d2628f09ed9551ff057d38e95 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Sun, 22 Sep 2024 23:08:45 +0200 Subject: [PATCH 3/9] console: import Console_Eval --- include/libtrx/game/console/common.h | 1 + include/libtrx/game/game_string.def | 3 ++ src/game/console/common.c | 62 ++++++++++++++++++++++++++-- src/game/console/extern.h | 9 ++++ 4 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/game/console/extern.h diff --git a/include/libtrx/game/console/common.h b/include/libtrx/game/console/common.h index 25f77f3..48e9b1f 100644 --- a/include/libtrx/game/console/common.h +++ b/include/libtrx/game/console/common.h @@ -17,3 +17,4 @@ typedef struct __PACKING { } CONSOLE_COMMAND; void Console_Log(const char *fmt, ...); +COMMAND_RESULT Console_Eval(const char *cmdline); diff --git a/include/libtrx/game/game_string.def b/include/libtrx/game/game_string.def index 91ef726..a5930cc 100644 --- a/include/libtrx/game/game_string.def +++ b/include/libtrx/game/game_string.def @@ -19,3 +19,6 @@ GS_DEFINE(OSD_INVALID_SAMPLE, "Invalid sound: %d") GS_DEFINE(OSD_OBJECT_NOT_FOUND, "Object not found") GS_DEFINE(OSD_SOUND_AVAILABLE_SAMPLES, "Available sounds: %s") GS_DEFINE(OSD_SOUND_PLAYING_SAMPLE, "Playing sound %d") +GS_DEFINE(OSD_UNKNOWN_COMMAND, "Unknown command: %s") +GS_DEFINE(OSD_COMMAND_BAD_INVOCATION, "Invalid invocation: %s") +GS_DEFINE(OSD_COMMAND_UNAVAILABLE, "This command is not currently available") diff --git a/src/game/console/common.c b/src/game/console/common.c index c05b4bf..3fd8f05 100644 --- a/src/game/console/common.c +++ b/src/game/console/common.c @@ -1,5 +1,7 @@ #include "game/console/common.h" +#include "./extern.h" +#include "game/game_string.h" #include "log.h" #include "memory.h" #include "strings.h" @@ -9,9 +11,6 @@ #include #include -extern int32_t Console_GetMaxLineLength(void); -extern void Console_LogImpl(const char *const text); - static void M_LogMultiline(const char *text); static void M_Log(const char *text); @@ -63,3 +62,60 @@ void Console_Log(const char *fmt, ...) M_LogMultiline(text); Memory_FreePointer(&text); } + +COMMAND_RESULT Console_Eval(const char *const cmdline) +{ + LOG_INFO("executing command: %s", cmdline); + + const char *args = NULL; + const CONSOLE_COMMAND *matching_cmd = NULL; + + CONSOLE_COMMAND **cmd = Console_GetCommands(); + for (int32_t i = 0;; i++) { + CONSOLE_COMMAND *cur_cmd = cmd[i]; + if (cur_cmd == NULL) { + break; + } + + char regex[strlen(cur_cmd->prefix) + 13]; + sprintf(regex, "^(%s)(\\s+.*)?$", cur_cmd->prefix); + if (!String_Match(cmdline, regex)) { + continue; + } + + args = strstr(cmdline, " "); + if (args != NULL) { + args++; + } else { + args = ""; + } + + matching_cmd = cur_cmd; + break; + } + + if (matching_cmd == NULL) { + Console_Log(GS(OSD_UNKNOWN_COMMAND), cmdline); + return CR_BAD_INVOCATION; + } + + assert(matching_cmd->proc != NULL); + const COMMAND_RESULT result = matching_cmd->proc(args); + + switch (result) { + case CR_BAD_INVOCATION: + Console_Log(GS(OSD_COMMAND_BAD_INVOCATION), cmdline); + break; + + case CR_UNAVAILABLE: + Console_Log(GS(OSD_COMMAND_UNAVAILABLE)); + break; + + case CR_SUCCESS: + case CR_FAILURE: + // The commands themselves are responsible for handling logging in + // these scenarios. + break; + } + return result; +} diff --git a/src/game/console/extern.h b/src/game/console/extern.h new file mode 100644 index 0000000..8e46987 --- /dev/null +++ b/src/game/console/extern.h @@ -0,0 +1,9 @@ +#pragma once + +#include "game/console/common.h" + +#include + +extern int32_t Console_GetMaxLineLength(void); +extern void Console_LogImpl(const char *text); +extern CONSOLE_COMMAND **Console_GetCommands(void); From fc8e4bdcca85751080bbf1018f95aea3ba627279 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Sun, 22 Sep 2024 23:19:02 +0200 Subject: [PATCH 4/9] console: introduce COMMAND_CONTEXT --- include/libtrx/game/console/common.h | 10 ++++-- src/game/console/cmd/config.c | 54 ++++++++++++++-------------- src/game/console/cmd/die.c | 4 +-- src/game/console/cmd/fly.c | 4 +-- src/game/console/cmd/give_item.c | 15 ++++---- src/game/console/cmd/heal.c | 4 +-- src/game/console/cmd/kill.c | 16 ++++----- src/game/console/cmd/pos.c | 4 +-- src/game/console/cmd/set_health.c | 8 ++--- src/game/console/cmd/sfx.c | 8 ++--- src/game/console/common.c | 43 +++++++++++----------- 11 files changed, 89 insertions(+), 81 deletions(-) diff --git a/include/libtrx/game/console/common.h b/include/libtrx/game/console/common.h index 48e9b1f..d5e781d 100644 --- a/include/libtrx/game/console/common.h +++ b/include/libtrx/game/console/common.h @@ -11,9 +11,15 @@ typedef enum { CR_BAD_INVOCATION, } COMMAND_RESULT; -typedef struct __PACKING { +typedef struct { + const struct __PACKING CONSOLE_COMMAND *cmd; const char *prefix; - COMMAND_RESULT (*proc)(const char *args); + const char *args; +} COMMAND_CONTEXT; + +typedef struct __PACKING CONSOLE_COMMAND { + const char *prefix; + COMMAND_RESULT (*proc)(const COMMAND_CONTEXT *ctx); } CONSOLE_COMMAND; void Console_Log(const char *fmt, ...); diff --git a/src/game/console/cmd/config.c b/src/game/console/cmd/config.c index b565cb1..f0d2e82 100644 --- a/src/game/console/cmd/config.c +++ b/src/game/console/cmd/config.c @@ -19,6 +19,8 @@ static bool M_GetCurrentValue( static bool M_SetCurrentValue( const CONFIG_OPTION *option, const char *new_value); +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); + static const char *M_Resolve(const char *const option_name) { const char *dot = strrchr(option_name, '.'); @@ -161,6 +163,32 @@ static bool M_SetCurrentValue( return false; } +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) +{ + COMMAND_RESULT result = CR_BAD_INVOCATION; + + char *key = Memory_DupStr(ctx->args); + char *const space = strchr(key, ' '); + const char *new_value = NULL; + if (space != NULL) { + new_value = space + 1; + space[0] = '\0'; // NULL-terminate the key + } + + const CONFIG_OPTION *const option = + Console_Cmd_Config_GetOptionFromKey(key); + if (option == NULL) { + Console_Log(GS(OSD_CONFIG_OPTION_UNKNOWN_OPTION), key); + result = CR_FAILURE; + } else { + result = Console_Cmd_Config_Helper(option, new_value); + } + +cleanup: + Memory_FreePointer(&key); + return result; +} + const CONFIG_OPTION *Console_Cmd_Config_GetOptionFromKey(const char *const key) { for (const CONFIG_OPTION *option = Config_GetOptionMap(); @@ -218,32 +246,6 @@ COMMAND_RESULT Console_Cmd_Config_Helper( return result; } -static COMMAND_RESULT M_Entrypoint(const char *const args) -{ - COMMAND_RESULT result = CR_BAD_INVOCATION; - - char *key = Memory_DupStr(args); - char *const space = strchr(key, ' '); - const char *new_value = NULL; - if (space != NULL) { - new_value = space + 1; - space[0] = '\0'; // NULL-terminate the key - } - - const CONFIG_OPTION *const option = - Console_Cmd_Config_GetOptionFromKey(key); - if (option == NULL) { - Console_Log(GS(OSD_CONFIG_OPTION_UNKNOWN_OPTION), key); - result = CR_FAILURE; - } else { - result = Console_Cmd_Config_Helper(option, new_value); - } - -cleanup: - Memory_FreePointer(&key); - return result; -} - CONSOLE_COMMAND g_Console_Cmd_Config = { .prefix = "set", .proc = M_Entrypoint, diff --git a/src/game/console/cmd/die.c b/src/game/console/cmd/die.c index 3396b24..39b1c9f 100644 --- a/src/game/console/cmd/die.c +++ b/src/game/console/cmd/die.c @@ -7,9 +7,9 @@ #include "game/objects/ids.h" #include "game/sound.h" -static COMMAND_RESULT M_Entrypoint(const char *args); +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); -static COMMAND_RESULT M_Entrypoint(const char *const args) +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) { if (!Object_GetObject(O_LARA)->loaded) { return CR_UNAVAILABLE; diff --git a/src/game/console/cmd/fly.c b/src/game/console/cmd/fly.c index 242b587..4719328 100644 --- a/src/game/console/cmd/fly.c +++ b/src/game/console/cmd/fly.c @@ -4,9 +4,9 @@ #include "game/game_string.h" #include "game/lara/cheat.h" -static COMMAND_RESULT M_Entrypoint(const char *const args); +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); -static COMMAND_RESULT M_Entrypoint(const char *const args) +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) { if (!Game_IsPlayable()) { return CR_UNAVAILABLE; diff --git a/src/game/console/cmd/give_item.c b/src/game/console/cmd/give_item.c index 0299010..3504784 100644 --- a/src/game/console/cmd/give_item.c +++ b/src/game/console/cmd/give_item.c @@ -14,35 +14,36 @@ #include static bool M_CanTargetObjectPickup(GAME_OBJECT_ID object_id); -static COMMAND_RESULT M_Entrypoint(const char *args); +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); static bool M_CanTargetObjectPickup(const GAME_OBJECT_ID object_id) { return Object_IsObjectType(object_id, g_PickupObjects); } -static COMMAND_RESULT M_Entrypoint(const char *args) +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) { if (!Game_IsPlayable()) { return CR_UNAVAILABLE; } - if (String_Equivalent(args, "keys")) { + if (String_Equivalent(ctx->args, "keys")) { return Lara_Cheat_GiveAllKeys() ? CR_SUCCESS : CR_FAILURE; } - if (String_Equivalent(args, "guns")) { + if (String_Equivalent(ctx->args, "guns")) { return Lara_Cheat_GiveAllGuns() ? CR_SUCCESS : CR_FAILURE; } - if (String_Equivalent(args, "all")) { + if (String_Equivalent(ctx->args, "all")) { return Lara_Cheat_GiveAllItems() ? CR_SUCCESS : CR_FAILURE; } int32_t num = 1; - if (sscanf(args, "%d ", &num) == 1) { + const char *args = ctx->args; + if (sscanf(ctx->args, "%d ", &num) == 1) { args = strstr(args, " "); - if (!args) { + if (args == NULL) { return CR_BAD_INVOCATION; } args++; diff --git a/src/game/console/cmd/heal.c b/src/game/console/cmd/heal.c index d46d38c..fd620bd 100644 --- a/src/game/console/cmd/heal.c +++ b/src/game/console/cmd/heal.c @@ -6,9 +6,9 @@ #include "game/lara/const.h" #include "game/lara/misc.h" -static COMMAND_RESULT M_Entrypoint(const char *args); +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); -static COMMAND_RESULT M_Entrypoint(const char *const args) +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) { if (!Game_IsPlayable()) { return CR_UNAVAILABLE; diff --git a/src/game/console/cmd/kill.c b/src/game/console/cmd/kill.c index 82ce71c..5a586d4 100644 --- a/src/game/console/cmd/kill.c +++ b/src/game/console/cmd/kill.c @@ -15,7 +15,7 @@ #include "strings.h" static bool M_CanTargetObjectCreature(GAME_OBJECT_ID object_id); -static COMMAND_RESULT M_Entrypoint(const char *args); +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); static bool M_CanTargetObjectCreature(const GAME_OBJECT_ID object_id) { @@ -23,10 +23,10 @@ static bool M_CanTargetObjectCreature(const GAME_OBJECT_ID object_id) || Object_IsObjectType(object_id, g_AllyObjects); } -static COMMAND_RESULT M_Entrypoint(const char *const args) +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) { // kill all the enemies in the level - if (String_Equivalent(args, "all")) { + if (String_Equivalent(ctx->args, "all")) { int32_t num_killed = 0; for (int16_t item_num = 0; item_num < Item_GetTotalCount(); item_num++) { @@ -50,7 +50,7 @@ static COMMAND_RESULT M_Entrypoint(const char *const args) // kill all the enemies around Lara within one tile, or a single nearest // enemy - if (String_Equivalent(args, "")) { + if (String_Equivalent(ctx->args, "")) { bool found = false; while (true) { const int16_t best_item_num = Lara_GetNearestEnemy(); @@ -81,8 +81,8 @@ static COMMAND_RESULT M_Entrypoint(const char *const args) bool matches_found = false; int32_t num_killed = 0; int32_t match_count = 0; - GAME_OBJECT_ID *matching_objs = - Object_IdsFromName(args, &match_count, M_CanTargetObjectCreature); + GAME_OBJECT_ID *matching_objs = Object_IdsFromName( + ctx->args, &match_count, M_CanTargetObjectCreature); for (int16_t item_num = 0; item_num < Item_GetTotalCount(); item_num++) { @@ -107,11 +107,11 @@ static COMMAND_RESULT M_Entrypoint(const char *const args) Memory_FreePointer(&matching_objs); if (!matches_found) { - Console_Log(GS(OSD_INVALID_OBJECT), args); + Console_Log(GS(OSD_INVALID_OBJECT), ctx->args); return CR_FAILURE; } if (num_killed == 0) { - Console_Log(GS(OSD_OBJECT_NOT_FOUND), args); + Console_Log(GS(OSD_OBJECT_NOT_FOUND), ctx->args); return CR_FAILURE; } Console_Log(GS(OSD_KILL_ALL), num_killed); diff --git a/src/game/console/cmd/pos.c b/src/game/console/cmd/pos.c index b6afc5a..cbd76cf 100644 --- a/src/game/console/cmd/pos.c +++ b/src/game/console/cmd/pos.c @@ -6,9 +6,9 @@ #include "game/lara/common.h" #include "game/objects/common.h" -static COMMAND_RESULT M_Entrypoint(const char *args); +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); -static COMMAND_RESULT M_Entrypoint(const char *const args) +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) { const OBJECT_INFO *const object = Object_GetObject(O_LARA); if (!object->loaded) { diff --git a/src/game/console/cmd/set_health.c b/src/game/console/cmd/set_health.c index 8d9bdce..265b512 100644 --- a/src/game/console/cmd/set_health.c +++ b/src/game/console/cmd/set_health.c @@ -9,22 +9,22 @@ #include "strings.h" #include "utils.h" -static COMMAND_RESULT M_Entrypoint(const char *args); +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); -static COMMAND_RESULT M_Entrypoint(const char *const args) +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) { if (!Game_IsPlayable()) { return CR_UNAVAILABLE; } ITEM_INFO *const lara_item = Lara_GetItem(); - if (String_IsEmpty(args)) { + if (String_IsEmpty(ctx->args)) { Console_Log(GS(OSD_CURRENT_HEALTH_GET), lara_item->hit_points); return CR_SUCCESS; } int32_t hp; - if (!String_ParseInteger(args, &hp)) { + if (!String_ParseInteger(ctx->args, &hp)) { return CR_BAD_INVOCATION; } CLAMP(hp, 0, LARA_MAX_HITPOINTS); diff --git a/src/game/console/cmd/sfx.c b/src/game/console/cmd/sfx.c index 97562a5..5fe134a 100644 --- a/src/game/console/cmd/sfx.c +++ b/src/game/console/cmd/sfx.c @@ -11,7 +11,7 @@ #include static char *M_CreateRangeString(void); -static COMMAND_RESULT M_Entrypoint(const char *const args); +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); static char *M_CreateRangeString(void) { @@ -55,9 +55,9 @@ static char *M_CreateRangeString(void) return result; } -static COMMAND_RESULT M_Entrypoint(const char *const args) +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) { - if (String_IsEmpty(args)) { + if (String_IsEmpty(ctx->args)) { char *ranges = M_CreateRangeString(); Console_Log(GS(OSD_SOUND_AVAILABLE_SAMPLES), ranges); Memory_FreePointer(&ranges); @@ -65,7 +65,7 @@ static COMMAND_RESULT M_Entrypoint(const char *const args) } int32_t sfx_id; - if (!String_ParseInteger(args, &sfx_id)) { + if (!String_ParseInteger(ctx->args, &sfx_id)) { return CR_BAD_INVOCATION; } diff --git a/src/game/console/common.c b/src/game/console/common.c index 3fd8f05..57de5c6 100644 --- a/src/game/console/common.c +++ b/src/game/console/common.c @@ -67,31 +67,16 @@ COMMAND_RESULT Console_Eval(const char *const cmdline) { LOG_INFO("executing command: %s", cmdline); - const char *args = NULL; const CONSOLE_COMMAND *matching_cmd = NULL; - CONSOLE_COMMAND **cmd = Console_GetCommands(); - for (int32_t i = 0;; i++) { - CONSOLE_COMMAND *cur_cmd = cmd[i]; - if (cur_cmd == NULL) { + while (*cmd != NULL) { + char regex[strlen((*cmd)->prefix) + 13]; + sprintf(regex, "^(%s)(\\s+.*)?$", (*cmd)->prefix); + if (String_Match(cmdline, regex)) { + matching_cmd = *cmd; break; } - - char regex[strlen(cur_cmd->prefix) + 13]; - sprintf(regex, "^(%s)(\\s+.*)?$", cur_cmd->prefix); - if (!String_Match(cmdline, regex)) { - continue; - } - - args = strstr(cmdline, " "); - if (args != NULL) { - args++; - } else { - args = ""; - } - - matching_cmd = cur_cmd; - break; + *cmd++; } if (matching_cmd == NULL) { @@ -99,8 +84,22 @@ COMMAND_RESULT Console_Eval(const char *const cmdline) return CR_BAD_INVOCATION; } + char *prefix = Memory_DupStr(cmdline); + char *args = ""; + char *space = strchr(prefix, ' '); + if (space != NULL) { + *space = '\0'; + args = space + 1; + } + + const COMMAND_CONTEXT ctx = { + .cmd = matching_cmd, + .prefix = prefix, + .args = args, + }; assert(matching_cmd->proc != NULL); - const COMMAND_RESULT result = matching_cmd->proc(args); + const COMMAND_RESULT result = matching_cmd->proc(&ctx); + Memory_FreePointer(&prefix); switch (result) { case CR_BAD_INVOCATION: From a298cac906c864d0e40e7c1cb729d676c242e259 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Sun, 22 Sep 2024 23:30:38 +0200 Subject: [PATCH 5/9] console/cmd: import /endlevel --- include/libtrx/game/console/cmd/end_level.h | 5 +++++ include/libtrx/game/lara/cheat.h | 13 +++++++------ meson.build | 1 + src/game/console/cmd/end_level.c | 21 +++++++++++++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 include/libtrx/game/console/cmd/end_level.h create mode 100644 src/game/console/cmd/end_level.c diff --git a/include/libtrx/game/console/cmd/end_level.h b/include/libtrx/game/console/cmd/end_level.h new file mode 100644 index 0000000..309e53e --- /dev/null +++ b/include/libtrx/game/console/cmd/end_level.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../common.h" + +extern CONSOLE_COMMAND g_Console_Cmd_EndLevel; diff --git a/include/libtrx/game/lara/cheat.h b/include/libtrx/game/lara/cheat.h index 06f40fe..68311c4 100644 --- a/include/libtrx/game/lara/cheat.h +++ b/include/libtrx/game/lara/cheat.h @@ -3,9 +3,10 @@ #include #include -bool Lara_Cheat_GiveAllKeys(void); -bool Lara_Cheat_GiveAllGuns(void); -bool Lara_Cheat_GiveAllItems(void); -bool Lara_Cheat_EnterFlyMode(void); -bool Lara_Cheat_ExitFlyMode(void); -bool Lara_Cheat_KillEnemy(int16_t item_num); +extern bool Lara_Cheat_GiveAllKeys(void); +extern bool Lara_Cheat_GiveAllGuns(void); +extern bool Lara_Cheat_GiveAllItems(void); +extern bool Lara_Cheat_EnterFlyMode(void); +extern bool Lara_Cheat_ExitFlyMode(void); +extern bool Lara_Cheat_KillEnemy(int16_t item_num); +extern void Lara_Cheat_EndLevel(void); diff --git a/meson.build b/meson.build index 1bea252..9987de5 100644 --- a/meson.build +++ b/meson.build @@ -69,6 +69,7 @@ sources = [ 'src/game/backpack.c', 'src/game/console/cmd/config.c', 'src/game/console/cmd/die.c', + 'src/game/console/cmd/end_level.c', 'src/game/console/cmd/fly.c', 'src/game/console/cmd/give_item.c', 'src/game/console/cmd/heal.c', diff --git a/src/game/console/cmd/end_level.c b/src/game/console/cmd/end_level.c new file mode 100644 index 0000000..9cac5ab --- /dev/null +++ b/src/game/console/cmd/end_level.c @@ -0,0 +1,21 @@ +#include "game/console/cmd/end_level.h" + +#include "game/lara/cheat.h" +#include "strings.h" + +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); + +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) +{ + if (!String_Equivalent(ctx->args, "")) { + return CR_BAD_INVOCATION; + } + + Lara_Cheat_EndLevel(); + return CR_SUCCESS; +} + +CONSOLE_COMMAND g_Console_Cmd_EndLevel = { + .prefix = "endlevel", + .proc = M_Entrypoint, +}; From cfeed8c28af92ba43cce46e7c081e28c85211dc7 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Sun, 22 Sep 2024 23:53:27 +0200 Subject: [PATCH 6/9] console/cmd: import /exit --- include/libtrx/game/console/cmd/exit_game.h | 5 +++++ include/libtrx/game/gameflow/common.h | 5 +++++ include/libtrx/game/gameflow/types.h | 24 +++++++++++++++++++++ meson.build | 1 + src/game/console/cmd/exit_game.c | 16 ++++++++++++++ 5 files changed, 51 insertions(+) create mode 100644 include/libtrx/game/console/cmd/exit_game.h create mode 100644 include/libtrx/game/gameflow/common.h create mode 100644 include/libtrx/game/gameflow/types.h create mode 100644 src/game/console/cmd/exit_game.c diff --git a/include/libtrx/game/console/cmd/exit_game.h b/include/libtrx/game/console/cmd/exit_game.h new file mode 100644 index 0000000..1b7b757 --- /dev/null +++ b/include/libtrx/game/console/cmd/exit_game.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../common.h" + +extern CONSOLE_COMMAND g_Console_Cmd_ExitGame; diff --git a/include/libtrx/game/gameflow/common.h b/include/libtrx/game/gameflow/common.h new file mode 100644 index 0000000..d1fff77 --- /dev/null +++ b/include/libtrx/game/gameflow/common.h @@ -0,0 +1,5 @@ +#pragma once + +#include "types.h" + +extern void Gameflow_OverrideCommand(GAMEFLOW_COMMAND action); diff --git a/include/libtrx/game/gameflow/types.h b/include/libtrx/game/gameflow/types.h new file mode 100644 index 0000000..71a0659 --- /dev/null +++ b/include/libtrx/game/gameflow/types.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +typedef enum { + GF_CONTINUE_SEQUENCE, + GF_START_GAME, + GF_START_CINE, + GF_START_FMV, + GF_START_DEMO, + GF_EXIT_TO_TITLE, + GF_LEVEL_COMPLETE, + GF_EXIT_GAME, + GF_START_SAVED_GAME, + GF_RESTART_GAME, + GF_SELECT_GAME, + GF_START_GYM, + GF_STORY_SO_FAR, +} GAMEFLOW_ACTION; + +typedef struct GAMEFLOW_COMMAND { + GAMEFLOW_ACTION action; + int32_t param; +} GAMEFLOW_COMMAND; diff --git a/meson.build b/meson.build index 9987de5..69345a3 100644 --- a/meson.build +++ b/meson.build @@ -70,6 +70,7 @@ sources = [ 'src/game/console/cmd/config.c', 'src/game/console/cmd/die.c', 'src/game/console/cmd/end_level.c', + 'src/game/console/cmd/exit_game.c', 'src/game/console/cmd/fly.c', 'src/game/console/cmd/give_item.c', 'src/game/console/cmd/heal.c', diff --git a/src/game/console/cmd/exit_game.c b/src/game/console/cmd/exit_game.c new file mode 100644 index 0000000..37edd28 --- /dev/null +++ b/src/game/console/cmd/exit_game.c @@ -0,0 +1,16 @@ +#include "game/console/cmd/exit_game.h" + +#include "game/gameflow/common.h" + +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); + +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) +{ + Gameflow_OverrideCommand((GAMEFLOW_COMMAND) { .action = GF_EXIT_GAME }); + return CR_SUCCESS; +} + +CONSOLE_COMMAND g_Console_Cmd_ExitGame = { + .prefix = "exit", + .proc = M_Entrypoint, +}; From 28fd44933585a8ceae19bf999159e84260d0e0d1 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Sun, 22 Sep 2024 23:58:24 +0200 Subject: [PATCH 7/9] console/cmd: import /title --- include/libtrx/game/console/cmd/exit_to_title.h | 5 +++++ meson.build | 1 + src/game/console/cmd/exit_to_title.c | 16 ++++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 include/libtrx/game/console/cmd/exit_to_title.h create mode 100644 src/game/console/cmd/exit_to_title.c diff --git a/include/libtrx/game/console/cmd/exit_to_title.h b/include/libtrx/game/console/cmd/exit_to_title.h new file mode 100644 index 0000000..506e9cb --- /dev/null +++ b/include/libtrx/game/console/cmd/exit_to_title.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../common.h" + +extern CONSOLE_COMMAND g_Console_Cmd_ExitToTitle; diff --git a/meson.build b/meson.build index 69345a3..cee4d1e 100644 --- a/meson.build +++ b/meson.build @@ -71,6 +71,7 @@ sources = [ 'src/game/console/cmd/die.c', 'src/game/console/cmd/end_level.c', 'src/game/console/cmd/exit_game.c', + 'src/game/console/cmd/exit_to_title.c', 'src/game/console/cmd/fly.c', 'src/game/console/cmd/give_item.c', 'src/game/console/cmd/heal.c', diff --git a/src/game/console/cmd/exit_to_title.c b/src/game/console/cmd/exit_to_title.c new file mode 100644 index 0000000..5d89e93 --- /dev/null +++ b/src/game/console/cmd/exit_to_title.c @@ -0,0 +1,16 @@ +#include "game/console/cmd/exit_to_title.h" + +#include "game/gameflow/common.h" + +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); + +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) +{ + Gameflow_OverrideCommand((GAMEFLOW_COMMAND) { .action = GF_EXIT_TO_TITLE }); + return CR_SUCCESS; +} + +CONSOLE_COMMAND g_Console_Cmd_ExitToTitle = { + .prefix = "title", + .proc = M_Entrypoint, +}; From 9dd6c5c1a8c66ff990897c7df30c7278a16d8859 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Mon, 23 Sep 2024 00:16:40 +0200 Subject: [PATCH 8/9] console/cmd: import /tp --- include/libtrx/game/console/cmd/teleport.h | 5 + include/libtrx/game/const.h | 3 +- include/libtrx/game/game_string.def | 7 + include/libtrx/game/gameflow/common.h | 2 +- include/libtrx/game/items/common.h | 2 +- include/libtrx/game/items/enum.h | 9 + include/libtrx/game/items/types.h | 2 +- include/libtrx/game/lara/cheat.h | 1 + include/libtrx/game/objects/vars.h | 3 + include/libtrx/game/random.h | 8 + include/libtrx/game/rooms.h | 5 + include/libtrx/game/rooms/common.h | 6 + include/libtrx/game/rooms/const.h | 4 + include/libtrx/game/rooms/enum.h | 29 ++++ include/libtrx/game/rooms/types.h | 171 ++++++++++++++++++ meson.build | 1 + src/game/console/cmd/give_item.c | 2 +- src/game/console/cmd/teleport.c | 191 +++++++++++++++++++++ 18 files changed, 446 insertions(+), 5 deletions(-) create mode 100644 include/libtrx/game/console/cmd/teleport.h create mode 100644 include/libtrx/game/random.h create mode 100644 include/libtrx/game/rooms.h create mode 100644 include/libtrx/game/rooms/common.h create mode 100644 include/libtrx/game/rooms/const.h create mode 100644 include/libtrx/game/rooms/enum.h create mode 100644 include/libtrx/game/rooms/types.h create mode 100644 src/game/console/cmd/teleport.c diff --git a/include/libtrx/game/console/cmd/teleport.h b/include/libtrx/game/console/cmd/teleport.h new file mode 100644 index 0000000..cab7108 --- /dev/null +++ b/include/libtrx/game/console/cmd/teleport.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../common.h" + +extern CONSOLE_COMMAND g_Console_Cmd_Teleport; diff --git a/include/libtrx/game/const.h b/include/libtrx/game/const.h index 5a28c68..b982bc8 100644 --- a/include/libtrx/game/const.h +++ b/include/libtrx/game/const.h @@ -4,4 +4,5 @@ #define PHD_ONE 0x10000 #define STEP_L 256 -#define WALL_L 1024 +#define WALL_L 1024 // 1 << WALL_SHIFT +#define WALL_SHIFT 10 diff --git a/include/libtrx/game/game_string.def b/include/libtrx/game/game_string.def index a5930cc..c577fea 100644 --- a/include/libtrx/game/game_string.def +++ b/include/libtrx/game/game_string.def @@ -22,3 +22,10 @@ GS_DEFINE(OSD_SOUND_PLAYING_SAMPLE, "Playing sound %d") GS_DEFINE(OSD_UNKNOWN_COMMAND, "Unknown command: %s") GS_DEFINE(OSD_COMMAND_BAD_INVOCATION, "Invalid invocation: %s") GS_DEFINE(OSD_COMMAND_UNAVAILABLE, "This command is not currently available") +GS_DEFINE(OSD_INVALID_ROOM, "Invalid room: %d. Valid rooms are 0-%d") +GS_DEFINE(OSD_POS_SET_POS, "Teleported to position: %.3f %.3f %.3f") +GS_DEFINE(OSD_POS_SET_POS_FAIL, "Failed to teleport to position: %.3f %.3f %.3f") +GS_DEFINE(OSD_POS_SET_ROOM, "Teleported to room: %d") +GS_DEFINE(OSD_POS_SET_ROOM_FAIL, "Failed to teleport to room: %d") +GS_DEFINE(OSD_POS_SET_ITEM, "Teleported to object: %s") +GS_DEFINE(OSD_POS_SET_ITEM_FAIL, "Failed to teleport to object: %s") diff --git a/include/libtrx/game/gameflow/common.h b/include/libtrx/game/gameflow/common.h index d1fff77..3958a2d 100644 --- a/include/libtrx/game/gameflow/common.h +++ b/include/libtrx/game/gameflow/common.h @@ -1,5 +1,5 @@ #pragma once -#include "types.h" +#include "./types.h" extern void Gameflow_OverrideCommand(GAMEFLOW_COMMAND action); diff --git a/include/libtrx/game/items/common.h b/include/libtrx/game/items/common.h index 1633f4d..16a7aa1 100644 --- a/include/libtrx/game/items/common.h +++ b/include/libtrx/game/items/common.h @@ -1,6 +1,6 @@ #pragma once -#include "types.h" +#include "./types.h" #include diff --git a/include/libtrx/game/items/enum.h b/include/libtrx/game/items/enum.h index 0127a89..8c3cb93 100644 --- a/include/libtrx/game/items/enum.h +++ b/include/libtrx/game/items/enum.h @@ -17,4 +17,13 @@ typedef enum { IF_INVISIBLE = 0x0100, IF_KILLED = 0x8000, } ITEM_FLAG; + + +typedef enum { + IS_INACTIVE = 0, + IS_ACTIVE = 1, + IS_DEACTIVATED = 2, + IS_INVISIBLE = 3, +} ITEM_STATUS; + // clang-format on diff --git a/include/libtrx/game/items/types.h b/include/libtrx/game/items/types.h index 34bafc8..807eaec 100644 --- a/include/libtrx/game/items/types.h +++ b/include/libtrx/game/items/types.h @@ -2,7 +2,7 @@ #include "../math.h" #include "../objects/ids.h" -#include "enum.h" +#include "./enum.h" #if TR_VERSION == 1 typedef struct __PACKING CARRIED_ITEM { diff --git a/include/libtrx/game/lara/cheat.h b/include/libtrx/game/lara/cheat.h index 68311c4..f3297dc 100644 --- a/include/libtrx/game/lara/cheat.h +++ b/include/libtrx/game/lara/cheat.h @@ -10,3 +10,4 @@ extern bool Lara_Cheat_EnterFlyMode(void); extern bool Lara_Cheat_ExitFlyMode(void); extern bool Lara_Cheat_KillEnemy(int16_t item_num); extern void Lara_Cheat_EndLevel(void); +extern bool Lara_Cheat_Teleport(int32_t x, int32_t y, int32_t z); diff --git a/include/libtrx/game/objects/vars.h b/include/libtrx/game/objects/vars.h index b56a77c..5e8b2a0 100644 --- a/include/libtrx/game/objects/vars.h +++ b/include/libtrx/game/objects/vars.h @@ -5,3 +5,6 @@ extern const GAME_OBJECT_ID g_EnemyObjects[]; extern const GAME_OBJECT_ID g_AllyObjects[]; extern const GAME_OBJECT_ID g_PickupObjects[]; +extern const GAME_OBJECT_ID g_AnimObjects[]; +extern const GAME_OBJECT_ID g_NullObjects[]; +extern const GAME_OBJECT_ID g_InvObjects[]; diff --git a/include/libtrx/game/random.h b/include/libtrx/game/random.h new file mode 100644 index 0000000..14a10b7 --- /dev/null +++ b/include/libtrx/game/random.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +extern void Random_SeedControl(int32_t seed); +extern void Random_SeedDraw(int32_t seed); +extern int32_t Random_GetControl(void); +extern int32_t Random_GetDraw(void); diff --git a/include/libtrx/game/rooms.h b/include/libtrx/game/rooms.h new file mode 100644 index 0000000..33cb40b --- /dev/null +++ b/include/libtrx/game/rooms.h @@ -0,0 +1,5 @@ +#pragma once + +#include "rooms/common.h" +#include "rooms/const.h" +#include "rooms/enum.h" diff --git a/include/libtrx/game/rooms/common.h b/include/libtrx/game/rooms/common.h new file mode 100644 index 0000000..a38a9c4 --- /dev/null +++ b/include/libtrx/game/rooms/common.h @@ -0,0 +1,6 @@ +#pragma once + +#include "types.h" + +extern int32_t Room_GetTotalCount(void); +ROOM_INFO *Room_Get(int32_t room_num); diff --git a/include/libtrx/game/rooms/const.h b/include/libtrx/game/rooms/const.h new file mode 100644 index 0000000..521dedf --- /dev/null +++ b/include/libtrx/game/rooms/const.h @@ -0,0 +1,4 @@ +#pragma once + +#define NO_ROOM_NEG (-1) +#define NO_ROOM 255 diff --git a/include/libtrx/game/rooms/enum.h b/include/libtrx/game/rooms/enum.h new file mode 100644 index 0000000..e4c633c --- /dev/null +++ b/include/libtrx/game/rooms/enum.h @@ -0,0 +1,29 @@ +#pragma once + +#if TR_VERSION == 1 +typedef enum { + TO_OBJECT = 0, + TO_CAMERA = 1, + TO_SINK = 2, + TO_FLIPMAP = 3, + TO_FLIPON = 4, + TO_FLIPOFF = 5, + TO_TARGET = 6, + TO_FINISH = 7, + TO_CD = 8, + TO_FLIPEFFECT = 9, + TO_SECRET = 10, +} TRIGGER_OBJECT; + +typedef enum { + TT_TRIGGER = 0, + TT_PAD = 1, + TT_SWITCH = 2, + TT_KEY = 3, + TT_PICKUP = 4, + TT_HEAVY = 5, + TT_ANTIPAD = 6, + TT_COMBAT = 7, + TT_DUMMY = 8, +} TRIGGER_TYPE; +#endif diff --git a/include/libtrx/game/rooms/types.h b/include/libtrx/game/rooms/types.h new file mode 100644 index 0000000..004986a --- /dev/null +++ b/include/libtrx/game/rooms/types.h @@ -0,0 +1,171 @@ +#pragma once + +#include "../math.h" +#include "./enum.h" + +#include + +#if TR_VERSION == 1 +typedef struct { + TRIGGER_OBJECT type; + void *parameter; +} TRIGGER_CMD; + +typedef struct { + int16_t camera_num; + uint8_t timer; + uint8_t glide; + bool one_shot; +} TRIGGER_CAMERA_DATA; + +typedef struct { + TRIGGER_TYPE type; + int8_t timer; + int16_t mask; + bool one_shot; + int16_t item_index; + int32_t command_count; + TRIGGER_CMD *commands; +} TRIGGER; + +typedef struct __PACKING { + int16_t room_num; + XYZ_16 normal; + XYZ_16 vertex[4]; +} DOOR_INFO; + +#elif TR_VERSION == 2 +typedef struct __PACKING { + int16_t room; + int16_t x; + int16_t y; + int16_t z; + XYZ_16 vertex[4]; +} DOOR_INFO; +#endif + +typedef struct __PACKING { + uint16_t count; + DOOR_INFO door[]; +} DOOR_INFOS; + +#if TR_VERSION == 1 +typedef struct __PACKING { + uint16_t index; + int16_t box; + bool is_death_sector; + TRIGGER *trigger; + struct { + uint8_t pit; + uint8_t sky; + int16_t wall; + } portal_room; + struct { + int16_t height; + int16_t tilt; + } floor, ceiling; +} SECTOR_INFO; + +typedef struct LIGHT_INFO { + XYZ_32 pos; + int16_t intensity; + int32_t falloff; +} LIGHT_INFO; + +typedef struct MESH_INFO { + XYZ_32 pos; + struct { + int16_t y; + } rot; + uint16_t shade; + uint16_t static_num; +} MESH_INFO; + +typedef struct __PACKING { + int16_t *data; + DOOR_INFOS *doors; + SECTOR_INFO *sectors; + LIGHT_INFO *light; + MESH_INFO *mesh; + int32_t x; + int32_t y; + int32_t z; + int32_t min_floor; + int32_t max_ceiling; + int16_t z_size; + int16_t x_size; + int16_t ambient; + int16_t num_lights; + int16_t num_meshes; + int16_t left; + int16_t right; + int16_t top; + int16_t bottom; + int16_t bound_active; + int16_t item_num; + int16_t fx_num; + int16_t flipped_room; + uint16_t flags; +} ROOM_INFO; + +#elif TR_VERSION == 2 +typedef struct __PACKING { + uint16_t idx; + int16_t box; + uint8_t pit_room; + int8_t floor; + uint8_t sky_room; + int8_t ceiling; +} SECTOR_INFO; + +typedef struct __PACKING { + int32_t x; + int32_t y; + int32_t z; + int16_t intensity1; + int16_t intensity2; + int32_t falloff1; + int32_t falloff2; +} LIGHT_INFO; + +typedef struct __PACKING { + int32_t x; + int32_t y; + int32_t z; + int16_t y_rot; + int16_t shade1; + int16_t shade2; + int16_t static_num; +} MESH_INFO; + +typedef struct __PACKING { + int16_t *data; + DOOR_INFOS *doors; + SECTOR_INFO *sector; + LIGHT_INFO *light; + MESH_INFO *mesh; + XYZ_32 pos; + int32_t min_floor; + int32_t max_ceiling; + int16_t z_size; + int16_t x_size; + int16_t ambient1; + int16_t ambient2; + int16_t light_mode; + int16_t num_lights; + int16_t num_meshes; + int16_t bound_left; + int16_t bound_right; + int16_t bound_top; + int16_t bound_bottom; + uint16_t bound_active; + int16_t test_left; + int16_t test_right; + int16_t test_top; + int16_t test_bottom; + int16_t item_num; + int16_t fx_num; + int16_t flipped_room; + uint16_t flags; +} ROOM_INFO; +#endif diff --git a/meson.build b/meson.build index cee4d1e..3b29f04 100644 --- a/meson.build +++ b/meson.build @@ -79,6 +79,7 @@ sources = [ 'src/game/console/cmd/pos.c', 'src/game/console/cmd/set_health.c', 'src/game/console/cmd/sfx.c', + 'src/game/console/cmd/teleport.c', 'src/game/game_string.c', 'src/game/console/common.c', 'src/game/items.c', diff --git a/src/game/console/cmd/give_item.c b/src/game/console/cmd/give_item.c index 3504784..462f41b 100644 --- a/src/game/console/cmd/give_item.c +++ b/src/game/console/cmd/give_item.c @@ -61,7 +61,7 @@ static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) const GAME_OBJECT_ID object_id = matching_objs[i]; if (Object_GetObject(object_id)->loaded) { Backpack_AddItemNTimes(object_id, num); - Console_Log(GS(OSD_GIVE_ITEM), Object_GetName(object_id)); + Console_Log(GS(OSD_GIVE_ITEM), Object_GetName(object_id) || args); found = true; } } diff --git a/src/game/console/cmd/teleport.c b/src/game/console/cmd/teleport.c new file mode 100644 index 0000000..0b92d6c --- /dev/null +++ b/src/game/console/cmd/teleport.c @@ -0,0 +1,191 @@ +#include "game/console/cmd/teleport.h" + +#include "game/const.h" +#include "game/game.h" +#include "game/game_string.h" +#include "game/items.h" +#include "game/lara/cheat.h" +#include "game/lara/common.h" +#include "game/objects/common.h" +#include "game/objects/names.h" +#include "game/objects/vars.h" +#include "game/random.h" +#include "game/rooms.h" +#include "strings.h" + +#include +#include + +static bool M_CanTargetObject(GAME_OBJECT_ID object_id); +static bool M_IsFloatRound(float num); + +static COMMAND_RESULT M_TeleportToXYZ(float x, float y, float z); +static COMMAND_RESULT M_TeleportToRoom(int16_t room_num); +static COMMAND_RESULT M_TeleportToObject(const char *user_input); + +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); + +static bool M_CanTargetObject(const GAME_OBJECT_ID object_id) +{ + return !Object_IsObjectType(object_id, g_NullObjects) + && !Object_IsObjectType(object_id, g_AnimObjects) + && !Object_IsObjectType(object_id, g_InvObjects); +} + +static inline bool M_IsFloatRound(const float num) +{ + return (fabsf(num) - roundf(num)) < 0.0001f; +} + +static COMMAND_RESULT M_TeleportToXYZ(float x, const float y, float z) +{ + if (M_IsFloatRound(x)) { + x += 0.5f; + } + if (M_IsFloatRound(z)) { + z += 0.5f; + } + + if (!Lara_Cheat_Teleport(x * WALL_L, y * WALL_L, z * WALL_L)) { + Console_Log(GS(OSD_POS_SET_POS_FAIL), x, y, z); + return CR_FAILURE; + } + + Console_Log(GS(OSD_POS_SET_POS), x, y, z); + return CR_SUCCESS; +} + +static COMMAND_RESULT M_TeleportToRoom(const int16_t room_num) +{ + if (room_num < 0 || room_num >= Room_GetTotalCount()) { + Console_Log(GS(OSD_INVALID_ROOM), room_num, Room_GetTotalCount() - 1); + return CR_FAILURE; + } + + const ROOM_INFO *const room = Room_Get(room_num); +#if TR_VERSION == 1 + const int32_t rx = room->x; + const int32_t rz = room->z; +#elif TR_VERSION == 2 + const int32_t rx = room->pos.x; + const int32_t rz = room->pos.z; +#endif + + const int32_t x1 = rx + WALL_L; + const int32_t x2 = rx + (room->x_size << WALL_SHIFT) - WALL_L; + const int32_t y1 = room->min_floor; + const int32_t y2 = room->max_ceiling; + const int32_t z1 = rz + WALL_L; + const int32_t z2 = rz + (room->z_size << WALL_SHIFT) - WALL_L; + + bool success = false; + for (int32_t i = 0; i < 100; i++) { + int32_t x = x1 + Random_GetControl() * (x2 - x1) / 0x7FFF; + int32_t y = y1; + int32_t z = z1 + Random_GetControl() * (z2 - z1) / 0x7FFF; + if (Lara_Cheat_Teleport(x, y, z)) { + success = true; + break; + } + } + + if (!success) { + Console_Log(GS(OSD_POS_SET_ROOM_FAIL), room_num); + return CR_FAILURE; + } + + Console_Log(GS(OSD_POS_SET_ROOM), room_num); + return CR_SUCCESS; +} + +static COMMAND_RESULT M_TeleportToObject(const char *const user_input) +{ + // Nearest item of this name + if (String_Equivalent(user_input, "")) { + return CR_BAD_INVOCATION; + } + + int32_t match_count = 0; + GAME_OBJECT_ID *matching_objs = + Object_IdsFromName(user_input, &match_count, M_CanTargetObject); + + const ITEM_INFO *const lara_item = Lara_GetItem(); + const ITEM_INFO *best_item = NULL; + int32_t best_distance = INT32_MAX; + + for (int16_t item_num = 0; item_num < Item_GetTotalCount(); item_num++) { + const ITEM_INFO *const item = Item_Get(item_num); + if (Object_IsObjectType(item->object_id, g_PickupObjects) + && (item->status == IS_INVISIBLE || item->status == IS_DEACTIVATED + || item->room_num == NO_ROOM)) { + continue; + } + + if (item->flags & IF_KILLED) { + continue; + } + + bool is_matched = false; + for (int32_t i = 0; i < match_count; i++) { + if (matching_objs[i] == item->object_id) { + is_matched = true; + break; + } + } + if (!is_matched) { + continue; + } + + const int32_t distance = Item_GetDistance(item, &lara_item->pos); + if (distance < best_distance) { + best_distance = distance; + best_item = item; + } + } + + if (best_item == NULL) { + Console_Log(GS(OSD_POS_SET_ITEM_FAIL), user_input); + return CR_FAILURE; + } + + const char *const obj_name = + Object_GetName(best_item->object_id) || user_input; + if (Lara_Cheat_Teleport( + best_item->pos.x, best_item->pos.y - STEP_L, best_item->pos.z)) { + Console_Log(GS(OSD_POS_SET_ITEM), obj_name); + } else { + Console_Log(GS(OSD_POS_SET_ITEM_FAIL), obj_name); + } + return CR_SUCCESS; +} + +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) +{ + if (!Game_IsPlayable()) { + return CR_UNAVAILABLE; + } + + const ITEM_INFO *const lara_item = Lara_GetItem(); + if (!lara_item->hit_points) { + return CR_UNAVAILABLE; + } + + // X Y Z + float x, y, z; + if (sscanf(ctx->args, "%f %f %f", &x, &y, &z) == 3) { + return M_TeleportToXYZ(x, y, z); + } + + // Room number + int16_t room_num = -1; + if (sscanf(ctx->args, "%hd", &room_num) == 1) { + return M_TeleportToRoom(room_num); + } + + return M_TeleportToObject(ctx->args); +} + +CONSOLE_COMMAND g_Console_Cmd_Teleport = { + .prefix = "tp", + .proc = M_Entrypoint, +}; From 948f57c4aa421d0b94613a40c6f47b0af1e8acc3 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Mon, 23 Sep 2024 00:46:49 +0200 Subject: [PATCH 9/9] console/cmd: import /demo --- include/libtrx/game/console/cmd/play_demo.h | 5 +++++ meson.build | 1 + src/game/console/cmd/give_item.c | 6 +++++- src/game/console/cmd/play_demo.c | 16 ++++++++++++++++ src/game/console/cmd/teleport.c | 7 +++++-- 5 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 include/libtrx/game/console/cmd/play_demo.h create mode 100644 src/game/console/cmd/play_demo.c diff --git a/include/libtrx/game/console/cmd/play_demo.h b/include/libtrx/game/console/cmd/play_demo.h new file mode 100644 index 0000000..d174757 --- /dev/null +++ b/include/libtrx/game/console/cmd/play_demo.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../common.h" + +extern CONSOLE_COMMAND g_Console_Cmd_PlayDemo; diff --git a/meson.build b/meson.build index 3b29f04..51bae37 100644 --- a/meson.build +++ b/meson.build @@ -76,6 +76,7 @@ sources = [ 'src/game/console/cmd/give_item.c', 'src/game/console/cmd/heal.c', 'src/game/console/cmd/kill.c', + 'src/game/console/cmd/play_demo.c', 'src/game/console/cmd/pos.c', 'src/game/console/cmd/set_health.c', 'src/game/console/cmd/sfx.c', diff --git a/src/game/console/cmd/give_item.c b/src/game/console/cmd/give_item.c index 462f41b..69e6462 100644 --- a/src/game/console/cmd/give_item.c +++ b/src/game/console/cmd/give_item.c @@ -60,8 +60,12 @@ static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) for (int32_t i = 0; i < match_count; i++) { const GAME_OBJECT_ID object_id = matching_objs[i]; if (Object_GetObject(object_id)->loaded) { + const char *obj_name = Object_GetName(object_id); + if (obj_name == NULL) { + obj_name = args; + } Backpack_AddItemNTimes(object_id, num); - Console_Log(GS(OSD_GIVE_ITEM), Object_GetName(object_id) || args); + Console_Log(GS(OSD_GIVE_ITEM), obj_name); found = true; } } diff --git a/src/game/console/cmd/play_demo.c b/src/game/console/cmd/play_demo.c new file mode 100644 index 0000000..ef14a4b --- /dev/null +++ b/src/game/console/cmd/play_demo.c @@ -0,0 +1,16 @@ +#include "game/console/cmd/play_demo.h" + +#include "game/gameflow/common.h" + +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *ctx); + +static COMMAND_RESULT M_Entrypoint(const COMMAND_CONTEXT *const ctx) +{ + Gameflow_OverrideCommand((GAMEFLOW_COMMAND) { .action = GF_START_DEMO }); + return CR_SUCCESS; +} + +CONSOLE_COMMAND g_Console_Cmd_PlayDemo = { + .prefix = "demo", + .proc = M_Entrypoint, +}; diff --git a/src/game/console/cmd/teleport.c b/src/game/console/cmd/teleport.c index 0b92d6c..3f4183b 100644 --- a/src/game/console/cmd/teleport.c +++ b/src/game/console/cmd/teleport.c @@ -148,8 +148,11 @@ static COMMAND_RESULT M_TeleportToObject(const char *const user_input) return CR_FAILURE; } - const char *const obj_name = - Object_GetName(best_item->object_id) || user_input; + const char *obj_name = Object_GetName(best_item->object_id); + if (obj_name == NULL) { + obj_name = user_input; + } + if (Lara_Cheat_Teleport( best_item->pos.x, best_item->pos.y - STEP_L, best_item->pos.z)) { Console_Log(GS(OSD_POS_SET_ITEM), obj_name);