Merge pull request #2857 from LogicParrot/wolf
AI: Tamed wolf defends owner
This commit is contained in:
commit
e2a053263f
@ -1619,6 +1619,14 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
|
|||||||
// If it is a left click, attack the entity:
|
// If it is a left click, attack the entity:
|
||||||
class cDamageEntity : public cEntityCallback
|
class cDamageEntity : public cEntityCallback
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
cPlayer * m_Me;
|
||||||
|
|
||||||
|
cDamageEntity(cPlayer * a_Player) :
|
||||||
|
m_Me(a_Player)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool Item(cEntity * a_Entity) override
|
virtual bool Item(cEntity * a_Entity) override
|
||||||
{
|
{
|
||||||
if (!a_Entity->GetWorld()->IsPVPEnabled())
|
if (!a_Entity->GetWorld()->IsPVPEnabled())
|
||||||
@ -1630,21 +1638,15 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a_Entity->TakeDamage(*m_Attacker);
|
a_Entity->TakeDamage(*m_Me);
|
||||||
return false;
|
m_Me->AddFoodExhaustion(0.3);
|
||||||
|
m_Me->NotifyFriendlyWolves(a_Entity);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
public:
|
} Callback(m_Player);
|
||||||
cPawn * m_Attacker;
|
|
||||||
} Callback;
|
|
||||||
|
|
||||||
Callback.m_Attacker = m_Player;
|
|
||||||
|
|
||||||
cWorld * World = m_Player->GetWorld();
|
cWorld * World = m_Player->GetWorld();
|
||||||
if (World->DoWithEntityByID(a_TargetEntityID, Callback))
|
World->DoWithEntityByID(a_TargetEntityID, Callback);
|
||||||
{
|
|
||||||
// Any kind of an attack implies food exhaustion
|
|
||||||
m_Player->AddFoodExhaustion(0.3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,6 +102,8 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa
|
|||||||
|
|
||||||
void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
||||||
{
|
{
|
||||||
|
super::OnHitEntity(a_EntityHit, a_HitPos);
|
||||||
|
|
||||||
int Damage = static_cast<int>(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5);
|
int Damage = static_cast<int>(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5);
|
||||||
if (m_IsCritical)
|
if (m_IsCritical)
|
||||||
{
|
{
|
||||||
|
@ -43,6 +43,8 @@ void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_
|
|||||||
|
|
||||||
void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
||||||
{
|
{
|
||||||
|
super::OnHitEntity(a_EntityHit, a_HitPos);
|
||||||
|
|
||||||
Destroy();
|
Destroy();
|
||||||
Explode(a_HitPos.Floor());
|
Explode(a_HitPos.Floor());
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||||
|
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
|
#include "Mobs/Wolf.h"
|
||||||
|
#include "../BoundingBox.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "../ChatColor.h"
|
#include "../ChatColor.h"
|
||||||
#include "../Server.h"
|
#include "../Server.h"
|
||||||
@ -850,6 +852,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
|
|||||||
AddFoodExhaustion(0.3f);
|
AddFoodExhaustion(0.3f);
|
||||||
SendHealth();
|
SendHealth();
|
||||||
|
|
||||||
|
NotifyFriendlyWolves(a_TDI.Attacker);
|
||||||
m_Stats.AddValue(statDamageTaken, FloorC<StatValue>(a_TDI.FinalDamage * 10 + 0.5));
|
m_Stats.AddValue(statDamageTaken, FloorC<StatValue>(a_TDI.FinalDamage * 10 + 0.5));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -860,6 +863,42 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPlayer::NotifyFriendlyWolves(cEntity * a_Opponent)
|
||||||
|
{
|
||||||
|
class LookForWolves : public cEntityCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cPlayer * m_Player;
|
||||||
|
cEntity * m_Attacker;
|
||||||
|
|
||||||
|
LookForWolves(cPlayer * a_Me, cEntity * a_MyAttacker) :
|
||||||
|
m_Player(a_Me),
|
||||||
|
m_Attacker(a_MyAttacker)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool Item(cEntity * a_Entity) override
|
||||||
|
{
|
||||||
|
if (a_Entity->IsMob())
|
||||||
|
{
|
||||||
|
cMonster * Mob = static_cast<cMonster*>(a_Entity);
|
||||||
|
if (Mob->GetMobType() == mtWolf)
|
||||||
|
{
|
||||||
|
cWolf * Wolf = static_cast<cWolf*>(Mob);
|
||||||
|
Wolf->NearbyPlayerIsFighting(m_Player, m_Attacker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} Callback(this, a_Opponent);
|
||||||
|
|
||||||
|
m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 16, 16), Callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::KilledBy(TakeDamageInfo & a_TDI)
|
void cPlayer::KilledBy(TakeDamageInfo & a_TDI)
|
||||||
{
|
{
|
||||||
super::KilledBy(a_TDI);
|
super::KilledBy(a_TDI);
|
||||||
|
@ -499,6 +499,9 @@ public:
|
|||||||
Assumes that all the blocks are in currently loaded chunks. */
|
Assumes that all the blocks are in currently loaded chunks. */
|
||||||
bool PlaceBlocks(const sSetBlockVector & a_Blocks);
|
bool PlaceBlocks(const sSetBlockVector & a_Blocks);
|
||||||
|
|
||||||
|
/** Notify friendly wolves that we took damage or did damage to an entity so that they might assist us. */
|
||||||
|
void NotifyFriendlyWolves(cEntity * a_Opponent);
|
||||||
|
|
||||||
// cEntity overrides:
|
// cEntity overrides:
|
||||||
virtual bool IsCrouched (void) const override { return m_IsCrouched; }
|
virtual bool IsCrouched (void) const override { return m_IsCrouched; }
|
||||||
virtual bool IsSprinting(void) const override { return m_IsSprinting; }
|
virtual bool IsSprinting(void) const override { return m_IsSprinting; }
|
||||||
|
@ -312,6 +312,35 @@ void cProjectileEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cProjectileEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
||||||
|
{
|
||||||
|
if (a_EntityHit.IsPawn() && (GetCreatorName() != "")) // If we're hitting a mob or a player and we were created by a player
|
||||||
|
{
|
||||||
|
class cNotifyWolves : public cEntityCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cEntity * m_EntityHit;
|
||||||
|
|
||||||
|
cNotifyWolves(cEntity * a_Entity) :
|
||||||
|
m_EntityHit(a_Entity)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool Item(cEntity * a_Player) override
|
||||||
|
{
|
||||||
|
static_cast<cPlayer*>(a_Player)->NotifyFriendlyWolves(m_EntityHit);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} Callback(&a_EntityHit);
|
||||||
|
|
||||||
|
m_World->DoWithEntityByID(GetCreatorUniqueID(), Callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AString cProjectileEntity::GetMCAClassName(void) const
|
AString cProjectileEntity::GetMCAClassName(void) const
|
||||||
{
|
{
|
||||||
switch (m_ProjectileKind)
|
switch (m_ProjectileKind)
|
||||||
|
@ -52,11 +52,7 @@ public:
|
|||||||
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace);
|
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace);
|
||||||
|
|
||||||
/** Called by the physics blocktracer when the entity hits another entity */
|
/** Called by the physics blocktracer when the entity hits another entity */
|
||||||
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos);
|
||||||
{
|
|
||||||
UNUSED(a_EntityHit);
|
|
||||||
UNUSED(a_HitPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called by Chunk when the projectile is eligible for player collection */
|
/** Called by Chunk when the projectile is eligible for player collection */
|
||||||
virtual void CollectedBy(cPlayer & a_Dest);
|
virtual void CollectedBy(cPlayer & a_Dest);
|
||||||
|
@ -29,6 +29,8 @@ void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFac
|
|||||||
|
|
||||||
void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
||||||
{
|
{
|
||||||
|
super::OnHitEntity(a_EntityHit, a_HitPos);
|
||||||
|
|
||||||
int TotalDamage = 0;
|
int TotalDamage = 0;
|
||||||
if (a_EntityHit.IsMob())
|
if (a_EntityHit.IsMob())
|
||||||
{
|
{
|
||||||
|
@ -188,7 +188,7 @@ protected:
|
|||||||
bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS); }
|
bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS); }
|
||||||
|
|
||||||
/** Returns whether or not the target is close enough for attack. */
|
/** Returns whether or not the target is close enough for attack. */
|
||||||
bool TargetIsInRange(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); }
|
bool TargetIsInRange(void) { ASSERT(m_Target != nullptr); return ((m_Target->GetPosition() - GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); }
|
||||||
|
|
||||||
/** Returns if a monster can reach a given height by jumping. */
|
/** Returns if a monster can reach a given height by jumping. */
|
||||||
inline bool DoesPosYRequireJump(int a_PosY)
|
inline bool DoesPosYRequireJump(int a_PosY)
|
||||||
|
@ -29,7 +29,7 @@ cWolf::cWolf(void) :
|
|||||||
|
|
||||||
bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
|
bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||||
{
|
{
|
||||||
if (super::DoTakeDamage(a_TDI))
|
if (!super::DoTakeDamage(a_TDI))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -55,6 +55,10 @@ bool cWolf::Attack(std::chrono::milliseconds a_Dt)
|
|||||||
{
|
{
|
||||||
return super::Attack(a_Dt);
|
return super::Attack(a_Dt);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_Target = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -68,6 +72,19 @@ bool cWolf::Attack(std::chrono::milliseconds a_Dt)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cWolf::NearbyPlayerIsFighting(cPlayer * a_Player, cEntity * a_Opponent)
|
||||||
|
{
|
||||||
|
if ((m_Target == nullptr) && (a_Player->GetName() == m_OwnerName) && !IsSitting())
|
||||||
|
{
|
||||||
|
m_Target = a_Opponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cWolf::OnRightClicked(cPlayer & a_Player)
|
void cWolf::OnRightClicked(cPlayer & a_Player)
|
||||||
{
|
{
|
||||||
if (!IsTame() && !IsAngry())
|
if (!IsTame() && !IsAngry())
|
||||||
@ -160,6 +177,8 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_Target == nullptr)
|
||||||
|
{
|
||||||
cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
|
cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
|
||||||
if (a_Closest_Player != nullptr)
|
if (a_Closest_Player != nullptr)
|
||||||
{
|
{
|
||||||
@ -200,6 +219,26 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (IsSitting())
|
||||||
|
{
|
||||||
|
m_Target = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (TargetIsInRange())
|
||||||
|
{
|
||||||
|
StopMovingToPosition();
|
||||||
|
Attack(a_Dt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MoveToPosition(m_Target->GetPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (IsTame() && !IsSitting())
|
if (IsTame() && !IsSitting())
|
||||||
{
|
{
|
||||||
@ -237,14 +276,33 @@ void cWolf::TickFollowPlayer()
|
|||||||
{
|
{
|
||||||
Callback.OwnerPos.y = FindFirstNonAirBlockPosition(Callback.OwnerPos.x, Callback.OwnerPos.z);
|
Callback.OwnerPos.y = FindFirstNonAirBlockPosition(Callback.OwnerPos.x, Callback.OwnerPos.z);
|
||||||
TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z);
|
TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z);
|
||||||
|
m_Target = nullptr;
|
||||||
|
}
|
||||||
|
if (Distance < 2)
|
||||||
|
{
|
||||||
|
if (m_Target == nullptr)
|
||||||
|
{
|
||||||
|
StopMovingToPosition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (m_Target == nullptr)
|
||||||
{
|
{
|
||||||
MoveToPosition(Callback.OwnerPos);
|
MoveToPosition(Callback.OwnerPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cWolf::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
|
{
|
||||||
|
if (!IsTame())
|
||||||
|
{
|
||||||
|
cMonster::InStateIdle(a_Dt, a_Chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,6 +45,16 @@ public:
|
|||||||
m_OwnerUUID = a_NewOwnerUUID;
|
m_OwnerUUID = a_NewOwnerUUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Notfies the wolf that the player a_Player is being attacked by a_Attacker.
|
||||||
|
The wolf will then defend the player by attacking a_Attacker if all these conditions are met:
|
||||||
|
- a_Player is the wolf's owner.
|
||||||
|
- The wolf is not already attacking a mob.
|
||||||
|
- The wolf is not sitting.
|
||||||
|
This is called by cPlayer::NotifyFriendlyWolves whenever a player takes or deals damage and a wolf is nearby. */
|
||||||
|
void NearbyPlayerIsFighting(cPlayer * a_Player, cEntity * a_Opponent);
|
||||||
|
|
||||||
|
virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
bool m_IsSitting;
|
bool m_IsSitting;
|
||||||
|
Loading…
Reference in New Issue
Block a user