1
0
Fork 0

cMonster::m_Target safety across worlds

This commit is contained in:
LogicParrot 2016-02-01 22:49:34 +02:00
parent a5403c8976
commit 4aade202e0
17 changed files with 294 additions and 83 deletions

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;

View File

@ -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())
ASSERT(!GetTarget()->IsDestroyed());
if (GetTarget()->IsPlayer())
{
m_Target = nullptr;
}
else if (m_Target->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;

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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();