summaryrefslogblamecommitdiffstats
path: root/src/Items/ItemFishingRod.h
blob: 34f7c0875af81fc689baa918ed516d10a1cea08e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
 



                                
                    
                    


 


                                                                                
                   
                      
 
         
                                
                                                                        


         
                                           
         




                                                                   
                                  


                            

                                                                                        
                                                                       

                                                             
 
            
                         
                               
                       
                           
  
 



 
                                                        
 
                                   
 
         
                           
 

 

 
                               





                                                          
                        
         
                                                          



                                     

                                          





                                                            

                                                                                                                                     
                                                                  

                                                               




                                                                                  
                         
                                             
                         








                                                                                

                                                                                                                        
                                                               






























                                                                                                   
                                                                                        

                                                        


                                               
                         










                                                                                
 
                                                                                                    
         

                                                                                                                          



                                                                                                          
 







                                                                                                          
                                 
                                                                                            
                                                                                                             

                                                       
                                 
                                       
                                 
                                                                
                                                                                        

                                                        
                                 
                                       
                                 
                                                                                                    
                                                                                                             

                                                       
                                 
                                       
                                 

                                                                          
                                 





                                                                        
                                 

                                                                           

                                 
 
                                                                                           


















































                                                                                                                                   
                                                                                       


                    

                                                                                    
                         
                                                                                                
                         












                                                                                                 
                                                                                       
                 
 

                                                       
                                                                 
                                                                                                         








                                                                                                                
                                                                               


                                            
                                                                                                    
         
  

#pragma once

#include "../Entities/Floater.h"
#include "../Entities/Entity.h"
#include "../Item.h"
#include "../Root.h"





////////////////////////////////////////////////////////////////////////////////
// cFloaterCallback
class cFloaterCallback
{
  public:
	cFloaterCallback(void) :
		m_CanPickup(false), m_AttachedMobID(cEntity::INVALID_ID)
	{
	}

	bool operator()(cEntity & a_Entity)
	{
		auto & Floater = static_cast<cFloater &>(a_Entity);
		m_CanPickup = Floater.CanPickup();
		m_Pos = Floater.GetPosition();
		m_BitePos = Floater.GetBitePos();
		m_AttachedMobID = Floater.GetAttachedMobID();
		Floater.Destroy();
		return true;
	}

	bool CanPickup(void) const { return m_CanPickup; }
	bool IsAttached(void) const { return (m_AttachedMobID != cEntity::INVALID_ID); }
	UInt32 GetAttachedMobID(void) const { return m_AttachedMobID; }
	Vector3d GetPos(void) const { return m_Pos; }
	Vector3d GetBitePos(void) const { return m_BitePos; }

  protected:
	bool m_CanPickup;
	UInt32 m_AttachedMobID;
	Vector3d m_Pos;
	Vector3d m_BitePos;
};





class cItemFishingRodHandler final : public cItemHandler
{
	using Super = cItemHandler;

  public:
	using Super::Super;





	virtual bool OnItemUse(
		cWorld * a_World,
		cPlayer * a_Player,
		cBlockPluginInterface & a_PluginInterface,
		const cItem & a_HeldItem,
		const Vector3i a_ClickedBlockPos,
		eBlockFace a_ClickedBlockFace
	) const override
	{
		if (a_ClickedBlockFace != BLOCK_FACE_NONE)
		{
			return false;
		}

		if (a_Player->IsFishing())
		{
			ReelIn(*a_World, *a_Player);
		}
		else
		{
			// Cast a hook:
			auto & Random = GetRandomProvider();
			auto CountDownTime = Random.RandInt(100, 900) -
				static_cast<int>(a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLure) * 100);
			auto Floater = std::make_unique<cFloater>(
				a_Player->GetEyePosition(),
				a_Player->GetLookVector() * 15,
				a_Player->GetUniqueID(),
				CountDownTime
			);
			auto FloaterPtr = Floater.get();
			if (!FloaterPtr->Initialize(std::move(Floater), *a_World))
			{
				return false;
			}
			a_Player->SetIsFishing(true, FloaterPtr->GetUniqueID());
		}
		return true;
	}





	/** Reels back the fishing line, reeling any attached mob, or creating fished loot, or just breaking the fishing
	 * rod. */
	void ReelIn(cWorld & a_World, cPlayer & a_Player) const
	{
		cFloaterCallback FloaterInfo;
		a_World.DoWithEntityByID(a_Player.GetFloaterID(), FloaterInfo);
		a_Player.SetIsFishing(false);

		// If attached to an entity, reel it in:
		if (FloaterInfo.IsAttached())
		{
			ReelInEntity(a_World, a_Player, FloaterInfo.GetAttachedMobID());
			return;
		}

		// If loot can be caught, get it:
		if (FloaterInfo.CanPickup())
		{
			ReelInLoot(a_World, a_Player, FloaterInfo.GetBitePos());
			return;
		}

		// Empty fishing rod, just damage it:
		auto BlockType = a_World.GetBlock(FloaterInfo.GetPos() - Vector3d(0, 0.1, 0));
		if ((BlockType != E_BLOCK_AIR) && !IsBlockWater(BlockType))
		{
			a_Player.UseEquippedItem(2);
		}
	}




	/** Reels back the entity, specified by the ID, and damages the fishing rod accordingly. */
	void ReelInEntity(cWorld & a_World, cPlayer & a_Player, UInt32 a_EntityID) const
	{
		auto PlayerPos = a_Player.GetPosition();
		a_World.DoWithEntityByID(
			a_EntityID,
			[=](cEntity & a_Entity)
			{
				auto Speed = PlayerPos - a_Entity.GetPosition();
				a_Entity.AddSpeed(Speed);
				return true;
			}
		);
		a_Player.UseEquippedItem(5);
	}





	void ReelInLoot(cWorld & a_World, cPlayer & a_Player, const Vector3d a_FloaterBitePos) const
	{
		auto LotSLevel =
			std::min(a_Player.GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLuckOfTheSea), 3u);

		// Chances for getting an item from the category for each level of Luck of the Sea (0 - 3)
		const int TreasureChances[] = {50, 71, 92, 113};  // 5% | 7.1% | 9.2% | 11.3%
		const int JunkChances[] = {100, 81, 61, 42};  // 10% | 8.1% | 6.1% | 4.2%

		cItems Drops;
		auto & Random = GetRandomProvider();
		int ItemCategory = Random.RandInt(999);
		if (ItemCategory < TreasureChances[LotSLevel])
		{
			switch (Random.RandInt(5))  // Each piece of treasure has an equal chance of 1 / 6
			{
				case 0:
				{
					cItem Bow(E_ITEM_BOW, 1, Random.RandInt<short>(50));
					Bow.EnchantByXPLevels(Random.RandInt(22U, 30U), GetRandomProvider());
					Drops.Add(Bow);
					break;
				}
				case 1:
				{
					cItem Book(E_ITEM_BOOK);
					Book.EnchantByXPLevels(30, GetRandomProvider());
					Drops.Add(Book);
					break;
				}
				case 2:
				{
					cItem Rod(E_ITEM_FISHING_ROD, 1, Random.RandInt<short>(50));
					Rod.EnchantByXPLevels(Random.RandInt(22U, 30U), GetRandomProvider());
					Drops.Add(Rod);
					break;
				}
				case 3:
				{
					Drops.Add(cItem(E_ITEM_NAME_TAG));
					break;
				}
				case 4:
				{
					Drops.Add(cItem(E_ITEM_SADDLE));
					break;
				}
				case 5:
				{
					Drops.Add(cItem(E_BLOCK_LILY_PAD));
					break;
				}
			}

			a_Player.GetStatistics().Custom[CustomStatistic::TreasureFished]++;
		}
		else if (ItemCategory < JunkChances[LotSLevel])
		{
			int Junk = Random.RandInt(82);
			if (Junk < 10)  // 10 / 83 chance of spawning a bowl
			{
				Drops.Add(cItem(E_ITEM_BOWL));
			}
			else if (Junk < 12)  // 2 / 83 chance of spawning a fishing rod
			{
				// Fishing Rods caught from the Junk category will be 10% .. 100% damaged, and always unenchanted.
				Drops.Add(cItem(E_ITEM_FISHING_ROD, 1, Random.RandInt<short>(7, 65)));
			}
			else if (Junk < 22)  // 10 / 83 chance of spawning leather
			{
				Drops.Add(cItem(E_ITEM_LEATHER));
			}
			else if (Junk < 32)  // 10 / 83 chance of spawning leather boots
			{
				// Leather boots caught from the Junk category will be 10% .. 100% damaged, and always unenchanted.
				Drops.Add(cItem(E_ITEM_LEATHER_BOOTS, 1, Random.RandInt<short>(7, 66)));
			}
			else if (Junk < 42)  // 10 / 83 chance of spawning rotten flesh
			{
				Drops.Add(cItem(E_ITEM_ROTTEN_FLESH));
			}
			else if (Junk < 47)  // 5 / 83 chance of spawning a stick
			{
				Drops.Add(cItem(E_ITEM_STICK));
			}
			else if (Junk < 52)  // 5 / 83 chance of spawning string
			{
				Drops.Add(cItem(E_ITEM_STRING));
			}
			else if (Junk < 62)  // 10 / 83 chance of spawning a water bottle
			{
				Drops.Add(cItem(E_ITEM_POTION));
			}
			else if (Junk < 72)  // 10 / 83 chance of spawning a bone
			{
				Drops.Add(cItem(E_ITEM_BONE));
			}
			else if (Junk < 73)  // 1 / 83 chance of spawning an ink sac
			{
				Drops.Add(cItem(E_ITEM_DYE));
			}
			else  // 10 / 83 chance of spawning a tripwire hook
			{
				Drops.Add(cItem(E_BLOCK_TRIPWIRE_HOOK));
			}

			a_Player.GetStatistics().Custom[CustomStatistic::JunkFished]++;
		}
		else
		{
			int FishType = Random.RandInt(99);
			if (FishType <= 1)  // Clownfish has a 2% chance of spawning
			{
				Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_CLOWNFISH));
			}
			else if (FishType <= 12)  // Pufferfish has a 13% chance of spawning
			{
				Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_PUFFERFISH));
			}
			else if (FishType <= 24)  // Raw salmon has a 25% chance of spawning
			{
				Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_SALMON));
			}
			else  // Raw fish has a 60% chance of spawning
			{
				Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_FISH));
			}

			a_Player.GetStatistics().Custom[CustomStatistic::FishCaught]++;
		}

		auto Experience = Random.RandInt(1, 6);

		// Check with plugins if this loot is acceptable:
		if (cRoot::Get()->GetPluginManager()->CallHookPlayerFishing(a_Player, Drops, Experience))
		{
			return;
		}

		// Spawn the loot and the experience orb:
		auto FloaterPos = a_FloaterBitePos.addedY(0.5);
		const float FISH_SPEED_MULT = 2.25f;
		Vector3d FlyDirection = (a_Player.GetEyePosition() - FloaterPos).addedY(1.0f) * FISH_SPEED_MULT;
		a_World.SpawnItemPickups(Drops, FloaterPos, FlyDirection);
		a_World.SpawnExperienceOrb(a_Player.GetPosition(), Experience);
		a_Player.UseEquippedItem(1);

		// Notify plugins
		cRoot::Get()->GetPluginManager()->CallHookPlayerFished(a_Player, Drops, Experience);
	}
};