Browse Source

♻️Use monster by reference when possible

pull/2381/head
Anders Jenbo 5 years ago
parent
commit
77b72da276
  1. 7
      Source/control.cpp
  2. 29
      Source/controls/plrctrls.cpp
  3. 18
      Source/dead.cpp
  4. 12
      Source/debug.cpp
  5. 18
      Source/diablo.cpp
  6. 58
      Source/effects.cpp
  7. 5
      Source/effects.h
  8. 2
      Source/effects_stubs.cpp
  9. 3
      Source/inv.cpp
  10. 38
      Source/items.cpp
  11. 5
      Source/items.h
  12. 278
      Source/loadsave.cpp
  13. 280
      Source/missiles.cpp
  14. 3
      Source/missiles.h
  15. 3444
      Source/monster.cpp
  16. 21
      Source/monster.h
  17. 61
      Source/msg.cpp
  18. 36
      Source/objects.cpp
  19. 3
      Source/objects.h
  20. 70
      Source/player.cpp
  21. 22
      Source/qol/monhealthbar.cpp
  22. 17
      Source/quests.cpp
  23. 3
      Source/quests.h
  24. 51
      Source/scrollrt.cpp
  25. 46
      Source/sync.cpp
  26. 20
      test/effects_test.cpp

7
Source/control.cpp

@ -1284,15 +1284,16 @@ void DrawInfoBox(const Surface &out)
else if (pcursobj != -1)
GetObjectStr(pcursobj);
if (pcursmonst != -1) {
const auto &monster = Monsters[pcursmonst];
if (leveltype != DTYPE_TOWN) {
infoclr = UIS_SILVER;
strcpy(infostr, _(Monsters[pcursmonst].mName));
strcpy(infostr, _(monster.mName));
ClearPanel();
if (Monsters[pcursmonst]._uniqtype != 0) {
if (monster._uniqtype != 0) {
infoclr = UIS_GOLD;
PrintUniqueHistory();
} else {
PrintMonstHistory(Monsters[pcursmonst].MType->mtype);
PrintMonstHistory(monster.MType->mtype);
}
} else if (pcursitem == -1) {
string_view townerName = Towners[pcursmonst]._tName;

29
Source/controls/plrctrls.cpp

@ -185,17 +185,15 @@ bool HasRangedSpell()
&& !spelldata[spl].sTownSpell;
}
bool CanTargetMonster(int mi)
bool CanTargetMonster(const MonsterStruct &monster)
{
const MonsterStruct &monst = Monsters[mi];
if ((monst._mFlags & (MFLAG_HIDDEN | MFLAG_GOLEM)) != 0)
if ((monster._mFlags & (MFLAG_HIDDEN | MFLAG_GOLEM)) != 0)
return false;
if (monst._mhitpoints >> 6 <= 0) // dead
if (monster._mhitpoints >> 6 <= 0) // dead
return false;
const int mx = monst.position.tile.x;
const int my = monst.position.tile.y;
const int mx = monster.position.tile.x;
const int my = monster.position.tile.y;
if ((dFlags[mx][my] & BFLAG_LIT) == 0) // not visible
return false;
if (dMonster[mx][my] == 0)
@ -212,14 +210,16 @@ void FindRangedTarget()
// The first MAX_PLRS monsters are reserved for players' golems.
for (int mi = MAX_PLRS; mi < MAXMONSTERS; mi++) {
if (!CanTargetMonster(mi))
const auto &monster = Monsters[mi];
if (!CanTargetMonster(monster))
continue;
const bool newCanTalk = CanTalkToMonst(mi);
const bool newCanTalk = CanTalkToMonst(monster);
if (pcursmonst != -1 && !canTalk && newCanTalk)
continue;
const int newDdistance = GetDistanceRanged(Monsters[mi].position.future);
const int newRotations = GetRotaryDistance(Monsters[mi].position.future);
const int newDdistance = GetDistanceRanged(monster.position.future);
const int newRotations = GetRotaryDistance(monster.position.future);
if (pcursmonst != -1 && canTalk == newCanTalk) {
if (distance < newDdistance)
continue;
@ -274,8 +274,9 @@ void FindMeleeTarget()
if (dMonster[dx][dy] != 0) {
const int mi = dMonster[dx][dy] > 0 ? dMonster[dx][dy] - 1 : -(dMonster[dx][dy] + 1);
if (CanTargetMonster(mi)) {
const bool newCanTalk = CanTalkToMonst(mi);
const auto &monster = Monsters[mi];
if (CanTargetMonster(monster)) {
const bool newCanTalk = CanTalkToMonst(monster);
if (pcursmonst != -1 && !canTalk && newCanTalk)
continue;
const int newRotations = GetRotaryDistance({ dx, dy });
@ -442,7 +443,7 @@ void Interact()
if (leveltype == DTYPE_TOWN && pcursmonst != -1) {
NetSendCmdLocParam1(true, CMD_TALKXY, Towners[pcursmonst].position, pcursmonst);
} else if (pcursmonst != -1) {
if (Players[MyPlayerId]._pwtype != WT_RANGED || CanTalkToMonst(pcursmonst)) {
if (Players[MyPlayerId]._pwtype != WT_RANGED || CanTalkToMonst(Monsters[pcursmonst])) {
NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst);
} else {
NetSendCmdParam1(true, CMD_RATTACKID, pcursmonst);

18
Source/dead.cpp

@ -63,13 +63,13 @@ void InitDead()
stonendx = nd;
for (int i = 0; i < ActiveMonsterCount; i++) {
int mi = ActiveMonsters[i];
if (Monsters[mi]._uniqtype != 0) {
InitDeadAnimationFromMonster(Dead[nd], *Monsters[mi].MType);
Dead[nd].translationPaletteIndex = Monsters[mi]._uniqtrans + 4;
auto &monster = Monsters[ActiveMonsters[i]];
if (monster._uniqtype != 0) {
InitDeadAnimationFromMonster(Dead[nd], *monster.MType);
Dead[nd].translationPaletteIndex = monster._uniqtrans + 4;
nd++;
Monsters[mi]._udeadval = nd;
monster._udeadval = nd;
}
}
@ -84,13 +84,13 @@ void AddDead(Point tilePosition, int8_t dv, Direction ddir)
void SetDead()
{
for (int i = 0; i < ActiveMonsterCount; i++) {
int mi = ActiveMonsters[i];
if (Monsters[mi]._uniqtype == 0)
auto &monster = Monsters[ActiveMonsters[i]];
if (monster._uniqtype == 0)
continue;
for (int dx = 0; dx < MAXDUNX; dx++) {
for (int dy = 0; dy < MAXDUNY; dy++) {
if ((dDead[dx][dy] & 0x1F) == Monsters[mi]._udeadval)
ChangeLightXY(Monsters[mi].mlid, { dx, dy });
if ((dDead[dx][dy] & 0x1F) == monster._udeadval)
ChangeLightXY(monster.mlid, { dx, dy });
}
}
}

12
Source/debug.cpp

@ -39,13 +39,15 @@ void PrintDebugMonster(int m)
{
char dstr[128];
sprintf(dstr, "Monster %i = %s", m, _(Monsters[m].mName));
auto &monster = Monsters[m];
sprintf(dstr, "Monster %i = %s", m, _(monster.mName));
NetSendCmdString(1 << MyPlayerId, dstr);
sprintf(dstr, "X = %i, Y = %i", Monsters[m].position.tile.x, Monsters[m].position.tile.y);
sprintf(dstr, "X = %i, Y = %i", monster.position.tile.x, monster.position.tile.y);
NetSendCmdString(1 << MyPlayerId, dstr);
sprintf(dstr, "Enemy = %i, HP = %i", Monsters[m]._menemy, Monsters[m]._mhitpoints);
sprintf(dstr, "Enemy = %i, HP = %i", monster._menemy, monster._mhitpoints);
NetSendCmdString(1 << MyPlayerId, dstr);
sprintf(dstr, "Mode = %i, Var1 = %i", Monsters[m]._mmode, Monsters[m]._mVar1);
sprintf(dstr, "Mode = %i, Var1 = %i", monster._mmode, monster._mVar1);
NetSendCmdString(1 << MyPlayerId, dstr);
bool bActive = false;
@ -55,7 +57,7 @@ void PrintDebugMonster(int m)
bActive = true;
}
sprintf(dstr, "Active List = %i, Squelch = %i", bActive ? 1 : 0, Monsters[m]._msquelch);
sprintf(dstr, "Active List = %i, Squelch = %i", bActive ? 1 : 0, monster._msquelch);
NetSendCmdString(1 << MyPlayerId, dstr);
}

18
Source/diablo.cpp

@ -250,7 +250,7 @@ bool LeftMouseCmd(bool bShift)
if (bShift) {
NetSendCmdLoc(MyPlayerId, true, CMD_RATTACKXY, { cursmx, cursmy });
} else if (pcursmonst != -1) {
if (CanTalkToMonst(pcursmonst)) {
if (CanTalkToMonst(Monsters[pcursmonst])) {
NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst);
} else {
NetSendCmdParam1(true, CMD_RATTACKID, pcursmonst);
@ -261,7 +261,7 @@ bool LeftMouseCmd(bool bShift)
} else {
if (bShift) {
if (pcursmonst != -1) {
if (CanTalkToMonst(pcursmonst)) {
if (CanTalkToMonst(Monsters[pcursmonst])) {
NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst);
} else {
NetSendCmdLoc(MyPlayerId, true, CMD_SATTACKXY, { cursmx, cursmy });
@ -1188,16 +1188,16 @@ void CreateLevel(lvl_entry lvldir)
void UpdateMonsterLights()
{
for (int i = 0; i < ActiveMonsterCount; i++) {
MonsterStruct *mon = &Monsters[ActiveMonsters[i]];
if (mon->mlid != NO_LIGHT) {
if (mon->mlid == Players[MyPlayerId]._plid) { // Fix old saves where some monsters had 0 instead of NO_LIGHT
mon->mlid = NO_LIGHT;
auto &monster = Monsters[ActiveMonsters[i]];
if (monster.mlid != NO_LIGHT) {
if (monster.mlid == Players[MyPlayerId]._plid) { // Fix old saves where some monsters had 0 instead of NO_LIGHT
monster.mlid = NO_LIGHT;
continue;
}
LightStruct *lid = &Lights[mon->mlid];
if (mon->position.tile != lid->position.tile) {
ChangeLightXY(mon->mlid, mon->position.tile);
LightStruct *lid = &Lights[monster.mlid];
if (monster.position.tile != lid->position.tile) {
ChangeLightXY(monster.mlid, monster.position.tile);
}
}
}

58
Source/effects.cpp

@ -1095,25 +1095,6 @@ void StreamUpdate()
}
}
bool CalculatePosition(Point soundPosition, int *plVolume, int *plPan)
{
const auto &playerPosition = Players[MyPlayerId].position.tile;
const auto delta = soundPosition - playerPosition;
int pan = (delta.deltaX - delta.deltaY) * 256;
*plPan = clamp(pan, PAN_MIN, PAN_MAX);
int volume = playerPosition.ApproxDistance(soundPosition);
volume *= -64;
if (volume <= ATTENUATION_MIN)
return false;
*plVolume = volume;
return true;
}
void PlaySfxPriv(TSFX *pSFX, bool loc, Point position)
{
if (Players[MyPlayerId].pLvlLoad != 0 && gbIsMultiplayer) {
@ -1129,7 +1110,7 @@ void PlaySfxPriv(TSFX *pSFX, bool loc, Point position)
int lVolume = 0;
int lPan = 0;
if (loc && !CalculatePosition(position, &lVolume, &lPan)) {
if (loc && !CalculateSoundPosition(position, &lVolume, &lPan)) {
return;
}
@ -1255,29 +1236,23 @@ void FreeMonsterSnd()
}
}
void PlayEffect(int i, int mode)
bool CalculateSoundPosition(Point soundPosition, int *plVolume, int *plPan)
{
if (Players[MyPlayerId].pLvlLoad != 0) {
return;
}
const auto &playerPosition = Players[MyPlayerId].position.tile;
const auto delta = soundPosition - playerPosition;
int sndIdx = GenerateRnd(2);
if (!gbSndInited || !gbSoundOn || gbBufferMsgs != 0) {
return;
}
int pan = (delta.deltaX - delta.deltaY) * 256;
*plPan = clamp(pan, PAN_MIN, PAN_MAX);
int mi = Monsters[i]._mMTidx;
TSnd *snd = LevelMonsterTypes[mi].Snds[mode][sndIdx].get();
if (snd == nullptr || snd->isPlaying()) {
return;
}
int volume = playerPosition.ApproxDistance(soundPosition);
volume *= -64;
int lVolume = 0;
int lPan = 0;
if (!CalculatePosition(Monsters[i].position.tile, &lVolume, &lPan))
return;
if (volume <= ATTENUATION_MIN)
return false;
*plVolume = volume;
snd_play_snd(snd, lVolume, lPan);
return true;
}
void PlaySFX(_sfx_id psfx)
@ -1391,11 +1366,4 @@ int GetSFXLength(int nSFX)
return sgSFX[nSFX].pSnd->DSB.GetLength();
}
#ifdef RUN_TESTS
bool TestCalculatePosition(Point soundPosition, int *plVolume, int *plPan)
{
return CalculatePosition(soundPosition, plVolume, plPan);
}
#endif
} // namespace devilution

5
Source/effects.h

@ -1177,7 +1177,7 @@ bool effect_is_playing(int nSFX);
void stream_stop();
void InitMonsterSND(int monst);
void FreeMonsterSnd();
void PlayEffect(int i, int mode);
bool CalculateSoundPosition(Point soundPosition, int *plVolume, int *plPan);
void PlaySFX(_sfx_id psfx);
void PlaySfxLoc(_sfx_id psfx, Point position, bool randomizeByCategory = true);
void sound_stop();
@ -1188,9 +1188,6 @@ void ui_sound_init();
void effects_play_sound(const char *sndFile);
#ifndef NOSOUND
#ifdef RUN_TESTS
bool TestCalculatePosition(Point soundPosition, int *plVolume, int *plPan);
#endif
int GetSFXLength(int nSFX);
#endif

2
Source/effects_stubs.cpp

@ -12,7 +12,7 @@ bool effect_is_playing(int nSFX) { return false; }
void stream_stop() { }
void InitMonsterSND(int monst) { }
void FreeMonsterSnd() { }
void PlayEffect(int i, int mode) { }
bool CalculateSoundPosition(Point soundPosition, int *plVolume, int *plPan) { }
void PlaySFX(_sfx_id psfx) { }
void PlaySfxLoc(_sfx_id psfx, Point position, bool randomizeByCategory) { }
void sound_stop() { }

3
Source/inv.cpp

@ -2145,7 +2145,8 @@ void DoTelekinesis()
NetSendCmdParam1(true, CMD_OPOBJT, pcursobj);
if (pcursitem != -1)
NetSendCmdGItem(true, CMD_REQUESTAGITEM, MyPlayerId, MyPlayerId, pcursitem);
if (pcursmonst != -1 && !M_Talker(pcursmonst) && Monsters[pcursmonst].mtalkmsg == TEXT_NONE)
auto &monter = Monsters[pcursmonst];
if (pcursmonst != -1 && !M_Talker(monter) && monter.mtalkmsg == TEXT_NONE)
NetSendCmdParam1(true, CMD_KNOCKBACK, pcursmonst);
NewCursor(CURSOR_HAND);
}

38
Source/items.cpp

@ -1394,10 +1394,10 @@ void GetItemBonus(int i, int minlvl, int maxlvl, bool onlygood, bool allowspells
}
}
int RndUItem(int m)
int RndUItem(MonsterStruct *monster)
{
if (m != -1 && (Monsters[m].MData->mTreasure & 0x8000) != 0 && !gbIsMultiplayer)
return -((Monsters[m].MData->mTreasure & 0xFFF) + 1);
if (monster != nullptr && (monster->MData->mTreasure & 0x8000) != 0 && !gbIsMultiplayer)
return -((monster->MData->mTreasure & 0xFFF) + 1);
int ril[512];
@ -1410,8 +1410,8 @@ int RndUItem(int m)
bool okflag = true;
if (AllItemsList[i].iRnd == IDROP_NEVER)
okflag = false;
if (m != -1) {
if (Monsters[m].mLevel < AllItemsList[i].iMinMLvl)
if (monster != nullptr) {
if (monster->mLevel < AllItemsList[i].iMinMLvl)
okflag = false;
} else {
if (2 * curlv < AllItemsList[i].iMinMLvl)
@ -3342,12 +3342,12 @@ void SetupItem(int i)
Items[i]._iIdentified = false;
}
int RndItem(int m)
int RndItem(const MonsterStruct &monster)
{
if ((Monsters[m].MData->mTreasure & 0x8000) != 0)
return -((Monsters[m].MData->mTreasure & 0xFFF) + 1);
if ((monster.MData->mTreasure & 0x8000) != 0)
return -((monster.MData->mTreasure & 0xFFF) + 1);
if ((Monsters[m].MData->mTreasure & 0x4000) != 0)
if ((monster.MData->mTreasure & 0x4000) != 0)
return 0;
if (GenerateRnd(100) > 40)
@ -3363,12 +3363,12 @@ int RndItem(int m)
if (!IsItemAvailable(i))
continue;
if (AllItemsList[i].iRnd == IDROP_DOUBLE && Monsters[m].mLevel >= AllItemsList[i].iMinMLvl
if (AllItemsList[i].iRnd == IDROP_DOUBLE && monster.mLevel >= AllItemsList[i].iMinMLvl
&& ri < 512) {
ril[ri] = i;
ri++;
}
if (AllItemsList[i].iRnd != IDROP_NEVER && Monsters[m].mLevel >= AllItemsList[i].iMinMLvl
if (AllItemsList[i].iRnd != IDROP_NEVER && monster.mLevel >= AllItemsList[i].iMinMLvl
&& ri < 512) {
ril[ri] = i;
ri++;
@ -3401,20 +3401,20 @@ void SpawnUnique(_unique_items uid, Point position)
SetupItem(ii);
}
void SpawnItem(int m, Point position, bool sendmsg)
void SpawnItem(MonsterStruct &monster, Point position, bool sendmsg)
{
int idx;
bool onlygood = true;
if (Monsters[m]._uniqtype != 0 || ((Monsters[m].MData->mTreasure & 0x8000) != 0 && gbIsMultiplayer)) {
idx = RndUItem(m);
if (monster._uniqtype != 0 || ((monster.MData->mTreasure & 0x8000) != 0 && gbIsMultiplayer)) {
idx = RndUItem(&monster);
if (idx < 0) {
SpawnUnique((_unique_items) - (idx + 1), position);
return;
}
onlygood = true;
} else if (Quests[Q_MUSHROOM]._qactive != QUEST_ACTIVE || Quests[Q_MUSHROOM]._qvar1 != QS_MUSHGIVEN) {
idx = RndItem(m);
idx = RndItem(monster);
if (idx == 0)
return;
if (idx > 0) {
@ -3434,10 +3434,10 @@ void SpawnItem(int m, Point position, bool sendmsg)
int ii = AllocateItem();
GetSuperItemSpace(position, ii);
int uper = Monsters[m]._uniqtype != 0 ? 15 : 1;
int uper = monster._uniqtype != 0 ? 15 : 1;
int8_t mLevel = Monsters[m].MData->mLevel;
if (!gbIsHellfire && Monsters[m].MType->mtype == MT_DIABLO)
int8_t mLevel = monster.MData->mLevel;
if (!gbIsHellfire && monster.MType->mtype == MT_DIABLO)
mLevel -= 15;
SetupAllItems(ii, idx, AdvanceRndSeed(), mLevel, uper, onlygood, false, false);
@ -3448,7 +3448,7 @@ void SpawnItem(int m, Point position, bool sendmsg)
void CreateRndItem(Point position, bool onlygood, bool sendmsg, bool delta)
{
int idx = onlygood ? RndUItem(-1) : RndAllItems();
int idx = onlygood ? RndUItem(nullptr) : RndAllItems();
SetupBaseItem(position, idx, onlygood, sendmsg, delta);
}

5
Source/items.h

@ -12,6 +12,7 @@
#include "engine/point.hpp"
#include "engine.h"
#include "itemdat.h"
#include "monster.h"
#include "utils/stdcompat/optional.hpp"
namespace devilution {
@ -421,9 +422,9 @@ int AllocateItem();
Point GetSuperItemLoc(Point position);
void GetItemAttrs(int i, int idata, int lvl);
void SetupItem(int i);
int RndItem(int m);
int RndItem(const MonsterStruct &monster);
void SpawnUnique(_unique_items uid, Point position);
void SpawnItem(int m, Point position, bool sendmsg);
void SpawnItem(MonsterStruct &monster, Point position, bool sendmsg);
void CreateRndItem(Point position, bool onlygood, bool sendmsg, bool delta);
void CreateRndUseful(Point position, bool sendmsg);
void CreateTypeItem(Point position, bool onlygood, int itype, int imisc, bool sendmsg, bool delta);

278
Source/loadsave.cpp

@ -532,100 +532,98 @@ void LoadPlayer(LoadHelper *file, int p)
bool gbSkipSync = false;
void LoadMonster(LoadHelper *file, int i)
void LoadMonster(LoadHelper *file, MonsterStruct &monster)
{
MonsterStruct *pMonster = &Monsters[i];
pMonster->_mMTidx = file->NextLE<int32_t>();
pMonster->_mmode = static_cast<MON_MODE>(file->NextLE<int32_t>());
pMonster->_mgoal = static_cast<monster_goal>(file->NextLE<uint8_t>());
monster._mMTidx = file->NextLE<int32_t>();
monster._mmode = static_cast<MON_MODE>(file->NextLE<int32_t>());
monster._mgoal = static_cast<monster_goal>(file->NextLE<uint8_t>());
file->Skip(3); // Alignment
pMonster->_mgoalvar1 = file->NextLE<int32_t>();
pMonster->_mgoalvar2 = file->NextLE<int32_t>();
pMonster->_mgoalvar3 = file->NextLE<int32_t>();
monster._mgoalvar1 = file->NextLE<int32_t>();
monster._mgoalvar2 = file->NextLE<int32_t>();
monster._mgoalvar3 = file->NextLE<int32_t>();
file->Skip(4); // Unused
pMonster->_pathcount = file->NextLE<uint8_t>();
monster._pathcount = file->NextLE<uint8_t>();
file->Skip(3); // Alignment
pMonster->position.tile.x = file->NextLE<int32_t>();
pMonster->position.tile.y = file->NextLE<int32_t>();
pMonster->position.future.x = file->NextLE<int32_t>();
pMonster->position.future.y = file->NextLE<int32_t>();
pMonster->position.old.x = file->NextLE<int32_t>();
pMonster->position.old.y = file->NextLE<int32_t>();
pMonster->position.offset.deltaX = file->NextLE<int32_t>();
pMonster->position.offset.deltaY = file->NextLE<int32_t>();
pMonster->position.velocity.deltaX = file->NextLE<int32_t>();
pMonster->position.velocity.deltaY = file->NextLE<int32_t>();
pMonster->_mdir = static_cast<Direction>(file->NextLE<int32_t>());
pMonster->_menemy = file->NextLE<int32_t>();
pMonster->enemyPosition.x = file->NextLE<uint8_t>();
pMonster->enemyPosition.y = file->NextLE<uint8_t>();
monster.position.tile.x = file->NextLE<int32_t>();
monster.position.tile.y = file->NextLE<int32_t>();
monster.position.future.x = file->NextLE<int32_t>();
monster.position.future.y = file->NextLE<int32_t>();
monster.position.old.x = file->NextLE<int32_t>();
monster.position.old.y = file->NextLE<int32_t>();
monster.position.offset.deltaX = file->NextLE<int32_t>();
monster.position.offset.deltaY = file->NextLE<int32_t>();
monster.position.velocity.deltaX = file->NextLE<int32_t>();
monster.position.velocity.deltaY = file->NextLE<int32_t>();
monster._mdir = static_cast<Direction>(file->NextLE<int32_t>());
monster._menemy = file->NextLE<int32_t>();
monster.enemyPosition.x = file->NextLE<uint8_t>();
monster.enemyPosition.y = file->NextLE<uint8_t>();
file->Skip(2); // Unused
file->Skip(4); // Skip pointer _mAnimData
pMonster->AnimInfo = {};
pMonster->AnimInfo.TicksPerFrame = file->NextLE<int32_t>();
pMonster->AnimInfo.TickCounterOfCurrentFrame = file->NextLE<int32_t>();
pMonster->AnimInfo.NumberOfFrames = file->NextLE<int32_t>();
pMonster->AnimInfo.CurrentFrame = file->NextLE<int32_t>();
monster.AnimInfo = {};
monster.AnimInfo.TicksPerFrame = file->NextLE<int32_t>();
monster.AnimInfo.TickCounterOfCurrentFrame = file->NextLE<int32_t>();
monster.AnimInfo.NumberOfFrames = file->NextLE<int32_t>();
monster.AnimInfo.CurrentFrame = file->NextLE<int32_t>();
file->Skip(4); // Skip _meflag
pMonster->_mDelFlag = file->NextBool32();
pMonster->_mVar1 = file->NextLE<int32_t>();
pMonster->_mVar2 = file->NextLE<int32_t>();
pMonster->_mVar3 = file->NextLE<int32_t>();
pMonster->position.temp.x = file->NextLE<int32_t>();
pMonster->position.temp.y = file->NextLE<int32_t>();
pMonster->position.offset2.deltaX = file->NextLE<int32_t>();
pMonster->position.offset2.deltaY = file->NextLE<int32_t>();
monster._mDelFlag = file->NextBool32();
monster._mVar1 = file->NextLE<int32_t>();
monster._mVar2 = file->NextLE<int32_t>();
monster._mVar3 = file->NextLE<int32_t>();
monster.position.temp.x = file->NextLE<int32_t>();
monster.position.temp.y = file->NextLE<int32_t>();
monster.position.offset2.deltaX = file->NextLE<int32_t>();
monster.position.offset2.deltaY = file->NextLE<int32_t>();
file->Skip(4); // Skip actionFrame
pMonster->_mmaxhp = file->NextLE<int32_t>();
pMonster->_mhitpoints = file->NextLE<int32_t>();
monster._mmaxhp = file->NextLE<int32_t>();
monster._mhitpoints = file->NextLE<int32_t>();
pMonster->_mAi = static_cast<_mai_id>(file->NextLE<uint8_t>());
pMonster->_mint = file->NextLE<uint8_t>();
monster._mAi = static_cast<_mai_id>(file->NextLE<uint8_t>());
monster._mint = file->NextLE<uint8_t>();
file->Skip(2); // Alignment
pMonster->_mFlags = file->NextLE<uint32_t>();
pMonster->_msquelch = file->NextLE<uint8_t>();
monster._mFlags = file->NextLE<uint32_t>();
monster._msquelch = file->NextLE<uint8_t>();
file->Skip(3); // Alignment
file->Skip(4); // Unused
pMonster->position.last.x = file->NextLE<int32_t>();
pMonster->position.last.y = file->NextLE<int32_t>();
pMonster->_mRndSeed = file->NextLE<uint32_t>();
pMonster->_mAISeed = file->NextLE<uint32_t>();
monster.position.last.x = file->NextLE<int32_t>();
monster.position.last.y = file->NextLE<int32_t>();
monster._mRndSeed = file->NextLE<uint32_t>();
monster._mAISeed = file->NextLE<uint32_t>();
file->Skip(4); // Unused
pMonster->_uniqtype = file->NextLE<uint8_t>();
pMonster->_uniqtrans = file->NextLE<uint8_t>();
pMonster->_udeadval = file->NextLE<int8_t>();
monster._uniqtype = file->NextLE<uint8_t>();
monster._uniqtrans = file->NextLE<uint8_t>();
monster._udeadval = file->NextLE<int8_t>();
pMonster->mWhoHit = file->NextLE<int8_t>();
pMonster->mLevel = file->NextLE<int8_t>();
monster.mWhoHit = file->NextLE<int8_t>();
monster.mLevel = file->NextLE<int8_t>();
file->Skip(1); // Alignment
pMonster->mExp = file->NextLE<uint16_t>();
monster.mExp = file->NextLE<uint16_t>();
if (i < MAX_PLRS) // Don't skip for golems
pMonster->mHit = file->NextLE<uint8_t>();
if ((monster._mFlags & MFLAG_GOLEM) != 0) // Don't skip for golems
monster.mHit = file->NextLE<uint8_t>();
else
file->Skip(1); // Skip mHit as it's already initialized
pMonster->mMinDamage = file->NextLE<uint8_t>();
pMonster->mMaxDamage = file->NextLE<uint8_t>();
monster.mMinDamage = file->NextLE<uint8_t>();
monster.mMaxDamage = file->NextLE<uint8_t>();
file->Skip(1); // Skip mHit2 as it's already initialized
pMonster->mMinDamage2 = file->NextLE<uint8_t>();
pMonster->mMaxDamage2 = file->NextLE<uint8_t>();
pMonster->mArmorClass = file->NextLE<uint8_t>();
monster.mMinDamage2 = file->NextLE<uint8_t>();
monster.mMaxDamage2 = file->NextLE<uint8_t>();
monster.mArmorClass = file->NextLE<uint8_t>();
file->Skip(1); // Alignment
pMonster->mMagicRes = file->NextLE<uint16_t>();
monster.mMagicRes = file->NextLE<uint16_t>();
file->Skip(2); // Alignment
pMonster->mtalkmsg = static_cast<_speech_id>(file->NextLE<int32_t>());
if (pMonster->mtalkmsg == TEXT_KING1) // Fix original bad mapping of NONE for monsters
pMonster->mtalkmsg = TEXT_NONE;
pMonster->leader = file->NextLE<uint8_t>();
pMonster->leaderflag = file->NextLE<uint8_t>();
pMonster->packsize = file->NextLE<uint8_t>();
pMonster->mlid = file->NextLE<int8_t>();
if (pMonster->mlid == Players[MyPlayerId]._plid)
pMonster->mlid = NO_LIGHT; // Correct incorect values in old saves
monster.mtalkmsg = static_cast<_speech_id>(file->NextLE<int32_t>());
if (monster.mtalkmsg == TEXT_KING1) // Fix original bad mapping of NONE for monsters
monster.mtalkmsg = TEXT_NONE;
monster.leader = file->NextLE<uint8_t>();
monster.leaderflag = file->NextLE<uint8_t>();
monster.packsize = file->NextLE<uint8_t>();
monster.mlid = file->NextLE<int8_t>();
if (monster.mlid == Players[MyPlayerId]._plid)
monster.mlid = NO_LIGHT; // Correct incorect values in old saves
// Omit pointer mName;
// Omit pointer MType;
@ -634,7 +632,7 @@ void LoadMonster(LoadHelper *file, int i)
if (gbSkipSync)
return;
SyncMonsterAnim(i);
SyncMonsterAnim(monster);
}
void LoadMissile(LoadHelper *file, int i)
@ -1196,92 +1194,90 @@ void SavePlayer(SaveHelper *file, int p)
// Omit pointer pReserved
}
void SaveMonster(SaveHelper *file, int i)
void SaveMonster(SaveHelper *file, MonsterStruct &monster)
{
MonsterStruct *pMonster = &Monsters[i];
file->WriteLE<int32_t>(pMonster->_mMTidx);
file->WriteLE<int32_t>(pMonster->_mmode);
file->WriteLE<uint8_t>(pMonster->_mgoal);
file->WriteLE<int32_t>(monster._mMTidx);
file->WriteLE<int32_t>(monster._mmode);
file->WriteLE<uint8_t>(monster._mgoal);
file->Skip(3); // Alignment
file->WriteLE<int32_t>(pMonster->_mgoalvar1);
file->WriteLE<int32_t>(pMonster->_mgoalvar2);
file->WriteLE<int32_t>(pMonster->_mgoalvar3);
file->WriteLE<int32_t>(monster._mgoalvar1);
file->WriteLE<int32_t>(monster._mgoalvar2);
file->WriteLE<int32_t>(monster._mgoalvar3);
file->Skip(4); // Unused
file->WriteLE<uint8_t>(pMonster->_pathcount);
file->WriteLE<uint8_t>(monster._pathcount);
file->Skip(3); // Alignment
file->WriteLE<int32_t>(pMonster->position.tile.x);
file->WriteLE<int32_t>(pMonster->position.tile.y);
file->WriteLE<int32_t>(pMonster->position.future.x);
file->WriteLE<int32_t>(pMonster->position.future.y);
file->WriteLE<int32_t>(pMonster->position.old.x);
file->WriteLE<int32_t>(pMonster->position.old.y);
file->WriteLE<int32_t>(pMonster->position.offset.deltaX);
file->WriteLE<int32_t>(pMonster->position.offset.deltaY);
file->WriteLE<int32_t>(pMonster->position.velocity.deltaX);
file->WriteLE<int32_t>(pMonster->position.velocity.deltaY);
file->WriteLE<int32_t>(pMonster->_mdir);
file->WriteLE<int32_t>(pMonster->_menemy);
file->WriteLE<uint8_t>(pMonster->enemyPosition.x);
file->WriteLE<uint8_t>(pMonster->enemyPosition.y);
file->WriteLE<int32_t>(monster.position.tile.x);
file->WriteLE<int32_t>(monster.position.tile.y);
file->WriteLE<int32_t>(monster.position.future.x);
file->WriteLE<int32_t>(monster.position.future.y);
file->WriteLE<int32_t>(monster.position.old.x);
file->WriteLE<int32_t>(monster.position.old.y);
file->WriteLE<int32_t>(monster.position.offset.deltaX);
file->WriteLE<int32_t>(monster.position.offset.deltaY);
file->WriteLE<int32_t>(monster.position.velocity.deltaX);
file->WriteLE<int32_t>(monster.position.velocity.deltaY);
file->WriteLE<int32_t>(monster._mdir);
file->WriteLE<int32_t>(monster._menemy);
file->WriteLE<uint8_t>(monster.enemyPosition.x);
file->WriteLE<uint8_t>(monster.enemyPosition.y);
file->Skip(2); // Unused
file->Skip(4); // Skip pointer _mAnimData
file->WriteLE<int32_t>(pMonster->AnimInfo.TicksPerFrame);
file->WriteLE<int32_t>(pMonster->AnimInfo.TickCounterOfCurrentFrame);
file->WriteLE<int32_t>(pMonster->AnimInfo.NumberOfFrames);
file->WriteLE<int32_t>(pMonster->AnimInfo.CurrentFrame);
file->WriteLE<int32_t>(monster.AnimInfo.TicksPerFrame);
file->WriteLE<int32_t>(monster.AnimInfo.TickCounterOfCurrentFrame);
file->WriteLE<int32_t>(monster.AnimInfo.NumberOfFrames);
file->WriteLE<int32_t>(monster.AnimInfo.CurrentFrame);
file->Skip<uint32_t>(); // Skip _meflag
file->WriteLE<uint32_t>(pMonster->_mDelFlag ? 1 : 0);
file->WriteLE<int32_t>(pMonster->_mVar1);
file->WriteLE<int32_t>(pMonster->_mVar2);
file->WriteLE<int32_t>(pMonster->_mVar3);
file->WriteLE<int32_t>(pMonster->position.temp.x);
file->WriteLE<int32_t>(pMonster->position.temp.y);
file->WriteLE<int32_t>(pMonster->position.offset2.deltaX);
file->WriteLE<int32_t>(pMonster->position.offset2.deltaY);
file->WriteLE<uint32_t>(monster._mDelFlag ? 1 : 0);
file->WriteLE<int32_t>(monster._mVar1);
file->WriteLE<int32_t>(monster._mVar2);
file->WriteLE<int32_t>(monster._mVar3);
file->WriteLE<int32_t>(monster.position.temp.x);
file->WriteLE<int32_t>(monster.position.temp.y);
file->WriteLE<int32_t>(monster.position.offset2.deltaX);
file->WriteLE<int32_t>(monster.position.offset2.deltaY);
file->Skip<int32_t>(); // Skip _mVar8
file->WriteLE<int32_t>(pMonster->_mmaxhp);
file->WriteLE<int32_t>(pMonster->_mhitpoints);
file->WriteLE<int32_t>(monster._mmaxhp);
file->WriteLE<int32_t>(monster._mhitpoints);
file->WriteLE<uint8_t>(pMonster->_mAi);
file->WriteLE<uint8_t>(pMonster->_mint);
file->WriteLE<uint8_t>(monster._mAi);
file->WriteLE<uint8_t>(monster._mint);
file->Skip(2); // Alignment
file->WriteLE<uint32_t>(pMonster->_mFlags);
file->WriteLE<uint8_t>(pMonster->_msquelch);
file->WriteLE<uint32_t>(monster._mFlags);
file->WriteLE<uint8_t>(monster._msquelch);
file->Skip(3); // Alignment
file->Skip(4); // Unused
file->WriteLE<int32_t>(pMonster->position.last.x);
file->WriteLE<int32_t>(pMonster->position.last.y);
file->WriteLE<uint32_t>(pMonster->_mRndSeed);
file->WriteLE<uint32_t>(pMonster->_mAISeed);
file->WriteLE<int32_t>(monster.position.last.x);
file->WriteLE<int32_t>(monster.position.last.y);
file->WriteLE<uint32_t>(monster._mRndSeed);
file->WriteLE<uint32_t>(monster._mAISeed);
file->Skip(4); // Unused
file->WriteLE<uint8_t>(pMonster->_uniqtype);
file->WriteLE<uint8_t>(pMonster->_uniqtrans);
file->WriteLE<int8_t>(pMonster->_udeadval);
file->WriteLE<uint8_t>(monster._uniqtype);
file->WriteLE<uint8_t>(monster._uniqtrans);
file->WriteLE<int8_t>(monster._udeadval);
file->WriteLE<int8_t>(pMonster->mWhoHit);
file->WriteLE<int8_t>(pMonster->mLevel);
file->WriteLE<int8_t>(monster.mWhoHit);
file->WriteLE<int8_t>(monster.mLevel);
file->Skip(1); // Alignment
file->WriteLE<uint16_t>(pMonster->mExp);
file->WriteLE<uint8_t>(std::min<uint16_t>(pMonster->mHit, std::numeric_limits<uint8_t>::max())); // For backwards compatibility
file->WriteLE<uint8_t>(pMonster->mMinDamage);
file->WriteLE<uint8_t>(pMonster->mMaxDamage);
file->WriteLE<uint8_t>(std::min<uint16_t>(pMonster->mHit2, std::numeric_limits<uint8_t>::max())); // For backwards compatibility
file->WriteLE<uint8_t>(pMonster->mMinDamage2);
file->WriteLE<uint8_t>(pMonster->mMaxDamage2);
file->WriteLE<uint8_t>(pMonster->mArmorClass);
file->WriteLE<uint16_t>(monster.mExp);
file->WriteLE<uint8_t>(std::min<uint16_t>(monster.mHit, std::numeric_limits<uint8_t>::max())); // For backwards compatibility
file->WriteLE<uint8_t>(monster.mMinDamage);
file->WriteLE<uint8_t>(monster.mMaxDamage);
file->WriteLE<uint8_t>(std::min<uint16_t>(monster.mHit2, std::numeric_limits<uint8_t>::max())); // For backwards compatibility
file->WriteLE<uint8_t>(monster.mMinDamage2);
file->WriteLE<uint8_t>(monster.mMaxDamage2);
file->WriteLE<uint8_t>(monster.mArmorClass);
file->Skip(1); // Alignment
file->WriteLE<uint16_t>(pMonster->mMagicRes);
file->WriteLE<uint16_t>(monster.mMagicRes);
file->Skip(2); // Alignment
file->WriteLE<int32_t>(pMonster->mtalkmsg == TEXT_NONE ? 0 : pMonster->mtalkmsg); // Replicate original bad mapping of none for monsters
file->WriteLE<uint8_t>(pMonster->leader);
file->WriteLE<uint8_t>(pMonster->leaderflag);
file->WriteLE<uint8_t>(pMonster->packsize);
file->WriteLE<int8_t>(pMonster->mlid);
file->WriteLE<int32_t>(monster.mtalkmsg == TEXT_NONE ? 0 : monster.mtalkmsg); // Replicate original bad mapping of none for monsters
file->WriteLE<uint8_t>(monster.leader);
file->WriteLE<uint8_t>(monster.leaderflag);
file->WriteLE<uint8_t>(monster.packsize);
file->WriteLE<int8_t>(monster.mlid);
// Omit pointer mName;
// Omit pointer MType;
@ -1739,7 +1735,7 @@ void LoadGame(bool firstflag)
for (int &monsterId : ActiveMonsters)
monsterId = file.NextBE<int32_t>();
for (int i = 0; i < ActiveMonsterCount; i++)
LoadMonster(&file, ActiveMonsters[i]);
LoadMonster(&file, Monsters[ActiveMonsters[i]]);
for (int &missileId : ActiveMissiles)
missileId = file.NextLE<int8_t>();
for (int &missileId : AvailableMissiles)
@ -1930,7 +1926,7 @@ void SaveGameData()
for (int monsterId : ActiveMonsters)
file.WriteBE<int32_t>(monsterId);
for (int i = 0; i < ActiveMonsterCount; i++)
SaveMonster(&file, ActiveMonsters[i]);
SaveMonster(&file, Monsters[ActiveMonsters[i]]);
for (int missileId : ActiveMissiles)
file.WriteLE<int8_t>(missileId);
for (int missileId : AvailableMissiles)
@ -2059,7 +2055,7 @@ void SaveLevel()
for (int monsterId : ActiveMonsters)
file.WriteBE<int32_t>(monsterId);
for (int i = 0; i < ActiveMonsterCount; i++)
SaveMonster(&file, ActiveMonsters[i]);
SaveMonster(&file, Monsters[ActiveMonsters[i]]);
for (int objectId : ActiveObjects)
file.WriteLE<int8_t>(objectId);
for (int objectId : AvailableObjects)
@ -2142,7 +2138,7 @@ void LoadLevel()
for (int &monsterId : ActiveMonsters)
monsterId = file.NextBE<int32_t>();
for (int i = 0; i < ActiveMonsterCount; i++)
LoadMonster(&file, ActiveMonsters[i]);
LoadMonster(&file, Monsters[ActiveMonsters[i]]);
for (int &objectId : ActiveObjects)
objectId = file.NextLE<int8_t>();
for (int &objectId : AvailableObjects)

280
Source/missiles.cpp

@ -16,6 +16,7 @@
#include "init.h"
#include "inv.h"
#include "lighting.h"
#include "monster.h"
#include "spells.h"
#include "trigs.h"
@ -84,7 +85,7 @@ inline bool InDungeonBounds(Point position)
return position.x > 0 && position.x < MAXDUNX && position.y > 0 && position.y < MAXDUNY;
}
int FindClosest(Point source, int rad)
MonsterStruct *FindClosest(Point source, int rad)
{
if (rad > 19)
rad = 19;
@ -98,11 +99,11 @@ int FindClosest(Point source, int rad)
if (InDungeonBounds({ tx, ty })) {
int mid = dMonster[tx][ty];
if (mid > 0 && !CheckBlock(source, { tx, ty }))
return mid - 1;
return &Monsters[mid - 1];
}
}
}
return -1;
return nullptr;
}
constexpr Direction16 Direction16Flip(Direction16 x, Direction16 pivot)
@ -197,7 +198,7 @@ void MoveMissilePos(int i)
}
int x = Missiles[i].position.tile.x + dx;
int y = Missiles[i].position.tile.y + dy;
if (PosOkMonst(Missiles[i]._misource, { x, y })) {
if (MonsterIsTileAvalible(Missiles[i]._misource, { x, y })) {
Missiles[i].position.tile.x += dx;
Missiles[i].position.tile.y += dy;
Missiles[i].position.offset.deltaX += (dy * 32) - (dx * 32);
@ -207,18 +208,20 @@ void MoveMissilePos(int i)
bool MonsterMHit(int pnum, int m, int mindam, int maxdam, int dist, int t, bool shift)
{
auto &monster = Monsters[m];
bool resist = false;
if (Monsters[m].mtalkmsg != TEXT_NONE
|| Monsters[m]._mhitpoints >> 6 <= 0
|| (t == MIS_HBOLT && Monsters[m].MType->mtype != MT_DIABLO && Monsters[m].MData->mMonstClass != MC_UNDEAD)) {
if (monster.mtalkmsg != TEXT_NONE
|| monster._mhitpoints >> 6 <= 0
|| (t == MIS_HBOLT && monster.MType->mtype != MT_DIABLO && monster.MData->mMonstClass != MC_UNDEAD)) {
return false;
}
if (Monsters[m].MType->mtype == MT_ILLWEAV && Monsters[m]._mgoal == MGOAL_RETREAT)
if (monster.MType->mtype == MT_ILLWEAV && monster._mgoal == MGOAL_RETREAT)
return false;
if (Monsters[m]._mmode == MM_CHARGE)
if (monster._mmode == MM_CHARGE)
return false;
uint8_t mor = Monsters[m].mMagicRes;
uint8_t mor = monster.mMagicRes;
missile_resistance mir = MissileData[t].mResist;
if (((mor & IMMUNE_MAGIC) != 0 && mir == MISR_MAGIC)
@ -232,7 +235,7 @@ bool MonsterMHit(int pnum, int m, int mindam, int maxdam, int dist, int t, bool
|| ((mor & RESIST_LIGHTNING) != 0 && mir == MISR_LIGHTNING))
resist = true;
if (gbIsHellfire && t == MIS_HBOLT && (Monsters[m].MType->mtype == MT_DIABLO || Monsters[m].MType->mtype == MT_BONEDEMN))
if (gbIsHellfire && t == MIS_HBOLT && (monster.MType->mtype == MT_DIABLO || monster.MType->mtype == MT_BONEDEMN))
resist = true;
int hit = GenerateRnd(100);
@ -243,7 +246,7 @@ bool MonsterMHit(int pnum, int m, int mindam, int maxdam, int dist, int t, bool
hper = player._pDexterity;
hper += player._pIBonusToHit;
hper += player._pLevel;
hper -= Monsters[m].mArmorClass;
hper -= monster.mArmorClass;
hper -= (dist * dist) / 2;
hper += player._pIEnAc;
hper += 50;
@ -252,23 +255,23 @@ bool MonsterMHit(int pnum, int m, int mindam, int maxdam, int dist, int t, bool
if (player._pClass == HeroClass::Warrior || player._pClass == HeroClass::Bard)
hper += 10;
} else {
hper = player._pMagic - (Monsters[m].mLevel * 2) - dist + 50;
hper = player._pMagic - (monster.mLevel * 2) - dist + 50;
if (player._pClass == HeroClass::Sorcerer)
hper += 20;
else if (player._pClass == HeroClass::Bard)
hper += 10;
}
} else {
hper = GenerateRnd(75) - Monsters[m].mLevel * 2;
hper = GenerateRnd(75) - monster.mLevel * 2;
}
hper = clamp(hper, 5, 95);
if (Monsters[m]._mmode == MM_STONE)
if (monster._mmode == MM_STONE)
hit = 0;
bool ret = false;
if (CheckMonsterHit(m, &ret))
if (CheckMonsterHit(monster, &ret))
return ret;
#ifdef _DEBUG
@ -281,7 +284,7 @@ bool MonsterMHit(int pnum, int m, int mindam, int maxdam, int dist, int t, bool
int dam;
if (t == MIS_BONESPIRIT) {
dam = Monsters[m]._mhitpoints / 3 >> 6;
dam = monster._mhitpoints / 3 >> 6;
} else {
dam = mindam + GenerateRnd(maxdam - mindam + 1);
}
@ -301,25 +304,25 @@ bool MonsterMHit(int pnum, int m, int mindam, int maxdam, int dist, int t, bool
dam >>= 2;
if (pnum == MyPlayerId)
Monsters[m]._mhitpoints -= dam;
monster._mhitpoints -= dam;
if ((gbIsHellfire && (Players[pnum]._pIFlags & ISPL_NOHEALMON) != 0) || (!gbIsHellfire && (Players[pnum]._pIFlags & ISPL_FIRE_ARROWS) != 0))
Monsters[m]._mFlags |= MFLAG_NOHEAL;
monster._mFlags |= MFLAG_NOHEAL;
if (Monsters[m]._mhitpoints >> 6 <= 0) {
if (Monsters[m]._mmode == MM_STONE) {
if (monster._mhitpoints >> 6 <= 0) {
if (monster._mmode == MM_STONE) {
M_StartKill(m, pnum);
Monsters[m].Petrify();
monster.Petrify();
} else {
M_StartKill(m, pnum);
}
} else {
if (resist) {
PlayEffect(m, 1);
} else if (Monsters[m]._mmode == MM_STONE) {
PlayEffect(monster, 1);
} else if (monster._mmode == MM_STONE) {
if (m > MAX_PLRS - 1)
M_StartHit(m, pnum, dam);
Monsters[m].Petrify();
monster.Petrify();
} else {
if (MissileData[t].mType == 0 && (Players[pnum]._pIFlags & ISPL_KNOCKBACK) != 0) {
M_GetKnockback(m);
@ -329,9 +332,9 @@ bool MonsterMHit(int pnum, int m, int mindam, int maxdam, int dist, int t, bool
}
}
if (Monsters[m]._msquelch == 0) {
Monsters[m]._msquelch = UINT8_MAX;
Monsters[m].position.last = Players[pnum].position.tile;
if (monster._msquelch == 0) {
monster._msquelch = UINT8_MAX;
monster.position.last = Players[pnum].position.tile;
}
return true;
@ -518,7 +521,8 @@ void CheckMissileCol(int i, int mindam, int maxdam, bool shift, Point position,
Missiles[i]._miHitFlag = true;
}
} else {
if ((Monsters[Missiles[i]._misource]._mFlags & MFLAG_TARGETS_MONSTER) != 0
auto &monster = Monsters[Missiles[i]._misource];
if ((monster._mFlags & MFLAG_TARGETS_MONSTER) != 0
&& dMonster[mx][my] > 0
&& (Monsters[dMonster[mx][my] - 1]._mFlags & MFLAG_GOLEM) != 0
&& MonsterTrapHit(dMonster[mx][my] - 1, mindam, maxdam, Missiles[i]._midist, Missiles[i]._mitype, shift)) {
@ -529,7 +533,7 @@ void CheckMissileCol(int i, int mindam, int maxdam, bool shift, Point position,
if (dPlayer[mx][my] > 0
&& PlayerMHit(
dPlayer[mx][my] - 1,
Missiles[i]._misource,
&monster,
Missiles[i]._midist,
mindam,
maxdam,
@ -576,7 +580,7 @@ void CheckMissileCol(int i, int mindam, int maxdam, bool shift, Point position,
if (dPlayer[mx][my] > 0) {
if (PlayerMHit(
dPlayer[mx][my] - 1,
-1,
nullptr,
Missiles[i]._midist,
mindam,
maxdam,
@ -1022,20 +1026,22 @@ void DeleteMissile(int mi, int i)
bool MonsterTrapHit(int m, int mindam, int maxdam, int dist, int t, bool shift)
{
auto &monster = Monsters[m];
bool resist = false;
if (Monsters[m].mtalkmsg != TEXT_NONE) {
if (monster.mtalkmsg != TEXT_NONE) {
return false;
}
if (Monsters[m]._mhitpoints >> 6 <= 0) {
if (monster._mhitpoints >> 6 <= 0) {
return false;
}
if (Monsters[m].MType->mtype == MT_ILLWEAV && Monsters[m]._mgoal == MGOAL_RETREAT)
if (monster.MType->mtype == MT_ILLWEAV && monster._mgoal == MGOAL_RETREAT)
return false;
if (Monsters[m]._mmode == MM_CHARGE)
if (monster._mmode == MM_CHARGE)
return false;
missile_resistance mir = MissileData[t].mResist;
int mor = Monsters[m].mMagicRes;
int mor = monster.mMagicRes;
if (((mor & IMMUNE_MAGIC) != 0 && mir == MISR_MAGIC)
|| ((mor & IMMUNE_FIRE) != 0 && mir == MISR_FIRE)
|| ((mor & IMMUNE_LIGHTNING) != 0 && mir == MISR_LIGHTNING)) {
@ -1049,45 +1055,45 @@ bool MonsterTrapHit(int m, int mindam, int maxdam, int dist, int t, bool shift)
}
int hit = GenerateRnd(100);
int hper = 90 - (BYTE)Monsters[m].mArmorClass - dist;
int hper = 90 - (BYTE)monster.mArmorClass - dist;
if (hper < 5)
hper = 5;
if (hper > 95)
hper = 95;
bool ret;
if (CheckMonsterHit(m, &ret)) {
if (CheckMonsterHit(monster, &ret)) {
return ret;
}
#ifdef _DEBUG
if (hit < hper || debug_mode_dollar_sign || debug_mode_key_inverted_v || Monsters[m]._mmode == MM_STONE) {
if (hit < hper || debug_mode_dollar_sign || debug_mode_key_inverted_v || monster._mmode == MM_STONE) {
#else
if (hit < hper || Monsters[m]._mmode == MM_STONE) {
if (hit < hper || monster._mmode == MM_STONE) {
#endif
int dam = mindam + GenerateRnd(maxdam - mindam + 1);
if (!shift)
dam <<= 6;
if (resist)
Monsters[m]._mhitpoints -= dam / 4;
monster._mhitpoints -= dam / 4;
else
Monsters[m]._mhitpoints -= dam;
monster._mhitpoints -= dam;
#ifdef _DEBUG
if (debug_mode_dollar_sign || debug_mode_key_inverted_v)
Monsters[m]._mhitpoints = 0;
monster._mhitpoints = 0;
#endif
if (Monsters[m]._mhitpoints >> 6 <= 0) {
if (Monsters[m]._mmode == MM_STONE) {
if (monster._mhitpoints >> 6 <= 0) {
if (monster._mmode == MM_STONE) {
M_StartKill(m, -1);
Monsters[m].Petrify();
monster.Petrify();
} else {
M_StartKill(m, -1);
}
} else {
if (resist) {
PlayEffect(m, 1);
} else if (Monsters[m]._mmode == MM_STONE) {
PlayEffect(monster, 1);
} else if (monster._mmode == MM_STONE) {
if (m > MAX_PLRS - 1)
M_StartHit(m, -1, dam);
Monsters[m].Petrify();
monster.Petrify();
} else {
if (m > MAX_PLRS - 1)
M_StartHit(m, -1, dam);
@ -1098,7 +1104,7 @@ bool MonsterTrapHit(int m, int mindam, int maxdam, int dist, int t, bool shift)
return false;
}
bool PlayerMHit(int pnum, int m, int dist, int mind, int maxd, int mtype, bool shift, int earflag, bool *blocked)
bool PlayerMHit(int pnum, MonsterStruct *monster, int dist, int mind, int maxd, int mtype, bool shift, int earflag, bool *blocked)
{
*blocked = false;
@ -1124,16 +1130,16 @@ bool PlayerMHit(int pnum, int m, int dist, int mind, int maxd, int mtype, bool s
int hper = 40;
if (MissileData[mtype].mType == 0) {
int tac = player._pIAC + player._pIBonusAC + player._pDexterity / 5;
if (m != -1) {
hper = Monsters[m].mHit
+ ((Monsters[m].mLevel - player._pLevel) * 2)
if (monster != nullptr) {
hper = monster->mHit
+ ((monster->mLevel - player._pLevel) * 2)
+ 30
- (dist * 2) - tac;
} else {
hper = 100 - (tac / 2) - (dist * 2);
}
} else if (m != -1) {
hper += (Monsters[m].mLevel * 2) - (player._pLevel * 2) - (dist * 2);
} else if (monster != nullptr) {
hper += (monster->mLevel * 2) - (player._pLevel * 2) - (dist * 2);
}
if (hper < 10)
@ -1159,8 +1165,8 @@ bool PlayerMHit(int pnum, int m, int dist, int mind, int maxd, int mtype, bool s
blk = 100;
int blkper = player._pBaseToBlk + player._pDexterity;
if (m != -1)
blkper -= (Monsters[m].mLevel - player._pLevel) * 2;
if (monster != nullptr)
blkper -= (monster->mLevel - player._pLevel) * 2;
if (blkper < 0)
blkper = 0;
@ -1194,13 +1200,13 @@ bool PlayerMHit(int pnum, int m, int dist, int mind, int maxd, int mtype, bool s
} else {
if (!shift) {
dam = (mind << 6) + GenerateRnd((maxd - mind + 1) << 6);
if (m == -1)
if (monster == nullptr)
if ((player._pIFlags & ISPL_ABSHALFTRAP) != 0)
dam /= 2;
dam += (player._pIGetHit << 6);
} else {
dam = mind + GenerateRnd(maxd - mind + 1);
if (m == -1)
if (monster == nullptr)
if ((player._pIFlags & ISPL_ABSHALFTRAP) != 0)
dam /= 2;
dam += player._pIGetHit;
@ -1211,8 +1217,8 @@ bool PlayerMHit(int pnum, int m, int dist, int mind, int maxd, int mtype, bool s
if ((resper <= 0 || gbIsHellfire) && blk < blkper) {
Direction dir = player._pdir;
if (m != -1) {
dir = GetDirection(player.position.tile, Monsters[m].position.tile);
if (monster != nullptr) {
dir = GetDirection(player.position.tile, monster->position.tile);
}
*blocked = true;
StartPlrBlock(pnum, dir);
@ -1481,27 +1487,28 @@ void AddBerserk(int mi, Point /*src*/, Point dst, int /*midir*/, int8_t /*mienem
dm = dm > 0 ? dm - 1 : -(dm + 1);
if (dm <= 3)
continue;
auto &monster = Monsters[dm];
if (Monsters[dm]._uniqtype != 0 || Monsters[dm]._mAi == AI_DIABLO)
if (monster._uniqtype != 0 || monster._mAi == AI_DIABLO)
continue;
if (Monsters[dm]._mmode == MM_FADEIN || Monsters[dm]._mmode == MM_FADEOUT)
if (monster._mmode == MM_FADEIN || monster._mmode == MM_FADEOUT)
continue;
if ((Monsters[dm].mMagicRes & IMMUNE_MAGIC) != 0)
if ((monster.mMagicRes & IMMUNE_MAGIC) != 0)
continue;
if ((Monsters[dm].mMagicRes & RESIST_MAGIC) != 0 && ((Monsters[dm].mMagicRes & RESIST_MAGIC) != 1 || GenerateRnd(2) != 0))
if ((monster.mMagicRes & RESIST_MAGIC) != 0 && ((monster.mMagicRes & RESIST_MAGIC) != 1 || GenerateRnd(2) != 0))
continue;
if (Monsters[dm]._mmode == MM_CHARGE)
if (monster._mmode == MM_CHARGE)
continue;
i = 6;
auto slvl = GetSpellLevel(id, SPL_BERSERK);
Monsters[dm]._mFlags |= MFLAG_BERSERK | MFLAG_GOLEM;
Monsters[dm].mMinDamage = (GenerateRnd(10) + 120) * Monsters[dm].mMinDamage / 100 + slvl;
Monsters[dm].mMaxDamage = (GenerateRnd(10) + 120) * Monsters[dm].mMaxDamage / 100 + slvl;
Monsters[dm].mMinDamage2 = (GenerateRnd(10) + 120) * Monsters[dm].mMinDamage2 / 100 + slvl;
Monsters[dm].mMaxDamage2 = (GenerateRnd(10) + 120) * Monsters[dm].mMaxDamage2 / 100 + slvl;
monster._mFlags |= MFLAG_BERSERK | MFLAG_GOLEM;
monster.mMinDamage = (GenerateRnd(10) + 120) * monster.mMinDamage / 100 + slvl;
monster.mMaxDamage = (GenerateRnd(10) + 120) * monster.mMaxDamage / 100 + slvl;
monster.mMinDamage2 = (GenerateRnd(10) + 120) * monster.mMinDamage2 / 100 + slvl;
monster.mMaxDamage2 = (GenerateRnd(10) + 120) * monster.mMaxDamage2 / 100 + slvl;
int r = (currlevel < 17 || currlevel > 20) ? 3 : 9;
Monsters[dm].mlid = AddLight(Monsters[dm].position.tile, r);
monster.mlid = AddLight(monster.position.tile, r);
UseMana(id, SPL_BERSERK);
break;
}
@ -2479,33 +2486,36 @@ void InitMissileAnimationFromMonster(MissileStruct &mis, int midir, const Monste
void AddRhino(int mi, Point src, Point dst, int midir, int8_t /*mienemy*/, int id, int /*dam*/)
{
auto &monster = Monsters[id];
MonsterGraphic graphic = MonsterGraphic::Special;
if (Monsters[id].MType->mtype < MT_HORNED || Monsters[id].MType->mtype > MT_OBLORD) {
if (Monsters[id].MType->mtype < MT_NSNAKE || Monsters[id].MType->mtype > MT_GSNAKE) {
if (monster.MType->mtype < MT_HORNED || monster.MType->mtype > MT_OBLORD) {
if (monster.MType->mtype < MT_NSNAKE || monster.MType->mtype > MT_GSNAKE) {
graphic = MonsterGraphic::Walk;
} else {
graphic = MonsterGraphic::Attack;
}
}
UpdateMissileVelocity(mi, src, dst, 18);
InitMissileAnimationFromMonster(Missiles[mi], midir, Monsters[id], graphic);
if (Monsters[id].MType->mtype >= MT_NSNAKE && Monsters[id].MType->mtype <= MT_GSNAKE)
InitMissileAnimationFromMonster(Missiles[mi], midir, monster, graphic);
if (monster.MType->mtype >= MT_NSNAKE && monster.MType->mtype <= MT_GSNAKE)
Missiles[mi]._miAnimFrame = 7;
if (Monsters[id]._uniqtype != 0) {
Missiles[mi]._miUniqTrans = Monsters[id]._uniqtrans + 1;
Missiles[mi]._mlid = Monsters[id].mlid;
if (monster._uniqtype != 0) {
Missiles[mi]._miUniqTrans = monster._uniqtrans + 1;
Missiles[mi]._mlid = monster.mlid;
}
PutMissile(mi);
}
void AddFireman(int mi, Point src, Point dst, int midir, int8_t /*mienemy*/, int id, int /*dam*/)
{
auto &monster = Monsters[id];
UpdateMissileVelocity(mi, src, dst, 16);
auto &mon = Monsters[id];
InitMissileAnimationFromMonster(Missiles[mi], midir, mon, MonsterGraphic::Walk);
if (mon._uniqtype != 0)
Missiles[mi]._miUniqTrans = mon._uniqtrans + 1;
dMonster[mon.position.tile.x][mon.position.tile.y] = 0;
InitMissileAnimationFromMonster(Missiles[mi], midir, monster, MonsterGraphic::Walk);
if (monster._uniqtype != 0)
Missiles[mi]._miUniqTrans = monster._uniqtrans + 1;
dMonster[monster.position.tile.x][monster.position.tile.y] = 0;
PutMissile(mi);
}
@ -2522,17 +2532,16 @@ void AddFlare(int mi, Point src, Point dst, int midir, int8_t mienemy, int id, i
if (mienemy == TARGET_MONSTERS) {
UseMana(id, SPL_FLARE);
ApplyPlrDamage(id, 5);
} else {
if (id > 0) {
if (Monsters[id].MType->mtype == MT_SUCCUBUS)
SetMissAnim(mi, MFILE_FLARE);
if (Monsters[id].MType->mtype == MT_SNOWWICH)
SetMissAnim(mi, MFILE_SCUBMISB);
if (Monsters[id].MType->mtype == MT_HLSPWN)
SetMissAnim(mi, MFILE_SCUBMISD);
if (Monsters[id].MType->mtype == MT_SOLBRNR)
SetMissAnim(mi, MFILE_SCUBMISC);
}
} else if (id > 0) {
auto &monster = Monsters[id];
if (monster.MType->mtype == MT_SUCCUBUS)
SetMissAnim(mi, MFILE_FLARE);
if (monster.MType->mtype == MT_SNOWWICH)
SetMissAnim(mi, MFILE_SCUBMISB);
if (monster.MType->mtype == MT_HLSPWN)
SetMissAnim(mi, MFILE_SCUBMISD);
if (monster.MType->mtype == MT_SOLBRNR)
SetMissAnim(mi, MFILE_SCUBMISC);
}
if (MissileSpriteData[Missiles[mi]._miAnimType].mAnimFAmt == 16) {
@ -2589,13 +2598,14 @@ void AddStone(int mi, Point /*src*/, Point dst, int /*midir*/, int8_t /*mienemy*
if (InDungeonBounds({ tx, ty })) {
int mid = dMonster[tx][ty];
mid = mid > 0 ? mid - 1 : -(mid + 1);
if (mid > MAX_PLRS - 1 && Monsters[mid]._mAi != AI_DIABLO && Monsters[mid].MType->mtype != MT_NAKRUL) {
if (Monsters[mid]._mmode != MM_FADEIN && Monsters[mid]._mmode != MM_FADEOUT && Monsters[mid]._mmode != MM_CHARGE) {
auto &monster = Monsters[mid];
if (mid > MAX_PLRS - 1 && monster._mAi != AI_DIABLO && monster.MType->mtype != MT_NAKRUL) {
if (monster._mmode != MM_FADEIN && monster._mmode != MM_FADEOUT && monster._mmode != MM_CHARGE) {
faded = true;
i = 6;
Missiles[mi]._miVar1 = Monsters[mid]._mmode;
Missiles[mi]._miVar1 = monster._mmode;
Missiles[mi]._miVar2 = mid;
Monsters[mid].Petrify();
monster.Petrify();
break;
}
}
@ -2936,7 +2946,8 @@ void AddFlame(int mi, Point src, Point dst, int midir, int8_t mienemy, int id, i
int i = GenerateRnd(Players[id]._pLevel) + GenerateRnd(2);
Missiles[mi]._midam = 8 * i + 16 + ((8 * i + 16) / 2);
} else {
Missiles[mi]._midam = Monsters[id].mMinDamage + GenerateRnd(Monsters[id].mMaxDamage - Monsters[id].mMinDamage + 1);
auto &monster = Monsters[id];
Missiles[mi]._midam = monster.mMinDamage + GenerateRnd(monster.mMaxDamage - monster.mMinDamage + 1);
}
}
@ -3300,7 +3311,8 @@ void MI_Firebolt(int i)
break;
}
} else {
d = Monsters[p].mMinDamage + GenerateRnd(Monsters[p].mMaxDamage - Monsters[p].mMinDamage + 1);
auto &monster = Monsters[p];
d = monster.mMinDamage + GenerateRnd(monster.mMaxDamage - monster.mMinDamage + 1);
}
} else {
d = currlevel + GenerateRnd(2 * currlevel);
@ -3468,8 +3480,8 @@ void MI_HorkSpawn(int mi)
if (!nSolidTable[dp] && dMonster[tx][ty] == 0 && dPlayer[tx][ty] == 0 && dObject[tx][ty] == 0) {
i = 6;
auto md = static_cast<Direction>(Missiles[mi]._miVar1);
int mon = AddMonster({ tx, ty }, md, 1, true);
M_StartStand(mon, md);
int monsterId = AddMonster({ tx, ty }, md, 1, true);
M_StartStand(Monsters[monsterId], md);
break;
}
}
@ -3771,7 +3783,8 @@ void MI_Lightctrl(int i)
if (Missiles[i]._micaster == TARGET_MONSTERS) {
dam = (GenerateRnd(2) + GenerateRnd(Players[id]._pLevel) + 2) << 6;
} else {
dam = 2 * (Monsters[id].mMinDamage + GenerateRnd(Monsters[id].mMaxDamage - Monsters[id].mMinDamage + 1));
auto &monster = Monsters[id];
dam = 2 * (monster.mMinDamage + GenerateRnd(monster.mMaxDamage - monster.mMinDamage + 1));
}
} else {
dam = GenerateRnd(currlevel) + 2 * currlevel;
@ -4215,25 +4228,25 @@ void MI_Teleport(int i)
void MI_Stone(int i)
{
Missiles[i]._mirange--;
int m = Missiles[i]._miVar2;
if (Monsters[m]._mhitpoints == 0 && Missiles[i]._miAnimType != MFILE_SHATTER1) {
auto &monster = Monsters[Missiles[i]._miVar2];
if (monster._mhitpoints == 0 && Missiles[i]._miAnimType != MFILE_SHATTER1) {
Missiles[i]._mimfnum = 0;
Missiles[i]._miDrawFlag = true;
SetMissAnim(i, MFILE_SHATTER1);
Missiles[i]._mirange = 11;
}
if (Monsters[m]._mmode != MM_STONE) {
if (monster._mmode != MM_STONE) {
Missiles[i]._miDelFlag = true;
return;
}
if (Missiles[i]._mirange == 0) {
Missiles[i]._miDelFlag = true;
if (Monsters[m]._mhitpoints > 0) {
Monsters[m]._mmode = (MON_MODE)Missiles[i]._miVar1;
Monsters[m].AnimInfo.IsPetrified = false;
if (monster._mhitpoints > 0) {
monster._mmode = (MON_MODE)Missiles[i]._miVar1;
monster.AnimInfo.IsPetrified = false;
} else {
AddDead(Monsters[m].position.tile, stonendx, Monsters[m]._mdir);
AddDead(monster.position.tile, stonendx, monster._mdir);
}
}
if (Missiles[i]._miAnimType == MFILE_SHATTER1)
@ -4255,7 +4268,8 @@ void MI_Boom(int i)
void MI_Rhino(int i)
{
int monst = Missiles[i]._misource;
if (Monsters[monst]._mmode != MM_CHARGE) {
auto &monster = Monsters[monst];
if (monster._mmode != MM_CHARGE) {
Missiles[i]._miDelFlag = true;
return;
}
@ -4263,7 +4277,7 @@ void MI_Rhino(int i)
Point prevPos = Missiles[i].position.tile;
Point newPosSnake;
dMonster[prevPos.x][prevPos.y] = 0;
if (Monsters[monst]._mAi == AI_SNAKE) {
if (monster._mAi == AI_SNAKE) {
Missiles[i].position.traveled += Missiles[i].position.velocity * 2;
UpdateMissilePos(i);
newPosSnake = Missiles[i].position.tile;
@ -4273,16 +4287,16 @@ void MI_Rhino(int i)
}
UpdateMissilePos(i);
Point newPos = Missiles[i].position.tile;
if (!PosOkMonst(monst, newPos) || (Monsters[monst]._mAi == AI_SNAKE && !PosOkMonst(monst, newPosSnake))) {
if (!MonsterIsTileAvalible(monst, newPos) || (monster._mAi == AI_SNAKE && !MonsterIsTileAvalible(monst, newPosSnake))) {
MissToMonst(i, prevPos);
Missiles[i]._miDelFlag = true;
return;
}
Monsters[monst].position.future = newPos;
Monsters[monst].position.old = newPos;
Monsters[monst].position.tile = newPos;
monster.position.future = newPos;
monster.position.old = newPos;
monster.position.tile = newPos;
dMonster[newPos.x][newPos.y] = -(monst + 1);
if (Monsters[monst]._uniqtype != 0)
if (monster._uniqtype != 0)
ChangeLightXY(Missiles[i]._mlid, newPos);
MoveMissilePos(i);
PutMissile(i);
@ -4294,16 +4308,16 @@ void MI_Fireman(int i)
Point a = Missiles[i].position.tile;
Missiles[i].position.traveled += Missiles[i].position.velocity;
UpdateMissilePos(i);
int src = Missiles[i]._misource;
auto &monster = Monsters[Missiles[i]._misource];
Point b = Missiles[i].position.tile;
int enemy = Monsters[src]._menemy;
Point c = (Monsters[src]._mFlags & MFLAG_TARGETS_MONSTER) == 0 ? Players[enemy].position.tile : Monsters[enemy].position.tile;
int enemy = monster._menemy;
Point c = (monster._mFlags & MFLAG_TARGETS_MONSTER) == 0 ? Players[enemy].position.tile : Monsters[enemy].position.tile;
int j = 0;
if (b != a && (((Missiles[i]._miVar1 & 1) != 0 && a.WalkingDistance(c) >= 4) || Missiles[i]._miVar2 > 1) && PosOkMonst(Missiles[i]._misource, a)) {
if (b != a && (((Missiles[i]._miVar1 & 1) != 0 && a.WalkingDistance(c) >= 4) || Missiles[i]._miVar2 > 1) && MonsterIsTileAvalible(Missiles[i]._misource, a)) {
MissToMonst(i, a);
Missiles[i]._miDelFlag = true;
} else if ((Monsters[src]._mFlags & MFLAG_TARGETS_MONSTER) == 0) {
} else if ((monster._mFlags & MFLAG_TARGETS_MONSTER) == 0) {
j = dPlayer[b.x][b.y];
} else {
j = dMonster[b.x][b.y];
@ -4311,7 +4325,7 @@ void MI_Fireman(int i)
if (!PosOkMissile(0, b) || (j > 0 && (Missiles[i]._miVar1 & 1) == 0)) {
Missiles[i].position.velocity *= -1;
Missiles[i]._mimfnum = opposite[Missiles[i]._mimfnum];
Missiles[i]._miAnimData = Monsters[src].MType->GetAnimData(MonsterGraphic::Walk).CelSpritesForDirections[Missiles[i]._mimfnum]->Data();
Missiles[i]._miAnimData = monster.MType->GetAnimData(MonsterGraphic::Walk).CelSpritesForDirections[Missiles[i]._mimfnum]->Data();
Missiles[i]._miVar2++;
if (j > 0)
Missiles[i]._miVar1 |= 1;
@ -4658,11 +4672,11 @@ void MI_Element(int i)
if (Missiles[i]._miVar3 == 1) {
Missiles[i]._miVar3 = 2;
Missiles[i]._mirange = 255;
int mid = FindClosest(c, 19);
if (mid > 0) {
Direction sd = GetDirection(c, Monsters[mid].position.tile);
auto *monster = FindClosest(c, 19);
if (monster != nullptr) {
Direction sd = GetDirection(c, monster->position.tile);
SetMissDir(i, sd);
UpdateMissileVelocity(i, c, Monsters[mid].position.tile, 16);
UpdateMissileVelocity(i, c, monster->position.tile, 16);
} else {
Direction sd = Players[id]._pdir;
SetMissDir(i, sd);
@ -4705,11 +4719,11 @@ void MI_Bonespirit(int i)
if (Missiles[i]._miVar3 == 1) {
Missiles[i]._miVar3 = 2;
Missiles[i]._mirange = 255;
int mid = FindClosest(c, 19);
if (mid > 0) {
Missiles[i]._midam = Monsters[mid]._mhitpoints >> 7;
SetMissDir(i, GetDirection(c, Monsters[mid].position.tile));
UpdateMissileVelocity(i, c, Monsters[mid].position.tile, 16);
auto *monster = FindClosest(c, 19);
if (monster != nullptr) {
Missiles[i]._midam = monster->_mhitpoints >> 7;
SetMissDir(i, GetDirection(c, monster->position.tile));
UpdateMissileVelocity(i, c, monster->position.tile, 16);
} else {
Direction sd = Players[id]._pdir;
SetMissDir(i, sd);

3
Source/missiles.h

@ -11,6 +11,7 @@
#include "engine.h"
#include "engine/point.hpp"
#include "misdat.h"
#include "monster.h"
#include "spelldat.h"
namespace devilution {
@ -128,7 +129,7 @@ int GetSpellLevel(int playerId, spell_id sn);
Direction16 GetDirection16(Point p1, Point p2);
void DeleteMissile(int mi, int i);
bool MonsterTrapHit(int m, int mindam, int maxdam, int dist, int t, bool shift);
bool PlayerMHit(int pnum, int m, int dist, int mind, int maxd, int mtype, bool shift, int earflag, bool *blocked);
bool PlayerMHit(int pnum, MonsterStruct *monster, int dist, int mind, int maxd, int mtype, bool shift, int earflag, bool *blocked);
void SetMissDir(int mi, int dir);
void LoadMissileGFX(BYTE mi);
void InitMissileGFX();

3444
Source/monster.cpp

File diff suppressed because it is too large Load Diff

21
Source/monster.h

@ -231,10 +231,10 @@ void InitMonsterGFX(int monst);
void monster_some_crypt();
void InitMonsters();
void SetMapMonsters(const uint16_t *dunData, Point startPosition);
int AddMonster(Point position, Direction dir, int mtype, bool InMap);
int AddMonster(Point position, Direction dir, int mtype, bool inMap);
void AddDoppelganger(MonsterStruct &monster);
bool M_Talker(int i);
void M_StartStand(int i, Direction md);
bool M_Talker(MonsterStruct &monster);
void M_StartStand(MonsterStruct &monster, Direction md);
void M_ClearSquares(int i);
void M_GetKnockback(int i);
void M_StartHit(int i, int pnum, int dam);
@ -252,22 +252,23 @@ bool DirOK(int i, Direction mdir);
bool PosOkMissile(int entity, Point position);
bool LineClearMissile(Point startPoint, Point endPoint);
bool LineClear(bool (*Clear)(int, Point), int entity, Point startPoint, Point endPoint);
void SyncMonsterAnim(int i);
void SyncMonsterAnim(MonsterStruct &monster);
void M_FallenFear(Point position);
void PrintMonstHistory(int mt);
void PrintUniqueHistory();
void PlayEffect(MonsterStruct &monster, int mode);
void MissToMonst(int i, Point position);
bool PosOkMonst(int i, Point position);
bool MonsterIsTileAvalible(int i, Point position);
bool IsSkel(int mt);
bool IsGoat(int mt);
bool SpawnSkeleton(int ii, Point position);
int PreSpawnSkeleton();
void TalktoMonster(int i);
void TalktoMonster(MonsterStruct &monster);
void SpawnGolum(int i, Point position, int mi);
bool CanTalkToMonst(int m);
bool CheckMonsterHit(int m, bool *ret);
int encode_enemy(int m);
void decode_enemy(int m, int enemy);
bool CanTalkToMonst(const MonsterStruct &monster);
bool CheckMonsterHit(MonsterStruct &monster, bool *ret);
int encode_enemy(MonsterStruct &monster);
void decode_enemy(MonsterStruct &monster, int enemy);
extern Direction left[8];
extern Direction right[8];

61
Source/msg.cpp

@ -387,16 +387,17 @@ void DeltaLeaveSync(BYTE bLevel)
for (int i = 0; i < ActiveMonsterCount; i++) {
int ma = ActiveMonsters[i];
if (Monsters[ma]._mhitpoints == 0)
auto &monster = Monsters[ma];
if (monster._mhitpoints == 0)
continue;
sgbDeltaChanged = true;
DMonsterStr *pD = &sgLevels[bLevel].monster[ma];
pD->_mx = Monsters[ma].position.tile.x;
pD->_my = Monsters[ma].position.tile.y;
pD->_mdir = Monsters[ma]._mdir;
pD->_menemy = encode_enemy(ma);
pD->_mhitpoints = Monsters[ma]._mhitpoints;
pD->_mactive = Monsters[ma]._msquelch;
pD->_mx = monster.position.tile.x;
pD->_my = monster.position.tile.y;
pD->_mdir = monster._mdir;
pD->_menemy = encode_enemy(monster);
pD->_mhitpoints = monster._mhitpoints;
pD->_mactive = monster._msquelch;
}
memcpy(&sgLocals[bLevel].automapsv, AutomapView, sizeof(AutomapView));
}
@ -1320,12 +1321,13 @@ DWORD OnMonstDamage(TCmd *pCmd, int pnum)
SendPacket(pnum, p, sizeof(*p)); // BUGFIX: change to sizeof(*p) or it still uses TCmdParam2 size for hellfire (fixed)
else if (pnum != MyPlayerId) {
if (currlevel == Players[pnum].plrlevel) {
Monsters[p->wMon].mWhoHit |= 1 << pnum;
if (Monsters[p->wMon]._mhitpoints > 0) {
Monsters[p->wMon]._mhitpoints -= p->dwDam;
if ((Monsters[p->wMon]._mhitpoints >> 6) < 1)
Monsters[p->wMon]._mhitpoints = 1 << 6;
delta_monster_hp(p->wMon, Monsters[p->wMon]._mhitpoints, Players[pnum].plrlevel);
auto &monster = Monsters[p->wMon];
monster.mWhoHit |= 1 << pnum;
if (monster._mhitpoints > 0) {
monster._mhitpoints -= p->dwDam;
if ((monster._mhitpoints >> 6) < 1)
monster._mhitpoints = 1 << 6;
delta_monster_hp(p->wMon, monster._mhitpoints, Players[pnum].plrlevel);
}
}
}
@ -2009,34 +2011,35 @@ void DeltaLoadLevel()
M_ClearSquares(i);
int x = sgLevels[currlevel].monster[i]._mx;
int y = sgLevels[currlevel].monster[i]._my;
Monsters[i].position.tile = { x, y };
Monsters[i].position.old = { x, y };
Monsters[i].position.future = { x, y };
auto &monster = Monsters[i];
monster.position.tile = { x, y };
monster.position.old = { x, y };
monster.position.future = { x, y };
if (sgLevels[currlevel].monster[i]._mhitpoints != -1)
Monsters[i]._mhitpoints = sgLevels[currlevel].monster[i]._mhitpoints;
monster._mhitpoints = sgLevels[currlevel].monster[i]._mhitpoints;
if (sgLevels[currlevel].monster[i]._mhitpoints == 0) {
M_ClearSquares(i);
if (Monsters[i]._mAi != AI_DIABLO) {
if (Monsters[i]._uniqtype == 0) {
assert(Monsters[i].MType != nullptr);
AddDead(Monsters[i].position.tile, Monsters[i].MType->mdeadval, Monsters[i]._mdir);
if (monster._mAi != AI_DIABLO) {
if (monster._uniqtype == 0) {
assert(monster.MType != nullptr);
AddDead(monster.position.tile, monster.MType->mdeadval, monster._mdir);
} else {
AddDead(Monsters[i].position.tile, Monsters[i]._udeadval, Monsters[i]._mdir);
AddDead(monster.position.tile, monster._udeadval, monster._mdir);
}
}
Monsters[i]._mDelFlag = true;
monster._mDelFlag = true;
M_UpdateLeader(i);
} else {
decode_enemy(i, sgLevels[currlevel].monster[i]._menemy);
if (Monsters[i].position.tile != Point { 0, 0 } && Monsters[i].position.tile != Point { 1, 0 })
dMonster[Monsters[i].position.tile.x][Monsters[i].position.tile.y] = i + 1;
decode_enemy(monster, sgLevels[currlevel].monster[i]._menemy);
if (monster.position.tile != Point { 0, 0 } && monster.position.tile != Point { 1, 0 })
dMonster[monster.position.tile.x][monster.position.tile.y] = i + 1;
if (i < MAX_PLRS) {
GolumAi(i);
Monsters[i]._mFlags |= (MFLAG_TARGETS_MONSTER | MFLAG_GOLEM);
monster._mFlags |= (MFLAG_TARGETS_MONSTER | MFLAG_GOLEM);
} else {
M_StartStand(i, Monsters[i]._mdir);
M_StartStand(monster, monster._mdir);
}
Monsters[i]._msquelch = sgLevels[currlevel].monster[i]._mactive;
monster._msquelch = sgLevels[currlevel].monster[i]._mactive;
}
}
}

36
Source/objects.cpp

@ -19,6 +19,7 @@
#include "lighting.h"
#include "minitext.h"
#include "missiles.h"
#include "monster.h"
#include "options.h"
#include "setmaps.h"
#include "stores.h"
@ -1987,7 +1988,7 @@ void Obj_FlameTrap(int i)
MonsterTrapHit(dMonster[x][y] - 1, mindam / 2, maxdam / 2, 0, MIS_FIREWALLC, false);
if (dPlayer[x][y] > 0) {
bool unused;
PlayerMHit(dPlayer[x][y] - 1, -1, 0, mindam, maxdam, MIS_FIREWALLC, false, 0, &unused);
PlayerMHit(dPlayer[x][y] - 1, nullptr, 0, mindam, maxdam, MIS_FIREWALLC, false, 0, &unused);
}
if (Objects[i]._oAnimFrame == Objects[i]._oAnimLen)
@ -2704,10 +2705,10 @@ void OperateL3LDoor(int pnum, int oi, bool sendflag)
}
}
void MonstCheckDoors(int m)
void MonstCheckDoors(MonsterStruct &monster)
{
int mx = Monsters[m].position.tile.x;
int my = Monsters[m].position.tile.y;
int mx = monster.position.tile.x;
int my = monster.position.tile.y;
if (dObject[mx - 1][my - 1] != 0
|| dObject[mx][my - 1] != 0
|| dObject[mx + 1][my - 1] != 0
@ -4421,15 +4422,18 @@ void OperateBookCase(int pnum, int i, bool sendmsg)
}
SetRndSeed(Objects[i]._oRndSeed);
CreateTypeItem(Objects[i].position, false, ITYPE_MISC, IMISC_BOOK, sendmsg, false);
if (QuestStatus(Q_ZHAR)
&& Monsters[MAX_PLRS]._mmode == MM_STAND // prevents playing the "angry" message for the second time if zhar got aggroed by losing vision and talking again
&& Monsters[MAX_PLRS]._uniqtype - 1 == UMT_ZHAR
&& Monsters[MAX_PLRS]._msquelch == UINT8_MAX
&& Monsters[MAX_PLRS]._mhitpoints > 0) {
Monsters[MAX_PLRS].mtalkmsg = TEXT_ZHAR2;
M_StartStand(0, Monsters[MAX_PLRS]._mdir); // BUGFIX: first parameter in call to M_StartStand should be MAX_PLRS, not 0.
Monsters[MAX_PLRS]._mgoal = MGOAL_ATTACK2;
Monsters[MAX_PLRS]._mmode = MM_TALK;
if (QuestStatus(Q_ZHAR)) {
auto &zhar = Monsters[MAX_PLRS];
if (zhar._mmode == MM_STAND // prevents playing the "angry" message for the second time if zhar got aggroed by losing vision and talking again
&& zhar._uniqtype - 1 == UMT_ZHAR
&& zhar._msquelch == UINT8_MAX
&& zhar._mhitpoints > 0) {
zhar.mtalkmsg = TEXT_ZHAR2;
M_StartStand(Monsters[0], zhar._mdir); // BUGFIX: first parameter in call to M_StartStand should be MAX_PLRS, not 0.
zhar._mgoal = MGOAL_ATTACK2;
zhar._mmode = MM_TALK;
}
}
if (pnum == MyPlayerId)
NetSendCmdParam1(false, CMD_OPERATEOBJ, i);
@ -4995,10 +4999,10 @@ void SyncOpObject(int pnum, int cmd, int i)
/**
* @brief Checks if all active crux objects of the given type have been broken.
*
*
* Called by BreakCrux and SyncCrux to see if the linked map area needs to be updated. In practice I think this is
* always true when called by BreakCrux as there *should* only be one instance of each crux with a given _oVar8 value?
*
*
* @param cruxType Discriminator/type (_oVar8 value) of the crux object which is currently changing state
* @return true if all active cruxes of that type on the level are broken, false if at least one remains unbroken
*/
@ -5083,7 +5087,7 @@ void BreakBarrel(int pnum, int i, int dam, bool forcebreak, bool sendmsg)
MonsterTrapHit(dMonster[xp][yp] - 1, 1, 4, 0, MIS_FIREBOLT, false);
bool unused;
if (dPlayer[xp][yp] > 0)
PlayerMHit(dPlayer[xp][yp] - 1, -1, 0, 8, 16, MIS_FIREBOLT, false, 0, &unused);
PlayerMHit(dPlayer[xp][yp] - 1, nullptr, 0, 8, 16, MIS_FIREBOLT, false, 0, &unused);
if (dObject[xp][yp] > 0) {
int oi = dObject[xp][yp] - 1;
if (Objects[oi]._otype == OBJ_BARRELEX && Objects[oi]._oBreak != -1)

3
Source/objects.h

@ -10,6 +10,7 @@
#include "engine/point.hpp"
#include "engine/rectangle.hpp"
#include "itemdat.h"
#include "monster.h"
#include "objdat.h"
#include "textdat.h"
@ -151,7 +152,7 @@ void Obj_Trap(int i);
void ProcessObjects();
void ObjSetMicro(Point position, int pn);
void RedoPlayerVision();
void MonstCheckDoors(int m);
void MonstCheckDoors(MonsterStruct &monster);
void ObjChangeMap(int x1, int y1, int x2, int y2);
void ObjChangeMapResync(int x1, int y1, int x2, int y2);
void TryDisarm(int pnum, int i);

70
Source/player.cpp

@ -1934,14 +1934,17 @@ void SyncPlrKill(int pnum, int earflag)
void RemovePlrMissiles(int pnum)
{
if (currlevel != 0 && pnum == MyPlayerId && (Monsters[MyPlayerId].position.tile.x != 1 || Monsters[MyPlayerId].position.tile.y != 0)) {
M_StartKill(MyPlayerId, MyPlayerId);
AddDead(Monsters[MyPlayerId].position.tile, (Monsters[MyPlayerId].MType)->mdeadval, Monsters[MyPlayerId]._mdir);
int mx = Monsters[MyPlayerId].position.tile.x;
int my = Monsters[MyPlayerId].position.tile.y;
dMonster[mx][my] = 0;
Monsters[MyPlayerId]._mDelFlag = true;
DeleteMonsterList();
if (currlevel != 0 && pnum == MyPlayerId) {
auto &golem = Monsters[MyPlayerId];
if (golem.position.tile.x != 1 || golem.position.tile.y != 0) {
M_StartKill(MyPlayerId, MyPlayerId);
AddDead(golem.position.tile, (golem.MType)->mdeadval, golem._mdir);
int mx = golem.position.tile.x;
int my = golem.position.tile.y;
dMonster[mx][my] = 0;
golem._mDelFlag = true;
DeleteMonsterList();
}
}
for (int i = 0; i < ActiveMissileCount; i++) {
@ -2254,17 +2257,18 @@ bool PlrHitMonst(int pnum, int m)
if ((DWORD)m >= MAXMONSTERS) {
app_fatal("PlrHitMonst: illegal monster %i", m);
}
auto &monster = Monsters[m];
auto &player = Players[pnum];
if ((Monsters[m]._mhitpoints >> 6) <= 0) {
if ((monster._mhitpoints >> 6) <= 0) {
return false;
}
if (Monsters[m].MType->mtype == MT_ILLWEAV && Monsters[m]._mgoal == MGOAL_RETREAT) {
if (monster.MType->mtype == MT_ILLWEAV && monster._mgoal == MGOAL_RETREAT) {
return false;
}
if (Monsters[m]._mmode == MM_CHARGE) {
if (monster._mmode == MM_CHARGE) {
return false;
}
@ -2282,11 +2286,11 @@ bool PlrHitMonst(int pnum, int m)
}
int hit = GenerateRnd(100);
if (Monsters[m]._mmode == MM_STONE) {
if (monster._mmode == MM_STONE) {
hit = 0;
}
int tmac = Monsters[m].mArmorClass;
int tmac = monster.mArmorClass;
if (gbIsHellfire && player._pIEnAc > 0) {
int pIEnAc = player._pIEnAc - 1;
if (pIEnAc > 0)
@ -2295,7 +2299,7 @@ bool PlrHitMonst(int pnum, int m)
tmac -= tmac / 4;
if (player._pClass == HeroClass::Barbarian) {
tmac -= Monsters[m].mArmorClass / 8;
tmac -= monster.mArmorClass / 8;
}
if (tmac < 0)
@ -2317,7 +2321,7 @@ bool PlrHitMonst(int pnum, int m)
}
bool ret = false;
if (CheckMonsterHit(m, &ret)) {
if (CheckMonsterHit(monster, &ret)) {
return ret;
}
#ifdef _DEBUG
@ -2352,7 +2356,7 @@ bool PlrHitMonst(int pnum, int m)
phanditype = ITYPE_MACE;
}
switch (Monsters[m].MData->mMonstClass) {
switch (monster.MData->mMonstClass) {
case MC_UNDEAD:
if (phanditype == ITYPE_SWORD) {
dam -= dam / 2;
@ -2378,8 +2382,8 @@ bool PlrHitMonst(int pnum, int m)
dam *= 3;
}
if ((player.pDamAcFlags & ISPLHF_DOPPELGANGER) != 0 && Monsters[m].MType->mtype != MT_DIABLO && Monsters[m]._uniqtype == 0 && GenerateRnd(100) < 10) {
AddDoppelganger(Monsters[m]);
if ((player.pDamAcFlags & ISPLHF_DOPPELGANGER) != 0 && monster.MType->mtype != MT_DIABLO && monster._uniqtype == 0 && GenerateRnd(100) < 10) {
AddDoppelganger(monster);
}
dam <<= 6;
@ -2401,7 +2405,7 @@ bool PlrHitMonst(int pnum, int m)
}
dam *= 2;
}
Monsters[m]._mhitpoints -= dam;
monster._mhitpoints -= dam;
}
int skdam = 0;
@ -2452,24 +2456,24 @@ bool PlrHitMonst(int pnum, int m)
drawhpflag = true;
}
if ((player._pIFlags & ISPL_NOHEALPLR) != 0) {
Monsters[m]._mFlags |= MFLAG_NOHEAL;
monster._mFlags |= MFLAG_NOHEAL;
}
#ifdef _DEBUG
if (debug_mode_dollar_sign || debug_mode_key_inverted_v) {
Monsters[m]._mhitpoints = 0; /* double check */
monster._mhitpoints = 0; /* double check */
}
#endif
if ((Monsters[m]._mhitpoints >> 6) <= 0) {
if (Monsters[m]._mmode == MM_STONE) {
if ((monster._mhitpoints >> 6) <= 0) {
if (monster._mmode == MM_STONE) {
M_StartKill(m, pnum);
Monsters[m].Petrify();
monster.Petrify();
} else {
M_StartKill(m, pnum);
}
} else {
if (Monsters[m]._mmode == MM_STONE) {
if (monster._mmode == MM_STONE) {
M_StartHit(m, pnum, dam);
Monsters[m].Petrify();
monster.Petrify();
} else {
if ((player._pIFlags & ISPL_KNOCKBACK) != 0) {
M_GetKnockback(m);
@ -2614,7 +2618,7 @@ bool PM_DoAttack(int pnum)
} else {
m = -(dMonster[dx][dy] + 1);
}
if (CanTalkToMonst(m)) {
if (CanTalkToMonst(Monsters[m])) {
player.position.temp.x = 0; /** @todo Looks to be irrelevant, probably just remove it */
return false;
}
@ -2662,15 +2666,15 @@ bool PM_DoAttack(int pnum)
dx = position.x;
dy = position.y;
int m = ((dMonster[dx][dy] > 0) ? dMonster[dx][dy] : -dMonster[dx][dy]) - 1;
if (dMonster[dx][dy] != 0 && !CanTalkToMonst(m) && Monsters[m].position.old.x == dx && Monsters[m].position.old.y == dy) {
auto &monster = Monsters[m];
if (dMonster[dx][dy] != 0 && !CanTalkToMonst(monster) && monster.position.old.x == dx && monster.position.old.y == dy) {
if (PlrHitMonst(-pnum, m))
didhit = true;
}
position = player.position.tile + left[player._pdir];
dx = position.x;
dy = position.y;
m = ((dMonster[dx][dy] > 0) ? dMonster[dx][dy] : -dMonster[dx][dy]) - 1;
if (dMonster[dx][dy] != 0 && !CanTalkToMonst(m) && Monsters[m].position.old.x == dx && Monsters[m].position.old.y == dy) {
if (dMonster[dx][dy] != 0 && !CanTalkToMonst(monster) && monster.position.old.x == dx && monster.position.old.y == dy) {
if (PlrHitMonst(-pnum, m))
didhit = true;
}
@ -2990,7 +2994,7 @@ void CheckNewPath(int pnum, bool pmWillBeCalled)
if (x < 2 && y < 2) {
ClrPlrPath(player);
if (player.destAction == ACTION_ATTACKMON && Monsters[i].mtalkmsg != TEXT_NONE && Monsters[i].mtalkmsg != TEXT_VILE14) {
TalktoMonster(i);
TalktoMonster(Monsters[i]);
} else {
StartAttack(pnum, d);
}
@ -3065,7 +3069,7 @@ void CheckNewPath(int pnum, bool pmWillBeCalled)
if (x <= 1 && y <= 1) {
d = GetDirection(player.position.future, Monsters[i].position.future);
if (Monsters[i].mtalkmsg != TEXT_NONE && Monsters[i].mtalkmsg != TEXT_VILE14) {
TalktoMonster(i);
TalktoMonster(Monsters[i]);
} else {
StartAttack(pnum, d);
}
@ -3088,7 +3092,7 @@ void CheckNewPath(int pnum, bool pmWillBeCalled)
i = player.destParam1;
d = GetDirection(player.position.future, Monsters[i].position.future);
if (Monsters[i].mtalkmsg != TEXT_NONE && Monsters[i].mtalkmsg != TEXT_VILE14) {
TalktoMonster(i);
TalktoMonster(Monsters[i]);
} else {
StartRangeAttack(pnum, d, Monsters[i].position.future.x, Monsters[i].position.future.y);
}

22
Source/qol/monhealthbar.cpp

@ -59,7 +59,7 @@ void DrawMonsterHealthBar(const Surface &out)
if (pcursmonst == -1)
return;
const MonsterStruct &mon = Monsters[pcursmonst];
const MonsterStruct &monster = Monsters[pcursmonst];
const int width = healthBox.w();
const int height = healthBox.h();
@ -75,18 +75,18 @@ void DrawMonsterHealthBar(const Surface &out)
const int yPos = 18;
const int border = 3;
const int maxLife = std::max(mon._mmaxhp, mon._mhitpoints);
const int maxLife = std::max(monster._mmaxhp, monster._mhitpoints);
DrawArt(out, xPos, yPos, &healthBox);
DrawHalfTransparentRectTo(out, xPos + border, yPos + border, width - (border * 2), height - (border * 2));
int barProgress = (width * mon._mhitpoints) / maxLife;
int barProgress = (width * monster._mhitpoints) / maxLife;
if (barProgress != 0) {
DrawArt(out, xPos + border + 1, yPos + border + 1, &health, 0, barProgress, height - (border * 2) - 2);
}
if (sgOptions.Gameplay.bShowMonsterType) {
Uint8 borderColors[] = { 248 /*undead*/, 232 /*demon*/, 150 /*beast*/ };
Uint8 borderColor = borderColors[mon.MData->mMonstClass];
Uint8 borderColor = borderColors[monster.MData->mMonstClass];
int borderWidth = width - (border * 2);
UnsafeDrawHorizontalLine(out, { xPos + border, yPos + border }, borderWidth, borderColor);
UnsafeDrawHorizontalLine(out, { xPos + border, yPos + height - border - 1 }, borderWidth, borderColor);
@ -96,24 +96,24 @@ void DrawMonsterHealthBar(const Surface &out)
}
int barLabelY = yPos + 10 + (height - 11) / 2;
DrawString(out, mon.mName, { { xPos - 1, barLabelY + 1 }, { width, height } }, UIS_CENTER | UIS_BLACK);
DrawString(out, monster.mName, { { xPos - 1, barLabelY + 1 }, { width, height } }, UIS_CENTER | UIS_BLACK);
uint16_t style = UIS_SILVER;
if (mon._uniqtype != 0)
if (monster._uniqtype != 0)
style = UIS_GOLD;
else if (mon.leader != 0)
else if (monster.leader != 0)
style = UIS_BLUE;
DrawString(out, mon.mName, { { xPos, barLabelY }, { width, height } }, UIS_CENTER | style);
DrawString(out, monster.mName, { { xPos, barLabelY }, { width, height } }, UIS_CENTER | style);
if (mon._uniqtype != 0 || MonsterKillCounts[mon.MType->mtype] >= 15) {
if (monster._uniqtype != 0 || MonsterKillCounts[monster.MType->mtype] >= 15) {
monster_resistance immunes[] = { IMMUNE_MAGIC, IMMUNE_FIRE, IMMUNE_LIGHTNING };
monster_resistance resists[] = { RESIST_MAGIC, RESIST_FIRE, RESIST_LIGHTNING };
int resOffset = 5;
for (int i = 0; i < 3; i++) {
if ((mon.mMagicRes & immunes[i]) != 0) {
if ((monster.mMagicRes & immunes[i]) != 0) {
DrawArt(out, xPos + resOffset, yPos + height - 6, &resistance, i * 2 + 1);
resOffset += resistance.w() + 2;
} else if ((mon.mMagicRes & resists[i]) != 0) {
} else if ((monster.mMagicRes & resists[i]) != 0) {
DrawArt(out, xPos + resOffset, yPos + height - 6, &resistance, i * 2);
resOffset += resistance.w() + 2;
}

17
Source/quests.cpp

@ -17,6 +17,7 @@
#include "init.h"
#include "minitext.h"
#include "missiles.h"
#include "monster.h"
#include "options.h"
#include "stores.h"
#include "towners.h"
@ -301,29 +302,29 @@ bool QuestStatus(int i)
return true;
}
void CheckQuestKill(int m, bool sendmsg)
void CheckQuestKill(const MonsterStruct &monster, bool sendmsg)
{
if (gbIsSpawn)
return;
if (Monsters[m].MType->mtype == MT_SKING) {
if (monster.MType->mtype == MT_SKING) {
Quests[Q_SKELKING]._qactive = QUEST_DONE;
Players[MyPlayerId].Say(HeroSpeech::RestWellLeoricIllFindYourSon, 30);
if (sendmsg)
NetSendCmdQuest(true, Q_SKELKING);
} else if (Monsters[m].MType->mtype == MT_CLEAVER) {
} else if (monster.MType->mtype == MT_CLEAVER) {
Quests[Q_BUTCHER]._qactive = QUEST_DONE;
Players[MyPlayerId].Say(HeroSpeech::TheSpiritsOfTheDeadAreNowAvenged, 30);
if (sendmsg)
NetSendCmdQuest(true, Q_BUTCHER);
} else if (Monsters[m]._uniqtype - 1 == UMT_GARBUD) { //"Gharbad the Weak"
} else if (monster._uniqtype - 1 == UMT_GARBUD) { //"Gharbad the Weak"
Quests[Q_GARBUD]._qactive = QUEST_DONE;
Players[MyPlayerId].Say(HeroSpeech::ImNotImpressed, 30);
} else if (Monsters[m]._uniqtype - 1 == UMT_ZHAR) { //"Zhar the Mad"
} else if (monster._uniqtype - 1 == UMT_ZHAR) { //"Zhar the Mad"
Quests[Q_ZHAR]._qactive = QUEST_DONE;
Players[MyPlayerId].Say(HeroSpeech::ImSorryDidIBreakYourConcentration, 30);
} else if (Monsters[m]._uniqtype - 1 == UMT_LAZARUS && gbIsMultiplayer) { //"Arch-Bishop Lazarus"
} else if (monster._uniqtype - 1 == UMT_LAZARUS && gbIsMultiplayer) { //"Arch-Bishop Lazarus"
Quests[Q_BETRAYER]._qactive = QUEST_DONE;
Quests[Q_BETRAYER]._qvar1 = 7;
Quests[Q_DIABLO]._qactive = QUEST_ACTIVE;
@ -342,7 +343,7 @@ void CheckQuestKill(int m, bool sendmsg)
NetSendCmdQuest(true, Q_BETRAYER);
NetSendCmdQuest(true, Q_DIABLO);
}
} else if (Monsters[m]._uniqtype - 1 == UMT_LAZARUS && !gbIsMultiplayer) { //"Arch-Bishop Lazarus"
} else if (monster._uniqtype - 1 == UMT_LAZARUS && !gbIsMultiplayer) { //"Arch-Bishop Lazarus"
Quests[Q_BETRAYER]._qactive = QUEST_DONE;
InitVPTriggers();
Quests[Q_BETRAYER]._qvar1 = 7;
@ -350,7 +351,7 @@ void CheckQuestKill(int m, bool sendmsg)
Quests[Q_DIABLO]._qactive = QUEST_ACTIVE;
AddMissile({ 35, 32 }, { 35, 32 }, 0, MIS_RPORTAL, TARGET_MONSTERS, MyPlayerId, 0, 0);
Players[MyPlayerId].Say(HeroSpeech::YourMadnessEndsHereBetrayer, 30);
} else if (Monsters[m]._uniqtype - 1 == UMT_WARLORD) { //"Warlord of Blood"
} else if (monster._uniqtype - 1 == UMT_WARLORD) { //"Warlord of Blood"
Quests[Q_WARLORD]._qactive = QUEST_DONE;
Players[MyPlayerId].Say(HeroSpeech::YourReignOfPainHasEnded, 30);
}

3
Source/quests.h

@ -11,6 +11,7 @@
#include "engine/cel_sprite.hpp"
#include "engine/point.hpp"
#include "gendung.h"
#include "monster.h"
#include "textdat.h"
#include "utils/stdcompat/optional.hpp"
@ -80,7 +81,7 @@ void InitQuests();
void CheckQuests();
bool ForceQuests();
bool QuestStatus(int i);
void CheckQuestKill(int m, bool sendmsg);
void CheckQuestKill(const MonsterStruct &monster, bool sendmsg);
void DRLG_CheckQuests(int x, int y);
void SetReturnLvlPos();
void GetReturnLvlPos();

51
Source/scrollrt.cpp

@ -414,45 +414,40 @@ void DrawMissile(const Surface &out, int x, int y, int sx, int sy, bool pre)
* @param my Output buffer coordinate
* @param m Id of monster
*/
static void DrawMonster(const Surface &out, int x, int y, int mx, int my, int m)
static void DrawMonster(const Surface &out, int x, int y, int mx, int my, const MonsterStruct &monster)
{
if (m < 0 || m >= MAXMONSTERS) {
Log("Draw Monster: tried to draw illegal monster {}", m);
if (monster.AnimInfo.pCelSprite == nullptr) {
Log("Draw Monster \"{}\": NULL Cel Buffer", monster.mName);
return;
}
if (Monsters[m].AnimInfo.pCelSprite == nullptr) {
Log("Draw Monster \"{}\": NULL Cel Buffer", Monsters[m].mName);
return;
}
int nCel = Monsters[m].AnimInfo.GetFrameToUseForRendering();
const auto *frameTable = reinterpret_cast<const uint32_t *>(Monsters[m].AnimInfo.pCelSprite->Data());
int nCel = monster.AnimInfo.GetFrameToUseForRendering();
const auto *frameTable = reinterpret_cast<const uint32_t *>(monster.AnimInfo.pCelSprite->Data());
int frames = SDL_SwapLE32(frameTable[0]);
if (nCel < 1 || frames > 50 || nCel > frames) {
const char *szMode = "unknown action";
if (Monsters[m]._mmode <= 17)
szMode = MonsterModeNames[Monsters[m]._mmode];
if (monster._mmode <= 17)
szMode = MonsterModeNames[monster._mmode];
Log(
"Draw Monster \"{}\" {}: facing {}, frame {} of {}",
Monsters[m].mName,
monster.mName,
szMode,
Monsters[m]._mdir,
monster._mdir,
nCel,
frames);
return;
}
const auto &cel = *Monsters[m].AnimInfo.pCelSprite;
const auto &cel = *monster.AnimInfo.pCelSprite;
if ((dFlags[x][y] & BFLAG_LIT) == 0) {
Cl2DrawLightTbl(out, mx, my, cel, nCel, 1);
return;
}
int trans = 0;
if (Monsters[m]._uniqtype != 0)
trans = Monsters[m]._uniqtrans + 4;
if (Monsters[m]._mmode == MM_STONE)
if (monster._uniqtype != 0)
trans = monster._uniqtrans + 4;
if (monster._mmode == MM_STONE)
trans = 2;
if (Players[MyPlayerId]._pInfraFlag && LightTableIndex > 8)
trans = 1;
@ -788,29 +783,29 @@ static void DrawMonsterHelper(const Surface &out, int x, int y, int oy, int sx,
return;
}
MonsterStruct *pMonster = &Monsters[mi];
if ((pMonster->_mFlags & MFLAG_HIDDEN) != 0) {
const auto &monster = Monsters[mi];
if ((monster._mFlags & MFLAG_HIDDEN) != 0) {
return;
}
if (pMonster->MType == nullptr) {
Log("Draw Monster \"{}\": uninitialized monster", pMonster->mName);
if (monster.MType == nullptr) {
Log("Draw Monster \"{}\": uninitialized monster", monster.mName);
return;
}
const CelSprite &cel = *pMonster->AnimInfo.pCelSprite;
const CelSprite &cel = *monster.AnimInfo.pCelSprite;
Displacement offset = pMonster->position.offset;
if (pMonster->IsWalking()) {
offset = GetOffsetForWalking(pMonster->AnimInfo, pMonster->_mdir);
Displacement offset = monster.position.offset;
if (monster.IsWalking()) {
offset = GetOffsetForWalking(monster.AnimInfo, monster._mdir);
}
int px = sx + offset.deltaX - CalculateWidth2(cel.Width());
int py = sy + offset.deltaY;
if (mi == pcursmonst) {
Cl2DrawOutline(out, 233, px, py, cel, pMonster->AnimInfo.GetFrameToUseForRendering());
Cl2DrawOutline(out, 233, px, py, cel, monster.AnimInfo.GetFrameToUseForRendering());
}
DrawMonster(out, x, y, px, py, mi);
DrawMonster(out, x, y, px, py, monster);
}
/**

46
Source/sync.cpp

@ -23,8 +23,9 @@ void SyncOneMonster()
{
for (int i = 0; i < ActiveMonsterCount; i++) {
int m = ActiveMonsters[i];
sgnMonsterPriority[m] = Players[MyPlayerId].position.tile.ManhattanDistance(Monsters[m].position.tile);
if (Monsters[m]._msquelch == 0) {
auto &monster = Monsters[m];
sgnMonsterPriority[m] = Players[MyPlayerId].position.tile.ManhattanDistance(monster.position.tile);
if (monster._msquelch == 0) {
sgnMonsterPriority[m] += 0x1000;
} else if (sgwLRU[m] != 0) {
sgwLRU[m]--;
@ -34,14 +35,15 @@ void SyncOneMonster()
void SyncMonsterPos(TSyncMonster *p, int ndx)
{
auto &monster = Monsters[ndx];
p->_mndx = ndx;
p->_mx = Monsters[ndx].position.tile.x;
p->_my = Monsters[ndx].position.tile.y;
p->_menemy = encode_enemy(ndx);
p->_mx = monster.position.tile.x;
p->_my = monster.position.tile.y;
p->_menemy = encode_enemy(monster);
p->_mdelta = sgnMonsterPriority[ndx] > 255 ? 255 : sgnMonsterPriority[ndx];
sgnMonsterPriority[ndx] = 0xFFFF;
sgwLRU[ndx] = Monsters[ndx]._msquelch == 0 ? 0xFFFF : 0xFFFE;
sgwLRU[ndx] = monster._msquelch == 0 ? 0xFFFF : 0xFFFE;
}
bool SyncMonsterActive(TSyncMonster *p)
@ -193,11 +195,13 @@ static void SyncMonster(int pnum, const TSyncMonster *p)
{
int ndx = p->_mndx;
if (Monsters[ndx]._mhitpoints <= 0) {
auto &monster = Monsters[ndx];
if (monster._mhitpoints <= 0) {
return;
}
uint32_t delta = Players[MyPlayerId].position.tile.ManhattanDistance(Monsters[ndx].position.tile);
uint32_t delta = Players[MyPlayerId].position.tile.ManhattanDistance(monster.position.tile);
if (delta > 255) {
delta = 255;
}
@ -205,34 +209,34 @@ static void SyncMonster(int pnum, const TSyncMonster *p)
if (delta < p->_mdelta || (delta == p->_mdelta && pnum > MyPlayerId)) {
return;
}
if (Monsters[ndx].position.future.x == p->_mx && Monsters[ndx].position.future.y == p->_my) {
if (monster.position.future.x == p->_mx && monster.position.future.y == p->_my) {
return;
}
if (Monsters[ndx]._mmode == MM_CHARGE || Monsters[ndx]._mmode == MM_STONE) {
if (monster._mmode == MM_CHARGE || monster._mmode == MM_STONE) {
return;
}
if (Monsters[ndx].position.tile.WalkingDistance({ p->_mx, p->_my }) <= 2) {
if (Monsters[ndx]._mmode < MM_WALK || Monsters[ndx]._mmode > MM_WALK3) {
Direction md = GetDirection(Monsters[ndx].position.tile, { p->_mx, p->_my });
if (monster.position.tile.WalkingDistance({ p->_mx, p->_my }) <= 2) {
if (monster._mmode < MM_WALK || monster._mmode > MM_WALK3) {
Direction md = GetDirection(monster.position.tile, { p->_mx, p->_my });
if (DirOK(ndx, md)) {
M_ClearSquares(ndx);
dMonster[Monsters[ndx].position.tile.x][Monsters[ndx].position.tile.y] = ndx + 1;
dMonster[monster.position.tile.x][monster.position.tile.y] = ndx + 1;
M_WalkDir(ndx, md);
Monsters[ndx]._msquelch = UINT8_MAX;
monster._msquelch = UINT8_MAX;
}
}
} else if (dMonster[p->_mx][p->_my] == 0) {
M_ClearSquares(ndx);
dMonster[p->_mx][p->_my] = ndx + 1;
Monsters[ndx].position.tile = { p->_mx, p->_my };
decode_enemy(ndx, p->_menemy);
Direction md = GetDirection({ p->_mx, p->_my }, Monsters[ndx].enemyPosition);
M_StartStand(ndx, md);
Monsters[ndx]._msquelch = UINT8_MAX;
monster.position.tile = { p->_mx, p->_my };
decode_enemy(monster, p->_menemy);
Direction md = GetDirection({ p->_mx, p->_my }, monster.enemyPosition);
M_StartStand(monster, md);
monster._msquelch = UINT8_MAX;
}
decode_enemy(ndx, p->_menemy);
decode_enemy(monster, p->_menemy);
}
uint32_t sync_update(int pnum, const byte *pbBuf)

20
test/effects_test.cpp

@ -5,52 +5,52 @@
using namespace devilution;
TEST(Effects, CalculatePosition_center)
TEST(Effects, CalculateSoundPosition_center)
{
Players[MyPlayerId].position.tile = { 50, 50 };
int plVolume = 0;
int plPan = 0;
EXPECT_EQ(TestCalculatePosition({ 50, 50 }, &plVolume, &plPan), true);
EXPECT_EQ(CalculateSoundPosition({ 50, 50 }, &plVolume, &plPan), true);
EXPECT_EQ(plVolume, 0);
EXPECT_EQ(plPan, 0);
}
TEST(Effects, CalculatePosition_near)
TEST(Effects, CalculateSoundPosition_near)
{
Players[MyPlayerId].position.tile = { 50, 50 };
int plVolume = 0;
int plPan = 0;
EXPECT_EQ(TestCalculatePosition({ 55, 50 }, &plVolume, &plPan), true);
EXPECT_EQ(CalculateSoundPosition({ 55, 50 }, &plVolume, &plPan), true);
EXPECT_EQ(plVolume, -320);
EXPECT_EQ(plPan, 1280);
}
TEST(Effects, CalculatePosition_out_of_range)
TEST(Effects, CalculateSoundPosition_out_of_range)
{
Players[MyPlayerId].position.tile = { 12, 12 };
int plVolume = 1234;
int plPan = 0;
EXPECT_EQ(TestCalculatePosition({ 112, 112 }, &plVolume, &plPan), false);
EXPECT_EQ(CalculateSoundPosition({ 112, 112 }, &plVolume, &plPan), false);
EXPECT_EQ(plVolume, 1234);
EXPECT_EQ(plPan, 0);
}
TEST(Effects, CalculatePosition_extreme_right)
TEST(Effects, CalculateSoundPosition_extreme_right)
{
Players[MyPlayerId].position.tile = { 50, 50 };
int plVolume = 0;
int plPan = 0;
EXPECT_EQ(TestCalculatePosition({ 75, 25 }, &plVolume, &plPan), true);
EXPECT_EQ(CalculateSoundPosition({ 75, 25 }, &plVolume, &plPan), true);
EXPECT_EQ(plVolume, -2176);
EXPECT_EQ(plPan, 6400);
}
TEST(Effects, CalculatePosition_extreme_left)
TEST(Effects, CalculateSoundPosition_extreme_left)
{
Players[MyPlayerId].position.tile = { 50, 50 };
int plVolume = 0;
int plPan = 0;
EXPECT_EQ(TestCalculatePosition({ 25, 75 }, &plVolume, &plPan), true);
EXPECT_EQ(CalculateSoundPosition({ 25, 75 }, &plVolume, &plPan), true);
EXPECT_EQ(plVolume, -2176);
EXPECT_EQ(plPan, -6400);
}

Loading…
Cancel
Save