From 021c7a652febe0dbf252166af7d96dad1d66f66a Mon Sep 17 00:00:00 2001 From: Andrew James Date: Sun, 7 Nov 2021 04:32:52 +1100 Subject: [PATCH] Add helpers for checking object type (#3357) * Add helper to test if an object is a trap source * Add helper to test if an object is a barrel * Add helper to test if an object is a crucifix * Add helpers to check if an object is a chest (and statuses) * Add helpers to check if an object is a (basic) shrine --- Source/engine.h | 8 +++--- Source/missiles.cpp | 8 +++--- Source/msg.cpp | 4 +-- Source/objects.cpp | 43 ++++++++++------------------ Source/objects.h | 70 ++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 94 insertions(+), 39 deletions(-) diff --git a/Source/engine.h b/Source/engine.h index 9b8356d9a..45f78073a 100644 --- a/Source/engine.h +++ b/Source/engine.h @@ -45,25 +45,25 @@ namespace devilution { template -bool IsAnyOf(const V &v, X x) +constexpr bool IsAnyOf(const V &v, X x) { return v == x; } template -bool IsAnyOf(const V &v, X x, Xs... xs) +constexpr bool IsAnyOf(const V &v, X x, Xs... xs) { return IsAnyOf(v, x) || IsAnyOf(v, xs...); } template -bool IsNoneOf(const V &v, X x) +constexpr bool IsNoneOf(const V &v, X x) { return v != x; } template -bool IsNoneOf(const V &v, X x, Xs... xs) +constexpr bool IsNoneOf(const V &v, X x, Xs... xs) { return IsNoneOf(v, x) && IsNoneOf(v, xs...); } diff --git a/Source/missiles.cpp b/Source/missiles.cpp index 7b7803bcd..ea5d1a83d 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -3042,11 +3042,11 @@ void MI_Lightball(Missile &missile) MoveMissileAndCheckMissileCol(missile, missile._midam, missile._midam, false, false); if (missile._miHitFlag) missile._mirange = j; - int8_t obj = dObject[tx][ty]; - if (obj != 0 && missile.position.tile == Point { tx, ty }) { - int oi = (obj > 0) ? (obj - 1) : -(obj + 1); - if (Objects[oi]._otype == OBJ_SHRINEL || Objects[oi]._otype == OBJ_SHRINER) + if (missile.position.tile == Point { tx, ty }) { + int8_t oi = abs(dObject[tx][ty]) - 1; + if (oi >= 0 && Objects[oi].IsShrine()) { missile._mirange = j; + } } if (missile._mirange == 0) missile._miDelFlag = true; diff --git a/Source/msg.cpp b/Source/msg.cpp index a767bb158..0690fc750 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -2280,9 +2280,9 @@ void DeltaLoadLevel() } for (int i = 0; i < ActiveObjectCount; i++) { - int ot = Objects[ActiveObjects[i]]._otype; - if (ot == OBJ_TRAPL || ot == OBJ_TRAPR) + if (Objects[ActiveObjects[i]].IsTrap()) { OperateTrap(Objects[ActiveObjects[i]]); + } } } deltaload = false; diff --git a/Source/objects.cpp b/Source/objects.cpp index f5255a1d6..af6094924 100644 --- a/Source/objects.cpp +++ b/Source/objects.cpp @@ -635,7 +635,7 @@ void AddChestTraps() for (int i = 0; i < MAXDUNX; i++) { // NOLINT(modernize-loop-convert) if (dObject[i][j] > 0) { int8_t oi = dObject[i][j] - 1; - if (Objects[oi]._otype >= OBJ_CHEST1 && Objects[oi]._otype <= OBJ_CHEST3 && !Objects[oi]._oTrapFlag && GenerateRnd(100) < 10) { + if (Objects[oi].IsUntrappedChest() && GenerateRnd(100) < 10) { switch (Objects[oi]._otype) { case OBJ_CHEST1: Objects[oi]._otype = OBJ_TCHEST1; @@ -2365,7 +2365,7 @@ void OperateChest(int pnum, int i, bool sendmsg) CreateRndUseful(Objects[i].position, sendmsg); } } - if (Objects[i]._oTrapFlag && Objects[i]._otype >= OBJ_TCHEST1 && Objects[i]._otype <= OBJ_TCHEST3) { + if (Objects[i].IsTrappedChest()) { auto &player = Players[pnum]; Direction mdir = GetDirection(Objects[i].position, player.position.tile); missile_id mtype; @@ -2854,7 +2854,7 @@ bool OperateShrineThaumaturgic(int pnum) for (int j = 0; j < ActiveObjectCount; j++) { int v1 = ActiveObjects[j]; assert(v1 >= 0 && v1 < MAXOBJECTS); - if (IsAnyOf(Objects[v1]._otype, OBJ_CHEST1, OBJ_CHEST2, OBJ_CHEST3, OBJ_TCHEST1, OBJ_TCHEST2, OBJ_TCHEST3) && Objects[v1]._oSelFlag == 0) { + if (Objects[v1].IsChest() && Objects[v1]._oSelFlag == 0) { Objects[v1]._oRndSeed = AdvanceRndSeed(); Objects[v1]._oSelFlag = 1; Objects[v1]._oAnimFrame -= 2; @@ -4101,7 +4101,7 @@ bool AreAllCruxesOfTypeBroken(int cruxType) { for (int j = 0; j < ActiveObjectCount; j++) { const auto &testObject = Objects[ActiveObjects[j]]; - if (IsNoneOf(testObject._otype, OBJ_CRUX1, OBJ_CRUX2, OBJ_CRUX3)) + if (!testObject.IsCrux()) continue; // Not a Crux object, keep searching if (cruxType != testObject._oVar8 || testObject._oBreak == -1) continue; // Found either a different crux or a previously broken crux, keep searching @@ -4338,7 +4338,7 @@ bool Object::IsDisabled() const if (IsAnyOf(_otype, _object_id::OBJ_GOATSHRINE, _object_id::OBJ_CAULDRON)) { return true; } - if (IsNoneOf(_otype, _object_id::OBJ_SHRINEL, _object_id::OBJ_SHRINER)) { + if (!IsShrine()) { return false; } return IsAnyOf(static_cast(_oVar1), shrine_type::ShrineFascinating, shrine_type::ShrineOrnate, shrine_type::ShrineSacred); @@ -5015,21 +5015,15 @@ void TryDisarm(int pnum, int i) return; } for (int j = 0; j < ActiveObjectCount; j++) { - bool checkflag = false; - int oi = ActiveObjects[j]; - int oti = Objects[oi]._otype; - if (oti == OBJ_TRAPL) - checkflag = true; - if (oti == OBJ_TRAPR) - checkflag = true; - if (checkflag && dObject[Objects[oi]._oVar1][Objects[oi]._oVar2] - 1 == i) { - Objects[oi]._oVar4 = 1; + Object &trap = Objects[ActiveObjects[j]]; + if (trap.IsTrap() && dObject[trap._oVar1][trap._oVar2] - 1 == i) { + trap._oVar4 = 1; Objects[i]._oTrapFlag = false; } } - int oti = Objects[i]._otype; - if (oti >= OBJ_TCHEST1 && oti <= OBJ_TCHEST3) + if (Objects[i].IsTrappedChest()) { Objects[i]._oTrapFlag = false; + } } int ItemMiscIdIdx(item_misc_id imiscid) @@ -5268,25 +5262,18 @@ void BreakObject(int pnum, int oi) objdam += player._pDamageMod + player._pIBonusDamMod + objdam * player._pIBonusDam / 100; } - switch (Objects[oi]._otype) { - case OBJ_CRUX1: - case OBJ_CRUX2: - case OBJ_CRUX3: - BreakCrux(Objects[oi]); - break; - case OBJ_BARREL: - case OBJ_BARRELEX: + if (Objects[oi].IsBarrel()) { BreakBarrel(pnum, oi, objdam, false, true); - break; - default: - break; + } else if (Objects[oi].IsCrux()) { + BreakCrux(Objects[oi]); } } void SyncBreakObj(int pnum, int oi) { - if (Objects[oi]._otype >= OBJ_BARREL && Objects[oi]._otype <= OBJ_BARRELEX) + if (Objects[oi].IsBarrel()) { BreakBarrel(pnum, oi, 0, true, false); + } } void SyncObjectAnim(Object &object) diff --git a/Source/objects.h b/Source/objects.h index 32978ddff..5f1799877 100644 --- a/Source/objects.h +++ b/Source/objects.h @@ -127,14 +127,82 @@ struct Object { */ [[nodiscard]] bool IsDisabled() const; + /** + * @brief Check if this object is barrel (or explosive barrel) + * @return True if the object is one of the barrel types (see _object_id) + */ + [[nodiscard]] constexpr bool IsBarrel() const + { + return IsAnyOf(_otype, _object_id::OBJ_BARREL, _object_id::OBJ_BARRELEX); + } + + /** + * @brief Check if this object is a chest (or trapped chest). + * + * Trapped chests get their base type change in addition to having the trap flag set, but if they get "refilled" by + * a Thaumaturgic shrine the base type is not reverted. This means you need to consider both the base type and the + * trap flag to differentiate between chests that are currently trapped and chests which have never been trapped. + * + * @return True if the object is any of the chest types (see _object_id) + */ + [[nodiscard]] constexpr bool IsChest() const + { + return IsAnyOf(_otype, _object_id::OBJ_CHEST1, _object_id::OBJ_CHEST2, _object_id::OBJ_CHEST3, _object_id::OBJ_TCHEST1, _object_id::OBJ_TCHEST2, _object_id::OBJ_TCHEST3); + } + + /** + * @brief Check if this object is a trapped chest (specifically a chest which is currently trapped). + * @return True if the object is one of the trapped chest types (see _object_id) and has an active trap. + */ + [[nodiscard]] constexpr bool IsTrappedChest() const + { + return IsAnyOf(_otype, _object_id::OBJ_TCHEST1, _object_id::OBJ_TCHEST2, _object_id::OBJ_TCHEST3) && _oTrapFlag; + } + + /** + * @brief Check if this object is an untrapped chest (specifically a chest which has not been trapped). + * @return True if the object is one of the untrapped chest types (see _object_id) and has no active trap. + */ + [[nodiscard]] constexpr bool IsUntrappedChest() const + { + return IsAnyOf(_otype, _object_id::OBJ_CHEST1, _object_id::OBJ_CHEST2, _object_id::OBJ_CHEST3) && !_oTrapFlag; + } + + /** + * @brief Check if this object is a crucifix + * @return True if the object is one of the crux types (see _object_id) + */ + [[nodiscard]] constexpr bool IsCrux() const + { + return IsAnyOf(_otype, _object_id::OBJ_CRUX1, _object_id::OBJ_CRUX2, _object_id::OBJ_CRUX3); + } + /** * @brief Check if this object is a door * @return True if the object is one of the door types (see _object_id) */ - bool IsDoor() const + [[nodiscard]] constexpr bool IsDoor() const { return IsAnyOf(_otype, _object_id::OBJ_L1LDOOR, _object_id::OBJ_L1RDOOR, _object_id::OBJ_L2LDOOR, _object_id::OBJ_L2RDOOR, _object_id::OBJ_L3LDOOR, _object_id::OBJ_L3RDOOR); } + + /** + * @brief Check if this object is a shrine + * @return True if the object is one of the shrine types (see _object_id) + */ + [[nodiscard]] constexpr bool IsShrine() const + { + return IsAnyOf(_otype, _object_id::OBJ_SHRINEL, _object_id::OBJ_SHRINER); + } + + /** + * @brief Check if this object is a trap source + * @return True if the object is one of the trap types (see _object_id) + */ + [[nodiscard]] constexpr bool IsTrap() const + { + return IsAnyOf(_otype, _object_id::OBJ_TRAPL, _object_id::OBJ_TRAPR); + } }; extern Object Objects[MAXOBJECTS];