summaryrefslogtreecommitdiffstats
path: root/src/Mobs/Monster.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Mobs/Monster.cpp80
1 files changed, 75 insertions, 5 deletions
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 246b433bd..7a33f3ce5 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -114,6 +114,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive)
, m_Target(nullptr)
, m_CurrentTickControllingBehavior(nullptr)
+ , m_NewTickControllingBehavior(nullptr)
{
if (!a_ConfigName.empty())
{
@@ -300,17 +301,86 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
return;
}
- if ((GetTarget() != nullptr))
+ // All behaviors can execute PostTick and PreTick.
+ // These are for bookkeeping or passive actions like laying eggs.
+ // They MUST NOT control mob movement or interefere with the main Tick.
+ for (cBehavior * Behavior : PreTickBehaviors)
{
- ASSERT(GetTarget()->IsTicking());
+ Behavior->PreTick();
+ }
+
+ // Note 1: Each monster tick, at most one Behavior executes its Tick method.
+ // Note 2: Each monster tick, exactly one of these is executed:
+ // ControlStarting, Tick, ControlEnding
- if (GetTarget()->IsPlayer())
+ // 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.
+ for (cBehavior * Behavior : TickBehaviors)
{
- if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
+ if (Behavior->IsControlDesired())
{
- SetTarget(nullptr);
+ 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.
+ m_CurrentTickControllingBehavior->Tick();
+ }
+ else
+ {
+ // The behavior asking for control is not the same as the behavior from last tick.
+ // Begin the control swapping process.
+ m_TickControllingBehaviorState = OldControlEnding;
+ }
+
+ }
+
+ // Make the current controlling behavior clean up
+ if (m_TickControllingBehaviorState == OldControlEnding)
+ {
+ if (m_CurrentTickControllingBehavior->ControlEnding())
+ {
+ // The current behavior told us it is ready for letting go of control
+ m_TickControllingBehaviorState = NewControlStarting;
+ }
+ else
+ {
+ // The current behavior is not ready for releasing control. We'll execute ControlEnding
+ // next tick too.
+ m_TickControllingBehaviorState = OldControlEnding;
+ }
+ }
+ // Make the new controlling behavior set up
+ else if (m_TickControllingBehaviorState == NewControlStarting)
+ {
+ if (m_NewTickControllingBehavior->ControlStarting())
+ {
+ // The new behavior told us it is ready for taking control
+ // The new behavior is now the current behavior. Next tick it will execute its Tick.
+ m_TickControllingBehaviorState = Normal;
+ m_CurrentTickControllingBehavior = m_NewTickControllingBehavior;
+ }
+ else
+ {
+ // The new behavior is not ready for taking control.
+ // We'll execute ControlStarting next tick too.
+ m_TickControllingBehaviorState = NewControlStarting;
+ }
+ }
+
+ // All behaviors can execute PostTick and PreTick.
+ // These are for bookkeeping or passive actions like laying eggs.
+ // They MUST NOT control mob movement or interefere with the main Tick.
+ for (cBehavior * Behavior : PostTickBehaviors)
+ {
+ Behavior->PostTick();
}
bool a_IsFollowingPath = false;