#ifdef _DEBUG #include "lua/modules/dev/monsters.hpp" #include #include #include #include "levels/gendung.h" #include "lighting.h" #include "lua/metadoc.hpp" #include "monstdat.h" #include "monster.h" #include "player.h" #include "utils/str_case.hpp" #include "utils/str_cat.hpp" namespace devilution { namespace { std::string DebugCmdSpawnUniqueMonster(std::string name, std::optional countOpt) { if (leveltype == DTYPE_TOWN) return "Can't spawn monsters in town"; if (name.empty()) return "name is required"; const unsigned count = countOpt.value_or(1); if (count < 1) return "count must be positive"; AsciiStrToLower(name); int mtype = -1; UniqueMonsterType uniqueIndex = UniqueMonsterType::None; for (size_t i = 0; i < UniqueMonstersData.size(); ++i) { const auto &mondata = UniqueMonstersData[i]; const std::string monsterName = AsciiStrToLower(std::string_view(mondata.mName)); if (monsterName.find(name) == std::string::npos) continue; mtype = mondata.mtype; uniqueIndex = static_cast(i); if (monsterName == name) // to support partial name matching but always choose the correct monster if full name is given break; } if (mtype == -1) return "Monster not found"; size_t id = MaxLvlMTypes - 1; bool found = false; for (size_t i = 0; i < LevelMonsterTypeCount; i++) { if (LevelMonsterTypes[i].type == mtype) { id = i; found = true; break; } } if (!found) { if (LevelMonsterTypeCount == MaxLvlMTypes) LevelMonsterTypeCount--; // we are running out of monster types, so override last used monster type id = AddMonsterType(uniqueIndex, PLACE_SCATTER); CMonster &monsterType = LevelMonsterTypes[id]; InitMonsterGFX(monsterType); monsterType.corpseId = 1; } Player &myPlayer = *MyPlayer; unsigned spawnedMonster = 0; auto ret = Crawl(0, MaxCrawlRadius, [&](Displacement displacement) -> std::optional { Point pos = myPlayer.position.tile + displacement; if (dPlayer[pos.x][pos.y] != 0 || dMonster[pos.x][pos.y] != 0) return {}; if (!IsTileWalkable(pos)) return {}; Monster *monster = AddMonster(pos, myPlayer._pdir, id, true); if (monster == nullptr) return StrCat("Spawned ", spawnedMonster, " monsters. (Unable to spawn more)"); PrepareUniqueMonst(*monster, uniqueIndex, 0, 0, UniqueMonstersData[static_cast(uniqueIndex)]); monster->corpseId = 1; spawnedMonster += 1; if (spawnedMonster >= count) return StrCat("Spawned ", spawnedMonster, " monsters."); return {}; }); if (!ret.has_value()) ret = StrCat("Spawned ", spawnedMonster, " monsters. (Unable to spawn more)"); return *ret; } std::string DebugCmdSpawnMonster(std::string name, std::optional countOpt) { if (leveltype == DTYPE_TOWN) return "Can't spawn monsters in town"; if (name.empty()) return "name is required"; const unsigned count = countOpt.value_or(1); if (count < 1) return "count must be positive"; AsciiStrToLower(name); int mtype = -1; for (int i = 0; i < NUM_MTYPES; i++) { const auto &mondata = MonstersData[i]; const std::string monsterName = AsciiStrToLower(std::string_view(mondata.name)); if (monsterName.find(name) == std::string::npos) continue; mtype = i; if (monsterName == name) // to support partial name matching but always choose the correct monster if full name is given break; } if (mtype == -1) return "Monster not found"; if (!MyPlayer->isLevelOwnedByLocalClient()) return "You are not the level owner."; size_t id = MaxLvlMTypes - 1; bool found = false; for (size_t i = 0; i < LevelMonsterTypeCount; i++) { if (LevelMonsterTypes[i].type == mtype) { id = i; found = true; break; } } if (!found) { if (LevelMonsterTypeCount == MaxLvlMTypes) LevelMonsterTypeCount--; // we are running out of monster types, so override last used monster type id = AddMonsterType(static_cast<_monster_id>(mtype), PLACE_SCATTER); CMonster &monsterType = LevelMonsterTypes[id]; InitMonsterGFX(monsterType); monsterType.corpseId = 1; } Player &myPlayer = *MyPlayer; size_t monstersToSpawn = std::min(MaxMonsters - ActiveMonsterCount, count); if (monstersToSpawn == 0) return "Can't spawn any monsters"; size_t spawnedMonster = 0; Crawl(0, MaxCrawlRadius, [&](Displacement displacement) { Point pos = myPlayer.position.tile + displacement; if (dPlayer[pos.x][pos.y] != 0 || dMonster[pos.x][pos.y] != 0) return false; if (!IsTileWalkable(pos)) return false; SpawnMonster(pos, myPlayer._pdir, id); spawnedMonster += 1; return spawnedMonster == monstersToSpawn; }); if (monstersToSpawn != count) return StrCat("Spawned ", spawnedMonster, " monsters. (Unable to spawn more)"); return StrCat("Spawned ", spawnedMonster, " monsters."); } } // namespace sol::table LuaDevMonstersModule(sol::state_view &lua) { sol::table table = lua.create_table(); SetDocumented(table, "spawn", "(name: string, count: number = 1)", "Spawn monster(s)", &DebugCmdSpawnMonster); SetDocumented(table, "spawnUnique", "(name: string, count: number = 1)", "Spawn unique monster(s)", &DebugCmdSpawnUniqueMonster); return table; } } // namespace devilution #endif // _DEBUG