From 1e0f2c149ca64e746127666979e00e511d42b549 Mon Sep 17 00:00:00 2001 From: ephphatha Date: Wed, 29 Jun 2022 08:56:56 +1000 Subject: [PATCH] Add getLeader helper for monsters in a pack --- Source/loadsave.cpp | 6 ++++-- Source/monster.cpp | 40 ++++++++++++++++++++++--------------- Source/monster.h | 4 ++++ Source/qol/monhealthbar.cpp | 2 +- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/Source/loadsave.cpp b/Source/loadsave.cpp index fdd92073b..eafeb129d 100644 --- a/Source/loadsave.cpp +++ b/Source/loadsave.cpp @@ -659,6 +659,8 @@ void LoadMonster(LoadHelper *file, Monster &monster) if (monster.talkMsg == TEXT_KING1) // Fix original bad mapping of NONE for monsters monster.talkMsg = TEXT_NONE; monster.leader = file->NextLE(); + if (monster.leader == 0) + monster.leader = Monster::NoLeader; // Golems shouldn't be leaders of other monsters monster.leaderRelation = static_cast(file->NextLE()); monster.packSize = file->NextLE(); monster.lightId = file->NextLE(); @@ -1400,8 +1402,8 @@ void SaveMonster(SaveHelper *file, Monster &monster) file->WriteLE(monster.magicResistance); file->Skip(2); // Alignment - file->WriteLE(monster.talkMsg == TEXT_NONE ? 0 : monster.talkMsg); // Replicate original bad mapping of none for monsters - file->WriteLE(monster.leader); + file->WriteLE(monster.talkMsg == TEXT_NONE ? 0 : monster.talkMsg); // Replicate original bad mapping of none for monsters + file->WriteLE(monster.leader == Monster::NoLeader ? 0 : monster.leader); // Vanilla uses 0 as the default leader which corresponds to player 0s golem file->WriteLE(static_cast(monster.leaderRelation)); file->WriteLE(monster.packSize); // vanilla compatibility diff --git a/Source/monster.cpp b/Source/monster.cpp index 3f3c2048d..f27fa0435 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -241,7 +241,7 @@ void InitMonster(Monster &monster, Direction rd, int mtype, Point position) monster.maxDamage2 = monster.data().mMaxDamage2; monster.armorClass = monster.data().mArmorClass; monster.magicResistance = monster.data().mMagicRes; - monster.leader = 0; + monster.leader = Monster::NoLeader; monster.leaderRelation = LeaderRelation::None; monster.flags = monster.data().mFlags; monster.talkMsg = TEXT_NONE; @@ -1754,18 +1754,18 @@ bool IsLineNotSolid(Point startPoint, Point endPoint) void FollowTheLeader(Monster &monster) { - if (monster.leader == 0) + if (monster.leaderRelation != LeaderRelation::Leashed) return; - if (monster.leaderRelation != LeaderRelation::Leashed) + Monster *leader = monster.getLeader(); + if (leader == nullptr) return; - auto &leader = Monsters[monster.leader]; - if (monster.activeForTicks >= leader.activeForTicks) + if (monster.activeForTicks >= leader->activeForTicks) return; - monster.position.last = leader.position.tile; - monster.activeForTicks = leader.activeForTicks - 1; + monster.position.last = leader->position.tile; + monster.activeForTicks = leader->activeForTicks - 1; } void GroupUnity(Monster &monster) @@ -1773,12 +1773,12 @@ void GroupUnity(Monster &monster) if (monster.leaderRelation == LeaderRelation::None) return; - // Someone with a leaderRelation should have a leader ... - assert(monster.leader >= 0); - // And no unique monster would be a minion of someone else! + // No unique monster would be a minion of someone else! assert(monster.uniqType == 0); - auto &leader = Monsters[monster.leader]; + // Someone with a leaderRelation should have a leader, if we end up trying to access a nullptr then the relation was already broken... + + auto &leader = *monster.getLeader(); if (IsLineNotSolid(monster.position.tile, leader.position.future)) { if (monster.leaderRelation == LeaderRelation::Separated && monster.position.tile.WalkingDistance(leader.position.future) < 4) { @@ -2345,7 +2345,7 @@ void ScavengerAi(int monsterId) if (monster.hitPoints < (monster.maxHitPoints / 2) && monster.goal != MonsterGoal::Healing) { if (monster.leaderRelation != LeaderRelation::None) { if (monster.leaderRelation == LeaderRelation::Leashed) - Monsters[monster.leader].packSize--; + monster.getLeader()->packSize--; monster.leaderRelation = LeaderRelation::None; } monster.goal = MonsterGoal::Healing; @@ -4007,12 +4007,12 @@ void M_UpdateLeader(int monsterId) for (size_t j = 0; j < ActiveMonsterCount; j++) { auto &minion = Monsters[ActiveMonsters[j]]; - if (minion.leaderRelation == LeaderRelation::Leashed && minion.leader == monsterId) + if (minion.leaderRelation == LeaderRelation::Leashed && minion.getLeader() == &monster) minion.leaderRelation = LeaderRelation::None; } if (monster.leaderRelation == LeaderRelation::Leashed) { - Monsters[monster.leader].packSize--; + monster.getLeader()->packSize--; } } @@ -4296,7 +4296,7 @@ bool DirOK(int monsterId, Direction mdir) if (!IsRelativeMoveOK(monster, position, mdir)) return false; if (monster.leaderRelation == LeaderRelation::Leashed) { - return futurePosition.WalkingDistance(Monsters[monster.leader].position.future) < 4; + return futurePosition.WalkingDistance(monster.getLeader()->position.future) < 4; } if (monster.uniqType == 0 || UniqueMonstersData[monster.uniqType - 1].monsterPack != UniqueMonsterPack::Leashed) return true; @@ -4310,7 +4310,7 @@ bool DirOK(int monsterId, Direction mdir) continue; auto &minion = Monsters[mi - 1]; - if (minion.leaderRelation == LeaderRelation::Leashed && minion.leader == monsterId) { + if (minion.leaderRelation == LeaderRelation::Leashed && minion.getLeader() == &monster) { mcount++; } } @@ -4845,6 +4845,14 @@ void decode_enemy(Monster &monster, int enemyId) return std::distance(&Monsters[0], this); } +Monster *Monster::getLeader() const +{ + if (leader == Monster::NoLeader) + return nullptr; + + return &Monsters[leader]; +} + void Monster::checkStandAnimationIsLoaded(Direction mdir) { if (IsAnyOf(mode, MonsterMode::Stand, MonsterMode::Talk)) { diff --git a/Source/monster.h b/Source/monster.h index 0af118f86..8b2bbe9c8 100644 --- a/Source/monster.h +++ b/Source/monster.h @@ -239,6 +239,8 @@ struct Monster { // note: missing field _mAFNum uint8_t packSize; int8_t lightId; + static constexpr uint8_t NoLeader = -1; + /** * @brief Sets the current cell sprite to match the desired desiredDirection and animation sequence * @param graphic Animation sequence of interest @@ -289,6 +291,8 @@ struct Monster { // note: missing field _mAFNum */ [[nodiscard]] size_t getId() const; + [[nodiscard]] Monster *getLeader() const; + /** * @brief Is the monster currently walking? */ diff --git a/Source/qol/monhealthbar.cpp b/Source/qol/monhealthbar.cpp index 4490b8345..13df75c5e 100644 --- a/Source/qol/monhealthbar.cpp +++ b/Source/qol/monhealthbar.cpp @@ -136,7 +136,7 @@ void DrawMonsterHealthBar(const Surface &out) DrawString(out, monster.name, { position + Displacement { -1, 1 }, { width, height } }, style | UiFlags::ColorBlack); if (monster.uniqType != 0) style |= UiFlags::ColorWhitegold; - else if (monster.leader != 0) + else if (monster.leader != Monster::NoLeader) style |= UiFlags::ColorBlue; else style |= UiFlags::ColorWhite;