1
0

TNT improvements

+ Added entity damage
+ Added entity propulsion
* Fixed #67 and fixed #230
This commit is contained in:
Tiger Wang 2014-02-02 19:16:38 +00:00
parent 275035eb70
commit e56d41175b
2 changed files with 116 additions and 37 deletions

View File

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

View File

@ -15,6 +15,9 @@
#include "Blocks/BlockHandler.h" #include "Blocks/BlockHandler.h"
#include "MobCensus.h" #include "MobCensus.h"
#include "MobSpawner.h" #include "MobSpawner.h"
#include "BoundingBox.h"
#include "Entities/Pickup.h"
#ifndef _WIN32 #ifndef _WIN32
#include <cstdlib> // abs #include <cstdlib> // abs
@ -1624,49 +1627,52 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
{ {
return; return;
} }
bool ShouldDestroyBlocks = true;
// Don't explode if the explosion center is inside a liquid block: // 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: ShouldDestroyBlocks = false;
case E_BLOCK_STATIONARY_WATER:
case E_BLOCK_LAVA:
case E_BLOCK_STATIONARY_LAVA:
{
return;
}
} }
cBlockArea area; int ExplosionSizeInt = (int)ceil(a_ExplosionSize);
int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt;
int bx = (int)floor(a_BlockX); int bx = (int)floor(a_BlockX);
int by = (int)floor(a_BlockY); int by = (int)floor(a_BlockY);
int bz = (int)floor(a_BlockZ); 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 MinY = std::max((int)floor(a_BlockY - ExplosionSizeInt), 0);
int MaxY = std::min((int)ceil(a_BlockY + ExplosionSizeInt), cChunkDef::Height - 1); int MaxY = std::min((int)ceil(a_BlockY + ExplosionSizeInt), cChunkDef::Height - 1);
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++) if (ShouldDestroyBlocks)
{ {
for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++) 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++)
{ {
if ((by + y >= cChunkDef::Height) || (by + y < 0)) for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++)
{ {
// Outside of the world if ((by + y >= cChunkDef::Height) || (by + y < 0))
continue;
}
for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++)
{
if ((x * x + y * y + z * z) > ExplosionSizeSq)
{ {
// Too far away // Outside of the world
continue; continue;
} }
for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++)
BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z);
switch (Block)
{ {
if ((x * x + y * y + z * z) > ExplosionSizeSq)
{
// Too far away
continue;
}
BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z);
switch (Block)
{
case E_BLOCK_TNT: case E_BLOCK_TNT:
{ {
// Activate the TNT, with a random fuse between 10 to 30 game ticks // Activate the TNT, with a random fuse between 10 to 30 game ticks
@ -1691,20 +1697,20 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_WATER); area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_WATER);
break; break;
} }
case E_BLOCK_STATIONARY_LAVA: case E_BLOCK_STATIONARY_LAVA:
{ {
// Turn into simulated lava: // Turn into simulated lava:
area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_LAVA); area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_LAVA);
break; break;
} }
case E_BLOCK_AIR: case E_BLOCK_AIR:
{ {
// No pickups for air // No pickups for air
break; break;
} }
default: default:
{ {
if (m_World->GetTickRandomNumber(100) <= 25) // 25% chance of pickups if (m_World->GetTickRandomNumber(100) <= 25) // 25% chance of pickups
@ -1713,16 +1719,88 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
cBlockHandler * Handler = BlockHandler(Block); cBlockHandler * Handler = BlockHandler(Block);
Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z)); // Stone becomes cobblestone, coal ore becomes coal, etc. Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z)); // Stone becomes cobblestone, coal ore becomes coal, etc.
m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z); //m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z);
} }
area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR); area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR);
a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z)); a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
} }
} // switch (BlockType) } // switch (BlockType)
} // for z } // for z
} // for y } // for y
} // for x } // for x
area.Write(m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt); 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
{
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
{
Vector3d AbsoluteEntityPos(abs(EntityPos.x), abs(EntityPos.y), abs(EntityPos.z));
Vector3d MaxExplosionBoundary(m_ExplosionSizeSq, m_ExplosionSizeSq, m_ExplosionSizeSq);
AbsoluteEntityPos -= m_ExplosionPos;
AbsoluteEntityPos = MaxExplosionBoundary - AbsoluteEntityPos;
double FinalDamage = ((AbsoluteEntityPos.x + AbsoluteEntityPos.y + AbsoluteEntityPos.z) / 3) * m_ExplosionSize;
FinalDamage = a_Entity->GetMaxHealth() - abs(FinalDamage);
if (FinalDamage > a_Entity->GetMaxHealth())
FinalDamage = a_Entity->GetMaxHealth();
else if (FinalDamage < 0)
return false;
if (!a_Entity->IsTNT())
{
a_Entity->TakeDamage(dtExplosion, NULL, (int)FinalDamage, 0);
}
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): // Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391):
WakeUpSimulatorsInArea( WakeUpSimulatorsInArea(