1
0

Merge pull request #623 from mc-server/tnt

TNT improvements
This commit is contained in:
Mattes D 2014-02-03 10:55:23 -08:00
commit 2450d0467f
2 changed files with 122 additions and 36 deletions

View File

@ -801,6 +801,7 @@ enum eDamageType
dtPotionOfHarming,
dtEnderPearl, // Thrown an ender pearl, teleported by it
dtAdmin, // Damage applied by an admin command
dtExplosion, // Damage applied by an explosion
// Some common synonyms:
dtPawnAttack = dtAttack,

View File

@ -15,6 +15,9 @@
#include "Blocks/BlockHandler.h"
#include "MobCensus.h"
#include "MobSpawner.h"
#include "BoundingBox.h"
#include "Entities/Pickup.h"
#ifndef _WIN32
#include <cstdlib> // abs
@ -1658,27 +1661,30 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
return;
}
bool ShouldDestroyBlocks = true;
// Don't explode if the explosion center is inside a liquid block:
switch (m_World->GetBlock((int)floor(a_BlockX), (int)floor(a_BlockY), (int)floor(a_BlockZ)))
if (IsBlockLiquid(m_World->GetBlock((int)floor(a_BlockX), (int)floor(a_BlockY), (int)floor(a_BlockZ))))
{
case E_BLOCK_WATER:
case E_BLOCK_STATIONARY_WATER:
case E_BLOCK_LAVA:
case E_BLOCK_STATIONARY_LAVA:
{
return;
}
ShouldDestroyBlocks = false;
}
cBlockArea area;
int ExplosionSizeInt = (int)ceil(a_ExplosionSize);
int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt;
int bx = (int)floor(a_BlockX);
int by = (int)floor(a_BlockY);
int bz = (int)floor(a_BlockZ);
int ExplosionSizeInt = (int) ceil(a_ExplosionSize);
int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt;
a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt);
int MinY = std::max((int)floor(a_BlockY - ExplosionSizeInt), 0);
int MaxY = std::min((int)ceil(a_BlockY + ExplosionSizeInt), cChunkDef::Height - 1);
if (ShouldDestroyBlocks)
{
cBlockArea area;
a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt);
area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt));
for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++)
{
@ -1756,6 +1762,85 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
} // for y
} // for x
area.Write(m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt);
}
class cTNTDamageCallback :
public cEntityCallback
{
public:
cTNTDamageCallback(cBoundingBox & a_bbTNT, Vector3d a_ExplosionPos, int a_ExplosionSize, int a_ExplosionSizeSq) :
m_bbTNT(a_bbTNT),
m_ExplosionPos(a_ExplosionPos),
m_ExplosionSize(a_ExplosionSize),
m_ExplosionSizeSq(a_ExplosionSizeSq)
{
}
virtual bool Item(cEntity * a_Entity) override
{
if (a_Entity->IsPickup())
{
if (((cPickup *)a_Entity)->GetAge() < 20) // If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
{
return false;
}
}
Vector3d EntityPos = a_Entity->GetPosition();
cBoundingBox bbEntity(EntityPos, a_Entity->GetWidth() / 2, a_Entity->GetHeight());
if (!m_bbTNT.IsInside(bbEntity)) // IsInside actually acts like DoesSurround
{
return false;
}
Vector3d AbsoluteEntityPos(abs(EntityPos.x), abs(EntityPos.y), abs(EntityPos.z));
Vector3d MaxExplosionBoundary(m_ExplosionSizeSq, m_ExplosionSizeSq, m_ExplosionSizeSq);
// Work out how far we are from the edge of the TNT's explosive effect
AbsoluteEntityPos -= m_ExplosionPos;
AbsoluteEntityPos = MaxExplosionBoundary - AbsoluteEntityPos;
double FinalDamage = ((AbsoluteEntityPos.x + AbsoluteEntityPos.y + AbsoluteEntityPos.z) / 3) * m_ExplosionSize;
FinalDamage = a_Entity->GetMaxHealth() - abs(FinalDamage);
// Clip damage values
if (FinalDamage > a_Entity->GetMaxHealth())
FinalDamage = a_Entity->GetMaxHealth();
else if (FinalDamage < 0)
return false;
if (!a_Entity->IsTNT()) // Don't apply damage to other TNT entities, they should be invincible
{
a_Entity->TakeDamage(dtExplosion, NULL, (int)FinalDamage, 0);
}
// Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt()
Vector3d distance_explosion = a_Entity->GetPosition() - m_ExplosionPos;
if (distance_explosion.SqrLength() < 4096.0)
{
distance_explosion.Normalize();
distance_explosion *= m_ExplosionSizeSq;
a_Entity->AddSpeed(distance_explosion);
}
return false;
}
protected:
cBoundingBox & m_bbTNT;
Vector3d m_ExplosionPos;
int m_ExplosionSize;
int m_ExplosionSizeSq;
};
cBoundingBox bbTNT(Vector3d(a_BlockX, a_BlockY, a_BlockZ), 0.5, 1);
bbTNT.Expand(ExplosionSizeInt * 2, ExplosionSizeInt * 2, ExplosionSizeInt * 2);
cTNTDamageCallback TNTDamageCallback(bbTNT, Vector3d(a_BlockX, a_BlockY, a_BlockZ), a_ExplosionSize, ExplosionSizeSq);
ForEachEntity(TNTDamageCallback);
// Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391):
WakeUpSimulatorsInArea(