/** * @file debug.cpp * * Implementation of debug functions. */ #ifdef _DEBUG #include #include #include #include "debug.h" #include "automap.h" #include "cursor.h" #include "engine/load_cel.hpp" #include "engine/point.hpp" #include "lighting.h" #include "monster.h" #include "plrmsg.h" #include "utils/str_case.hpp" #include "utils/str_cat.hpp" namespace devilution { std::string TestMapPath; OptionalOwnedClxSpriteList pSquareCel; bool DebugToggle = false; bool DebugGodMode = false; bool DebugVision = false; bool DebugPath = false; bool DebugGrid = false; std::unordered_map DebugCoordsMap; bool DebugScrollViewEnabled = false; std::string debugTRN; // Used for debugging level generation uint32_t glMid1Seed[NUMLEVELS]; uint32_t glMid2Seed[NUMLEVELS]; uint32_t glMid3Seed[NUMLEVELS]; uint32_t glEndSeed[NUMLEVELS]; namespace { DebugGridTextItem SelectedDebugGridTextItem; int DebugMonsterId; std::vector SearchMonsters; std::vector SearchItems; std::vector SearchObjects; void PrintDebugMonster(const Monster &monster) { EventPlrMsg(StrCat( "Monster ", static_cast(monster.getId()), " = ", monster.name(), "\nX = ", monster.position.tile.x, ", Y = ", monster.position.tile.y, "\nEnemy = ", monster.enemy, ", HP = ", monster.hitPoints, "\nMode = ", static_cast(monster.mode), ", Var1 = ", monster.var1), UiFlags::ColorWhite); bool bActive = false; for (size_t i = 0; i < ActiveMonsterCount; i++) { if (&Monsters[ActiveMonsters[i]] == &monster) { bActive = true; break; } } EventPlrMsg(StrCat("Active List = ", bActive ? 1 : 0, ", Squelch = ", monster.activeForTicks), UiFlags::ColorWhite); } } // namespace void LoadDebugGFX() { pSquareCel = LoadCel("data\\square", 64); } void FreeDebugGFX() { pSquareCel = std::nullopt; } void GetDebugMonster() { int monsterIndex = pcursmonst; if (monsterIndex == -1) monsterIndex = std::abs(dMonster[cursPosition.x][cursPosition.y]) - 1; if (monsterIndex == -1) monsterIndex = DebugMonsterId; PrintDebugMonster(Monsters[monsterIndex]); } void NextDebugMonster() { DebugMonsterId++; if (DebugMonsterId == MaxMonsters) DebugMonsterId = 0; EventPlrMsg(StrCat("Current debug monster = ", DebugMonsterId), UiFlags::ColorWhite); } void SetDebugLevelSeedInfos(uint32_t mid1Seed, uint32_t mid2Seed, uint32_t mid3Seed, uint32_t endSeed) { glMid1Seed[currlevel] = mid1Seed; glMid2Seed[currlevel] = mid2Seed; glMid3Seed[currlevel] = mid3Seed; glEndSeed[currlevel] = endSeed; } bool IsDebugGridTextNeeded() { return SelectedDebugGridTextItem != DebugGridTextItem::None; } bool IsDebugGridInMegatiles() { switch (SelectedDebugGridTextItem) { case DebugGridTextItem::AutomapView: case DebugGridTextItem::dungeon: case DebugGridTextItem::pdungeon: case DebugGridTextItem::Protected: return true; default: return false; } } DebugGridTextItem GetDebugGridTextType() { return SelectedDebugGridTextItem; } void SetDebugGridTextType(DebugGridTextItem value) { SelectedDebugGridTextItem = value; } bool GetDebugGridText(Point dungeonCoords, char *debugGridTextBuffer) { int info = 0; int blankValue = 0; Point megaCoords = dungeonCoords.worldToMega(); switch (SelectedDebugGridTextItem) { case DebugGridTextItem::coords: *BufCopy(debugGridTextBuffer, dungeonCoords.x, ":", dungeonCoords.y) = '\0'; return true; case DebugGridTextItem::cursorcoords: if (dungeonCoords != cursPosition) return false; *BufCopy(debugGridTextBuffer, dungeonCoords.x, ":", dungeonCoords.y) = '\0'; return true; case DebugGridTextItem::objectindex: { info = 0; Object *object = FindObjectAtPosition(dungeonCoords); if (object != nullptr) { info = static_cast(object->_otype); } break; } case DebugGridTextItem::dPiece: info = dPiece[dungeonCoords.x][dungeonCoords.y]; break; case DebugGridTextItem::dTransVal: info = dTransVal[dungeonCoords.x][dungeonCoords.y]; break; case DebugGridTextItem::dLight: info = dLight[dungeonCoords.x][dungeonCoords.y]; blankValue = LightsMax; break; case DebugGridTextItem::dPreLight: info = dPreLight[dungeonCoords.x][dungeonCoords.y]; blankValue = LightsMax; break; case DebugGridTextItem::dFlags: info = static_cast(dFlags[dungeonCoords.x][dungeonCoords.y]); break; case DebugGridTextItem::dPlayer: info = dPlayer[dungeonCoords.x][dungeonCoords.y]; break; case DebugGridTextItem::dMonster: info = dMonster[dungeonCoords.x][dungeonCoords.y]; break; case DebugGridTextItem::dCorpse: info = dCorpse[dungeonCoords.x][dungeonCoords.y]; break; case DebugGridTextItem::dItem: info = dItem[dungeonCoords.x][dungeonCoords.y]; break; case DebugGridTextItem::dSpecial: info = dSpecial[dungeonCoords.x][dungeonCoords.y]; break; case DebugGridTextItem::dObject: info = dObject[dungeonCoords.x][dungeonCoords.y]; break; case DebugGridTextItem::Solid: info = TileHasAny(dungeonCoords, TileProperties::Solid) << 0 | TileHasAny(dungeonCoords, TileProperties::BlockLight) << 1 | TileHasAny(dungeonCoords, TileProperties::BlockMissile) << 2; break; case DebugGridTextItem::Transparent: info = TileHasAny(dungeonCoords, TileProperties::Transparent) << 0 | TileHasAny(dungeonCoords, TileProperties::TransparentLeft) << 1 | TileHasAny(dungeonCoords, TileProperties::TransparentRight) << 2; break; case DebugGridTextItem::Trap: info = TileHasAny(dungeonCoords, TileProperties::Trap); break; case DebugGridTextItem::AutomapView: if (megaCoords.x >= 0 && megaCoords.x < DMAXX && megaCoords.y >= 0 && megaCoords.y < DMAXY) info = AutomapView[megaCoords.x][megaCoords.y]; break; case DebugGridTextItem::dungeon: if (megaCoords.x >= 0 && megaCoords.x < DMAXX && megaCoords.y >= 0 && megaCoords.y < DMAXY) info = dungeon[megaCoords.x][megaCoords.y]; break; case DebugGridTextItem::pdungeon: if (megaCoords.x >= 0 && megaCoords.x < DMAXX && megaCoords.y >= 0 && megaCoords.y < DMAXY) info = pdungeon[megaCoords.x][megaCoords.y]; break; case DebugGridTextItem::Protected: if (megaCoords.x >= 0 && megaCoords.x < DMAXX && megaCoords.y >= 0 && megaCoords.y < DMAXY) info = Protected.test(megaCoords.x, megaCoords.y); break; case DebugGridTextItem::None: return false; } if (info == blankValue) return false; *BufCopy(debugGridTextBuffer, info) = '\0'; return true; } bool IsDebugAutomapHighlightNeeded() { return SearchMonsters.size() > 0 || SearchItems.size() > 0 || SearchObjects.size() > 0; } bool ShouldHighlightDebugAutomapTile(Point position) { auto matchesSearched = [](const std::string_view name, const std::vector &searchedNames) { const std::string lowercaseName = AsciiStrToLower(name); for (const auto &searchedName : searchedNames) { if (lowercaseName.find(searchedName) != std::string::npos) { return true; } } return false; }; if (SearchMonsters.size() > 0 && dMonster[position.x][position.y] != 0) { const int mi = std::abs(dMonster[position.x][position.y]) - 1; const Monster &monster = Monsters[mi]; if (matchesSearched(monster.name(), SearchMonsters)) return true; } if (SearchItems.size() > 0 && dItem[position.x][position.y] != 0) { const int itemId = std::abs(dItem[position.x][position.y]) - 1; const Item &item = Items[itemId]; if (matchesSearched(item.getName(), SearchItems)) return true; } if (SearchObjects.size() > 0 && IsObjectAtPosition(position)) { const Object &object = ObjectAtPosition(position); if (matchesSearched(object.name(), SearchObjects)) return true; } return false; } void AddDebugAutomapMonsterHighlight(std::string_view name) { SearchMonsters.emplace_back(name); } void AddDebugAutomapItemHighlight(std::string_view name) { SearchItems.emplace_back(name); } void AddDebugAutomapObjectHighlight(std::string_view name) { SearchObjects.emplace_back(name); } void ClearDebugAutomapHighlights() { SearchMonsters.clear(); SearchItems.clear(); SearchObjects.clear(); } } // namespace devilution #endif