Improved tamed wolf pack cooperation and projectile reactions
This commit is contained in:
parent
30b95fcc4e
commit
439b3304f4
@ -191,7 +191,7 @@ MaxHealth=26
|
||||
SightDistance=25.0
|
||||
|
||||
[Wolf]
|
||||
AttackDamage=4.0
|
||||
AttackDamage=8.0
|
||||
AttackRange=2.0
|
||||
AttackRate=1
|
||||
MaxHealth=20
|
||||
|
@ -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) :
|
||||
m_Min(a_Orig.m_Min),
|
||||
m_Max(a_Orig.m_Max)
|
||||
|
@ -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(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_CubeLength);
|
||||
cBoundingBox(const cBoundingBox & a_Orig);
|
||||
|
||||
/** Moves the entire boundingbox by the specified offset */
|
||||
|
@ -1642,7 +1642,7 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
|
||||
m_Me->AddFoodExhaustion(0.3);
|
||||
if (a_Entity->IsPawn())
|
||||
{
|
||||
m_Me->NotifyFriendlyWolves(static_cast<cPawn*>(a_Entity));
|
||||
m_Me->NotifyNearbyWolves(static_cast<cPawn*>(a_Entity), true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -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, 0); // Until knockback is fixed.
|
||||
a_EntityHit.TakeDamage(dtRangedAttack, GetCreatorUniqueID(), Damage, 0); // Until knockback is fixed.
|
||||
|
||||
if (IsOnFire() && !a_EntityHit.IsSubmerged() && !a_EntityHit.IsSwimming())
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
TakeDamageInfo TDI;
|
||||
|
@ -263,6 +263,9 @@ public:
|
||||
/** 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);
|
||||
|
||||
/** 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() */
|
||||
void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount);
|
||||
|
||||
|
@ -862,7 +862,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
{
|
||||
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));
|
||||
@ -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);
|
||||
class LookForWolves : public cEntityCallback
|
||||
@ -883,10 +883,12 @@ void cPlayer::NotifyFriendlyWolves(cPawn * a_Opponent)
|
||||
public:
|
||||
cPlayer * m_Player;
|
||||
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_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)
|
||||
{
|
||||
cWolf * Wolf = static_cast<cWolf*>(Mob);
|
||||
Wolf->NearbyPlayerIsFighting(m_Player, m_Attacker);
|
||||
Wolf->ReceiveNearbyFightInfo(m_Player->GetUUID(), m_Attacker, m_IsPlayerInvolved);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -499,8 +499,11 @@ public:
|
||||
Assumes that all the blocks are in currently loaded chunks. */
|
||||
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(cPawn * a_Opponent);
|
||||
/** Notify nearby wolves that the player or one of the player's wolves took damage or did damage to an entity
|
||||
@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:
|
||||
virtual bool IsCrouched (void) const override { return m_IsCrouched; }
|
||||
|
@ -314,26 +314,24 @@ 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
|
||||
{
|
||||
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
|
||||
{
|
||||
public:
|
||||
cPawn * m_EntityHit;
|
||||
|
||||
cNotifyWolves(cPawn * a_Entity) :
|
||||
m_EntityHit(a_Entity)
|
||||
virtual bool Item(cEntity * a_Hitter) override
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool Item(cEntity * a_Player) override
|
||||
{
|
||||
static_cast<cPlayer*>(a_Player)->NotifyFriendlyWolves(m_EntityHit);
|
||||
static_cast<cPlayer*>(a_Hitter)->NotifyNearbyWolves(m_EntityHit, true);
|
||||
return true;
|
||||
}
|
||||
} Callback(static_cast<cPawn*>(&a_EntityHit));
|
||||
} Callback;
|
||||
|
||||
Callback.m_EntityHit = static_cast<cPawn*>(&a_EntityHit);
|
||||
m_World->DoWithEntityByID(GetCreatorUniqueID(), Callback);
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d &
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
@ -18,7 +18,8 @@ cWolf::cWolf(void) :
|
||||
m_IsBegging(false),
|
||||
m_IsAngry(false),
|
||||
m_OwnerName(""),
|
||||
m_CollarColor(E_META_DYE_ORANGE)
|
||||
m_CollarColor(E_META_DYE_ORANGE),
|
||||
m_NotificationCooldown(0)
|
||||
{
|
||||
m_RelativeWalkSpeed = 2;
|
||||
}
|
||||
@ -29,15 +30,42 @@ cWolf::cWolf(void) :
|
||||
|
||||
bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
{
|
||||
cEntity * PreviousTarget = m_Target;
|
||||
if (!super::DoTakeDamage(a_TDI))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (m_IsTame)
|
||||
{
|
||||
SetIsSitting(false);
|
||||
NotifyAlliesOfFight(static_cast<cPawn*>(a_TDI.Attacker));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face
|
||||
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)
|
||||
{
|
||||
UNUSED(a_Dt);
|
||||
|
||||
if ((m_Target != nullptr) && (m_Target->IsPlayer()))
|
||||
{
|
||||
if (static_cast<cPlayer *>(m_Target)->GetName() != m_OwnerName)
|
||||
{
|
||||
return super::Attack(a_Dt);
|
||||
}
|
||||
else
|
||||
if (static_cast<cPlayer *>(m_Target)->GetUUID() == m_OwnerUUID)
|
||||
{
|
||||
m_Target = nullptr;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 (m_Target->IsPlayer() && static_cast<cPlayer *>(m_Target)->GetName() == m_OwnerName)
|
||||
// If a wolf is asking for help and we already have a target, do nothing
|
||||
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())
|
||||
{
|
||||
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:
|
||||
{
|
||||
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);
|
||||
if (!a_Player.IsGameModeCreative())
|
||||
@ -168,7 +234,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
|
||||
}
|
||||
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());
|
||||
}
|
||||
@ -188,6 +254,10 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||
if (!IsAngry())
|
||||
{
|
||||
cMonster::Tick(a_Dt, a_Chunk);
|
||||
if (m_NotificationCooldown > 0)
|
||||
{
|
||||
m_NotificationCooldown -= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -275,13 +345,13 @@ void cWolf::TickFollowPlayer()
|
||||
virtual bool Item(cPlayer * a_Player) override
|
||||
{
|
||||
OwnerPos = a_Player->GetPosition();
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
Vector3d OwnerPos;
|
||||
} Callback;
|
||||
|
||||
if (m_World->DoWithPlayer(m_OwnerName, Callback))
|
||||
if (m_World->DoWithPlayerByUUID(m_OwnerUUID, Callback))
|
||||
{
|
||||
// The player is present in the world, follow him:
|
||||
double Distance = (Callback.OwnerPos - GetPosition()).Length();
|
||||
|
@ -18,6 +18,7 @@ public:
|
||||
|
||||
CLASS_PROTODEF(cWolf)
|
||||
|
||||
void NotifyAlliesOfFight(cPawn * a_Opponent);
|
||||
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
|
||||
virtual void OnRightClicked(cPlayer & a_Player) override;
|
||||
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
||||
@ -45,13 +46,14 @@ public:
|
||||
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, cPawn * a_Opponent);
|
||||
/** Notfies the wolf of a nearby fight.
|
||||
The wolf may then decide to attack a_Opponent.
|
||||
If a_IsPlayer is true, then the player whose ID is a_PlayerID is fighting a_Opponent
|
||||
If false, then a wolf owned by the player whose ID is a_PlayerID is fighting a_Opponent
|
||||
@param a_PlayerID The ID of the fighting player, or the ID of the owner whose wolf is fighting.
|
||||
@param a_Opponent The opponent who is being faught.
|
||||
@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;
|
||||
|
||||
@ -64,6 +66,7 @@ protected:
|
||||
AString m_OwnerName;
|
||||
AString m_OwnerUUID;
|
||||
int m_CollarColor;
|
||||
int m_NotificationCooldown;
|
||||
} ;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user