From fec64bb91c03c5e872a8f6fbc1a253f341373072 Mon Sep 17 00:00:00 2001 From: Persson-dev <66266021+Persson-dev@users.noreply.github.com> Date: Wed, 29 Dec 2021 20:28:41 +0100 Subject: Improved farmer AI & Fixed entity loading functions (#5351) * Allow villagers to pickup items * Add farmer villager harvesting * Use of auto keyword * Using for loop to check adjacent crops * Show particules when farmer harvest * Fix area comment * Move constants to header file * Removing unnecessary semicolon * Initialization of CropBlockType variable * Apply 12xx12 suggestion * Fixing area constant size * Refactor bounding box calculation, use vectors. * Add Api documentation * Update lua docs * Rework farmer ai * Fixing lua docs notes * Add missing capitalisation * Add villagers inventory save * Fixing loading entities from disk inconsistencies * Add farmer harvest animation * Fix beetroots grow state Co-authored-by: Alexander Harkness --- src/Entities/Pawn.cpp | 17 ++++++++ src/Entities/Pickup.cpp | 105 +++++++++++++++++++++++++++++++++++------------- src/Entities/Pickup.h | 2 +- src/Entities/Player.cpp | 2 - 4 files changed, 95 insertions(+), 31 deletions(-) (limited to 'src/Entities') diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp index a1c3a7610..e227dca6f 100644 --- a/src/Entities/Pawn.cpp +++ b/src/Entities/Pawn.cpp @@ -102,6 +102,23 @@ void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) return; } HandleFalling(); + + // Handle item pickup + if (m_Health > 0) + { + if (IsPlayer()) + { + m_World->CollectPickupsByEntity(*this); + } + else if (IsMob()) + { + cMonster & Mob = static_cast(*this); + if (Mob.CanPickUpLoot()) + { + m_World->CollectPickupsByEntity(*this); + } + } + } } diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index fd2cc3a4b..bbc3313da 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -7,10 +7,12 @@ #include "Pickup.h" #include "Player.h" +#include "../Mobs/Villager.h" #include "../ClientHandle.h" #include "../World.h" #include "../Server.h" #include "../Bindings/PluginManager.h" +#include "../Registries/Items.h" #include "../Root.h" #include "../Chunk.h" @@ -209,7 +211,7 @@ bool cPickup::DoTakeDamage(TakeDamageInfo & a_TDI) -bool cPickup::CollectedBy(cPlayer & a_Dest) +bool cPickup::CollectedBy(cEntity & a_Dest) { if (m_bCollected) { @@ -217,6 +219,12 @@ bool cPickup::CollectedBy(cPlayer & a_Dest) return false; // It's already collected! } + // This type of entity can't pickup items + if (!a_Dest.IsPawn()) + { + return false; + } + // Two seconds if player created the pickup (vomiting), half a second if anything else if (m_Timer < (m_bIsPlayerCreated ? std::chrono::seconds(2) : std::chrono::milliseconds(500))) { @@ -224,43 +232,84 @@ bool cPickup::CollectedBy(cPlayer & a_Dest) return false; // Not old enough } - // If the player is a spectator, he cannot collect anything - if (a_Dest.IsGameModeSpectator()) + // Checking for villagers + if (!a_Dest.IsPlayer() && a_Dest.IsMob()) { - return false; - } - if (cRoot::Get()->GetPluginManager()->CallHookCollectingPickup(a_Dest, *this)) - { - // LOG("Pickup %d cannot be collected by \"%s\", because a plugin has said no.", m_UniqueID, a_Dest->GetName().c_str()); - return false; - } + auto & Mob = static_cast(a_Dest); + if (Mob.GetMobType() == mtVillager) + { + // Villagers only pickup food + if (!ItemCategory::IsVillagerFood(m_Item.m_ItemType)) + { + return false; + } + + auto & Villager = static_cast(Mob); + int NumAdded = Villager.GetInventory().AddItem(m_Item); + if (NumAdded > 0) + { + m_Item.m_ItemCount -= NumAdded; + m_World->BroadcastCollectEntity(*this, a_Dest, static_cast(NumAdded)); + + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + m_World->BroadcastSoundEffect("entity.item.pickup", GetPosition(), 0.3f, (1.2f + (static_cast((GetUniqueID() * 23) % 32)) / 64)); + if (m_Item.m_ItemCount <= 0) + { + // All of the pickup has been collected, schedule the pickup for destroying + m_bCollected = true; + } + m_Timer = std::chrono::milliseconds(0); + return true; + } + // Pickup cannot be collected because the entity has not enough space + return false; + } - int NumAdded = a_Dest.GetInventory().AddItem(m_Item); - if (NumAdded > 0) + } + else if (a_Dest.IsPlayer()) { - // Check achievements - switch (m_Item.m_ItemType) + + auto & Player = static_cast(a_Dest); + + // If the player is a spectator, he cannot collect anything + if (Player.IsGameModeSpectator()) { - case E_BLOCK_LOG: a_Dest.AwardAchievement(CustomStatistic::AchMineWood); break; - case E_ITEM_LEATHER: a_Dest.AwardAchievement(CustomStatistic::AchKillCow); break; - case E_ITEM_DIAMOND: a_Dest.AwardAchievement(CustomStatistic::AchDiamonds); break; - case E_ITEM_BLAZE_ROD: a_Dest.AwardAchievement(CustomStatistic::AchBlazeRod); break; - default: break; + return false; } - m_Item.m_ItemCount -= NumAdded; - m_World->BroadcastCollectEntity(*this, a_Dest, static_cast(NumAdded)); + if (cRoot::Get()->GetPluginManager()->CallHookCollectingPickup(Player, *this)) + { + // LOG("Pickup %d cannot be collected by \"%s\", because a plugin has said no.", m_UniqueID, a_Dest->GetName().c_str()); + return false; + } - // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - m_World->BroadcastSoundEffect("entity.item.pickup", GetPosition(), 0.3f, (1.2f + (static_cast((GetUniqueID() * 23) % 32)) / 64)); - if (m_Item.m_ItemCount <= 0) + int NumAdded = Player.GetInventory().AddItem(m_Item); + if (NumAdded > 0) { - // All of the pickup has been collected, schedule the pickup for destroying - m_bCollected = true; + // Check achievements + switch (m_Item.m_ItemType) + { + case E_BLOCK_LOG: Player.AwardAchievement(CustomStatistic::AchMineWood); break; + case E_ITEM_LEATHER: Player.AwardAchievement(CustomStatistic::AchKillCow); break; + case E_ITEM_DIAMOND: Player.AwardAchievement(CustomStatistic::AchDiamonds); break; + case E_ITEM_BLAZE_ROD: Player.AwardAchievement(CustomStatistic::AchBlazeRod); break; + default: break; + } + + m_Item.m_ItemCount -= NumAdded; + m_World->BroadcastCollectEntity(*this, a_Dest, static_cast(NumAdded)); + + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + m_World->BroadcastSoundEffect("entity.item.pickup", GetPosition(), 0.3f, (1.2f + (static_cast((GetUniqueID() * 23) % 32)) / 64)); + if (m_Item.m_ItemCount <= 0) + { + // All of the pickup has been collected, schedule the pickup for destroying + m_bCollected = true; + } + m_Timer = std::chrono::milliseconds(0); + return true; } - m_Timer = std::chrono::milliseconds(0); - return true; } // LOG("Pickup %d cannot be collected by \"%s\", because there's no space in the inventory.", a_Dest->GetName().c_str(), m_UniqueID); diff --git a/src/Entities/Pickup.h b/src/Entities/Pickup.h index 73540a64e..c995055ff 100644 --- a/src/Entities/Pickup.h +++ b/src/Entities/Pickup.h @@ -33,7 +33,7 @@ public: // tolua_export virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - bool CollectedBy(cPlayer & a_Dest); // tolua_export + bool CollectedBy(cEntity & a_Dest); // tolua_export virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 1d3bad306..b0ab94874 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -3213,8 +3213,6 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (m_Health > 0) // make sure player is alive { - m_World->CollectPickupsByPlayer(*this); - if ((m_EatingFinishTick >= 0_tick) && (m_EatingFinishTick <= m_World->GetWorldAge())) { FinishEating(); -- cgit v1.2.3