From b3d2b5b2c94193fd9364b26293b7d96b748ff96d Mon Sep 17 00:00:00 2001 From: andrew Date: Mon, 12 May 2014 17:05:09 +0300 Subject: cEntity::Killed(cEntity *) Handler; Achievement triggers; cPlayer::AwardAchievement() --- src/Entities/Entity.cpp | 5 ++++ src/Entities/Entity.h | 3 ++ src/Entities/Pickup.cpp | 10 +++++++ src/Entities/Player.cpp | 70 +++++++++++++++++++++++++++++++++++++++++--- src/Entities/Player.h | 11 +++++++ src/Protocol/Protocol17x.cpp | 6 +--- src/UI/SlotArea.cpp | 42 +++++++++++++++++++++++++- src/UI/SlotArea.h | 6 ++++ 8 files changed, 143 insertions(+), 10 deletions(-) diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 4cf10a219..9eb03acde 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -370,6 +370,11 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) if (m_Health <= 0) { KilledBy(a_TDI.Attacker); + + if (a_TDI.Attacker != NULL) + { + a_TDI.Attacker->Killed(this); + } } return true; } diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index df03d635b..a111b128d 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -299,6 +299,9 @@ public: /// Called when the health drops below zero. a_Killer may be NULL (environmental damage) virtual void KilledBy(cEntity * a_Killer); + /// Called when the entity kills another entity + virtual void Killed(cEntity * a_Victim) {} + /// Heals the specified amount of HPs void Heal(int a_HitPoints); diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index 497b41683..0fd006485 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -192,6 +192,16 @@ bool cPickup::CollectedBy(cPlayer * a_Dest) int NumAdded = a_Dest->GetInventory().AddItem(m_Item); if (NumAdded > 0) { + // Check achievements + switch (m_Item.m_ItemType) + { + case E_BLOCK_LOG: a_Dest->AwardAchievement(achMineWood); break; + case E_ITEM_LEATHER: a_Dest->AwardAchievement(achKillCow); break; + case E_ITEM_DIAMOND: a_Dest->AwardAchievement(achDiamonds); break; + case E_ITEM_BLAZE_ROD: a_Dest->AwardAchievement(achBlazeRod); break; + default: break; + } + m_Item.m_ItemCount -= NumAdded; m_World->BroadcastCollectPickup(*this, *a_Dest); // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 1df473eb2..48bb509b9 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -17,6 +17,7 @@ #include "../Vector3.h" #include "../WorldStorage/StatSerializer.h" +#include "../CompositeChat.h" #include "inifile/iniFile.h" #include "json/json.h" @@ -876,10 +877,6 @@ void cPlayer::KilledBy(cEntity * a_Killer) cPlayer* Killer = (cPlayer*)a_Killer; GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str())); - - Killer->GetStatManager().AddValue(statPlayerKills); - - m_World->GetScoreBoard().AddPlayerScore(Killer->GetName(), cObjective::otPlayerKillCount, 1); } else { @@ -898,6 +895,33 @@ void cPlayer::KilledBy(cEntity * a_Killer) +void cPlayer::Killed(cEntity * a_Victim) +{ + cScoreboard & ScoreBoard = m_World->GetScoreBoard(); + + if (a_Victim->IsPlayer()) + { + m_Stats.AddValue(statPlayerKills); + + ScoreBoard.AddPlayerScore(GetName(), cObjective::otPlayerKillCount, 1); + } + else if (a_Victim->IsMob()) + { + if (((cMonster *)a_Victim)->GetMobFamily() == cMonster::mfHostile) + { + AwardAchievement(achKillMonster); + } + + m_Stats.AddValue(statMobKills); + } + + ScoreBoard.AddPlayerScore(GetName(), cObjective::otTotalKillCount, 1); +} + + + + + void cPlayer::Respawn(void) { m_Health = GetMaxHealth(); @@ -1116,6 +1140,44 @@ void cPlayer::SetIP(const AString & a_IP) +unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach) +{ + eStatistic Prerequisite = cStatInfo::GetPrerequisite(a_Ach); + + if (Prerequisite != statInvalid) + { + if (m_Stats.GetValue(Prerequisite) == 0) + { + return 0; + } + } + + StatValue Old = m_Stats.GetValue(a_Ach); + + if (Old > 0) + { + return m_Stats.AddValue(a_Ach); + } + else + { + cCompositeChat Msg; + Msg.AddTextPart(m_PlayerName + " has just earned the achievement "); + Msg.AddTextPart(cStatInfo::GetName(a_Ach)); // TODO 2014-05-12 xdot: Use the proper cCompositeChat submessage type and send the actual title + m_World->BroadcastChat(Msg); + + StatValue New = m_Stats.AddValue(a_Ach); + + /* Achievement Get! */ + m_ClientHandle->SendStatistics(m_Stats); + + return New; + } +} + + + + + void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { SetPosition(a_PosX, a_PosY, a_PosZ); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 82a138290..b5c9d75cc 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -179,6 +179,15 @@ public: /** Return the associated statistic and achievement manager. */ cStatManager & GetStatManager() { return m_Stats; } + + /** Awards the player an achievement. + * + * If all prerequisites are met, this method will award the achievement and will broadcast a chat message. + * If the achievement has been already awarded to the player, this method will just increment the stat counter. + * + * Returns the _new_ stat value. (0 = Could not award achievement) + */ + unsigned int AwardAchievement(const eStatistic a_Ach); void SetIP(const AString & a_IP); @@ -311,6 +320,8 @@ public: void AbortEating(void); virtual void KilledBy(cEntity * a_Killer) override; + + virtual void Killed(cEntity * a_Victim) override; void Respawn(void); // tolua_export diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 3b21f7821..39feee16f 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -1879,11 +1879,7 @@ void cProtocol172::HandlePacketClientStatus(cByteBuffer & a_ByteBuffer) case 2: { // Open Inventory achievement - cStatManager & Manager = m_Client->GetPlayer()->GetStatManager(); - Manager.AddValue(achOpenInv); - - SendStatistics(Manager); - + m_Client->GetPlayer()->AwardAchievement(achOpenInv); break; } } diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 788974f9c..1e3d8eaa4 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -496,6 +496,8 @@ void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player) DraggingItem = Result; Recipe.ConsumeIngredients(Grid); Grid.CopyToItems(PlayerSlots); + + HandleCraftItem(Result, a_Player); } else if (DraggingItem.IsEqual(Result)) { @@ -505,6 +507,8 @@ void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player) DraggingItem.m_ItemCount += Result.m_ItemCount; Recipe.ConsumeIngredients(Grid); Grid.CopyToItems(PlayerSlots); + + HandleCraftItem(Result, a_Player); } } @@ -594,6 +598,27 @@ cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player) +void cSlotAreaCrafting::HandleCraftItem(const cItem & a_Result, cPlayer & a_Player) +{ + switch (a_Result.m_ItemType) + { + case E_BLOCK_WORKBENCH: a_Player.AwardAchievement(achCraftWorkbench); break; + case E_BLOCK_FURNACE: a_Player.AwardAchievement(achCraftFurnace); break; + case E_BLOCK_CAKE: a_Player.AwardAchievement(achBakeCake); break; + case E_BLOCK_ENCHANTMENT_TABLE: a_Player.AwardAchievement(achCraftEnchantTable); break; + case E_BLOCK_BOOKCASE: a_Player.AwardAchievement(achBookshelf); break; + case E_ITEM_WOODEN_PICKAXE: a_Player.AwardAchievement(achCraftPickaxe); break; + case E_ITEM_WOODEN_SWORD: a_Player.AwardAchievement(achCraftSword); break; + case E_ITEM_STONE_PICKAXE: a_Player.AwardAchievement(achCraftBetterPick); break; + case E_ITEM_WOODEN_HOE: a_Player.AwardAchievement(achCraftHoe); break; + case E_ITEM_BREAD: a_Player.AwardAchievement(achMakeBread); break; + default: break; + } +} + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cSlotAreaAnvil: @@ -1393,7 +1418,7 @@ void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) { // Something has changed in the window, broadcast the entire window to all clients ASSERT(a_ItemGrid == &(m_Furnace->GetContents())); - + m_ParentWindow.BroadcastWholeWindow(); } @@ -1401,6 +1426,21 @@ void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +void cSlotAreaFurnace::HandleSmeltItem(const cItem & a_Result, cPlayer & a_Player) +{ + /** TODO 2014-05-12 xdot: Figure out when to call this method. */ + switch (a_Result.m_ItemType) + { + case E_ITEM_IRON: a_Player.AwardAchievement(achAcquireIron); break; + case E_ITEM_COOKED_FISH: a_Player.AwardAchievement(achCookFish); break; + default: break; + } +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cSlotAreaInventoryBase: diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index 4da6a672f..e297bcff7 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -254,6 +254,9 @@ protected: /// Retrieves the recipe for the specified player from the map, or creates one if not found cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player); + + /// Called after an item has been crafted to handle statistics e.t.c. + void HandleCraftItem(const cItem & a_Result, cPlayer & a_Player); } ; @@ -397,6 +400,9 @@ protected: // cItemGrid::cListener overrides: virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; + + /// Called after an item has been smelted to handle statistics e.t.c. + void HandleSmeltItem(const cItem & a_Result, cPlayer & a_Player); } ; -- cgit v1.2.3