Skip to content

Commit

Permalink
Compute monster toHit on the fly
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenCWills authored and AJenbo committed Nov 28, 2024
1 parent e81121a commit 6215de6
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 23 deletions.
16 changes: 11 additions & 5 deletions Source/loadsave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ class SaveHelper {
struct MonsterConversionData {
int8_t monsterLevel;
uint16_t experience;
uint8_t toHit;
uint8_t toHitSpecial;
};

Expand Down Expand Up @@ -678,16 +679,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>();
Expand Down Expand Up @@ -1472,7 +1475,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)
Expand Down
2 changes: 1 addition & 1 deletion Source/missiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ bool PlayerMHit(Player &player, Monster *monster, int dist, int mind, int maxd,
if (missileData.isArrow()) {
int tac = player.GetArmor();
if (monster != nullptr) {
hper = monster->toHit
hper = monster->toHit(sgGameInitInfo.nDifficulty)
+ ((monster->level(sgGameInitInfo.nDifficulty) - player.getCharacterLevel()) * 2)
+ 30
- (dist * 2) - tac;
Expand Down
39 changes: 23 additions & 16 deletions Source/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,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;
Expand All @@ -204,7 +203,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);
Expand All @@ -217,7 +215,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;
Expand Down Expand Up @@ -1221,17 +1218,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);
}
Expand Down Expand Up @@ -3229,15 +3226,6 @@ tl::expected<void, std::string> PrepareUniqueMonst(Monster &monster, UniqueMonst
RETURN_IF_ERROR(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;

Expand Down Expand Up @@ -4656,7 +4644,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.getCharacterLevel();
golem.golemToHit = 5 * (missile._mispllvl + 8) + 2 * player.getCharacterLevel();
golem.minDamage = 2 * (missile._mispllvl + 4);
golem.maxDamage = 2 * (missile._mispllvl + 8);
golem.flags |= MFLAG_GOLEM;
Expand Down Expand Up @@ -4827,6 +4815,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;
Expand Down
10 changes: 9 additions & 1 deletion Source/monster.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,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;

Expand Down Expand Up @@ -364,6 +364,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.
Expand Down

0 comments on commit 6215de6

Please sign in to comment.