Fix #5118
This commit is contained in:
parent
925f960ea2
commit
be2bf999c2
@ -1715,17 +1715,6 @@ void cEntity::SetIsTicking(bool a_IsTicking)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cEntity::DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
|
|
||||||
{
|
|
||||||
m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ);
|
|
||||||
|
|
||||||
WrapSpeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cEntity::HandleAir(void)
|
void cEntity::HandleAir(void)
|
||||||
{
|
{
|
||||||
// Ref.: https://minecraft.gamepedia.com/Chunk_format
|
// Ref.: https://minecraft.gamepedia.com/Chunk_format
|
||||||
@ -2095,7 +2084,8 @@ void cEntity::SetRoll(double a_Roll)
|
|||||||
|
|
||||||
void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
|
void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
|
||||||
{
|
{
|
||||||
DoSetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ);
|
m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ);
|
||||||
|
WrapSpeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2140,7 +2130,7 @@ void cEntity::SetWidth(double a_Width)
|
|||||||
|
|
||||||
void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ)
|
void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ)
|
||||||
{
|
{
|
||||||
DoSetSpeed(m_Speed.x + a_AddSpeedX, m_Speed.y + a_AddSpeedY, m_Speed.z + a_AddSpeedZ);
|
SetSpeed(m_Speed.x + a_AddSpeedX, m_Speed.y + a_AddSpeedY, m_Speed.z + a_AddSpeedZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2280,34 +2270,3 @@ void cEntity::BroadcastLeashedMobs()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
float cEntity::GetExplosionExposureRate(Vector3d a_ExplosionPosition, float a_ExlosionPower)
|
|
||||||
{
|
|
||||||
double EntitySize = m_Width * m_Width * m_Height;
|
|
||||||
if (EntitySize <= 0)
|
|
||||||
{
|
|
||||||
// Handle entity with invalid size
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto EntityBox = GetBoundingBox();
|
|
||||||
cBoundingBox ExplosionBox(a_ExplosionPosition, a_ExlosionPower * 2.0);
|
|
||||||
cBoundingBox IntersectionBox(EntityBox);
|
|
||||||
|
|
||||||
bool Overlap = EntityBox.Intersect(ExplosionBox, IntersectionBox);
|
|
||||||
if (Overlap)
|
|
||||||
{
|
|
||||||
Vector3d Diff = IntersectionBox.GetMax() - IntersectionBox.GetMin();
|
|
||||||
double OverlapSize = Diff.x * Diff.y * Diff.z;
|
|
||||||
|
|
||||||
return static_cast<float>(OverlapSize / EntitySize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -591,12 +591,6 @@ public:
|
|||||||
/** Returs whether the entity has any mob leashed to it. */
|
/** Returs whether the entity has any mob leashed to it. */
|
||||||
bool HasAnyMobLeashed() const { return m_LeashedMobs.size() > 0; }
|
bool HasAnyMobLeashed() const { return m_LeashedMobs.size() > 0; }
|
||||||
|
|
||||||
/** a lightweight calculation approach to get explosion exposure rate
|
|
||||||
@param a_ExplosionPosition explosion position
|
|
||||||
@param a_ExlosionPower explosion power
|
|
||||||
@return exposure rate */
|
|
||||||
virtual float GetExplosionExposureRate(Vector3d a_ExplosionPosition, float a_ExlosionPower);
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
@ -705,11 +699,6 @@ protected:
|
|||||||
/** The number of ticks this entity has been alive for */
|
/** The number of ticks this entity has been alive for */
|
||||||
long int m_TicksAlive;
|
long int m_TicksAlive;
|
||||||
|
|
||||||
|
|
||||||
/** Does the actual speed-setting. The default implementation just sets the member variable value;
|
|
||||||
overrides can provide further processing, such as forcing players to move at the given speed. */
|
|
||||||
virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ);
|
|
||||||
|
|
||||||
/** Handles the moving of this entity between worlds.
|
/** Handles the moving of this entity between worlds.
|
||||||
Should handle degenerate cases such as moving to the same world. */
|
Should handle degenerate cases such as moving to the same world. */
|
||||||
void DoMoveToWorld(const sWorldChangeInfo & a_WorldChangeInfo);
|
void DoMoveToWorld(const sWorldChangeInfo & a_WorldChangeInfo);
|
||||||
|
@ -216,6 +216,9 @@ void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
m_DetectorRailPosition = Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT);
|
m_DetectorRailPosition = Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enforce speed limit:
|
||||||
|
m_Speed.Clamp(MAX_SPEED_NEGATIVE, MAX_SPEED);
|
||||||
|
|
||||||
// Broadcast positioning changes to client
|
// Broadcast positioning changes to client
|
||||||
BroadcastMovementUpdate();
|
BroadcastMovementUpdate();
|
||||||
}
|
}
|
||||||
@ -1268,40 +1271,6 @@ void cMinecart::ApplyAcceleration(Vector3d a_ForwardDirection, double a_Accelera
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cMinecart::DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
|
|
||||||
{
|
|
||||||
if (a_SpeedX > MAX_SPEED)
|
|
||||||
{
|
|
||||||
a_SpeedX = MAX_SPEED;
|
|
||||||
}
|
|
||||||
else if (a_SpeedX < MAX_SPEED_NEGATIVE)
|
|
||||||
{
|
|
||||||
a_SpeedX = MAX_SPEED_NEGATIVE;
|
|
||||||
}
|
|
||||||
if (a_SpeedY > MAX_SPEED)
|
|
||||||
{
|
|
||||||
a_SpeedY = MAX_SPEED;
|
|
||||||
}
|
|
||||||
else if (a_SpeedY < MAX_SPEED_NEGATIVE)
|
|
||||||
{
|
|
||||||
a_SpeedY = MAX_SPEED_NEGATIVE;
|
|
||||||
}
|
|
||||||
if (a_SpeedZ > MAX_SPEED)
|
|
||||||
{
|
|
||||||
a_SpeedZ = MAX_SPEED;
|
|
||||||
}
|
|
||||||
else if (a_SpeedZ < MAX_SPEED_NEGATIVE)
|
|
||||||
{
|
|
||||||
a_SpeedZ = MAX_SPEED_NEGATIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
Super::DoSetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// cRideableMinecart:
|
// cRideableMinecart:
|
||||||
|
|
||||||
|
@ -56,9 +56,6 @@ protected:
|
|||||||
/** Applies an acceleration to the minecart parallel to a_ForwardDirection but without allowing backward speed. */
|
/** Applies an acceleration to the minecart parallel to a_ForwardDirection but without allowing backward speed. */
|
||||||
void ApplyAcceleration(Vector3d a_ForwardDirection, double a_Acceleration);
|
void ApplyAcceleration(Vector3d a_ForwardDirection, double a_Acceleration);
|
||||||
|
|
||||||
// Overwrite to enforce speed limit
|
|
||||||
virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) override;
|
|
||||||
|
|
||||||
cMinecart(ePayload a_Payload, Vector3d a_Pos);
|
cMinecart(ePayload a_Payload, Vector3d a_Pos);
|
||||||
|
|
||||||
/** Handles physics on normal rails
|
/** Handles physics on normal rails
|
||||||
|
@ -179,47 +179,6 @@ cPlayer::cPlayer(const cClientHandlePtr & a_Client) :
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::AddKnownItem(const cItem & a_Item)
|
|
||||||
{
|
|
||||||
if (a_Item.m_ItemType < 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Response = m_KnownItems.insert(a_Item.CopyOne());
|
|
||||||
if (!Response.second)
|
|
||||||
{
|
|
||||||
// The item was already known, bail out:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the recipes that got unlocked by this newly-known item:
|
|
||||||
auto Recipes = cRoot::Get()->GetCraftingRecipes()->FindNewRecipesForItem(a_Item, m_KnownItems);
|
|
||||||
for (const auto & RecipeId : Recipes)
|
|
||||||
{
|
|
||||||
AddKnownRecipe(RecipeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::AddKnownRecipe(UInt32 a_RecipeId)
|
|
||||||
{
|
|
||||||
auto Response = m_KnownRecipes.insert(a_RecipeId);
|
|
||||||
if (!Response.second)
|
|
||||||
{
|
|
||||||
// The recipe was already known, bail out:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_ClientHandle->SendUnlockRecipe(a_RecipeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cPlayer::~cPlayer(void)
|
cPlayer::~cPlayer(void)
|
||||||
{
|
{
|
||||||
LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), static_cast<void *>(this), GetUniqueID());
|
LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), static_cast<void *>(this), GetUniqueID());
|
||||||
@ -238,302 +197,6 @@ cPlayer::~cPlayer(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::OnAddToWorld(cWorld & a_World)
|
|
||||||
{
|
|
||||||
Super::OnAddToWorld(a_World);
|
|
||||||
|
|
||||||
// Update world name tracking:
|
|
||||||
m_CurrentWorldName = m_World->GetName();
|
|
||||||
|
|
||||||
// Fix to stop the player falling through the world, until we get serversided collision detection:
|
|
||||||
FreezeInternal(GetPosition(), false);
|
|
||||||
|
|
||||||
// Set capabilities based on new world:
|
|
||||||
SetCapabilities();
|
|
||||||
|
|
||||||
// Send contents of the inventory window:
|
|
||||||
m_ClientHandle->SendWholeInventory(*m_CurrentWindow);
|
|
||||||
|
|
||||||
// Send health (the respawn packet, which understandably resets health, is also used for world travel...):
|
|
||||||
m_ClientHandle->SendHealth();
|
|
||||||
|
|
||||||
// Send experience, similar story with the respawn packet:
|
|
||||||
m_ClientHandle->SendExperience();
|
|
||||||
|
|
||||||
// Send hotbar active slot (also reset by respawn):
|
|
||||||
m_ClientHandle->SendHeldItemChange(m_Inventory.GetEquippedSlotNum());
|
|
||||||
|
|
||||||
// Update player team:
|
|
||||||
UpdateTeam();
|
|
||||||
|
|
||||||
// Send scoreboard data:
|
|
||||||
m_World->GetScoreBoard().SendTo(*m_ClientHandle);
|
|
||||||
|
|
||||||
// Update the view distance:
|
|
||||||
m_ClientHandle->SetViewDistance(m_ClientHandle->GetRequestedViewDistance());
|
|
||||||
|
|
||||||
// Send current weather of target world:
|
|
||||||
m_ClientHandle->SendWeather(a_World.GetWeather());
|
|
||||||
|
|
||||||
// Send time:
|
|
||||||
m_ClientHandle->SendTimeUpdate(a_World.GetWorldAge(), a_World.GetTimeOfDay(), a_World.IsDaylightCycleEnabled());
|
|
||||||
|
|
||||||
// Finally, deliver the notification hook:
|
|
||||||
cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::OnRemoveFromWorld(cWorld & a_World)
|
|
||||||
{
|
|
||||||
Super::OnRemoveFromWorld(a_World);
|
|
||||||
|
|
||||||
// Remove any references to this player pointer by windows in the old world:
|
|
||||||
CloseWindow(false);
|
|
||||||
|
|
||||||
// Remove the client handle from the world:
|
|
||||||
m_World->RemoveClientFromChunks(m_ClientHandle.get());
|
|
||||||
|
|
||||||
if (m_ClientHandle->IsDestroyed()) // Note: checking IsWorldChangeScheduled not appropriate here since we can disconnecting while having a scheduled warp
|
|
||||||
{
|
|
||||||
// Disconnecting, do the necessary cleanup.
|
|
||||||
// This isn't in the destructor to avoid crashing accessing destroyed objects during shutdown.
|
|
||||||
|
|
||||||
if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*this))
|
|
||||||
{
|
|
||||||
cRoot::Get()->BroadcastChatLeave(Printf("%s has left the game", GetName().c_str()));
|
|
||||||
LOGINFO("Player %s has left the game", GetName().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove ourself from everyone's lists:
|
|
||||||
cRoot::Get()->BroadcastPlayerListsRemovePlayer(*this);
|
|
||||||
|
|
||||||
// Atomically decrement player count (in world thread):
|
|
||||||
cRoot::Get()->GetServer()->PlayerDestroyed();
|
|
||||||
|
|
||||||
// We're just disconnecting. The remaining code deals with going through portals, so bail:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto DestinationDimension = m_WorldChangeInfo.m_NewWorld->GetDimension();
|
|
||||||
|
|
||||||
// Award relevant achievements:
|
|
||||||
if (DestinationDimension == dimEnd)
|
|
||||||
{
|
|
||||||
AwardAchievement(Statistic::AchTheEnd);
|
|
||||||
}
|
|
||||||
else if (DestinationDimension == dimNether)
|
|
||||||
{
|
|
||||||
AwardAchievement(Statistic::AchPortal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear sent chunk lists from the clienthandle:
|
|
||||||
m_ClientHandle->RemoveFromWorld();
|
|
||||||
|
|
||||||
// The clienthandle caches the coords of the chunk we're standing at. Invalidate this.
|
|
||||||
m_ClientHandle->InvalidateCachedSentChunk();
|
|
||||||
|
|
||||||
// Clientside warp start:
|
|
||||||
m_ClientHandle->SendRespawn(DestinationDimension, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::SpawnOn(cClientHandle & a_Client)
|
|
||||||
{
|
|
||||||
if (!m_bVisible || (m_ClientHandle.get() == (&a_Client)))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGD("Spawing %s on %s", GetName().c_str(), a_Client.GetUsername().c_str());
|
|
||||||
|
|
||||||
a_Client.SendPlayerSpawn(*this);
|
|
||||||
a_Client.SendEntityHeadLook(*this);
|
|
||||||
a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem());
|
|
||||||
a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots());
|
|
||||||
a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings());
|
|
||||||
a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate());
|
|
||||||
a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|
||||||
{
|
|
||||||
m_ClientHandle->Tick(a_Dt.count());
|
|
||||||
|
|
||||||
if (m_ClientHandle->IsDestroyed())
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_ClientHandle->IsPlaying())
|
|
||||||
{
|
|
||||||
// We're not yet in the game, ignore everything:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Stats.AddValue(Statistic::PlayOneMinute);
|
|
||||||
m_Stats.AddValue(Statistic::TimeSinceDeath);
|
|
||||||
|
|
||||||
if (IsCrouched())
|
|
||||||
{
|
|
||||||
m_Stats.AddValue(Statistic::SneakTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the player detach, when the player is in spectator mode
|
|
||||||
if (
|
|
||||||
(IsGameModeSpectator()) &&
|
|
||||||
(m_AttachedTo != nullptr) &&
|
|
||||||
(
|
|
||||||
(m_AttachedTo->IsDestroyed()) || // Watching entity destruction
|
|
||||||
(m_AttachedTo->GetHealth() <= 0) || // Watching entity dead
|
|
||||||
(IsCrouched()) // Or the player wants to be detached
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!a_Chunk.IsValid())
|
|
||||||
{
|
|
||||||
// Players are ticked even if the parent chunk is invalid.
|
|
||||||
// We've processed as much as we can, bail:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT((GetParentChunk() != nullptr) && (GetParentChunk()->IsValid()));
|
|
||||||
ASSERT(a_Chunk.IsValid());
|
|
||||||
|
|
||||||
// Handle a frozen player:
|
|
||||||
TickFreezeCode();
|
|
||||||
|
|
||||||
if (
|
|
||||||
m_IsFrozen || // Don't do Tick updates if frozen
|
|
||||||
IsWorldChangeScheduled() // If we're about to change worlds (e.g. respawn), abort processing all world interactions (GH #3939)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Super::Tick(a_Dt, a_Chunk);
|
|
||||||
|
|
||||||
// Handle charging the bow:
|
|
||||||
if (m_IsChargingBow)
|
|
||||||
{
|
|
||||||
m_BowCharge += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
BroadcastMovementUpdate(m_ClientHandle.get());
|
|
||||||
|
|
||||||
if (m_Health > 0) // make sure player is alive
|
|
||||||
{
|
|
||||||
m_World->CollectPickupsByPlayer(*this);
|
|
||||||
|
|
||||||
if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge()))
|
|
||||||
{
|
|
||||||
FinishEating();
|
|
||||||
}
|
|
||||||
|
|
||||||
HandleFood();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_IsFishing)
|
|
||||||
{
|
|
||||||
HandleFloater();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update items (e.g. Maps)
|
|
||||||
m_Inventory.UpdateItems();
|
|
||||||
|
|
||||||
// Send Player List (Once per m_LastPlayerListTime/1000 ms)
|
|
||||||
if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= std::chrono::steady_clock::now())
|
|
||||||
{
|
|
||||||
m_World->BroadcastPlayerListUpdatePing(*this);
|
|
||||||
m_LastPlayerListTime = std::chrono::steady_clock::now();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_TicksUntilNextSave == 0)
|
|
||||||
{
|
|
||||||
SaveToDisk();
|
|
||||||
m_TicksUntilNextSave = PLAYER_INVENTORY_SAVE_INTERVAL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_TicksUntilNextSave--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::TickFreezeCode()
|
|
||||||
{
|
|
||||||
if (m_IsFrozen)
|
|
||||||
{
|
|
||||||
if ((!m_IsManuallyFrozen) && (GetClientHandle()->IsPlayerChunkSent()))
|
|
||||||
{
|
|
||||||
// If the player was automatically frozen, unfreeze if the chunk the player is inside is loaded and sent
|
|
||||||
Unfreeze();
|
|
||||||
|
|
||||||
// Pull the player out of any solids that might have loaded on them.
|
|
||||||
PREPARE_REL_AND_CHUNK(GetPosition(), *(GetParentChunk()));
|
|
||||||
if (RelSuccess)
|
|
||||||
{
|
|
||||||
int NewY = Rel.y;
|
|
||||||
if (NewY < 0)
|
|
||||||
{
|
|
||||||
NewY = 0;
|
|
||||||
}
|
|
||||||
while (NewY < cChunkDef::Height - 2)
|
|
||||||
{
|
|
||||||
// If we find a position with enough space for the player
|
|
||||||
if (
|
|
||||||
!cBlockInfo::IsSolid(Chunk->GetBlock(Rel.x, NewY, Rel.z)) &&
|
|
||||||
!cBlockInfo::IsSolid(Chunk->GetBlock(Rel.x, NewY + 1, Rel.z))
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// If the found position is not the same as the original
|
|
||||||
if (NewY != Rel.y)
|
|
||||||
{
|
|
||||||
SetPosition(GetPosition().x, NewY, GetPosition().z);
|
|
||||||
GetClientHandle()->SendPlayerPosition();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++NewY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (GetWorld()->GetWorldAge() % 100 == 0)
|
|
||||||
{
|
|
||||||
// Despite the client side freeze, the player may be able to move a little by
|
|
||||||
// Jumping or canceling flight. Re-freeze every now and then
|
|
||||||
FreezeInternal(GetPosition(), m_IsManuallyFrozen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!GetClientHandle()->IsPlayerChunkSent() || (!GetParentChunk()->IsValid()))
|
|
||||||
{
|
|
||||||
FreezeInternal(GetPosition(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int cPlayer::CalcLevelFromXp(int a_XpTotal)
|
int cPlayer::CalcLevelFromXp(int a_XpTotal)
|
||||||
{
|
{
|
||||||
// level 0 to 15
|
// level 0 to 15
|
||||||
@ -1092,69 +755,6 @@ void cPlayer::SetFlying(bool a_IsFlying)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::ApplyArmorDamage(int a_DamageBlocked)
|
|
||||||
{
|
|
||||||
short ArmorDamage = static_cast<short>(std::max(a_DamageBlocked / 4, 1));
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
UseItem(cInventory::invArmorOffset + i, ArmorDamage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
|
|
||||||
{
|
|
||||||
if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtPlugin))
|
|
||||||
{
|
|
||||||
if (IsGameModeCreative() || IsGameModeSpectator())
|
|
||||||
{
|
|
||||||
// No damage / health in creative or spectator mode if not void or plugin damage
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((a_TDI.Attacker != nullptr) && (a_TDI.Attacker->IsPlayer()))
|
|
||||||
{
|
|
||||||
cPlayer * Attacker = static_cast<cPlayer *>(a_TDI.Attacker);
|
|
||||||
|
|
||||||
if ((m_Team != nullptr) && (m_Team == Attacker->m_Team))
|
|
||||||
{
|
|
||||||
if (!m_Team->AllowsFriendlyFire())
|
|
||||||
{
|
|
||||||
// Friendly fire is disabled
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Super::DoTakeDamage(a_TDI))
|
|
||||||
{
|
|
||||||
// Any kind of damage adds food exhaustion
|
|
||||||
AddFoodExhaustion(0.3f);
|
|
||||||
m_ClientHandle->SendHealth();
|
|
||||||
|
|
||||||
// Tell the wolves
|
|
||||||
if (a_TDI.Attacker != nullptr)
|
|
||||||
{
|
|
||||||
if (a_TDI.Attacker->IsPawn())
|
|
||||||
{
|
|
||||||
NotifyNearbyWolves(static_cast<cPawn*>(a_TDI.Attacker), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_Stats.AddValue(Statistic::DamageTaken, FloorC<cStatManager::StatValue>(a_TDI.FinalDamage * 10 + 0.5));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::NotifyNearbyWolves(cPawn * a_Opponent, bool a_IsPlayerInvolved)
|
void cPlayer::NotifyNearbyWolves(cPawn * a_Opponent, bool a_IsPlayerInvolved)
|
||||||
{
|
{
|
||||||
ASSERT(a_Opponent != nullptr);
|
ASSERT(a_Opponent != nullptr);
|
||||||
@ -1888,22 +1488,6 @@ void cPlayer::ForceSetSpeed(const Vector3d & a_Speed)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
|
|
||||||
{
|
|
||||||
if (m_IsFrozen)
|
|
||||||
{
|
|
||||||
// Do not set speed to a frozen client
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Super::DoSetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ);
|
|
||||||
// Send the speed to the client so he actualy moves
|
|
||||||
m_ClientHandle->SendEntityVelocity(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::SetVisible(bool a_bVisible)
|
void cPlayer::SetVisible(bool a_bVisible)
|
||||||
{
|
{
|
||||||
// Need to Check if the player or other players are in gamemode spectator, but will break compatibility
|
// Need to Check if the player or other players are in gamemode spectator, but will break compatibility
|
||||||
@ -3211,15 +2795,436 @@ bool cPlayer::CanInstantlyMine(BLOCKTYPE a_Block)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
float cPlayer::GetExplosionExposureRate(Vector3d a_ExplosionPosition, float a_ExlosionPower)
|
void cPlayer::AddKnownItem(const cItem & a_Item)
|
||||||
|
{
|
||||||
|
if (a_Item.m_ItemType < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Response = m_KnownItems.insert(a_Item.CopyOne());
|
||||||
|
if (!Response.second)
|
||||||
|
{
|
||||||
|
// The item was already known, bail out:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the recipes that got unlocked by this newly-known item:
|
||||||
|
auto Recipes = cRoot::Get()->GetCraftingRecipes()->FindNewRecipesForItem(a_Item, m_KnownItems);
|
||||||
|
for (const auto & RecipeId : Recipes)
|
||||||
|
{
|
||||||
|
AddKnownRecipe(RecipeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPlayer::AddKnownRecipe(UInt32 a_RecipeId)
|
||||||
|
{
|
||||||
|
auto Response = m_KnownRecipes.insert(a_RecipeId);
|
||||||
|
if (!Response.second)
|
||||||
|
{
|
||||||
|
// The recipe was already known, bail out:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_ClientHandle->SendUnlockRecipe(a_RecipeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPlayer::TickFreezeCode()
|
||||||
|
{
|
||||||
|
if (m_IsFrozen)
|
||||||
|
{
|
||||||
|
if ((!m_IsManuallyFrozen) && (GetClientHandle()->IsPlayerChunkSent()))
|
||||||
|
{
|
||||||
|
// If the player was automatically frozen, unfreeze if the chunk the player is inside is loaded and sent
|
||||||
|
Unfreeze();
|
||||||
|
|
||||||
|
// Pull the player out of any solids that might have loaded on them.
|
||||||
|
PREPARE_REL_AND_CHUNK(GetPosition(), *(GetParentChunk()));
|
||||||
|
if (RelSuccess)
|
||||||
|
{
|
||||||
|
int NewY = Rel.y;
|
||||||
|
if (NewY < 0)
|
||||||
|
{
|
||||||
|
NewY = 0;
|
||||||
|
}
|
||||||
|
while (NewY < cChunkDef::Height - 2)
|
||||||
|
{
|
||||||
|
// If we find a position with enough space for the player
|
||||||
|
if (
|
||||||
|
!cBlockInfo::IsSolid(Chunk->GetBlock(Rel.x, NewY, Rel.z)) &&
|
||||||
|
!cBlockInfo::IsSolid(Chunk->GetBlock(Rel.x, NewY + 1, Rel.z))
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// If the found position is not the same as the original
|
||||||
|
if (NewY != Rel.y)
|
||||||
|
{
|
||||||
|
SetPosition(GetPosition().x, NewY, GetPosition().z);
|
||||||
|
GetClientHandle()->SendPlayerPosition();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++NewY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (GetWorld()->GetWorldAge() % 100 == 0)
|
||||||
|
{
|
||||||
|
// Despite the client side freeze, the player may be able to move a little by
|
||||||
|
// Jumping or canceling flight. Re-freeze every now and then
|
||||||
|
FreezeInternal(GetPosition(), m_IsManuallyFrozen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!GetClientHandle()->IsPlayerChunkSent() || (!GetParentChunk()->IsValid()))
|
||||||
|
{
|
||||||
|
FreezeInternal(GetPosition(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPlayer::ApplyArmorDamage(int a_DamageBlocked)
|
||||||
|
{
|
||||||
|
short ArmorDamage = static_cast<short>(std::max(a_DamageBlocked / 4, 1));
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
UseItem(cInventory::invArmorOffset + i, ArmorDamage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPlayer::BroadcastMovementUpdate(const cClientHandle * a_Exclude)
|
||||||
|
{
|
||||||
|
if (!m_IsFrozen && m_Speed.SqrLength() > 0.001)
|
||||||
|
{
|
||||||
|
// If the player is not frozen, has a non-zero speed,
|
||||||
|
// send the speed to the client so he is forced to move so:
|
||||||
|
m_ClientHandle->SendEntityVelocity(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we do no physics processing for players, speed will otherwise never decrease:
|
||||||
|
m_Speed.Set(0, 0, 0);
|
||||||
|
|
||||||
|
Super::BroadcastMovementUpdate(a_Exclude);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||||
|
{
|
||||||
|
// Filters out damage for creative mode / friendly fire.
|
||||||
|
|
||||||
|
if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtPlugin))
|
||||||
|
{
|
||||||
|
if (IsGameModeCreative() || IsGameModeSpectator())
|
||||||
|
{
|
||||||
|
// No damage / health in creative or spectator mode if not void or plugin damage
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((a_TDI.Attacker != nullptr) && (a_TDI.Attacker->IsPlayer()))
|
||||||
|
{
|
||||||
|
cPlayer * Attacker = static_cast<cPlayer *>(a_TDI.Attacker);
|
||||||
|
|
||||||
|
if ((m_Team != nullptr) && (m_Team == Attacker->m_Team))
|
||||||
|
{
|
||||||
|
if (!m_Team->AllowsFriendlyFire())
|
||||||
|
{
|
||||||
|
// Friendly fire is disabled
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Super::DoTakeDamage(a_TDI))
|
||||||
|
{
|
||||||
|
// Any kind of damage adds food exhaustion
|
||||||
|
AddFoodExhaustion(0.3f);
|
||||||
|
m_ClientHandle->SendHealth();
|
||||||
|
|
||||||
|
// Tell the wolves
|
||||||
|
if (a_TDI.Attacker != nullptr)
|
||||||
|
{
|
||||||
|
if (a_TDI.Attacker->IsPawn())
|
||||||
|
{
|
||||||
|
NotifyNearbyWolves(static_cast<cPawn*>(a_TDI.Attacker), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_Stats.AddValue(Statistic::DamageTaken, FloorC<cStatManager::StatValue>(a_TDI.FinalDamage * 10 + 0.5));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
float cPlayer::GetEnchantmentBlastKnockbackReduction()
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
IsGameModeSpectator() ||
|
IsGameModeSpectator() ||
|
||||||
(IsGameModeCreative() && !IsOnGround())
|
(IsGameModeCreative() && !IsOnGround())
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return 0; // No impact from explosion
|
return 1; // No impact from explosion
|
||||||
}
|
}
|
||||||
|
|
||||||
return Super::GetExplosionExposureRate(a_ExplosionPosition, a_ExlosionPower) / 30.0f;
|
return Super::GetEnchantmentBlastKnockbackReduction();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPlayer::OnAddToWorld(cWorld & a_World)
|
||||||
|
{
|
||||||
|
Super::OnAddToWorld(a_World);
|
||||||
|
|
||||||
|
// Update world name tracking:
|
||||||
|
m_CurrentWorldName = m_World->GetName();
|
||||||
|
|
||||||
|
// Fix to stop the player falling through the world, until we get serversided collision detection:
|
||||||
|
FreezeInternal(GetPosition(), false);
|
||||||
|
|
||||||
|
// Set capabilities based on new world:
|
||||||
|
SetCapabilities();
|
||||||
|
|
||||||
|
// Send contents of the inventory window:
|
||||||
|
m_ClientHandle->SendWholeInventory(*m_CurrentWindow);
|
||||||
|
|
||||||
|
// Send health (the respawn packet, which understandably resets health, is also used for world travel...):
|
||||||
|
m_ClientHandle->SendHealth();
|
||||||
|
|
||||||
|
// Send experience, similar story with the respawn packet:
|
||||||
|
m_ClientHandle->SendExperience();
|
||||||
|
|
||||||
|
// Send hotbar active slot (also reset by respawn):
|
||||||
|
m_ClientHandle->SendHeldItemChange(m_Inventory.GetEquippedSlotNum());
|
||||||
|
|
||||||
|
// Update player team:
|
||||||
|
UpdateTeam();
|
||||||
|
|
||||||
|
// Send scoreboard data:
|
||||||
|
m_World->GetScoreBoard().SendTo(*m_ClientHandle);
|
||||||
|
|
||||||
|
// Update the view distance:
|
||||||
|
m_ClientHandle->SetViewDistance(m_ClientHandle->GetRequestedViewDistance());
|
||||||
|
|
||||||
|
// Send current weather of target world:
|
||||||
|
m_ClientHandle->SendWeather(a_World.GetWeather());
|
||||||
|
|
||||||
|
// Send time:
|
||||||
|
m_ClientHandle->SendTimeUpdate(a_World.GetWorldAge(), a_World.GetTimeOfDay(), a_World.IsDaylightCycleEnabled());
|
||||||
|
|
||||||
|
// Finally, deliver the notification hook:
|
||||||
|
cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPlayer::OnRemoveFromWorld(cWorld & a_World)
|
||||||
|
{
|
||||||
|
Super::OnRemoveFromWorld(a_World);
|
||||||
|
|
||||||
|
// Remove any references to this player pointer by windows in the old world:
|
||||||
|
CloseWindow(false);
|
||||||
|
|
||||||
|
// Remove the client handle from the world:
|
||||||
|
m_World->RemoveClientFromChunks(m_ClientHandle.get());
|
||||||
|
|
||||||
|
if (m_ClientHandle->IsDestroyed()) // Note: checking IsWorldChangeScheduled not appropriate here since we can disconnecting while having a scheduled warp
|
||||||
|
{
|
||||||
|
// Disconnecting, do the necessary cleanup.
|
||||||
|
// This isn't in the destructor to avoid crashing accessing destroyed objects during shutdown.
|
||||||
|
|
||||||
|
if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*this))
|
||||||
|
{
|
||||||
|
cRoot::Get()->BroadcastChatLeave(Printf("%s has left the game", GetName().c_str()));
|
||||||
|
LOGINFO("Player %s has left the game", GetName().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove ourself from everyone's lists:
|
||||||
|
cRoot::Get()->BroadcastPlayerListsRemovePlayer(*this);
|
||||||
|
|
||||||
|
// Atomically decrement player count (in world thread):
|
||||||
|
cRoot::Get()->GetServer()->PlayerDestroyed();
|
||||||
|
|
||||||
|
// We're just disconnecting. The remaining code deals with going through portals, so bail:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto DestinationDimension = m_WorldChangeInfo.m_NewWorld->GetDimension();
|
||||||
|
|
||||||
|
// Award relevant achievements:
|
||||||
|
if (DestinationDimension == dimEnd)
|
||||||
|
{
|
||||||
|
AwardAchievement(Statistic::AchTheEnd);
|
||||||
|
}
|
||||||
|
else if (DestinationDimension == dimNether)
|
||||||
|
{
|
||||||
|
AwardAchievement(Statistic::AchPortal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear sent chunk lists from the clienthandle:
|
||||||
|
m_ClientHandle->RemoveFromWorld();
|
||||||
|
|
||||||
|
// The clienthandle caches the coords of the chunk we're standing at. Invalidate this.
|
||||||
|
m_ClientHandle->InvalidateCachedSentChunk();
|
||||||
|
|
||||||
|
// Clientside warp start:
|
||||||
|
m_ClientHandle->SendRespawn(DestinationDimension, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPlayer::SpawnOn(cClientHandle & a_Client)
|
||||||
|
{
|
||||||
|
if (!m_bVisible || (m_ClientHandle.get() == (&a_Client)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGD("Spawing %s on %s", GetName().c_str(), a_Client.GetUsername().c_str());
|
||||||
|
|
||||||
|
a_Client.SendPlayerSpawn(*this);
|
||||||
|
a_Client.SendEntityHeadLook(*this);
|
||||||
|
a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem());
|
||||||
|
a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots());
|
||||||
|
a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings());
|
||||||
|
a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate());
|
||||||
|
a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
|
{
|
||||||
|
m_ClientHandle->Tick(a_Dt.count());
|
||||||
|
|
||||||
|
if (m_ClientHandle->IsDestroyed())
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_ClientHandle->IsPlaying())
|
||||||
|
{
|
||||||
|
// We're not yet in the game, ignore everything:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Stats.AddValue(Statistic::PlayOneMinute);
|
||||||
|
m_Stats.AddValue(Statistic::TimeSinceDeath);
|
||||||
|
|
||||||
|
if (IsCrouched())
|
||||||
|
{
|
||||||
|
m_Stats.AddValue(Statistic::SneakTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the player detach, when the player is in spectator mode
|
||||||
|
if (
|
||||||
|
(IsGameModeSpectator()) &&
|
||||||
|
(m_AttachedTo != nullptr) &&
|
||||||
|
(
|
||||||
|
(m_AttachedTo->IsDestroyed()) || // Watching entity destruction
|
||||||
|
(m_AttachedTo->GetHealth() <= 0) || // Watching entity dead
|
||||||
|
(IsCrouched()) // Or the player wants to be detached
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!a_Chunk.IsValid())
|
||||||
|
{
|
||||||
|
// Players are ticked even if the parent chunk is invalid.
|
||||||
|
// We've processed as much as we can, bail:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT((GetParentChunk() != nullptr) && (GetParentChunk()->IsValid()));
|
||||||
|
ASSERT(a_Chunk.IsValid());
|
||||||
|
|
||||||
|
// Handle a frozen player:
|
||||||
|
TickFreezeCode();
|
||||||
|
|
||||||
|
if (
|
||||||
|
m_IsFrozen || // Don't do Tick updates if frozen
|
||||||
|
IsWorldChangeScheduled() // If we're about to change worlds (e.g. respawn), abort processing all world interactions (GH #3939)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Super::Tick(a_Dt, a_Chunk);
|
||||||
|
|
||||||
|
// Handle charging the bow:
|
||||||
|
if (m_IsChargingBow)
|
||||||
|
{
|
||||||
|
m_BowCharge += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
BroadcastMovementUpdate(m_ClientHandle.get());
|
||||||
|
|
||||||
|
if (m_Health > 0) // make sure player is alive
|
||||||
|
{
|
||||||
|
m_World->CollectPickupsByPlayer(*this);
|
||||||
|
|
||||||
|
if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge()))
|
||||||
|
{
|
||||||
|
FinishEating();
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleFood();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_IsFishing)
|
||||||
|
{
|
||||||
|
HandleFloater();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update items (e.g. Maps)
|
||||||
|
m_Inventory.UpdateItems();
|
||||||
|
|
||||||
|
// Send Player List (Once per m_LastPlayerListTime/1000 ms)
|
||||||
|
if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= std::chrono::steady_clock::now())
|
||||||
|
{
|
||||||
|
m_World->BroadcastPlayerListUpdatePing(*this);
|
||||||
|
m_LastPlayerListTime = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_TicksUntilNextSave == 0)
|
||||||
|
{
|
||||||
|
SaveToDisk();
|
||||||
|
m_TicksUntilNextSave = PLAYER_INVENTORY_SAVE_INTERVAL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_TicksUntilNextSave--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,42 +47,9 @@ public:
|
|||||||
|
|
||||||
CLASS_PROTODEF(cPlayer)
|
CLASS_PROTODEF(cPlayer)
|
||||||
|
|
||||||
|
|
||||||
cPlayer(const cClientHandlePtr & a_Client);
|
cPlayer(const cClientHandlePtr & a_Client);
|
||||||
|
|
||||||
virtual ~cPlayer() override;
|
virtual ~cPlayer() override;
|
||||||
|
|
||||||
virtual void OnAddToWorld(cWorld & a_World) override;
|
|
||||||
virtual void OnRemoveFromWorld(cWorld & a_World) override;
|
|
||||||
|
|
||||||
virtual void SpawnOn(cClientHandle & a_Client) override;
|
|
||||||
|
|
||||||
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
|
||||||
|
|
||||||
void TickFreezeCode();
|
|
||||||
|
|
||||||
virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &) override { UNUSED(a_Dt); }
|
|
||||||
|
|
||||||
/** Returns the currently equipped weapon; empty item if none */
|
|
||||||
virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); }
|
|
||||||
|
|
||||||
/** Returns the currently equipped helmet; empty item if none */
|
|
||||||
virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); }
|
|
||||||
|
|
||||||
/** Returns the currently equipped chestplate; empty item if none */
|
|
||||||
virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); }
|
|
||||||
|
|
||||||
/** Returns the currently equipped leggings; empty item if none */
|
|
||||||
virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); }
|
|
||||||
|
|
||||||
/** Returns the currently equipped boots; empty item if none */
|
|
||||||
virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); }
|
|
||||||
|
|
||||||
/** Returns the currently offhand equipped item; empty item if none */
|
|
||||||
virtual cItem GetOffHandEquipedItem(void) const override { return m_Inventory.GetShieldSlot(); }
|
|
||||||
|
|
||||||
virtual void ApplyArmorDamage(int DamageBlocked) override;
|
|
||||||
|
|
||||||
// tolua_begin
|
// tolua_begin
|
||||||
|
|
||||||
/** Sets the experience total
|
/** Sets the experience total
|
||||||
@ -598,14 +565,19 @@ public:
|
|||||||
Source: https://minecraft.gamepedia.com/Breaking#Instant_breaking */
|
Source: https://minecraft.gamepedia.com/Breaking#Instant_breaking */
|
||||||
bool CanInstantlyMine(BLOCKTYPE a_Block);
|
bool CanInstantlyMine(BLOCKTYPE a_Block);
|
||||||
|
|
||||||
/** get player explosion exposure rate */
|
|
||||||
virtual float GetExplosionExposureRate(Vector3d a_ExplosionPosition, float a_ExlosionPower) override;
|
|
||||||
|
|
||||||
/** Adds an Item to the list of known items.
|
/** Adds an Item to the list of known items.
|
||||||
If the item is already known, does nothing. */
|
If the item is already known, does nothing. */
|
||||||
void AddKnownItem(const cItem & a_Item);
|
void AddKnownItem(const cItem & a_Item);
|
||||||
|
|
||||||
protected:
|
// cEntity overrides:
|
||||||
|
virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); }
|
||||||
|
virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); }
|
||||||
|
virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); }
|
||||||
|
virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); }
|
||||||
|
virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); }
|
||||||
|
virtual cItem GetOffHandEquipedItem(void) const override { return m_Inventory.GetShieldSlot(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
typedef std::vector<std::vector<AString> > AStringVectorVector;
|
typedef std::vector<std::vector<AString> > AStringVectorVector;
|
||||||
|
|
||||||
@ -766,12 +738,6 @@ protected:
|
|||||||
/** List of known items as Ids */
|
/** List of known items as Ids */
|
||||||
std::set<cItem, cItem::sItemCompare> m_KnownItems;
|
std::set<cItem, cItem::sItemCompare> m_KnownItems;
|
||||||
|
|
||||||
/** Sets the speed and sends it to the client, so that they are forced to move so. */
|
|
||||||
virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) override;
|
|
||||||
|
|
||||||
/** Filters out damage for creative mode / friendly fire */
|
|
||||||
virtual bool DoTakeDamage(TakeDamageInfo & TDI) override;
|
|
||||||
|
|
||||||
/** Called in each tick to handle food-related processing */
|
/** Called in each tick to handle food-related processing */
|
||||||
void HandleFood(void);
|
void HandleFood(void);
|
||||||
|
|
||||||
@ -782,8 +748,6 @@ protected:
|
|||||||
This can be used both for online and offline UUIDs. */
|
This can be used both for online and offline UUIDs. */
|
||||||
AString GetUUIDFileName(const cUUID & a_UUID);
|
AString GetUUIDFileName(const cUUID & a_UUID);
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/** Pins the player to a_Location until Unfreeze() is called.
|
/** Pins the player to a_Location until Unfreeze() is called.
|
||||||
If ManuallyFrozen is false, the player will unfreeze when the chunk is loaded. */
|
If ManuallyFrozen is false, the player will unfreeze when the chunk is loaded. */
|
||||||
void FreezeInternal(const Vector3d & a_Location, bool a_ManuallyFrozen);
|
void FreezeInternal(const Vector3d & a_Location, bool a_ManuallyFrozen);
|
||||||
@ -807,4 +771,17 @@ private:
|
|||||||
If the recipe is already known, does nothing. */
|
If the recipe is already known, does nothing. */
|
||||||
void AddKnownRecipe(UInt32 RecipeId);
|
void AddKnownRecipe(UInt32 RecipeId);
|
||||||
|
|
||||||
|
void TickFreezeCode();
|
||||||
|
|
||||||
|
// cEntity overrides:
|
||||||
|
virtual void ApplyArmorDamage(int DamageBlocked) override;
|
||||||
|
virtual void BroadcastMovementUpdate(const cClientHandle * a_Exclude = nullptr) override;
|
||||||
|
virtual bool DoTakeDamage(TakeDamageInfo & TDI) override;
|
||||||
|
virtual float GetEnchantmentBlastKnockbackReduction() override;
|
||||||
|
virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &) override { UNUSED(a_Dt); }
|
||||||
|
virtual void OnAddToWorld(cWorld & a_World) override;
|
||||||
|
virtual void OnRemoveFromWorld(cWorld & a_World) override;
|
||||||
|
virtual void SpawnOn(cClientHandle & a_Client) override;
|
||||||
|
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
||||||
|
|
||||||
} ; // tolua_export
|
} ; // tolua_export
|
||||||
|
@ -96,11 +96,11 @@ namespace Explodinator
|
|||||||
if (Entity.IsPawn())
|
if (Entity.IsPawn())
|
||||||
{
|
{
|
||||||
const auto ReducedImpact = Impact - Impact * Entity.GetEnchantmentBlastKnockbackReduction();
|
const auto ReducedImpact = Impact - Impact * Entity.GetEnchantmentBlastKnockbackReduction();
|
||||||
Entity.SetSpeed(Direction.NormalizeCopy() * KnockbackFactor * ReducedImpact);
|
Entity.AddSpeed(Direction.NormalizeCopy() * KnockbackFactor * ReducedImpact);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Entity.SetSpeed(Direction.NormalizeCopy() * KnockbackFactor * Impact);
|
Entity.AddSpeed(Direction.NormalizeCopy() * KnockbackFactor * Impact);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue iteration:
|
// Continue iteration:
|
||||||
|
Loading…
Reference in New Issue
Block a user