1
0
cuberite-2a/src/Entities/Floater.cpp
Mattes D 01b8ed5295
Pulled the BlockID and BlockInfo headers from Globals.h. (#4591)
The BlockID.h file was removed from Globals.h and renamed to BlockType.h (main change)
The BlockInfo.h file was removed from Globals.h (main change)
The ENUM_BLOCK_ID and ENUM_ITEM_ID enum names were replaced with ENUM_BLOCK_TYPE and ENUM_ITEM_TYPE (cosmetics)
The various enums, such as eDimension, eDamageType and eExplosionSource were moved from BlockType.h to Defines.h, together with the helper functions for converting between them and strings (StringToDimension et al.) (minor)
Many inline functions were moved from headers to their respective cpp files, so that BlockType.h could be included only into the cpp file, rather than the header.
That broke our tests a bit, since they pick bits and pieces out of the main code and provide stubs for the rest; they had to be re-stubbed and re-verified.
eMonsterType values are no longer tied to E_ITEM_SPAWN_EGG_META_* values
2020-04-03 08:57:01 +02:00

200 lines
5.6 KiB
C++

#include "Globals.h"
#include "../BlockInfo.h"
#include "../BoundingBox.h"
#include "../Chunk.h"
#include "Floater.h"
#include "Player.h"
#include "../ClientHandle.h"
////////////////////////////////////////////////////////////////////////////////
// cFloaterEntityCollisionCallback
class cFloaterEntityCollisionCallback
{
public:
cFloaterEntityCollisionCallback(cFloater * a_Floater, const Vector3d & a_Pos, const Vector3d & a_NextPos) :
m_Floater(a_Floater),
m_Pos(a_Pos),
m_NextPos(a_NextPos),
m_MinCoeff(1),
m_HitEntity(nullptr)
{
}
bool operator () (cEntity & a_Entity)
{
if (!a_Entity.IsMob()) // Floaters can only pull mobs not other entities.
{
return false;
}
cBoundingBox EntBox(a_Entity.GetPosition(), a_Entity.GetWidth() / 2, a_Entity.GetHeight());
double LineCoeff;
eBlockFace Face;
EntBox.Expand(m_Floater->GetWidth() / 2, m_Floater->GetHeight() / 2, m_Floater->GetWidth() / 2);
if (!EntBox.CalcLineIntersection(m_Pos, m_NextPos, LineCoeff, Face))
{
// No intersection whatsoever
return false;
}
if (LineCoeff < m_MinCoeff)
{
// The entity is closer than anything we've stored so far, replace it as the potential victim
m_MinCoeff = LineCoeff;
m_HitEntity = &a_Entity;
}
// Don't break the enumeration, we want all the entities
return false;
}
/** Returns the nearest entity that was hit, after the enumeration has been completed */
cEntity * GetHitEntity(void) const { return m_HitEntity; }
/** Returns true if the callback has encountered a true hit */
bool HasHit(void) const { return (m_MinCoeff < 1); }
protected:
cFloater * m_Floater;
const Vector3d & m_Pos;
const Vector3d & m_NextPos;
double m_MinCoeff; // The coefficient of the nearest hit on the Pos line
// Although it's bad(tm) to store entity ptrs from a callback, we can afford it here, because the entire callback
// is processed inside the tick thread, so the entities won't be removed in between the calls and the final processing
cEntity * m_HitEntity; // The nearest hit entity
} ;
cFloater::cFloater(Vector3d a_Pos, Vector3d a_Speed, UInt32 a_PlayerID, int a_CountDownTime) :
super(etFloater, a_Pos, 0.2, 0.2),
m_BitePos(a_Pos),
m_CanPickupItem(false),
m_PickupCountDown(0),
m_CountDownTime(a_CountDownTime),
m_PlayerID(a_PlayerID),
m_AttachedMobID(cEntity::INVALID_ID)
{
SetSpeed(a_Speed);
}
void cFloater::SpawnOn(cClientHandle & a_Client)
{
a_Client.SendSpawnObject(*this, 90, static_cast<int>(m_PlayerID), 0, 0);
}
void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
auto & Random = GetRandomProvider();
HandlePhysics(a_Dt, a_Chunk);
if (IsBlockWater(m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT))
&& (m_World->GetBlockMeta(POSX_TOINT, POSY_TOINT, POSZ_TOINT) == 0))
{
if ((!m_CanPickupItem) && (m_AttachedMobID == cEntity::INVALID_ID)) // Check if you can't already pickup a fish and if the floater isn't attached to a mob.
{
if (m_CountDownTime <= 0)
{
m_BitePos = GetPosition();
m_World->BroadcastSoundEffect("entity.bobber.splash", GetPosition(), 1, 1);
SetPosY(GetPosY() - 1);
m_CanPickupItem = true;
m_PickupCountDown = 20;
m_CountDownTime = Random.RandInt(100, 900);
LOGD("Floater %i can be picked up", GetUniqueID());
}
else if (m_CountDownTime == 20) // Calculate the position where the particles should spawn and start producing them.
{
LOGD("Started producing particles for floater %i", GetUniqueID());
m_ParticlePos.Set(GetPosX() + Random.RandInt(-4, 4), GetPosY(), GetPosZ() + Random.RandInt(-4, 4));
m_World->BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15);
}
else if (m_CountDownTime < 20)
{
m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6);
m_World->BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15);
}
m_CountDownTime--;
if (m_World->GetHeight(POSX_TOINT, POSZ_TOINT) == POSY_TOINT)
{
if (m_World->IsWeatherWet() && Random.RandBool(0.25)) // 25% chance of an extra countdown when being rained on.
{
m_CountDownTime--;
}
}
else // if the floater is underground it has a 50% chance of not decreasing the countdown.
{
if (Random.RandBool())
{
m_CountDownTime++;
}
}
}
}
// Check water at the top of floater otherwise it floats into the air above the water
if (IsBlockWater(m_World->GetBlock(POSX_TOINT, FloorC(GetPosY() + GetHeight()), POSZ_TOINT)))
{
SetSpeedY(0.7);
}
if (CanPickup()) // Make sure the floater "loses its fish"
{
m_PickupCountDown--;
if (m_PickupCountDown == 0)
{
m_CanPickupItem = false;
LOGD("The fish is gone. Floater %i can not pick an item up.", GetUniqueID());
}
}
if ((GetSpeed().Length() > 4) && (m_AttachedMobID == cEntity::INVALID_ID))
{
cFloaterEntityCollisionCallback Callback(this, GetPosition(), GetPosition() + GetSpeed() / 20);
a_Chunk.ForEachEntity(Callback);
if (Callback.HasHit())
{
AttachTo(Callback.GetHitEntity());
Callback.GetHitEntity()->TakeDamage(*this); // TODO: the player attacked the mob not the floater.
m_AttachedMobID = Callback.GetHitEntity()->GetUniqueID();
}
}
if (!m_World->DoWithEntityByID(m_PlayerID, [](cEntity &) { return true; })) // The owner doesn't exist anymore. Destroy the floater entity.
{
Destroy();
}
if (m_AttachedMobID != cEntity::INVALID_ID)
{
if (!m_World->DoWithEntityByID(m_AttachedMobID, [](cEntity &) { return true; }))
{
// The mob the floater was attached to doesn't exist anymore.
m_AttachedMobID = cEntity::INVALID_ID;
}
}
SetSpeedX(GetSpeedX() * 0.95);
SetSpeedZ(GetSpeedZ() * 0.95);
BroadcastMovementUpdate();
}