From 01e72ddb6567531b16f92af2564b853878b6ef65 Mon Sep 17 00:00:00 2001 From: changyong guo Date: Mon, 23 Jul 2018 17:24:00 +0800 Subject: Rewrite explosion knock back (#4251) 1. Base knockback on an entity's bounding box intersection with the explosion 2. Armor blast protection reduces knockback 3. Don't apply knockback to players flying in creative mode Fixes #4139 --- Server/Plugins/APIDump/APIDesc.lua | 10 ++++++ src/ChunkMap.cpp | 19 ++++++++--- src/Entities/Entity.cpp | 64 +++++++++++++++++++++++++++++++++++++- src/Entities/Entity.h | 11 ++++++- src/Entities/Player.cpp | 28 +++++++++++++++++ src/Entities/Player.h | 2 ++ src/World.cpp | 15 +-------- 7 files changed, 129 insertions(+), 20 deletions(-) diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index ee1ed0d04..b6a4ec81e 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -3231,6 +3231,16 @@ local Hash = cCryptoHash.sha1HexString("DataToHash") }, Notes = "Returns the entity classname that this class implements. Each descendant overrides this function.", }, + GetEnchantmentBlastKnockbackReduction = + { + Returns = + { + { + Type = "number", + }, + }, + Notes = "Returns explosion knock back reduction percent from blast protection level.", + }, GetEnchantmentCoverAgainst = { Params = diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index d970956f3..770d34b5a 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -1814,10 +1814,21 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_ a_Entity.TakeDamage(dtExplosion, nullptr, static_cast((1 / DistanceFromExplosion.Length()) * 6 * ExplosionSizeInt), 0); } - // Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt() - DistanceFromExplosion.Normalize(); - DistanceFromExplosion *= ExplosionSizeInt * ExplosionSizeInt; - a_Entity.AddSpeed(DistanceFromExplosion); + double Length = DistanceFromExplosion.Length(); + if (Length <= ExplosionSizeInt) // Entity is impacted by explosion + { + float EntityExposure = a_Entity.GetExplosionExposureRate(ExplosionPos, ExplosionSizeInt); + + // Exposure reduced by armor + EntityExposure = EntityExposure * (1.0f - a_Entity.GetEnchantmentBlastKnockbackReduction()); + + double Impact = (1 - ((Length / ExplosionSizeInt) / 2)) * EntityExposure; + + DistanceFromExplosion.Normalize(); + DistanceFromExplosion *= Impact; + + a_Entity.AddSpeed(DistanceFromExplosion); + } return false; } diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index d1fdcfd39..4ecc5c4da 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -14,7 +14,7 @@ #include "Items/ItemHandler.h" #include "../FastRandom.h" #include "../NetherPortalScanner.h" - +#include "../BoundingBox.h" @@ -690,6 +690,33 @@ int cEntity::GetEnchantmentCoverAgainst(const cEntity * a_Attacker, eDamageType + +float cEntity::GetEnchantmentBlastKnockbackReduction() +{ + UInt32 MaxLevel = 0; + + const cItem ArmorItems[] = { GetEquippedHelmet(), GetEquippedChestplate(), GetEquippedLeggings(), GetEquippedBoots() }; + + for (auto & Item : ArmorItems) + { + UInt32 Level = Item.m_Enchantments.GetLevel(cEnchantments::enchBlastProtection); + if (Level > MaxLevel) + { + // Get max blast protection + MaxLevel = Level; + } + } + + // Max blast protect level is 4, each level provide 15% knock back reduction + MaxLevel = std::min(MaxLevel, 4); + return MaxLevel * 0.15f; +} + + + + + + int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage) { // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover @@ -2241,3 +2268,38 @@ void cEntity::RemoveLeashedMob(cMonster * a_Monster) m_LeashedMobs.remove(a_Monster); } + + + + + + +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; + } + + cBoundingBox EntityBox(GetPosition(), m_Width / 2, m_Height); + 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(OverlapSize / EntitySize); + } + else + { + return 0; + } +} + + + diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index 7ac12c95b..ae150b4e2 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -333,6 +333,10 @@ public: /** Returns the hitpoints that the currently equipped armor's enchantments would cover */ virtual int GetEnchantmentCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage); + /** Returns explosion knock back reduction percent from blast protection level + @return knock back reduce percent */ + virtual float GetEnchantmentBlastKnockbackReduction(); + /** Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit */ virtual double GetKnockbackAmountAgainst(const cEntity & a_Receiver); @@ -545,6 +549,12 @@ public: /** Returs whether the entity has any mob leashed to */ 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: /** Structure storing the portal delay timer and cooldown boolean */ struct sPortalCooldownData @@ -714,5 +724,4 @@ private: /** List of leashed mobs to this entity */ cMonsterList m_LeashedMobs; - } ; // tolua_export diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index d8bb22e2f..181a54870 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -3069,3 +3069,31 @@ float cPlayer::GetPlayerRelativeBlockHardness(BLOCKTYPE a_Block) // LOGD("blockHardness: %f, digSpeed: %f, canHarvestBlockDivisor: %f\n", blockHardness, digSpeed, canHarvestBlockDivisor); return (blockHardness < 0) ? 0 : ((digSpeed / blockHardness) / canHarvestBlockDivisor); } + + + + + + +float cPlayer::GetExplosionExposureRate(Vector3d a_ExplosionPosition, float a_ExlosionPower) +{ + if ( + IsGameModeSpectator() || + (IsGameModeCreative() && !IsOnGround()) + ) + { + return 0; // No impact from explosion + } + + return super::GetExplosionExposureRate(a_ExplosionPosition, a_ExlosionPower); +} + + + + + + + + + + diff --git a/src/Entities/Player.h b/src/Entities/Player.h index e710e8396..c9249b2f1 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -759,6 +759,8 @@ protected: This can be used both for online and offline UUIDs. */ AString GetUUIDFileName(const cUUID & a_UUID); + /** get player explosion exposure rate */ + virtual float GetExplosionExposureRate(Vector3d a_ExplosionPosition, float a_ExlosionPower) override; private: /** Pins the player to a_Location until Unfreeze() is called. diff --git a/src/World.cpp b/src/World.cpp index adbac33bf..97155c872 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1430,7 +1430,6 @@ void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_Blo } // TODO: Implement block hardiness - Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ); cVector3iArray BlocksAffected; m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected); BroadcastSoundEffect("entity.generic.explode", Vector3d(a_BlockX, a_BlockY, a_BlockZ), 1.0f, 0.6f); @@ -1445,19 +1444,7 @@ void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_Blo continue; } - Vector3d distance_explosion = (*itr)->GetPosition() - explosion_pos; - if (distance_explosion.SqrLength() < 4096.0) - { - double real_distance = std::max(0.004, distance_explosion.Length()); - double power = a_ExplosionSize / real_distance; - if (power <= 1) - { - power = 0; - } - distance_explosion.Normalize(); - distance_explosion *= power; - ch->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, static_cast(a_ExplosionSize), BlocksAffected, distance_explosion); - } + ch->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, static_cast(a_ExplosionSize), BlocksAffected, (*itr)->GetSpeed()); } } -- cgit v1.2.3