Browse Source

fix spawning unique monsters

pull/4096/head
qndel 4 years ago committed by Anders Jenbo
parent
commit
d3995736b9
  1. 127
      Source/debug.cpp
  2. 4
      Source/effects.cpp
  3. 223
      Source/monster.cpp
  4. 1
      Source/monster.h

127
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]); 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) if (currlevel == 0)
return "Do you want to kill the towners?!?"; 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::stringstream paramsStream(parameter.data());
std::string name; std::string name;
int count = 1; int count = 1;
if (std::getline(paramsStream, name, ' ')) { for (std::string tmp; std::getline(paramsStream, tmp, ' '); name += tmp + " ") {
count = atoi(name.c_str()); int num = atoi(tmp.c_str());
if (count > 0) if (num > 0) {
name.clear(); count = num;
else break;
count = 1; }
std::getline(paramsStream, name, ' ');
}
std::string singleWord;
while (std::getline(paramsStream, singleWord, ' ')) {
name.append(" ");
name.append(singleWord);
} }
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); }); std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); });
int mtype = -1; int mtype = -1;
for (int i = 0; i < 138; i++) { int uniqueIndex = -1;
auto mondata = MonstersData[i]; for (int i = 0; UniqueMonstersData[i].mtype != MT_INVALID; i++) {
auto mondata = UniqueMonstersData[i];
std::string monsterName(mondata.mName); std::string monsterName(mondata.mName);
std::transform(monsterName.begin(), monsterName.end(), monsterName.begin(), [](unsigned char c) { return std::tolower(c); }); std::transform(monsterName.begin(), monsterName.end(), monsterName.begin(), [](unsigned char c) { return std::tolower(c); });
if (monsterName.find(name) == std::string::npos) if (monsterName.find(name) == std::string::npos)
continue; continue;
mtype = i; mtype = mondata.mtype;
break; 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) { if (!found) {
for (int i = 0; i < 100; i++) { LevelMonsterTypes[id].mtype = static_cast<_monster_id>(mtype);
auto mondata = UniqueMonstersData[i]; InitMonsterGFX(id);
std::string monsterName(mondata.mName); InitMonsterSND(id);
std::transform(monsterName.begin(), monsterName.end(), monsterName.begin(), [](unsigned char c) { return std::tolower(c); }); LevelMonsterTypes[id].mPlaceFlags |= PLACE_SCATTER;
if (monsterName.find(name) == std::string::npos) LevelMonsterTypes[id].mdeadval = 1;
}
auto &myPlayer = Players[MyPlayerId];
int spawnedMonster = 0;
for (int k : CrawlNum) {
int ck = k + 2;
for (auto j = static_cast<uint8_t>(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; 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; 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) if (mtype == -1)
return "Monster not found!"; return "Monster not found!";
@ -608,6 +681,7 @@ std::string DebugCmdSpawnMonster(const string_view parameter)
if (!found) { if (!found) {
LevelMonsterTypes[id].mtype = static_cast<_monster_id>(mtype); LevelMonsterTypes[id].mtype = static_cast<_monster_id>(mtype);
InitMonsterGFX(id); InitMonsterGFX(id);
InitMonsterSND(id);
LevelMonsterTypes[id].mPlaceFlags |= PLACE_SCATTER; LevelMonsterTypes[id].mPlaceFlags |= PLACE_SCATTER;
LevelMonsterTypes[id].mdeadval = 1; LevelMonsterTypes[id].mdeadval = 1;
} }
@ -796,7 +870,8 @@ std::vector<DebugCmdItem> DebugCmdList = {
{ "arrow", "Changes arrow effect (normal, fire, lightning, explosion).", "{effect}", &DebugCmdArrow }, { "arrow", "Changes arrow effect (normal, fire, lightning, explosion).", "{effect}", &DebugCmdArrow },
{ "grid", "Toggles showing grid.", "", &DebugCmdShowGrid }, { "grid", "Toggles showing grid.", "", &DebugCmdShowGrid },
{ "seedinfo", "Show seed infos for current level.", "", &DebugCmdLevelSeed }, { "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 }, { "tiledata", "Toggles showing tile data {name} (leave name empty to see a list).", "{name}", &DebugCmdShowTileData },
{ "scrollview", "Toggles scroll view feature (with shift+mouse).", "", &DebugCmdScrollView }, { "scrollview", "Toggles scroll view feature (with shift+mouse).", "", &DebugCmdScrollView },
{ "iteminfo", "Shows info of currently selected item.", "", &DebugCmdItemInfo }, { "iteminfo", "Shows info of currently selected item.", "", &DebugCmdItemInfo },

4
Source/effects.cpp

@ -1227,7 +1227,11 @@ void InitMonsterSND(int monst)
void FreeMonsterSnd() void FreeMonsterSnd()
{ {
#ifdef _DEBUG
for (int i = 0; i < MAX_LVLMTYPES; i++) {
#else
for (int i = 0; i < LevelMonsterTypeCount; i++) { for (int i = 0; i < LevelMonsterTypeCount; i++) {
#endif
for (auto &variants : LevelMonsterTypes[i].Snds) { for (auto &variants : LevelMonsterTypes[i].Snds) {
for (auto &snd : variants) { for (auto &snd : variants) {
snd = nullptr; snd = nullptr;

223
Source/monster.cpp

@ -477,115 +477,7 @@ void PlaceUniqueMonst(int uniqindex, int miniontype, int bosspacksize)
UberDiabloMonsterIndex = ActiveMonsterCount; UberDiabloMonsterIndex = ActiveMonsterCount;
} }
PlaceMonster(ActiveMonsterCount, uniqtype, xp, yp); PlaceMonster(ActiveMonsterCount, uniqtype, xp, yp);
monster._uniqtype = uniqindex + 1; PrepareUniqueMonst(monster, uniqindex, miniontype, bosspacksize, uniqueMonsterData);
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<uint8_t>(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;
}
} }
int AddMonsterType(_monster_id type, placeflag placeflag) int AddMonsterType(_monster_id type, placeflag placeflag)
@ -3564,6 +3456,119 @@ bool IsRelativeMoveOK(const Monster &monster, Point position, Direction mdir)
} // namespace } // 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<uint8_t>(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() void InitLevelMonsters()
{ {
LevelMonsterTypeCount = 0; LevelMonsterTypeCount = 0;

1
Source/monster.h

@ -270,6 +270,7 @@ extern int ActiveMonsterCount;
extern int MonsterKillCounts[MAXMONSTERS]; extern int MonsterKillCounts[MAXMONSTERS];
extern bool sgbSaveSoundOn; extern bool sgbSaveSoundOn;
void PrepareUniqueMonst(Monster &monster, int uniqindex, int miniontype, int bosspacksize, const UniqueMonsterData &uniqueMonsterData);
void InitLevelMonsters(); void InitLevelMonsters();
void GetLevelMTypes(); void GetLevelMTypes();
void InitMonsterGFX(int monst); void InitMonsterGFX(int monst);

Loading…
Cancel
Save