summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Doxyfile2
-rw-r--r--LICENSE4
-rw-r--r--Server/BACKERS11
-rw-r--r--Server/webadmin/files/background.gifbin57 -> 0 bytes
-rw-r--r--Server/webadmin/files/loading.gifbin7364 -> 0 bytes
-rw-r--r--Server/webadmin/files/logo.pngbin3315 -> 0 bytes
-rw-r--r--Server/webadmin/files/mc-logo.pngbin66137 -> 0 bytes
-rw-r--r--Server/webadmin/template.html140
-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/PassiveMonster.cpp36
-rw-r--r--src/Mobs/PathFinder.h2
-rw-r--r--src/Mobs/Skeleton.cpp4
-rw-r--r--src/Mobs/Wolf.cpp33
-rw-r--r--src/Root.cpp2
-rw-r--r--src/World.cpp20
28 files changed, 335 insertions, 239 deletions
diff --git a/Doxyfile b/Doxyfile
index 1b1606243..1b958f2e0 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NUMBER =
# for a project that appears at the top of each page and should give viewer
# a quick idea about the purpose of the project. Keep the description short.
-PROJECT_BRIEF = "C++ Minecraft Server"
+PROJECT_BRIEF = "A custom Minecraft compatible game server written in C++"
# With the PROJECT_LOGO tag one can specify an logo or icon that is
# included in the documentation. The maximum height of the logo should not
diff --git a/LICENSE b/LICENSE
index 1ded9b926..048be9885 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
-Cuberite: A performant C++ Minecraft Server
+Cuberite: A custom Minecraft compatible game server written in C++
www: https://github.com/cuberite/cuberite
-Copyright 2011-2015 Cuberite Team
+Copyright 2011-2016 Cuberite Team
------
diff --git a/Server/BACKERS b/Server/BACKERS
new file mode 100644
index 000000000..f2a1cfa8c
--- /dev/null
+++ b/Server/BACKERS
@@ -0,0 +1,11 @@
+Thanks to the following people for supporting the Cuberite project with a donation:
+
+ - Anonymous
+ - chrobione
+ - DrMasik
+ - PureTryOut
+ - SphinxC0re
+ - VaiN474
+
+If you enjoy Cuberite, feel free to donate to the project on Bountysource:
+https://salt.bountysource.com/teams/cuberite
diff --git a/Server/webadmin/files/background.gif b/Server/webadmin/files/background.gif
deleted file mode 100644
index cab9bed56..000000000
--- a/Server/webadmin/files/background.gif
+++ /dev/null
Binary files differ
diff --git a/Server/webadmin/files/loading.gif b/Server/webadmin/files/loading.gif
deleted file mode 100644
index b8d06f669..000000000
--- a/Server/webadmin/files/loading.gif
+++ /dev/null
Binary files differ
diff --git a/Server/webadmin/files/logo.png b/Server/webadmin/files/logo.png
deleted file mode 100644
index 50733e824..000000000
--- a/Server/webadmin/files/logo.png
+++ /dev/null
Binary files differ
diff --git a/Server/webadmin/files/mc-logo.png b/Server/webadmin/files/mc-logo.png
deleted file mode 100644
index 9a77a490f..000000000
--- a/Server/webadmin/files/mc-logo.png
+++ /dev/null
Binary files differ
diff --git a/Server/webadmin/template.html b/Server/webadmin/template.html
deleted file mode 100644
index 305c566cd..000000000
--- a/Server/webadmin/template.html
+++ /dev/null
@@ -1,140 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <title>{TITLE} | {PLUGIN_NAME}</title>
- <style type="text/css">
- body {
- line-height: 1;
- background: #B8B8B8;
- }
-
- #maincontent {
- padding: 0px 25px 10px 25px;
- }
-
- #wrapper {
- min-width: 850px;
- width: 75%;
- margin: 10px auto;
- background-color: white;
- border: 4px #888888 solid;
- border-radius: 10px;
- font-family: Calibri, Trebuchet MS;
- }
-
- header {
- text-align:center;
- padding: 10px; 0px;
- }
-
- span {
- text-align: right;
- float: right;
- border-left: 2px #C8C8C8 solid;
- border-bottom: 2px #C8C8C8 solid;
- padding: 2px 10px;
- }
-
- footer {
- font-family: helvetica;
- font-size: 10px;
- text-align: center;
- border-top: 1px #000 dotted;
- padding: 1px 0px 1px 0px;
- }
-
- table {
- border-collapse: collapse;
- border-spacing: 10;
- }
-
- table {
- border-top: 1px solid #ddd;
- width: 700px;
- }
-
- table tr th {
- text-align: left;
- background: #f6f6f6;
- padding: 0px 20px;
- height: 25px;
- line-height: 25px;
- border: 1px solid #ddd;
- border-radius: 3px;
- }
-
- table tr td {
- background: #f6f6f6;
- padding: 0px 20px;
- height: 29px;
- line-height: 29px;
- border: 1px solid #ddd;
- border-radius: 3px;
- }
-
- #main table tr.odd td {
- background: #fbfbfb;
- }
-
- table tr:hover td {
- background: #fdfcf6;
- }
-
- table .action {
- text-align: right;
- padding: 0 20px 0 10px;
- }
-
- table tr .action a {
- color: #9b9b9b;
- }
-
- #cssmenu{ height:10px; display:table; padding:0; margin: 0 auto; border:1px #707070 solid; border-radius:5px; }
- #cssmenu > ul {list-style:inside none; padding:0; margin:0;}
- #cssmenu > ul > li {list-style:inside none; padding:0; margin:0; float:left; display:block; position:relative;}
- #cssmenu > ul > li > a{ outline:none; display:block; position:relative; color:#E8E8E8; padding:10px 10px; font:bold 13px/100% Arial, Helvetica, sans-serif; text-align:center; text-decoration:none; text-shadow:1px 1px 0 rgba(0,0,0, 0.4); }
- #cssmenu > ul > li:first-child > a{border-radius:5px 0 0 5px;}
- /* #cssmenu > ul > li > a:after{ content:''; position:absolute; border-right:1px solid #FFFFFF; top:-1px; bottom:-1px; right:-2px; z-index:99; } */
- #cssmenu ul li.has-sub:hover > a:after{top:0; bottom:0;}
- #cssmenu > ul > li.has-sub > a:before{ content:''; position:absolute; top:18px; right:6px; border:5px solid transparent; border-top:5px solid #707070; }
- #cssmenu > ul > li.has-sub:hover > a:before{top:19px;}
- #cssmenu ul li.has-sub:hover > a{ background:#3f3f3f; border-color:#707070; padding-bottom:13px; padding-top:13px; top:-1px; z-index:999; }
- #cssmenu ul li.has-sub:hover > ul, #cssmenu ul li.has-sub:hover > div{display:block;}
- #cssmenu ul li.has-sub > a:hover{background:#3f3f3f; border-color:#3f3f3f;}
- #cssmenu ul li > ul, #cssmenu ul li > div{ display:none; width:auto; position:absolute; top:38px; padding:10px 0; background:#3f3f3f; border-radius:0 0 5px 5px; z-index:999; }
- #cssmenu ul li > ul{width:200px;}
- #cssmenu ul li > ul li{display:block; list-style:inside none; padding:0; margin:0; position:relative;}
- #cssmenu ul li > ul li a{ outline:none; display:block; position:relative; margin:0; padding:8px 20px; font:10pt Arial, Helvetica, sans-serif; color:#fff; text-decoration:none; text-shadow:1px 1px 0 rgba(0,0,0, 0.5); }
- #cssmenu, #cssmenu > ul > li > ul > li a:hover{ background:#C8C8C8;}
- #cssmenu > ul > li > a { border-right:1px solid #707070; color:#FFFFFF; }
- #cssmenu > ul > li > a:after { border-color:#707070; }
- #cssmenu > ul > li > a:hover { background:#B8B8B8; }
- </style>
- <meta name="msapplication-tooltip" content="Cuberite WebAdmin"/>
- <meta name="msapplication-navbutton-color" content="#B8B8B8" />
- <link rel="shortcut icon" href="http://cuberite.org/favicon.ico" />
- </head>
- <body>
- <div id="wrapper">
- <span>
- <b>Login: {USERNAME}</b>
- </span>
- <br />
- <header>
- <img alt="" src="files/logo.png" />
- </header>
- <nav id="cssmenu">
- <ul>
- {MENU}
- </ul>
- </nav>
- <div id="maincontent">
- {CONTENT}
- </div>
- <footer>
- <p>Cuberite is using {MEM}MB of memory, with {NUMCHUNKS} chunks loaded.</p>
- <p>Web Design by <a href="https://github.com/WebFreak001"@WebFreak001</a></p>
- </footer>
- </div>
- </body>
-</html>
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/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp
index 30b46500d..53288a54c 100644
--- a/src/Mobs/PassiveMonster.cpp
+++ b/src/Mobs/PassiveMonster.cpp
@@ -157,19 +157,33 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
virtual bool Item(cEntity * a_Entity) override
{
- // if we're the same species as someone around and they don't have a partner, start mating with them
- if ((a_Entity->GetEntityType() == m_Me->GetEntityType()) && (a_Entity != m_Me))
+ // If the entity is not a monster, don't breed with it
+ // Also, do not self-breed
+ if ((a_Entity->GetEntityType() != etMonster) || (a_Entity == m_Me))
{
- cPassiveMonster * Me = static_cast<cPassiveMonster*>(m_Me);
- cPassiveMonster * Partner = static_cast<cPassiveMonster*>(a_Entity);
- if (Partner->IsInLove() && (Partner->GetPartner() == nullptr))
- {
- Partner->EngageLoveMode(Me);
- Me->EngageLoveMode(Partner);
- return true;
- }
+ return false;
}
- return false;
+
+ cPassiveMonster * Me = static_cast<cPassiveMonster*>(m_Me);
+ cPassiveMonster * PotentialPartner = static_cast<cPassiveMonster*>(a_Entity);
+
+ // If the potential partner is not of the same species, don't breed with it
+ if (PotentialPartner->GetMobType() != Me->GetMobType())
+ {
+ return false;
+ }
+
+ // If the potential partner is not in love
+ // Or they already have a mate, do not breed with them
+ if ((!PotentialPartner->IsInLove()) || (PotentialPartner->GetPartner() != nullptr))
+ {
+ return false;
+ }
+
+ // All conditions met, let's breed!
+ PotentialPartner->EngageLoveMode(Me);
+ Me->EngageLoveMode(PotentialPartner);
+ return true;
}
} Callback(this);
diff --git a/src/Mobs/PathFinder.h b/src/Mobs/PathFinder.h
index 1bdc13a32..213530b11 100644
--- a/src/Mobs/PathFinder.h
+++ b/src/Mobs/PathFinder.h
@@ -14,7 +14,7 @@ class cPathFinder
public:
/** Creates a cPathFinder instance. Each mob should have one cPathFinder throughout its lifetime.
@param a_MobWidth The mob width.
- @param a_MobWidth The mob height.
+ @param a_MobHeight The mob height.
*/
cPathFinder(double a_MobWidth, double a_MobHeight);
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/Root.cpp b/src/Root.cpp
index aa532f88c..737d350ff 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -358,7 +358,7 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn
// Fix servers that have default world configs created prior to #2815. See #2810.
// This can probably be removed several years after 2016
// We start by inspecting the world linkage and determining if it's the default one
- if (DefaultWorldName == "world")
+ if ((DefaultWorldName == "world") && (Worlds.size() == 1))
{
auto DefaultWorldIniFile= cpp14::make_unique<cIniFile>();
if (DefaultWorldIniFile->ReadFile("world/world.ini"))
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();