summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLogicParrot <LogicParrot@users.noreply.github.com>2016-02-04 08:20:08 +0100
committerLogicParrot <LogicParrot@users.noreply.github.com>2016-02-04 08:20:08 +0100
commit7767acdc4bdb4bcebcc2ae3fbf84b537d1016cd1 (patch)
treee83b2297221f3f74360304cbb4671fa8a7079722
parentMerge pull request #2941 from LogicParrot/chunkBug2 (diff)
parentcMonster::m_Target safety across worlds (diff)
downloadcuberite-7767acdc4bdb4bcebcc2ae3fbf84b537d1016cd1.tar
cuberite-7767acdc4bdb4bcebcc2ae3fbf84b537d1016cd1.tar.gz
cuberite-7767acdc4bdb4bcebcc2ae3fbf84b537d1016cd1.tar.bz2
cuberite-7767acdc4bdb4bcebcc2ae3fbf84b537d1016cd1.tar.lz
cuberite-7767acdc4bdb4bcebcc2ae3fbf84b537d1016cd1.tar.xz
cuberite-7767acdc4bdb4bcebcc2ae3fbf84b537d1016cd1.tar.zst
cuberite-7767acdc4bdb4bcebcc2ae3fbf84b537d1016cd1.zip
-rw-r--r--src/Chunk.cpp10
-rw-r--r--src/Entities/Entity.cpp7
-rw-r--r--src/Entities/Pawn.cpp88
-rw-r--r--src/Entities/Pawn.h35
-rw-r--r--src/Entities/Player.cpp8
-rw-r--r--src/Mobs/AggressiveMonster.cpp12
-rw-r--r--src/Mobs/Blaze.cpp2
-rw-r--r--src/Mobs/CaveSpider.cpp6
-rw-r--r--src/Mobs/Creeper.cpp2
-rw-r--r--src/Mobs/Enderman.cpp2
-rw-r--r--src/Mobs/Ghast.cpp2
-rw-r--r--src/Mobs/Monster.cpp121
-rw-r--r--src/Mobs/Monster.h21
-rw-r--r--src/Mobs/PassiveAggressiveMonster.cpp4
-rw-r--r--src/Mobs/Skeleton.cpp4
-rw-r--r--src/Mobs/Wolf.cpp33
-rw-r--r--src/World.cpp20
17 files changed, 294 insertions, 83 deletions
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 1e3e749fd..f5d447c45 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -1959,6 +1959,10 @@ bool cChunk::ForEachEntity(cEntityCallback & a_Callback)
for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2)
{
++itr2;
+ if ((*itr)->IsDestroyed())
+ {
+ continue;
+ }
if (a_Callback.Item(*itr))
{
return false;
@@ -1977,6 +1981,10 @@ bool cChunk::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_
for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2)
{
++itr2;
+ if ((*itr)->IsDestroyed())
+ {
+ continue;
+ }
cBoundingBox EntBox((*itr)->GetPosition(), (*itr)->GetWidth() / 2, (*itr)->GetHeight());
if (!EntBox.DoesIntersect(a_Box))
{
@@ -2000,7 +2008,7 @@ bool cChunk::DoWithEntityByID(UInt32 a_EntityID, cEntityCallback & a_Callback, b
// The entity list is locked by the parent chunkmap's CS
for (cEntityList::iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr)
{
- if ((*itr)->GetUniqueID() == a_EntityID)
+ if (((*itr)->GetUniqueID() == a_EntityID) && (!(*itr)->IsDestroyed()))
{
a_CallbackResult = a_Callback.Item(*itr);
return true;
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 0706a1676..b207e79c9 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -1514,6 +1514,13 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d
SetPosition(a_NewPosition);
+ if (this->IsMob())
+ {
+ cMonster * Monster = static_cast<cMonster*>(this);
+ Monster->SetTarget(nullptr);
+ Monster->StopEveryoneFromTargetingMe();
+ }
+
// Queue add to new world
a_World->AddEntity(this);
cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp
index 2d86dfecf..c8780c326 100644
--- a/src/Entities/Pawn.cpp
+++ b/src/Entities/Pawn.cpp
@@ -8,7 +8,7 @@
#include "BoundingBox.h"
#include "../Blocks/BlockHandler.h"
#include "EffectID.h"
-
+#include "../Mobs/Monster.h"
@@ -27,6 +27,25 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) :
+cPawn::~cPawn()
+{
+ ASSERT(m_TargetingMe.size() == 0);
+}
+
+
+
+
+
+void cPawn::Destroyed()
+{
+ StopEveryoneFromTargetingMe();
+ super::Destroyed();
+}
+
+
+
+
+
void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
// Iterate through this entity's applied effects
@@ -35,18 +54,18 @@ void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
// Copies values to prevent pesky wrong accesses and erasures
cEntityEffect::eType EffectType = iter->first;
cEntityEffect * Effect = iter->second;
-
+
Effect->OnTick(*this);
-
+
// Iterates (must be called before any possible erasure)
++iter;
-
+
// Remove effect if duration has elapsed
if (Effect->GetDuration() - Effect->GetTicks() <= 0)
{
RemoveEntityEffect(EffectType);
}
-
+
// TODO: Check for discrepancies between client and server effect values
}
@@ -126,7 +145,7 @@ void cPawn::HandleAir(void)
// Prevent the oxygen from decreasing
return;
}
-
+
super::HandleAir();
}
@@ -142,14 +161,14 @@ void cPawn::AddEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, s
// A plugin disallows the addition, bail out.
return;
}
-
+
// No need to add empty effects:
if (a_EffectType == cEntityEffect::effNoEffect)
{
return;
}
a_Duration = static_cast<int>(a_Duration * a_DistanceModifier);
-
+
m_EntityEffects[a_EffectType] = cEntityEffect::CreateEntityEffect(a_EffectType, a_Duration, a_Intensity, a_DistanceModifier);
m_World->BroadcastEntityEffect(*this, a_EffectType, a_Intensity, static_cast<short>(a_Duration));
m_EntityEffects[a_EffectType]->OnActivate(*this);
@@ -187,10 +206,10 @@ void cPawn::ClearEntityEffects()
{
// Copy values to prevent pesky wrong erasures
cEntityEffect::eType EffectType = iter->first;
-
+
// Iterates (must be called before any possible erasure)
++iter;
-
+
// Remove effect
RemoveEntityEffect(EffectType);
}
@@ -200,6 +219,38 @@ void cPawn::ClearEntityEffects()
+void cPawn::NoLongerTargetingMe(cMonster * a_Monster)
+{
+ ASSERT(!IsDestroyed()); // Our destroy override is supposed to clear all targets before we're destroyed.
+ for (auto i = m_TargetingMe.begin(); i != m_TargetingMe.end(); ++i)
+ {
+ cMonster * Monster = *i;
+ if (Monster == a_Monster)
+ {
+ ASSERT(Monster->GetTarget() != this); // The monster is notifying us it is no longer targeting us, assert if that's a lie
+ m_TargetingMe.erase(i);
+ return;
+ }
+ }
+ ASSERT(false); // If this happens, something is wrong. Perhaps the monster never called TargetingMe() or called NoLongerTargetingMe() twice.
+}
+
+
+
+
+
+void cPawn::TargetingMe(cMonster * a_Monster)
+{
+ ASSERT(!IsDestroyed());
+ ASSERT(m_TargetingMe.size() < 10000);
+ ASSERT(a_Monster->GetTarget() == this);
+ m_TargetingMe.push_back(a_Monster);
+}
+
+
+
+
+
void cPawn::HandleFalling(void)
{
/* Not pretty looking, and is more suited to wherever server-sided collision detection is implemented.
@@ -369,3 +420,20 @@ void cPawn::HandleFalling(void)
m_LastGroundHeight = GetPosY();
}
}
+
+
+
+
+
+void cPawn::StopEveryoneFromTargetingMe()
+{
+ std::vector<cMonster*>::iterator i = m_TargetingMe.begin();
+ while (i != m_TargetingMe.end())
+ {
+ cMonster * Monster = *i;
+ ASSERT(Monster->GetTarget() == this);
+ Monster->UnsafeUnsetTarget();
+ i = m_TargetingMe.erase(i);
+ }
+ ASSERT(m_TargetingMe.size() == 0);
+}
diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h
index 0ceb1073e..05bc09e88 100644
--- a/src/Entities/Pawn.h
+++ b/src/Entities/Pawn.h
@@ -4,6 +4,9 @@
#include "Entity.h"
#include "EntityEffect.h"
+// fwd cMonster
+class cMonster;
+
@@ -14,21 +17,28 @@ class cPawn :
{
// tolua_end
typedef cEntity super;
-
+
public:
CLASS_PROTODEF(cPawn)
cPawn(eEntityType a_EntityType, double a_Width, double a_Height);
-
+ ~cPawn();
+ virtual void Destroyed() override;
+
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void KilledBy(TakeDamageInfo & a_TDI) override;
-
+
virtual bool IsFireproof(void) const override;
virtual void HandleAir(void) override;
virtual void HandleFalling(void);
+ /** Tells all pawns which are targeting us to stop targeting us. */
+ void StopEveryoneFromTargetingMe();
+
+
+
// tolua_begin
-
+
/** Applies an entity effect
Checks with plugins if they allow the addition.
@param a_EffectType The entity effect to apply
@@ -37,28 +47,39 @@ public:
@param a_DistanceModifier The scalar multiplied to the potion duration, only applies to splash potions)
*/
void AddEntityEffect(cEntityEffect::eType a_EffectType, int a_EffectDurationTicks, short a_EffectIntensity, double a_DistanceModifier = 1);
-
+
/** Removes a currently applied entity effect
@param a_EffectType The entity effect to remove
*/
void RemoveEntityEffect(cEntityEffect::eType a_EffectType);
-
+
/** Returns true, if the entity effect is currently applied
@param a_EffectType The entity effect to check
*/
bool HasEntityEffect(cEntityEffect::eType a_EffectType) const;
-
+
/** Removes all currently applied entity effects (used when drinking milk) */
void ClearEntityEffects(void);
// tolua_end
+ /** remove the monster from the list of monsters targeting this pawn. */
+ void NoLongerTargetingMe(cMonster * a_Monster);
+
+ /** Add the monster to the list of monsters targeting this pawn. (Does not check if already in list!) */
+ void TargetingMe(cMonster * a_Monster);
+
protected:
typedef std::map<cEntityEffect::eType, cEntityEffect *> tEffectMap;
tEffectMap m_EntityEffects;
double m_LastGroundHeight;
bool m_bTouchGround;
+
+private:
+
+ /** A list of all monsters that are targeting this pawn. */
+ std::vector<cMonster*> m_TargetingMe;
} ; // tolua_export
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 7ba6b2bf6..5606e9668 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -176,6 +176,7 @@ cPlayer::~cPlayer(void)
void cPlayer::Destroyed()
{
CloseWindow(false);
+ super::Destroyed();
}
@@ -1681,7 +1682,6 @@ void cPlayer::FreezeInternal(const Vector3d & a_Location, bool a_ManuallyFrozen)
bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition)
{
ASSERT(a_World != nullptr);
-
if (GetWorld() == a_World)
{
// Don't move to same world
@@ -1708,6 +1708,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d
// Remove player from world
GetWorld()->RemovePlayer(this, false);
+ // Stop all mobs from targeting this player
+
+ StopEveryoneFromTargetingMe();
// Send the respawn packet:
if (a_ShouldSendRespawn && (m_ClientHandle != nullptr))
@@ -1720,6 +1723,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d
SetPosition(a_NewPosition);
+ // Stop all mobs from targeting this player
+ StopEveryoneFromTargetingMe();
+
// Queue adding player to the new world, including all the necessary adjustments to the object
a_World->AddPlayer(this);
cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index 512bfb4a1..c67f01b8f 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -26,9 +26,9 @@ void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk &
{
super::InStateChasing(a_Dt, a_Chunk);
- if (m_Target != nullptr)
+ if (GetTarget() != nullptr)
{
- MoveToPosition(m_Target->GetPosition());
+ MoveToPosition(GetTarget()->GetPosition());
}
}
@@ -62,14 +62,14 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
CheckEventSeePlayer(a_Chunk);
}
- if (m_Target == nullptr)
+ if (GetTarget() == nullptr)
{
return;
}
cTracer LineOfSight(GetWorld());
Vector3d MyHeadPosition = GetPosition() + Vector3d(0, GetHeight(), 0);
- Vector3d AttackDirection(m_Target->GetPosition() + Vector3d(0, m_Target->GetHeight(), 0) - MyHeadPosition);
+ Vector3d AttackDirection(GetTarget()->GetPosition() + Vector3d(0, GetTarget()->GetHeight(), 0) - MyHeadPosition);
if (TargetIsInRange() && !LineOfSight.Trace(MyHeadPosition, AttackDirection, static_cast<int>(AttackDirection.Length())) && (GetHealth() > 0.0))
@@ -85,14 +85,14 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
bool cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt)
{
- if ((m_Target == nullptr) || (m_AttackCoolDownTicksLeft != 0))
+ if ((GetTarget() == nullptr) || (m_AttackCoolDownTicksLeft != 0))
{
return false;
}
// Setting this higher gives us more wiggle room for attackrate
ResetAttackCooldown();
- m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0);
+ GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0);
return true;
}
diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp
index bd3b3f776..d002e14e7 100644
--- a/src/Mobs/Blaze.cpp
+++ b/src/Mobs/Blaze.cpp
@@ -34,7 +34,7 @@ void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer)
bool cBlaze::Attack(std::chrono::milliseconds a_Dt)
{
- if ((m_Target != nullptr) && (m_AttackCoolDownTicksLeft == 0))
+ if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0))
{
// Setting this higher gives us more wiggle room for attackrate
Vector3d Speed = GetLookVector() * 20;
diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp
index ee3f4803c..2a4975126 100644
--- a/src/Mobs/CaveSpider.cpp
+++ b/src/Mobs/CaveSpider.cpp
@@ -33,11 +33,11 @@ bool cCaveSpider::Attack(std::chrono::milliseconds a_Dt)
{
return false;
}
-
- if (m_Target->IsPawn())
+
+ if (GetTarget()->IsPawn())
{
// TODO: Easy = no poison, Medium = 7 seconds, Hard = 15 seconds
- static_cast<cPawn *>(m_Target)->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0);
+ static_cast<cPawn *>(GetTarget())->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0);
}
return true;
}
diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp
index d88c99953..47d294a30 100644
--- a/src/Mobs/Creeper.cpp
+++ b/src/Mobs/Creeper.cpp
@@ -27,7 +27,7 @@ void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
- if ((m_Target == nullptr) || (!TargetIsInRange() && !m_BurnedWithFlintAndSteel))
+ if ((GetTarget() == nullptr) || (!TargetIsInRange() && !m_BurnedWithFlintAndSteel))
{
if (m_bIsBlowing)
{
diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp
index 4a30a0acd..ccfd44110 100644
--- a/src/Mobs/Enderman.cpp
+++ b/src/Mobs/Enderman.cpp
@@ -104,7 +104,7 @@ void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cEnderman::CheckEventSeePlayer(cChunk & a_Chunk)
{
- if (m_Target != nullptr)
+ if (GetTarget() != nullptr)
{
return;
}
diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp
index 61813d0fe..0544255df 100644
--- a/src/Mobs/Ghast.cpp
+++ b/src/Mobs/Ghast.cpp
@@ -34,7 +34,7 @@ void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer)
bool cGhast::Attack(std::chrono::milliseconds a_Dt)
{
- if ((m_Target != nullptr) && (m_AttackCoolDownTicksLeft == 0))
+ if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0))
{
// Setting this higher gives us more wiggle room for attackrate
Vector3d Speed = GetLookVector() * 20;
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 4a543e400..28cb10238 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -74,7 +74,6 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
: super(etMonster, a_Width, a_Height)
, m_EMState(IDLE)
, m_EMPersonality(AGGRESSIVE)
- , m_Target(nullptr)
, m_PathFinder(a_Width, a_Height)
, m_PathfinderActivated(false)
, m_JumpCoolDown(0)
@@ -101,6 +100,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_RelativeWalkSpeed(1)
, m_Age(1)
, m_AgingTimer(20 * 60 * 20) // about 20 minutes
+ , m_Target(nullptr)
{
if (!a_ConfigName.empty())
{
@@ -112,6 +112,25 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
+cMonster::~cMonster()
+{
+ ASSERT(GetTarget() == nullptr);
+}
+
+
+
+
+
+void cMonster::Destroyed()
+{
+ SetTarget(nullptr); // Tell them we're no longer targeting them.
+ super::Destroyed();
+}
+
+
+
+
+
void cMonster::SpawnOn(cClientHandle & a_Client)
{
a_Client.SendSpawnMob(*this);
@@ -214,6 +233,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
super::Tick(a_Dt, a_Chunk);
GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT);
+ ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld())));
if (m_AttackCoolDownTicksLeft > 0)
{
m_AttackCoolDownTicksLeft -= 1;
@@ -234,17 +254,15 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
++m_TicksSinceLastDamaged;
}
- if ((m_Target != nullptr))
+ if ((GetTarget() != nullptr))
{
- if (m_Target->IsDestroyed())
- {
- m_Target = nullptr;
- }
- else if (m_Target->IsPlayer())
+ ASSERT(!GetTarget()->IsDestroyed());
+
+ if (GetTarget()->IsPlayer())
{
- if (static_cast<cPlayer *>(m_Target)->IsGameModeCreative())
+ if (static_cast<cPlayer *>(GetTarget())->IsGameModeCreative())
{
- m_Target = nullptr;
+ SetTarget(nullptr);
m_EMState = IDLE;
}
}
@@ -343,11 +361,10 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath)
{
- /* Todo Buggy */
Vector3d BodyDistance;
- if (!a_IsFollowingPath && (m_Target != nullptr))
+ if (!a_IsFollowingPath && (GetTarget() != nullptr))
{
- BodyDistance = m_Target->GetPosition() - GetPosition();
+ BodyDistance = GetTarget()->GetPosition() - GetPosition();
}
else
{
@@ -359,17 +376,16 @@ void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath)
SetYaw(BodyRotation);
Vector3d HeadDistance;
- if (m_Target != nullptr)
+ if (GetTarget() != nullptr)
{
- if (m_Target->IsPlayer()) // Look at a player
+ if (GetTarget()->IsPlayer()) // Look at a player
{
- HeadDistance = m_Target->GetPosition() - GetPosition();
- // HeadDistance.y = static_cast<cPlayer *>(m_Target)->GetStance() - 1;
+ HeadDistance = GetTarget()->GetPosition() - GetPosition();
}
else // Look at some other entity
{
- HeadDistance = m_Target->GetPosition() - GetPosition();
- // HeadDistance.y = m_Target->GetPosY() + GetHeight();
+ HeadDistance = GetTarget()->GetPosition() - GetPosition();
+ // HeadDistance.y = GetTarget()->GetPosY() + GetHeight();
}
}
else // Look straight
@@ -448,9 +464,9 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f);
}
- if (a_TDI.Attacker != nullptr)
+ if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn())
{
- m_Target = a_TDI.Attacker;
+ SetTarget(static_cast<cPawn*>(a_TDI.Attacker));
m_TicksSinceLastDamaged = 0;
}
return true;
@@ -577,9 +593,9 @@ void cMonster::CheckEventSeePlayer(cChunk & a_Chunk)
void cMonster::CheckEventLostPlayer(void)
{
- if (m_Target != nullptr)
+ if (GetTarget() != nullptr)
{
- if ((m_Target->GetPosition() - GetPosition()).Length() > m_SightDistance)
+ if ((GetTarget()->GetPosition() - GetPosition()).Length() > m_SightDistance)
{
EventLosePlayer();
}
@@ -598,7 +614,9 @@ void cMonster::CheckEventLostPlayer(void)
// default to change state to chasing
void cMonster::EventSeePlayer(cEntity * a_SeenPlayer, cChunk & a_Chunk)
{
- m_Target = a_SeenPlayer;
+ UNUSED(a_Chunk);
+ ASSERT(a_SeenPlayer->IsPlayer());
+ SetTarget(static_cast<cPawn*>(a_SeenPlayer));
}
@@ -607,7 +625,7 @@ void cMonster::EventSeePlayer(cEntity * a_SeenPlayer, cChunk & a_Chunk)
void cMonster::EventLosePlayer(void)
{
- m_Target = nullptr;
+ SetTarget(nullptr);
m_EMState = IDLE;
}
@@ -678,11 +696,11 @@ void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
UNUSED(a_Dt);
- if (m_Target != nullptr)
+ if (GetTarget() != nullptr)
{
Vector3d newloc = GetPosition();
- newloc.x = (m_Target->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance);
- newloc.z = (m_Target->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance);
+ newloc.x = (GetTarget()->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance);
+ newloc.z = (GetTarget()->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance);
MoveToPosition(newloc);
}
else
@@ -890,6 +908,55 @@ int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily)
+
+/** Sets the target. */
+void cMonster::SetTarget (cPawn * a_NewTarget)
+{
+ ASSERT((a_NewTarget == nullptr) || (!IsDestroyed()));
+ if (m_Target == a_NewTarget)
+ {
+ return;
+ }
+ cPawn * OldTarget = m_Target;
+ m_Target = a_NewTarget;
+
+ if (OldTarget != nullptr)
+ {
+ // Notify the old target that we are no longer targeting it.
+ OldTarget->NoLongerTargetingMe(this);
+ }
+
+ if (a_NewTarget != nullptr)
+ {
+ ASSERT(!a_NewTarget->IsDestroyed());
+ // Notify the new target that we are now targeting it.
+ m_Target->TargetingMe(this);
+ }
+
+}
+
+
+
+
+
+void cMonster::UnsafeUnsetTarget()
+{
+ m_Target = nullptr;
+}
+
+
+
+
+
+cPawn * cMonster::GetTarget ()
+{
+ return m_Target;
+}
+
+
+
+
+
cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
{
cFastRandom Random;
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 7c4683942..2155a4a7c 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -44,6 +44,10 @@ public:
*/
cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+ ~cMonster();
+
+ virtual void Destroyed() override;
+
CLASS_PROTODEF(cMonster)
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
@@ -156,6 +160,16 @@ public:
// tolua_end
+ /** Sets the target that this mob will chase. Pass a nullptr to unset. */
+ void SetTarget (cPawn * a_NewTarget);
+
+ /** Unset the target without notifying the target entity. Do not use this, use SetTarget(nullptr) instead.
+ This is only used by cPawn internally. */
+ void UnsafeUnsetTarget();
+
+ /** Returns the current target. */
+ cPawn * GetTarget ();
+
/** Creates a new object of the specified mob.
a_MobType is the type of the mob to be created
Asserts and returns null if mob type is not specified
@@ -164,9 +178,6 @@ public:
protected:
- /** A pointer to the entity this mobile is aiming to reach */
- cEntity * m_Target;
-
/** The pathfinder instance handles pathfinding for this monster. */
cPathFinder m_PathFinder;
@@ -255,4 +266,8 @@ protected:
/** Adds weapon that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */
void AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel);
+private:
+ /** A pointer to the entity this mobile is aiming to reach */
+ cPawn * m_Target;
+
} ; // tolua_export
diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp
index 71ac7bd89..a1bb1138f 100644
--- a/src/Mobs/PassiveAggressiveMonster.cpp
+++ b/src/Mobs/PassiveAggressiveMonster.cpp
@@ -26,9 +26,9 @@ bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
return false;
}
- if ((m_Target != nullptr) && (m_Target->IsPlayer()))
+ if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer()))
{
- if (!static_cast<cPlayer *>(m_Target)->IsGameModeCreative())
+ if (!static_cast<cPlayer *>(GetTarget())->IsGameModeCreative())
{
m_EMState = CHASING;
}
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
index adad543d2..7697f1279 100644
--- a/src/Mobs/Skeleton.cpp
+++ b/src/Mobs/Skeleton.cpp
@@ -52,10 +52,10 @@ bool cSkeleton::Attack(std::chrono::milliseconds a_Dt)
{
StopMovingToPosition(); // Todo handle this in a better way, the skeleton does some uneeded recalcs due to inStateChasing
cFastRandom Random;
- if ((m_Target != nullptr) && (m_AttackCoolDownTicksLeft == 0))
+ if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0))
{
Vector3d Inaccuracy = Vector3d(Random.NextFloat(0.5) - 0.25, Random.NextFloat(0.5) - 0.25, Random.NextFloat(0.5) - 0.25);
- Vector3d Speed = (m_Target->GetPosition() + Inaccuracy - GetPosition()) * 5;
+ Vector3d Speed = (GetTarget()->GetPosition() + Inaccuracy - GetPosition()) * 5;
Speed.y = Speed.y - 1 + Random.NextInt(3);
cArrowEntity * Arrow = new cArrowEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
if (Arrow == nullptr)
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index 3be14211b..d188d91eb 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -30,7 +30,7 @@ cWolf::cWolf(void) :
bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
{
- cEntity * PreviousTarget = m_Target;
+ cPawn * PreviousTarget = GetTarget();
if (!super::DoTakeDamage(a_TDI))
{
return false;
@@ -38,14 +38,13 @@ bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn())
{
- cPawn * Pawn = static_cast<cPawn*>(m_Target);
- if (Pawn->IsPlayer())
+ if (GetTarget()->IsPlayer())
{
if (m_IsTame)
{
- if ((static_cast<cPlayer*>(Pawn)->GetUUID() == m_OwnerUUID))
+ if ((static_cast<cPlayer*>(GetTarget())->GetUUID() == m_OwnerUUID))
{
- m_Target = PreviousTarget; // Do not attack owner
+ SetTarget(PreviousTarget); // Do not attack owner
}
else
{
@@ -100,16 +99,16 @@ bool cWolf::Attack(std::chrono::milliseconds a_Dt)
{
UNUSED(a_Dt);
- if ((m_Target != nullptr) && (m_Target->IsPlayer()))
+ if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer()))
{
- if (static_cast<cPlayer *>(m_Target)->GetUUID() == m_OwnerUUID)
+ if (static_cast<cPlayer *>(GetTarget())->GetUUID() == m_OwnerUUID)
{
- m_Target = nullptr;
+ SetTarget(nullptr);
return false;
}
}
- NotifyAlliesOfFight(static_cast<cPawn*>(m_Target));
+ NotifyAlliesOfFight(static_cast<cPawn*>(GetTarget()));
return super::Attack(a_Dt);
}
@@ -129,7 +128,7 @@ void cWolf::ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool
}
// If we already have a target
- if (m_Target != nullptr)
+ if (GetTarget() != nullptr)
{
// If a wolf is asking for help and we already have a target, do nothing
if (!a_IsPlayerInvolved)
@@ -159,7 +158,7 @@ void cWolf::ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool
}
}
- m_Target = a_Opponent;
+ SetTarget(a_Opponent);
}
@@ -264,7 +263,7 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
super::Tick(a_Dt, a_Chunk);
}
- if (m_Target == nullptr)
+ if (GetTarget() == nullptr)
{
cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
if (a_Closest_Player != nullptr)
@@ -311,11 +310,11 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (IsSitting())
{
- m_Target = nullptr;
+ SetTarget(nullptr);
}
else
{
- MoveToPosition(m_Target->GetPosition());
+ MoveToPosition(GetTarget()->GetPosition());
if (TargetIsInRange())
{
Attack(a_Dt);
@@ -359,18 +358,18 @@ void cWolf::TickFollowPlayer()
{
Callback.OwnerPos.y = FindFirstNonAirBlockPosition(Callback.OwnerPos.x, Callback.OwnerPos.z);
TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z);
- m_Target = nullptr;
+ SetTarget(nullptr);
}
if (Distance < 2)
{
- if (m_Target == nullptr)
+ if (GetTarget() == nullptr)
{
StopMovingToPosition();
}
}
else
{
- if (m_Target == nullptr)
+ if (GetTarget() == nullptr)
{
MoveToPosition(Callback.OwnerPos);
}
diff --git a/src/World.cpp b/src/World.cpp
index 6bb5b5940..5b6a215d8 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -2875,6 +2875,10 @@ bool cWorld::ForEachPlayer(cPlayerListCallback & a_Callback)
for (cPlayerList::iterator itr = m_Players.begin(), itr2 = itr; itr != m_Players.end(); itr = itr2)
{
++itr2;
+ if ((*itr)->IsDestroyed())
+ {
+ continue;
+ }
if (a_Callback.Item(*itr))
{
return false;
@@ -2893,6 +2897,10 @@ bool cWorld::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
+ if ((*itr)->IsDestroyed())
+ {
+ continue;
+ }
if (NoCaseCompare((*itr)->GetName(), a_PlayerName) == 0)
{
a_Callback.Item(*itr);
@@ -2915,6 +2923,10 @@ bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCa
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
+ if ((*itr)->IsDestroyed())
+ {
+ continue;
+ }
size_t Rating = RateCompareString (a_PlayerNameHint, (*itr)->GetName());
if (Rating >= BestRating)
{
@@ -2943,6 +2955,10 @@ bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallbac
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
+ if ((*itr)->IsDestroyed())
+ {
+ continue;
+ }
if ((*itr)->GetUUID() == a_PlayerUUID)
{
return a_Callback.Item(*itr);
@@ -2966,6 +2982,10 @@ cPlayer * cWorld::FindClosestPlayer(const Vector3d & a_Pos, float a_SightLimit,
cCSLock Lock(m_CSPlayers);
for (cPlayerList::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
+ if ((*itr)->IsDestroyed())
+ {
+ continue;
+ }
Vector3f Pos = (*itr)->GetPosition();
double Distance = (Pos - a_Pos).Length();