Adding wolf breading and moving breeding functionality to cMonster (#4951)
* added wolf breading * mpoved breeding to monster * checkstyle * fixed my IDE "helping" * removed magic number and fixed faster aging * added flooring to age manipulation * fixed copiler error * fixed typo * moved tps to Defines.h * removed the TPS constant from the lua API exposure * added inline constexpr added explanation * fixed broken build * "fixed" build Co-authored-by: 12xx12 <12xx12100@gmail.com>
This commit is contained in:
+201
-4
@@ -14,6 +14,8 @@
|
||||
#include "../MonsterConfig.h"
|
||||
#include "../BoundingBox.h"
|
||||
|
||||
#include "Items/ItemSpawnEgg.h"
|
||||
|
||||
#include "../Chunk.h"
|
||||
#include "../FastRandom.h"
|
||||
|
||||
@@ -22,8 +24,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Map for eType <-> string
|
||||
Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType()
|
||||
The strings need to be lowercase (for more efficient comparisons in StringToMobType())
|
||||
@@ -109,12 +109,16 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
|
||||
, m_BurnsInDaylight(false)
|
||||
, m_RelativeWalkSpeed(1)
|
||||
, m_Age(1)
|
||||
, m_AgingTimer(20 * 60 * 20) // about 20 minutes
|
||||
, m_AgingTimer(TPS * 60 * 20) // about 20 minutes
|
||||
, m_WasLastTargetAPlayer(false)
|
||||
, m_LeashedTo(nullptr)
|
||||
, m_LeashToPos(nullptr)
|
||||
, m_IsLeashActionJustDone(false)
|
||||
, m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive)
|
||||
, m_LovePartner(nullptr)
|
||||
, m_LoveTimer(0)
|
||||
, m_LoveCooldown(0)
|
||||
, m_MatingTimer(0)
|
||||
, m_Target(nullptr)
|
||||
{
|
||||
if (!a_ConfigName.empty())
|
||||
@@ -163,6 +167,10 @@ void cMonster::OnRemoveFromWorld(cWorld & a_World)
|
||||
void cMonster::Destroyed()
|
||||
{
|
||||
SetTarget(nullptr); // Tell them we're no longer targeting them.
|
||||
if (m_LovePartner != nullptr)
|
||||
{
|
||||
m_LovePartner->ResetLoveMode();
|
||||
}
|
||||
Super::Destroyed();
|
||||
}
|
||||
|
||||
@@ -896,7 +904,7 @@ void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||
|
||||
void cMonster::ResetAttackCooldown()
|
||||
{
|
||||
m_AttackCoolDownTicksLeft = static_cast<int>(20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every second
|
||||
m_AttackCoolDownTicksLeft = static_cast<int>(TPS * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every second
|
||||
}
|
||||
|
||||
|
||||
@@ -1248,6 +1256,195 @@ std::unique_ptr<cMonster> cMonster::NewMonsterFromType(eMonsterType a_MobType)
|
||||
|
||||
|
||||
|
||||
void cMonster::EngageLoveMode(cMonster *a_Partner)
|
||||
{
|
||||
m_LovePartner = a_Partner;
|
||||
m_MatingTimer = 50; // about 3 seconds of mating
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cMonster::ResetLoveMode()
|
||||
{
|
||||
m_LovePartner = nullptr;
|
||||
m_LoveTimer = 0;
|
||||
m_MatingTimer = 0;
|
||||
m_LoveCooldown = TPS * 60 * 5; // 5 minutes
|
||||
|
||||
// when an animal is in love mode, the client only stops sending the hearts if we let them know it's in cooldown, which is done with the "age" metadata
|
||||
m_World->BroadcastEntityMetadata(*this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cMonster::LoveTick(void)
|
||||
{
|
||||
// if we have a partner, mate
|
||||
if (m_LovePartner != nullptr)
|
||||
{
|
||||
|
||||
if (m_MatingTimer > 0)
|
||||
{
|
||||
// If we should still mate, keep bumping into them until baby is made
|
||||
Vector3d Pos = m_LovePartner->GetPosition();
|
||||
MoveToPosition(Pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mating finished. Spawn baby
|
||||
Vector3f Pos = (GetPosition() + m_LovePartner->GetPosition()) * 0.5;
|
||||
UInt32 BabyID = m_World->SpawnMob(Pos.x, Pos.y, Pos.z, GetMobType(), true);
|
||||
|
||||
cMonster * Baby = nullptr;
|
||||
|
||||
m_World->DoWithEntityByID(BabyID, [&](cEntity & a_Entity)
|
||||
{
|
||||
Baby = static_cast<cMonster *>(&a_Entity);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (Baby != nullptr)
|
||||
{
|
||||
Baby->InheritFromParents(this, m_LovePartner);
|
||||
}
|
||||
|
||||
m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, GetRandomProvider().RandInt(1, 6));
|
||||
|
||||
m_World->DoWithPlayerByUUID(m_Feeder, [&] (cPlayer & a_Player)
|
||||
{
|
||||
a_Player.GetStatManager().AddValue(Statistic::AnimalsBred);
|
||||
if (GetMobType() == eMonsterType::mtCow)
|
||||
{
|
||||
a_Player.AwardAchievement(Statistic::AchBreedCow);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
m_LovePartner->ResetLoveMode();
|
||||
ResetLoveMode();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have no partner, so we just chase the player if they have our breeding item
|
||||
cItems FollowedItems;
|
||||
GetFollowedItems(FollowedItems);
|
||||
if (FollowedItems.Size() > 0)
|
||||
{
|
||||
m_World->DoWithNearestPlayer(GetPosition(), static_cast<float>(m_SightDistance), [&](cPlayer & a_Player) -> bool
|
||||
{
|
||||
const cItem & EquippedItem = a_Player.GetEquippedItem();
|
||||
if (FollowedItems.ContainsType(EquippedItem))
|
||||
{
|
||||
Vector3d PlayerPos = a_Player.GetPosition();
|
||||
MoveToPosition(PlayerPos);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// If we are in love mode but we have no partner, search for a partner neabry
|
||||
if (m_LoveTimer > 0)
|
||||
{
|
||||
if (m_LovePartner == nullptr)
|
||||
{
|
||||
m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 8, 8), [=](cEntity & a_Entity)
|
||||
{
|
||||
// If the entity is not a monster, don't breed with it
|
||||
// Also, do not self-breed
|
||||
if ((a_Entity.GetEntityType() != etMonster) || (&a_Entity == this))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto & Me = static_cast<cMonster &>(*this);
|
||||
auto & PotentialPartner = static_cast<cMonster &>(a_Entity);
|
||||
|
||||
// If the potential partner is not of the same species, don't breed with it
|
||||
if (PotentialPartner.GetMobType() != Me.GetMobType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the potential partner is not in love
|
||||
// Or they already have a mate, do not breed with them
|
||||
if ((!PotentialPartner.IsInLove()) || (PotentialPartner.GetPartner() != nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// All conditions met, let's breed!
|
||||
PotentialPartner.EngageLoveMode(&Me);
|
||||
Me.EngageLoveMode(&PotentialPartner);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
m_LoveTimer--;
|
||||
}
|
||||
if (m_MatingTimer > 0)
|
||||
{
|
||||
m_MatingTimer--;
|
||||
}
|
||||
if (m_LoveCooldown > 0)
|
||||
{
|
||||
m_LoveCooldown--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cMonster::RightClickFeed(cPlayer & a_Player)
|
||||
{
|
||||
|
||||
const cItem & EquippedItem = a_Player.GetEquippedItem();
|
||||
|
||||
// If a player holding breeding items right-clicked me, go into love mode
|
||||
if ((m_LoveCooldown == 0) && !IsInLove() && !IsBaby())
|
||||
{
|
||||
cItems Items;
|
||||
GetBreedingItems(Items);
|
||||
if (Items.ContainsType(EquippedItem.m_ItemType))
|
||||
{
|
||||
if (!a_Player.IsGameModeCreative())
|
||||
{
|
||||
a_Player.GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
m_LoveTimer = TPS * 30; // half a minute
|
||||
m_World->BroadcastEntityStatus(*this, esMobInLove);
|
||||
}
|
||||
}
|
||||
// If a player holding my spawn egg right-clicked me, spawn a new baby
|
||||
if (EquippedItem.m_ItemType == E_ITEM_SPAWN_EGG)
|
||||
{
|
||||
eMonsterType MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(EquippedItem.m_ItemDamage);
|
||||
if (
|
||||
(MonsterType == m_MobType) &&
|
||||
(m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), m_MobType, true) != cEntity::INVALID_ID) // Spawning succeeded
|
||||
)
|
||||
{
|
||||
if (!a_Player.IsGameModeCreative())
|
||||
{
|
||||
// The mob was spawned, "use" the item:
|
||||
a_Player.GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Stores feeder UUID for statistic tracking
|
||||
m_Feeder = a_Player.GetUUID();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth)
|
||||
{
|
||||
auto Count = GetRandomProvider().RandInt<unsigned int>(a_Min, a_Max);
|
||||
|
||||
Reference in New Issue
Block a user