Basic elytra flight (#5124)
* Basic elytra flight Co-authored-by: 12xx12 <44411062+12xx12@users.noreply.github.com> Co-authored-by: Tiger Wang <ziwei.tiger@outlook.com>
This commit is contained in:
@@ -66,38 +66,72 @@ const int cPlayer::EATING_TICKS = 30;
|
||||
|
||||
|
||||
|
||||
cPlayer::BodyStanceCrouching::BodyStanceCrouching(cPlayer & a_Player)
|
||||
{
|
||||
a_Player.SetSize(0.6f, 1.65f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPlayer::BodyStanceSleeping::BodyStanceSleeping(cPlayer & a_Player)
|
||||
{
|
||||
a_Player.SetSize(0.2f, 0.2f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPlayer::BodyStanceStanding::BodyStanceStanding(cPlayer & a_Player)
|
||||
{
|
||||
a_Player.SetSize(0.6f, 1.8f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPlayer::BodyStanceGliding::BodyStanceGliding(cPlayer & a_Player) :
|
||||
TicksElytraFlying(0)
|
||||
{
|
||||
a_Player.SetSize(0.6f, 0.6f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPlayer::cPlayer(const std::shared_ptr<cClientHandle> & a_Client) :
|
||||
Super(etPlayer, 0.6, 1.8),
|
||||
m_bVisible(true),
|
||||
m_BodyStance(BodyStanceStanding(*this)),
|
||||
m_FoodLevel(MAX_FOOD_LEVEL),
|
||||
m_FoodSaturationLevel(5.0),
|
||||
m_FoodTickTimer(0),
|
||||
m_FoodExhaustionLevel(0.0),
|
||||
m_Stance(0.0),
|
||||
m_Inventory(*this),
|
||||
m_EnderChestContents(9, 3),
|
||||
m_DefaultWorldPath(cRoot::Get()->GetDefaultWorld()->GetDataPath()),
|
||||
m_GameMode(eGameMode_NotSet),
|
||||
m_ClientHandle(a_Client),
|
||||
m_IsFrozen(false),
|
||||
m_NormalMaxSpeed(1.0),
|
||||
m_SprintingMaxSpeed(1.3),
|
||||
m_FlyingMaxSpeed(1.0),
|
||||
m_IsCrouched(false),
|
||||
m_IsSprinting(false),
|
||||
m_IsFlying(false),
|
||||
m_IsChargingBow(false),
|
||||
m_IsFishing(false),
|
||||
m_CanFly(false),
|
||||
m_IsFlightCapable(false),
|
||||
m_IsFlying(false),
|
||||
m_IsFrozen(false),
|
||||
m_IsTeleporting(false),
|
||||
m_IsVisible(true),
|
||||
m_EatingFinishTick(-1),
|
||||
m_LifetimeTotalXp(0),
|
||||
m_CurrentXp(0),
|
||||
m_IsChargingBow(false),
|
||||
m_BowCharge(0),
|
||||
m_FloaterID(cEntity::INVALID_ID),
|
||||
m_Team(nullptr),
|
||||
m_bIsInBed(false),
|
||||
m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL),
|
||||
m_bIsTeleporting(false),
|
||||
m_SkinParts(0),
|
||||
m_MainHand(mhRight)
|
||||
{
|
||||
@@ -114,26 +148,25 @@ cPlayer::cPlayer(const std::shared_ptr<cClientHandle> & a_Client) :
|
||||
LoadFromDisk(World);
|
||||
|
||||
m_LastGroundHeight = static_cast<float>(GetPosY());
|
||||
m_Stance = GetPosY() + 1.62;
|
||||
|
||||
if (m_GameMode == gmNotSet)
|
||||
{
|
||||
if (World->IsGameModeCreative())
|
||||
{
|
||||
m_CanFly = true;
|
||||
m_IsFlightCapable = true;
|
||||
}
|
||||
if (World->IsGameModeSpectator()) // Otherwise Player will fall out of the world on join
|
||||
{
|
||||
m_CanFly = true;
|
||||
m_IsFlightCapable = true;
|
||||
m_IsFlying = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_GameMode == gmSpectator) // If player is reconnecting to the server in spectator mode
|
||||
{
|
||||
m_CanFly = true;
|
||||
m_IsFlightCapable = true;
|
||||
m_IsFlying = true;
|
||||
m_bVisible = false;
|
||||
m_IsVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,6 +460,24 @@ void cPlayer::AddFoodExhaustion(double a_Exhaustion)
|
||||
|
||||
|
||||
|
||||
bool cPlayer::IsInBed(void) const
|
||||
{
|
||||
return std::holds_alternative<BodyStanceSleeping>(m_BodyStance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPlayer::IsStanding() const
|
||||
{
|
||||
return std::holds_alternative<BodyStanceStanding>(m_BodyStance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::TossItems(const cItems & a_Items)
|
||||
{
|
||||
if (IsGameModeSpectator()) // Players can't toss items in spectator
|
||||
@@ -445,6 +496,23 @@ void cPlayer::TossItems(const cItems & a_Items)
|
||||
|
||||
|
||||
|
||||
void cPlayer::SetIsInBed(const bool a_GoToBed)
|
||||
{
|
||||
if (a_GoToBed && IsStanding())
|
||||
{
|
||||
m_BodyStance = BodyStanceSleeping(*this);
|
||||
}
|
||||
else if (!a_GoToBed && IsInBed())
|
||||
{
|
||||
m_BodyStance = BodyStanceStanding(*this);
|
||||
GetWorld()->BroadcastEntityAnimation(*this, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::StartEating(void)
|
||||
{
|
||||
// Set the timer:
|
||||
@@ -525,11 +593,11 @@ const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const
|
||||
|
||||
double cPlayer::GetMaxSpeed(void) const
|
||||
{
|
||||
if (m_IsFlying)
|
||||
if (IsFlying())
|
||||
{
|
||||
return m_FlyingMaxSpeed;
|
||||
}
|
||||
else if (m_IsSprinting)
|
||||
else if (IsSprinting())
|
||||
{
|
||||
return m_SprintingMaxSpeed;
|
||||
}
|
||||
@@ -546,7 +614,7 @@ double cPlayer::GetMaxSpeed(void) const
|
||||
void cPlayer::SetNormalMaxSpeed(double a_Speed)
|
||||
{
|
||||
m_NormalMaxSpeed = a_Speed;
|
||||
if (!m_IsSprinting && !m_IsFlying && !m_IsFrozen)
|
||||
if (!IsSprinting() && !IsFlying() && !IsFrozen())
|
||||
{
|
||||
// If we are frozen, we do not send this yet. We send when unfreeze() is called
|
||||
m_ClientHandle->SendPlayerMaxSpeed();
|
||||
@@ -560,7 +628,7 @@ void cPlayer::SetNormalMaxSpeed(double a_Speed)
|
||||
void cPlayer::SetSprintingMaxSpeed(double a_Speed)
|
||||
{
|
||||
m_SprintingMaxSpeed = a_Speed;
|
||||
if (m_IsSprinting && !m_IsFlying && !m_IsFrozen)
|
||||
if (IsSprinting() && !m_IsFlying && !m_IsFrozen)
|
||||
{
|
||||
// If we are frozen, we do not send this yet. We send when unfreeze() is called
|
||||
m_ClientHandle->SendPlayerMaxSpeed();
|
||||
@@ -587,21 +655,18 @@ void cPlayer::SetFlyingMaxSpeed(double a_Speed)
|
||||
|
||||
|
||||
|
||||
void cPlayer::SetCrouch(bool a_IsCrouched)
|
||||
void cPlayer::SetCrouch(const bool a_ShouldCrouch)
|
||||
{
|
||||
// Set the crouch status, broadcast to all visible players
|
||||
if (a_IsCrouched == m_IsCrouched)
|
||||
{
|
||||
// No change
|
||||
return;
|
||||
}
|
||||
|
||||
if (a_IsCrouched)
|
||||
if (a_ShouldCrouch && IsStanding())
|
||||
{
|
||||
m_BodyStance = BodyStanceCrouching(*this);
|
||||
cRoot::Get()->GetPluginManager()->CallHookPlayerCrouched(*this);
|
||||
}
|
||||
else if (!a_ShouldCrouch && IsCrouched())
|
||||
{
|
||||
m_BodyStance = BodyStanceStanding(*this);
|
||||
}
|
||||
|
||||
m_IsCrouched = a_IsCrouched;
|
||||
m_World->BroadcastEntityMetadata(*this);
|
||||
}
|
||||
|
||||
@@ -609,16 +674,55 @@ void cPlayer::SetCrouch(bool a_IsCrouched)
|
||||
|
||||
|
||||
|
||||
void cPlayer::SetSprint(bool a_IsSprinting)
|
||||
void cPlayer::SetElytraFlight(const bool a_ShouldElytraFly)
|
||||
{
|
||||
if (a_IsSprinting == m_IsSprinting)
|
||||
if (a_ShouldElytraFly && IsStanding() && !IsOnGround() && !IsInWater() && !IsRiding() && (GetEquippedChestplate().m_ItemType == E_ITEM_ELYTRA))
|
||||
{
|
||||
m_BodyStance = BodyStanceGliding(*this);
|
||||
}
|
||||
else if (!a_ShouldElytraFly && IsElytraFlying())
|
||||
{
|
||||
m_BodyStance = BodyStanceStanding(*this);
|
||||
}
|
||||
|
||||
m_World->BroadcastEntityMetadata(*this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::SetFlying(const bool a_ShouldFly)
|
||||
{
|
||||
if (a_ShouldFly == m_IsFlying)
|
||||
{
|
||||
// No change
|
||||
return;
|
||||
}
|
||||
|
||||
m_IsSprinting = a_IsSprinting;
|
||||
m_ClientHandle->SendPlayerMaxSpeed();
|
||||
m_IsFlying = a_ShouldFly;
|
||||
if (!m_IsFrozen)
|
||||
{
|
||||
// If we are frozen, we do not send this yet. We send when unfreeze() is called
|
||||
m_ClientHandle->SendPlayerAbilities();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::SetSprint(const bool a_ShouldSprint)
|
||||
{
|
||||
if (a_ShouldSprint && IsStanding())
|
||||
{
|
||||
m_BodyStance = BodyStanceSprinting();
|
||||
}
|
||||
else if (!a_ShouldSprint && IsSprinting())
|
||||
{
|
||||
m_BodyStance = BodyStanceStanding(*this);
|
||||
}
|
||||
|
||||
m_World->BroadcastEntityMetadata(*this);
|
||||
}
|
||||
|
||||
|
||||
@@ -627,12 +731,12 @@ void cPlayer::SetSprint(bool a_IsSprinting)
|
||||
|
||||
void cPlayer::SetCanFly(bool a_CanFly)
|
||||
{
|
||||
if (a_CanFly == m_CanFly)
|
||||
if (a_CanFly == m_IsFlightCapable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_CanFly = a_CanFly;
|
||||
m_IsFlightCapable = a_CanFly;
|
||||
m_ClientHandle->SendPlayerAbilities();
|
||||
}
|
||||
|
||||
@@ -698,25 +802,6 @@ cWorld * cPlayer::GetBedWorld()
|
||||
|
||||
|
||||
|
||||
void cPlayer::SetFlying(bool a_IsFlying)
|
||||
{
|
||||
if (a_IsFlying == m_IsFlying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_IsFlying = a_IsFlying;
|
||||
if (!m_IsFrozen)
|
||||
{
|
||||
// If we are frozen, we do not send this yet. We send when unfreeze() is called
|
||||
m_ClientHandle->SendPlayerAbilities();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::NotifyNearbyWolves(cPawn * a_Opponent, bool a_IsPlayerInvolved)
|
||||
{
|
||||
ASSERT(a_Opponent != nullptr);
|
||||
@@ -750,7 +835,7 @@ void cPlayer::KilledBy(TakeDamageInfo & a_TDI)
|
||||
return; // not dead yet =]
|
||||
}
|
||||
|
||||
m_bVisible = false; // So new clients don't see the player
|
||||
m_IsVisible = false; // So new clients don't see the player
|
||||
|
||||
// Detach player from object / entity. If the player dies, the server still says
|
||||
// that the player is attached to the entity / object
|
||||
@@ -905,7 +990,7 @@ void cPlayer::Respawn(void)
|
||||
|
||||
double cPlayer::GetEyeHeight(void) const
|
||||
{
|
||||
return m_Stance;
|
||||
return GetEyePosition().y - GetPosY();
|
||||
}
|
||||
|
||||
|
||||
@@ -914,7 +999,22 @@ double cPlayer::GetEyeHeight(void) const
|
||||
|
||||
Vector3d cPlayer::GetEyePosition(void) const
|
||||
{
|
||||
return Vector3d( GetPosX(), m_Stance, GetPosZ());
|
||||
if (IsCrouched())
|
||||
{
|
||||
return GetPosition().addedY(1.54);
|
||||
}
|
||||
|
||||
if (IsElytraFlying())
|
||||
{
|
||||
return GetPosition().addedY(0.4);
|
||||
}
|
||||
|
||||
if (IsInBed())
|
||||
{
|
||||
return GetPosition().addedY(0.2);
|
||||
}
|
||||
|
||||
return GetPosition().addedY(1.6);
|
||||
}
|
||||
|
||||
|
||||
@@ -1314,7 +1414,7 @@ void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
|
||||
{
|
||||
SetPosition({a_PosX, a_PosY, a_PosZ});
|
||||
FreezeInternal(GetPosition(), false);
|
||||
m_bIsTeleporting = true;
|
||||
m_IsTeleporting = true;
|
||||
|
||||
m_ClientHandle->SendPlayerMoveLook();
|
||||
}
|
||||
@@ -1344,6 +1444,11 @@ bool cPlayer::IsFrozen()
|
||||
|
||||
void cPlayer::Unfreeze()
|
||||
{
|
||||
if (IsElytraFlying())
|
||||
{
|
||||
m_World->BroadcastEntityMetadata(*this);
|
||||
}
|
||||
|
||||
GetClientHandle()->SendPlayerAbilities();
|
||||
GetClientHandle()->SendPlayerMaxSpeed();
|
||||
|
||||
@@ -1449,14 +1554,14 @@ void cPlayer::ForceSetSpeed(const Vector3d & a_Speed)
|
||||
void cPlayer::SetVisible(bool a_bVisible)
|
||||
{
|
||||
// Need to Check if the player or other players are in gamemode spectator, but will break compatibility
|
||||
if (a_bVisible && !m_bVisible) // Make visible
|
||||
if (a_bVisible && !m_IsVisible) // Make visible
|
||||
{
|
||||
m_bVisible = true;
|
||||
m_IsVisible = true;
|
||||
m_World->BroadcastSpawnEntity(*this);
|
||||
}
|
||||
if (!a_bVisible && m_bVisible)
|
||||
if (!a_bVisible && m_IsVisible)
|
||||
{
|
||||
m_bVisible = false;
|
||||
m_IsVisible = false;
|
||||
m_World->BroadcastDestroyEntity(*this, m_ClientHandle.get()); // Destroy on all clients
|
||||
}
|
||||
}
|
||||
@@ -1809,7 +1914,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
|
||||
|
||||
if (m_GameMode == eGameMode_Creative)
|
||||
{
|
||||
m_CanFly = true;
|
||||
m_IsFlightCapable = true;
|
||||
}
|
||||
|
||||
m_Inventory.LoadFromJson(Root["inventory"]);
|
||||
@@ -2141,9 +2246,9 @@ bool cPlayer::IsClimbing(void) const
|
||||
|
||||
void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround)
|
||||
{
|
||||
if (m_bIsTeleporting)
|
||||
if (m_IsTeleporting)
|
||||
{
|
||||
m_bIsTeleporting = false;
|
||||
m_IsTeleporting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2599,16 +2704,19 @@ float cPlayer::GetLiquidHeightPercent(NIBBLETYPE a_Meta)
|
||||
|
||||
bool cPlayer::IsInsideWater()
|
||||
{
|
||||
BLOCKTYPE Block = m_World->GetBlock(Vector3d(GetPosX(), m_Stance, GetPosZ()).Floor());
|
||||
BLOCKTYPE Block;
|
||||
NIBBLETYPE Meta;
|
||||
m_World->GetBlockTypeMeta(GetEyePosition().Floor(), Block, Meta);
|
||||
|
||||
if ((Block != E_BLOCK_WATER) && (Block != E_BLOCK_STATIONARY_WATER))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
NIBBLETYPE Meta = GetWorld()->GetBlockMeta(Vector3d(GetPosX(), m_Stance, GetPosZ()).Floor());
|
||||
|
||||
const auto EyeHeight = GetEyeHeight();
|
||||
float f = GetLiquidHeightPercent(Meta) - 0.11111111f;
|
||||
float f1 = static_cast<float>(m_Stance + 1) - f;
|
||||
bool flag = (m_Stance < f1);
|
||||
return flag;
|
||||
float f1 = static_cast<float>(EyeHeight + 1) - f;
|
||||
return EyeHeight < f1;
|
||||
}
|
||||
|
||||
|
||||
@@ -2742,6 +2850,16 @@ void cPlayer::AddKnownItem(const cItem & a_Item)
|
||||
|
||||
|
||||
|
||||
void cPlayer::SetSize(const float a_Width, const float a_Height)
|
||||
{
|
||||
m_Width = a_Width;
|
||||
m_Height = a_Height;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::AddKnownRecipe(UInt32 a_RecipeId)
|
||||
{
|
||||
auto Response = m_KnownRecipes.insert(a_RecipeId);
|
||||
@@ -2916,6 +3034,33 @@ float cPlayer::GetEnchantmentBlastKnockbackReduction()
|
||||
|
||||
|
||||
|
||||
bool cPlayer::IsCrouched(void) const
|
||||
{
|
||||
return std::holds_alternative<BodyStanceCrouching>(m_BodyStance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPlayer::IsElytraFlying(void) const
|
||||
{
|
||||
return std::holds_alternative<BodyStanceGliding>(m_BodyStance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPlayer::IsSprinting(void) const
|
||||
{
|
||||
return std::holds_alternative<BodyStanceSprinting>(m_BodyStance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::OnAddToWorld(cWorld & a_World)
|
||||
{
|
||||
Super::OnAddToWorld(a_World);
|
||||
@@ -3023,7 +3168,7 @@ void cPlayer::OnRemoveFromWorld(cWorld & a_World)
|
||||
|
||||
void cPlayer::SpawnOn(cClientHandle & a_Client)
|
||||
{
|
||||
if (!m_bVisible || (m_ClientHandle.get() == (&a_Client)))
|
||||
if (!m_IsVisible || (m_ClientHandle.get() == (&a_Client)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -3110,6 +3255,27 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||
m_BowCharge += 1;
|
||||
}
|
||||
|
||||
if (IsElytraFlying())
|
||||
{
|
||||
// Damage elytra, once per second:
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
auto & TicksFlying = std::get<BodyStanceGliding>(m_BodyStance).TicksElytraFlying;
|
||||
const auto TotalFlew = TicksFlying + a_Dt;
|
||||
const auto Periods = static_cast<short>(TotalFlew / 1s);
|
||||
TicksFlying = std::chrono::duration_cast<cTickTime>(TotalFlew - Periods * 1s);
|
||||
|
||||
UseItem(cInventory::invArmorOffset + 1, Periods);
|
||||
}
|
||||
|
||||
// Check if flight is still possible:
|
||||
if (IsOnGround() || IsInWater() || IsRiding() || (GetEquippedChestplate().m_ItemType != E_ITEM_ELYTRA))
|
||||
{
|
||||
SetElytraFlight(false);
|
||||
}
|
||||
}
|
||||
|
||||
BroadcastMovementUpdate(m_ClientHandle.get());
|
||||
|
||||
if (m_Health > 0) // make sure player is alive
|
||||
|
||||
Reference in New Issue
Block a user