From 359e772dee0ef4595c8ad982f17e13725f255219 Mon Sep 17 00:00:00 2001 From: LogicParrot Date: Mon, 11 Jan 2016 21:34:41 +0200 Subject: Tamed wolf assists owner (attack / defence) --- src/ClientHandle.cpp | 136 +++++++++++++++++----------------- src/Entities/ArrowEntity.cpp | 14 ++-- src/Entities/FireChargeEntity.cpp | 4 +- src/Entities/Player.cpp | 39 ++++++++++ src/Entities/Player.h | 3 + src/Entities/ProjectileEntity.cpp | 69 ++++++++++++----- src/Entities/ProjectileEntity.h | 40 +++++----- src/Entities/ThrownSnowballEntity.cpp | 4 +- src/Mobs/Monster.h | 2 +- src/Mobs/Wolf.cpp | 118 +++++++++++++++++++++-------- src/Mobs/Wolf.h | 12 ++- 11 files changed, 292 insertions(+), 149 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 74556d2a0..0f1164a1e 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -91,7 +91,7 @@ cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) : m_ProtocolVersion(0) { m_Protocol = new cProtocolRecognizer(this); - + s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread m_UniqueID = s_ClientCount; m_PingStartTime = std::chrono::steady_clock::now(); @@ -106,7 +106,7 @@ cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) : cClientHandle::~cClientHandle() { ASSERT(m_State == csDestroyed); // Has Destroy() been called? - + LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), static_cast(this)); { @@ -125,7 +125,7 @@ cClientHandle::~cClientHandle() // Send the Offline PlayerList packet: World->BroadcastPlayerListRemovePlayer(*m_Player, this); } - + World->RemovePlayer(m_Player, true); // Must be called before cPlayer::Destroy() as otherwise cChunk tries to delete the player, and then we do it again m_Player->Destroy(); } @@ -137,10 +137,10 @@ cClientHandle::~cClientHandle() { SendDisconnect("Server shut down? Kthnxbai"); } - + delete m_Protocol; m_Protocol = nullptr; - + LOGD("ClientHandle at %p deleted", static_cast(this)); } @@ -163,10 +163,10 @@ void cClientHandle::Destroy(void) } m_State = csDestroying; } - + // DEBUG: LOGD("%s: client %p, \"%s\"", __FUNCTION__, static_cast(this), m_Username.c_str()); - + if ((m_Player != nullptr) && (m_Player->GetWorld() != nullptr)) { RemoveFromAllChunks(); @@ -249,7 +249,7 @@ AString cClientHandle::GenerateOfflineUUID(const AString & a_Username) // Proper format for a version 3 UUID is: // xxxxxxxx-xxxx-3xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal digit and y is one of 8, 9, A, or B // Note that we generate a short UUID (without the dashes) - + // First make the username lowercase: AString lcUsername = StrToLower(a_Username); @@ -315,11 +315,11 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, { return; } - + ASSERT(m_Player == nullptr); m_Username = a_Name; - + // Only assign UUID and properties if not already pre-assigned (BungeeCord sends those in the Handshake packet): if (m_UUID.empty()) { @@ -329,7 +329,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, { m_Properties = a_Properties; } - + // Send login success (if the protocol supports it): m_Protocol->SendLoginSuccess(); @@ -342,7 +342,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, { World = cRoot::Get()->GetDefaultWorld(); } - + if (m_Player->GetGameMode() == eGameMode_NotSet) { m_Player->LoginSetGameMode(World->GetGameMode()); @@ -355,7 +355,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, cRoot::Get()->BroadcastChatJoin(Printf("%s has joined the game", GetUsername().c_str())); LOGINFO("Player %s has joined the game", m_Username.c_str()); } - + m_ConfirmPosition = m_Player->GetPosition(); // Return a server login packet @@ -574,7 +574,7 @@ void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ, cChunkSender::eChunk // Don't stream chunks to clients that are being destroyed return; } - + cWorld * World = m_Player->GetWorld(); ASSERT(World != nullptr); @@ -601,7 +601,7 @@ void cClientHandle::RemoveFromAllChunks() { World->RemoveClientFromChunks(this); } - + { // Reset all chunk lists: cCSLock Lock(m_CSChunkLists); @@ -690,7 +690,7 @@ void cClientHandle::HandleCreativeInventory(Int16 a_SlotNum, const cItem & a_Hel LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str()); return; } - + m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, a_ClickAction, a_HeldItem); } @@ -715,7 +715,7 @@ void cClientHandle::HandleEnchantItem(UInt8 a_WindowID, UInt8 a_Enchantment) { return; } - + cEnchantingWindow * Window = reinterpret_cast(m_Player->GetWindow()); cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player); // Make a copy of the item short BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment); @@ -763,7 +763,7 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, Vector3d NewPosition(a_PosX, a_PosY, a_PosZ); Vector3d OldPosition = GetPlayer()->GetPosition(); auto PreviousIsOnGround = GetPlayer()->IsOnGround(); - + // If the player has moved too far, "repair" them: if ((OldPosition - NewPosition).SqrLength() > 100 * 100) { @@ -780,7 +780,7 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, // TODO: should do some checks to see if player is not moving through terrain // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too - + m_Player->SetPosition(NewPosition); m_Player->SetStance(a_Stance); m_Player->SetTouchGround(a_IsOnGround); @@ -1069,7 +1069,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB } return; } - + case DIG_STATUS_STARTED: { BLOCKTYPE OldBlock; @@ -1078,7 +1078,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB HandleBlockDigStarted(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta); return; } - + case DIG_STATUS_FINISHED: { BLOCKTYPE OldBlock; @@ -1087,7 +1087,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta); return; } - + case DIG_STATUS_CANCELLED: { // Block breaking cancelled by player @@ -1353,13 +1353,13 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e } m_NumBlockChangeInteractionsThisTick++; - + if (!CheckBlockInteractionsRate()) { Kick("Too many blocks were placed / interacted with per unit time - hacked client?"); return; } - + const cItem & Equipped = m_Player->GetInventory().GetEquippedItem(); if ((Equipped.m_ItemType != a_HeldItem.m_ItemType) && (a_HeldItem.m_ItemType != -1)) @@ -1370,7 +1370,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e LOGWARN("Player %s tried to place a block that was not equipped (exp %d, got %d)", m_Username.c_str(), Equipped.m_ItemType, a_HeldItem.m_ItemType ); - + // Let's send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block if (a_BlockFace != BLOCK_FACE_NONE) { @@ -1401,10 +1401,10 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e } } } - + short EquippedDamage = Equipped.m_ItemDamage; cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType); - + if (ItemHandler->IsPlaceable() && (a_BlockFace != BLOCK_FACE_NONE)) { if (!ItemHandler->OnPlayerPlace(*World, *m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) @@ -1451,14 +1451,14 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e void cClientHandle::HandleChat(const AString & a_Message) { // We no longer need to postpone message processing, because the messages already arrive in the Tick thread - + // If a command, perform it: AString Message(a_Message); if (cRoot::Get()->GetServer()->Command(*this, Message)) { return; } - + // Not a command, broadcast as a message: cCompositeChat Msg; AString Color = m_Player->GetColor(); @@ -1486,7 +1486,7 @@ void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsO { return; } - + m_Player->SetYaw (a_Rotation); m_Player->SetHeadYaw (a_Rotation); m_Player->SetPitch (a_Pitch); @@ -1556,14 +1556,14 @@ void cClientHandle::HandleWindowClick(UInt8 a_WindowID, Int16 a_SlotNum, eClickA a_WindowID, a_SlotNum, ClickActionToString(a_ClickAction), ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount ); - + cWindow * Window = m_Player->GetWindow(); if (Window == nullptr) { LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str()); return; } - + Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_ClickAction, a_HeldItem); } @@ -1591,7 +1591,7 @@ void cClientHandle::HandleUpdateSign( void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick) { // TODO: Let plugins interfere via a hook - + // If it is a right click, call the entity's OnRightClicked() handler: if (!a_IsLeftClick) { @@ -1610,7 +1610,7 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick) public: cRclkEntity(cPlayer & a_Player) : m_Player(a_Player) {} } Callback (*m_Player); - + cWorld * World = m_Player->GetWorld(); World->DoWithEntityByID(a_TargetEntityID, Callback); return; @@ -1619,6 +1619,14 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick) // If it is a left click, attack the entity: class cDamageEntity : public cEntityCallback { + public: + cPlayer * m_Me; + + cDamageEntity(cPlayer * a_Player) : + m_Me(a_Player) + { + } + virtual bool Item(cEntity * a_Entity) override { if (!a_Entity->GetWorld()->IsPVPEnabled()) @@ -1630,21 +1638,15 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick) return true; } } - a_Entity->TakeDamage(*m_Attacker); - return false; + a_Entity->TakeDamage(*m_Me); + m_Me->AddFoodExhaustion(0.3); + m_Me->NotifyFriendlyWolves(a_Entity); + return true; } - public: - cPawn * m_Attacker; - } Callback; - - Callback.m_Attacker = m_Player; + } Callback(m_Player); cWorld * World = m_Player->GetWorld(); - if (World->DoWithEntityByID(a_TargetEntityID, Callback)) - { - // Any kind of an attack implies food exhaustion - m_Player->AddFoodExhaustion(0.3); - } + World->DoWithEntityByID(a_TargetEntityID, Callback); } @@ -1685,7 +1687,7 @@ bool cClientHandle::CheckMultiLogin(const AString & a_Username) { return true; } - + // Check if the player is waiting to be transferred to the World. if (cRoot::Get()->GetServer()->IsPlayerInQueue(a_Username)) { @@ -1895,14 +1897,14 @@ void cClientHandle::Tick(float a_Dt) Link->Send(OutgoingData.data(), OutgoingData.size()); } } - + m_TicksSinceLastPacket += 1; if (m_TicksSinceLastPacket > 600) // 30 seconds time-out { SendDisconnect("Nooooo!! You timed out! D: Come back!"); return; } - + if (m_Player == nullptr) { return; @@ -1960,7 +1962,7 @@ void cClientHandle::Tick(float a_Dt) m_Player->GetWorld()->BroadcastBlockBreakAnimation(static_cast(m_UniqueID), m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, static_cast(m_BlockDigAnimStage / 1000), this); } } - + // Reset explosion & block change counters: m_NumExplosionsThisTick = 0; m_NumBlockChangeInteractionsThisTick = 0; @@ -1982,7 +1984,7 @@ void cClientHandle::ServerTick(float a_Dt) { m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); } - + // Send any queued outgoing data: AString OutgoingData; { @@ -1993,20 +1995,20 @@ void cClientHandle::ServerTick(float a_Dt) { m_Link->Send(OutgoingData.data(), OutgoingData.size()); } - + if (m_State == csAuthenticated) { StreamNextChunk(); // Remove the client handle from the server, it will be ticked from its cPlayer object from now on cRoot::Get()->GetServer()->ClientMovedToWorld(this); - + // Add the player to the world (start ticking from there): m_State = csDownloadingWorld; m_Player->GetWorld()->AddPlayer(m_Player); return; } - + m_TicksSinceLastPacket += 1; if (m_TicksSinceLastPacket > 600) // 30 seconds { @@ -2185,7 +2187,7 @@ void cClientHandle::SendChatSystem(const cCompositeChat & a_Message) void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) { ASSERT(m_Player != nullptr); - + // Check chunks being sent, erase them from m_ChunksToSend: bool Found = false; { @@ -2204,13 +2206,13 @@ void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializ // LOGD("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ()); return; } - + if (m_Protocol == nullptr) { // TODO (#2588): investigate if and why this occurs return; } - + m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); // Add the chunk to the list of chunks sent to the player: @@ -2298,7 +2300,7 @@ void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNu void cClientHandle::SendEntityHeadLook(const cEntity & a_Entity) { ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - + m_Protocol->SendEntityHeadLook(a_Entity); } @@ -2309,7 +2311,7 @@ void cClientHandle::SendEntityHeadLook(const cEntity & a_Entity) void cClientHandle::SendEntityLook(const cEntity & a_Entity) { ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - + m_Protocol->SendEntityLook(a_Entity); } @@ -2329,7 +2331,7 @@ void cClientHandle::SendEntityMetadata(const cEntity & a_Entity) void cClientHandle::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) { ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - + m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); } @@ -2340,7 +2342,7 @@ void cClientHandle::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, cha void cClientHandle::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) { ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - + m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); } @@ -2373,10 +2375,10 @@ void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_Blo LOGD("Dropped an explosion!"); return; } - + // Update the statistics: m_NumExplosionsThisTick++; - + m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion); } @@ -2566,11 +2568,11 @@ void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player) // Do NOT send this packet to myself return; } - + LOGD("Spawning player \"%s\" on client \"%s\" @ %s", a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str() ); - + m_Protocol->SendPlayerSpawn(a_Player); } @@ -2944,7 +2946,7 @@ bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkZ) { return false; } - + cCSLock Lock(m_CSChunkLists); return m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) != m_ChunksToSend.end(); } @@ -2959,7 +2961,7 @@ void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ) { return; } - + LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, static_cast(this)); cCSLock Lock(m_CSChunkLists); if (m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end()) diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index 76b4c9758..47789992c 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -102,6 +102,8 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) { + super::OnHitEntity(a_EntityHit, a_HitPos); + int Damage = static_cast(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5); if (m_IsCritical) { @@ -140,7 +142,7 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) // Broadcast successful hit sound GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, static_cast(0.75 + (static_cast((GetUniqueID() * 23) % 32)) / 64)); - + Destroy(); } @@ -177,7 +179,7 @@ void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); m_Timer += a_Dt; - + if (m_bIsCollected) { if (m_Timer > std::chrono::milliseconds(500)) @@ -191,7 +193,7 @@ void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) Destroy(); return; } - + if (m_IsInGround) { if (!m_HasTeleported) // Sent a teleport already, don't do again @@ -206,17 +208,17 @@ void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) m_HitGroundTimer += a_Dt; } } - + int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width; int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width; cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); - + if (Chunk == nullptr) { // Inside an unloaded chunk, abort return; } - + if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed? { m_IsInGround = false; // Yes, begin simulating physics again diff --git a/src/Entities/FireChargeEntity.cpp b/src/Entities/FireChargeEntity.cpp index 0db4b66d3..56ce9ef3f 100644 --- a/src/Entities/FireChargeEntity.cpp +++ b/src/Entities/FireChargeEntity.cpp @@ -43,9 +43,11 @@ void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_ void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) { + super::OnHitEntity(a_EntityHit, a_HitPos); + Destroy(); Explode(a_HitPos.Floor()); - + if (!a_EntityHit.IsFireproof()) { // TODO Damage Entity with 5 damage(from http://minecraft.gamepedia.com/Blaze#Blaze_fireball) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 80f07cb65..c5a1e0f95 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -2,6 +2,8 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Player.h" +#include "Mobs/Wolf.h" +#include "../BoundingBox.h" #include #include "../ChatColor.h" #include "../Server.h" @@ -850,6 +852,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) AddFoodExhaustion(0.3f); SendHealth(); + NotifyFriendlyWolves(a_TDI.Attacker); m_Stats.AddValue(statDamageTaken, FloorC(a_TDI.FinalDamage * 10 + 0.5)); 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(a_Entity); + if (Mob->GetMobType() == mtWolf) + { + cWolf * Wolf = static_cast(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) { super::KilledBy(a_TDI); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 10c9106a3..47f98c157 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -499,6 +499,9 @@ 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(cEntity * a_Opponent); + // cEntity overrides: virtual bool IsCrouched (void) const override { return m_IsCrouched; } virtual bool IsSprinting(void) const override { return m_IsSprinting; } diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index 064076456..4c89ea965 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -48,13 +48,13 @@ public: m_SlowdownCoeff(0.99) // Default slowdown when not in water { } - + double GetSlowdownCoeff(void) const { return m_SlowdownCoeff; } - + protected: cProjectileEntity * m_Projectile; double m_SlowdownCoeff; - + // cCallbacks overrides: virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override { @@ -67,7 +67,7 @@ protected: ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str() ); */ - + if (cBlockInfo::IsSolid(a_BlockType)) { // The projectile hit a solid block, calculate the exact hit coords: @@ -94,7 +94,7 @@ protected: LOGD("WEIRD! block tracer reports a hit, but BBox tracer doesn't. Ignoring the hit."); } } - + // Convey some special effects from special blocks: switch (a_BlockType) { @@ -113,7 +113,7 @@ protected: break; } } // switch (a_BlockType) - + // Continue tracing return false; } @@ -138,8 +138,8 @@ public: m_HitEntity(nullptr) { } - - + + virtual bool Item(cEntity * a_Entity) override { if ( @@ -153,9 +153,9 @@ public: return false; } } - + cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); - + // Instead of colliding the bounding box with another bounding box in motion, we collide an enlarged bounding box with a hairline. // The results should be good enough for our purposes double LineCoeff; @@ -178,27 +178,27 @@ public: // A plugin disagreed. return false; } - + if (LineCoeff < m_MinCoeff) { // The entity is closer than anything we've stored so far, replace it as the potential victim m_MinCoeff = LineCoeff; m_HitEntity = a_Entity; } - + // Don't break the enumeration, we want all the entities return false; } - + /** Returns the nearest entity that was hit, after the enumeration has been completed */ cEntity * GetHitEntity(void) const { return m_HitEntity; } - + /** Returns the line coeff where the hit was encountered, after the enumeration has been completed */ double GetMinCoeff(void) const { return m_MinCoeff; } - + /** Returns true if the callback has encountered a true hit */ bool HasHit(void) const { return (m_MinCoeff < 1); } - + protected: cProjectileEntity * m_Projectile; const Vector3d & m_Pos; @@ -283,7 +283,7 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, } case pkFishingFloat: break; } - + LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind); return nullptr; } @@ -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(a_Player)->NotifyFriendlyWolves(m_EntityHit); + return true; + } + } Callback(&a_EntityHit); + + m_World->DoWithEntityByID(GetCreatorUniqueID(), Callback); + } +} + + + + + AString cProjectileEntity::GetMCAClassName(void) const { switch (m_ProjectileKind) @@ -353,7 +382,7 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a // Already-grounded projectiles don't move at all return; } - + auto DtSec = std::chrono::duration_cast>(a_Dt); const Vector3d DeltaSpeed = GetSpeed() * DtSec.count(); @@ -380,7 +409,7 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos); } // TODO: Test the entities in the neighboring chunks, too - + // Trace the tick's worth of movement as a line: cProjectileTracerCallback TracerCallback(this); if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) @@ -392,7 +421,7 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a // Update the position: SetPosition(NextPos); - + // Add slowdown and gravity effect to the speed: Vector3d NewSpeed(GetSpeed()); NewSpeed.y += m_Gravity * DtSec.count(); diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h index 7ee87f93a..b354c7cfc 100644 --- a/src/Entities/ProjectileEntity.h +++ b/src/Entities/ProjectileEntity.h @@ -21,7 +21,7 @@ class cProjectileEntity : public cEntity { typedef cEntity super; - + public: /** The kind of the projectile. The numbers correspond to the network type ID used for spawning them in the protocol. */ enum eKind @@ -38,26 +38,22 @@ public: pkWitherSkull = 66, pkFishingFloat = 90, } ; - + // tolua_end - + CLASS_PROTODEF(cProjectileEntity) cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height); - + static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem * a_Item, const Vector3d * a_Speed = nullptr); - + /** Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given */ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace); - + /** Called by the physics blocktracer when the entity hits another entity */ - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) - { - UNUSED(a_EntityHit); - UNUSED(a_HitPos); - } - + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos); + /** Called by Chunk when the projectile is eligible for player collection */ virtual void CollectedBy(cPlayer & a_Dest); @@ -65,7 +61,7 @@ public: /** Returns the kind of the projectile (fast class identification) */ eKind GetProjectileKind(void) const { return m_ProjectileKind; } - + /** Returns the unique ID of the entity who created this projectile May return an ID <0 */ @@ -75,18 +71,18 @@ public: Will be empty for non-player creators */ AString GetCreatorName(void) const { return m_CreatorData.m_Name; } - + /** Returns the string that is used as the entity type (class name) in MCA files */ AString GetMCAClassName(void) const; - + /** Returns true if the projectile has hit the ground and is stuck there */ bool IsInGround(void) const { return m_IsInGround; } - + // tolua_end - + /** Sets the internal InGround flag. To be used by MCA loader only! */ void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; } - + protected: /** A structure that stores the Entity ID and Playername of the projectile's creator @@ -107,17 +103,17 @@ protected: /** The type of projectile I am */ eKind m_ProjectileKind; - + /** The structure for containing the entity ID and name who has created this projectile The ID and / or name may be nullptr (e.g. for dispensers / mobs). */ CreatorData m_CreatorData; - + /** True if the projectile has hit the ground and is stuck there */ bool m_IsInGround; - + // cEntity overrides: virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SpawnOn(cClientHandle & a_Client) override; - + } ; // tolua_export diff --git a/src/Entities/ThrownSnowballEntity.cpp b/src/Entities/ThrownSnowballEntity.cpp index b28205d44..ab5429011 100644 --- a/src/Entities/ThrownSnowballEntity.cpp +++ b/src/Entities/ThrownSnowballEntity.cpp @@ -29,6 +29,8 @@ void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFac void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) { + super::OnHitEntity(a_EntityHit, a_HitPos); + int TotalDamage = 0; if (a_EntityHit.IsMob()) { @@ -40,7 +42,7 @@ void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & } // TODO: If entity is Ender Crystal, destroy it a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1); - + m_DestroyTimer = 5; } diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index cc830a058..43ea91471 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -188,7 +188,7 @@ protected: bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS); } /** 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. */ inline bool DoesPosYRequireJump(int a_PosY) diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index 2d5cfb13f..c755e9058 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -29,7 +29,7 @@ cWolf::cWolf(void) : bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) { - if (super::DoTakeDamage(a_TDI)) + if (!super::DoTakeDamage(a_TDI)) { return false; } @@ -55,12 +55,16 @@ bool cWolf::Attack(std::chrono::milliseconds a_Dt) { return super::Attack(a_Dt); } + else + { + m_Target = nullptr; + } } else { return super::Attack(a_Dt); } - + return false; } @@ -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) { if (!IsTame() && !IsAngry()) @@ -160,43 +177,65 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) super::Tick(a_Dt, a_Chunk); } - cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast(m_SightDistance)); - if (a_Closest_Player != nullptr) + if (m_Target == nullptr) { - switch (a_Closest_Player->GetEquippedItem().m_ItemType) + cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast(m_SightDistance)); + if (a_Closest_Player != nullptr) { - case E_ITEM_BONE: - case E_ITEM_RAW_BEEF: - case E_ITEM_STEAK: - case E_ITEM_RAW_CHICKEN: - case E_ITEM_COOKED_CHICKEN: - case E_ITEM_ROTTEN_FLESH: - case E_ITEM_RAW_PORKCHOP: - case E_ITEM_COOKED_PORKCHOP: + switch (a_Closest_Player->GetEquippedItem().m_ItemType) { - if (!IsBegging()) + case E_ITEM_BONE: + case E_ITEM_RAW_BEEF: + case E_ITEM_STEAK: + case E_ITEM_RAW_CHICKEN: + case E_ITEM_COOKED_CHICKEN: + case E_ITEM_ROTTEN_FLESH: + case E_ITEM_RAW_PORKCHOP: + case E_ITEM_COOKED_PORKCHOP: { - SetIsBegging(true); - m_World->BroadcastEntityMetadata(*this); - } + if (!IsBegging()) + { + SetIsBegging(true); + m_World->BroadcastEntityMetadata(*this); + } - m_FinalDestination = a_Closest_Player->GetPosition(); // So that we will look at a player holding food + m_FinalDestination = a_Closest_Player->GetPosition(); // So that we will look at a player holding food - // Don't move to the player if the wolf is sitting. - if (!IsSitting()) + // Don't move to the player if the wolf is sitting. + if (!IsSitting()) + { + MoveToPosition(a_Closest_Player->GetPosition()); + } + + break; + } + default: { - MoveToPosition(a_Closest_Player->GetPosition()); + if (IsBegging()) + { + SetIsBegging(false); + m_World->BroadcastEntityMetadata(*this); + } } - - break; } - default: + } + } + else + { + if (IsSitting()) + { + m_Target = nullptr; + } + else + { + if (TargetIsInRange()) { - if (IsBegging()) - { - SetIsBegging(false); - m_World->BroadcastEntityMetadata(*this); - } + StopMovingToPosition(); + Attack(a_Dt); + } + else + { + MoveToPosition(m_Target->GetPosition()); } } } @@ -237,14 +276,33 @@ 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; + } + if (Distance < 2) + { + if (m_Target == nullptr) + { + StopMovingToPosition(); + } } else { - MoveToPosition(Callback.OwnerPos); + if (m_Target == nullptr) + { + MoveToPosition(Callback.OwnerPos); + } } } } +void cWolf::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + if (!IsTame()) + { + cMonster::InStateIdle(a_Dt, a_Chunk); + } +} + diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h index ed37367b9..57178a3f4 100644 --- a/src/Mobs/Wolf.h +++ b/src/Mobs/Wolf.h @@ -12,7 +12,7 @@ class cWolf : public cPassiveAggressiveMonster { typedef cPassiveAggressiveMonster super; - + public: cWolf(void); @@ -45,6 +45,16 @@ 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, cEntity * a_Opponent); + + virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + protected: bool m_IsSitting; -- cgit v1.2.3