Browse Source

Migrate some debug.cpp commands to Lua

Introduces a new `devilutionx.dev` Lua module, automatically loaded in
the console prelude.

Arguments and basic help are shown in autocompletion.
pull/6776/head
Gleb Mazovetskiy 2 years ago
parent
commit
8bffbedcb4
  1. 1
      Packaging/resources/assets/lua/repl_prelude.lua
  2. 2
      Source/CMakeLists.txt
  3. 253
      Source/debug.cpp
  4. 68
      Source/lua/autocomplete.cpp
  5. 13
      Source/lua/lua.cpp
  6. 46
      Source/lua/metadoc.hpp
  7. 12
      Source/lua/modules/audio.cpp
  8. 197
      Source/lua/modules/dev.cpp
  9. 10
      Source/lua/modules/dev.hpp
  10. 78
      Source/lua/modules/dev/quests.cpp
  11. 10
      Source/lua/modules/dev/quests.hpp
  12. 11
      Source/panels/console.cpp

1
Packaging/resources/assets/lua/repl_prelude.lua

@ -2,4 +2,5 @@ log = require('devilutionx.log')
audio = require('devilutionx.audio')
render = require('devilutionx.render')
message = require('devilutionx.message')
if _DEBUG then dev = require('devilutionx.dev') end
inspect = require('inspect')

2
Source/CMakeLists.txt

@ -136,6 +136,8 @@ set(libdevilutionx_SRCS
lua/lua.cpp
lua/repl.cpp
lua/modules/audio.cpp
lua/modules/dev.cpp
lua/modules/dev/quests.cpp
lua/modules/render.cpp
lua/modules/log.cpp

253
Source/debug.cpp

@ -153,92 +153,6 @@ std::string DebugCmdHelp(const std::string_view parameter)
return StrCat("Description: ", dbgCmdItem.description, "\nParameters: ", dbgCmdItem.requiredParameter);
}
std::string DebugCmdGiveGoldCheat(const std::string_view parameter)
{
Player &myPlayer = *MyPlayer;
std::string cmdLabel = "[givegold] ";
int goldToAdd = 0;
if (parameter.empty())
goldToAdd = GOLD_MAX_LIMIT * InventoryGridCells;
else
goldToAdd = ParseInt<int>(parameter, /*min=*/1).value_or(1);
const int goldAmountBefore = myPlayer._pGold;
for (int8_t &itemIndex : myPlayer.InvGrid) {
if (itemIndex < 0)
continue;
Item &item = myPlayer.InvList[itemIndex != 0 ? itemIndex - 1 : myPlayer._pNumInv];
if (itemIndex != 0) {
if ((!item.isGold() && !item.isEmpty()) || (item.isGold() && item._ivalue == GOLD_MAX_LIMIT))
continue;
} else {
if (item.isEmpty()) {
MakeGoldStack(item, 0);
myPlayer._pNumInv++;
itemIndex = myPlayer._pNumInv;
}
}
int goldThatCanBeAdded = (GOLD_MAX_LIMIT - item._ivalue);
if (goldThatCanBeAdded >= goldToAdd) {
item._ivalue += goldToAdd;
myPlayer._pGold += goldToAdd;
break;
}
item._ivalue += goldThatCanBeAdded;
goldToAdd -= goldThatCanBeAdded;
myPlayer._pGold += goldThatCanBeAdded;
}
CalcPlrInv(myPlayer, true);
return StrCat(cmdLabel, "Set your gold to ", myPlayer._pGold, ", added ", myPlayer._pGold - goldAmountBefore, ".");
}
std::string DebugCmdTakeGoldCheat(const std::string_view parameter)
{
Player &myPlayer = *MyPlayer;
std::string cmdLabel = "[takegold] ";
int goldToRemove = 0;
if (parameter.empty())
goldToRemove = GOLD_MAX_LIMIT * InventoryGridCells;
else
goldToRemove = ParseInt<int>(parameter, /*min=*/1).value_or(1);
const int goldAmountBefore = myPlayer._pGold;
for (auto itemIndex : myPlayer.InvGrid) {
itemIndex -= 1;
if (itemIndex < 0)
continue;
Item &item = myPlayer.InvList[itemIndex];
if (!item.isGold())
continue;
if (item._ivalue >= goldToRemove) {
myPlayer._pGold -= goldToRemove;
item._ivalue -= goldToRemove;
if (item._ivalue == 0)
myPlayer.RemoveInvItem(itemIndex);
break;
}
myPlayer._pGold -= item._ivalue;
goldToRemove -= item._ivalue;
myPlayer.RemoveInvItem(itemIndex);
}
return StrCat(cmdLabel, "Set your gold to ", myPlayer._pGold, ", removed ", goldAmountBefore - myPlayer._pGold, ".");
}
std::string DebugCmdWarpToLevel(const std::string_view parameter)
{
Player &myPlayer = *MyPlayer;
@ -521,28 +435,6 @@ std::string DebugCmdLighting(const std::string_view parameter)
return StrCat(cmdLabel, "Fullbright: ", DisableLighting ? "On" : "Off");
}
std::string DebugCmdMapReveal(const std::string_view parameter)
{
std::string cmdLabel = "[givemap] ";
for (int x = 0; x < DMAXX; x++)
for (int y = 0; y < DMAXY; y++)
UpdateAutomapExplorer({ x, y }, MAP_EXP_SHRINE);
return StrCat(cmdLabel, "Automap fully explored.");
}
std::string DebugCmdMapHide(const std::string_view parameter)
{
std::string cmdLabel = "[takemap] ";
for (int x = 0; x < DMAXX; x++)
for (int y = 0; y < DMAXY; y++)
AutomapView[x][y] = MAP_EXP_NONE;
return StrCat(cmdLabel, "Automap exploration removed.");
}
std::string DebugCmdVision(const std::string_view parameter)
{
std::string cmdLabel = "[drawvision] ";
@ -561,106 +453,6 @@ std::string DebugCmdPath(const std::string_view parameter)
return StrCat(cmdLabel, "Path highlighting: ", DebugPath ? "On" : "Off");
}
std::string DebugCmdQuest(const std::string_view parameter)
{
std::string cmdLabel = "[givequest] ";
if (parameter.empty()) {
std::string ret = StrCat(cmdLabel, "Missing quest id! (This can be: all)");
for (auto &quest : Quests) {
if (IsNoneOf(quest._qactive, QUEST_NOTAVAIL, QUEST_INIT))
continue;
StrAppend(ret, ", ", quest._qidx, " (", QuestsData[quest._qidx]._qlstr, ")");
}
return ret;
}
if (parameter.compare("all") == 0) {
for (auto &quest : Quests) {
if (IsNoneOf(quest._qactive, QUEST_NOTAVAIL, QUEST_INIT))
continue;
quest._qactive = QUEST_ACTIVE;
quest._qlog = true;
}
return StrCat(cmdLabel, "Activated all quests.");
}
const ParseIntResult<int> parsedArg = ParseInt<int>(parameter, /*min=*/0);
if (!parsedArg.has_value())
return StrCat(cmdLabel, "Failed to parse argument as integer!");
const int questId = parsedArg.value();
if (questId >= MAXQUESTS)
return StrCat(cmdLabel, "Quest ", questId, " does not exist!");
auto &quest = Quests[questId];
if (IsNoneOf(quest._qactive, QUEST_NOTAVAIL, QUEST_INIT))
return StrCat(cmdLabel, QuestsData[questId]._qlstr, " is already active!");
quest._qactive = QUEST_ACTIVE;
quest._qlog = true;
return StrCat(cmdLabel, QuestsData[questId]._qlstr, " activated.");
}
std::string DebugCmdLevelUp(const std::string_view parameter)
{
Player &myPlayer = *MyPlayer;
std::string cmdLabel = "[givelvl] ";
const int levels = std::min<int>(ParseInt<int>(parameter, /*min=*/1).value_or(1), GetMaximumCharacterLevel() - myPlayer.getCharacterLevel());
for (int i = 0; i < levels; i++)
NetSendCmd(true, CMD_CHEAT_EXPERIENCE);
return StrCat(cmdLabel, "New character level: ", myPlayer.getCharacterLevel() + levels);
}
std::string DebugCmdMaxStats(const std::string_view parameter)
{
Player &myPlayer = *MyPlayer;
std::string cmdLabel = "[maxstats] ";
ModifyPlrStr(myPlayer, myPlayer.GetMaximumAttributeValue(CharacterAttribute::Strength) - myPlayer._pBaseStr);
ModifyPlrMag(myPlayer, myPlayer.GetMaximumAttributeValue(CharacterAttribute::Magic) - myPlayer._pBaseMag);
ModifyPlrDex(myPlayer, myPlayer.GetMaximumAttributeValue(CharacterAttribute::Dexterity) - myPlayer._pBaseDex);
ModifyPlrVit(myPlayer, myPlayer.GetMaximumAttributeValue(CharacterAttribute::Vitality) - myPlayer._pBaseVit);
return StrCat(cmdLabel, "Set all character base attributes to maximum.");
}
std::string DebugCmdMinStats(const std::string_view parameter)
{
Player &myPlayer = *MyPlayer;
std::string cmdLabel = "[minstats] ";
ModifyPlrStr(myPlayer, -myPlayer._pBaseStr);
ModifyPlrMag(myPlayer, -myPlayer._pBaseMag);
ModifyPlrDex(myPlayer, -myPlayer._pBaseDex);
ModifyPlrVit(myPlayer, -myPlayer._pBaseVit);
return StrCat(cmdLabel, "Set all character base attributes to minimum.");
}
std::string DebugCmdSetSpellsLevel(const std::string_view parameter)
{
std::string cmdLabel = "[setspells] ";
const ParseIntResult<uint8_t> parsedArg = ParseInt<uint8_t>(parameter);
if (!parsedArg.has_value())
return StrCat(cmdLabel, "Failed to parse argument as uint8_t!");
const uint8_t level = parsedArg.value();
for (uint8_t i = static_cast<uint8_t>(SpellID::Firebolt); i < MAX_SPELLS; i++) {
if (GetSpellBookLevel(static_cast<SpellID>(i)) != -1) {
NetSendCmdParam2(true, CMD_CHANGE_SPELL_LEVEL, i, level);
}
}
if (level == 0)
MyPlayer->_pMemSpells = 0;
return StrCat(cmdLabel, "Set all spell levels to ", level);
}
std::string DebugCmdRefillHealthMana(const std::string_view parameter)
{
std::string cmdLabel = "[fill] ";
@ -773,15 +565,6 @@ std::string DebugCmdTalkToTowner(const std::string_view parameter)
return StrCat(cmdLabel, "Towner not found!");
}
std::string DebugCmdShowGrid(const std::string_view parameter)
{
std::string cmdLabel = "[grid] ";
DebugGrid = !DebugGrid;
return StrCat(cmdLabel, "Tile grid highlighting: ", DebugGrid ? "On" : "Off");
}
std::string DebugCmdSpawnUniqueMonster(const std::string_view parameter)
{
std::string cmdLabel = "[spawnu] ";
@ -1079,31 +862,6 @@ std::string DebugCmdItemInfo(const std::string_view parameter)
return StrCat(cmdLabel, "Numitems: ", ActiveItemCount);
}
std::string DebugCmdQuestInfo(const std::string_view parameter)
{
std::string cmdLabel = "[questinfo] ";
if (parameter.empty()) {
std::string ret = StrCat(cmdLabel, "You must provide an id! This could be: ");
for (auto &quest : Quests) {
if (IsNoneOf(quest._qactive, QUEST_NOTAVAIL, QUEST_INIT))
continue;
StrAppend(ret, ", ", quest._qidx, " (", QuestsData[quest._qidx]._qlstr, ")");
}
return ret;
}
const ParseIntResult<int> parsedArg = ParseInt<int>(parameter, /*min=*/0);
if (!parsedArg.has_value())
return StrCat(cmdLabel, "Failed to parse argument as integer!");
const int questId = parsedArg.value();
if (questId >= MAXQUESTS)
return StrCat(cmdLabel, "Quest ", questId, " does not exist!");
auto &quest = Quests[questId];
return StrCat(cmdLabel, "\nQuest: ", QuestsData[quest._qidx]._qlstr, "\nActive: ", quest._qactive, " Var1: ", quest._qvar1, " Var2: ", quest._qvar2);
}
std::string DebugCmdPlayerInfo(const std::string_view parameter)
{
std::string cmdLabel = "[playerinfo] ";
@ -1235,15 +993,6 @@ std::string DebugCmdClearSearch(const std::string_view parameter)
std::vector<DebugCmdItem> DebugCmdList = {
{ "help", "Prints help overview or help for a specific command.", "({command})", &DebugCmdHelp },
{ "givegold", "Gives player {amount} of gold or max amount if didn't specify.", "{amount}", &DebugCmdGiveGoldCheat },
{ "givelvl", "Levels the player up (min 1 level or {levels}).", "({levels})", &DebugCmdLevelUp },
{ "maxstats", "Sets all stat values to maximum.", "", &DebugCmdMaxStats },
{ "minstats", "Sets all stat values to minimum.", "", &DebugCmdMinStats },
{ "setspells", "Set spell level to {level} for all spells.", "{level}", &DebugCmdSetSpellsLevel },
{ "takegold", "Removes {amount} gold or if didn't specify - all gold from inventory.", "{amount}", &DebugCmdTakeGoldCheat },
{ "givequest", "Enable a given quest.", "({id})", &DebugCmdQuest },
{ "givemap", "Reveal the map.", "", &DebugCmdMapReveal },
{ "takemap", "Hide the map.", "", &DebugCmdMapHide },
{ "goto", "Moves to specifided {level} (use 0 for town).", "{level}", &DebugCmdWarpToLevel },
{ "questmap", "Load a quest level {level}.", "{level}", &DebugCmdLoadQuestMap },
{ "map", "Load custom level from a given {path}.dun.", "{path} {type} {x} {y}", &DebugCmdLoadMap },
@ -1262,13 +1011,11 @@ std::vector<DebugCmdItem> DebugCmdList = {
{ "talkto", "Interacts with a NPC whose name contains {name}.", "{name}", &DebugCmdTalkToTowner },
{ "exit", "Exits the game.", "", &DebugCmdExit },
{ "arrow", "Changes arrow effect (normal, fire, lightning, explosion).", "{effect}", &DebugCmdArrow },
{ "grid", "Toggles showing grid.", "", &DebugCmdShowGrid },
{ "spawnu", "Spawns unique monster {name}.", "{name} ({count})", &DebugCmdSpawnUniqueMonster },
{ "spawn", "Spawns monster {name}.", "{name} ({count})", &DebugCmdSpawnMonster },
{ "tiledata", "Toggles showing tile data {name} (leave name empty to see a list).", "{name}", &DebugCmdShowTileData },
{ "scrollview", "Toggles scroll view feature (with shift+mouse).", "", &DebugCmdScrollView },
{ "iteminfo", "Shows info of currently selected item.", "", &DebugCmdItemInfo },
{ "questinfo", "Shows info of quests.", "{id}", &DebugCmdQuestInfo },
{ "playerinfo", "Shows info of player.", "{playerid}", &DebugCmdPlayerInfo },
{ "fps", "Toggles displaying FPS", "", &DebugCmdToggleFPS },
{ "trn", "Makes player use TRN {trn} - Write 'plr' before it to look in plrgfx\\ or 'mon' to look in monsters\\monsters\\ - example: trn plr infra is equal to 'plrgfx\\infra.trn'", "{trn}", &DebugCmdChangeTRN },

68
Source/lua/autocomplete.cpp

@ -9,7 +9,10 @@
#include <sol/sol.hpp>
#include "lua/metadoc.hpp"
#include "utils/algorithm/container.hpp"
#include "utils/str_cat.hpp"
#include "utils/str_split.hpp"
namespace devilution {
@ -20,23 +23,39 @@ std::string_view GetLastToken(std::string_view text)
if (text.empty())
return {};
size_t i = text.size();
while (i > 0 && text[i - 1] != ' ')
while (i > 0 && text[i - 1] != ' ' && text[i - 1] != '(' && text[i - 1] != ',')
--i;
return text.substr(i);
}
bool IsCallable(const sol::object &value)
struct ValueInfo {
bool callable = false;
std::string signature;
std::string docstring;
};
ValueInfo GetValueInfo(const sol::table &table, std::string_view key, const sol::object &value)
{
if (value.get_type() == sol::type::function)
return true;
if (!value.is<sol::table>())
return false;
const auto table = value.as<sol::table>();
const auto metatable = table.get<std::optional<sol::object>>(sol::metatable_key);
ValueInfo info;
if (std::optional<std::string> signature = GetSignature(table, key); signature.has_value()) {
info.signature = *std::move(signature);
}
if (std::optional<std::string> docstring = GetDocstring(table, key); docstring.has_value()) {
info.docstring = *std::move(docstring);
}
if (value.get_type() == sol::type::function) {
info.callable = true;
return info;
}
if (!value.is<sol::table>()) return info;
const auto valueAsTable = value.as<sol::table>();
const auto metatable = valueAsTable.get<std::optional<sol::object>>(sol::metatable_key);
if (!metatable || !metatable->is<sol::table>())
return false;
const auto callFn = metatable->as<sol::table>().get<std::optional<sol::object>>(sol::meta_function::call);
return callFn.has_value();
return info;
const auto metatableTbl = metatable->as<sol::table>();
const auto callFn = metatableTbl.get<std::optional<sol::object>>(sol::meta_function::call);
info.callable = callFn.has_value();
return info;
}
void SuggestionsFromTable(const sol::table &table, std::string_view prefix,
@ -54,12 +73,23 @@ void SuggestionsFromTable(const sol::table &table, std::string_view prefix,
|| keyStr.find("") != std::string::npos
|| keyStr.find("🔩") != std::string::npos)
continue;
ValueInfo info = GetValueInfo(table, keyStr, value);
std::string completionText = keyStr.substr(prefix.size());
LuaAutocompleteSuggestion suggestion { std::move(keyStr), std::move(completionText) };
if (IsCallable(value)) {
if (info.callable) {
suggestion.completionText.append("()");
suggestion.cursorAdjust = -1;
}
if (!info.signature.empty()) {
StrAppend(suggestion.displayText, info.signature);
}
if (!info.docstring.empty()) {
std::string_view firstLine = info.docstring;
if (const size_t newlinePos = firstLine.find('\n'); newlinePos != std::string_view::npos) {
firstLine = firstLine.substr(0, newlinePos);
}
StrAppend(suggestion.displayText, " - ", firstLine);
}
out.insert(std::move(suggestion));
if (out.size() == maxSuggestions)
break;
@ -77,9 +107,10 @@ void GetLuaAutocompleteSuggestions(std::string_view text, const sol::environment
size_t maxSuggestions, std::vector<LuaAutocompleteSuggestion> &out)
{
out.clear();
if (text.empty())
return;
if (text.empty()) return;
std::string_view token = GetLastToken(text);
const char prevChar = token.data() == text.data() ? '\0' : *(token.data() - 1);
if (prevChar == '(' || prevChar == ',') return;
const size_t dotPos = token.rfind('.');
const std::string_view prefix = token.substr(dotPos + 1);
token.remove_suffix(token.size() - (dotPos == std::string_view::npos ? 0 : dotPos));
@ -96,9 +127,12 @@ void GetLuaAutocompleteSuggestions(std::string_view text, const sol::environment
addSuggestions(fallback->as<sol::table>());
}
} else {
const auto obj = lua.traverse_get<std::optional<sol::object>>(token);
if (!obj.has_value())
return;
std::optional<sol::object> obj = lua;
for (const std::string_view part : SplitByChar(token, '.')) {
obj = obj->as<sol::table>().get<std::optional<sol::object>>(part);
if (!obj.has_value() || obj->get_type() != sol::type::table)
return;
}
if (obj->get_type() == sol::type::table) {
addSuggestions(obj->as<sol::table>());
}

13
Source/lua/lua.cpp

@ -18,6 +18,10 @@
#include "utils/log.hpp"
#include "utils/str_cat.hpp"
#ifdef _DEBUG
#include "lua/modules/dev.hpp"
#endif
namespace devilution {
namespace {
@ -172,11 +176,20 @@ void LuaInitialize()
// Registering globals
lua.set(
"print", LuaPrint,
"_DEBUG",
#ifdef _DEBUG
true,
#else
false,
#endif
"_VERSION", LUA_VERSION);
// Registering devilutionx object table
CheckResult(lua.safe_script(RequireGenSrc), /*optional=*/false);
const sol::table loaded = lua.create_table_with(
#ifdef _DEBUG
"devilutionx.dev", LuaDevModule(lua),
#endif
"devilutionx.version", PROJECT_VERSION,
"devilutionx.log", LuaLogModule(lua),
"devilutionx.audio", LuaAudioModule(lua),

46
Source/lua/metadoc.hpp

@ -0,0 +1,46 @@
#pragma once
#include <sol/sol.hpp>
#include <string_view>
#include "utils/str_cat.hpp"
namespace devilution {
inline std::string LuaSignatureKey(std::string_view key)
{
return StrCat("__sig_", key);
}
inline std::string LuaDocstringKey(std::string_view key)
{
return StrCat("__doc_", key);
}
template <typename T>
void SetWithSignatureAndDoc(sol::table &table, std::string_view key, std::string_view signature, std::string_view doc, T &&value)
{
table[key] = std::forward<T>(value);
table[LuaSignatureKey(key)] = signature;
table[LuaDocstringKey(key)] = doc;
}
template <typename T>
void SetWithSignature(sol::table &table, std::string_view key, std::string_view signature, T &&value)
{
table[key] = std::forward<T>(value);
table[LuaSignatureKey(key)] = signature;
}
inline std::optional<std::string> GetSignature(const sol::table &table, std::string_view key)
{
return table.get<std::optional<std::string>>(LuaSignatureKey(key));
}
inline std::optional<std::string> GetDocstring(const sol::table &table, std::string_view key)
{
return table.get<std::optional<std::string>>(LuaDocstringKey(key));
}
} // namespace devilution

12
Source/lua/modules/audio.cpp

@ -3,6 +3,7 @@
#include <sol/sol.hpp>
#include "effects.h"
#include "lua/metadoc.hpp"
namespace devilution {
@ -17,9 +18,14 @@ bool IsValidSfx(int16_t psfx)
sol::table LuaAudioModule(sol::state_view &lua)
{
return lua.create_table_with(
"playSfx", [](int psfx) { if (IsValidSfx(psfx)) PlaySFX(static_cast<SfxID>(psfx)); },
"playSfxLoc", [](int psfx, int x, int y) { if (IsValidSfx(psfx)) PlaySfxLoc(static_cast<SfxID>(psfx), { x, y }); });
sol::table table = lua.create_table();
SetWithSignature(table,
"playSfx", "(id: number)",
[](int16_t psfx) { if (IsValidSfx(psfx)) PlaySFX(static_cast<SfxID>(psfx)); });
SetWithSignature(table,
"playSfxLoc", "(id: number, x: number, y: number)",
[](int16_t psfx, int x, int y) { if (IsValidSfx(psfx)) PlaySfxLoc(static_cast<SfxID>(psfx), { x, y }); });
return table;
}
} // namespace devilution

197
Source/lua/modules/dev.cpp

@ -0,0 +1,197 @@
#ifdef _DEBUG
#include "lua/modules/dev.hpp"
#include <sol/sol.hpp>
#include "automap.h"
#include "debug.h"
#include "items.h"
#include "lua/metadoc.hpp"
#include "lua/modules/dev/quests.hpp"
#include "player.h"
#include "spells.h"
#include "utils/str_cat.hpp"
namespace devilution {
namespace {
std::string DebugCmdShowGrid()
{
DebugGrid = !DebugGrid;
return StrCat("Tile grid highlighting: ", DebugGrid ? "On" : "Off");
}
std::string DebugCmdLevelUp(int levels = 1)
{
if (levels <= 0) return "amount must be positive";
Player &myPlayer = *MyPlayer;
for (int i = 0; i < levels; i++)
NetSendCmd(true, CMD_CHEAT_EXPERIENCE);
return StrCat("New character level: ", myPlayer.getCharacterLevel() + levels);
}
std::string DebugCmdMaxStats()
{
Player &myPlayer = *MyPlayer;
ModifyPlrStr(myPlayer, myPlayer.GetMaximumAttributeValue(CharacterAttribute::Strength) - myPlayer._pBaseStr);
ModifyPlrMag(myPlayer, myPlayer.GetMaximumAttributeValue(CharacterAttribute::Magic) - myPlayer._pBaseMag);
ModifyPlrDex(myPlayer, myPlayer.GetMaximumAttributeValue(CharacterAttribute::Dexterity) - myPlayer._pBaseDex);
ModifyPlrVit(myPlayer, myPlayer.GetMaximumAttributeValue(CharacterAttribute::Vitality) - myPlayer._pBaseVit);
return "Set all character base attributes to maximum.";
}
std::string DebugCmdMinStats()
{
Player &myPlayer = *MyPlayer;
ModifyPlrStr(myPlayer, -myPlayer._pBaseStr);
ModifyPlrMag(myPlayer, -myPlayer._pBaseMag);
ModifyPlrDex(myPlayer, -myPlayer._pBaseDex);
ModifyPlrVit(myPlayer, -myPlayer._pBaseVit);
return "Set all character base attributes to minimum.";
}
std::string DebugCmdGiveGoldCheat(int goldToAdd = GOLD_MAX_LIMIT * InventoryGridCells)
{
if (goldToAdd <= 0) return "amount must be positive";
Player &myPlayer = *MyPlayer;
const int goldAmountBefore = myPlayer._pGold;
for (int8_t &itemIndex : myPlayer.InvGrid) {
if (itemIndex < 0)
continue;
Item &item = myPlayer.InvList[itemIndex != 0 ? itemIndex - 1 : myPlayer._pNumInv];
if (itemIndex != 0) {
if ((!item.isGold() && !item.isEmpty()) || (item.isGold() && item._ivalue == GOLD_MAX_LIMIT))
continue;
} else {
if (item.isEmpty()) {
MakeGoldStack(item, 0);
myPlayer._pNumInv++;
itemIndex = myPlayer._pNumInv;
}
}
int goldThatCanBeAdded = (GOLD_MAX_LIMIT - item._ivalue);
if (goldThatCanBeAdded >= goldToAdd) {
item._ivalue += goldToAdd;
myPlayer._pGold += goldToAdd;
break;
}
item._ivalue += goldThatCanBeAdded;
goldToAdd -= goldThatCanBeAdded;
myPlayer._pGold += goldThatCanBeAdded;
}
CalcPlrInv(myPlayer, true);
return StrCat("Set your gold to ", myPlayer._pGold, ", added ", myPlayer._pGold - goldAmountBefore, ".");
}
std::string DebugCmdTakeGoldCheat(int goldToRemove = GOLD_MAX_LIMIT * InventoryGridCells)
{
Player &myPlayer = *MyPlayer;
if (goldToRemove <= 0) return "amount must be positive";
const int goldAmountBefore = myPlayer._pGold;
for (auto itemIndex : myPlayer.InvGrid) {
itemIndex -= 1;
if (itemIndex < 0)
continue;
Item &item = myPlayer.InvList[itemIndex];
if (!item.isGold())
continue;
if (item._ivalue >= goldToRemove) {
myPlayer._pGold -= goldToRemove;
item._ivalue -= goldToRemove;
if (item._ivalue == 0)
myPlayer.RemoveInvItem(itemIndex);
break;
}
myPlayer._pGold -= item._ivalue;
goldToRemove -= item._ivalue;
myPlayer.RemoveInvItem(itemIndex);
}
return StrCat("Set your gold to ", myPlayer._pGold, ", removed ", goldAmountBefore - myPlayer._pGold, ".");
}
std::string DebugCmdSetSpellsLevel(uint8_t level)
{
for (uint8_t i = static_cast<uint8_t>(SpellID::Firebolt); i < MAX_SPELLS; i++) {
if (GetSpellBookLevel(static_cast<SpellID>(i)) != -1) {
NetSendCmdParam2(true, CMD_CHANGE_SPELL_LEVEL, i, level);
}
}
if (level == 0)
MyPlayer->_pMemSpells = 0;
return StrCat("Set all spell levels to ", level);
}
std::string DebugCmdMapReveal()
{
for (int x = 0; x < DMAXX; x++)
for (int y = 0; y < DMAXY; y++)
UpdateAutomapExplorer({ x, y }, MAP_EXP_SHRINE);
return "Automap fully explored.";
}
std::string DebugCmdMapHide()
{
for (int x = 0; x < DMAXX; x++)
for (int y = 0; y < DMAXY; y++)
AutomapView[x][y] = MAP_EXP_NONE;
return "Automap exploration removed.";
}
} // namespace
sol::table LuaDevModule(sol::state_view &lua)
{
sol::table table = lua.create_table();
SetWithSignatureAndDoc(table, "grid", "()",
"Toggles showing grid.", &DebugCmdShowGrid);
SetWithSignatureAndDoc(table, "giveGold", "(amount: number = MAX)",
"Gives the player gold.",
sol::overload(
[]() { return DebugCmdGiveGoldCheat(); },
[](int amount) { return DebugCmdGiveGoldCheat(amount); }));
SetWithSignatureAndDoc(table, "giveLvl", "(amount: number = 1)",
"Levels the player up.",
sol::overload(
[]() { return DebugCmdLevelUp(); },
[](int amount) { return DebugCmdLevelUp(amount); }));
SetWithSignatureAndDoc(table, "giveMap", "()",
"Reveal the map.",
&DebugCmdMapReveal);
SetWithSignatureAndDoc(table, "quests", "",
"Quest-related commands.",
LuaDevQuestsModule(lua));
SetWithSignatureAndDoc(table, "takeGold", "(amount: number = MAX)",
"Takes the player's gold away.",
sol::overload(
[]() { return DebugCmdTakeGoldCheat(); },
[](int amount) { return DebugCmdTakeGoldCheat(amount); }));
SetWithSignatureAndDoc(table, "takeMap", "()",
"Hide the map.",
&DebugCmdMapHide);
SetWithSignatureAndDoc(table, "maxStats", "()",
"Sets all stat values to maximum.",
&DebugCmdMaxStats);
SetWithSignatureAndDoc(table, "minStats", "()",
"Sets all stat values to minimum.",
&DebugCmdMinStats);
SetWithSignatureAndDoc(table, "setSpells", "(level: number)",
"Set spell level for all spells.", &DebugCmdSetSpellsLevel);
return table;
}
} // namespace devilution
#endif // _DEBUG

10
Source/lua/modules/dev.hpp

@ -0,0 +1,10 @@
#pragma once
#ifdef _DEBUG
#include <sol/sol.hpp>
namespace devilution {
sol::table LuaDevModule(sol::state_view &lua);
} // namespace devilution
#endif // _DEBUG

78
Source/lua/modules/dev/quests.cpp

@ -0,0 +1,78 @@
#pragma once
#ifdef _DEBUG
#include "lua/modules/dev/quests.hpp"
#include <sol/sol.hpp>
#include "lua/metadoc.hpp"
#include "quests.h"
#include "utils/str_cat.hpp"
namespace devilution {
namespace {
std::string DebugCmdEnableQuest(uint8_t questId)
{
if (questId >= MAXQUESTS) return StrCat("Quest ", questId, " does not exist!");
Quest &quest = Quests[questId];
if (IsNoneOf(quest._qactive, QUEST_NOTAVAIL, QUEST_INIT))
return StrCat(QuestsData[questId]._qlstr, " is already active!");
quest._qactive = QUEST_ACTIVE;
quest._qlog = true;
return StrCat(QuestsData[questId]._qlstr, " activated.");
}
std::string DebugCmdEnableQuests()
{
for (Quest &quest : Quests) {
if (IsNoneOf(quest._qactive, QUEST_NOTAVAIL, QUEST_INIT)) continue;
quest._qactive = QUEST_ACTIVE;
quest._qlog = true;
}
return StrCat("Activated all quests.");
}
std::string DebugCmdQuestInfo(const uint8_t questId)
{
if (questId >= MAXQUESTS) return StrCat("Quest ", questId, " does not exist!");
const Quest &quest = Quests[questId];
return StrCat("Quest id=", quest._qidx, " ", QuestsData[quest._qidx]._qlstr,
" active=", quest._qactive, " var1=", quest._qvar1, " var2=", quest._qvar2);
}
std::string DebugCmdQuestsInfo()
{
std::string ret;
for (const Quest &quest : Quests) {
StrAppend(ret, "Quest id=", quest._qidx, " ", QuestsData[quest._qidx]._qlstr,
" active=", quest._qactive, " var1=", quest._qvar1, " var2=", quest._qvar2, "\n");
}
if (!ret.empty()) ret.pop_back();
return ret;
}
} // namespace
sol::table LuaDevQuestsModule(sol::state_view &lua)
{
sol::table table = lua.create_table();
SetWithSignatureAndDoc(table, "activate", "(id: number)",
"Activates the given quest.",
&DebugCmdEnableQuest);
SetWithSignatureAndDoc(table, "activateAll", "()",
"Activates all available quests.",
&DebugCmdEnableQuests);
SetWithSignatureAndDoc(table, "info", "(id: number)",
"Information on the given quest.",
&DebugCmdQuestInfo);
SetWithSignatureAndDoc(table, "all", "()",
"Information on all available quest.",
&DebugCmdQuestsInfo);
return table;
}
} // namespace devilution
#endif // _DEBUG

10
Source/lua/modules/dev/quests.hpp

@ -0,0 +1,10 @@
#pragma once
#ifdef _DEBUG
#include <sol/sol.hpp>
namespace devilution {
sol::table LuaDevQuestsModule(sol::state_view &lua);
} // namespace devilution
#endif // _DEBUG

11
Source/panels/console.cpp

@ -1,6 +1,7 @@
#ifdef _DEBUG
#include "panels/console.hpp"
#include <algorithm>
#include <cstdint>
#include <string_view>
@ -166,18 +167,19 @@ void SendInput()
void DrawAutocompleteSuggestions(const Surface &out, const std::vector<LuaAutocompleteSuggestion> &suggestions, Point position)
{
const int maxInnerWidth = out.w() - TextPaddingX * 2;
if (AutocompleteSuggestionsMaxWidth == -1) {
int maxWidth = 0;
for (const LuaAutocompleteSuggestion &suggestion : suggestions) {
maxWidth = std::max(maxWidth, GetLineWidth(suggestion.displayText, AutocompleteSuggestionsTextFontSize, TextSpacing));
}
AutocompleteSuggestionsMaxWidth = maxWidth;
AutocompleteSuggestionsMaxWidth = std::min(maxWidth, maxInnerWidth);
}
const int outerWidth = AutocompleteSuggestionsMaxWidth + TextPaddingX * 2;
if (position.x + outerWidth > out.w()) {
position.x -= AutocompleteSuggestionsMaxWidth;
position.x = out.w() - outerWidth;
}
const int height = static_cast<int>(suggestions.size()) * LineHeight + TextPaddingYBottom + TextPaddingYTop;
@ -192,7 +194,10 @@ void DrawAutocompleteSuggestions(const Surface &out, const std::vector<LuaAutoco
const int extraHeight = extraTop + TextPaddingYBottom;
FillRect(out, position.x, textPosition.y - extraTop, outerWidth, LineHeight + extraHeight, PAL16_BLUE + 8);
}
DrawString(out, suggestion.displayText, textPosition,
const int textHeight = LineHeight + TextPaddingYBottom;
DrawString(
out.subregion(textPosition.x, textPosition.y, maxInnerWidth, textHeight), suggestion.displayText,
Rectangle { Point { 0, 0 }, Size { maxInnerWidth, textHeight } },
TextRenderOptions {
.flags = AutocompleteSuggestionsTextUiFlags,
.spacing = TextSpacing,

Loading…
Cancel
Save