From d3995736b97fd97dfa2ce5744fca2b691b9eca06 Mon Sep 17 00:00:00 2001 From: qndel Date: Wed, 23 Feb 2022 15:27:17 +0100 Subject: [PATCH] fix spawning unique monsters --- Source/debug.cpp | 127 ++++++++++++++++++++------ Source/effects.cpp | 4 + Source/monster.cpp | 223 +++++++++++++++++++++++---------------------- Source/monster.h | 1 + 4 files changed, 220 insertions(+), 135 deletions(-) diff --git a/Source/debug.cpp b/Source/debug.cpp index 0550afc22..5a072f2b6 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -543,7 +543,7 @@ std::string DebugCmdLevelSeed(const string_view parameter) return fmt::format("Seedinfo for level {}\nseed: {}\nMid1: {}\nMid2: {}\nMid3: {}\nEnd: {}", currlevel, glSeedTbl[currlevel], glMid1Seed[currlevel], glMid2Seed[currlevel], glMid3Seed[currlevel], glEndSeed[currlevel]); } -std::string DebugCmdSpawnMonster(const string_view parameter) +std::string DebugCmdSpawnUniqueMonster(const string_view parameter) { if (currlevel == 0) return "Do you want to kill the towners?!?"; @@ -551,45 +551,118 @@ std::string DebugCmdSpawnMonster(const string_view parameter) std::stringstream paramsStream(parameter.data()); std::string name; int count = 1; - if (std::getline(paramsStream, name, ' ')) { - count = atoi(name.c_str()); - if (count > 0) - name.clear(); - else - count = 1; - std::getline(paramsStream, name, ' '); - } - - std::string singleWord; - while (std::getline(paramsStream, singleWord, ' ')) { - name.append(" "); - name.append(singleWord); + for (std::string tmp; std::getline(paramsStream, tmp, ' '); name += tmp + " ") { + int num = atoi(tmp.c_str()); + if (num > 0) { + count = num; + break; + } } + if (name.empty()) + return "Monster name cannot be empty. Duh."; + name.pop_back(); // remove last space std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); }); int mtype = -1; - for (int i = 0; i < 138; i++) { - auto mondata = MonstersData[i]; + int uniqueIndex = -1; + for (int i = 0; UniqueMonstersData[i].mtype != MT_INVALID; i++) { + auto mondata = UniqueMonstersData[i]; std::string monsterName(mondata.mName); std::transform(monsterName.begin(), monsterName.end(), monsterName.begin(), [](unsigned char c) { return std::tolower(c); }); if (monsterName.find(name) == std::string::npos) continue; - mtype = i; - break; + mtype = mondata.mtype; + uniqueIndex = 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!"; + + int id = MAX_LVLMTYPES - 1; + bool found = false; + + for (int i = 0; i < LevelMonsterTypeCount; i++) { + if (LevelMonsterTypes[i].mtype == mtype) { + id = i; + found = true; + break; + } } - if (mtype == -1) { - for (int i = 0; i < 100; i++) { - auto mondata = UniqueMonstersData[i]; - std::string monsterName(mondata.mName); - std::transform(monsterName.begin(), monsterName.end(), monsterName.begin(), [](unsigned char c) { return std::tolower(c); }); - if (monsterName.find(name) == std::string::npos) + if (!found) { + LevelMonsterTypes[id].mtype = static_cast<_monster_id>(mtype); + InitMonsterGFX(id); + InitMonsterSND(id); + LevelMonsterTypes[id].mPlaceFlags |= PLACE_SCATTER; + LevelMonsterTypes[id].mdeadval = 1; + } + + auto &myPlayer = Players[MyPlayerId]; + + int spawnedMonster = 0; + + for (int k : CrawlNum) { + int ck = k + 2; + for (auto j = static_cast(CrawlTable[k]); j > 0; j--, ck += 2) { + Point pos = myPlayer.position.tile + Displacement { CrawlTable[ck - 1], CrawlTable[ck] }; + if (dPlayer[pos.x][pos.y] != 0 || dMonster[pos.x][pos.y] != 0) continue; - mtype = mondata.mtype; + if (!IsTileWalkable(pos)) + continue; + + int mon = AddMonster(pos, myPlayer._pdir, id, true); + if (mon < 0) + return fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster); + auto &monster = Monsters[mon]; + PrepareUniqueMonst(monster, uniqueIndex, 0, 0, UniqueMonstersData[uniqueIndex]); + ActiveMonsterCount--; + monster._udeadval = 1; + spawnedMonster += 1; + + if (spawnedMonster >= count) + return "Let the fighting begin!"; + } + } + + return fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster); +} + +std::string DebugCmdSpawnMonster(const string_view parameter) +{ + if (currlevel == 0) + return "Do you want to kill the towners?!?"; + + std::stringstream paramsStream(parameter.data()); + std::string name; + int count = 1; + for (std::string tmp; std::getline(paramsStream, tmp, ' '); name += tmp + " ") { + int num = atoi(tmp.c_str()); + if (num > 0) { + count = num; break; } } + if (name.empty()) + return "Monster name cannot be empty. Duh."; + + name.pop_back(); // remove last space + std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); }); + + int mtype = -1; + + for (int i = 0; i < NUM_MTYPES; i++) { + auto mondata = MonstersData[i]; + std::string monsterName(mondata.mName); + std::transform(monsterName.begin(), monsterName.end(), monsterName.begin(), [](unsigned char c) { return std::tolower(c); }); + 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!"; @@ -608,6 +681,7 @@ std::string DebugCmdSpawnMonster(const string_view parameter) if (!found) { LevelMonsterTypes[id].mtype = static_cast<_monster_id>(mtype); InitMonsterGFX(id); + InitMonsterSND(id); LevelMonsterTypes[id].mPlaceFlags |= PLACE_SCATTER; LevelMonsterTypes[id].mdeadval = 1; } @@ -796,7 +870,8 @@ std::vector DebugCmdList = { { "arrow", "Changes arrow effect (normal, fire, lightning, explosion).", "{effect}", &DebugCmdArrow }, { "grid", "Toggles showing grid.", "", &DebugCmdShowGrid }, { "seedinfo", "Show seed infos for current level.", "", &DebugCmdLevelSeed }, - { "spawn", "Spawns monster {name}.", "({count}) {name}", &DebugCmdSpawnMonster }, + { "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 }, diff --git a/Source/effects.cpp b/Source/effects.cpp index 783f48a0e..5e989bd3b 100644 --- a/Source/effects.cpp +++ b/Source/effects.cpp @@ -1227,7 +1227,11 @@ void InitMonsterSND(int monst) void FreeMonsterSnd() { +#ifdef _DEBUG + for (int i = 0; i < MAX_LVLMTYPES; i++) { +#else for (int i = 0; i < LevelMonsterTypeCount; i++) { +#endif for (auto &variants : LevelMonsterTypes[i].Snds) { for (auto &snd : variants) { snd = nullptr; diff --git a/Source/monster.cpp b/Source/monster.cpp index f8f9b4ac5..dfbb8bb07 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -477,115 +477,7 @@ void PlaceUniqueMonst(int uniqindex, int miniontype, int bosspacksize) UberDiabloMonsterIndex = ActiveMonsterCount; } PlaceMonster(ActiveMonsterCount, uniqtype, xp, yp); - monster._uniqtype = uniqindex + 1; - - if (uniqueMonsterData.mlevel != 0) { - monster.mLevel = 2 * uniqueMonsterData.mlevel; - } else { - monster.mLevel = monster.MData->mLevel + 5; - } - - monster.mExp *= 2; - monster.mName = pgettext("monster", uniqueMonsterData.mName); - monster._mmaxhp = uniqueMonsterData.mmaxhp << 6; - - if (!gbIsMultiplayer) - monster._mmaxhp = std::max(monster._mmaxhp / 2, 64); - - monster._mhitpoints = monster._mmaxhp; - monster._mAi = uniqueMonsterData.mAi; - monster._mint = uniqueMonsterData.mint; - monster.mMinDamage = uniqueMonsterData.mMinDamage; - monster.mMaxDamage = uniqueMonsterData.mMaxDamage; - monster.mMinDamage2 = uniqueMonsterData.mMinDamage; - monster.mMaxDamage2 = uniqueMonsterData.mMaxDamage; - monster.mMagicRes = uniqueMonsterData.mMagicRes; - monster.mtalkmsg = uniqueMonsterData.mtalkmsg; - if (uniqindex == UMT_HORKDMN) - monster.mlid = NO_LIGHT; // BUGFIX monsters initial light id should be -1 (fixed) - else - monster.mlid = AddLight(monster.position.tile, 3); - - if (gbIsMultiplayer) { - if (monster._mAi == AI_LAZHELP) - monster.mtalkmsg = TEXT_NONE; - if (monster._mAi == AI_LAZARUS && Quests[Q_BETRAYER]._qvar1 > 3) { - monster._mgoal = MGOAL_NORMAL; - } else if (monster.mtalkmsg != TEXT_NONE) { - monster._mgoal = MGOAL_INQUIRING; - } - } else if (monster.mtalkmsg != TEXT_NONE) { - monster._mgoal = MGOAL_INQUIRING; - } - - if (sgGameInitInfo.nDifficulty == DIFF_NIGHTMARE) { - monster._mmaxhp = 3 * monster._mmaxhp; - if (gbIsHellfire) - monster._mmaxhp += (gbIsMultiplayer ? 100 : 50) << 6; - else - monster._mmaxhp += 64; - monster.mLevel += 15; - monster._mhitpoints = monster._mmaxhp; - monster.mExp = 2 * (monster.mExp + 1000); - monster.mMinDamage = 2 * (monster.mMinDamage + 2); - monster.mMaxDamage = 2 * (monster.mMaxDamage + 2); - monster.mMinDamage2 = 2 * (monster.mMinDamage2 + 2); - monster.mMaxDamage2 = 2 * (monster.mMaxDamage2 + 2); - } else if (sgGameInitInfo.nDifficulty == DIFF_HELL) { - monster._mmaxhp = 4 * monster._mmaxhp; - if (gbIsHellfire) - monster._mmaxhp += (gbIsMultiplayer ? 200 : 100) << 6; - else - monster._mmaxhp += 192; - monster.mLevel += 30; - monster._mhitpoints = monster._mmaxhp; - monster.mExp = 4 * (monster.mExp + 1000); - monster.mMinDamage = 4 * monster.mMinDamage + 6; - monster.mMaxDamage = 4 * monster.mMaxDamage + 6; - monster.mMinDamage2 = 4 * monster.mMinDamage2 + 6; - monster.mMaxDamage2 = 4 * monster.mMaxDamage2 + 6; - } - - char filestr[64]; - sprintf(filestr, "Monsters\\Monsters\\%s.TRN", uniqueMonsterData.mTrnName); - monster.uniqueTRN = LoadFileInMem(filestr); - - monster._uniqtrans = uniquetrans++; - - if (uniqueMonsterData.customToHit != 0) { - monster.mHit = uniqueMonsterData.customToHit; - monster.mHit2 = uniqueMonsterData.customToHit; - - if (sgGameInitInfo.nDifficulty == DIFF_NIGHTMARE) { - monster.mHit += NIGHTMARE_TO_HIT_BONUS; - monster.mHit2 += NIGHTMARE_TO_HIT_BONUS; - } else if (sgGameInitInfo.nDifficulty == DIFF_HELL) { - monster.mHit += HELL_TO_HIT_BONUS; - monster.mHit2 += HELL_TO_HIT_BONUS; - } - } - if (uniqueMonsterData.customArmorClass != 0) { - monster.mArmorClass = uniqueMonsterData.customArmorClass; - - if (sgGameInitInfo.nDifficulty == DIFF_NIGHTMARE) { - monster.mArmorClass += NIGHTMARE_AC_BONUS; - } else if (sgGameInitInfo.nDifficulty == DIFF_HELL) { - monster.mArmorClass += HELL_AC_BONUS; - } - } - - ActiveMonsterCount++; - - if (uniqueMonsterData.monsterPack != UniqueMonsterPack::None) { - PlaceGroup(miniontype, bosspacksize, uniqueMonsterData.monsterPack, ActiveMonsterCount - 1); - } - - if (monster._mAi != AI_GARG) { - monster.ChangeAnimationData(MonsterGraphic::Stand); - monster.AnimInfo.CurrentFrame = GenerateRnd(monster.AnimInfo.NumberOfFrames - 1) + 1; - monster._mFlags &= ~MFLAG_ALLOW_SPECIAL; - monster._mmode = MonsterMode::Stand; - } + PrepareUniqueMonst(monster, uniqindex, miniontype, bosspacksize, uniqueMonsterData); } int AddMonsterType(_monster_id type, placeflag placeflag) @@ -3564,6 +3456,119 @@ bool IsRelativeMoveOK(const Monster &monster, Point position, Direction mdir) } // namespace +void PrepareUniqueMonst(Monster &monster, int uniqindex, int miniontype, int bosspacksize, const UniqueMonsterData &uniqueMonsterData) +{ + monster._uniqtype = uniqindex + 1; + + if (uniqueMonsterData.mlevel != 0) { + monster.mLevel = 2 * uniqueMonsterData.mlevel; + } else { + monster.mLevel = monster.MData->mLevel + 5; + } + + monster.mExp *= 2; + monster.mName = pgettext("monster", uniqueMonsterData.mName); + monster._mmaxhp = uniqueMonsterData.mmaxhp << 6; + + if (!gbIsMultiplayer) + monster._mmaxhp = std::max(monster._mmaxhp / 2, 64); + + monster._mhitpoints = monster._mmaxhp; + monster._mAi = uniqueMonsterData.mAi; + monster._mint = uniqueMonsterData.mint; + monster.mMinDamage = uniqueMonsterData.mMinDamage; + monster.mMaxDamage = uniqueMonsterData.mMaxDamage; + monster.mMinDamage2 = uniqueMonsterData.mMinDamage; + monster.mMaxDamage2 = uniqueMonsterData.mMaxDamage; + monster.mMagicRes = uniqueMonsterData.mMagicRes; + monster.mtalkmsg = uniqueMonsterData.mtalkmsg; + if (uniqindex == UMT_HORKDMN) + monster.mlid = NO_LIGHT; // BUGFIX monsters initial light id should be -1 (fixed) + else + monster.mlid = AddLight(monster.position.tile, 3); + + if (gbIsMultiplayer) { + if (monster._mAi == AI_LAZHELP) + monster.mtalkmsg = TEXT_NONE; + if (monster._mAi == AI_LAZARUS && Quests[Q_BETRAYER]._qvar1 > 3) { + monster._mgoal = MGOAL_NORMAL; + } else if (monster.mtalkmsg != TEXT_NONE) { + monster._mgoal = MGOAL_INQUIRING; + } + } else if (monster.mtalkmsg != TEXT_NONE) { + monster._mgoal = MGOAL_INQUIRING; + } + + if (sgGameInitInfo.nDifficulty == DIFF_NIGHTMARE) { + monster._mmaxhp = 3 * monster._mmaxhp; + if (gbIsHellfire) + monster._mmaxhp += (gbIsMultiplayer ? 100 : 50) << 6; + else + monster._mmaxhp += 64; + monster.mLevel += 15; + monster._mhitpoints = monster._mmaxhp; + monster.mExp = 2 * (monster.mExp + 1000); + monster.mMinDamage = 2 * (monster.mMinDamage + 2); + monster.mMaxDamage = 2 * (monster.mMaxDamage + 2); + monster.mMinDamage2 = 2 * (monster.mMinDamage2 + 2); + monster.mMaxDamage2 = 2 * (monster.mMaxDamage2 + 2); + } else if (sgGameInitInfo.nDifficulty == DIFF_HELL) { + monster._mmaxhp = 4 * monster._mmaxhp; + if (gbIsHellfire) + monster._mmaxhp += (gbIsMultiplayer ? 200 : 100) << 6; + else + monster._mmaxhp += 192; + monster.mLevel += 30; + monster._mhitpoints = monster._mmaxhp; + monster.mExp = 4 * (monster.mExp + 1000); + monster.mMinDamage = 4 * monster.mMinDamage + 6; + monster.mMaxDamage = 4 * monster.mMaxDamage + 6; + monster.mMinDamage2 = 4 * monster.mMinDamage2 + 6; + monster.mMaxDamage2 = 4 * monster.mMaxDamage2 + 6; + } + + char filestr[64]; + sprintf(filestr, "Monsters\\Monsters\\%s.TRN", uniqueMonsterData.mTrnName); + monster.uniqueTRN = LoadFileInMem(filestr); + + monster._uniqtrans = uniquetrans++; + + if (uniqueMonsterData.customToHit != 0) { + monster.mHit = uniqueMonsterData.customToHit; + monster.mHit2 = uniqueMonsterData.customToHit; + + if (sgGameInitInfo.nDifficulty == DIFF_NIGHTMARE) { + monster.mHit += NIGHTMARE_TO_HIT_BONUS; + monster.mHit2 += NIGHTMARE_TO_HIT_BONUS; + } else if (sgGameInitInfo.nDifficulty == DIFF_HELL) { + monster.mHit += HELL_TO_HIT_BONUS; + monster.mHit2 += HELL_TO_HIT_BONUS; + } + } + if (uniqueMonsterData.customArmorClass != 0) { + monster.mArmorClass = uniqueMonsterData.customArmorClass; + + if (sgGameInitInfo.nDifficulty == DIFF_NIGHTMARE) { + monster.mArmorClass += NIGHTMARE_AC_BONUS; + } else if (sgGameInitInfo.nDifficulty == DIFF_HELL) { + monster.mArmorClass += HELL_AC_BONUS; + } + } + + ActiveMonsterCount++; + + if (uniqueMonsterData.monsterPack != UniqueMonsterPack::None) { + PlaceGroup(miniontype, bosspacksize, uniqueMonsterData.monsterPack, ActiveMonsterCount - 1); + } + + if (monster._mAi != AI_GARG) { + monster.ChangeAnimationData(MonsterGraphic::Stand); + monster.AnimInfo.CurrentFrame = GenerateRnd(monster.AnimInfo.NumberOfFrames - 1) + 1; + monster._mFlags &= ~MFLAG_ALLOW_SPECIAL; + monster._mmode = MonsterMode::Stand; + } +} + void InitLevelMonsters() { LevelMonsterTypeCount = 0; diff --git a/Source/monster.h b/Source/monster.h index bce32c70e..c8eb7654b 100644 --- a/Source/monster.h +++ b/Source/monster.h @@ -270,6 +270,7 @@ extern int ActiveMonsterCount; extern int MonsterKillCounts[MAXMONSTERS]; extern bool sgbSaveSoundOn; +void PrepareUniqueMonst(Monster &monster, int uniqindex, int miniontype, int bosspacksize, const UniqueMonsterData &uniqueMonsterData); void InitLevelMonsters(); void GetLevelMTypes(); void InitMonsterGFX(int monst);