diff options
Diffstat (limited to 'src/Mobs')
-rw-r--r-- | src/Mobs/Behaviors/BehaviorAttacker.cpp | 45 | ||||
-rw-r--r-- | src/Mobs/Behaviors/BehaviorAttacker.h | 25 | ||||
-rw-r--r-- | src/Mobs/Behaviors/BehaviorAttackerMelee.cpp | 11 | ||||
-rw-r--r-- | src/Mobs/Behaviors/BehaviorAttackerMelee.h | 11 | ||||
-rw-r--r-- | src/Mobs/Behaviors/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/Mobs/Monster.cpp | 80 | ||||
-rw-r--r-- | src/Mobs/Monster.h | 3 |
7 files changed, 138 insertions, 39 deletions
diff --git a/src/Mobs/Behaviors/BehaviorAttacker.cpp b/src/Mobs/Behaviors/BehaviorAttacker.cpp index 3113516ff..30c18019e 100644 --- a/src/Mobs/Behaviors/BehaviorAttacker.cpp +++ b/src/Mobs/Behaviors/BehaviorAttacker.cpp @@ -43,7 +43,7 @@ bool cBehaviorAttacker::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk UNUSED(a_Chunk); // If we have a target, we have something to do! Return true and control the mob Ticks. // Otherwise return false. - return (GetTarget() != nullptr); + return (m_IsStriking || (GetTarget() != nullptr)); } @@ -55,10 +55,23 @@ void cBehaviorAttacker::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) UNUSED(a_Dt); UNUSED(a_Chunk); - if (m_IsStriking) return; - // If we're striking, return. This allows derived classes to implement multi-tick strikes - // E.g. a blaze shooting 3 fireballs in consequative ticks. - // Derived class is expected to set m_IsStriking to false when the strike is done. + if (m_IsStriking) + { + if (StrikeTarget(a_Dt, a_Chunk, ++m_StrikeTickCnt)) + { + m_Parent->UnpinBehavior(this); + m_IsStriking = false; + ResetStrikeCooldown(); + } + #ifdef _DEBUG + if (m_StrikeTickCnt > 100) + { + LOGD("Sanity check failed. An attack took more than 5 seconds. Hmm"); + ASSERT(1 == 0); + } + #endif + return; + } if ((GetTarget() != nullptr)) { @@ -75,7 +88,6 @@ void cBehaviorAttacker::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == m_Parent->GetWorld()))); - // Stop targeting out of range targets if (GetTarget() != nullptr) { if (TargetOutOfSight()) @@ -91,7 +103,7 @@ void cBehaviorAttacker::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) else { m_Parent->MoveToPosition(m_Target->GetPosition()); - // todo BehaviorApproacher + // todo BehaviorApproacher for creeper sneaking, etc } } } @@ -190,6 +202,21 @@ void cBehaviorAttacker::SetTarget(cPawn * a_Target) +void cBehaviorAttacker::StrikeTarget() +{ + if (m_IsStriking || (m_Target == nullptr)) + { + return; + } + m_IsStriking = true; + m_StrikeTickCnt = 0; + m_Parent->PinBehavior(this); +} + + + + + bool cBehaviorAttacker::TargetIsInStrikeRadius(void) { ASSERT(GetTarget() != nullptr); @@ -254,8 +281,6 @@ void cBehaviorAttacker::StrikeTargetIfReady() { if (m_AttackCoolDownTicksLeft != 0) { - m_IsStriking = true; - StrikeTarget(); // Different derived classes implement strikes in different ways - ResetStrikeCooldown(); + StrikeTarget(); } } diff --git a/src/Mobs/Behaviors/BehaviorAttacker.h b/src/Mobs/Behaviors/BehaviorAttacker.h index 4573f9a4a..099cb1987 100644 --- a/src/Mobs/Behaviors/BehaviorAttacker.h +++ b/src/Mobs/Behaviors/BehaviorAttacker.h @@ -24,25 +24,38 @@ public: void SetAttackDamage(int a_AttackDamage); // Behavior functions - virtual bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; void Destroyed() override; - virtual void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; void DoTakeDamage(TakeDamageInfo & a_TDI) override; /** Returns the target pointer, or a nullptr if we're not targeting anyone. */ cPawn * GetTarget(); - /** Sets a new target. Forgets the older target if present. */ + /** Sets a new target. Forgets the older target if present. Set this to nullptr to unset target. */ void SetTarget(cPawn * a_Target); + + /** Makes the mob strike a target the next tick. Ignores the strike cooldown. + * Ignored if already striking or if no target is set. */ + void StrikeTarget(); + + /** Makes the mob strike a target the next tick only if the strike cooldown permits it. + * Ignored if already striking or if no target is set. */ + void StrikeTargetIfReady(); protected: - virtual void StrikeTarget() = 0; + + /** Called when the actual attack should be made. Will be called again and again every tick until + it returns false. a_StrikeTickCnt tracks how many times it was called. It is 1 the first call. + It increments by 1 each call. This mechanism allows multi-tick attacks, like blazes shooting multiple + fireballs, but most attacks are single tick and return true the first call. */ + virtual bool StrikeTarget(std::chrono::milliseconds a_Dt, cChunk & a_Chunk, int a_StrikeTickCnt) = 0; // Target related methods bool TargetIsInStrikeRadius(); bool TargetIsInStrikeRadiusAndLineOfSight(); bool TargetOutOfSight(); - void StrikeTargetIfReady(); + void StrikeTargetIfReady(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); // Cooldown stuff void ResetStrikeCooldown(); @@ -64,4 +77,6 @@ private: // The mob we want to attack cPawn * m_Target; + int m_StrikeTickCnt; + }; diff --git a/src/Mobs/Behaviors/BehaviorAttackerMelee.cpp b/src/Mobs/Behaviors/BehaviorAttackerMelee.cpp new file mode 100644 index 000000000..42e88c637 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorAttackerMelee.cpp @@ -0,0 +1,11 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BehaviorAttackerMelee.h" +#include "../Monster.h" +#include "../../Entities/Pawn.h" + +bool cBehaviorAttackerMelee::StrikeTarget(std::chrono::milliseconds a_Dt, cChunk & a_Chunk, int a_StrikeTickCnt) +{ + GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); + return true; +} diff --git a/src/Mobs/Behaviors/BehaviorAttackerMelee.h b/src/Mobs/Behaviors/BehaviorAttackerMelee.h new file mode 100644 index 000000000..6bfda0aa3 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorAttackerMelee.h @@ -0,0 +1,11 @@ +#pragma once + +#include "BehaviorAttacker.h" + +/** Makes the mob fight back any other mob that damages it. Mob should have BehaviorAttacker to work. +This behavior does not make sense in combination with BehaviorCoward. */ +class cBehaviorAttackerMelee : cBehaviorAttacker +{ +public: + bool StrikeTarget(std::chrono::milliseconds a_Dt, cChunk & a_Chunk, int a_StrikeTickCnt) override; +}; diff --git a/src/Mobs/Behaviors/CMakeLists.txt b/src/Mobs/Behaviors/CMakeLists.txt index 9aa3a1a4a..686d0937b 100644 --- a/src/Mobs/Behaviors/CMakeLists.txt +++ b/src/Mobs/Behaviors/CMakeLists.txt @@ -16,6 +16,7 @@ SET (SRCS BehaviorItemDropper.cpp BehaviorItemFollower.cpp BehaviorItemReplacer.cpp + BehaviorAttackerMelee.cpp BehaviorStriker.cpp BehaviorWanderer.cpp ) @@ -32,6 +33,7 @@ SET (HDRS BehaviorItemDropper.h BehaviorItemFollower.h BehaviorItemReplacer.h + BehaviorAttackerMelee.h BehaviorStriker.h BehaviorWanderer.h ) diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 0e0197e3d..7472e3391 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -328,35 +328,45 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // If we're in a regular tick cycle if (m_TickControllingBehaviorState == Normal) { - // ask the behaviors sequentially if they are interested in controlling this mob - // Stop at the first one that says yes. - m_NewTickControllingBehavior = nullptr; - for (cBehavior * Behavior : m_AttachedTickBehaviors) - { - if (Behavior->IsControlDesired(a_Dt, a_Chunk)) - { - m_NewTickControllingBehavior = Behavior; - break; - } - } - ASSERT(m_NewTickControllingBehavior != nullptr); // it's not OK if no one asks for control - if (m_CurrentTickControllingBehavior == m_NewTickControllingBehavior) + + if (m_PinnedBehavior != nullptr) { - // The Behavior asking for control is the same as the behavior from last tick. - // Nothing special, just tick it. - // LOGD("mobDebug - Tick"); + // A behavior is pinned. We give it control automatically. + ASSERT(m_CurrentTickControllingBehavior == m_PinnedBehavior); m_CurrentTickControllingBehavior->Tick(a_Dt, a_Chunk); } - else if (m_CurrentTickControllingBehavior == nullptr) - { - // first behavior to ever control - m_TickControllingBehaviorState = NewControlStarting; - } else { - // The behavior asking for control is not the same as the behavior from last tick. - // Begin the control swapping process. - m_TickControllingBehaviorState = OldControlEnding; + // ask the behaviors sequentially if they are interested in controlling this mob + // Stop at the first one that says yes. + m_NewTickControllingBehavior = nullptr; + for (cBehavior * Behavior : m_AttachedTickBehaviors) + { + if (Behavior->IsControlDesired(a_Dt, a_Chunk)) + { + m_NewTickControllingBehavior = Behavior; + break; + } + } + ASSERT(m_NewTickControllingBehavior != nullptr); // it's not OK if no one asks for control + if (m_CurrentTickControllingBehavior == m_NewTickControllingBehavior) + { + // The Behavior asking for control is the same as the behavior from last tick. + // Nothing special, just tick it. + // LOGD("mobDebug - Tick"); + m_CurrentTickControllingBehavior->Tick(a_Dt, a_Chunk); + } + else if (m_CurrentTickControllingBehavior == nullptr) + { + // first behavior to ever control + m_TickControllingBehaviorState = NewControlStarting; + } + else + { + // The behavior asking for control is not the same as the behavior from last tick. + // Begin the control swapping process. + m_TickControllingBehaviorState = OldControlEnding; + } } } @@ -1346,6 +1356,28 @@ void cMonster::AttachDoTakeDamageBehavior(cBehavior * a_Behavior) +void cMonster::PinBehavior(cBehavior * a_Behavior) +{ + ASSERT(m_TickControllingBehaviorState == Normal); + m_PinnedBehavior = a_Behavior; + ASSERT(m_CurrentTickControllingBehavior == m_PinnedBehavior); +} + + + + + +void cMonster::UnpinBehavior(cBehavior * a_Behavior) +{ + ASSERT(m_TickControllingBehaviorState == Normal); + ASSERT(m_PinnedBehavior = a_Behavior); + m_PinnedBehavior = nullptr; +} + + + + + cMonster::eFamily cMonster::GetMobFamily(void) const { return FamilyFromType(m_MobType); diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index c893aed49..f97cd3e3f 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -220,6 +220,8 @@ public: void AttachRightClickBehavior(cBehavior * a_Behavior); void AttachDoTakeDamageBehavior(cBehavior * a_Behavior); + void PinBehavior(cBehavior * a_Behavior); + void UnpinBehavior(cBehavior * a_Behavior); protected: /** Whether or not m_NearestPlayer is stale. Always true at the beginning of a tick. @@ -335,6 +337,7 @@ private: cBehavior * m_CurrentTickControllingBehavior; cBehavior * m_NewTickControllingBehavior; + cBehavior * m_PinnedBehavior; enum TickState{NewControlStarting, OldControlEnding, Normal} m_TickControllingBehaviorState; } ; // tolua_export |