Implement farmland trampling (#5401)

* Add DarkoGNU to CONTRIBUTORS

* HandleFarmlandTrampling function & its docs

* Fix decimal separators (, -> .)

* Fix style. Adjust thresholds. Make function non-virtual

* Adjust thresholds again. Prepare for fixing #5402

* Trying to fix falling through farmlands

* Another style fix

* Add FarmlandTramplingEnabled to world.ini

* Docs for IsFarmlandTramplingEnabled

* Style

* Farmland trampling - handling the random chance

* Trampling kinda works, very buggy

* Trying to fix clang-tidy

* Fix trampling

* Trying to fix the 'undocumented API symbol'

* Implement bearbin's suggestions

* Calculate volume properly

* Don't use std::pow for squaring

* Improved comments

* Really, should comments' style be checked?
This commit is contained in:
DarkoGNU 2022-04-21 20:56:21 +02:00 committed by GitHub
parent 844a339330
commit 4f554e91ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 143 additions and 4 deletions

View File

@ -17,6 +17,7 @@ birkett (Anthony Birkett)
Bond_009
changyongGuo
Cocosushi6
DarkoGNU
derouinw
dImrich (Damian Imrich)
Diusrex

View File

@ -2245,6 +2245,16 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
},
Notes = "Returns whether the configuration has DeepSnow enabled.",
},
IsFarmlandTramplingEnabled =
{
Returns =
{
{
Type = "boolean",
},
},
Notes = "Returns true if farmland trampling is enabled.",
},
IsGameModeAdventure =
{
Returns =

View File

@ -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;

View File

@ -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;
});
}
}

View File

@ -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();

View File

@ -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();

View File

@ -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);

View File

@ -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;