summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/BlockInfo.cpp1
-rw-r--r--src/Blocks/BlockFarmland.h67
-rw-r--r--src/Entities/Pawn.cpp52
-rw-r--r--src/Entities/Pawn.h10
-rw-r--r--src/World.cpp2
-rw-r--r--src/World.h4
6 files changed, 132 insertions, 4 deletions
diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp
index 4f2513928..9f29d3b31 100644
--- a/src/BlockInfo.cpp
+++ b/src/BlockInfo.cpp
@@ -1164,6 +1164,7 @@ float cBlockInfo::GetBlockHeight(const BLOCKTYPE Block)
case E_BLOCK_DARK_OAK_FENCE: return 1.5;
case E_BLOCK_DARK_OAK_FENCE_GATE: return 1.5;
case E_BLOCK_ENCHANTMENT_TABLE: return 0.75; // 12 pixels
+ case E_BLOCK_FARMLAND: return 0.9375; // 15 pixels
case E_BLOCK_FENCE: return 1.5;
case E_BLOCK_JUNGLE_FENCE: return 1.5;
case E_BLOCK_JUNGLE_FENCE_GATE: return 1.5;
diff --git a/src/Blocks/BlockFarmland.h b/src/Blocks/BlockFarmland.h
index 7ed1bb035..61ac872c8 100644
--- a/src/Blocks/BlockFarmland.h
+++ b/src/Blocks/BlockFarmland.h
@@ -10,7 +10,10 @@
#pragma once
#include "BlockHandler.h"
+#include "ChunkInterface.h"
#include "../BlockArea.h"
+#include "../Chunk.h"
+#include "../ClientHandle.h"
@@ -25,6 +28,58 @@ public:
using Super::Super;
+ /** Turns farmland into dirt.
+ Will first check for any colliding entities and teleport them to a higher position.
+ */
+ static void TurnToDirt(cChunk & a_Chunk, Vector3i a_AbsPos)
+ {
+ auto RelPos = cChunkDef::AbsoluteToRelative(a_AbsPos);
+ TurnToDirt(a_Chunk, a_AbsPos, RelPos);
+ }
+
+
+
+
+
+ /** Turns farmland into dirt.
+ Will first check for any colliding entities and teleport them to a higher position.
+ */
+ static void TurnToDirt(cChunk & a_Chunk, Vector3i a_AbsPos, Vector3i a_RelPos)
+ {
+ static const auto FarmlandHeight = cBlockInfo::GetBlockHeight(E_BLOCK_FARMLAND);
+ static const auto FullHeightDelta = 1 - FarmlandHeight;
+
+ a_Chunk.ForEachEntityInBox(
+ cBoundingBox(Vector3d(0.5, FarmlandHeight, 0.5) + a_AbsPos, 0.5, FullHeightDelta),
+ [&](cEntity & Entity)
+ {
+ if (!Entity.IsOnGround())
+ {
+ return false;
+ }
+ Entity.AddPosY(FullHeightDelta);
+
+ // Players need a packet that will update their position
+ if (Entity.IsPlayer())
+ {
+ auto Player = static_cast<cPlayer *>(&Entity);
+ // This works, but it's much worse than Vanilla.
+ // This can be easily improved by implementing relative
+ // "Player Position And Look" packets! See
+ // https://wiki.vg/Protocol#Player_Position_And_Look_.28clientbound.29
+ Player->GetClientHandle()->SendPlayerMoveLook();
+ }
+
+ return false;
+ });
+
+ a_Chunk.SetBlock(a_RelPos, E_BLOCK_DIRT, 0);
+ }
+
+
+
+
+
private:
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
@@ -76,7 +131,8 @@ private:
}
default:
{
- a_Chunk.SetBlock(a_RelPos, E_BLOCK_DIRT, 0);
+ auto AbsPos = a_Chunk.RelativeToAbsolute(a_RelPos);
+ TurnToDirt(a_Chunk, AbsPos, a_RelPos);
break;
}
}
@@ -101,10 +157,17 @@ private:
}
// Check whether we should revert to dirt:
+ // TODO: fix for signs and slabs (possibly more blocks) - they should destroy farmland
auto upperBlock = a_ChunkInterface.GetBlock(a_BlockPos.addedY(1));
if (cBlockInfo::FullyOccupiesVoxel(upperBlock))
{
- a_ChunkInterface.SetBlock(a_BlockPos, E_BLOCK_DIRT, 0);
+ // Until the fix above is done, this line should also suffice:
+ // a_ChunkInterface.SetBlock(a_BlockPos, E_BLOCK_DIRT, 0);
+ a_ChunkInterface.DoWithChunkAt(a_BlockPos, [&](cChunk & Chunk)
+ {
+ TurnToDirt(Chunk, a_BlockPos);
+ return true;
+ });
}
}
diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp
index 990ea8096..cf2dd274f 100644
--- a/src/Entities/Pawn.cpp
+++ b/src/Entities/Pawn.cpp
@@ -8,6 +8,7 @@
#include "../Bindings/PluginManager.h"
#include "../BoundingBox.h"
#include "../Blocks/BlockHandler.h"
+#include "../Blocks/BlockFarmland.h"
#include "../EffectID.h"
#include "../Mobs/Monster.h"
@@ -430,7 +431,9 @@ void cPawn::HandleFalling(void)
if (OnGround)
{
- auto Damage = static_cast<int>(m_LastGroundHeight - GetPosY() - 3.0);
+ auto FallHeight = m_LastGroundHeight - GetPosY();
+ auto Damage = static_cast<int>(FallHeight - 3.0);
+
if ((Damage > 0) && !FallDamageAbsorbed)
{
if (IsElytraFlying())
@@ -438,7 +441,6 @@ void cPawn::HandleFalling(void)
Damage = static_cast<int>(static_cast<float>(Damage) * 0.33);
}
- // Fall particles:
if (const auto Below = POS_TOINT.addedY(-1); Below.y >= 0)
{
const auto BlockBelow = GetWorld()->GetBlock(Below);
@@ -448,6 +450,7 @@ void cPawn::HandleFalling(void)
Damage = std::clamp(static_cast<int>(static_cast<float>(Damage) * 0.2), 1, 20);
}
+ // Fall particles
GetWorld()->BroadcastParticleEffect(
"blockdust",
GetPosition(),
@@ -463,6 +466,15 @@ void cPawn::HandleFalling(void)
m_bTouchGround = true;
m_LastGroundHeight = GetPosY();
+
+ // Farmland trampling. Mobs smaller than 0.512 cubic blocks won't trample (Java Edition's behavior)
+ // We only have width and height, so we have to calculate Width^2
+ if (GetWorld()->IsFarmlandTramplingEnabled() &&
+ (BlockAtFoot == E_BLOCK_FARMLAND) &&
+ (GetWidth() * GetWidth() * GetHeight() >= 0.512))
+ {
+ HandleFarmlandTrampling(FallHeight);
+ }
}
else
{
@@ -478,6 +490,42 @@ void cPawn::HandleFalling(void)
+void cPawn::HandleFarmlandTrampling(double a_FallHeight)
+{
+ bool ShouldTrample = true;
+ auto & Random = GetRandomProvider();
+
+ // No trampling if FallHeight <= 0.6875
+ if (a_FallHeight <= 0.6875)
+ {
+ ShouldTrample = false;
+ }
+ // For FallHeight <= 1.5625 we need to get a random bool
+ else if (a_FallHeight <= 1.0625)
+ {
+ ShouldTrample = Random.RandBool(0.25);
+ }
+ else if (a_FallHeight <= 1.5625)
+ {
+ ShouldTrample = Random.RandBool(0.66);
+ }
+ // For FallHeight > 1.5625 we always trample - ShouldTrample remains true
+
+ if (ShouldTrample)
+ {
+ auto AbsPos = GetPosition().Floor();
+ GetWorld()->DoWithChunkAt(AbsPos, [&](cChunk & Chunk)
+ {
+ cBlockFarmlandHandler::TurnToDirt(Chunk, AbsPos);
+ return true;
+ });
+ }
+}
+
+
+
+
+
void cPawn::OnRemoveFromWorld(cWorld & a_World)
{
StopEveryoneFromTargetingMe();
diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h
index 222e8f3b8..c0801ebb3 100644
--- a/src/Entities/Pawn.h
+++ b/src/Entities/Pawn.h
@@ -33,6 +33,16 @@ public:
virtual void HandleFalling(void);
virtual void OnRemoveFromWorld(cWorld & a_World) override;
+ /** Handles farmland trampling when hitting the ground.
+ Algorithm:
+ fall height <= 0.6875 blocks: no trampling
+ fall height > 0.6875 and <= 1.0625: 25% chance of trampling
+ fall height > 1.0625 and <= 1.5625: 66% chance of trampling
+ fall height > 1.5625: always trample
+ The values may differ from vanilla, they were determined experimentally.
+ */
+ void HandleFarmlandTrampling(double a_FallHeight);
+
/** Tells all pawns which are targeting us to stop targeting us. */
void StopEveryoneFromTargetingMe();
diff --git a/src/World.cpp b/src/World.cpp
index 65908ed9d..57ba656e8 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -166,6 +166,7 @@ cWorld::cWorld(
m_SkyDarkness(0),
m_GameMode(gmSurvival),
m_bEnabledPVP(false),
+ m_bFarmlandTramplingEnabled(false),
m_IsDeepSnowEnabled(false),
m_ShouldLavaSpawnFire(true),
m_VillagersShouldHarvestCrops(true),
@@ -300,6 +301,7 @@ cWorld::cWorld(
int TNTShrapnelLevel = IniFile.GetValueSetI("Physics", "TNTShrapnelLevel", static_cast<int>(slAll));
m_bCommandBlocksEnabled = IniFile.GetValueSetB("Mechanics", "CommandBlocksEnabled", false);
m_bEnabledPVP = IniFile.GetValueSetB("Mechanics", "PVPEnabled", true);
+ m_bFarmlandTramplingEnabled = IniFile.GetValueSetB("Mechanics", "FarmlandTramplingEnabled", true);
m_bUseChatPrefixes = IniFile.GetValueSetB("Mechanics", "UseChatPrefixes", true);
m_MinNetherPortalWidth = IniFile.GetValueSetI("Mechanics", "MinNetherPortalWidth", 2);
m_MaxNetherPortalWidth = IniFile.GetValueSetI("Mechanics", "MaxNetherPortalWidth", 21);
diff --git a/src/World.h b/src/World.h
index 8f37d59df..ea995ebdc 100644
--- a/src/World.h
+++ b/src/World.h
@@ -121,6 +121,9 @@ public:
bool IsPVPEnabled(void) const { return m_bEnabledPVP; }
+ /** Returns true if farmland trampling is enabled */
+ bool IsFarmlandTramplingEnabled(void) const { return m_bFarmlandTramplingEnabled; }
+
bool IsDeepSnowEnabled(void) const { return m_IsDeepSnowEnabled; }
bool ShouldLavaSpawnFire(void) const { return m_ShouldLavaSpawnFire; }
@@ -997,6 +1000,7 @@ private:
eGameMode m_GameMode;
bool m_bEnabledPVP;
+ bool m_bFarmlandTramplingEnabled;
bool m_IsDeepSnowEnabled;
bool m_ShouldLavaSpawnFire;
bool m_VillagersShouldHarvestCrops;