Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.5] Compute monster toHit on the fly #7570

Merged
merged 1 commit into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions Source/loadsave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ class SaveHelper {
struct MonsterConversionData {
int8_t monsterLevel;
uint16_t experience;
uint8_t toHit;
uint8_t toHitSpecial;
};

Expand Down Expand Up @@ -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>();
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Source/missiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
39 changes: 23 additions & 16 deletions Source/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 9 additions & 1 deletion Source/monster.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand Down
Loading