1
0
Fork 0

Merged branch 'Projectiles'.

This commit is contained in:
madmaxoft 2013-09-07 22:06:30 +02:00
commit d31142811d
27 changed files with 2418 additions and 205 deletions

View File

@ -1,6 +1,5 @@
-- Global variables
PLUGIN = {}; -- Reference to own plugin object
-- Global variables
g_DropSpensersToActivate = {}; -- A list of dispensers and droppers (as {World, X, Y Z} quadruplets) that are to be activated every tick
g_HungerReportTick = 10;
g_ShowFoodStats = false; -- When true, each player's food stats are sent to them every 10 ticks
@ -11,8 +10,6 @@ g_ShowFoodStats = false; -- When true, each player's food stats are sent to the
function Initialize(Plugin)
PLUGIN = Plugin
Plugin:SetName("Debuggers")
Plugin:SetVersion(1)
@ -46,6 +43,7 @@ function Initialize(Plugin)
PluginManager:BindCommand("/ench", "debuggers", HandleEnchCmd, "- Provides an instant dummy enchantment window");
PluginManager:BindCommand("/fs", "debuggers", HandleFoodStatsCmd, "- Turns regular foodstats message on or off");
PluginManager:BindCommand("/arr", "debuggers", HandleArrowCmd, "- Creates an arrow going away from the player");
PluginManager:BindCommand("/fb", "debuggers", HandleFireballCmd, "- Creates a ghast fireball as if shot by the player");
-- Enable the following line for BlockArea / Generator interface testing:
-- PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED);
@ -480,6 +478,7 @@ end
function OnWorldTick(a_World, a_Dt)
-- Report food stats, if switched on:
local Tick = a_World:GetWorldAge();
if (not(g_ShowFoodStats) or (math.mod(Tick, 10) ~= 0)) then
return false;
@ -825,3 +824,18 @@ end
function HandleFireballCmd(a_Split, a_Player)
local World = a_Player:GetWorld();
local Pos = a_Player:GetEyePosition();
local Speed = a_Player:GetLookVector();
Speed:Normalize();
Pos = Pos + Speed * 2;
World:CreateProjectile(Pos.x, Pos.y, Pos.z, cProjectileEntity.pkGhastFireball, a_Player, Speed * 10);
return true;
end

View File

@ -6,4 +6,4 @@ Release/
Profiling
*.png
world/
*.html
*.html

View File

@ -232,6 +232,18 @@ enum
AString PrintableAbsIntTriplet(int a_X, int a_Y, int a_Z, double a_Divisor = 32)
{
return Printf("<%d, %d, %d> ~ {%.02f, %.02f, %.02f}",
a_X, a_Y, a_Z,
(double)a_X / a_Divisor, (double)a_Y / a_Divisor, (double)a_Z / a_Divisor
);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cConnection:
@ -1481,7 +1493,7 @@ bool cConnection::HandleServerEntityRelativeMove(void)
HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dz);
Log("Received a PACKET_ENTITY_RELATIVE_MOVE from the server:");
Log(" EntityID = %d", EntityID);
Log(" RelMove = <%d, %d, %d>", dx, dy, dz);
Log(" RelMove = %s", PrintableAbsIntTriplet(dx, dy, dz).c_str());
COPY_TO_CLIENT();
return true;
}
@ -1500,7 +1512,7 @@ bool cConnection::HandleServerEntityRelativeMoveLook(void)
HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch);
Log("Received a PACKET_ENTITY_RELATIVE_MOVE_LOOK from the server:");
Log(" EntityID = %d", EntityID);
Log(" RelMove = <%d, %d, %d>", dx, dy, dz);
Log(" RelMove = %s", PrintableAbsIntTriplet(dx, dy, dz).c_str());
Log(" Yaw = %d", Yaw);
Log(" Pitch = %d", Pitch);
COPY_TO_CLIENT();
@ -1529,14 +1541,14 @@ bool cConnection::HandleServerEntityStatus(void)
bool cConnection::HandleServerEntityTeleport(void)
{
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockY);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, AbsX);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, AbsY);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, AbsZ);
HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw);
HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch);
Log("Received a PACKET_ENTITY_TELEPORT from the server:");
Log(" EntityID = %d", EntityID);
Log(" Pos = {%d, %d, %d}", BlockX, BlockY, BlockZ);
Log(" Pos = (%d, %d, %d) ~ {%.02f, %.02f, %.02f}", AbsX, AbsY, AbsZ, (double)AbsX / 32, (double)AbsY / 32, (double)AbsZ / 32);
Log(" Yaw = %d", Yaw);
Log(" Pitch = %d", Pitch);
COPY_TO_CLIENT();
@ -1555,7 +1567,7 @@ bool cConnection::HandleServerEntityVelocity(void)
HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityZ);
Log("Received a PACKET_ENTITY_VELOCITY from the server:");
Log(" EntityID = %d", EntityID);
Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ);
Log(" Velocity = %s", PrintableAbsIntTriplet(VelocityX, VelocityY, VelocityZ, 8000).c_str());
COPY_TO_CLIENT();
return true;
}
@ -1996,9 +2008,9 @@ bool cConnection::HandleServerSpawnMob(void)
Log("Received a PACKET_SPAWN_MOB from the server:");
Log(" EntityID = %d", EntityID);
Log(" MobType = %d", MobType);
Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32);
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
Log(" Angles = [%d, %d, %d]", Yaw, Pitch, HeadYaw);
Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ);
Log(" Velocity = %s", PrintableAbsIntTriplet(VelocityX, VelocityY, VelocityZ, 8000).c_str());
Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str());
LogMetadata(Metadata, 4);
COPY_TO_CLIENT();
@ -2029,7 +2041,7 @@ bool cConnection::HandleServerSpawnNamedEntity(void)
Log("Received a PACKET_SPAWN_NAMED_ENTITY from the server:");
Log(" EntityID = %d (0x%x)", EntityID, EntityID);
Log(" Name = %s", EntityName.c_str());
Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32);
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
Log(" Rotation = <yaw %d, pitch %d>", Yaw, Pitch);
Log(" CurrentItem = %d", CurrentItem);
Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str());
@ -2102,12 +2114,12 @@ bool cConnection::HandleServerSpawnObjectVehicle(void)
Log("Received a PACKET_SPAWN_OBJECT_VEHICLE from the server:");
Log(" EntityID = %d (0x%x)", EntityID, EntityID);
Log(" ObjType = %d (0x%x)", ObjType, ObjType);
Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32);
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
Log(" Rotation = <yaw %d, pitch %d>", Yaw, Pitch);
Log(" DataIndicator = %d (0x%x)", DataIndicator, DataIndicator);
if (DataIndicator != 0)
{
Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ);
Log(" Velocity = %s", PrintableAbsIntTriplet(VelocityX, VelocityY, VelocityZ, 8000).c_str());
DataLog(ExtraData.data(), ExtraData.size(), " ExtraData size = %d:", ExtraData.size());
}
COPY_TO_CLIENT();
@ -2129,7 +2141,7 @@ bool cConnection::HandleServerSpawnPainting(void)
Log("Received a PACKET_SPAWN_PAINTING from the server:");
Log(" EntityID = %d", EntityID);
Log(" ImageName = \"%s\"", ImageName.c_str());
Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32);
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
Log(" Direction = %d", Direction);
COPY_TO_CLIENT();
return true;
@ -2156,7 +2168,7 @@ bool cConnection::HandleServerSpawnPickup(void)
Log("Received a PACKET_SPAWN_PICKUP from the server:");
Log(" EntityID = %d", EntityID);
Log(" Item = %s", ItemDesc.c_str());
Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32);
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
Log(" Angles = [%d, %d, %d]", Rotation, Pitch, Roll);
COPY_TO_CLIENT();
return true;

View File

@ -386,6 +386,14 @@
RelativePath="..\source\BlockTracer.h"
>
</File>
<File
RelativePath="..\source\BoundingBox.cpp"
>
</File>
<File
RelativePath="..\source\BoundingBox.h"
>
</File>
<File
RelativePath="..\source\ByteBuffer.cpp"
>

View File

@ -53,6 +53,7 @@ $cfile "Vector3d.h"
$cfile "Vector3i.h"
$cfile "Matrix4f.h"
$cfile "Cuboid.h"
$cfile "BoundingBox.h"
$cfile "Tracer.h"
$cfile "Group.h"
$cfile "BlockArea.h"

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on 09/01/13 14:42:05.
** Generated automatically by tolua++-1.0.92 on 09/07/13 22:05:19.
*/
/* Exported function */

View File

@ -419,6 +419,7 @@ AString DamageTypeToString(eDamageType a_DamageType)
switch (a_DamageType)
{
case dtAttack: return "dtAttack";
case dtRangedAttack: return "dtRangedAttack";
case dtLightning: return "dtLightning";
case dtFalling: return "dtFalling";
case dtDrowning: return "dtDrowning";
@ -463,6 +464,7 @@ eDamageType StringToDamageType(const AString & a_DamageTypeString)
{
// Cannonical names:
{ dtAttack, "dtAttack"},
{ dtRangedAttack, "dtRangedAttack"},
{ dtLightning, "dtLightning"},
{ dtFalling, "dtFalling"},
{ dtDrowning, "dtDrowning"},
@ -478,23 +480,26 @@ eDamageType StringToDamageType(const AString & a_DamageTypeString)
{ dtAdmin, "dtAdmin"},
// Common synonyms:
{ dtPawnAttack, "dtAttack"},
{ dtEntityAttack, "dtAttack"},
{ dtMob, "dtAttack"},
{ dtMobAttack, "dtAttack"},
{ dtFall, "dtFalling"},
{ dtDrown, "dtDrowning"},
{ dtSuffocation, "dtSuffocating"},
{ dtStarvation, "dtStarving"},
{ dtHunger, "dtStarving"},
{ dtCactus, "dtCactusContact"},
{ dtCactuses, "dtCactusContact"},
{ dtCacti, "dtCactusContact"},
{ dtLava, "dtLavaContact"},
{ dtPoison, "dtPoisoning"},
{ dtBurning, "dtOnFire"},
{ dtInFire, "dtFireContact"},
{ dtPlugin, "dtAdmin"},
{ dtAttack, "dtPawnAttack"},
{ dtAttack, "dtEntityAttack"},
{ dtAttack, "dtMob"},
{ dtAttack, "dtMobAttack"},
{ dtRangedAttack, "dtArrowAttack"},
{ dtRangedAttack, "dtArrow"},
{ dtRangedAttack, "dtProjectile"},
{ dtFalling, "dtFall"},
{ dtDrowning, "dtDrown"},
{ dtSuffocating, "dtSuffocation"},
{ dtStarving, "dtStarvation"},
{ dtStarving, "dtHunger"},
{ dtCactusContact, "dtCactus"},
{ dtCactusContact, "dtCactuses"},
{ dtCactusContact, "dtCacti"},
{ dtLavaContact, "dtLava"},
{ dtPoisoning, "dtPoison"},
{ dtOnFire, "dtBurning"},
{ dtFireContact, "dtInFire"},
{ dtAdmin, "dtPlugin"},
} ;
for (int i = 0; i < ARRAYCOUNT(DamageTypeMap); i++)
{

View File

@ -659,6 +659,7 @@ enum eDamageType
{
// Canonical names for the types (as documented in the plugin wiki):
dtAttack, // Being attacked by a mob
dtRangedAttack, // Being attacked by a projectile, possibly from a mob
dtLightning, // Hit by a lightning strike
dtFalling, // Falling down; dealt when hitting the ground
dtDrowning, // Drowning in water / lava
@ -671,6 +672,7 @@ enum eDamageType
dtFireContact, // Standing inside a fire block
dtInVoid, // Falling into the Void (Y < 0)
dtPotionOfHarming,
dtEnderPearl, // Thrown an ender pearl, teleported by it
dtAdmin, // Damage applied by an admin command
// Some common synonyms:
@ -678,6 +680,9 @@ enum eDamageType
dtEntityAttack = dtAttack,
dtMob = dtAttack,
dtMobAttack = dtAttack,
dtArrowAttack = dtRangedAttack,
dtArrow = dtRangedAttack,
dtProjectile = dtRangedAttack,
dtFall = dtFalling,
dtDrown = dtDrowning,
dtSuffocation = dtSuffocating,

View File

@ -56,7 +56,7 @@ void cBlockBedHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, i
if (a_World->GetDimension() != dimOverworld)
{
Vector3i Coords(a_BlockX, a_BlockY, a_BlockZ);
a_World->DoExplosiontAt(5, a_BlockX, a_BlockY, a_BlockZ, true, esBed, &Coords);
a_World->DoExplosionAt(5, a_BlockX, a_BlockY, a_BlockZ, true, esBed, &Coords);
}
else
{

331
source/BoundingBox.cpp Normal file
View File

@ -0,0 +1,331 @@
// BoundingBox.cpp
// Implements the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords
#include "Globals.h"
#include "BoundingBox.h"
#include "Defines.h"
#if 0
/// A simple self-test that is executed on program start, used to verify bbox functionality
class SelfTest
{
public:
SelfTest(void)
{
Vector3d Min(1, 1, 1);
Vector3d Max(2, 2, 2);
Vector3d LineDefs[] =
{
Vector3d(1.5, 4, 1.5), Vector3d(1.5, 3, 1.5), // Should intersect at 2, face 1 (YP)
Vector3d(1.5, 0, 1.5), Vector3d(1.5, 4, 1.5), // Should intersect at 0.25, face 0 (YM)
Vector3d(0, 0, 0), Vector3d(2, 2, 2), // Should intersect at 0.5, face 0, 3 or 5 (anyM)
Vector3d(0.999, 0, 1.5), Vector3d(0.999, 4, 1.5), // Should not intersect
Vector3d(1.999, 0, 1.5), Vector3d(1.999, 4, 1.5), // Should intersect at 0.25, face 0 (YM)
Vector3d(2.001, 0, 1.5), Vector3d(2.001, 4, 1.5), // Should not intersect
} ;
for (int i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++)
{
double LineCoeff;
char Face;
Vector3d Line1 = LineDefs[2 * i];
Vector3d Line2 = LineDefs[2 * i + 1];
bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face);
printf("LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n",
Line1.x, Line1.y, Line1.z,
Line2.x, Line2.y, Line2.z,
res ? 1 : 0, LineCoeff, Face
);
} // for i - LineDefs[]
printf("BoundingBox selftest complete.");
}
} Test;
#endif
cBoundingBox::cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ) :
m_Min(a_MinX, a_MinY, a_MinZ),
m_Max(a_MaxX, a_MaxY, a_MaxZ)
{
}
cBoundingBox::cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max) :
m_Min(a_Min),
m_Max(a_Max)
{
}
cBoundingBox::cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height) :
m_Min(a_Pos.x - a_Radius, a_Pos.y, a_Pos.z - a_Radius),
m_Max(a_Pos.x + a_Radius, a_Pos.y + a_Height, a_Pos.z + a_Radius)
{
}
cBoundingBox::cBoundingBox(const cBoundingBox & a_Orig) :
m_Min(a_Orig.m_Min),
m_Max(a_Orig.m_Max)
{
}
void cBoundingBox::Move(double a_OffX, double a_OffY, double a_OffZ)
{
m_Min.x += a_OffX;
m_Min.y += a_OffY;
m_Min.z += a_OffZ;
m_Max.x += a_OffX;
m_Max.y += a_OffY;
m_Max.z += a_OffZ;
}
void cBoundingBox::Move(const Vector3d & a_Off)
{
m_Min.x += a_Off.x;
m_Min.y += a_Off.y;
m_Min.z += a_Off.z;
m_Max.x += a_Off.x;
m_Max.y += a_Off.y;
m_Max.z += a_Off.z;
}
void cBoundingBox::Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ)
{
m_Min.x -= a_ExpandX;
m_Min.y -= a_ExpandY;
m_Min.z -= a_ExpandZ;
m_Max.x += a_ExpandX;
m_Max.y += a_ExpandY;
m_Max.z += a_ExpandZ;
}
bool cBoundingBox::DoesIntersect(const cBoundingBox & a_Other)
{
return (
((a_Other.m_Min.x <= m_Max.x) && (a_Other.m_Max.x >= m_Min.x)) && // X coords intersect
((a_Other.m_Min.y <= m_Max.y) && (a_Other.m_Max.y >= m_Min.y)) && // Y coords intersect
((a_Other.m_Min.z <= m_Max.z) && (a_Other.m_Max.z >= m_Min.z)) // Z coords intersect
);
}
cBoundingBox cBoundingBox::Union(const cBoundingBox & a_Other)
{
return cBoundingBox(
std::min(m_Min.x, a_Other.m_Min.x),
std::min(m_Min.y, a_Other.m_Min.y),
std::min(m_Min.z, a_Other.m_Min.z),
std::max(m_Max.x, a_Other.m_Max.x),
std::max(m_Max.y, a_Other.m_Max.y),
std::max(m_Max.z, a_Other.m_Max.z)
);
}
bool cBoundingBox::IsInside(const Vector3d & a_Point)
{
return IsInside(m_Min, m_Max, a_Point);
}
bool cBoundingBox::IsInside(double a_X, double a_Y,double a_Z)
{
return IsInside(m_Min, m_Max, a_X, a_Y, a_Z);
}
bool cBoundingBox::IsInside(cBoundingBox & a_Other)
{
// If both a_Other's coords are inside this, then the entire a_Other is inside
return (IsInside(a_Other.m_Min) && IsInside(a_Other.m_Max));
}
bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max)
{
// If both coords are inside this, then the entire a_Other is inside
return (IsInside(a_Min) && IsInside(a_Max));
}
bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point)
{
return (
((a_Point.x >= a_Min.x) && (a_Point.x <= a_Max.x)) &&
((a_Point.y >= a_Min.y) && (a_Point.y <= a_Max.y)) &&
((a_Point.z >= a_Min.z) && (a_Point.z <= a_Max.z))
);
}
bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z)
{
return (
((a_X >= a_Min.x) && (a_X <= a_Max.x)) &&
((a_Y >= a_Min.y) && (a_Y <= a_Max.y)) &&
((a_Z >= a_Min.z) && (a_Z <= a_Max.z))
);
}
bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face)
{
return CalcLineIntersection(m_Min, m_Max, a_Line1, a_Line2, a_LineCoeff, a_Face);
}
bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face)
{
if (IsInside(a_Min, a_Max, a_Line1))
{
// The starting point is inside the bounding box.
a_LineCoeff = 0;
a_Face = BLOCK_FACE_YM; // Make it look as the top face was hit, although none really are.
return true;
}
char Face = 0;
double Coeff = Vector3d::NO_INTERSECTION;
// Check each individual bbox face for intersection with the line, remember the one with the lowest coeff
double c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Min.z);
if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
{
Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM;
Coeff = c;
}
c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Max.z);
if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
{
Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM;
Coeff = c;
}
c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Min.y);
if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
{
Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM;
Coeff = c;
}
c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Max.y);
if ((c >= 0) && (c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
{
Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM;
Coeff = c;
}
c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Min.x);
if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
{
Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM;
Coeff = c;
}
c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Max.x);
if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
{
Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM;
Coeff = c;
}
if (Coeff >= Vector3d::NO_INTERSECTION)
{
// There has been no intersection
return false;
}
a_LineCoeff = Coeff;
a_Face = Face;
return true;
}
bool cBoundingBox::Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection)
{
a_Intersection.m_Min.x = std::max(m_Min.x, a_Other.m_Min.x);
a_Intersection.m_Max.x = std::min(m_Max.x, a_Other.m_Max.x);
if (a_Intersection.m_Min.x >= a_Intersection.m_Max.x)
{
return false;
}
a_Intersection.m_Min.y = std::max(m_Min.y, a_Other.m_Min.y);
a_Intersection.m_Max.y = std::min(m_Max.y, a_Other.m_Max.y);
if (a_Intersection.m_Min.y >= a_Intersection.m_Max.y)
{
return false;
}
a_Intersection.m_Min.z = std::max(m_Min.z, a_Other.m_Min.z);
a_Intersection.m_Max.z = std::min(m_Max.z, a_Other.m_Max.z);
if (a_Intersection.m_Min.z >= a_Intersection.m_Max.z)
{
return false;
}
return true;
}

90
source/BoundingBox.h Normal file
View File

@ -0,0 +1,90 @@
// BoundingBox.h
// Declares the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords
#pragma once
#include "Vector3d.h"
// tolua_begin
/** Represents two sets of coords, minimum and maximum for each direction.
All the coords within those limits (inclusive the edges) are considered "inside" the box.
For intersection purposes, though, if the intersection is "sharp" in any coord (i. e. zero volume),
the boxes are considered non-intersecting.
*/
class cBoundingBox
{
public:
cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ);
cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max);
cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height);
cBoundingBox(const cBoundingBox & a_Orig);
/// Moves the entire boundingbox by the specified offset
void Move(double a_OffX, double a_OffY, double a_OffZ);
/// Moves the entire boundingbox by the specified offset
void Move(const Vector3d & a_Off);
/// Expands the bounding box by the specified amount in each direction (so the box becomes larger by 2 * Expand in each direction)
void Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ);
/// Returns true if the two bounding boxes intersect
bool DoesIntersect(const cBoundingBox & a_Other);
/// Returns the union of the two bounding boxes
cBoundingBox Union(const cBoundingBox & a_Other);
/// Returns true if the point is inside the bounding box
bool IsInside(const Vector3d & a_Point);
/// Returns true if the point is inside the bounding box
bool IsInside(double a_X, double a_Y,double a_Z);
/// Returns true if a_Other is inside this bounding box
bool IsInside(cBoundingBox & a_Other);
/// Returns true if a boundingbox specified by a_Min and a_Max is inside this bounding box
bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max);
/// Returns true if the specified point is inside the bounding box specified by its min/max corners
static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point);
/// Returns true if the specified point is inside the bounding box specified by its min/max corners
static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z);
/** Returns true if this bounding box is intersected by the line specified by its two points
Also calculates the distance along the line in which the intersection occurs (0 .. 1)
Only forward collisions (a_LineCoeff >= 0) are returned.
*/
bool CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face);
/** Returns true if the specified bounding box is intersected by the line specified by its two points
Also calculates the distance along the line in which the intersection occurs (0 .. 1) and the face hit (BLOCK_FACE_ constants)
Only forward collisions (a_LineCoeff >= 0) are returned.
*/
static bool CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face);
// tolua_end
/// Calculates the intersection of the two bounding boxes; returns true if nonempty
bool Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection);
protected:
Vector3d m_Min;
Vector3d m_Max;
} ; // tolua_export

View File

@ -1562,7 +1562,7 @@ bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback
void cChunkMap::DoExplosiontAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlocksAffected)
void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlocksAffected)
{
// Don't explode if outside of Y range (prevents the following test running into unallocated memory):
if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height - 1))

View File

@ -184,7 +184,7 @@ public:
bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible
/// Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates
void DoExplosiontAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected);
void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected);
/// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false.
bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Lua-accessible

View File

@ -43,16 +43,24 @@ extern bool g_BlockIsSolid[256];
/// Block face constants, used in PlayerDigging and PlayerBlockPlacement packets
enum
/// Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc
enum eBlockFace
{
BLOCK_FACE_NONE = -1, // Interacting with no block face - swinging the item in the air
BLOCK_FACE_BOTTOM = 0, // Interacting with the bottom face of the block (YM)
BLOCK_FACE_TOP = 1, // Interacting with the top face of the block (YP)
BLOCK_FACE_NORTH = 2, // Interacting with the northern face of the block (ZP)
BLOCK_FACE_SOUTH = 3, // Interacting with the southern face of the block (ZM)
BLOCK_FACE_WEST = 4, // Interacting with the western face of the block (XP)
BLOCK_FACE_EAST = 5, // Interacting with the eastern face of the block (XM)
BLOCK_FACE_NONE = -1, // Interacting with no block face - swinging the item in the air
BLOCK_FACE_XM = 5, // Interacting with the X- face of the block
BLOCK_FACE_XP = 4, // Interacting with the X+ face of the block
BLOCK_FACE_YM = 0, // Interacting with the Y- face of the block
BLOCK_FACE_YP = 1, // Interacting with the Y+ face of the block
BLOCK_FACE_ZM = 3, // Interacting with the Z- face of the block
BLOCK_FACE_ZP = 2, // Interacting with the Z+ face of the block
// Synonyms using the (deprecated) world directions:
BLOCK_FACE_BOTTOM = BLOCK_FACE_YM, // Interacting with the bottom face of the block
BLOCK_FACE_TOP = BLOCK_FACE_YP, // Interacting with the top face of the block
BLOCK_FACE_NORTH = BLOCK_FACE_ZP, // Interacting with the northern face of the block
BLOCK_FACE_SOUTH = BLOCK_FACE_ZM, // Interacting with the southern face of the block
BLOCK_FACE_WEST = BLOCK_FACE_XP, // Interacting with the western face of the block
BLOCK_FACE_EAST = BLOCK_FACE_XM, // Interacting with the eastern face of the block
} ;
@ -345,8 +353,7 @@ inline void AddFaceDirection(int & a_BlockX, unsigned char & a_BlockY, int & a_B
#include <math.h>
#define PI 3.14159265358979323846264338327950288419716939937510582097494459072381640628620899862803482534211706798f
#define PI 3.14159265358979323846264338327950288419716939937510582097494459072381640628620899862803482534211706798f
inline void EulerToVector(double a_Pan, double a_Pitch, double & a_X, double & a_Y, double & a_Z)
{

View File

@ -253,6 +253,39 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R
void cEntity::SetRotationFromSpeed(void)
{
const double EPS = 0.0000001;
if ((abs(m_Speed.x) < EPS) && (abs(m_Speed.z) < EPS))
{
// atan2() may overflow or is undefined, pick any number
SetRotation(0);
return;
}
SetRotation(atan2(m_Speed.x, m_Speed.z) * 180 / PI);
}
void cEntity::SetPitchFromSpeed(void)
{
const double EPS = 0.0000001;
double xz = sqrt(m_Speed.x * m_Speed.x + m_Speed.z * m_Speed.z); // Speed XZ-plane component
if ((abs(xz) < EPS) && (abs(m_Speed.y) < EPS))
{
// atan2() may overflow or is undefined, pick any number
SetPitch(0);
return;
}
SetPitch(atan2(m_Speed.y, xz) * 180 / PI);
}
void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI))

View File

@ -203,6 +203,16 @@ public:
/// Makes this entity take the specified damage. The values are packed into a TDI, knockback calculated, then sent through DoTakeDamage()
void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount);
float GetGravity(void) const { return m_Gravity; }
void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; }
/// Sets the rotation to match the speed vector (entity goes "face-forward")
void SetRotationFromSpeed(void);
/// Sets the pitch to match the speed vector (entity gies "face-forward")
void SetPitchFromSpeed(void);
// tolua_end
/// Makes this entity take damage specified in the a_TDI. The TDI is sent through plugins first, then applied

View File

@ -8,6 +8,16 @@
#include "../ClientHandle.h"
#include "Player.h"
#include "../LineBlockTracer.h"
#include "../BoundingBox.h"
#include "../ChunkMap.h"
#include "../Chunk.h"
/// Converts an angle in radians into a byte representation used by the network protocol
#define ANGLE_TO_PROTO(X) (Byte)(X * 255 / 360)
@ -21,20 +31,49 @@ class cProjectileTracerCallback :
{
public:
cProjectileTracerCallback(cProjectileEntity * a_Projectile) :
m_Projectile(a_Projectile)
m_Projectile(a_Projectile),
m_SlowdownCoeff(0.99) // Default slowdown when not in water
{
}
double GetSlowdownCoeff(void) const { return m_SlowdownCoeff; }
protected:
cProjectileEntity * m_Projectile;
double m_SlowdownCoeff;
// cCallbacks overrides:
virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override
{
/*
// DEBUG:
LOGD("Hit block %d:%d at {%d, %d, %d} face %d, %s (%s)",
a_BlockType, a_BlockMeta,
a_BlockX, a_BlockY, a_BlockZ, a_EntryFace,
g_BlockIsSolid[a_BlockType] ? "solid" : "non-solid",
ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str()
);
*/
if (g_BlockIsSolid[a_BlockType])
{
// The projectile hit a solid block
m_Projectile->OnHitSolidBlock(a_BlockX, a_BlockY, a_BlockZ, a_EntryFace);
return true;
// Calculate the exact hit coords:
cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1);
Vector3d Line1 = m_Projectile->GetPosition();
Vector3d Line2 = Line1 + m_Projectile->GetSpeed();
double LineCoeff = 0;
char Face;
if (bb.CalcLineIntersection(Line1, Line2, LineCoeff, Face))
{
Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff;
m_Projectile->OnHitSolidBlock(Intersection, Face);
return true;
}
else
{
LOGD("WEIRD! block tracer reports a hit, but BBox tracer doesn't. Ignoring the hit.");
}
}
// Convey some special effects from special blocks:
@ -44,12 +83,14 @@ protected:
case E_BLOCK_STATIONARY_LAVA:
{
m_Projectile->StartBurning(30);
m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.9); // Slow down to 0.9* the speed each tick when moving through lava
break;
}
case E_BLOCK_WATER:
case E_BLOCK_STATIONARY_WATER:
{
m_Projectile->StopBurning();
m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.8); // Slow down to 0.8* the speed each tick when moving through water
break;
}
} // switch (a_BlockType)
@ -63,6 +104,86 @@ protected:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProjectileEntityCollisionCallback:
class cProjectileEntityCollisionCallback :
public cEntityCallback
{
public:
cProjectileEntityCollisionCallback(cProjectileEntity * a_Projectile, const Vector3d & a_Pos, const Vector3d & a_NextPos) :
m_Projectile(a_Projectile),
m_Pos(a_Pos),
m_NextPos(a_NextPos),
m_MinCoeff(1),
m_HitEntity(NULL)
{
}
virtual bool Item(cEntity * a_Entity) override
{
if (
(a_Entity == m_Projectile) || // Do not check collisions with self
(a_Entity == m_Projectile->GetCreator()) // Do not check whoever shot the projectile
)
{
// TODO: Don't check creator only for the first 5 ticks
// so that arrows stuck in ground and dug up can hurt the player
return false;
}
cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight());
// Instead of colliding the bounding box with another bounding box in motion, we collide an enlarged bounding box with a hairline.
// The results should be good enough for our purposes
double LineCoeff;
char Face;
EntBox.Expand(m_Projectile->GetWidth() / 2, m_Projectile->GetHeight() / 2, m_Projectile->GetWidth() / 2);
if (!EntBox.CalcLineIntersection(m_Pos, m_NextPos, LineCoeff, Face))
{
// No intersection whatsoever
return false;
}
// TODO: Some entities don't interact with the projectiles (pickups, falling blocks)
// TODO: Allow plugins to interfere about which entities can be hit
if (LineCoeff < m_MinCoeff)
{
// The entity is closer than anything we've stored so far, replace it as the potential victim
m_MinCoeff = LineCoeff;
m_HitEntity = a_Entity;
}
// Don't break the enumeration, we want all the entities
return false;
}
/// Returns the nearest entity that was hit, after the enumeration has been completed
cEntity * GetHitEntity(void) const { return m_HitEntity; }
/// Returns the line coeff where the hit was encountered, after the enumeration has been completed
double GetMinCoeff(void) const { return m_MinCoeff; }
/// Returns true if the callback has encountered a true hit
bool HasHit(void) const { return (m_MinCoeff < 1); }
protected:
cProjectileEntity * m_Projectile;
const Vector3d & m_Pos;
const Vector3d & m_NextPos;
double m_MinCoeff; // The coefficient of the nearest hit on the Pos line
// Although it's bad(tm) to store entity ptrs from a callback, we can afford it here, because the entire callback
// is processed inside the tick thread, so the entities won't be removed in between the calls and the final processing
cEntity * m_HitEntity; // The nearest hit entity
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProjectileEntity:
@ -85,6 +206,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve
m_IsInGround(false)
{
SetSpeed(a_Speed);
SetRotationFromSpeed();
SetPitchFromSpeed();
}
@ -101,10 +224,12 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator,
switch (a_Kind)
{
case pkArrow: return new cArrowEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkEgg: return new cThrownEggEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkEnderPearl: return new cThrownEnderPearlEntity(a_Creator, a_X, a_Y, a_Z, Speed);
case pkSnowball: return new cThrownSnowballEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkArrow: return new cArrowEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkEgg: return new cThrownEggEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkEnderPearl: return new cThrownEnderPearlEntity(a_Creator, a_X, a_Y, a_Z, Speed);
case pkSnowball: return new cThrownSnowballEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed);
// TODO: the rest
}
@ -116,26 +241,17 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator,
void cProjectileEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
void cProjectileEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
// TODO: Set proper position based on what face was hit
switch (a_BlockFace)
{
case BLOCK_FACE_TOP: SetPosition(0.5 + a_BlockX, 1.0 + a_BlockY, 0.5 + a_BlockZ); break;
case BLOCK_FACE_BOTTOM: SetPosition(0.5 + a_BlockX, a_BlockY, 0.5 + a_BlockZ); break;
case BLOCK_FACE_EAST: SetPosition( a_BlockX, 0.5 + a_BlockY, 0.5 + a_BlockZ); break;
case BLOCK_FACE_WEST: SetPosition(1.0 + a_BlockX, 0.5 + a_BlockY, 0.5 + a_BlockZ); break;
case BLOCK_FACE_NORTH: SetPosition(0.5 + a_BlockX, 0.5 + a_BlockY, 1.0 + a_BlockZ); break;
case BLOCK_FACE_SOUTH: SetPosition(0.5 + a_BlockX, 0.5 + a_BlockY, a_BlockZ); break;
case BLOCK_FACE_NONE: SetPosition(0.5 + a_BlockX, 0.5 + a_BlockY, 0.5 + a_BlockZ); break;
}
// Set the position based on what face was hit:
SetPosition(a_HitPos);
SetSpeed(0, 0, 0);
// DEBUG:
LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, hit solid block at face %d",
m_UniqueID,
GetPosX(), GetPosY(), GetPosZ(),
a_BlockFace
a_HitPos.x, a_HitPos.y, a_HitPos.z,
a_HitFace
);
m_IsInGround = true;
@ -192,20 +308,51 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
// Trace the tick's worth of movement as a line:
Vector3d NextPos = Pos + PerTickSpeed;
cProjectileTracerCallback TracerCallback(this);
if (cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
{
// Nothing in the way, update the position
SetPosition(NextPos);
// Something has been hit, abort all other processing
return;
}
// The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff
// Add gravity effect to the vertical speed component:
SetSpeedY(GetSpeedY() + m_Gravity / 20);
// Test for entity collisions:
cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos);
a_Chunk.ForEachEntity(EntityCollisionCallback);
if (EntityCollisionCallback.HasHit())
{
// An entity was hit:
Vector3d HitPos = Pos + (NextPos - Pos) * EntityCollisionCallback.GetMinCoeff();
// DEBUG:
LOGD("Projectile %d has hit an entity %d (%s) at {%.02f, %.02f, %.02f} (coeff %.03f)",
m_UniqueID,
EntityCollisionCallback.GetHitEntity()->GetUniqueID(),
EntityCollisionCallback.GetHitEntity()->GetClass(),
HitPos.x, HitPos.y, HitPos.z,
EntityCollisionCallback.GetMinCoeff()
);
OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos);
}
// TODO: Test the entities in the neighboring chunks, too
// Update the position:
SetPosition(NextPos);
// Add slowdown and gravity effect to the speed:
Vector3d NewSpeed(GetSpeed());
NewSpeed.y += m_Gravity / 20;
NewSpeed *= TracerCallback.GetSlowdownCoeff();
SetSpeed(NewSpeed);
SetRotationFromSpeed();
SetPitchFromSpeed();
// DEBUG:
LOGD("Arrow %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}",
LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}",
m_UniqueID,
GetPosX(), GetPosY(), GetPosZ(),
GetSpeedX(), GetSpeedY(), GetSpeedZ()
GetSpeedX(), GetSpeedY(), GetSpeedZ(),
GetRotation(), GetPitch()
);
}
@ -216,7 +363,8 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
void cProjectileEntity::SpawnOn(cClientHandle & a_Client)
{
// Default spawning - use the projectile kind to spawn an object:
a_Client.SendSpawnObject(*this, m_ProjectileKind, 0, 0, 0);
a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetRotation()), ANGLE_TO_PROTO(GetPitch()));
a_Client.SendEntityMetadata(*this);
}
@ -229,12 +377,16 @@ void cProjectileEntity::SpawnOn(cClientHandle & a_Client)
cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5),
m_PickupState(psNoPickup),
m_DamageCoeff(2)
m_DamageCoeff(2),
m_IsCritical(false)
{
SetSpeed(a_Speed);
SetMass(0.1);
LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f}",
m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ()
SetRotationFromSpeed();
SetPitchFromSpeed();
LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}",
m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(),
GetRotation(), GetPitch()
);
}
@ -245,7 +397,8 @@ cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a
cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) :
super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5),
m_PickupState(psInSurvivalOrCreative),
m_DamageCoeff(2)
m_DamageCoeff(2),
m_IsCritical((a_Force >= 1))
{
}
@ -269,14 +422,43 @@ bool cArrowEntity::CanPickup(const cPlayer & a_Player) const
void cArrowEntity::SpawnOn(cClientHandle & a_Client)
void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
a_Client.SendSpawnObject(*this, pkArrow, 0, 0, 0);
super::OnHitSolidBlock(a_HitPos, a_HitFace);
// Broadcast the position and speed packets before teleporting:
BroadcastMovementUpdate();
// Teleport the entity to the exact hit coords:
m_World->BroadcastTeleportEntity(*this);
}
void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer())
{
// Not an entity that interacts with an arrow
return;
}
int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5);
if (m_IsCritical)
{
Damage += m_World->GetTickRandomNumber(Damage / 2 + 2);
}
a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
Destroy();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cThrownEggEntity:
@ -290,7 +472,7 @@ cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y,
void cThrownEggEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
// TODO: Random-spawn a chicken or four
@ -314,9 +496,15 @@ cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X
void cThrownEnderPearlEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
// TODO: Teleport the creator here, make them take 5 damage
// Teleport the creator here, make them take 5 damage:
if (m_Creator != NULL)
{
// TODO: The coords might need some tweaking based on the block face
m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5);
m_Creator->TakeDamage(dtEnderPearl, this, 5, 0);
}
Destroy();
}
@ -338,7 +526,7 @@ cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, do
void cThrownSnowballEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
// TODO: Apply damage to certain mobs (blaze etc.) and anger all mobs
@ -349,3 +537,94 @@ void cThrownSnowballEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_Bl
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cGhastFireballEntity :
cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkGhastFireball, a_Creator, a_X, a_Y, a_Z, 1, 1)
{
SetSpeed(a_Speed);
SetGravity(0);
}
void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
{
m_World->DoExplosionAt(1, a_BlockX, a_BlockY, a_BlockZ, true, esGhastFireball, this);
}
void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
}
void cGhastFireballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFireChargeEntity :
cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125)
{
SetSpeed(a_Speed);
SetGravity(0);
}
void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
{
if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR)
{
m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1);
}
}
void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
}
void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
// TODO: Some entities are immune to hits
a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning
}

View File

@ -47,8 +47,11 @@ public:
static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed = NULL);
/// Called by the physics blocktracer when the entity hits a solid block, the block's coords and the face hit is given
virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace);
/// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace);
/// Called by the physics blocktracer when the entity hits another entity
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) {}
// tolua_begin
@ -64,6 +67,11 @@ public:
/// Returns true if the projectile has hit the ground and is stuck there
bool IsInGround(void) const { return m_IsInGround; }
// tolua_end
/// Sets the internal InGround flag. To be used by MCA loader only!
void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; }
protected:
eKind m_ProjectileKind;
@ -73,8 +81,6 @@ protected:
/// True if the projectile has hit the ground and is stuck there
bool m_IsInGround;
// tolua_end
// cEntity overrides:
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
@ -128,6 +134,12 @@ public:
/// Returns true if the specified player can pick the arrow up
bool CanPickup(const cPlayer & a_Player) const;
/// Returns true if the arrow is set as critical
bool IsCritical(void) const { return m_IsCritical; }
/// Sets the IsCritical flag
void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; }
// tolua_end
protected:
@ -137,9 +149,13 @@ protected:
/// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow
double m_DamageCoeff;
/// If true, the arrow deals more damage
bool m_IsCritical;
// cProjectileEntity overrides:
virtual void SpawnOn(cClientHandle & a_Client) override;
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// tolua_begin
} ;
@ -166,7 +182,7 @@ protected:
// tolua_end
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override;
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
// tolua_begin
@ -194,7 +210,7 @@ protected:
// tolua_end
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override;
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
// tolua_begin
@ -218,11 +234,9 @@ public:
cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// tolua_end
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override;
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
// tolua_begin
@ -232,6 +246,65 @@ protected:
class cGhastFireballEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cGhastFireballEntity);
cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// TODO: Deflecting the fireballs by arrow- or sword- hits
// tolua_begin
} ;
class cFireChargeEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cFireChargeEntity);
cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// tolua_begin
} ;
// tolua_end

View File

@ -52,7 +52,7 @@ void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
Destroy(true);
LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ());
m_World->DoExplosiontAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this);
m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this);
return;
}
}

View File

@ -8,16 +8,70 @@
Vector3d::Vector3d(const Vector3f & v )
: x( v.x )
, y( v.y )
, z( v.z )
const double Vector3d::EPS = 0.000001; ///< The max difference between two coords for which the coords are assumed equal
const double Vector3d::NO_INTERSECTION = 1e70; ///< Return value of LineCoeffToPlane() if the line is parallel to the plane
Vector3d::Vector3d(const Vector3f & v) :
x(v.x),
y(v.y),
z(v.z)
{
}
Vector3d::Vector3d(const Vector3f * v )
: x( v->x )
, y( v->y )
, z( v->z )
Vector3d::Vector3d(const Vector3f * v) :
x(v->x),
y(v->y),
z(v->z)
{
}
}
double Vector3d::LineCoeffToXYPlane(const Vector3d & a_OtherEnd, double a_Z) const
{
if (abs(z - a_OtherEnd.z) < EPS)
{
return NO_INTERSECTION;
}
return (a_Z - z) / (a_OtherEnd.z - z);
}
double Vector3d::LineCoeffToXZPlane(const Vector3d & a_OtherEnd, double a_Y) const
{
if (abs(y - a_OtherEnd.y) < EPS)
{
return NO_INTERSECTION;
}
return (a_Y - y) / (a_OtherEnd.y - y);
}
double Vector3d::LineCoeffToYZPlane(const Vector3d & a_OtherEnd, double a_X) const
{
if (abs(x - a_OtherEnd.x) < EPS)
{
return NO_INTERSECTION;
}
return (a_X - x) / (a_OtherEnd.x - x);
}

View File

@ -3,26 +3,54 @@
#include <math.h>
class Vector3f;
class Vector3d // tolua_export
{ // tolua_export
public: // tolua_export
// tolua_begin
class Vector3d
{
public:
// convert from float
Vector3d(const Vector3f & v ); // tolua_export
Vector3d(const Vector3f * v ); // tolua_export
Vector3d(const Vector3f & v);
Vector3d(const Vector3f * v);
Vector3d() : x(0), y(0), z(0) {} // tolua_export
Vector3d(double a_x, double a_y, double a_z) : x(a_x), y(a_y), z(a_z) {} // tolua_export
Vector3d() : x(0), y(0), z(0) {}
Vector3d(double a_x, double a_y, double a_z) : x(a_x), y(a_y), z(a_z) {}
inline void Set(double a_x, double a_y, double a_z) { x = a_x, y = a_y, z = a_z; } // tolua_export
inline void Normalize() { double l = 1.0f / Length(); x *= l; y *= l; z *= l; } // tolua_export
inline Vector3d NormalizeCopy() { double l = 1.0f / Length(); return Vector3d( x * l, y * l, z * l ); } // tolua_export
inline void NormalizeCopy(Vector3d & a_V) { double l = 1.0f / Length(); a_V.Set(x*l, y*l, z*l ); } // tolua_export
inline double Length() const { return (double)sqrt( x * x + y * y + z * z ); } // tolua_export
inline double SqrLength() const { return x * x + y * y + z * z; } // tolua_export
inline double Dot( const Vector3d & a_V ) const { return x * a_V.x + y * a_V.y + z * a_V.z; } // tolua_export
inline Vector3d Cross( const Vector3d & v ) const { return Vector3d( y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x ); } // tolua_export
inline void Set(double a_x, double a_y, double a_z) { x = a_x, y = a_y, z = a_z; }
inline void Normalize() { double l = 1.0f / Length(); x *= l; y *= l; z *= l; }
inline Vector3d NormalizeCopy() { double l = 1.0f / Length(); return Vector3d( x * l, y * l, z * l ); }
inline void NormalizeCopy(Vector3d & a_V) { double l = 1.0f / Length(); a_V.Set(x*l, y*l, z*l ); }
inline double Length() const { return (double)sqrt( x * x + y * y + z * z ); }
inline double SqrLength() const { return x * x + y * y + z * z; }
inline double Dot( const Vector3d & a_V ) const { return x * a_V.x + y * a_V.y + z * a_V.z; }
inline Vector3d Cross( const Vector3d & v ) const { return Vector3d( y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x ); }
/** Returns the coefficient for the (a_OtherEnd - this) line to reach the specified Z coord
The result satisfies the following equation:
(*this + Result * (a_OtherEnd - *this)).z = a_Z
If the line is too close to being parallel, this function returns NO_INTERSECTION
*/
double LineCoeffToXYPlane(const Vector3d & a_OtherEnd, double a_Z) const;
inline bool Equals( const Vector3d & v ) const { return (x == v.x && y == v.y && z == v.z ); } // tolua_export
/** Returns the coefficient for the (a_OtherEnd - this) line to reach the specified Y coord
The result satisfies the following equation:
(*this + Result * (a_OtherEnd - *this)).y = a_Y
If the line is too close to being parallel, this function returns NO_INTERSECTION
*/
double LineCoeffToXZPlane(const Vector3d & a_OtherEnd, double a_Y) const;
/** Returns the coefficient for the (a_OtherEnd - this) line to reach the specified X coord
The result satisfies the following equation:
(*this + Result * (a_OtherEnd - *this)).x = a_X
If the line is too close to being parallel, this function returns NO_INTERSECTION
*/
double LineCoeffToYZPlane(const Vector3d & a_OtherEnd, double a_X) const;
inline bool Equals(const Vector3d & v) const { return ((x == v.x) && (y == v.y) && (z == v.z)); }
// tolua_end
void operator += ( const Vector3d& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; }
void operator += ( Vector3d* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; }
@ -30,14 +58,24 @@ public: // tolua_export
void operator -= ( Vector3d* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; }
void operator *= ( double a_f ) { x *= a_f; y *= a_f; z *= a_f; }
Vector3d operator + (const Vector3d & v2) const { return Vector3d(x + v2.x, y + v2.y, z + v2.z ); } // tolua_export
Vector3d operator + (const Vector3d * v2) const { return Vector3d(x + v2->x, y + v2->y, z + v2->z ); } // tolua_export
Vector3d operator - (const Vector3d & v2) const { return Vector3d(x - v2.x, y - v2.y, z - v2.z ); } // tolua_export
Vector3d operator - (const Vector3d * v2) const { return Vector3d(x - v2->x, y - v2->y, z - v2->z ); } // tolua_export
Vector3d operator * (const double f) const { return Vector3d(x * f, y * f, z * f ); } // tolua_export
Vector3d operator * (const Vector3d & v2) const { return Vector3d(x * v2.x, y * v2.y, z * v2.z ); } // tolua_export
Vector3d operator / (const double f) const { return Vector3d(x / f, y / f, z / f ); } // tolua_export
// tolua_begin
Vector3d operator + (const Vector3d & v2) const { return Vector3d(x + v2.x, y + v2.y, z + v2.z ); }
Vector3d operator + (const Vector3d * v2) const { return Vector3d(x + v2->x, y + v2->y, z + v2->z ); }
Vector3d operator - (const Vector3d & v2) const { return Vector3d(x - v2.x, y - v2.y, z - v2.z ); }
Vector3d operator - (const Vector3d * v2) const { return Vector3d(x - v2->x, y - v2->y, z - v2->z ); }
Vector3d operator * (const double f) const { return Vector3d(x * f, y * f, z * f ); }
Vector3d operator * (const Vector3d & v2) const { return Vector3d(x * v2.x, y * v2.y, z * v2.z ); }
Vector3d operator / (const double f) const { return Vector3d(x / f, y / f, z / f ); }
double x, y, z;
static const double EPS; ///< The max difference between two coords for which the coords are assumed equal
static const double NO_INTERSECTION; ///< Return value of LineCoeffToPlane() if the line is parallel to the plane
} ;
// tolua_end
double x, y, z; // tolua_export
};// tolua_export

View File

@ -969,7 +969,7 @@ bool cWorld::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback
void cWorld::DoExplosiontAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData)
void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData)
{
if (cPluginManager::Get()->CallHookExploding(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData) || (a_ExplosionSize <= 0))
{
@ -979,7 +979,7 @@ void cWorld::DoExplosiontAt(double a_ExplosionSize, double a_BlockX, double a_Bl
// TODO: Add damage to entities, add support for pickups, and implement block hardiness
Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ);
cVector3iArray BlocksAffected;
m_ChunkMap->DoExplosiontAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected);
m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected);
BroadcastSoundEffect("random.explode", (int)floor(a_BlockX * 8), (int)floor(a_BlockY * 8), (int)floor(a_BlockZ * 8), 1.0f, 0.6f);
{
cCSLock Lock(m_CSPlayers);

View File

@ -414,13 +414,13 @@ public:
| esCreeper | cCreeper * |
| esBed | cVector3i * |
| esEnderCrystal | Vector3i * |
| esGhastFireball | TBD |
| esGhastFireball | cGhastFireball * |
| esWitherSkullBlack | TBD |
| esWitherSkullBlue | TBD |
| esWitherBirth | TBD |
| esPlugin | void * |
*/
void DoExplosiontAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData); // tolua_export
void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData); // tolua_export
/// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found
bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp

View File

@ -340,7 +340,7 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
m_Writer.AddShort("xTile", (Int16)floor(Pos.x));
m_Writer.AddShort("yTile", (Int16)floor(Pos.y));
m_Writer.AddShort("zTile", (Int16)floor(Pos.z));
m_Writer.AddShort("inTile", 0); // TODO: Query the block type (is it needed?)
m_Writer.AddShort("inTile", 0); // TODO: Query the block type
m_Writer.AddShort("shake", 0); // TODO: Any shake?
m_Writer.AddByte ("inGround", a_Projectile->IsInGround() ? 1 : 0);

View File

@ -952,14 +952,34 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadMinecartHFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
if (strncmp(a_IDTag, "Item", a_IDTagLength) == 0)
else if (strncmp(a_IDTag, "Item", a_IDTagLength) == 0)
{
LoadPickupFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
if (strncmp(a_IDTag, "Arrow", a_IDTagLength) == 0)
else if (strncmp(a_IDTag, "Arrow", a_IDTagLength) == 0)
{
LoadArrowFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
else if (strncmp(a_IDTag, "Snowball", a_IDTagLength) == 0)
{
LoadSnowballFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
else if (strncmp(a_IDTag, "Egg", a_IDTagLength) == 0)
{
LoadEggFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
else if (strncmp(a_IDTag, "Fireball", a_IDTagLength) == 0)
{
LoadFireballFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
else if (strncmp(a_IDTag, "SmallFireball", a_IDTagLength) == 0)
{
LoadFireChargeFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
else if (strncmp(a_IDTag, "ThrownEnderpearl", a_IDTagLength) == 0)
{
LoadThrownEnderpearlFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
// TODO: other entities
}
@ -1100,7 +1120,7 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cArrowEntity> Arrow(new cArrowEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadEntityBaseFromNBT(*Arrow.get(), a_NBT, a_TagIdx))
if (!LoadProjectileBaseFromNBT(*Arrow.get(), a_NBT, a_TagIdx))
{
return;
}
@ -1136,6 +1156,86 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cThrownSnowballEntity> Snowball(new cThrownSnowballEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Snowball.get(), a_NBT, a_TagIdx))
{
return;
}
// Store the new snowball in the entities list:
a_Entities.push_back(Snowball.release());
}
void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cThrownEggEntity> Egg(new cThrownEggEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Egg.get(), a_NBT, a_TagIdx))
{
return;
}
// Store the new egg in the entities list:
a_Entities.push_back(Egg.release());
}
void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cGhastFireballEntity> Fireball(new cGhastFireballEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Fireball.get(), a_NBT, a_TagIdx))
{
return;
}
// Store the new fireball in the entities list:
a_Entities.push_back(Fireball.release());
}
void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cFireChargeEntity> FireCharge(new cFireChargeEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*FireCharge.get(), a_NBT, a_TagIdx))
{
return;
}
// Store the new FireCharge in the entities list:
a_Entities.push_back(FireCharge.release());
}
void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cThrownEnderPearlEntity> Enderpearl(new cThrownEnderPearlEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Enderpearl.get(), a_NBT, a_TagIdx))
{
return;
}
// Store the new enderpearl in the entities list:
a_Entities.push_back(Enderpearl.release());
}
bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx)
{
double Pos[3];
@ -1167,6 +1267,30 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N
bool cWSSAnvil::LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx)
{
if (!LoadEntityBaseFromNBT(a_Entity, a_NBT, a_TagIdx))
{
return false;
}
bool IsInGround = false;
int InGroundIdx = a_NBT.FindChildByName(a_TagIdx, "inGround");
if (InGroundIdx > 0)
{
IsInGround = (a_NBT.GetByte(InGroundIdx) != 0);
}
a_Entity.SetIsInGround(IsInGround);
// TODO: Load inTile, TileCoords
return true;
}
bool cWSSAnvil::LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx)
{
if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List) || (a_NBT.GetChildrenType(a_TagIdx) != TAG_Double))

View File

@ -18,6 +18,8 @@
// fwd: ItemGrid.h
class cItemGrid;
class cProjectileEntity;
@ -138,18 +140,26 @@ protected:
void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength);
void LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartFFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartTFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartHFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadArrowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFallingBlockFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartFFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartTFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartHFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadArrowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadSnowballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadEggFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFireballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFireChargeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
/// Loads entity common data from the NBT compound; returns true if successful
bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx);
/// Loads projectile common data from the NBT compound; returns true if successful
bool LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIx);
/// Loads an array of doubles of the specified length from the specified NBT list tag a_TagIdx; returns true if successful
bool LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx);