1
0

Tamed wolf assists owner (attack / defence)

This commit is contained in:
LogicParrot 2016-01-11 21:34:41 +02:00
parent 1f9908c712
commit 359e772dee
11 changed files with 293 additions and 150 deletions

View File

@ -91,7 +91,7 @@ cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
m_ProtocolVersion(0) m_ProtocolVersion(0)
{ {
m_Protocol = new cProtocolRecognizer(this); m_Protocol = new cProtocolRecognizer(this);
s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread
m_UniqueID = s_ClientCount; m_UniqueID = s_ClientCount;
m_PingStartTime = std::chrono::steady_clock::now(); m_PingStartTime = std::chrono::steady_clock::now();
@ -106,7 +106,7 @@ cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
cClientHandle::~cClientHandle() cClientHandle::~cClientHandle()
{ {
ASSERT(m_State == csDestroyed); // Has Destroy() been called? ASSERT(m_State == csDestroyed); // Has Destroy() been called?
LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), static_cast<void *>(this)); LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), static_cast<void *>(this));
{ {
@ -125,7 +125,7 @@ cClientHandle::~cClientHandle()
// Send the Offline PlayerList packet: // Send the Offline PlayerList packet:
World->BroadcastPlayerListRemovePlayer(*m_Player, this); 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 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(); m_Player->Destroy();
} }
@ -137,10 +137,10 @@ cClientHandle::~cClientHandle()
{ {
SendDisconnect("Server shut down? Kthnxbai"); SendDisconnect("Server shut down? Kthnxbai");
} }
delete m_Protocol; delete m_Protocol;
m_Protocol = nullptr; m_Protocol = nullptr;
LOGD("ClientHandle at %p deleted", static_cast<void *>(this)); LOGD("ClientHandle at %p deleted", static_cast<void *>(this));
} }
@ -163,10 +163,10 @@ void cClientHandle::Destroy(void)
} }
m_State = csDestroying; m_State = csDestroying;
} }
// DEBUG: // DEBUG:
LOGD("%s: client %p, \"%s\"", __FUNCTION__, static_cast<void *>(this), m_Username.c_str()); LOGD("%s: client %p, \"%s\"", __FUNCTION__, static_cast<void *>(this), m_Username.c_str());
if ((m_Player != nullptr) && (m_Player->GetWorld() != nullptr)) if ((m_Player != nullptr) && (m_Player->GetWorld() != nullptr))
{ {
RemoveFromAllChunks(); RemoveFromAllChunks();
@ -249,7 +249,7 @@ AString cClientHandle::GenerateOfflineUUID(const AString & a_Username)
// Proper format for a version 3 UUID is: // 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 // 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) // Note that we generate a short UUID (without the dashes)
// First make the username lowercase: // First make the username lowercase:
AString lcUsername = StrToLower(a_Username); AString lcUsername = StrToLower(a_Username);
@ -315,11 +315,11 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
{ {
return; return;
} }
ASSERT(m_Player == nullptr); ASSERT(m_Player == nullptr);
m_Username = a_Name; m_Username = a_Name;
// Only assign UUID and properties if not already pre-assigned (BungeeCord sends those in the Handshake packet): // Only assign UUID and properties if not already pre-assigned (BungeeCord sends those in the Handshake packet):
if (m_UUID.empty()) if (m_UUID.empty())
{ {
@ -329,7 +329,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
{ {
m_Properties = a_Properties; m_Properties = a_Properties;
} }
// Send login success (if the protocol supports it): // Send login success (if the protocol supports it):
m_Protocol->SendLoginSuccess(); m_Protocol->SendLoginSuccess();
@ -342,7 +342,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
{ {
World = cRoot::Get()->GetDefaultWorld(); World = cRoot::Get()->GetDefaultWorld();
} }
if (m_Player->GetGameMode() == eGameMode_NotSet) if (m_Player->GetGameMode() == eGameMode_NotSet)
{ {
m_Player->LoginSetGameMode(World->GetGameMode()); 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())); cRoot::Get()->BroadcastChatJoin(Printf("%s has joined the game", GetUsername().c_str()));
LOGINFO("Player %s has joined the game", m_Username.c_str()); LOGINFO("Player %s has joined the game", m_Username.c_str());
} }
m_ConfirmPosition = m_Player->GetPosition(); m_ConfirmPosition = m_Player->GetPosition();
// Return a server login packet // 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 // Don't stream chunks to clients that are being destroyed
return; return;
} }
cWorld * World = m_Player->GetWorld(); cWorld * World = m_Player->GetWorld();
ASSERT(World != nullptr); ASSERT(World != nullptr);
@ -601,7 +601,7 @@ void cClientHandle::RemoveFromAllChunks()
{ {
World->RemoveClientFromChunks(this); World->RemoveClientFromChunks(this);
} }
{ {
// Reset all chunk lists: // Reset all chunk lists:
cCSLock Lock(m_CSChunkLists); 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()); LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str());
return; return;
} }
m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, a_ClickAction, a_HeldItem); 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; return;
} }
cEnchantingWindow * Window = reinterpret_cast<cEnchantingWindow *>(m_Player->GetWindow()); cEnchantingWindow * Window = reinterpret_cast<cEnchantingWindow *>(m_Player->GetWindow());
cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player); // Make a copy of the item cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player); // Make a copy of the item
short BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment); 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 NewPosition(a_PosX, a_PosY, a_PosZ);
Vector3d OldPosition = GetPlayer()->GetPosition(); Vector3d OldPosition = GetPlayer()->GetPosition();
auto PreviousIsOnGround = GetPlayer()->IsOnGround(); auto PreviousIsOnGround = GetPlayer()->IsOnGround();
// If the player has moved too far, "repair" them: // If the player has moved too far, "repair" them:
if ((OldPosition - NewPosition).SqrLength() > 100 * 100) 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: 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 // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
m_Player->SetPosition(NewPosition); m_Player->SetPosition(NewPosition);
m_Player->SetStance(a_Stance); m_Player->SetStance(a_Stance);
m_Player->SetTouchGround(a_IsOnGround); m_Player->SetTouchGround(a_IsOnGround);
@ -1069,7 +1069,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
} }
return; return;
} }
case DIG_STATUS_STARTED: case DIG_STATUS_STARTED:
{ {
BLOCKTYPE OldBlock; 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); HandleBlockDigStarted(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta);
return; return;
} }
case DIG_STATUS_FINISHED: case DIG_STATUS_FINISHED:
{ {
BLOCKTYPE OldBlock; 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); HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta);
return; return;
} }
case DIG_STATUS_CANCELLED: case DIG_STATUS_CANCELLED:
{ {
// Block breaking cancelled by player // Block breaking cancelled by player
@ -1353,13 +1353,13 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
} }
m_NumBlockChangeInteractionsThisTick++; m_NumBlockChangeInteractionsThisTick++;
if (!CheckBlockInteractionsRate()) if (!CheckBlockInteractionsRate())
{ {
Kick("Too many blocks were placed / interacted with per unit time - hacked client?"); Kick("Too many blocks were placed / interacted with per unit time - hacked client?");
return; return;
} }
const cItem & Equipped = m_Player->GetInventory().GetEquippedItem(); const cItem & Equipped = m_Player->GetInventory().GetEquippedItem();
if ((Equipped.m_ItemType != a_HeldItem.m_ItemType) && (a_HeldItem.m_ItemType != -1)) 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)", 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 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 // 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) 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; short EquippedDamage = Equipped.m_ItemDamage;
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType); cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType);
if (ItemHandler->IsPlaceable() && (a_BlockFace != BLOCK_FACE_NONE)) 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)) 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) void cClientHandle::HandleChat(const AString & a_Message)
{ {
// We no longer need to postpone message processing, because the messages already arrive in the Tick thread // We no longer need to postpone message processing, because the messages already arrive in the Tick thread
// If a command, perform it: // If a command, perform it:
AString Message(a_Message); AString Message(a_Message);
if (cRoot::Get()->GetServer()->Command(*this, Message)) if (cRoot::Get()->GetServer()->Command(*this, Message))
{ {
return; return;
} }
// Not a command, broadcast as a message: // Not a command, broadcast as a message:
cCompositeChat Msg; cCompositeChat Msg;
AString Color = m_Player->GetColor(); AString Color = m_Player->GetColor();
@ -1486,7 +1486,7 @@ void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsO
{ {
return; return;
} }
m_Player->SetYaw (a_Rotation); m_Player->SetYaw (a_Rotation);
m_Player->SetHeadYaw (a_Rotation); m_Player->SetHeadYaw (a_Rotation);
m_Player->SetPitch (a_Pitch); 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), a_WindowID, a_SlotNum, ClickActionToString(a_ClickAction),
ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount
); );
cWindow * Window = m_Player->GetWindow(); cWindow * Window = m_Player->GetWindow();
if (Window == nullptr) if (Window == nullptr)
{ {
LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str()); LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str());
return; return;
} }
Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_ClickAction, a_HeldItem); 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) void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
{ {
// TODO: Let plugins interfere via a hook // TODO: Let plugins interfere via a hook
// If it is a right click, call the entity's OnRightClicked() handler: // If it is a right click, call the entity's OnRightClicked() handler:
if (!a_IsLeftClick) if (!a_IsLeftClick)
{ {
@ -1610,7 +1610,7 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
public: public:
cRclkEntity(cPlayer & a_Player) : m_Player(a_Player) {} cRclkEntity(cPlayer & a_Player) : m_Player(a_Player) {}
} Callback (*m_Player); } Callback (*m_Player);
cWorld * World = m_Player->GetWorld(); cWorld * World = m_Player->GetWorld();
World->DoWithEntityByID(a_TargetEntityID, Callback); World->DoWithEntityByID(a_TargetEntityID, Callback);
return; return;
@ -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);
}
} }
@ -1685,7 +1687,7 @@ bool cClientHandle::CheckMultiLogin(const AString & a_Username)
{ {
return true; return true;
} }
// Check if the player is waiting to be transferred to the World. // Check if the player is waiting to be transferred to the World.
if (cRoot::Get()->GetServer()->IsPlayerInQueue(a_Username)) if (cRoot::Get()->GetServer()->IsPlayerInQueue(a_Username))
{ {
@ -1895,14 +1897,14 @@ void cClientHandle::Tick(float a_Dt)
Link->Send(OutgoingData.data(), OutgoingData.size()); Link->Send(OutgoingData.data(), OutgoingData.size());
} }
} }
m_TicksSinceLastPacket += 1; m_TicksSinceLastPacket += 1;
if (m_TicksSinceLastPacket > 600) // 30 seconds time-out if (m_TicksSinceLastPacket > 600) // 30 seconds time-out
{ {
SendDisconnect("Nooooo!! You timed out! D: Come back!"); SendDisconnect("Nooooo!! You timed out! D: Come back!");
return; return;
} }
if (m_Player == nullptr) if (m_Player == nullptr)
{ {
return; return;
@ -1960,7 +1962,7 @@ void cClientHandle::Tick(float a_Dt)
m_Player->GetWorld()->BroadcastBlockBreakAnimation(static_cast<UInt32>(m_UniqueID), m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, static_cast<char>(m_BlockDigAnimStage / 1000), this); m_Player->GetWorld()->BroadcastBlockBreakAnimation(static_cast<UInt32>(m_UniqueID), m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, static_cast<char>(m_BlockDigAnimStage / 1000), this);
} }
} }
// Reset explosion & block change counters: // Reset explosion & block change counters:
m_NumExplosionsThisTick = 0; m_NumExplosionsThisTick = 0;
m_NumBlockChangeInteractionsThisTick = 0; m_NumBlockChangeInteractionsThisTick = 0;
@ -1982,7 +1984,7 @@ void cClientHandle::ServerTick(float a_Dt)
{ {
m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
} }
// Send any queued outgoing data: // Send any queued outgoing data:
AString OutgoingData; AString OutgoingData;
{ {
@ -1993,20 +1995,20 @@ void cClientHandle::ServerTick(float a_Dt)
{ {
m_Link->Send(OutgoingData.data(), OutgoingData.size()); m_Link->Send(OutgoingData.data(), OutgoingData.size());
} }
if (m_State == csAuthenticated) if (m_State == csAuthenticated)
{ {
StreamNextChunk(); StreamNextChunk();
// Remove the client handle from the server, it will be ticked from its cPlayer object from now on // Remove the client handle from the server, it will be ticked from its cPlayer object from now on
cRoot::Get()->GetServer()->ClientMovedToWorld(this); cRoot::Get()->GetServer()->ClientMovedToWorld(this);
// Add the player to the world (start ticking from there): // Add the player to the world (start ticking from there):
m_State = csDownloadingWorld; m_State = csDownloadingWorld;
m_Player->GetWorld()->AddPlayer(m_Player); m_Player->GetWorld()->AddPlayer(m_Player);
return; return;
} }
m_TicksSinceLastPacket += 1; m_TicksSinceLastPacket += 1;
if (m_TicksSinceLastPacket > 600) // 30 seconds 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) void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{ {
ASSERT(m_Player != nullptr); ASSERT(m_Player != nullptr);
// Check chunks being sent, erase them from m_ChunksToSend: // Check chunks being sent, erase them from m_ChunksToSend:
bool Found = false; 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()); // 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; return;
} }
if (m_Protocol == nullptr) if (m_Protocol == nullptr)
{ {
// TODO (#2588): investigate if and why this occurs // TODO (#2588): investigate if and why this occurs
return; return;
} }
m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer);
// Add the chunk to the list of chunks sent to the player: // 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) void cClientHandle::SendEntityHeadLook(const cEntity & a_Entity)
{ {
ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
m_Protocol->SendEntityHeadLook(a_Entity); m_Protocol->SendEntityHeadLook(a_Entity);
} }
@ -2309,7 +2311,7 @@ void cClientHandle::SendEntityHeadLook(const cEntity & a_Entity)
void cClientHandle::SendEntityLook(const cEntity & a_Entity) void cClientHandle::SendEntityLook(const cEntity & a_Entity)
{ {
ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
m_Protocol->SendEntityLook(a_Entity); 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) 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 ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); 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) 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 ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); 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!"); LOGD("Dropped an explosion!");
return; return;
} }
// Update the statistics: // Update the statistics:
m_NumExplosionsThisTick++; m_NumExplosionsThisTick++;
m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion); 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 // Do NOT send this packet to myself
return; return;
} }
LOGD("Spawning player \"%s\" on client \"%s\" @ %s", LOGD("Spawning player \"%s\" on client \"%s\" @ %s",
a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str() a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str()
); );
m_Protocol->SendPlayerSpawn(a_Player); m_Protocol->SendPlayerSpawn(a_Player);
} }
@ -2944,7 +2946,7 @@ bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkZ)
{ {
return false; return false;
} }
cCSLock Lock(m_CSChunkLists); cCSLock Lock(m_CSChunkLists);
return m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) != m_ChunksToSend.end(); 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; return;
} }
LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, static_cast<void *>(this)); LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, static_cast<void *>(this));
cCSLock Lock(m_CSChunkLists); cCSLock Lock(m_CSChunkLists);
if (m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end()) if (m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end())

View File

@ -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)
{ {
@ -140,7 +142,7 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
// Broadcast successful hit sound // Broadcast successful hit sound
GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, static_cast<float>(0.75 + (static_cast<float>((GetUniqueID() * 23) % 32)) / 64)); GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, static_cast<float>(0.75 + (static_cast<float>((GetUniqueID() * 23) % 32)) / 64));
Destroy(); Destroy();
} }
@ -177,7 +179,7 @@ void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{ {
super::Tick(a_Dt, a_Chunk); super::Tick(a_Dt, a_Chunk);
m_Timer += a_Dt; m_Timer += a_Dt;
if (m_bIsCollected) if (m_bIsCollected)
{ {
if (m_Timer > std::chrono::milliseconds(500)) if (m_Timer > std::chrono::milliseconds(500))
@ -191,7 +193,7 @@ void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
Destroy(); Destroy();
return; return;
} }
if (m_IsInGround) if (m_IsInGround)
{ {
if (!m_HasTeleported) // Sent a teleport already, don't do again 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; m_HitGroundTimer += a_Dt;
} }
} }
int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width; int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width;
int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width; int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width;
cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
if (Chunk == nullptr) if (Chunk == nullptr)
{ {
// Inside an unloaded chunk, abort // Inside an unloaded chunk, abort
return; return;
} }
if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed? if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed?
{ {
m_IsInGround = false; // Yes, begin simulating physics again m_IsInGround = false; // Yes, begin simulating physics again

View File

@ -43,9 +43,11 @@ 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());
if (!a_EntityHit.IsFireproof()) if (!a_EntityHit.IsFireproof())
{ {
// TODO Damage Entity with 5 damage(from http://minecraft.gamepedia.com/Blaze#Blaze_fireball) // TODO Damage Entity with 5 damage(from http://minecraft.gamepedia.com/Blaze#Blaze_fireball)

View File

@ -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);

View File

@ -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; }

View File

@ -48,13 +48,13 @@ public:
m_SlowdownCoeff(0.99) // Default slowdown when not in water m_SlowdownCoeff(0.99) // Default slowdown when not in water
{ {
} }
double GetSlowdownCoeff(void) const { return m_SlowdownCoeff; } double GetSlowdownCoeff(void) const { return m_SlowdownCoeff; }
protected: protected:
cProjectileEntity * m_Projectile; cProjectileEntity * m_Projectile;
double m_SlowdownCoeff; double m_SlowdownCoeff;
// cCallbacks overrides: // cCallbacks overrides:
virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override 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() ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str()
); );
*/ */
if (cBlockInfo::IsSolid(a_BlockType)) if (cBlockInfo::IsSolid(a_BlockType))
{ {
// The projectile hit a solid block, calculate the exact hit coords: // 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."); LOGD("WEIRD! block tracer reports a hit, but BBox tracer doesn't. Ignoring the hit.");
} }
} }
// Convey some special effects from special blocks: // Convey some special effects from special blocks:
switch (a_BlockType) switch (a_BlockType)
{ {
@ -113,7 +113,7 @@ protected:
break; break;
} }
} // switch (a_BlockType) } // switch (a_BlockType)
// Continue tracing // Continue tracing
return false; return false;
} }
@ -138,8 +138,8 @@ public:
m_HitEntity(nullptr) m_HitEntity(nullptr)
{ {
} }
virtual bool Item(cEntity * a_Entity) override virtual bool Item(cEntity * a_Entity) override
{ {
if ( if (
@ -153,9 +153,9 @@ public:
return false; return false;
} }
} }
cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); 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. // 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 // The results should be good enough for our purposes
double LineCoeff; double LineCoeff;
@ -178,27 +178,27 @@ public:
// A plugin disagreed. // A plugin disagreed.
return false; return false;
} }
if (LineCoeff < m_MinCoeff) if (LineCoeff < m_MinCoeff)
{ {
// The entity is closer than anything we've stored so far, replace it as the potential victim // The entity is closer than anything we've stored so far, replace it as the potential victim
m_MinCoeff = LineCoeff; m_MinCoeff = LineCoeff;
m_HitEntity = a_Entity; m_HitEntity = a_Entity;
} }
// Don't break the enumeration, we want all the entities // Don't break the enumeration, we want all the entities
return false; return false;
} }
/** Returns the nearest entity that was hit, after the enumeration has been completed */ /** Returns the nearest entity that was hit, after the enumeration has been completed */
cEntity * GetHitEntity(void) const { return m_HitEntity; } cEntity * GetHitEntity(void) const { return m_HitEntity; }
/** Returns the line coeff where the hit was encountered, after the enumeration has been completed */ /** Returns the line coeff where the hit was encountered, after the enumeration has been completed */
double GetMinCoeff(void) const { return m_MinCoeff; } double GetMinCoeff(void) const { return m_MinCoeff; }
/** Returns true if the callback has encountered a true hit */ /** Returns true if the callback has encountered a true hit */
bool HasHit(void) const { return (m_MinCoeff < 1); } bool HasHit(void) const { return (m_MinCoeff < 1); }
protected: protected:
cProjectileEntity * m_Projectile; cProjectileEntity * m_Projectile;
const Vector3d & m_Pos; const Vector3d & m_Pos;
@ -283,7 +283,7 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator,
} }
case pkFishingFloat: break; case pkFishingFloat: break;
} }
LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind); LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind);
return nullptr; 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<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)
@ -353,7 +382,7 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a
// Already-grounded projectiles don't move at all // Already-grounded projectiles don't move at all
return; return;
} }
auto DtSec = std::chrono::duration_cast<std::chrono::duration<double>>(a_Dt); auto DtSec = std::chrono::duration_cast<std::chrono::duration<double>>(a_Dt);
const Vector3d DeltaSpeed = GetSpeed() * DtSec.count(); const Vector3d DeltaSpeed = GetSpeed() * DtSec.count();
@ -380,7 +409,7 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a
OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos); OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos);
} }
// TODO: Test the entities in the neighboring chunks, too // TODO: Test the entities in the neighboring chunks, too
// Trace the tick's worth of movement as a line: // Trace the tick's worth of movement as a line:
cProjectileTracerCallback TracerCallback(this); cProjectileTracerCallback TracerCallback(this);
if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) 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: // Update the position:
SetPosition(NextPos); SetPosition(NextPos);
// Add slowdown and gravity effect to the speed: // Add slowdown and gravity effect to the speed:
Vector3d NewSpeed(GetSpeed()); Vector3d NewSpeed(GetSpeed());
NewSpeed.y += m_Gravity * DtSec.count(); NewSpeed.y += m_Gravity * DtSec.count();

View File

@ -21,7 +21,7 @@ class cProjectileEntity :
public cEntity public cEntity
{ {
typedef cEntity super; typedef cEntity super;
public: public:
/** The kind of the projectile. The numbers correspond to the network type ID used for spawning them in the protocol. */ /** The kind of the projectile. The numbers correspond to the network type ID used for spawning them in the protocol. */
enum eKind enum eKind
@ -38,26 +38,22 @@ public:
pkWitherSkull = 66, pkWitherSkull = 66,
pkFishingFloat = 90, pkFishingFloat = 90,
} ; } ;
// tolua_end // tolua_end
CLASS_PROTODEF(cProjectileEntity) 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, 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); 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); 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 */ /** 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); 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);
@ -65,7 +61,7 @@ public:
/** Returns the kind of the projectile (fast class identification) */ /** Returns the kind of the projectile (fast class identification) */
eKind GetProjectileKind(void) const { return m_ProjectileKind; } eKind GetProjectileKind(void) const { return m_ProjectileKind; }
/** Returns the unique ID of the entity who created this projectile /** Returns the unique ID of the entity who created this projectile
May return an ID <0 May return an ID <0
*/ */
@ -75,18 +71,18 @@ public:
Will be empty for non-player creators Will be empty for non-player creators
*/ */
AString GetCreatorName(void) const { return m_CreatorData.m_Name; } AString GetCreatorName(void) const { return m_CreatorData.m_Name; }
/** Returns the string that is used as the entity type (class name) in MCA files */ /** Returns the string that is used as the entity type (class name) in MCA files */
AString GetMCAClassName(void) const; AString GetMCAClassName(void) const;
/** Returns true if the projectile has hit the ground and is stuck there */ /** Returns true if the projectile has hit the ground and is stuck there */
bool IsInGround(void) const { return m_IsInGround; } bool IsInGround(void) const { return m_IsInGround; }
// tolua_end // tolua_end
/** Sets the internal InGround flag. To be used by MCA loader only! */ /** Sets the internal InGround flag. To be used by MCA loader only! */
void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; } void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; }
protected: protected:
/** A structure that stores the Entity ID and Playername of the projectile's creator /** 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 */ /** The type of projectile I am */
eKind m_ProjectileKind; eKind m_ProjectileKind;
/** The structure for containing the entity ID and name who has created this projectile /** 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). */ The ID and / or name may be nullptr (e.g. for dispensers / mobs). */
CreatorData m_CreatorData; CreatorData m_CreatorData;
/** True if the projectile has hit the ground and is stuck there */ /** True if the projectile has hit the ground and is stuck there */
bool m_IsInGround; bool m_IsInGround;
// cEntity overrides: // cEntity overrides:
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; 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 HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void SpawnOn(cClientHandle & a_Client) override; virtual void SpawnOn(cClientHandle & a_Client) override;
} ; // tolua_export } ; // tolua_export

View File

@ -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())
{ {
@ -40,7 +42,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, this, TotalDamage, 1);
m_DestroyTimer = 5; m_DestroyTimer = 5;
} }

View File

@ -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)

View File

@ -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,12 +55,16 @@ bool cWolf::Attack(std::chrono::milliseconds a_Dt)
{ {
return super::Attack(a_Dt); return super::Attack(a_Dt);
} }
else
{
m_Target = nullptr;
}
} }
else else
{ {
return super::Attack(a_Dt); return super::Attack(a_Dt);
} }
return false; 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) void cWolf::OnRightClicked(cPlayer & a_Player)
{ {
if (!IsTame() && !IsAngry()) if (!IsTame() && !IsAngry())
@ -160,43 +177,65 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
super::Tick(a_Dt, a_Chunk); super::Tick(a_Dt, a_Chunk);
} }
cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance)); if (m_Target == nullptr)
if (a_Closest_Player != nullptr)
{ {
switch (a_Closest_Player->GetEquippedItem().m_ItemType) cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
if (a_Closest_Player != nullptr)
{ {
case E_ITEM_BONE: switch (a_Closest_Player->GetEquippedItem().m_ItemType)
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:
{ {
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); if (!IsBegging())
m_World->BroadcastEntityMetadata(*this); {
SetIsBegging(true);
m_World->BroadcastEntityMetadata(*this);
}
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())
{
MoveToPosition(a_Closest_Player->GetPosition());
}
break;
} }
default:
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())
{ {
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()) StopMovingToPosition();
{ Attack(a_Dt);
SetIsBegging(false); }
m_World->BroadcastEntityMetadata(*this); else
} {
MoveToPosition(m_Target->GetPosition());
} }
} }
} }
@ -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
{ {
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);
}
}

View File

@ -12,7 +12,7 @@ class cWolf :
public cPassiveAggressiveMonster public cPassiveAggressiveMonster
{ {
typedef cPassiveAggressiveMonster super; typedef cPassiveAggressiveMonster super;
public: public:
cWolf(void); cWolf(void);
@ -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;