You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
5.3 KiB
176 lines
5.3 KiB
#ifdef _DEBUG |
|
#include "lua/modules/dev/monsters.hpp" |
|
|
|
#include <optional> |
|
#include <string> |
|
|
|
#include <sol/sol.hpp> |
|
|
|
#include "crawl.hpp" |
|
#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<unsigned> 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<UniqueMonsterType>(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<std::string> { |
|
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<size_t>(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<unsigned> 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<size_t>(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
|
|
|