1
0
Fork 0

Improved tamed wolf pack cooperation and projectile reactions

This commit is contained in:
LogicParrot 2016-01-22 20:55:46 +02:00
parent 30b95fcc4e
commit 439b3304f4
13 changed files with 214 additions and 83 deletions

View File

@ -191,7 +191,7 @@ MaxHealth=26
SightDistance=25.0 SightDistance=25.0
[Wolf] [Wolf]
AttackDamage=4.0 AttackDamage=8.0
AttackRange=2.0 AttackRange=2.0
AttackRate=1 AttackRate=1
MaxHealth=20 MaxHealth=20

View File

@ -106,6 +106,16 @@ cBoundingBox::cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Hei
cBoundingBox::cBoundingBox(const Vector3d & a_Pos, double a_CubeLength) :
m_Min(a_Pos.x - a_CubeLength / 2, a_Pos.y - a_CubeLength / 2, a_Pos.z - a_CubeLength / 2),
m_Max(a_Pos.x + a_CubeLength / 2, a_Pos.y + a_CubeLength / 2, a_Pos.z + a_CubeLength / 2)
{
}
cBoundingBox::cBoundingBox(const cBoundingBox & a_Orig) : cBoundingBox::cBoundingBox(const cBoundingBox & a_Orig) :
m_Min(a_Orig.m_Min), m_Min(a_Orig.m_Min),
m_Max(a_Orig.m_Max) m_Max(a_Orig.m_Max)

View File

@ -27,6 +27,7 @@ public:
cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ); cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ);
cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max); cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max);
cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height); cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height);
cBoundingBox(const Vector3d & a_Pos, double a_CubeLength);
cBoundingBox(const cBoundingBox & a_Orig); cBoundingBox(const cBoundingBox & a_Orig);
/** Moves the entire boundingbox by the specified offset */ /** Moves the entire boundingbox by the specified offset */

View File

@ -1642,7 +1642,7 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
m_Me->AddFoodExhaustion(0.3); m_Me->AddFoodExhaustion(0.3);
if (a_Entity->IsPawn()) if (a_Entity->IsPawn())
{ {
m_Me->NotifyFriendlyWolves(static_cast<cPawn*>(a_Entity)); m_Me->NotifyNearbyWolves(static_cast<cPawn*>(a_Entity), true);
} }
return true; return true;
} }

View File

@ -133,7 +133,7 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
} }
// a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, KnockbackAmount); // TODO fix knockback. // a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, KnockbackAmount); // TODO fix knockback.
a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 0); // Until knockback is fixed. a_EntityHit.TakeDamage(dtRangedAttack, GetCreatorUniqueID(), Damage, 0); // Until knockback is fixed.
if (IsOnFire() && !a_EntityHit.IsSubmerged() && !a_EntityHit.IsSwimming()) if (IsOnFire() && !a_EntityHit.IsSubmerged() && !a_EntityHit.IsSwimming())
{ {

View File

@ -238,6 +238,47 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R
void cEntity::TakeDamage(eDamageType a_DamageType, UInt32 a_AttackerID, int a_RawDamage, double a_KnockbackAmount)
{
class cNotifyWolves : public cEntityCallback
{
public:
cEntity * m_Entity;
eDamageType m_DamageType;
int m_RawDamage;
double m_KnockbackAmount;
virtual bool Item(cEntity * a_Attacker) override
{
cPawn * Attacker;
if (a_Attacker->IsPawn())
{
Attacker = static_cast<cPawn*>(a_Attacker);
}
else
{
Attacker = nullptr;
}
int FinalDamage = m_RawDamage - m_Entity->GetArmorCoverAgainst(Attacker, m_DamageType, m_RawDamage);
m_Entity->TakeDamage(m_DamageType, Attacker, m_RawDamage, FinalDamage, m_KnockbackAmount);
return true;
}
} Callback;
Callback.m_Entity = this;
Callback.m_DamageType = a_DamageType;
Callback.m_RawDamage = a_RawDamage;
Callback.m_KnockbackAmount = a_KnockbackAmount;
m_World->DoWithEntityByID(a_AttackerID, Callback);
}
void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount) void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount)
{ {
TakeDamageInfo TDI; TakeDamageInfo TDI;

View File

@ -263,6 +263,9 @@ public:
/** Makes this entity take the specified damage. The final damage is calculated using current armor, then DoTakeDamage() called */ /** Makes this entity take the specified damage. The final damage is calculated using current armor, then DoTakeDamage() called */
void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount); void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount);
/** Makes this entity take the specified damage. The final damage is calculated using current armor, then DoTakeDamage() called */
void TakeDamage(eDamageType a_DamageType, UInt32 a_Attacker, int a_RawDamage, double a_KnockbackAmount);
/** Makes this entity take the specified damage. The values are packed into a TDI, knockback calculated, then sent through DoTakeDamage() */ /** Makes this entity take the specified damage. The values are packed into a TDI, knockback calculated, then sent through DoTakeDamage() */
void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount); void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount);

View File

@ -862,7 +862,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
{ {
if (a_TDI.Attacker->IsPawn()) if (a_TDI.Attacker->IsPawn())
{ {
NotifyFriendlyWolves(static_cast<cPawn*>(a_TDI.Attacker)); NotifyNearbyWolves(static_cast<cPawn*>(a_TDI.Attacker), true);
} }
} }
m_Stats.AddValue(statDamageTaken, FloorC<StatValue>(a_TDI.FinalDamage * 10 + 0.5)); m_Stats.AddValue(statDamageTaken, FloorC<StatValue>(a_TDI.FinalDamage * 10 + 0.5));
@ -875,7 +875,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
void cPlayer::NotifyFriendlyWolves(cPawn * a_Opponent) void cPlayer::NotifyNearbyWolves(cPawn * a_Opponent, bool a_IsPlayerInvolved)
{ {
ASSERT(a_Opponent != nullptr); ASSERT(a_Opponent != nullptr);
class LookForWolves : public cEntityCallback class LookForWolves : public cEntityCallback
@ -883,10 +883,12 @@ void cPlayer::NotifyFriendlyWolves(cPawn * a_Opponent)
public: public:
cPlayer * m_Player; cPlayer * m_Player;
cPawn * m_Attacker; cPawn * m_Attacker;
bool m_IsPlayerInvolved;
LookForWolves(cPlayer * a_Me, cPawn * a_MyAttacker) : LookForWolves(cPlayer * a_Me, cPawn * a_MyAttacker, bool a_PlayerInvolved) :
m_Player(a_Me), m_Player(a_Me),
m_Attacker(a_MyAttacker) m_Attacker(a_MyAttacker),
m_IsPlayerInvolved(a_PlayerInvolved)
{ {
} }
@ -898,14 +900,14 @@ void cPlayer::NotifyFriendlyWolves(cPawn * a_Opponent)
if (Mob->GetMobType() == mtWolf) if (Mob->GetMobType() == mtWolf)
{ {
cWolf * Wolf = static_cast<cWolf*>(Mob); cWolf * Wolf = static_cast<cWolf*>(Mob);
Wolf->NearbyPlayerIsFighting(m_Player, m_Attacker); Wolf->ReceiveNearbyFightInfo(m_Player->GetUUID(), m_Attacker, m_IsPlayerInvolved);
} }
} }
return false; return false;
} }
} Callback(this, a_Opponent); } Callback(this, a_Opponent, a_IsPlayerInvolved);
m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 16, 16), Callback); m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 16), Callback);
} }

View File

@ -499,8 +499,11 @@ 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. */ /** Notify nearby wolves that the player or one of the player's wolves took damage or did damage to an entity
void NotifyFriendlyWolves(cPawn * a_Opponent); @param a_Opponent the opponent we're fighting.
@param a_IsPlayerInvolved Should be true if the player took or did damage, and false if one of the player's wolves took or did damage.
*/
void NotifyNearbyWolves(cPawn * a_Opponent, bool a_IsPlayerInvolved);
// cEntity overrides: // cEntity overrides:
virtual bool IsCrouched (void) const override { return m_IsCrouched; } virtual bool IsCrouched (void) const override { return m_IsCrouched; }

View File

@ -314,26 +314,24 @@ void cProjectileEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_
void cProjectileEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) 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 UNUSED(a_HitPos);
{
// If we were created by a player and we hit a pawn, notify attacking player's wolves
if (a_EntityHit.IsPawn() && (GetCreatorName() != ""))
{
class cNotifyWolves : public cEntityCallback class cNotifyWolves : public cEntityCallback
{ {
public: public:
cPawn * m_EntityHit; cPawn * m_EntityHit;
cNotifyWolves(cPawn * a_Entity) : virtual bool Item(cEntity * a_Hitter) override
m_EntityHit(a_Entity)
{ {
} static_cast<cPlayer*>(a_Hitter)->NotifyNearbyWolves(m_EntityHit, true);
virtual bool Item(cEntity * a_Player) override
{
static_cast<cPlayer*>(a_Player)->NotifyFriendlyWolves(m_EntityHit);
return true; return true;
} }
} Callback(static_cast<cPawn*>(&a_EntityHit)); } Callback;
Callback.m_EntityHit = static_cast<cPawn*>(&a_EntityHit);
m_World->DoWithEntityByID(GetCreatorUniqueID(), Callback); m_World->DoWithEntityByID(GetCreatorUniqueID(), Callback);
} }
} }

View File

@ -41,7 +41,7 @@ void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d &
} }
} }
// TODO: If entity is Ender Crystal, destroy it // TODO: If entity is Ender Crystal, destroy it
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1); a_EntityHit.TakeDamage(dtRangedAttack, GetCreatorUniqueID(), TotalDamage, 1);
m_DestroyTimer = 5; m_DestroyTimer = 5;
} }

View File

@ -18,7 +18,8 @@ cWolf::cWolf(void) :
m_IsBegging(false), m_IsBegging(false),
m_IsAngry(false), m_IsAngry(false),
m_OwnerName(""), m_OwnerName(""),
m_CollarColor(E_META_DYE_ORANGE) m_CollarColor(E_META_DYE_ORANGE),
m_NotificationCooldown(0)
{ {
m_RelativeWalkSpeed = 2; m_RelativeWalkSpeed = 2;
} }
@ -29,15 +30,42 @@ cWolf::cWolf(void) :
bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
{ {
cEntity * PreviousTarget = m_Target;
if (!super::DoTakeDamage(a_TDI)) if (!super::DoTakeDamage(a_TDI))
{ {
return false; return false;
} }
if (!m_IsTame) if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn())
{
cPawn * Pawn = static_cast<cPawn*>(m_Target);
if (Pawn->IsPlayer())
{
if (m_IsTame)
{
if ((static_cast<cPlayer*>(Pawn)->GetUUID() == m_OwnerUUID))
{
m_Target = PreviousTarget; // Do not attack owner
}
else
{
SetIsSitting(false);
NotifyAlliesOfFight(static_cast<cPawn*>(a_TDI.Attacker));
}
}
else
{ {
m_IsAngry = true; m_IsAngry = true;
} }
}
else if (m_IsTame)
{
SetIsSitting(false);
NotifyAlliesOfFight(static_cast<cPawn*>(a_TDI.Attacker));
}
}
m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face
return true; return true;
} }
@ -46,56 +74,94 @@ bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
void cWolf::NotifyAlliesOfFight(cPawn * a_Opponent)
{
if (GetOwnerName() == "")
{
return;
}
m_NotificationCooldown = 15;
class cCallback : public cPlayerListCallback
{
virtual bool Item(cPlayer * a_Player) override
{
a_Player->NotifyNearbyWolves(m_Opponent, false);
return false;
}
public:
cPawn * m_Opponent;
} Callback;
Callback.m_Opponent = a_Opponent;
m_World->DoWithPlayerByUUID(m_OwnerUUID, Callback);
}
bool cWolf::Attack(std::chrono::milliseconds a_Dt) bool cWolf::Attack(std::chrono::milliseconds a_Dt)
{ {
UNUSED(a_Dt); UNUSED(a_Dt);
if ((m_Target != nullptr) && (m_Target->IsPlayer())) if ((m_Target != nullptr) && (m_Target->IsPlayer()))
{ {
if (static_cast<cPlayer *>(m_Target)->GetName() != m_OwnerName) if (static_cast<cPlayer *>(m_Target)->GetUUID() == m_OwnerUUID)
{
return super::Attack(a_Dt);
}
else
{ {
m_Target = nullptr; m_Target = nullptr;
return false;
} }
} }
else
{
return super::Attack(a_Dt);
}
return false; NotifyAlliesOfFight(static_cast<cPawn*>(m_Target));
return super::Attack(a_Dt);
} }
void cWolf::NearbyPlayerIsFighting(cPlayer * a_Player, cPawn * a_Opponent) void cWolf::ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool a_IsPlayerInvolved)
{ {
if (a_Opponent == nullptr) if (
(a_Opponent == nullptr) || IsSitting() || (!IsTame()) ||
(!a_Opponent->IsPawn()) || (a_PlayerID != m_OwnerUUID)
)
{ {
return; return;
} }
if ((m_Target == nullptr) && (a_Player->GetName() == m_OwnerName) && !IsSitting() && (a_Opponent->IsPawn()))
// If we already have a target
if (m_Target != nullptr)
{ {
m_Target = a_Opponent; // If a wolf is asking for help and we already have a target, do nothing
if (m_Target->IsPlayer() && static_cast<cPlayer *>(m_Target)->GetName() == m_OwnerName) if (!a_IsPlayerInvolved)
{ {
m_Target = nullptr; // Our owner has hurt himself, avoid attacking them. return;
} }
if (m_Target->IsMob() && static_cast<cMonster *>(m_Target)->GetMobType() == mtWolf) // If a player is asking for help and we already have a target,
// there's a 50% chance of helping and a 50% chance of doing nothing
// This helps spread a wolf pack's targets over several mobs
else if (m_World->GetTickRandomNumber(9)> 4)
{ {
cWolf * Wolf = static_cast<cWolf *>(m_Target); return;
}
}
if (a_Opponent->IsPlayer() && static_cast<cPlayer *>(a_Opponent)->GetUUID() == m_OwnerUUID)
{
return; // Our owner has hurt himself, avoid attacking them.
}
if (a_Opponent->IsMob() && static_cast<cMonster *>(a_Opponent)->GetMobType() == mtWolf)
{
cWolf * Wolf = static_cast<cWolf *>(a_Opponent);
if (Wolf->GetOwnerUUID() == GetOwnerUUID()) if (Wolf->GetOwnerUUID() == GetOwnerUUID())
{ {
m_Target = nullptr; // Our owner attacked one of their wolves. Abort attacking wolf. return; // Our owner attacked one of their wolves. Abort attacking wolf.
}
} }
} }
m_Target = a_Opponent;
} }
@ -156,7 +222,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
} }
case E_ITEM_DYE: case E_ITEM_DYE:
{ {
if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog? if (a_Player.GetUUID() == m_OwnerUUID) // Is the player the owner of the dog?
{ {
SetCollarColor(a_Player.GetEquippedItem().m_ItemDamage); SetCollarColor(a_Player.GetEquippedItem().m_ItemDamage);
if (!a_Player.IsGameModeCreative()) if (!a_Player.IsGameModeCreative())
@ -168,7 +234,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
} }
default: default:
{ {
if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog? if (a_Player.GetUUID() == m_OwnerUUID) // Is the player the owner of the dog?
{ {
SetIsSitting(!IsSitting()); SetIsSitting(!IsSitting());
} }
@ -188,6 +254,10 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (!IsAngry()) if (!IsAngry())
{ {
cMonster::Tick(a_Dt, a_Chunk); cMonster::Tick(a_Dt, a_Chunk);
if (m_NotificationCooldown > 0)
{
m_NotificationCooldown -= 1;
}
} }
else else
{ {
@ -275,13 +345,13 @@ void cWolf::TickFollowPlayer()
virtual bool Item(cPlayer * a_Player) override virtual bool Item(cPlayer * a_Player) override
{ {
OwnerPos = a_Player->GetPosition(); OwnerPos = a_Player->GetPosition();
return false; return true;
} }
public: public:
Vector3d OwnerPos; Vector3d OwnerPos;
} Callback; } Callback;
if (m_World->DoWithPlayer(m_OwnerName, Callback)) if (m_World->DoWithPlayerByUUID(m_OwnerUUID, Callback))
{ {
// The player is present in the world, follow him: // The player is present in the world, follow him:
double Distance = (Callback.OwnerPos - GetPosition()).Length(); double Distance = (Callback.OwnerPos - GetPosition()).Length();

View File

@ -18,6 +18,7 @@ public:
CLASS_PROTODEF(cWolf) CLASS_PROTODEF(cWolf)
void NotifyAlliesOfFight(cPawn * a_Opponent);
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
virtual void OnRightClicked(cPlayer & a_Player) override; virtual void OnRightClicked(cPlayer & a_Player) override;
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
@ -45,13 +46,14 @@ public:
m_OwnerUUID = a_NewOwnerUUID; m_OwnerUUID = a_NewOwnerUUID;
} }
/** Notfies the wolf that the player a_Player is being attacked by a_Attacker. /** Notfies the wolf of a nearby fight.
The wolf will then defend the player by attacking a_Attacker if all these conditions are met: The wolf may then decide to attack a_Opponent.
- a_Player is the wolf's owner. If a_IsPlayer is true, then the player whose ID is a_PlayerID is fighting a_Opponent
- The wolf is not already attacking a mob. If false, then a wolf owned by the player whose ID is a_PlayerID is fighting a_Opponent
- The wolf is not sitting. @param a_PlayerID The ID of the fighting player, or the ID of the owner whose wolf is fighting.
This is called by cPlayer::NotifyFriendlyWolves whenever a player takes or deals damage and a wolf is nearby. */ @param a_Opponent The opponent who is being faught.
void NearbyPlayerIsFighting(cPlayer * a_Player, cPawn * a_Opponent); @param a_IsPlayerInvolved Whether the fighter a player or a wolf. */
void ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool a_IsPlayerInvolved);
virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
@ -64,6 +66,7 @@ protected:
AString m_OwnerName; AString m_OwnerName;
AString m_OwnerUUID; AString m_OwnerUUID;
int m_CollarColor; int m_CollarColor;
int m_NotificationCooldown;
} ; } ;