diff --git a/Source/monster.cpp b/Source/monster.cpp index e8905a23d..3b35e5479 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -1259,6 +1259,46 @@ void MonsterAttackPlayer(Monster &monster, Player &player, int hit, int minDam, } } +struct MeleeAttackWindow { + int frame; + int toHitDelta; + int minDamageDelta; + int maxDamageDelta; + bool playAttackSound; +}; + +struct MeleeAttackWindowsView { + const MeleeAttackWindow *data; + size_t size; +}; + +MeleeAttackWindowsView GetExtraMeleeAttackWindows(const Monster &monster) +{ + static constexpr MeleeAttackWindow MagmaAttackWindows[] = { + { 8, 10, -2, -2, true }, + }; + + static constexpr MeleeAttackWindow StormAttackWindows[] = { + { 12, -20, 4, 4, true }, + }; + + switch (monster.type().type) { + case MT_NMAGMA: + case MT_YMAGMA: + case MT_BMAGMA: + case MT_WMAGMA: + return { MagmaAttackWindows, std::size(MagmaAttackWindows) }; + + case MT_STORM: + case MT_RSTORM: + case MT_STORML: + case MT_MAEL: + return { StormAttackWindows, std::size(StormAttackWindows) }; + } + + return { nullptr, 0 }; +} + void MonsterAttackEnemy(Monster &monster, int hit, int minDam, int maxDam) { if ((monster.flags & MFLAG_NO_ENEMY) == 0) { @@ -1271,22 +1311,32 @@ void MonsterAttackEnemy(Monster &monster, int hit, int minDam, int maxDam) bool MonsterAttack(Monster &monster) { - if (monster.animInfo.currentFrame == monster.data().animFrameNum - 1) { - MonsterAttackEnemy(monster, monster.toHit(sgGameInitInfo.nDifficulty), monster.minDamage, monster.maxDamage); + const int currentFrame = monster.animInfo.currentFrame; + const int baseToHit = monster.toHit(sgGameInitInfo.nDifficulty); + + if (currentFrame == monster.data().animFrameNum - 1) { + MonsterAttackEnemy(monster, baseToHit, monster.minDamage, monster.maxDamage); if (monster.ai != MonsterAIID::Snake) PlayEffect(monster, MonsterSound::Attack); } - if (IsAnyOf(monster.type().type, MT_NMAGMA, MT_YMAGMA, MT_BMAGMA, MT_WMAGMA) && monster.animInfo.currentFrame == 8) { - MonsterAttackEnemy(monster, monster.toHit(sgGameInitInfo.nDifficulty) + 10, monster.minDamage - 2, monster.maxDamage - 2); - PlayEffect(monster, MonsterSound::Attack); - } - if (IsAnyOf(monster.type().type, MT_STORM, MT_RSTORM, MT_STORML, MT_MAEL) && monster.animInfo.currentFrame == 12) { - MonsterAttackEnemy(monster, monster.toHit(sgGameInitInfo.nDifficulty) - 20, monster.minDamage + 4, monster.maxDamage + 4); + const MeleeAttackWindowsView extraAttackWindows = GetExtraMeleeAttackWindows(monster); + for (size_t i = 0; i < extraAttackWindows.size; ++i) { + const MeleeAttackWindow &window = extraAttackWindows.data[i]; + if (currentFrame != window.frame) + continue; - PlayEffect(monster, MonsterSound::Attack); + MonsterAttackEnemy( + monster, + baseToHit + window.toHitDelta, + monster.minDamage + window.minDamageDelta, + monster.maxDamage + window.maxDamageDelta); + + if (window.playAttackSound) + PlayEffect(monster, MonsterSound::Attack); } - if (monster.ai == MonsterAIID::Snake && monster.animInfo.currentFrame == 0) + + if (monster.ai == MonsterAIID::Snake && currentFrame == 0) PlayEffect(monster, MonsterSound::Attack); if (monster.animInfo.isLastFrame()) { M_StartStand(monster, monster.direction);