Browse Source

Compute monster toHit on the fly

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

16
Source/loadsave.cpp

@ -237,6 +237,7 @@ public:
struct MonsterConversionData { struct MonsterConversionData {
int8_t monsterLevel; int8_t monsterLevel;
uint16_t experience; uint16_t experience;
uint8_t toHit;
uint8_t toHitSpecial; uint8_t toHitSpecial;
}; };
@ -678,16 +679,18 @@ void LoadMonster(LoadHelper *file, Monster &monster, MonsterConversionData *mons
else else
file->Skip(2); // Skip exp - now calculated from monstdat when the monster dies file->Skip(2); // Skip exp - now calculated from monstdat when the monster dies
if (monster.isPlayerMinion()) // Don't skip for golems if (monsterConversionData != nullptr)
monster.toHit = file->NextLE<uint8_t>(); monsterConversionData->toHit = file->NextLE<uint8_t>();
else if (monster.isPlayerMinion()) // Don't skip for golems
monster.golemToHit = file->NextLE<uint8_t>();
else 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.minDamage = file->NextLE<uint8_t>();
monster.maxDamage = file->NextLE<uint8_t>(); monster.maxDamage = file->NextLE<uint8_t>();
if (monsterConversionData != nullptr) if (monsterConversionData != nullptr)
monsterConversionData->toHitSpecial = file->NextLE<uint8_t>(); monsterConversionData->toHitSpecial = file->NextLE<uint8_t>();
else 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.minDamageSpecial = file->NextLE<uint8_t>();
monster.maxDamageSpecial = file->NextLE<uint8_t>(); monster.maxDamageSpecial = file->NextLE<uint8_t>();
monster.armorClass = file->NextLE<uint8_t>(); monster.armorClass = file->NextLE<uint8_t>();
@ -1472,7 +1475,10 @@ void SaveMonster(SaveHelper *file, Monster &monster, MonsterConversionData *mons
else else
file->WriteLE<uint16_t>(static_cast<uint16_t>(std::min<unsigned>(std::numeric_limits<uint16_t>::max(), monster.exp(sgGameInitInfo.nDifficulty)))); 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.minDamage);
file->WriteLE<uint8_t>(monster.maxDamage); file->WriteLE<uint8_t>(monster.maxDamage);
if (monsterConversionData != nullptr) if (monsterConversionData != nullptr)

2
Source/missiles.cpp

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

39
Source/monster.cpp

@ -178,7 +178,6 @@ void InitMonster(Monster &monster, Direction rd, size_t typeIndex, Point positio
monster.rndItemSeed = AdvanceRndSeed(); monster.rndItemSeed = AdvanceRndSeed();
monster.aiSeed = AdvanceRndSeed(); monster.aiSeed = AdvanceRndSeed();
monster.whoHit = 0; monster.whoHit = 0;
monster.toHit = monster.data().toHit;
monster.minDamage = monster.data().minDamage; monster.minDamage = monster.data().minDamage;
monster.maxDamage = monster.data().maxDamage; monster.maxDamage = monster.data().maxDamage;
monster.minDamageSpecial = monster.data().minDamageSpecial; monster.minDamageSpecial = monster.data().minDamageSpecial;
@ -204,7 +203,6 @@ void InitMonster(Monster &monster, Direction rd, size_t typeIndex, Point positio
else else
monster.maxHitPoints += 100 << 6; monster.maxHitPoints += 100 << 6;
monster.hitPoints = monster.maxHitPoints; monster.hitPoints = monster.maxHitPoints;
monster.toHit += NightmareToHitBonus;
monster.minDamage = 2 * (monster.minDamage + 2); monster.minDamage = 2 * (monster.minDamage + 2);
monster.maxDamage = 2 * (monster.maxDamage + 2); monster.maxDamage = 2 * (monster.maxDamage + 2);
monster.minDamageSpecial = 2 * (monster.minDamageSpecial + 2); monster.minDamageSpecial = 2 * (monster.minDamageSpecial + 2);
@ -217,7 +215,6 @@ void InitMonster(Monster &monster, Direction rd, size_t typeIndex, Point positio
else else
monster.maxHitPoints += 200 << 6; monster.maxHitPoints += 200 << 6;
monster.hitPoints = monster.maxHitPoints; monster.hitPoints = monster.maxHitPoints;
monster.toHit += HellToHitBonus;
monster.minDamage = 4 * monster.minDamage + 6; monster.minDamage = 4 * monster.minDamage + 6;
monster.maxDamage = 4 * monster.maxDamage + 6; monster.maxDamage = 4 * monster.maxDamage + 6;
monster.minDamageSpecial = 4 * monster.minDamageSpecial + 6; monster.minDamageSpecial = 4 * monster.minDamageSpecial + 6;
@ -1221,17 +1218,17 @@ void MonsterAttackEnemy(Monster &monster, int hit, int minDam, int maxDam)
bool MonsterAttack(Monster &monster) bool MonsterAttack(Monster &monster)
{ {
if (monster.animInfo.currentFrame == monster.data().animFrameNum - 1) { 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) if (monster.ai != MonsterAIID::Snake)
PlayEffect(monster, MonsterSound::Attack); PlayEffect(monster, MonsterSound::Attack);
} }
if (IsAnyOf(monster.type().type, MT_NMAGMA, MT_YMAGMA, MT_BMAGMA, MT_WMAGMA) && monster.animInfo.currentFrame == 8) { 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); PlayEffect(monster, MonsterSound::Attack);
} }
if (IsAnyOf(monster.type().type, MT_STORM, MT_RSTORM, MT_STORML, MT_MAEL) && monster.animInfo.currentFrame == 12) { 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); PlayEffect(monster, MonsterSound::Attack);
} }
@ -3229,15 +3226,6 @@ tl::expected<void, std::string> PrepareUniqueMonst(Monster &monster, UniqueMonst
RETURN_IF_ERROR(InitTRNForUniqueMonster(monster)); RETURN_IF_ERROR(InitTRNForUniqueMonster(monster));
monster.uniqTrans = uniquetrans++; 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) { if (uniqueMonsterData.customArmorClass != 0) {
monster.armorClass = uniqueMonsterData.customArmorClass; monster.armorClass = uniqueMonsterData.customArmorClass;
@ -4656,7 +4644,7 @@ void SpawnGolem(Player &player, Monster &golem, Point position, Missile &missile
golem.maxHitPoints = 2 * (320 * missile._mispllvl + player._pMaxMana / 3); golem.maxHitPoints = 2 * (320 * missile._mispllvl + player._pMaxMana / 3);
golem.hitPoints = golem.maxHitPoints; golem.hitPoints = golem.maxHitPoints;
golem.armorClass = 25; golem.armorClass = 25;
golem.toHit = 5 * (missile._mispllvl + 8) + 2 * player.getCharacterLevel(); golem.golemToHit = 5 * (missile._mispllvl + 8) + 2 * player.getCharacterLevel();
golem.minDamage = 2 * (missile._mispllvl + 4); golem.minDamage = 2 * (missile._mispllvl + 4);
golem.maxDamage = 2 * (missile._mispllvl + 8); golem.maxDamage = 2 * (missile._mispllvl + 8);
golem.flags |= MFLAG_GOLEM; golem.flags |= MFLAG_GOLEM;
@ -4827,6 +4815,25 @@ MonsterMode Monster::getVisualMonsterMode() const
return MonsterMode::Petrified; 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 Monster::toHitSpecial(_difficulty difficulty) const
{ {
unsigned int baseToHitSpecial = data().toHitSpecial; unsigned int baseToHitSpecial = data().toHitSpecial;

10
Source/monster.h

@ -222,7 +222,7 @@ struct Monster { // note: missing field _mAFNum
uint32_t rndItemSeed; uint32_t rndItemSeed;
/** Seed used to determine AI behaviour/sync sounds in multiplayer games? */ /** Seed used to determine AI behaviour/sync sounds in multiplayer games? */
uint32_t aiSeed; uint32_t aiSeed;
uint16_t toHit; uint16_t golemToHit;
uint16_t resistance; uint16_t resistance;
_speech_id talkMsg; _speech_id talkMsg;
@ -364,6 +364,14 @@ struct Monster { // note: missing field _mAFNum
return monsterExp; 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. * @brief Calculates monster's chance to hit with special attack.
* Fetches base value from @p MonstersData array or @p UniqueMonstersData. * Fetches base value from @p MonstersData array or @p UniqueMonstersData.

Loading…
Cancel
Save