Merge pull request #2939 from LogicParrot/m_Target
cMonster::m_target safety across worlds
This commit is contained in:
commit
7767acdc4b
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user