From 36f13b34dcadca23c08527805173bc1244d3f1e1 Mon Sep 17 00:00:00 2001 From: obligaron Date: Sat, 14 Aug 2021 13:13:25 +0200 Subject: [PATCH] Add debug text commands --- Source/control.cpp | 18 ++++++ Source/debug.cpp | 150 +++++++++++++++++++++++++++++++++++++++++++- Source/debug.h | 3 + Source/interfac.cpp | 3 +- 4 files changed, 169 insertions(+), 5 deletions(-) diff --git a/Source/control.cpp b/Source/control.cpp index c69b71295..eeb14cac7 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -32,6 +32,10 @@ #include "utils/sdl_geometry.h" #include "options.h" +#ifdef _DEBUG +#include "debug.h" +#endif + namespace devilution { /** * @brief Set if the life flask needs to be redrawn during next frame @@ -513,12 +517,18 @@ void ControlSetGoldCurs(PlayerStruct &player) void ResetTalkMsg() { +#ifdef _DEBUG + if (CheckDebugTextCommand(TalkMessage)) + return; +#endif + uint32_t pmask = 0; for (int i = 0; i < MAX_PLRS; i++) { if (WhisperList[i]) pmask |= 1 << i; } + NetSendCmdString(pmask, TalkMessage); } @@ -659,7 +669,11 @@ bool GetSpellListSelection(spell_id &pSpell, spell_type &pSplType) bool IsChatAvailable() { +#ifdef _DEBUG + return true; +#else return gbIsMultiplayer; +#endif } } // namespace @@ -2074,6 +2088,10 @@ void DiabloHotkeyMsg(uint32_t dwMsg) assert(dwMsg < QUICK_MESSAGE_OPTIONS); NetSendCmdString(0xFFFFFF, sgOptions.Chat.szHotKeyMsgs[dwMsg]); + +#ifdef _DEBUG + CheckDebugTextCommand(sgOptions.Chat.szHotKeyMsgs[dwMsg]); +#endif } } // namespace devilution diff --git a/Source/debug.cpp b/Source/debug.cpp index dcf074217..36c4e7631 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -4,7 +4,10 @@ * Implementation of debug functions. */ +#ifdef _DEBUG + #include "cursor.h" +#include "debug.h" #include "engine/cel_sprite.hpp" #include "engine/load_cel.hpp" #include "engine/point.hpp" @@ -16,8 +19,6 @@ namespace devilution { std::optional pSquareCel; -#ifdef _DEBUG - namespace { int DebugPlayerId; @@ -58,6 +59,122 @@ void PrintDebugMonster(int m) NetSendCmdString(1 << MyPlayerId, dstr); } +struct DebugCmdItem { + const std::string_view text; + const std::string_view description; + const std::string_view requiredParameter; + std::string (*actionProc)(const std::string_view); +}; + +extern std::vector DebugCmdList; + +std::string DebugCmdHelp(const std::string_view parameter) +{ + if (parameter.empty()) { + std::string ret = "Available Debug Commands: "; + int lenCurrentLine = ret.length(); + bool first = true; + for (const auto &dbgCmd : DebugCmdList) { + if ((dbgCmd.text.length() + lenCurrentLine + 2) > MAX_SEND_STR_LEN) { + ret.append("\n"); + lenCurrentLine = dbgCmd.text.length(); + } else { + if (first) + first = false; + else + ret.append(" - "); + lenCurrentLine += (dbgCmd.text.length() + 2); + } + ret.append(dbgCmd.text); + } + return ret; + } else { + auto debugCmdIterator = std::find_if(DebugCmdList.begin(), DebugCmdList.end(), [&](const DebugCmdItem &elem) { return elem.text == parameter; }); + if (debugCmdIterator == DebugCmdList.end()) + return fmt::format("Debug command {} wasn't found", parameter); + auto &dbgCmdItem = *debugCmdIterator; + if (dbgCmdItem.requiredParameter.empty()) + return fmt::format("Description: {}\nParameters: No additional parameter needed.", dbgCmdItem.description); + return fmt::format("Description: {}\nParameters: {}", dbgCmdItem.description, dbgCmdItem.requiredParameter); + } +} + +std::string DebugCmdGiveGoldCheat(const std::string_view parameter) +{ + GiveGoldCheat(); + return "You are now rich! If only this was as easy in real life..."; +} + +std::string DebugCmdTakeGoldCheat(const std::string_view parameter) +{ + TakeGoldCheat(); + return "You are poor..."; +} + +std::string DebugCmdWarpToLevel(const std::string_view parameter) +{ + auto &myPlayer = Players[MyPlayerId]; + auto level = atoi(parameter.data()); + if (level < 0 || level > (gbIsHellfire ? 24 : 16)) + return fmt::format("Level {} is not known. Do you want to write an extension mod?", level); + if (myPlayer.plrlevel == level) + return fmt::format("I did nothing but fulfilled your wish. You are already at level {}.", level); + StartNewLvl(MyPlayerId, (level != 21) ? interface_mode::WM_DIABNEXTLVL : interface_mode::WM_DIABTWARPUP, level); + return fmt::format("Welcome to level {}.", level); +} + +std::string DebugCmdResetLevel(const std::string_view parameter) +{ + auto &myPlayer = Players[MyPlayerId]; + auto level = atoi(parameter.data()); + if (level < 0 || level > (gbIsHellfire ? 24 : 16)) + return fmt::format("Level {} is not known. Do you want to write an extension mod?", level); + myPlayer._pLvlVisited[level] = false; + if (myPlayer.plrlevel == level) + return fmt::format("Level {} can't be cleaned, cause you still occupy it!", level); + return fmt::format("Level {} was restored and looks fabulous.", level); +} + +std::string DebugCmdGodMode(const std::string_view parameter) +{ + debug_mode_key_inverted_v = !debug_mode_key_inverted_v; + if (debug_mode_key_inverted_v) + return "A god descended."; + return "You are mortal, beware of the darkness."; +} + +std::string DebugCmdLevelUp(const std::string_view parameter) +{ + int levels = std::max(1, atoi(parameter.data())); + for (int i = 0; i < levels; i++) + NetSendCmd(true, CMD_CHEAT_EXPERIENCE); + return "New experience leads to new insights."; +} + +std::string DebugCmdGetAllSpells(const std::string_view parameter) +{ + SetAllSpellsCheat(); + return "Magic is no mystery for me."; +} + +std::string DebugCmdMaxSpellLevel(const std::string_view parameter) +{ + MaxSpellsCheat(); + return "Knowledge is power."; +} + +std::vector DebugCmdList = { + { "help", "Prints help overview or help for a specific command.", "({command})", &DebugCmdHelp }, + { "give gold", "Fills the inventory with gold.", "", &DebugCmdGiveGoldCheat }, + { "give xp", "Levels the player up (min 1 level or {levels}).", "({levels})", &DebugCmdLevelUp }, + { "give spells", "Add all spells to player.", "", &DebugCmdGetAllSpells }, + { "give spells 10", "Set spell level to 10 for all spells.", "", &DebugCmdMaxSpellLevel }, + { "take gold", "Removes all gold from inventory.", "", &DebugCmdTakeGoldCheat }, + { "changelevel", "Moves to specifided {level} (use 0 for town).", "{level}", &DebugCmdWarpToLevel }, + { "restart", "Resets specified {level}.", "{level}", &DebugCmdResetLevel }, + { "god", "Togggles godmode.", "", &DebugCmdGodMode }, +}; + } // namespace void LoadDebugGFX() @@ -212,6 +329,33 @@ void NextDebugMonster() NetSendCmdString(1 << MyPlayerId, dstr); } -#endif +bool CheckDebugTextCommand(const std::string_view text) +{ + auto debugCmdIterator = std::find_if(DebugCmdList.begin(), DebugCmdList.end(), [&](const DebugCmdItem &elem) { return text.find(elem.text) == 0; }); + if (debugCmdIterator == DebugCmdList.end()) + return false; + + auto &dbgCmd = *debugCmdIterator; + std::string_view parameter = ""; + if (text.length() > (dbgCmd.text.length() + 1)) + parameter = text.substr(dbgCmd.text.length() + 1); + const auto result = dbgCmd.actionProc(parameter); + + const std::string delim = "\n"; + auto start = 0U; + auto end = result.find(delim); + while (end != std::string::npos) { + const auto line = result.substr(start, end - start); + NetSendCmdString(1 << MyPlayerId, line.c_str()); + start = end + delim.length(); + end = result.find(delim, start); + } + if (start != result.length()) + NetSendCmdString(1 << MyPlayerId, result.substr(start).c_str()); + + return true; +} } // namespace devilution + +#endif diff --git a/Source/debug.h b/Source/debug.h index 8a0816a8f..52a8977b8 100644 --- a/Source/debug.h +++ b/Source/debug.h @@ -5,6 +5,8 @@ */ #pragma once +#include + #include "engine.h" #include "miniwin/miniwin.h" #include "utils/stdcompat/optional.hpp" @@ -23,5 +25,6 @@ void PrintDebugPlayer(bool bNextPlayer); void PrintDebugQuest(); void GetDebugMonster(); void NextDebugMonster(); +bool CheckDebugTextCommand(const std::string_view text); } // namespace devilution diff --git a/Source/interfac.cpp b/Source/interfac.cpp index 045a96a1c..0d665d9ac 100644 --- a/Source/interfac.cpp +++ b/Source/interfac.cpp @@ -267,9 +267,8 @@ void ShowProgress(interface_mode uMsg) } IncProgress(); FreeGameMem(); - currlevel++; + currlevel = myPlayer.plrlevel; leveltype = gnLevelTypeTbl[currlevel]; - assert(myPlayer.plrlevel == currlevel); IncProgress(); LoadGameLevel(false, ENTRY_MAIN); IncProgress();