From 3cbb05a941f449a393d11596060ecb5bc4f2b737 Mon Sep 17 00:00:00 2001 From: LogicParrot Date: Thu, 24 Aug 2017 10:14:42 +0300 Subject: Initial Monster Behavior vector logic --- src/Mobs/Behaviors/Behavior.cpp | 24 ++++++-- src/Mobs/Behaviors/Behavior.h | 15 +++-- src/Mobs/Behaviors/BehaviorAggressive.h | 2 +- src/Mobs/Behaviors/BehaviorBreeder.h | 6 +- src/Mobs/Behaviors/BehaviorChaser.cpp | 15 +++++ src/Mobs/Behaviors/BehaviorChaser.h | 6 +- src/Mobs/Behaviors/BehaviorCoward.h | 4 +- src/Mobs/Behaviors/BehaviorDayLightBurner.cpp | 3 +- src/Mobs/Behaviors/BehaviorDayLightBurner.h | 7 ++- src/Mobs/Behaviors/BehaviorItemFollower.h | 4 +- src/Mobs/Behaviors/BehaviorWanderer.h | 1 + src/Mobs/Monster.cpp | 80 +++++++++++++++++++++++++-- src/Mobs/Monster.h | 5 +- 13 files changed, 139 insertions(+), 33 deletions(-) (limited to 'src/Mobs') diff --git a/src/Mobs/Behaviors/Behavior.cpp b/src/Mobs/Behaviors/Behavior.cpp index 7b54a3340..695343732 100644 --- a/src/Mobs/Behaviors/Behavior.cpp +++ b/src/Mobs/Behaviors/Behavior.cpp @@ -6,8 +6,10 @@ -bool cBehavior::IsControlDesired() +bool cBehavior::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { + UNUSED(a_Dt); + UNUSED(a_Chunk); LOGD("ERROR: Probably forgot to implement cBehavior::IsControlDesired but implement cBehavior::Tick"); return false; } @@ -16,8 +18,10 @@ bool cBehavior::IsControlDesired() -bool cBehavior::ControlStarting() +bool cBehavior::ControlStarting(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { + UNUSED(a_Dt); + UNUSED(a_Chunk); return true; } @@ -25,8 +29,10 @@ bool cBehavior::ControlStarting() -bool cBehavior::ControlEnding() +bool cBehavior::ControlEnding(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { + UNUSED(a_Dt); + UNUSED(a_Chunk); return true; } @@ -34,8 +40,10 @@ bool cBehavior::ControlEnding() -void cBehavior::Tick() +void cBehavior::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { + UNUSED(a_Dt); + UNUSED(a_Chunk); LOGD("ERROR: Called a TICK on a behavior that doesn't have one."); ASSERT(1 == 0); } @@ -44,8 +52,10 @@ void cBehavior::Tick() -void cBehavior::PostTick() +void cBehavior::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { + UNUSED(a_Dt); + UNUSED(a_Chunk); LOGD("ERROR: Called a PostTick on a behavior that doesn't have one."); ASSERT(1 == 0); } @@ -54,8 +64,10 @@ void cBehavior::PostTick() -void cBehavior::PreTick() +void cBehavior::PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { + UNUSED(a_Dt); + UNUSED(a_Chunk); LOGD("ERROR: Called a PreTick on a behavior that doesn't have one."); ASSERT(1 == 0); } diff --git a/src/Mobs/Behaviors/Behavior.h b/src/Mobs/Behaviors/Behavior.h index 81499eade..989addf8d 100644 --- a/src/Mobs/Behaviors/Behavior.h +++ b/src/Mobs/Behaviors/Behavior.h @@ -1,15 +1,18 @@ #pragma once struct TakeDamageInfo; +class cChunk; +#include + class cBehavior { public: - virtual bool IsControlDesired(); - virtual bool ControlStarting(); - virtual bool ControlEnding(); - virtual void Tick(); - virtual void PostTick(); - virtual void PreTick(); + virtual bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual bool ControlStarting(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual bool ControlEnding(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual void PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); virtual void onRightClicked(); diff --git a/src/Mobs/Behaviors/BehaviorAggressive.h b/src/Mobs/Behaviors/BehaviorAggressive.h index 14dfc1ab0..5bf86b23f 100644 --- a/src/Mobs/Behaviors/BehaviorAggressive.h +++ b/src/Mobs/Behaviors/BehaviorAggressive.h @@ -26,7 +26,7 @@ public: // Agression under specific conditions (nighttime, etc) // Functions our host Monster should invoke: - void PreTick() override; + void PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; private: cPawn * FindNewTarget(); diff --git a/src/Mobs/Behaviors/BehaviorBreeder.h b/src/Mobs/Behaviors/BehaviorBreeder.h index d95840f5e..cf030b8f3 100644 --- a/src/Mobs/Behaviors/BehaviorBreeder.h +++ b/src/Mobs/Behaviors/BehaviorBreeder.h @@ -21,9 +21,9 @@ public: cBehaviorBreeder(cMonster * a_Parent); // Functions our host Monster should invoke: - bool IsControlDesired() override; - void Tick() override; - void PostTick() override; + bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; void OnRightClicked(cPlayer & a_Player); void Destroyed() override; diff --git a/src/Mobs/Behaviors/BehaviorChaser.cpp b/src/Mobs/Behaviors/BehaviorChaser.cpp index 71a50c55b..c486c049a 100644 --- a/src/Mobs/Behaviors/BehaviorChaser.cpp +++ b/src/Mobs/Behaviors/BehaviorChaser.cpp @@ -36,6 +36,21 @@ bool cBehaviorChaser::IsControlDesired() void cBehaviorChaser::Tick() { + /* + * if ((GetTarget() != nullptr)) + { + ASSERT(GetTarget()->IsTicking()); + + if (GetTarget()->IsPlayer()) + { + if (!static_cast(GetTarget())->CanMobsTarget()) + { + SetTarget(nullptr); + } + } + } //mobTodo copied from monster.cpp + * */ + ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == m_Parent->GetWorld()))); // Stop targeting out of range targets if (GetTarget() != nullptr) diff --git a/src/Mobs/Behaviors/BehaviorChaser.h b/src/Mobs/Behaviors/BehaviorChaser.h index b71d503b3..d0910e11e 100644 --- a/src/Mobs/Behaviors/BehaviorChaser.h +++ b/src/Mobs/Behaviors/BehaviorChaser.h @@ -20,10 +20,10 @@ public: cBehaviorChaser(cMonster * a_Parent); // Functions our host Monster should invoke: - bool IsControlDesired() override; - void Tick() override; + bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; void Destroyed() override; - void PostTick() override; + void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; void DoTakeDamage(TakeDamageInfo & a_TDI) override; // Our host monster will call these once it loads its config file diff --git a/src/Mobs/Behaviors/BehaviorCoward.h b/src/Mobs/Behaviors/BehaviorCoward.h index 13deece61..048101d5d 100644 --- a/src/Mobs/Behaviors/BehaviorCoward.h +++ b/src/Mobs/Behaviors/BehaviorCoward.h @@ -16,8 +16,8 @@ public: cBehaviorCoward(cMonster * a_Parent); // Functions our host Monster should invoke: - bool IsControlDesired() override; - void Tick() override; + bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; void DoTakeDamage(TakeDamageInfo & a_TDI) override; diff --git a/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp b/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp index 062d60bae..81307f36a 100644 --- a/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp +++ b/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp @@ -12,8 +12,9 @@ cBehaviorDayLightBurner::cBehaviorDayLightBurner(cMonster * a_Parent) : m_Parent ASSERT(m_Parent != nullptr); } -void cBehaviorDayLightBurner::PostTick(cChunk & a_Chunk, bool WouldBurn) +void cBehaviorDayLightBurner::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { + // mobTodo WouldBurn int RelY = static_cast(m_Parent->GetPosY()); if ((RelY < 0) || (RelY >= cChunkDef::Height)) { diff --git a/src/Mobs/Behaviors/BehaviorDayLightBurner.h b/src/Mobs/Behaviors/BehaviorDayLightBurner.h index f059965c5..77fcce281 100644 --- a/src/Mobs/Behaviors/BehaviorDayLightBurner.h +++ b/src/Mobs/Behaviors/BehaviorDayLightBurner.h @@ -1,6 +1,7 @@ #pragma once // mobTodo I just need vector3d +#include "Behavior.h" #include "../../World.h" // fwds @@ -8,16 +9,16 @@ class cMonster; class cEntity; class cChunk; -class cBehaviorDayLightBurner +class cBehaviorDayLightBurner : cBehavior { public: cBehaviorDayLightBurner(cMonster * a_Parent); - void PostTick(cChunk & a_Chunk, bool WouldBurn); + void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk); // Functions our host Monster should invoke: - void Tick(); + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); private: cMonster * m_Parent; // Our Parent diff --git a/src/Mobs/Behaviors/BehaviorItemFollower.h b/src/Mobs/Behaviors/BehaviorItemFollower.h index ab33de65f..94f8bda15 100644 --- a/src/Mobs/Behaviors/BehaviorItemFollower.h +++ b/src/Mobs/Behaviors/BehaviorItemFollower.h @@ -17,8 +17,8 @@ public: void GetBreedingItems(cItems & a_Items); // Functions our host Monster should invoke: - bool IsControlDesired() override; - void Tick() override; + bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; private: diff --git a/src/Mobs/Behaviors/BehaviorWanderer.h b/src/Mobs/Behaviors/BehaviorWanderer.h index 23d86f0d2..258bf5f59 100644 --- a/src/Mobs/Behaviors/BehaviorWanderer.h +++ b/src/Mobs/Behaviors/BehaviorWanderer.h @@ -1,6 +1,7 @@ #pragma once // The mob will wander around +#include class cMonster; class cEntity; 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(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; diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 11737b5bf..652b0a49f 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -329,11 +329,14 @@ private: /** Leash calculations inside Tick function */ void CalcLeashActions(); + std::vector PreTickBehaviors; std::vector TickBehaviors; + std::vector PostTickBehaviors; std::vector OnDestroyBehaviors; std::vector OnRightClickBehaviors; cBehavior * m_CurrentTickControllingBehavior; - enum TickState{ControlStarting, ControlEnding, Normal} m_TickControllingBehaviorState; + cBehavior * m_NewTickControllingBehavior; + enum TickState{NewControlStarting, OldControlEnding, Normal} m_TickControllingBehaviorState; } ; // tolua_export -- cgit v1.2.3