Browse Source

Compute monster toHit on the fly

pull/7593/head
staphen 1 year ago committed by Anders Jenbo
parent
commit
51f5082489
  1. 16
      Source/loadsave.cpp
  2. 2
      Source/missiles.cpp
  3. 39
      Source/monster.cpp
  4. 10
      Source/monster.h

16
Source/loadsave.cpp

@ -234,6 +234,7 @@ public:
struct MonsterConversionData {
int8_t monsterLevel;
uint16_t experience;
uint8_t toHit;
uint8_t toHitSpecial;
};
@ -661,16 +662,18 @@ void LoadMonster(LoadHelper *file, Monster &monster, MonsterConversionData *mons
else
file->Skip(2); // Skip exp - now calculated from monstdat when the monster dies
if (monster.isPlayerMinion()) // Don't skip for golems
monster.toHit = file->NextLE<uint8_t>();
if (monsterConversionData != nullptr)
monsterConversionData->toHit = file->NextLE<uint8_t>();
else if (monster.isPlayerMinion()) // Don't skip for golems
monster.golemToHit = file->NextLE<uint8_t>();
else
file->Skip(1); // Skip hit as it's already initialized
file->Skip(1); // Skip toHit - now calculated on the fly
monster.minDamage = file->NextLE<uint8_t>();
monster.maxDamage = file->NextLE<uint8_t>();
if (monsterConversionData != nullptr)
monsterConversionData->toHitSpecial = file->NextLE<uint8_t>();
else
file->Skip(1); // Skip toHitSpecial as it's already initialized
file->Skip(1); // Skip toHitSpecial - now calculated on the fly
monster.minDamageSpecial = file->NextLE<uint8_t>();
monster.maxDamageSpecial = file->NextLE<uint8_t>();
monster.armorClass = file->NextLE<uint8_t>();
@ -1477,7 +1480,10 @@ void SaveMonster(SaveHelper *file, Monster &monster, MonsterConversionData *mons
else
file->WriteLE<uint16_t>(static_cast<uint16_t>(std::min<unsigned>(std::numeric_limits<uint16_t>::max(), monster.exp(sgGameInitInfo.nDifficulty))));
file->WriteLE<uint8_t>(static_cast<uint8_t>(std::min<uint16_t>(monster.toHit, std::numeric_limits<uint8_t>::max()))); // For backwards compatibility
if (monsterConversionData != nullptr)
file->WriteLE<uint8_t>(monsterConversionData->toHit);
else
file->WriteLE<uint8_t>(static_cast<uint8_t>(std::min<uint16_t>(monster.toHit(sgGameInitInfo.nDifficulty), std::numeric_limits<uint8_t>::max()))); // For backwards compatibility
file->WriteLE<uint8_t>(monster.minDamage);
file->WriteLE<uint8_t>(monster.maxDamage);
if (monsterConversionData != nullptr)

2
Source/missiles.cpp

@ -1007,7 +1007,7 @@ bool PlayerMHit(int pnum, Monster *monster, int dist, int mind, int maxd, Missil
if (missileData.isArrow()) {
int tac = player.GetArmor();
if (monster != nullptr) {
hper = monster->toHit
hper = monster->toHit(sgGameInitInfo.nDifficulty)
+ ((monster->level(sgGameInitInfo.nDifficulty) - player._pLevel) * 2)
+ 30
- (dist * 2) - tac;

39
Source/monster.cpp

@ -156,7 +156,6 @@ void InitMonster(Monster &monster, Direction rd, size_t typeIndex, Point positio
monster.rndItemSeed = AdvanceRndSeed();
monster.aiSeed = AdvanceRndSeed();
monster.whoHit = 0;
monster.toHit = monster.data().toHit;
monster.minDamage = monster.data().minDamage;
monster.maxDamage = monster.data().maxDamage;
monster.minDamageSpecial = monster.data().minDamageSpecial;
@ -182,7 +181,6 @@ void InitMonster(Monster &monster, Direction rd, size_t typeIndex, Point positio
else
monster.maxHitPoints += 100 << 6;
monster.hitPoints = monster.maxHitPoints;
monster.toHit += NightmareToHitBonus;
monster.minDamage = 2 * (monster.minDamage + 2);
monster.maxDamage = 2 * (monster.maxDamage + 2);
monster.minDamageSpecial = 2 * (monster.minDamageSpecial + 2);
@ -195,7 +193,6 @@ void InitMonster(Monster &monster, Direction rd, size_t typeIndex, Point positio
else
monster.maxHitPoints += 200 << 6;
monster.hitPoints = monster.maxHitPoints;
monster.toHit += HellToHitBonus;
monster.minDamage = 4 * monster.minDamage + 6;
monster.maxDamage = 4 * monster.maxDamage + 6;
monster.minDamageSpecial = 4 * monster.minDamageSpecial + 6;
@ -1253,17 +1250,17 @@ 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, monster.minDamage, monster.maxDamage);
MonsterAttackEnemy(monster, monster.toHit(sgGameInitInfo.nDifficulty), 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 + 10, monster.minDamage - 2, monster.maxDamage - 2);
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 - 20, monster.minDamage + 4, monster.maxDamage + 4);
MonsterAttackEnemy(monster, monster.toHit(sgGameInitInfo.nDifficulty) - 20, monster.minDamage + 4, monster.maxDamage + 4);
PlayEffect(monster, MonsterSound::Attack);
}
@ -3181,15 +3178,6 @@ void PrepareUniqueMonst(Monster &monster, UniqueMonsterType monsterType, size_t
InitTRNForUniqueMonster(monster);
monster.uniqTrans = uniquetrans++;
if (uniqueMonsterData.customToHit != 0) {
monster.toHit = uniqueMonsterData.customToHit;
if (sgGameInitInfo.nDifficulty == DIFF_NIGHTMARE) {
monster.toHit += NightmareToHitBonus;
} else if (sgGameInitInfo.nDifficulty == DIFF_HELL) {
monster.toHit += HellToHitBonus;
}
}
if (uniqueMonsterData.customArmorClass != 0) {
monster.armorClass = uniqueMonsterData.customArmorClass;
@ -4571,7 +4559,7 @@ void SpawnGolem(Player &player, Monster &golem, Point position, Missile &missile
golem.maxHitPoints = 2 * (320 * missile._mispllvl + player._pMaxMana / 3);
golem.hitPoints = golem.maxHitPoints;
golem.armorClass = 25;
golem.toHit = 5 * (missile._mispllvl + 8) + 2 * player._pLevel;
golem.golemToHit = 5 * (missile._mispllvl + 8) + 2 * player._pLevel;
golem.minDamage = 2 * (missile._mispllvl + 4);
golem.maxDamage = 2 * (missile._mispllvl + 8);
golem.flags |= MFLAG_GOLEM;
@ -4742,6 +4730,25 @@ MonsterMode Monster::getVisualMonsterMode() const
return MonsterMode::Petrified;
}
unsigned int Monster::toHit(_difficulty difficulty) const
{
if (isPlayerMinion())
return golemToHit;
unsigned int baseToHit = data().toHit;
if (isUnique() && UniqueMonstersData[static_cast<size_t>(uniqueType)].customToHit != 0) {
baseToHit = UniqueMonstersData[static_cast<size_t>(uniqueType)].customToHit;
}
if (difficulty == DIFF_NIGHTMARE) {
baseToHit += NightmareToHitBonus;
} else if (difficulty == DIFF_HELL) {
baseToHit += HellToHitBonus;
}
return baseToHit;
}
unsigned int Monster::toHitSpecial(_difficulty difficulty) const
{
unsigned int baseToHitSpecial = data().toHitSpecial;

10
Source/monster.h

@ -210,7 +210,7 @@ struct Monster { // note: missing field _mAFNum
uint32_t rndItemSeed;
/** Seed used to determine AI behaviour/sync sounds in multiplayer games? */
uint32_t aiSeed;
uint16_t toHit;
uint16_t golemToHit;
uint16_t resistance;
_speech_id talkMsg;
@ -352,6 +352,14 @@ struct Monster { // note: missing field _mAFNum
return monsterExp;
}
/**
* @brief Calculates monster's chance to hit with normal attack.
* Fetches base value from @p MonstersData array or @p UniqueMonstersData.
* @param difficulty - difficulty on which calculation is performed
* @return Monster's chance to hit with normal attack, including bonuses from difficulty and monster being unique
*/
unsigned int toHit(_difficulty difficulty) const;
/**
* @brief Calculates monster's chance to hit with special attack.
* Fetches base value from @p MonstersData array or @p UniqueMonstersData.

Loading…
Cancel
Save