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]);
}
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<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;
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<DebugCmdItem> 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 },

4
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;

223
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<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;
}
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<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()
{
LevelMonsterTypeCount = 0;

1
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);

Loading…
Cancel
Save