2014-04-24 17:03:47 -04:00
2012-06-14 09:06:06 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 18:09:57 -04:00
# include "Entity.h"
2013-08-19 05:39:13 -04:00
# include "../World.h"
# include "../Root.h"
2014-03-11 12:32:33 -04:00
# include "../Matrix4.h"
2013-08-19 05:39:13 -04:00
# include "../ClientHandle.h"
# include "../Chunk.h"
# include "../Simulator/FluidSimulator.h"
2013-12-08 06:17:54 -05:00
# include "../Bindings/PluginManager.h"
2013-08-19 05:39:13 -04:00
# include "../Tracer.h"
2013-12-22 15:04:17 -05:00
# include "Player.h"
2014-07-23 10:32:09 -04:00
# include "Items/ItemHandler.h"
2014-08-21 06:08:38 -04:00
# include "../FastRandom.h"
2015-05-26 21:35:28 -04:00
# include "../NetherPortalScanner.h"
2012-06-14 09:06:06 -04:00
2015-03-21 10:18:17 -04:00
UInt32 cEntity : : m_EntityCount = 0 ;
2012-06-14 09:06:06 -04:00
cCriticalSection cEntity : : m_CSCount ;
2015-03-21 10:18:17 -04:00
cEntity : : cEntity ( eEntityType a_EntityType , double a_X , double a_Y , double a_Z , double a_Width , double a_Height ) :
m_UniqueID ( INVALID_ID ) , // Proper ID will be assigned later in the constructor code
m_Health ( 1 ) ,
m_MaxHealth ( 1 ) ,
m_AttachedTo ( nullptr ) ,
m_Attachee ( nullptr ) ,
m_bDirtyHead ( true ) ,
m_bDirtyOrientation ( true ) ,
m_bHasSentNoSpeed ( true ) ,
m_bOnGround ( false ) ,
m_Gravity ( - 9.81f ) ,
2015-03-31 11:03:35 -04:00
m_AirDrag ( 0.02f ) ,
2015-03-21 10:18:17 -04:00
m_LastPos ( a_X , a_Y , a_Z ) ,
m_IsInitialized ( false ) ,
m_WorldTravellingFrom ( nullptr ) ,
m_EntityType ( a_EntityType ) ,
m_World ( nullptr ) ,
2015-05-26 21:35:28 -04:00
m_IsWorldChangeScheduled ( false ) ,
2015-03-21 10:18:17 -04:00
m_IsFireproof ( false ) ,
m_TicksSinceLastBurnDamage ( 0 ) ,
m_TicksSinceLastLavaDamage ( 0 ) ,
m_TicksSinceLastFireDamage ( 0 ) ,
m_TicksLeftBurning ( 0 ) ,
m_TicksSinceLastVoidDamage ( 0 ) ,
m_IsSwimming ( false ) ,
m_IsSubmerged ( false ) ,
m_AirLevel ( 0 ) ,
m_AirTickTimer ( 0 ) ,
m_TicksAlive ( 0 ) ,
m_HeadYaw ( 0.0 ) ,
m_Rot ( 0.0 , 0.0 , 0.0 ) ,
m_Pos ( a_X , a_Y , a_Z ) ,
m_WaterSpeed ( 0 , 0 , 0 ) ,
m_Mass ( 0.001 ) , // Default 1g
m_Width ( a_Width ) ,
m_Height ( a_Height ) ,
m_InvulnerableTicks ( 0 )
2012-06-14 09:06:06 -04:00
{
2015-03-21 10:18:17 -04:00
// Assign a proper ID:
2012-06-14 09:06:06 -04:00
cCSLock Lock ( m_CSCount ) ;
m_EntityCount + + ;
m_UniqueID = m_EntityCount ;
}
cEntity : : ~ cEntity ( )
{
2014-01-24 03:57:12 -05:00
// Before deleting, the entity needs to have been removed from the world, if ever added
2014-10-20 16:55:07 -04:00
ASSERT ( ( m_World = = nullptr ) | | ! m_World - > HasEntity ( m_UniqueID ) ) ;
2013-04-13 17:02:10 -04:00
2014-01-16 14:31:06 -05:00
/*
// DEBUG:
2014-07-17 16:50:58 -04:00
LOGD ( " Deleting entity %d at pos {%.2f, %.2f, %.2f} ~ [%d, %d]; ptr %p " ,
2012-06-14 09:06:06 -04:00
m_UniqueID ,
2013-03-02 14:57:09 -05:00
m_Pos . x , m_Pos . y , m_Pos . z ,
2012-06-14 09:06:06 -04:00
( int ) ( m_Pos . x / cChunkDef : : Width ) , ( int ) ( m_Pos . z / cChunkDef : : Width ) ,
this
2013-04-02 02:48:31 -04:00
) ;
2014-01-16 14:31:06 -05:00
*/
2014-10-20 16:55:07 -04:00
if ( m_AttachedTo ! = nullptr )
2013-03-03 14:05:11 -05:00
{
Detach ( ) ;
}
2014-10-20 16:55:07 -04:00
if ( m_Attachee ! = nullptr )
2013-03-03 14:05:11 -05:00
{
m_Attachee - > Detach ( ) ;
}
2013-04-02 02:48:31 -04:00
2013-04-13 17:02:10 -04:00
if ( m_IsInitialized )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
LOGWARNING ( " ERROR: Entity deallocated without being destroyed " ) ;
2012-06-14 09:06:06 -04:00
ASSERT ( ! " Entity deallocated without being destroyed or unlinked " ) ;
}
}
2013-02-02 18:55:29 -05:00
const char * cEntity : : GetClass ( void ) const
{
return " cEntity " ;
}
const char * cEntity : : GetClassStatic ( void )
{
return " cEntity " ;
}
2012-08-24 03:58:26 -04:00
2012-12-21 07:21:20 -05:00
const char * cEntity : : GetParentClass ( void ) const
{
return " " ;
}
2014-06-08 15:58:08 -04:00
bool cEntity : : Initialize ( cWorld & a_World )
2012-06-14 09:06:06 -04:00
{
2014-09-08 21:02:25 -04:00
if ( cPluginManager : : Get ( ) - > CallHookSpawningEntity ( a_World , * this ) & & ! IsPlayer ( ) )
2013-08-08 03:13:13 -04:00
{
return false ;
}
2014-01-16 14:31:06 -05:00
/*
// DEBUG:
2013-04-13 17:02:10 -04:00
LOGD ( " Initializing entity #%d (%s) at {%.02f, %.02f, %.02f} " ,
m_UniqueID , GetClass ( ) , m_Pos . x , m_Pos . y , m_Pos . z
) ;
2014-01-16 14:31:06 -05:00
*/
2013-04-13 17:02:10 -04:00
m_IsInitialized = true ;
2014-06-08 15:58:08 -04:00
m_World = & a_World ;
2013-03-03 14:05:11 -05:00
m_World - > AddEntity ( this ) ;
2013-08-08 03:13:13 -04:00
2014-06-08 15:58:08 -04:00
cPluginManager : : Get ( ) - > CallHookSpawnedEntity ( a_World , * this ) ;
2013-08-25 15:25:13 -04:00
// Spawn the entity on the clients:
2014-06-08 15:58:08 -04:00
a_World . BroadcastSpawnEntity ( * this ) ;
2013-08-25 15:25:13 -04:00
2013-08-08 03:13:13 -04:00
return true ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cEntity : : WrapHeadYaw ( void )
2013-04-02 02:48:31 -04:00
{
2013-12-08 07:08:56 -05:00
m_HeadYaw = NormalizeAngleDegrees ( m_HeadYaw ) ;
2013-04-02 02:48:31 -04:00
}
2013-04-13 17:02:10 -04:00
void cEntity : : WrapRotation ( void )
2012-06-14 09:06:06 -04:00
{
2013-12-08 07:08:56 -05:00
m_Rot . x = NormalizeAngleDegrees ( m_Rot . x ) ;
2013-12-08 12:54:04 -05:00
m_Rot . y = NormalizeAngleDegrees ( m_Rot . y ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
void cEntity : : WrapSpeed ( void )
2013-03-22 02:33:10 -04:00
{
2014-06-16 10:12:50 -04:00
m_Speed . x = Clamp ( m_Speed . x , - 78.0 , 78.0 ) ;
m_Speed . y = Clamp ( m_Speed . y , - 78.0 , 78.0 ) ;
m_Speed . z = Clamp ( m_Speed . z , - 78.0 , 78.0 ) ;
2013-03-22 02:33:10 -04:00
}
2012-06-14 09:06:06 -04:00
2013-06-25 02:36:59 -04:00
void cEntity : : Destroy ( bool a_ShouldBroadcast )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
if ( ! m_IsInitialized )
2012-06-14 09:06:06 -04:00
{
return ;
}
2013-06-25 02:36:59 -04:00
if ( a_ShouldBroadcast )
{
m_World - > BroadcastDestroyEntity ( * this ) ;
}
2013-04-02 02:48:31 -04:00
2013-04-13 17:02:10 -04:00
m_IsInitialized = false ;
2012-06-14 09:06:06 -04:00
Destroyed ( ) ;
}
2013-07-01 06:39:56 -04:00
void cEntity : : TakeDamage ( cEntity & a_Attacker )
{
int RawDamage = a_Attacker . GetRawDamageAgainst ( * this ) ;
TakeDamage ( dtAttack , & a_Attacker , RawDamage , a_Attacker . GetKnockbackAmountAgainst ( * this ) ) ;
}
void cEntity : : TakeDamage ( eDamageType a_DamageType , cEntity * a_Attacker , int a_RawDamage , double a_KnockbackAmount )
{
int FinalDamage = a_RawDamage - GetArmorCoverAgainst ( a_Attacker , a_DamageType , a_RawDamage ) ;
cEntity : : TakeDamage ( a_DamageType , a_Attacker , a_RawDamage , FinalDamage , a_KnockbackAmount ) ;
}
void cEntity : : TakeDamage ( eDamageType a_DamageType , cEntity * a_Attacker , int a_RawDamage , int a_FinalDamage , double a_KnockbackAmount )
{
TakeDamageInfo TDI ;
TDI . DamageType = a_DamageType ;
TDI . Attacker = a_Attacker ;
TDI . RawDamage = a_RawDamage ;
TDI . FinalDamage = a_FinalDamage ;
2013-12-22 15:04:17 -05:00
Vector3d Heading ( 0 , 0 , 0 ) ;
2014-10-20 16:55:07 -04:00
if ( a_Attacker ! = nullptr )
2013-12-22 15:04:17 -05:00
{
2014-08-03 19:34:12 -04:00
Heading = a_Attacker - > GetLookVector ( ) * ( a_Attacker - > IsSprinting ( ) ? 16 : 11 ) ;
2013-12-22 15:04:17 -05:00
}
2013-07-01 06:39:56 -04:00
TDI . Knockback = Heading * a_KnockbackAmount ;
DoTakeDamage ( TDI ) ;
}
2014-01-16 14:00:49 -05:00
void cEntity : : SetYawFromSpeed ( void )
2013-09-07 11:14:37 -04:00
{
const double EPS = 0.0000001 ;
2014-09-22 03:22:36 -04:00
if ( ( std : : abs ( m_Speed . x ) < EPS ) & & ( std : : abs ( m_Speed . z ) < EPS ) )
2013-09-07 11:14:37 -04:00
{
// atan2() may overflow or is undefined, pick any number
2014-01-16 14:00:49 -05:00
SetYaw ( 0 ) ;
2013-09-07 11:14:37 -04:00
return ;
}
2014-10-20 16:55:07 -04:00
SetYaw ( atan2 ( m_Speed . x , m_Speed . z ) * 180 / M_PI ) ;
2013-09-07 11:14:37 -04:00
}
void cEntity : : SetPitchFromSpeed ( void )
{
const double EPS = 0.0000001 ;
2014-07-17 16:15:34 -04:00
double xz = sqrt ( m_Speed . x * m_Speed . x + m_Speed . z * m_Speed . z ) ; // Speed XZ-plane component
2014-09-22 03:22:36 -04:00
if ( ( std : : abs ( xz ) < EPS ) & & ( std : : abs ( m_Speed . y ) < EPS ) )
2013-09-07 11:14:37 -04:00
{
// atan2() may overflow or is undefined, pick any number
SetPitch ( 0 ) ;
return ;
}
2014-10-20 16:55:07 -04:00
SetPitch ( atan2 ( m_Speed . y , xz ) * 180 / M_PI ) ;
2013-09-07 11:14:37 -04:00
}
2014-04-25 18:32:30 -04:00
bool cEntity : : DoTakeDamage ( TakeDamageInfo & a_TDI )
2013-07-01 06:39:56 -04:00
{
if ( m_Health < = 0 )
{
// Can't take damage if already dead
2014-04-25 18:32:30 -04:00
return false ;
}
if ( m_InvulnerableTicks > 0 )
{
// Entity is invulnerable
return false ;
2013-07-01 06:39:56 -04:00
}
2014-07-23 10:32:09 -04:00
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookTakeDamage ( * this , a_TDI ) )
{
return false ;
}
2014-10-20 16:55:07 -04:00
if ( ( a_TDI . Attacker ! = nullptr ) & & ( a_TDI . Attacker - > IsPlayer ( ) ) )
2013-12-22 15:04:17 -05:00
{
2014-05-12 14:38:52 -04:00
cPlayer * Player = ( cPlayer * ) a_TDI . Attacker ;
2014-07-23 10:32:09 -04:00
Player - > GetEquippedItem ( ) . GetHandler ( ) - > OnEntityAttack ( Player , this ) ;
2013-12-23 04:41:45 -05:00
// IsOnGround() only is false if the player is moving downwards
2014-06-07 22:58:41 -04:00
// TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain)
2014-09-01 08:31:05 -04:00
const cEnchantments & Enchantments = Player - > GetEquippedItem ( ) . m_Enchantments ;
2014-08-19 06:38:15 -04:00
int SharpnessLevel = Enchantments . GetLevel ( cEnchantments : : enchSharpness ) ;
int SmiteLevel = Enchantments . GetLevel ( cEnchantments : : enchSmite ) ;
int BaneOfArthropodsLevel = Enchantments . GetLevel ( cEnchantments : : enchBaneOfArthropods ) ;
if ( SharpnessLevel > 0 )
{
2014-09-01 08:31:05 -04:00
a_TDI . FinalDamage + = ( int ) ceil ( 1.25 * SharpnessLevel ) ;
2014-08-19 06:38:15 -04:00
}
else if ( SmiteLevel > 0 )
{
if ( IsMob ( ) )
{
cMonster * Monster = ( cMonster * ) this ;
switch ( Monster - > GetMobType ( ) )
{
2014-09-17 13:40:10 -04:00
case mtSkeleton :
case mtZombie :
case mtWither :
case mtZombiePigman :
2014-08-19 06:38:15 -04:00
{
2014-09-01 08:31:05 -04:00
a_TDI . FinalDamage + = ( int ) ceil ( 2.5 * SmiteLevel ) ;
2014-08-19 06:38:15 -04:00
break ;
}
2014-09-22 18:23:56 -04:00
default : break ;
2014-08-19 06:38:15 -04:00
}
}
}
else if ( BaneOfArthropodsLevel > 0 )
2013-12-22 15:04:17 -05:00
{
2014-08-19 06:38:15 -04:00
if ( IsMob ( ) )
{
cMonster * Monster = ( cMonster * ) this ;
switch ( Monster - > GetMobType ( ) )
{
2014-09-17 13:40:10 -04:00
case mtSpider :
case mtCaveSpider :
case mtSilverfish :
2014-08-19 06:38:15 -04:00
{
2014-09-01 08:31:05 -04:00
a_TDI . RawDamage + = ( int ) ceil ( 2.5 * BaneOfArthropodsLevel ) ;
2014-08-22 05:49:49 -04:00
// TODO: Add slowness effect
2014-08-19 06:38:15 -04:00
break ;
2014-08-22 05:49:49 -04:00
} ;
default : break ;
2014-08-19 06:38:15 -04:00
}
}
}
int FireAspectLevel = Enchantments . GetLevel ( cEnchantments : : enchFireAspect ) ;
if ( FireAspectLevel > 0 )
{
int BurnTicks = 3 ;
if ( FireAspectLevel > 1 )
{
BurnTicks + = 4 * ( FireAspectLevel - 1 ) ;
}
2014-08-19 10:08:17 -04:00
if ( ! IsMob ( ) & & ! IsSubmerged ( ) & & ! IsSwimming ( ) )
{
StartBurning ( BurnTicks * 20 ) ;
}
else if ( IsMob ( ) & & ! IsSubmerged ( ) & & ! IsSwimming ( ) )
{
cMonster * Monster = ( cMonster * ) this ;
switch ( Monster - > GetMobType ( ) )
{
2014-09-17 13:40:10 -04:00
case mtGhast :
case mtZombiePigman :
case mtMagmaCube :
2014-09-03 04:29:10 -04:00
{
2014-08-19 10:08:17 -04:00
break ;
} ;
2014-08-19 10:47:33 -04:00
default : StartBurning ( BurnTicks * 20 ) ;
2014-08-19 10:08:17 -04:00
}
}
2014-08-22 05:49:49 -04:00
}
2014-08-19 06:38:15 -04:00
2015-05-19 14:32:10 -04:00
unsigned int ThornsLevel = 0 ;
2014-09-01 08:35:52 -04:00
const cItem ArmorItems [ ] = { GetEquippedHelmet ( ) , GetEquippedChestplate ( ) , GetEquippedLeggings ( ) , GetEquippedBoots ( ) } ;
2014-08-22 05:49:49 -04:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( ArmorItems ) ; i + + )
{
2014-09-01 08:35:52 -04:00
const cItem & Item = ArmorItems [ i ] ;
ThornsLevel = std : : max ( ThornsLevel , Item . m_Enchantments . GetLevel ( cEnchantments : : enchThorns ) ) ;
2014-08-22 05:49:49 -04:00
}
2014-08-29 18:27:33 -04:00
2014-08-22 05:49:49 -04:00
if ( ThornsLevel > 0 )
{
int Chance = ThornsLevel * 15 ;
cFastRandom Random ;
int RandomValue = Random . GenerateRandomInteger ( 0 , 100 ) ;
if ( RandomValue < = Chance )
{
a_TDI . Attacker - > TakeDamage ( dtAttack , this , 0 , Random . GenerateRandomInteger ( 1 , 4 ) , 0 ) ;
}
2014-08-19 06:38:15 -04:00
}
if ( ! Player - > IsOnGround ( ) )
2014-09-03 04:29:10 -04:00
{
2014-07-14 16:46:15 -04:00
if ( ( a_TDI . DamageType = = dtAttack ) | | ( a_TDI . DamageType = = dtArrowAttack ) )
{
a_TDI . FinalDamage + = 2 ;
2014-07-17 16:15:34 -04:00
m_World - > BroadcastEntityAnimation ( * this , 4 ) ; // Critical hit
2014-07-14 16:46:15 -04:00
}
2013-12-22 15:04:17 -05:00
}
2014-05-12 14:38:52 -04:00
2014-05-18 16:49:27 -04:00
Player - > GetStatManager ( ) . AddValue ( statDamageDealt , ( StatValue ) floor ( a_TDI . FinalDamage * 10 + 0.5 ) ) ;
2013-12-22 15:04:17 -05:00
}
2014-08-22 05:49:49 -04:00
2014-08-31 05:28:42 -04:00
if ( IsPlayer ( ) )
{
2014-08-21 06:08:38 -04:00
double TotalEPF = 0.0 ;
double EPFProtection = 0.00 ;
double EPFFireProtection = 0.00 ;
double EPFBlastProtection = 0.00 ;
double EPFProjectileProtection = 0.00 ;
double EPFFeatherFalling = 0.00 ;
2014-09-01 08:35:52 -04:00
const cItem ArmorItems [ ] = { GetEquippedHelmet ( ) , GetEquippedChestplate ( ) , GetEquippedLeggings ( ) , GetEquippedBoots ( ) } ;
2014-08-22 05:49:49 -04:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( ArmorItems ) ; i + + )
2014-08-21 06:08:38 -04:00
{
2014-09-01 08:35:52 -04:00
const cItem & Item = ArmorItems [ i ] ;
int Level = Item . m_Enchantments . GetLevel ( cEnchantments : : enchProtection ) ;
2014-09-01 08:31:05 -04:00
if ( Level > 0 )
2014-08-22 05:49:49 -04:00
{
2014-09-01 08:31:05 -04:00
EPFProtection + = ( 6 + Level * Level ) * 0.75 / 3 ;
2014-08-22 05:49:49 -04:00
}
2014-08-21 06:08:38 -04:00
2014-09-01 08:35:52 -04:00
Level = Item . m_Enchantments . GetLevel ( cEnchantments : : enchFireProtection ) ;
2014-09-01 08:31:05 -04:00
if ( Level > 0 )
2014-08-22 05:49:49 -04:00
{
2014-09-01 08:31:05 -04:00
EPFFireProtection + = ( 6 + Level * Level ) * 1.25 / 3 ;
2014-08-22 05:49:49 -04:00
}
2014-08-21 06:08:38 -04:00
2014-09-01 08:35:52 -04:00
Level = Item . m_Enchantments . GetLevel ( cEnchantments : : enchFeatherFalling ) ;
2014-09-01 08:31:05 -04:00
if ( Level > 0 )
2014-08-22 05:49:49 -04:00
{
2014-09-01 08:31:05 -04:00
EPFFeatherFalling + = ( 6 + Level * Level ) * 2.5 / 3 ;
2014-08-22 05:49:49 -04:00
}
2014-08-21 06:08:38 -04:00
2014-09-01 08:35:52 -04:00
Level = Item . m_Enchantments . GetLevel ( cEnchantments : : enchBlastProtection ) ;
2014-09-01 08:31:05 -04:00
if ( Level > 0 )
2014-08-22 05:49:49 -04:00
{
2014-09-01 08:31:05 -04:00
EPFBlastProtection + = ( 6 + Level * Level ) * 1.5 / 3 ;
2014-08-22 05:49:49 -04:00
}
2014-08-21 06:08:38 -04:00
2014-09-01 08:35:52 -04:00
Level = Item . m_Enchantments . GetLevel ( cEnchantments : : enchProjectileProtection ) ;
2014-09-01 08:31:05 -04:00
if ( Level > 0 )
2014-08-22 05:49:49 -04:00
{
2014-09-01 08:31:05 -04:00
EPFProjectileProtection + = ( 6 + Level * Level ) * 1.5 / 3 ;
2014-08-22 05:49:49 -04:00
}
2014-08-21 06:08:38 -04:00
}
2014-08-22 05:49:49 -04:00
TotalEPF = EPFProtection + EPFFireProtection + EPFFeatherFalling + EPFBlastProtection + EPFProjectileProtection ;
2014-08-21 06:08:38 -04:00
EPFProtection = EPFProtection / TotalEPF ;
EPFFireProtection = EPFFireProtection / TotalEPF ;
EPFFeatherFalling = EPFFeatherFalling / TotalEPF ;
EPFBlastProtection = EPFBlastProtection / TotalEPF ;
EPFProjectileProtection = EPFProjectileProtection / TotalEPF ;
2014-09-01 08:31:05 -04:00
if ( TotalEPF > 25 )
{
TotalEPF = 25 ;
}
2014-08-21 06:08:38 -04:00
cFastRandom Random ;
2014-09-01 08:31:05 -04:00
float RandomValue = Random . GenerateRandomInteger ( 50 , 100 ) * 0.01f ;
2014-08-21 06:08:38 -04:00
2014-08-22 05:49:49 -04:00
TotalEPF = ceil ( TotalEPF * RandomValue ) ;
2014-08-21 06:08:38 -04:00
2014-09-01 08:31:05 -04:00
if ( TotalEPF > 20 )
{
TotalEPF = 20 ;
}
2014-08-21 06:08:38 -04:00
EPFProtection = TotalEPF * EPFProtection ;
EPFFireProtection = TotalEPF * EPFFireProtection ;
EPFFeatherFalling = TotalEPF * EPFFeatherFalling ;
EPFBlastProtection = TotalEPF * EPFBlastProtection ;
EPFProjectileProtection = TotalEPF * EPFProjectileProtection ;
int RemovedDamage = 0 ;
2014-09-01 08:31:05 -04:00
if ( ( a_TDI . DamageType ! = dtInVoid ) & & ( a_TDI . DamageType ! = dtAdmin ) )
{
RemovedDamage + = ( int ) ceil ( EPFProtection * 0.04 * a_TDI . FinalDamage ) ;
}
2014-08-21 06:08:38 -04:00
2014-09-01 08:31:05 -04:00
if ( ( a_TDI . DamageType = = dtFalling ) | | ( a_TDI . DamageType = = dtFall ) | | ( a_TDI . DamageType = = dtEnderPearl ) )
{
RemovedDamage + = ( int ) ceil ( EPFFeatherFalling * 0.04 * a_TDI . FinalDamage ) ;
}
2014-08-21 06:08:38 -04:00
2014-09-01 08:31:05 -04:00
if ( a_TDI . DamageType = = dtBurning )
{
RemovedDamage + = ( int ) ceil ( EPFFireProtection * 0.04 * a_TDI . FinalDamage ) ;
}
2014-08-21 06:08:38 -04:00
2014-09-01 08:31:05 -04:00
if ( a_TDI . DamageType = = dtExplosion )
{
RemovedDamage + = ( int ) ceil ( EPFBlastProtection * 0.04 * a_TDI . FinalDamage ) ;
}
2014-08-21 06:08:38 -04:00
2014-09-01 08:31:05 -04:00
if ( a_TDI . DamageType = = dtProjectile )
{
RemovedDamage + = ( int ) ceil ( EPFBlastProtection * 0.04 * a_TDI . FinalDamage ) ;
}
2014-08-21 06:08:38 -04:00
2014-09-01 08:31:05 -04:00
if ( a_TDI . FinalDamage < RemovedDamage )
{
RemovedDamage = 0 ;
}
2014-08-22 05:49:49 -04:00
2014-08-21 06:08:38 -04:00
a_TDI . FinalDamage - = RemovedDamage ;
}
2014-08-22 05:49:49 -04:00
2013-07-01 06:39:56 -04:00
m_Health - = ( short ) a_TDI . FinalDamage ;
// TODO: Apply damage to armor
2014-07-19 04:40:29 -04:00
m_Health = std : : max ( m_Health , 0 ) ;
2013-07-01 06:39:56 -04:00
2014-09-01 08:31:05 -04:00
// Add knockback:
2014-10-20 16:55:07 -04:00
if ( ( IsMob ( ) | | IsPlayer ( ) ) & & ( a_TDI . Attacker ! = nullptr ) )
2014-01-12 08:28:37 -05:00
{
2014-07-17 16:23:08 -04:00
int KnockbackLevel = a_TDI . Attacker - > GetEquippedWeapon ( ) . m_Enchantments . GetLevel ( cEnchantments : : enchKnockback ) ; // More common enchantment
2014-07-17 09:32:52 -04:00
if ( KnockbackLevel < 1 )
2014-04-23 10:15:04 -04:00
{
2014-07-17 09:32:52 -04:00
// We support punch on swords and vice versa! :)
2014-04-23 10:15:04 -04:00
KnockbackLevel = a_TDI . Attacker - > GetEquippedWeapon ( ) . m_Enchantments . GetLevel ( cEnchantments : : enchPunch ) ;
}
2014-07-17 09:32:52 -04:00
Vector3d AdditionalSpeed ( 0 , 0 , 0 ) ;
2014-04-23 10:15:04 -04:00
switch ( KnockbackLevel )
{
2014-07-17 09:32:52 -04:00
case 1 : AdditionalSpeed . Set ( 5 , 0.3 , 5 ) ; break ;
2014-07-17 17:15:53 -04:00
case 2 : AdditionalSpeed . Set ( 8 , 0.3 , 8 ) ; break ;
2014-07-17 09:32:52 -04:00
default : break ;
2014-04-23 10:15:04 -04:00
}
2014-08-22 05:49:49 -04:00
AddSpeed ( a_TDI . Knockback + AdditionalSpeed ) ;
2014-01-12 08:28:37 -05:00
}
2013-12-22 15:04:17 -05:00
2014-04-12 08:16:48 -04:00
m_World - > BroadcastEntityStatus ( * this , esGenericHurt ) ;
2013-07-01 06:39:56 -04:00
2014-04-25 18:32:30 -04:00
m_InvulnerableTicks = 10 ;
2013-07-01 06:39:56 -04:00
if ( m_Health < = 0 )
{
2014-07-04 05:55:09 -04:00
KilledBy ( a_TDI ) ;
2014-05-12 10:05:09 -04:00
2014-10-20 16:55:07 -04:00
if ( a_TDI . Attacker ! = nullptr )
2014-05-12 10:05:09 -04:00
{
a_TDI . Attacker - > Killed ( this ) ;
}
2013-07-01 06:39:56 -04:00
}
2014-04-25 18:32:30 -04:00
return true ;
2013-07-01 06:39:56 -04:00
}
int cEntity : : GetRawDamageAgainst ( const cEntity & a_Receiver )
{
// Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items
// Ref: http://www.minecraftwiki.net/wiki/Damage#Dealing_damage as of 2012_12_20
switch ( this - > GetEquippedWeapon ( ) . m_ItemType )
{
case E_ITEM_WOODEN_SWORD : return 4 ;
case E_ITEM_GOLD_SWORD : return 4 ;
case E_ITEM_STONE_SWORD : return 5 ;
case E_ITEM_IRON_SWORD : return 6 ;
case E_ITEM_DIAMOND_SWORD : return 7 ;
case E_ITEM_WOODEN_AXE : return 3 ;
case E_ITEM_GOLD_AXE : return 3 ;
case E_ITEM_STONE_AXE : return 4 ;
case E_ITEM_IRON_AXE : return 5 ;
case E_ITEM_DIAMOND_AXE : return 6 ;
case E_ITEM_WOODEN_PICKAXE : return 2 ;
case E_ITEM_GOLD_PICKAXE : return 2 ;
case E_ITEM_STONE_PICKAXE : return 3 ;
case E_ITEM_IRON_PICKAXE : return 4 ;
case E_ITEM_DIAMOND_PICKAXE : return 5 ;
case E_ITEM_WOODEN_SHOVEL : return 1 ;
case E_ITEM_GOLD_SHOVEL : return 1 ;
case E_ITEM_STONE_SHOVEL : return 2 ;
case E_ITEM_IRON_SHOVEL : return 3 ;
case E_ITEM_DIAMOND_SHOVEL : return 4 ;
}
// All other equipped items give a damage of 1:
return 1 ;
}
2014-04-25 22:49:08 -04:00
bool cEntity : : ArmorCoversAgainst ( eDamageType a_DamageType )
2013-07-01 06:39:56 -04:00
{
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Effects as of 2012_12_20
switch ( a_DamageType )
{
case dtOnFire :
case dtSuffocating :
case dtDrowning : // TODO: This one could be a special case - in various MC versions (PC vs XBox) it is and isn't armor-protected
case dtStarving :
case dtInVoid :
case dtPoisoning :
2014-06-09 00:51:55 -04:00
case dtWithering :
2013-07-01 06:39:56 -04:00
case dtPotionOfHarming :
case dtFalling :
case dtLightning :
2014-02-02 07:47:17 -05:00
case dtPlugin :
2013-07-01 06:39:56 -04:00
{
2014-04-25 22:49:08 -04:00
return false ;
}
case dtAttack :
case dtArrowAttack :
case dtCactusContact :
case dtLavaContact :
case dtFireContact :
case dtEnderPearl :
case dtExplosion :
{
return true ;
2013-07-01 06:39:56 -04:00
}
}
2014-04-26 12:21:49 -04:00
ASSERT ( ! " Invalid damage type! " ) ;
2014-04-27 17:12:52 -04:00
return false ;
2014-04-25 22:49:08 -04:00
}
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
// Filter out damage types that are not protected by armor:
2014-12-05 10:59:11 -05:00
if ( ! ArmorCoversAgainst ( a_DamageType ) )
{
return 0 ;
}
2013-07-01 06:39:56 -04:00
// Add up all armor points:
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20
int ArmorValue = 0 ;
switch ( GetEquippedHelmet ( ) . m_ItemType )
{
case E_ITEM_LEATHER_CAP : ArmorValue + = 1 ; break ;
case E_ITEM_GOLD_HELMET : ArmorValue + = 2 ; break ;
case E_ITEM_CHAIN_HELMET : ArmorValue + = 2 ; break ;
case E_ITEM_IRON_HELMET : ArmorValue + = 2 ; break ;
case E_ITEM_DIAMOND_HELMET : ArmorValue + = 3 ; break ;
}
switch ( GetEquippedChestplate ( ) . m_ItemType )
{
case E_ITEM_LEATHER_TUNIC : ArmorValue + = 3 ; break ;
case E_ITEM_GOLD_CHESTPLATE : ArmorValue + = 5 ; break ;
case E_ITEM_CHAIN_CHESTPLATE : ArmorValue + = 5 ; break ;
case E_ITEM_IRON_CHESTPLATE : ArmorValue + = 6 ; break ;
case E_ITEM_DIAMOND_CHESTPLATE : ArmorValue + = 8 ; break ;
}
switch ( GetEquippedLeggings ( ) . m_ItemType )
{
case E_ITEM_LEATHER_PANTS : ArmorValue + = 2 ; break ;
case E_ITEM_GOLD_LEGGINGS : ArmorValue + = 3 ; break ;
case E_ITEM_CHAIN_LEGGINGS : ArmorValue + = 4 ; break ;
case E_ITEM_IRON_LEGGINGS : ArmorValue + = 5 ; break ;
case E_ITEM_DIAMOND_LEGGINGS : ArmorValue + = 6 ; break ;
}
switch ( GetEquippedBoots ( ) . m_ItemType )
{
case E_ITEM_LEATHER_BOOTS : ArmorValue + = 1 ; break ;
case E_ITEM_GOLD_BOOTS : ArmorValue + = 1 ; break ;
case E_ITEM_CHAIN_BOOTS : ArmorValue + = 1 ; break ;
case E_ITEM_IRON_BOOTS : ArmorValue + = 2 ; break ;
case E_ITEM_DIAMOND_BOOTS : ArmorValue + = 3 ; break ;
}
// TODO: Special armor cases, such as wool, saddles, dog's collar
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Mob_armor as of 2012_12_20
// Now ArmorValue is in [0, 20] range, which corresponds to [0, 80%] protection. Calculate the hitpoints from that:
return a_Damage * ( ArmorValue * 4 ) / 100 ;
}
double cEntity : : GetKnockbackAmountAgainst ( const cEntity & a_Receiver )
{
// Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit
// TODO: Enchantments
return 1 ;
}
2014-07-04 05:55:09 -04:00
void cEntity : : KilledBy ( TakeDamageInfo & a_TDI )
2013-07-01 06:39:56 -04:00
{
m_Health = 0 ;
2014-07-16 06:38:52 -04:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookKilling ( * this , a_TDI . Attacker , a_TDI ) ;
2013-07-01 06:39:56 -04:00
if ( m_Health > 0 )
{
// Plugin wants to 'unkill' the pawn. Abort
return ;
}
2015-06-02 11:06:18 -04:00
// If the victim is a player the hook is handled by the cPlayer class
if ( ! IsPlayer ( ) )
{
AString emptystring = AString ( " " ) ;
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookKilled ( * this , a_TDI , emptystring ) ;
}
2014-02-02 07:47:17 -05:00
// Drop loot:
2013-07-01 06:39:56 -04:00
cItems Drops ;
2014-07-04 05:55:09 -04:00
GetDrops ( Drops , a_TDI . Attacker ) ;
2013-07-01 06:39:56 -04:00
m_World - > SpawnItemPickups ( Drops , GetPosX ( ) , GetPosY ( ) , GetPosZ ( ) ) ;
2014-04-12 08:16:48 -04:00
m_World - > BroadcastEntityStatus ( * this , esGenericDead ) ;
2013-07-01 06:39:56 -04:00
}
void cEntity : : Heal ( int a_HitPoints )
{
m_Health + = a_HitPoints ;
2014-08-03 01:35:29 -04:00
m_Health = std : : min ( m_Health , m_MaxHealth ) ;
2013-07-01 06:39:56 -04:00
}
2013-07-07 11:09:05 -04:00
void cEntity : : SetHealth ( int a_Health )
{
2014-08-03 01:35:29 -04:00
m_Health = Clamp ( a_Health , 0 , m_MaxHealth ) ;
2013-07-07 11:09:05 -04:00
}
2015-01-11 16:12:26 -05:00
void cEntity : : Tick ( std : : chrono : : milliseconds a_Dt , cChunk & a_Chunk )
2012-06-14 09:06:06 -04:00
{
2014-07-20 04:43:07 -04:00
m_TicksAlive + + ;
2014-04-25 18:32:30 -04:00
if ( m_InvulnerableTicks > 0 )
{
m_InvulnerableTicks - - ;
}
2014-10-20 16:55:07 -04:00
if ( m_AttachedTo ! = nullptr )
2013-03-03 14:05:11 -05:00
{
2014-05-12 14:38:52 -04:00
Vector3d DeltaPos = m_Pos - m_AttachedTo - > GetPosition ( ) ;
if ( DeltaPos . Length ( ) > 0.5 )
2013-03-03 14:05:11 -05:00
{
SetPosition ( m_AttachedTo - > GetPosition ( ) ) ;
2014-05-12 14:38:52 -04:00
if ( IsPlayer ( ) )
{
cPlayer * Player = ( cPlayer * ) this ;
Player - > UpdateMovementStats ( DeltaPos ) ;
}
2013-03-03 14:05:11 -05:00
}
}
else
{
2014-04-12 08:16:48 -04:00
if ( ! a_Chunk . IsValid ( ) )
2013-07-24 16:36:12 -04:00
{
2014-04-12 08:16:48 -04:00
return ;
}
2014-01-24 18:58:51 -05:00
2014-04-12 08:16:48 -04:00
// Position changed -> super::Tick() called
GET_AND_VERIFY_CURRENT_CHUNK ( NextChunk , POSX_TOINT , POSZ_TOINT )
2014-03-05 17:12:48 -05:00
2014-04-12 08:16:48 -04:00
TickBurning ( * NextChunk ) ;
2014-01-24 18:58:51 -05:00
2014-04-12 08:16:48 -04:00
if ( GetPosY ( ) < VOID_BOUNDARY )
{
TickInVoid ( * NextChunk ) ;
}
else
{
m_TicksSinceLastVoidDamage = 0 ;
}
2014-03-05 17:12:48 -05:00
2014-05-18 17:41:42 -04:00
if ( IsMob ( ) | | IsPlayer ( ) | | IsPickup ( ) | | IsExpOrb ( ) )
{
DetectCacti ( ) ;
}
2014-04-12 08:16:48 -04:00
if ( IsMob ( ) | | IsPlayer ( ) )
{
// Set swimming state
SetSwimState ( * NextChunk ) ;
2014-03-05 17:12:48 -05:00
2014-04-12 08:16:48 -04:00
// Handle drowning
HandleAir ( ) ;
2014-07-29 11:36:24 -04:00
}
2014-07-22 12:26:48 -04:00
if ( ! DetectPortal ( ) ) // Our chunk is invalid if we have moved to another world
{
// None of the above functions changed position, we remain in the chunk of NextChunk
HandlePhysics ( a_Dt , * NextChunk ) ;
2014-03-05 17:12:48 -05:00
}
2014-01-24 18:58:51 -05:00
}
2013-03-03 14:05:11 -05:00
}
2015-01-11 16:12:26 -05:00
void cEntity : : HandlePhysics ( std : : chrono : : milliseconds a_Dt , cChunk & a_Chunk )
2013-04-22 03:18:03 -04:00
{
2014-04-12 08:16:48 -04:00
int BlockX = POSX_TOINT ;
int BlockY = POSY_TOINT ;
int BlockZ = POSZ_TOINT ;
// Position changed -> super::HandlePhysics() called
GET_AND_VERIFY_CURRENT_CHUNK ( NextChunk , BlockX , BlockZ )
2013-06-18 04:24:34 -04:00
// TODO Add collision detection with entities.
2015-01-11 16:12:26 -05:00
auto DtSec = std : : chrono : : duration_cast < std : : chrono : : duration < double > > ( a_Dt ) ;
2014-04-12 08:16:48 -04:00
Vector3d NextPos = Vector3d ( GetPosX ( ) , GetPosY ( ) , GetPosZ ( ) ) ;
Vector3d NextSpeed = Vector3d ( GetSpeedX ( ) , GetSpeedY ( ) , GetSpeedZ ( ) ) ;
2013-06-18 04:24:34 -04:00
if ( ( BlockY > = cChunkDef : : Height ) | | ( BlockY < 0 ) )
{
2014-07-17 16:59:02 -04:00
// Outside of the world
2015-01-11 16:12:26 -05:00
AddSpeedY ( m_Gravity * DtSec . count ( ) ) ;
AddPosition ( GetSpeed ( ) * DtSec . count ( ) ) ;
2013-06-18 04:24:34 -04:00
return ;
}
2014-04-12 08:16:48 -04:00
int RelBlockX = BlockX - ( NextChunk - > GetPosX ( ) * cChunkDef : : Width ) ;
int RelBlockZ = BlockZ - ( NextChunk - > GetPosZ ( ) * cChunkDef : : Width ) ;
2014-07-21 09:19:48 -04:00
BLOCKTYPE BlockIn = NextChunk - > GetBlock ( RelBlockX , BlockY , RelBlockZ ) ;
2014-04-12 08:16:48 -04:00
BLOCKTYPE BlockBelow = ( BlockY > 0 ) ? NextChunk - > GetBlock ( RelBlockX , BlockY - 1 , RelBlockZ ) : E_BLOCK_AIR ;
2014-03-05 17:12:48 -05:00
if ( ! cBlockInfo : : IsSolid ( BlockIn ) ) // Making sure we are not inside a solid block
2013-04-22 03:18:03 -04:00
{
2014-03-05 17:12:48 -05:00
if ( m_bOnGround ) // check if it's still on the ground
2013-04-22 03:18:03 -04:00
{
2014-03-05 17:12:48 -05:00
if ( ! cBlockInfo : : IsSolid ( BlockBelow ) ) // Check if block below is air or water.
2013-04-22 03:18:03 -04:00
{
2014-03-05 17:12:48 -05:00
m_bOnGround = false ;
2013-04-22 03:18:03 -04:00
}
}
2014-03-05 17:12:48 -05:00
}
else
{
// Push out entity.
BLOCKTYPE GotBlock ;
2013-09-04 12:52:15 -04:00
2014-03-05 17:12:48 -05:00
static const struct
{
int x , y , z ;
} gCrossCoords [ ] =
{
{ 1 , 0 , 0 } ,
{ - 1 , 0 , 0 } ,
{ 0 , 0 , 1 } ,
{ 0 , 0 , - 1 } ,
} ;
bool IsNoAirSurrounding = true ;
for ( size_t i = 0 ; i < ARRAYCOUNT ( gCrossCoords ) ; i + + )
{
2014-04-12 08:16:48 -04:00
if ( ! NextChunk - > UnboundedRelGetBlockType ( RelBlockX + gCrossCoords [ i ] . x , BlockY , RelBlockZ + gCrossCoords [ i ] . z , GotBlock ) )
2013-09-11 17:10:29 -04:00
{
2014-03-05 17:12:48 -05:00
// The pickup is too close to an unloaded chunk, bail out of any physics handling
return ;
}
if ( ! cBlockInfo : : IsSolid ( GotBlock ) )
2013-09-15 14:49:08 -04:00
{
2014-03-05 17:12:48 -05:00
NextPos . x + = gCrossCoords [ i ] . x ;
NextPos . z + = gCrossCoords [ i ] . z ;
IsNoAirSurrounding = false ;
break ;
2013-09-15 14:49:08 -04:00
}
2014-03-05 17:12:48 -05:00
} // for i - gCrossCoords[]
if ( IsNoAirSurrounding )
{
NextPos . y + = 0.5 ;
}
2013-09-13 14:54:50 -04:00
2014-03-05 17:12:48 -05:00
m_bOnGround = true ;
2013-09-05 16:41:47 -04:00
2014-03-05 17:12:48 -05:00
/*
// DEBUG:
LOGD ( " Entity #%d (%s) is inside a block at {%d, %d, %d} " ,
m_UniqueID , GetClass ( ) , BlockX , BlockY , BlockZ
) ;
*/
}
2013-04-22 03:18:03 -04:00
2014-03-05 17:12:48 -05:00
if ( ! m_bOnGround )
{
2015-01-18 05:02:17 -05:00
double fallspeed ;
2014-03-05 17:12:48 -05:00
if ( IsBlockWater ( BlockIn ) )
2013-04-22 03:18:03 -04:00
{
2015-01-11 16:12:26 -05:00
fallspeed = m_Gravity * DtSec . count ( ) / 3 ; // Fall 3x slower in water
2015-01-18 05:02:17 -05:00
ApplyFriction ( NextSpeed , 0.7 , static_cast < float > ( DtSec . count ( ) ) ) ;
2014-03-05 17:12:48 -05:00
}
else if ( BlockIn = = E_BLOCK_COBWEB )
{
NextSpeed . y * = 0.05 ; // Reduce overall falling speed
2014-09-13 17:49:05 -04:00
fallspeed = 0 ; // No falling
2013-04-22 03:18:03 -04:00
}
else
{
2014-03-05 17:12:48 -05:00
// Normal gravity
2015-01-11 16:12:26 -05:00
fallspeed = m_Gravity * DtSec . count ( ) ;
2015-03-31 11:03:35 -04:00
NextSpeed - = NextSpeed * ( m_AirDrag * 20.0f ) * DtSec . count ( ) ;
2014-03-05 17:12:48 -05:00
}
2015-01-18 05:02:17 -05:00
NextSpeed . y + = static_cast < float > ( fallspeed ) ;
2014-03-05 17:12:48 -05:00
}
2014-09-12 15:50:24 -04:00
else
2014-03-05 17:12:48 -05:00
{
2015-01-18 05:02:17 -05:00
ApplyFriction ( NextSpeed , 0.7 , static_cast < float > ( DtSec . count ( ) ) ) ;
2014-03-05 17:12:48 -05:00
}
2013-05-21 01:51:38 -04:00
2014-03-05 17:12:48 -05:00
// Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
// might have different speed modifiers according to terrain.
if ( BlockIn = = E_BLOCK_COBWEB )
{
NextSpeed . x * = 0.25 ;
NextSpeed . z * = 0.25 ;
}
2014-07-17 16:15:34 -04:00
// Get water direction
2014-03-05 17:12:48 -05:00
Direction WaterDir = m_World - > GetWaterSimulator ( ) - > GetFlowingDirection ( BlockX , BlockY , BlockZ ) ;
2013-04-22 03:18:03 -04:00
2014-07-17 16:15:34 -04:00
m_WaterSpeed * = 0.9f ; // Reduce speed each tick
2013-04-22 03:18:03 -04:00
2014-07-21 09:19:48 -04:00
switch ( WaterDir )
2014-03-05 17:12:48 -05:00
{
case X_PLUS :
m_WaterSpeed . x = 0.2f ;
m_bOnGround = false ;
2013-04-22 03:18:03 -04:00
break ;
2014-03-05 17:12:48 -05:00
case X_MINUS :
m_WaterSpeed . x = - 0.2f ;
m_bOnGround = false ;
break ;
case Z_PLUS :
m_WaterSpeed . z = 0.2f ;
m_bOnGround = false ;
break ;
case Z_MINUS :
m_WaterSpeed . z = - 0.2f ;
m_bOnGround = false ;
break ;
default :
break ;
}
2013-04-22 03:18:03 -04:00
2014-03-05 17:12:48 -05:00
if ( fabs ( m_WaterSpeed . x ) < 0.05 )
{
m_WaterSpeed . x = 0 ;
}
2013-04-22 03:18:03 -04:00
2014-03-05 17:12:48 -05:00
if ( fabs ( m_WaterSpeed . z ) < 0.05 )
{
m_WaterSpeed . z = 0 ;
}
2013-04-22 03:18:03 -04:00
2014-03-05 17:12:48 -05:00
NextSpeed + = m_WaterSpeed ;
2013-04-22 03:18:03 -04:00
2015-03-30 19:42:32 -04:00
if ( NextSpeed . SqrLength ( ) > 0.0f )
2014-03-05 17:12:48 -05:00
{
2014-04-23 16:06:46 -04:00
cTracer Tracer ( GetWorld ( ) ) ;
2014-04-24 17:03:47 -04:00
// Distance traced is an integer, so we round up from the distance we should go (Speed * Delta), else we will encounter collision detection failurse
2015-01-11 16:12:26 -05:00
int DistanceToTrace = ( int ) ( ceil ( ( NextSpeed * DtSec . count ( ) ) . SqrLength ( ) ) * 2 ) ;
2014-04-24 17:03:47 -04:00
bool HasHit = Tracer . Trace ( NextPos , NextSpeed , DistanceToTrace ) ;
2014-04-23 16:06:46 -04:00
if ( HasHit )
2013-04-22 03:18:03 -04:00
{
2014-04-23 16:06:46 -04:00
// Oh noez! We hit something: verify that the (hit position - current) was smaller or equal to the (position that we should travel without obstacles - current)
// This is because previously, we traced with a length that was rounded up (due to integer limitations), and in the case that something was hit, we don't want to overshoot our projected movement
2015-01-11 16:12:26 -05:00
if ( ( Tracer . RealHit - NextPos ) . SqrLength ( ) < = ( NextSpeed * DtSec . count ( ) ) . SqrLength ( ) )
2013-04-22 03:18:03 -04:00
{
2014-04-23 16:06:46 -04:00
// Block hit was within our projected path
// Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1.
2014-04-24 17:03:47 -04:00
// For example: HitNormal.y = -1 : BLOCK_FACE_YM; HitNormal.y = 1 : BLOCK_FACE_YP
2015-03-30 19:42:32 -04:00
if ( Tracer . HitNormal . x ! = 0.0f )
2014-12-05 10:59:11 -05:00
{
2015-03-30 19:42:32 -04:00
NextSpeed . x = 0.0f ;
2014-12-05 10:59:11 -05:00
}
2015-03-30 19:42:32 -04:00
if ( Tracer . HitNormal . y ! = 0.0f )
2014-12-05 10:59:11 -05:00
{
2015-03-30 19:42:32 -04:00
NextSpeed . y = 0.0f ;
2014-12-05 10:59:11 -05:00
}
2015-03-30 19:42:32 -04:00
if ( Tracer . HitNormal . z ! = 0.0f )
2014-12-05 10:59:11 -05:00
{
2015-03-30 19:42:32 -04:00
NextSpeed . z = 0.0f ;
2014-12-05 10:59:11 -05:00
}
2014-03-19 19:07:16 -04:00
2015-03-30 19:42:32 -04:00
if ( Tracer . HitNormal . y = = 1.0f ) // Hit BLOCK_FACE_YP, we are on the ground
2013-04-22 03:18:03 -04:00
{
2014-03-19 19:07:16 -04:00
m_bOnGround = true ;
2013-09-12 18:31:26 -04:00
}
2014-04-23 16:06:46 -04:00
// Now, set our position to the hit block (i.e. move part way along our intended trajectory)
NextPos . Set ( Tracer . RealHit . x , Tracer . RealHit . y , Tracer . RealHit . z ) ;
NextPos . x + = Tracer . HitNormal . x * 0.1 ;
NextPos . y + = Tracer . HitNormal . y * 0.05 ;
NextPos . z + = Tracer . HitNormal . z * 0.1 ;
2013-04-22 03:18:03 -04:00
}
else
2013-06-18 04:24:34 -04:00
{
2014-04-23 16:06:46 -04:00
// We have hit a block but overshot our intended trajectory, move normally, safe in the warm cocoon of knowledge that we won't appear to teleport forwards on clients,
// and that this piece of software will come to be hailed as the epitome of performance and functionality in C++, never before seen, and of such a like that will never
// be henceforth seen again in the time of programmers and man alike
// </&sensationalist>
2015-01-11 16:12:26 -05:00
NextPos + = ( NextSpeed * DtSec . count ( ) ) ;
2013-04-22 03:18:03 -04:00
}
}
2014-03-05 17:12:48 -05:00
else
2013-04-22 03:18:03 -04:00
{
2014-03-05 17:12:48 -05:00
// We didn't hit anything, so move =]
2015-01-11 16:12:26 -05:00
NextPos + = ( NextSpeed * DtSec . count ( ) ) ;
2013-04-22 03:18:03 -04:00
}
}
2014-03-05 17:12:48 -05:00
2014-04-12 08:16:48 -04:00
SetPosition ( NextPos ) ;
SetSpeed ( NextSpeed ) ;
2013-04-22 03:18:03 -04:00
}
2014-09-13 17:49:05 -04:00
void cEntity : : ApplyFriction ( Vector3d & a_Speed , double a_SlowdownMultiplier , float a_Dt )
{
if ( a_Speed . SqrLength ( ) > 0.0004f )
{
a_Speed . x * = a_SlowdownMultiplier / ( 1 + a_Dt ) ;
if ( fabs ( a_Speed . x ) < 0.05 )
{
a_Speed . x = 0 ;
}
a_Speed . z * = a_SlowdownMultiplier / ( 1 + a_Dt ) ;
if ( fabs ( a_Speed . z ) < 0.05 )
{
a_Speed . z = 0 ;
}
}
}
2013-07-01 06:39:56 -04:00
void cEntity : : TickBurning ( cChunk & a_Chunk )
{
2013-07-01 14:37:27 -04:00
// Remember the current burning state:
bool HasBeenBurning = ( m_TicksLeftBurning > 0 ) ;
2014-04-18 07:59:14 -04:00
2014-06-21 15:42:10 -04:00
if ( GetWorld ( ) - > IsWeatherWetAt ( POSX_TOINT , POSZ_TOINT ) )
2014-04-18 07:59:14 -04:00
{
2014-04-25 02:58:48 -04:00
if ( POSY_TOINT > m_World - > GetHeight ( POSX_TOINT , POSZ_TOINT ) )
2014-04-18 07:59:14 -04:00
{
m_TicksLeftBurning = 0 ;
2014-07-17 16:59:02 -04:00
}
2014-04-18 07:59:14 -04:00
}
2013-07-01 14:37:27 -04:00
2013-07-01 06:39:56 -04:00
// Do the burning damage:
if ( m_TicksLeftBurning > 0 )
{
m_TicksSinceLastBurnDamage + + ;
if ( m_TicksSinceLastBurnDamage > = BURN_TICKS_PER_DAMAGE )
{
2014-04-22 08:36:39 -04:00
if ( ! m_IsFireproof )
{
2014-10-20 16:55:07 -04:00
TakeDamage ( dtOnFire , nullptr , BURN_DAMAGE , 0 ) ;
2014-04-22 08:36:39 -04:00
}
2013-07-01 14:37:27 -04:00
m_TicksSinceLastBurnDamage = 0 ;
2013-07-01 06:39:56 -04:00
}
m_TicksLeftBurning - - ;
}
// Update the burning times, based on surroundings:
int MinRelX = ( int ) floor ( GetPosX ( ) - m_Width / 2 ) - a_Chunk . GetPosX ( ) * cChunkDef : : Width ;
int MaxRelX = ( int ) floor ( GetPosX ( ) + m_Width / 2 ) - a_Chunk . GetPosX ( ) * cChunkDef : : Width ;
int MinRelZ = ( int ) floor ( GetPosZ ( ) - m_Width / 2 ) - a_Chunk . GetPosZ ( ) * cChunkDef : : Width ;
int MaxRelZ = ( int ) floor ( GetPosZ ( ) + m_Width / 2 ) - a_Chunk . GetPosZ ( ) * cChunkDef : : Width ;
2014-04-17 13:50:25 -04:00
int MinY = std : : max ( 0 , std : : min ( cChunkDef : : Height - 1 , POSY_TOINT ) ) ;
2013-07-01 06:39:56 -04:00
int MaxY = std : : max ( 0 , std : : min ( cChunkDef : : Height - 1 , ( int ) ceil ( GetPosY ( ) + m_Height ) ) ) ;
bool HasWater = false ;
bool HasLava = false ;
bool HasFire = false ;
for ( int x = MinRelX ; x < = MaxRelX ; x + + )
{
for ( int z = MinRelZ ; z < = MaxRelZ ; z + + )
{
int RelX = x ;
int RelZ = z ;
2014-03-05 17:12:48 -05:00
2013-07-01 06:39:56 -04:00
for ( int y = MinY ; y < = MaxY ; y + + )
{
2014-03-05 17:12:48 -05:00
BLOCKTYPE Block ;
a_Chunk . UnboundedRelGetBlockType ( RelX , y , RelZ , Block ) ;
switch ( Block )
2013-07-01 06:39:56 -04:00
{
case E_BLOCK_FIRE :
{
HasFire = true ;
break ;
}
case E_BLOCK_LAVA :
case E_BLOCK_STATIONARY_LAVA :
{
HasLava = true ;
break ;
}
case E_BLOCK_STATIONARY_WATER :
case E_BLOCK_WATER :
{
HasWater = true ;
break ;
}
} // switch (BlockType)
} // for y
} // for z
} // for x
if ( HasWater )
{
// Extinguish the fire
m_TicksLeftBurning = 0 ;
}
if ( HasLava )
{
// Burn:
m_TicksLeftBurning = BURN_TICKS ;
// Periodically damage:
m_TicksSinceLastLavaDamage + + ;
2013-07-01 14:37:27 -04:00
if ( m_TicksSinceLastLavaDamage > = LAVA_TICKS_PER_DAMAGE )
2013-07-01 06:39:56 -04:00
{
2014-04-22 08:36:39 -04:00
if ( ! m_IsFireproof )
{
2014-10-20 16:55:07 -04:00
TakeDamage ( dtLavaContact , nullptr , LAVA_DAMAGE , 0 ) ;
2014-04-22 08:36:39 -04:00
}
2013-07-01 06:39:56 -04:00
m_TicksSinceLastLavaDamage = 0 ;
}
}
else
{
m_TicksSinceLastLavaDamage = 0 ;
}
if ( HasFire )
{
// Burn:
m_TicksLeftBurning = BURN_TICKS ;
// Periodically damage:
m_TicksSinceLastFireDamage + + ;
if ( m_TicksSinceLastFireDamage > = FIRE_TICKS_PER_DAMAGE )
{
2014-04-22 08:36:39 -04:00
if ( ! m_IsFireproof )
{
2014-10-20 16:55:07 -04:00
TakeDamage ( dtFireContact , nullptr , FIRE_DAMAGE , 0 ) ;
2014-04-22 08:36:39 -04:00
}
2013-07-01 14:37:27 -04:00
m_TicksSinceLastFireDamage = 0 ;
2013-07-01 06:39:56 -04:00
}
}
else
{
m_TicksSinceLastFireDamage = 0 ;
}
// If just started / finished burning, notify descendants:
if ( ( m_TicksLeftBurning > 0 ) & & ! HasBeenBurning )
{
OnStartedBurning ( ) ;
}
else if ( ( m_TicksLeftBurning < = 0 ) & & HasBeenBurning )
{
OnFinishedBurning ( ) ;
}
}
2013-09-10 18:01:02 -04:00
void cEntity : : TickInVoid ( cChunk & a_Chunk )
{
if ( m_TicksSinceLastVoidDamage = = 20 )
{
2014-10-20 16:55:07 -04:00
TakeDamage ( dtInVoid , nullptr , 2 , 0 ) ;
2013-09-10 18:01:02 -04:00
m_TicksSinceLastVoidDamage = 0 ;
}
else
{
m_TicksSinceLastVoidDamage + + ;
}
}
2014-05-23 06:33:30 -04:00
void cEntity : : DetectCacti ( void )
2014-05-18 17:41:42 -04:00
{
int X = POSX_TOINT , Y = POSY_TOINT , Z = POSZ_TOINT ;
2014-05-23 06:33:30 -04:00
double w = m_Width / 2 ;
2014-05-18 17:41:42 -04:00
if (
2014-05-19 17:15:39 -04:00
( ( Y > 0 ) & & ( Y < cChunkDef : : Height ) ) & &
( ( ( ( X + 1 ) - GetPosX ( ) < w ) & & ( GetWorld ( ) - > GetBlock ( X + 1 , Y , Z ) = = E_BLOCK_CACTUS ) ) | |
2014-05-19 16:17:28 -04:00
( ( GetPosX ( ) - X < w ) & & ( GetWorld ( ) - > GetBlock ( X - 1 , Y , Z ) = = E_BLOCK_CACTUS ) ) | |
2014-05-19 05:35:21 -04:00
( ( ( Z + 1 ) - GetPosZ ( ) < w ) & & ( GetWorld ( ) - > GetBlock ( X , Y , Z + 1 ) = = E_BLOCK_CACTUS ) ) | |
2014-05-19 16:17:28 -04:00
( ( GetPosZ ( ) - Z < w ) & & ( GetWorld ( ) - > GetBlock ( X , Y , Z - 1 ) = = E_BLOCK_CACTUS ) ) | |
2014-05-19 17:15:39 -04:00
( ( ( GetPosY ( ) - Y < 1 ) & & ( GetWorld ( ) - > GetBlock ( X , Y , Z ) = = E_BLOCK_CACTUS ) ) ) )
2014-05-18 17:41:42 -04:00
)
{
2014-10-20 16:55:07 -04:00
TakeDamage ( dtCactusContact , nullptr , 1 , 0 ) ;
2014-05-18 17:41:42 -04:00
}
}
2015-05-26 21:35:28 -04:00
void cEntity : : ScheduleMoveToWorld ( cWorld * a_World , Vector3d a_NewPosition )
{
m_NewWorld = a_World ;
m_NewWorldPosition = a_NewPosition ;
m_IsWorldChangeScheduled = true ;
}
2014-05-18 17:41:42 -04:00
2014-07-22 12:26:48 -04:00
bool cEntity : : DetectPortal ( )
2014-05-31 17:28:51 -04:00
{
2015-05-26 21:35:28 -04:00
// If somebody scheduled a world change with ScheduleMoveToWorld, change worlds now.
if ( m_IsWorldChangeScheduled )
{
m_IsWorldChangeScheduled = false ;
MoveToWorld ( m_NewWorld , false , m_NewWorldPosition ) ;
return true ;
}
2014-07-20 05:46:45 -04:00
if ( GetWorld ( ) - > GetDimension ( ) = = dimOverworld )
2014-06-04 15:00:55 -04:00
{
2015-03-21 10:20:31 -04:00
if ( GetWorld ( ) - > GetLinkedNetherWorldName ( ) . empty ( ) & & GetWorld ( ) - > GetLinkedEndWorldName ( ) . empty ( ) )
2014-07-21 17:49:06 -04:00
{
2014-07-23 16:12:59 -04:00
// Teleportation to either dimension not enabled, don't bother proceeding
2014-07-22 12:26:48 -04:00
return false ;
2014-07-21 17:49:06 -04:00
}
}
else if ( GetWorld ( ) - > GetLinkedOverworldName ( ) . empty ( ) )
{
2014-07-23 16:12:59 -04:00
// Overworld teleportation disabled, abort
2014-07-22 12:26:48 -04:00
return false ;
2014-06-04 15:00:55 -04:00
}
2014-05-31 17:28:51 -04:00
int X = POSX_TOINT , Y = POSY_TOINT , Z = POSZ_TOINT ;
if ( ( Y > 0 ) & & ( Y < cChunkDef : : Height ) )
{
switch ( GetWorld ( ) - > GetBlock ( X , Y , Z ) )
{
case E_BLOCK_NETHER_PORTAL :
{
2014-07-21 17:49:06 -04:00
if ( m_PortalCooldownData . m_ShouldPreventTeleportation )
2014-06-04 15:00:55 -04:00
{
2014-07-23 16:12:59 -04:00
// Just exited a portal, don't teleport again
2014-07-22 12:26:48 -04:00
return false ;
2014-06-04 15:00:55 -04:00
}
2014-12-05 06:56:53 -05:00
if ( IsPlayer ( ) & & ! ( ( cPlayer * ) this ) - > IsGameModeCreative ( ) & & ( m_PortalCooldownData . m_TicksDelayed ! = 80 ) )
2014-06-12 10:21:07 -04:00
{
2014-07-23 16:12:59 -04:00
// Delay teleportation for four seconds if the entity is a non-creative player
2014-07-21 17:49:06 -04:00
m_PortalCooldownData . m_TicksDelayed + + ;
2014-07-22 12:26:48 -04:00
return false ;
2014-06-12 10:21:07 -04:00
}
2014-07-21 17:49:06 -04:00
m_PortalCooldownData . m_TicksDelayed = 0 ;
2014-06-12 10:21:07 -04:00
2014-08-03 00:44:02 -04:00
if ( GetWorld ( ) - > GetDimension ( ) = = dimNether )
2014-05-31 17:28:51 -04:00
{
2014-08-03 00:44:02 -04:00
if ( GetWorld ( ) - > GetLinkedOverworldName ( ) . empty ( ) )
2014-06-12 10:21:07 -04:00
{
2014-08-03 00:44:02 -04:00
return false ;
2014-06-12 10:21:07 -04:00
}
2014-08-03 00:44:02 -04:00
m_PortalCooldownData . m_ShouldPreventTeleportation = true ; // Stop portals from working on respawn
if ( IsPlayer ( ) )
2014-05-31 17:28:51 -04:00
{
2015-05-09 03:25:09 -04:00
// Send a respawn packet before world is loaded / generated so the client isn't left in limbo
( ( cPlayer * ) this ) - > GetClientHandle ( ) - > SendRespawn ( dimOverworld ) ;
2014-05-31 17:28:51 -04:00
}
2015-05-26 21:35:28 -04:00
Vector3d TargetPos = GetPosition ( ) ;
TargetPos . x * = 8.0 ;
TargetPos . z * = 8.0 ;
cWorld * TargetWorld = cRoot : : Get ( ) - > CreateAndInitializeWorld ( GetWorld ( ) - > GetLinkedOverworldName ( ) , dimNether , GetWorld ( ) - > GetName ( ) ) ;
LOGD ( " Jumping nether -> overworld " ) ;
new cNetherPortalScanner ( this , TargetWorld , TargetPos , 256 ) ;
return false ;
2014-08-03 00:44:02 -04:00
}
else
{
2015-03-21 10:20:31 -04:00
if ( GetWorld ( ) - > GetLinkedNetherWorldName ( ) . empty ( ) )
2014-08-03 00:44:02 -04:00
{
return false ;
}
m_PortalCooldownData . m_ShouldPreventTeleportation = true ;
if ( IsPlayer ( ) )
{
( ( cPlayer * ) this ) - > AwardAchievement ( achEnterPortal ) ;
( ( cPlayer * ) this ) - > GetClientHandle ( ) - > SendRespawn ( dimNether ) ;
}
2015-05-26 21:35:28 -04:00
Vector3d TargetPos = GetPosition ( ) ;
TargetPos . x / = 8.0 ;
TargetPos . z / = 8.0 ;
cWorld * TargetWorld = cRoot : : Get ( ) - > CreateAndInitializeWorld ( GetWorld ( ) - > GetLinkedNetherWorldName ( ) , dimNether , GetWorld ( ) - > GetName ( ) ) ;
LOGD ( " Jumping overworld -> nether " ) ;
new cNetherPortalScanner ( this , TargetWorld , TargetPos , 128 ) ;
return false ;
2014-05-31 17:28:51 -04:00
}
}
case E_BLOCK_END_PORTAL :
{
2014-07-21 17:49:06 -04:00
if ( m_PortalCooldownData . m_ShouldPreventTeleportation )
2014-06-04 15:00:55 -04:00
{
2014-07-22 12:26:48 -04:00
return false ;
2014-06-04 15:00:55 -04:00
}
2014-08-03 00:44:02 -04:00
if ( GetWorld ( ) - > GetDimension ( ) = = dimEnd )
2014-05-31 17:28:51 -04:00
{
2014-08-03 00:44:02 -04:00
if ( GetWorld ( ) - > GetLinkedOverworldName ( ) . empty ( ) )
2014-05-31 17:28:51 -04:00
{
2014-08-03 00:44:02 -04:00
return false ;
}
2014-06-01 13:46:59 -04:00
2014-08-03 00:44:02 -04:00
m_PortalCooldownData . m_ShouldPreventTeleportation = true ;
2014-06-12 10:21:07 -04:00
2014-08-03 00:44:02 -04:00
if ( IsPlayer ( ) )
2014-05-31 17:28:51 -04:00
{
2014-08-03 00:44:02 -04:00
cPlayer * Player = ( cPlayer * ) this ;
Player - > TeleportToCoords ( Player - > GetLastBedPos ( ) . x , Player - > GetLastBedPos ( ) . y , Player - > GetLastBedPos ( ) . z ) ;
Player - > GetClientHandle ( ) - > SendRespawn ( dimOverworld ) ;
}
2014-07-20 05:46:45 -04:00
2014-08-03 00:44:02 -04:00
return MoveToWorld ( cRoot : : Get ( ) - > CreateAndInitializeWorld ( GetWorld ( ) - > GetLinkedOverworldName ( ) ) , false ) ;
}
else
{
2015-03-21 10:20:31 -04:00
if ( GetWorld ( ) - > GetLinkedEndWorldName ( ) . empty ( ) )
2014-08-03 00:44:02 -04:00
{
return false ;
}
2014-06-12 10:21:07 -04:00
2014-08-03 00:44:02 -04:00
m_PortalCooldownData . m_ShouldPreventTeleportation = true ;
2014-06-12 10:21:07 -04:00
2014-08-03 00:44:02 -04:00
if ( IsPlayer ( ) )
{
( ( cPlayer * ) this ) - > AwardAchievement ( achEnterTheEnd ) ;
( ( cPlayer * ) this ) - > GetClientHandle ( ) - > SendRespawn ( dimEnd ) ;
2014-05-31 17:28:51 -04:00
}
2014-08-03 00:44:02 -04:00
2015-03-21 10:20:31 -04:00
return MoveToWorld ( cRoot : : Get ( ) - > CreateAndInitializeWorld ( GetWorld ( ) - > GetLinkedEndWorldName ( ) , dimEnd , GetWorld ( ) - > GetName ( ) ) , false ) ;
2014-05-31 17:28:51 -04:00
}
2014-08-03 00:44:02 -04:00
2014-05-31 17:28:51 -04:00
}
default : break ;
}
}
2014-06-12 10:21:07 -04:00
// Allow portals to work again
2014-07-21 17:49:06 -04:00
m_PortalCooldownData . m_ShouldPreventTeleportation = false ;
2014-07-23 16:12:59 -04:00
m_PortalCooldownData . m_TicksDelayed = 0 ;
2014-07-22 12:26:48 -04:00
return false ;
2014-05-31 17:28:51 -04:00
}
2015-05-26 21:35:28 -04:00
bool cEntity : : DoMoveToWorld ( cWorld * a_World , bool a_ShouldSendRespawn , Vector3d a_NewPosition )
2014-05-31 17:28:51 -04:00
{
2014-06-12 10:21:07 -04:00
UNUSED ( a_ShouldSendRespawn ) ;
2014-10-20 16:55:07 -04:00
ASSERT ( a_World ! = nullptr ) ;
2014-06-12 10:21:07 -04:00
2014-07-21 17:49:06 -04:00
if ( GetWorld ( ) = = a_World )
2014-05-31 17:28:51 -04:00
{
2014-06-12 10:21:07 -04:00
// Don't move to same world
2014-05-31 17:28:51 -04:00
return false ;
}
2015-05-21 06:27:54 -04:00
// Ask the plugins if the entity is allowed to changing the world
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookEntityChangingWorld ( * this , * a_World ) )
2015-05-18 16:29:39 -04:00
{
2015-05-21 06:27:54 -04:00
// A Plugin doesn't allow the entity to changing the world
2015-05-18 16:29:39 -04:00
return false ;
}
2014-05-31 17:28:51 -04:00
// Remove all links to the old world
2014-07-23 16:12:59 -04:00
SetWorldTravellingFrom ( GetWorld ( ) ) ; // cChunk::Tick() handles entity removal
2014-05-31 17:28:51 -04:00
GetWorld ( ) - > BroadcastDestroyEntity ( * this ) ;
2015-05-26 21:35:28 -04:00
SetPosition ( a_NewPosition ) ;
2014-06-12 10:21:07 -04:00
// Queue add to new world
2014-07-21 17:49:06 -04:00
a_World - > AddEntity ( this ) ;
2015-05-18 16:29:39 -04:00
cWorld * OldWorld = cRoot : : Get ( ) - > GetWorld ( GetWorld ( ) - > GetName ( ) ) ; // Required for the hook HOOK_ENTITY_CHANGED_WORLD
2014-07-23 16:12:59 -04:00
SetWorld ( a_World ) ;
2014-05-31 17:28:51 -04:00
2015-05-18 16:29:39 -04:00
// Entity changed the world, call the hook
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookEntityChangedWorld ( * this , * OldWorld ) ;
2014-05-31 17:28:51 -04:00
return true ;
}
2014-07-21 17:49:06 -04:00
bool cEntity : : MoveToWorld ( const AString & a_WorldName , bool a_ShouldSendRespawn )
{
cWorld * World = cRoot : : Get ( ) - > GetWorld ( a_WorldName ) ;
2014-10-20 16:55:07 -04:00
if ( World = = nullptr )
2014-07-21 17:49:06 -04:00
{
LOG ( " %s: Couldn't find world \" %s \" . " , __FUNCTION__ , a_WorldName . c_str ( ) ) ;
return false ;
}
2015-05-26 21:35:28 -04:00
return DoMoveToWorld ( World , a_ShouldSendRespawn , GetPosition ( ) ) ;
2014-07-21 17:49:06 -04:00
}
2014-01-24 18:58:51 -05:00
void cEntity : : SetSwimState ( cChunk & a_Chunk )
{
2014-03-05 17:12:48 -05:00
int RelY = ( int ) floor ( GetPosY ( ) + 0.1 ) ;
2014-01-24 18:58:51 -05:00
if ( ( RelY < 0 ) | | ( RelY > = cChunkDef : : Height - 1 ) )
{
m_IsSwimming = false ;
m_IsSubmerged = false ;
return ;
}
BLOCKTYPE BlockIn ;
2014-03-05 17:12:48 -05:00
int RelX = POSX_TOINT - a_Chunk . GetPosX ( ) * cChunkDef : : Width ;
int RelZ = POSZ_TOINT - a_Chunk . GetPosZ ( ) * cChunkDef : : Width ;
2014-01-24 18:58:51 -05:00
// Check if the player is swimming:
if ( ! a_Chunk . UnboundedRelGetBlockType ( RelX , RelY , RelZ , BlockIn ) )
{
// This sometimes happens on Linux machines
// Ref.: http://forum.mc-server.org/showthread.php?tid=1244
2014-04-23 16:06:46 -04:00
LOGD ( " SetSwimState failure: RelX = %d, RelZ = %d, Pos = %.02f, %.02f} " ,
RelX , RelY , GetPosX ( ) , GetPosZ ( )
2014-04-24 17:03:47 -04:00
) ;
2014-01-24 18:58:51 -05:00
m_IsSwimming = false ;
m_IsSubmerged = false ;
return ;
}
m_IsSwimming = IsBlockWater ( BlockIn ) ;
// Check if the player is submerged:
VERIFY ( a_Chunk . UnboundedRelGetBlockType ( RelX , RelY + 1 , RelZ , BlockIn ) ) ;
m_IsSubmerged = IsBlockWater ( BlockIn ) ;
}
2014-06-16 10:12:50 -04:00
void cEntity : : DoSetSpeed ( double a_SpeedX , double a_SpeedY , double a_SpeedZ )
{
m_Speed . Set ( a_SpeedX , a_SpeedY , a_SpeedZ ) ;
WrapSpeed ( ) ;
}
2014-01-24 18:58:51 -05:00
void cEntity : : HandleAir ( void )
{
// Ref.: http://www.minecraftwiki.net/wiki/Chunk_format
// See if the entity is /submerged/ water (block above is water)
// Get the type of block the entity is standing in:
2014-08-22 05:49:49 -04:00
int RespirationLevel = GetEquippedHelmet ( ) . m_Enchantments . GetLevel ( cEnchantments : : enchRespiration ) ;
2014-01-24 18:58:51 -05:00
if ( IsSubmerged ( ) )
{
2014-07-17 16:15:34 -04:00
if ( ! IsPlayer ( ) ) // Players control themselves
2014-06-27 18:13:26 -04:00
{
2014-07-17 16:15:34 -04:00
SetSpeedY ( 1 ) ; // Float in the water
2014-06-27 18:13:26 -04:00
}
2014-01-24 18:58:51 -05:00
2014-08-22 05:49:49 -04:00
if ( RespirationLevel > 0 )
{
( ( cPawn * ) this ) - > AddEntityEffect ( cEntityEffect : : effNightVision , 200 , 5 , 0 ) ;
}
2014-08-03 01:35:29 -04:00
if ( m_AirLevel < = 0 )
2014-01-24 18:58:51 -05:00
{
2014-08-03 05:20:48 -04:00
// Runs the air tick timer to check whether the player should be damaged
2014-08-03 01:35:29 -04:00
if ( m_AirTickTimer < = 0 )
2014-01-24 18:58:51 -05:00
{
2014-07-17 16:50:58 -04:00
// Damage player
2014-10-20 16:55:07 -04:00
TakeDamage ( dtDrowning , nullptr , 1 , 1 , 0 ) ;
2014-01-24 18:58:51 -05:00
// Reset timer
m_AirTickTimer = DROWNING_TICKS ;
}
else
{
2014-04-12 08:16:48 -04:00
m_AirTickTimer - - ;
2014-01-24 18:58:51 -05:00
}
}
else
{
// Reduce air supply
2014-04-12 08:16:48 -04:00
m_AirLevel - - ;
2014-01-24 18:58:51 -05:00
}
}
else
{
// Set the air back to maximum
m_AirLevel = MAX_AIR_LEVEL ;
m_AirTickTimer = DROWNING_TICKS ;
2014-08-22 05:49:49 -04:00
if ( RespirationLevel > 0 )
{
m_AirTickTimer = DROWNING_TICKS + ( RespirationLevel * 15 * 20 ) ;
}
2014-01-24 18:58:51 -05:00
}
}
2013-07-01 06:39:56 -04:00
/// Called when the entity starts burning
void cEntity : : OnStartedBurning ( void )
{
// Broadcast the change:
2013-07-07 09:06:06 -04:00
m_World - > BroadcastEntityMetadata ( * this ) ;
2013-07-01 06:39:56 -04:00
}
/// Called when the entity finishes burning
void cEntity : : OnFinishedBurning ( void )
{
// Broadcast the change:
2013-07-07 09:06:06 -04:00
m_World - > BroadcastEntityMetadata ( * this ) ;
2013-07-01 06:39:56 -04:00
}
/// Sets the maximum value for the health
void cEntity : : SetMaxHealth ( int a_MaxHealth )
{
m_MaxHealth = a_MaxHealth ;
// Reset health, if too high:
2014-07-17 16:32:23 -04:00
m_Health = std : : min ( m_Health , a_MaxHealth ) ;
2013-07-01 06:39:56 -04:00
}
2014-04-22 18:59:31 -04:00
/// Sets whether the entity is fireproof
2014-04-23 03:12:37 -04:00
void cEntity : : SetIsFireproof ( bool a_IsFireproof )
2014-04-22 18:59:31 -04:00
{
m_IsFireproof = a_IsFireproof ;
}
2013-07-01 06:39:56 -04:00
/// Puts the entity on fire for the specified amount of ticks
void cEntity : : StartBurning ( int a_TicksLeftBurning )
{
if ( m_TicksLeftBurning > 0 )
{
// Already burning, top up the ticks left burning and bail out:
m_TicksLeftBurning = std : : max ( m_TicksLeftBurning , a_TicksLeftBurning ) ;
return ;
}
m_TicksLeftBurning = a_TicksLeftBurning ;
OnStartedBurning ( ) ;
}
/// Stops the entity from burning, resets all burning timers
void cEntity : : StopBurning ( void )
{
bool HasBeenBurning = ( m_TicksLeftBurning > 0 ) ;
m_TicksLeftBurning = 0 ;
m_TicksSinceLastBurnDamage = 0 ;
m_TicksSinceLastFireDamage = 0 ;
m_TicksSinceLastLavaDamage = 0 ;
// Notify if the entity has stopped burning
if ( HasBeenBurning )
{
OnFinishedBurning ( ) ;
}
}
void cEntity : : TeleportToEntity ( cEntity & a_Entity )
{
TeleportToCoords ( a_Entity . GetPosX ( ) , a_Entity . GetPosY ( ) , a_Entity . GetPosZ ( ) ) ;
}
void cEntity : : TeleportToCoords ( double a_PosX , double a_PosY , double a_PosZ )
{
2015-03-05 05:52:42 -05:00
// ask the plugins to allow teleport to the new position.
if ( ! cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookEntityTeleport ( * this , m_LastPos , Vector3d ( a_PosX , a_PosY , a_PosZ ) ) )
{
SetPosition ( a_PosX , a_PosY , a_PosZ ) ;
m_World - > BroadcastTeleportEntity ( * this ) ;
}
2013-07-01 06:39:56 -04:00
}
2013-03-22 02:33:10 -04:00
void cEntity : : BroadcastMovementUpdate ( const cClientHandle * a_Exclude )
{
2014-04-23 16:06:46 -04:00
// Process packet sending every two ticks
if ( GetWorld ( ) - > GetWorldAge ( ) % 2 = = 0 )
2013-04-07 02:53:17 -04:00
{
2014-04-23 16:06:46 -04:00
double SpeedSqr = GetSpeed ( ) . SqrLength ( ) ;
if ( SpeedSqr = = 0.0 )
2013-03-22 02:33:10 -04:00
{
2014-04-23 16:06:46 -04:00
// Speed is zero, send this to clients once only as well as an absolute position
if ( ! m_bHasSentNoSpeed )
{
m_World - > BroadcastEntityVelocity ( * this , a_Exclude ) ;
m_World - > BroadcastTeleportEntity ( * this , a_Exclude ) ;
m_bHasSentNoSpeed = true ;
}
2013-03-22 02:33:10 -04:00
}
else
{
2014-04-23 16:06:46 -04:00
// Movin'
m_World - > BroadcastEntityVelocity ( * this , a_Exclude ) ;
m_bHasSentNoSpeed = false ;
}
// TODO: Pickups move disgracefully if relative move packets are sent as opposed to just velocity. Have a system to send relmove only when SetPosXXX() is called with a large difference in position
int DiffX = ( int ) ( floor ( GetPosX ( ) * 32.0 ) - floor ( m_LastPos . x * 32.0 ) ) ;
int DiffY = ( int ) ( floor ( GetPosY ( ) * 32.0 ) - floor ( m_LastPos . y * 32.0 ) ) ;
int DiffZ = ( int ) ( floor ( GetPosZ ( ) * 32.0 ) - floor ( m_LastPos . z * 32.0 ) ) ;
2014-07-17 16:15:34 -04:00
if ( ( DiffX ! = 0 ) | | ( DiffY ! = 0 ) | | ( DiffZ ! = 0 ) ) // Have we moved?
2014-04-23 16:06:46 -04:00
{
2014-07-17 16:15:34 -04:00
if ( ( abs ( DiffX ) < = 127 ) & & ( abs ( DiffY ) < = 127 ) & & ( abs ( DiffZ ) < = 127 ) ) // Limitations of a Byte
2013-03-22 02:33:10 -04:00
{
2014-04-23 16:06:46 -04:00
// Difference within Byte limitations, use a relative move packet
2013-03-22 02:33:10 -04:00
if ( m_bDirtyOrientation )
{
2014-04-23 16:06:46 -04:00
m_World - > BroadcastEntityRelMoveLook ( * this , ( char ) DiffX , ( char ) DiffY , ( char ) DiffZ , a_Exclude ) ;
2013-03-22 02:33:10 -04:00
m_bDirtyOrientation = false ;
}
else
{
2014-04-23 16:06:46 -04:00
m_World - > BroadcastEntityRelMove ( * this , ( char ) DiffX , ( char ) DiffY , ( char ) DiffZ , a_Exclude ) ;
2013-03-22 02:33:10 -04:00
}
2015-05-09 03:25:09 -04:00
// Clients seem to store two positions, one for the velocity packet and one for the teleport / relmove packet
// The latter is only changed with a relmove / teleport, and m_LastPos stores this position
2014-04-23 16:06:46 -04:00
m_LastPos = GetPosition ( ) ;
2013-03-22 02:33:10 -04:00
}
2013-04-02 02:48:31 -04:00
else
{
2014-04-23 16:06:46 -04:00
// Too big a movement, do a teleport
m_World - > BroadcastTeleportEntity ( * this , a_Exclude ) ;
2014-07-17 16:15:34 -04:00
m_LastPos = GetPosition ( ) ; // See above
2014-04-23 16:06:46 -04:00
m_bDirtyOrientation = false ;
}
2013-04-02 02:48:31 -04:00
}
2014-04-23 16:06:46 -04:00
2013-04-02 02:48:31 -04:00
if ( m_bDirtyHead )
{
2014-04-23 16:06:46 -04:00
m_World - > BroadcastEntityHeadLook ( * this , a_Exclude ) ;
2013-04-02 02:48:31 -04:00
m_bDirtyHead = false ;
2013-03-22 02:33:10 -04:00
}
2014-04-23 16:06:46 -04:00
if ( m_bDirtyOrientation )
{
// Send individual update in case above (sending with rel-move packet) wasn't done
GetWorld ( ) - > BroadcastEntityLook ( * this , a_Exclude ) ;
m_bDirtyOrientation = false ;
}
2013-03-22 02:33:10 -04:00
}
}
2013-03-03 14:05:11 -05:00
void cEntity : : AttachTo ( cEntity * a_AttachTo )
{
if ( m_AttachedTo = = a_AttachTo )
{
// Already attached to that entity, nothing to do here
return ;
}
2014-10-20 16:55:07 -04:00
if ( m_AttachedTo ! = nullptr )
2014-01-12 18:23:36 -05:00
{
// Detach from any previous entity:
Detach ( ) ;
}
2013-04-02 02:48:31 -04:00
2013-03-03 14:05:11 -05:00
// Attach to the new entity:
m_AttachedTo = a_AttachTo ;
a_AttachTo - > m_Attachee = this ;
m_World - > BroadcastAttachEntity ( * this , a_AttachTo ) ;
}
void cEntity : : Detach ( void )
{
2014-10-20 16:55:07 -04:00
if ( m_AttachedTo = = nullptr )
2013-03-03 14:05:11 -05:00
{
// Attached to no entity, our work is done
return ;
}
2014-10-20 16:55:07 -04:00
m_AttachedTo - > m_Attachee = nullptr ;
m_AttachedTo = nullptr ;
m_World - > BroadcastAttachEntity ( * this , nullptr ) ;
2012-12-22 05:15:53 -05:00
}
2012-12-21 07:21:20 -05:00
bool cEntity : : IsA ( const char * a_ClassName ) const
2012-06-14 09:06:06 -04:00
{
2012-12-21 07:21:20 -05:00
return ( strcmp ( a_ClassName , " cEntity " ) = = 0 ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-03 14:05:11 -05:00
void cEntity : : SetRot ( const Vector3f & a_Rot )
2012-06-14 09:06:06 -04:00
{
m_Rot = a_Rot ;
m_bDirtyOrientation = true ;
}
2013-04-02 02:48:31 -04:00
void cEntity : : SetHeadYaw ( double a_HeadYaw )
{
m_HeadYaw = a_HeadYaw ;
m_bDirtyHead = true ;
WrapHeadYaw ( ) ;
}
2013-05-21 08:58:21 -04:00
void cEntity : : SetHeight ( double a_Height )
{
m_Height = a_Height ;
}
2013-04-28 14:54:43 -04:00
void cEntity : : SetMass ( double a_Mass )
{
2014-08-03 01:35:29 -04:00
// Make sure that mass is not zero. 1g is the default because we
// have to choose a number. It's perfectly legal to have a mass
// less than 1g as long as is NOT equal or less than zero.
m_Mass = std : : max ( a_Mass , 0.001 ) ;
2013-04-28 14:54:43 -04:00
}
2013-11-02 12:45:48 -04:00
void cEntity : : SetYaw ( double a_Yaw )
2012-06-14 09:06:06 -04:00
{
2013-11-02 12:45:48 -04:00
m_Rot . x = a_Yaw ;
2012-06-14 09:06:06 -04:00
m_bDirtyOrientation = true ;
2013-04-03 01:11:00 -04:00
WrapRotation ( ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-09 09:35:43 -05:00
void cEntity : : SetPitch ( double a_Pitch )
2012-06-14 09:06:06 -04:00
{
m_Rot . y = a_Pitch ;
m_bDirtyOrientation = true ;
2013-04-03 01:11:00 -04:00
WrapRotation ( ) ;
2012-06-14 09:06:06 -04:00
}
2013-03-09 09:35:43 -05:00
void cEntity : : SetRoll ( double a_Roll )
2012-06-14 09:06:06 -04:00
{
m_Rot . z = a_Roll ;
m_bDirtyOrientation = true ;
}
2013-03-09 09:35:43 -05:00
void cEntity : : SetSpeed ( double a_SpeedX , double a_SpeedY , double a_SpeedZ )
{
2014-06-16 10:12:50 -04:00
DoSetSpeed ( a_SpeedX , a_SpeedY , a_SpeedZ ) ;
2013-03-09 09:35:43 -05:00
}
2013-03-22 02:33:10 -04:00
void cEntity : : SetSpeedX ( double a_SpeedX )
{
2014-06-16 10:12:50 -04:00
SetSpeed ( a_SpeedX , m_Speed . y , m_Speed . z ) ;
2013-03-22 02:33:10 -04:00
}
void cEntity : : SetSpeedY ( double a_SpeedY )
{
2014-06-16 10:12:50 -04:00
SetSpeed ( m_Speed . x , a_SpeedY , m_Speed . z ) ;
2013-03-22 02:33:10 -04:00
}
void cEntity : : SetSpeedZ ( double a_SpeedZ )
{
2014-06-16 10:12:50 -04:00
SetSpeed ( m_Speed . x , m_Speed . y , a_SpeedZ ) ;
2013-03-22 02:33:10 -04:00
}
2013-03-09 09:35:43 -05:00
2013-05-21 08:58:21 -04:00
void cEntity : : SetWidth ( double a_Width )
{
m_Width = a_Width ;
}
2013-03-23 00:33:47 -04:00
void cEntity : : AddPosX ( double a_AddPosX )
{
m_Pos . x + = a_AddPosX ;
}
void cEntity : : AddPosY ( double a_AddPosY )
{
m_Pos . y + = a_AddPosY ;
}
void cEntity : : AddPosZ ( double a_AddPosZ )
{
m_Pos . z + = a_AddPosZ ;
}
void cEntity : : AddPosition ( double a_AddPosX , double a_AddPosY , double a_AddPosZ )
{
m_Pos . x + = a_AddPosX ;
m_Pos . y + = a_AddPosY ;
m_Pos . z + = a_AddPosZ ;
}
void cEntity : : AddSpeed ( double a_AddSpeedX , double a_AddSpeedY , double a_AddSpeedZ )
{
2015-02-25 20:56:45 -05:00
DoSetSpeed ( m_Speed . x + a_AddSpeedX , m_Speed . y + a_AddSpeedY , m_Speed . z + a_AddSpeedZ ) ;
2013-03-23 00:33:47 -04:00
}
void cEntity : : AddSpeedX ( double a_AddSpeedX )
{
2015-02-25 20:56:45 -05:00
AddSpeed ( a_AddSpeedX , 0 , 0 ) ;
2013-03-23 00:33:47 -04:00
}
void cEntity : : AddSpeedY ( double a_AddSpeedY )
{
2015-02-25 20:56:45 -05:00
AddSpeed ( 0 , a_AddSpeedY , 0 ) ;
2013-03-23 00:33:47 -04:00
}
void cEntity : : AddSpeedZ ( double a_AddSpeedZ )
2013-03-03 14:05:11 -05:00
{
2015-02-25 20:56:45 -05:00
AddSpeed ( 0 , 0 , a_AddSpeedZ ) ;
2013-03-03 14:05:11 -05:00
}
2013-12-16 12:02:33 -05:00
void cEntity : : HandleSpeedFromAttachee ( float a_Forward , float a_Sideways )
{
Vector3d LookVector = m_Attachee - > GetLookVector ( ) ;
double AddSpeedX = LookVector . x * a_Forward + LookVector . z * a_Sideways ;
double AddSpeedZ = LookVector . z * a_Forward - LookVector . x * a_Sideways ;
SetSpeed ( AddSpeedX , 0 , AddSpeedZ ) ;
BroadcastMovementUpdate ( ) ;
}
2013-09-05 18:04:49 -04:00
void cEntity : : SteerVehicle ( float a_Forward , float a_Sideways )
{
2014-10-20 16:55:07 -04:00
if ( m_AttachedTo = = nullptr )
2013-09-05 18:04:49 -04:00
{
return ;
}
2015-03-30 19:42:32 -04:00
if ( ( a_Forward ! = 0.0f ) | | ( a_Sideways ! = 0.0f ) )
2013-09-05 18:04:49 -04:00
{
2013-12-16 12:02:33 -05:00
m_AttachedTo - > HandleSpeedFromAttachee ( a_Forward , a_Sideways ) ;
2013-09-05 18:04:49 -04:00
}
}
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 09:06:06 -04:00
// Get look vector (this is NOT a rotation!)
2013-03-09 09:35:43 -05:00
Vector3d cEntity : : GetLookVector ( void ) const
2012-06-14 09:06:06 -04:00
{
2013-03-09 09:35:43 -05:00
Matrix4d m ;
2014-04-05 16:34:05 -04:00
m . Init ( Vector3d ( ) , 0 , m_Rot . x , - m_Rot . y ) ;
2013-03-09 09:35:43 -05:00
Vector3d Look = m . Transform ( Vector3d ( 0 , 0 , 1 ) ) ;
2012-06-14 09:06:06 -04:00
return Look ;
}
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 09:06:06 -04:00
// Set position
2012-12-21 05:59:59 -05:00
void cEntity : : SetPosition ( double a_PosX , double a_PosY , double a_PosZ )
2012-06-14 09:06:06 -04:00
{
2014-07-17 16:50:58 -04:00
m_Pos . Set ( a_PosX , a_PosY , a_PosZ ) ;
2012-06-14 09:06:06 -04:00
}
2012-12-21 05:59:59 -05:00
void cEntity : : SetPosX ( double a_PosX )
2012-06-14 09:06:06 -04:00
{
2014-07-17 16:50:58 -04:00
m_Pos . x = a_PosX ;
2012-06-14 09:06:06 -04:00
}
2012-12-21 05:59:59 -05:00
void cEntity : : SetPosY ( double a_PosY )
2012-06-14 09:06:06 -04:00
{
2014-07-17 16:50:58 -04:00
m_Pos . y = a_PosY ;
2012-06-14 09:06:06 -04:00
}
2012-12-21 05:59:59 -05:00
void cEntity : : SetPosZ ( double a_PosZ )
2012-06-14 09:06:06 -04:00
{
m_Pos . z = a_PosZ ;
}