From 4f554e91abd861323aaf9f46d75ff0350a519cef Mon Sep 17 00:00:00 2001 From: DarkoGNU <42816979+DarkoGNU@users.noreply.github.com> Date: Thu, 21 Apr 2022 20:56:21 +0200 Subject: 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? --- CONTRIBUTORS | 1 + Server/Plugins/APIDump/Classes/World.lua | 10 +++++ src/BlockInfo.cpp | 1 + src/Blocks/BlockFarmland.h | 67 +++++++++++++++++++++++++++++++- src/Entities/Pawn.cpp | 52 ++++++++++++++++++++++++- src/Entities/Pawn.h | 10 +++++ src/World.cpp | 2 + src/World.h | 4 ++ 8 files changed, 143 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 856b44ed3..5f885478b 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -17,6 +17,7 @@ birkett (Anthony Birkett) Bond_009 changyongGuo Cocosushi6 +DarkoGNU derouinw dImrich (Damian Imrich) Diusrex diff --git a/Server/Plugins/APIDump/Classes/World.lua b/Server/Plugins/APIDump/Classes/World.lua index 2a9e3d4e8..ab12c89cf 100644 --- a/Server/Plugins/APIDump/Classes/World.lua +++ b/Server/Plugins/APIDump/Classes/World.lua @@ -2245,6 +2245,16 @@ function OnAllChunksAvailable() 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 = 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(&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(m_LastGroundHeight - GetPosY() - 3.0); + auto FallHeight = m_LastGroundHeight - GetPosY(); + auto Damage = static_cast(FallHeight - 3.0); + if ((Damage > 0) && !FallDamageAbsorbed) { if (IsElytraFlying()) @@ -438,7 +441,6 @@ void cPawn::HandleFalling(void) Damage = static_cast(static_cast(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(static_cast(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(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; -- cgit v1.2.3