summaryrefslogtreecommitdiffstats
path: root/src/Mobs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Mobs')
-rw-r--r--src/Mobs/Behaviors/BehaviorAttacker.cpp45
-rw-r--r--src/Mobs/Behaviors/BehaviorAttacker.h25
-rw-r--r--src/Mobs/Behaviors/BehaviorAttackerMelee.cpp11
-rw-r--r--src/Mobs/Behaviors/BehaviorAttackerMelee.h11
-rw-r--r--src/Mobs/Behaviors/CMakeLists.txt2
-rw-r--r--src/Mobs/Monster.cpp80
-rw-r--r--src/Mobs/Monster.h3
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