From 071b7be3d4d08c337c01de7abca034e6c3746194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Imrich?= Date: Sat, 3 Apr 2021 19:45:20 +0200 Subject: Basic elytra flight (#5124) * Basic elytra flight Co-authored-by: 12xx12 <44411062+12xx12@users.noreply.github.com> Co-authored-by: Tiger Wang --- src/Entities/Player.cpp | 306 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 236 insertions(+), 70 deletions(-) (limited to 'src/Entities/Player.cpp') diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 9497d326b..a727429d4 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -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 & 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 & a_Client) : LoadFromDisk(World); m_LastGroundHeight = static_cast(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(m_BodyStance); +} + + + + + +bool cPlayer::IsStanding() const +{ + return std::holds_alternative(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,36 @@ 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) + if (a_ShouldCrouch && IsStanding()) { - // No change - return; + m_BodyStance = BodyStanceCrouching(*this); + cRoot::Get()->GetPluginManager()->CallHookPlayerCrouched(*this); } + else if (!a_ShouldCrouch && IsCrouched()) + { + m_BodyStance = BodyStanceStanding(*this); + } + + m_World->BroadcastEntityMetadata(*this); +} - if (a_IsCrouched) + + + + +void cPlayer::SetElytraFlight(const bool a_ShouldElytraFly) +{ + if (a_ShouldElytraFly && IsStanding() && !IsOnGround() && !IsInWater() && !IsRiding() && (GetEquippedChestplate().m_ItemType == E_ITEM_ELYTRA)) { - cRoot::Get()->GetPluginManager()->CallHookPlayerCrouched(*this); + m_BodyStance = BodyStanceGliding(*this); + } + else if (!a_ShouldElytraFly && IsElytraFlying()) + { + m_BodyStance = BodyStanceStanding(*this); } - m_IsCrouched = a_IsCrouched; m_World->BroadcastEntityMetadata(*this); } @@ -609,16 +692,37 @@ void cPlayer::SetCrouch(bool a_IsCrouched) -void cPlayer::SetSprint(bool a_IsSprinting) +void cPlayer::SetFlying(const bool a_ShouldFly) { - if (a_IsSprinting == m_IsSprinting) + 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(m_Stance + 1) - f; - bool flag = (m_Stance < f1); - return flag; + float f1 = static_cast(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(m_BodyStance); +} + + + + + +bool cPlayer::IsElytraFlying(void) const +{ + return std::holds_alternative(m_BodyStance); +} + + + + + +bool cPlayer::IsSprinting(void) const +{ + return std::holds_alternative(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(m_BodyStance).TicksElytraFlying; + const auto TotalFlew = TicksFlying + a_Dt; + const auto Periods = static_cast(TotalFlew / 1s); + TicksFlying = std::chrono::duration_cast(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 -- cgit v1.2.3