2014-04-26 19:45:39 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
# include "Player.h"
2014-04-27 20:03:06 -04:00
# include "ArrowEntity.h"
2014-04-26 19:45:39 -04:00
# include "../Chunk.h"
cArrowEntity : : cArrowEntity ( cEntity * a_Creator , double a_X , double a_Y , double a_Z , const Vector3d & a_Speed ) :
2014-04-27 20:03:06 -04:00
super ( pkArrow , a_Creator , a_X , a_Y , a_Z , 0.5 , 0.5 ) ,
m_PickupState ( psNoPickup ) ,
m_DamageCoeff ( 2 ) ,
m_IsCritical ( false ) ,
m_Timer ( 0 ) ,
m_HitGroundTimer ( 0 ) ,
2014-05-09 11:56:29 -04:00
m_HasTeleported ( false ) ,
2014-04-27 20:03:06 -04:00
m_bIsCollected ( false ) ,
m_HitBlockPos ( Vector3i ( 0 , 0 , 0 ) )
2014-04-26 19:45:39 -04:00
{
SetSpeed ( a_Speed ) ;
SetMass ( 0.1 ) ;
SetYawFromSpeed ( ) ;
SetPitchFromSpeed ( ) ;
LOGD ( " Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f} " ,
2014-06-16 16:57:13 -04:00
m_UniqueID , GetSpeedX ( ) , GetSpeedY ( ) , GetSpeedZ ( ) ,
GetYaw ( ) , GetPitch ( )
) ;
2014-04-26 19:45:39 -04:00
}
cArrowEntity : : cArrowEntity ( cPlayer & a_Player , double a_Force ) :
2014-04-27 20:03:06 -04:00
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_IsCritical ( ( a_Force > = 1 ) ) ,
m_Timer ( 0 ) ,
m_HitGroundTimer ( 0 ) ,
m_HasTeleported ( false ) ,
m_bIsCollected ( false ) ,
m_HitBlockPos ( 0 , 0 , 0 )
2014-04-26 19:45:39 -04:00
{
2014-06-16 16:57:13 -04:00
if ( a_Player . IsGameModeCreative ( ) )
{
m_PickupState = psInCreative ;
}
2014-04-26 19:45:39 -04:00
}
bool cArrowEntity : : CanPickup ( const cPlayer & a_Player ) const
{
switch ( m_PickupState )
{
case psNoPickup : return false ;
case psInSurvivalOrCreative : return ( a_Player . IsGameModeSurvival ( ) | | a_Player . IsGameModeCreative ( ) ) ;
case psInCreative : return a_Player . IsGameModeCreative ( ) ;
}
ASSERT ( ! " Unhandled pickup state " ) ;
return false ;
}
2014-08-19 11:57:32 -04:00
2014-04-26 19:45:39 -04:00
void cArrowEntity : : OnHitSolidBlock ( const Vector3d & a_HitPos , eBlockFace a_HitFace )
2014-07-17 16:50:58 -04:00
{
2014-07-04 07:50:40 -04:00
if ( GetSpeed ( ) . EqualsEps ( Vector3d ( 0 , 0 , 0 ) , 0.0000001 ) )
2014-04-26 19:45:39 -04:00
{
2014-07-17 16:15:34 -04:00
SetSpeed ( GetLookVector ( ) . NormalizeCopy ( ) * 0.1 ) ; // Ensure that no division by zero happens later
2014-04-26 19:45:39 -04:00
}
2014-07-02 16:07:34 -04:00
2014-06-22 15:44:01 -04:00
Vector3d Hit = a_HitPos ;
2014-07-14 16:56:40 -04:00
Vector3d SinkMovement = ( GetSpeed ( ) / 1000 ) ;
2014-07-17 16:15:34 -04:00
Hit + = SinkMovement * ( 0.0005 / SinkMovement . Length ( ) ) ; // Make arrow sink into block a centimetre so it lodges (but not to far so it goes black clientside)
2014-06-22 15:44:01 -04:00
super : : OnHitSolidBlock ( Hit , a_HitFace ) ;
2014-07-01 17:39:37 -04:00
Vector3i BlockHit = Hit . Floor ( ) ;
2014-06-30 14:21:21 -04:00
2014-07-01 17:39:37 -04:00
int X = BlockHit . x , Y = BlockHit . y , Z = BlockHit . z ;
2014-06-22 15:44:01 -04:00
m_HitBlockPos = Vector3i ( X , Y , Z ) ;
2014-04-26 19:45:39 -04:00
// Broadcast arrow hit sound
2014-07-12 20:08:02 -04:00
m_World - > BroadcastSoundEffect ( " random.bowhit " , ( double ) X , ( double ) Y , ( double ) Z , 0.5f , ( float ) ( 0.75 + ( ( float ) ( ( GetUniqueID ( ) * 23 ) % 32 ) ) / 64 ) ) ;
2014-08-19 06:38:15 -04:00
2014-08-21 06:08:38 -04:00
if ( ( m_World - > GetBlock ( Hit ) = = E_BLOCK_TNT ) & & IsOnFire ( ) )
2014-08-19 06:38:15 -04:00
{
m_World - > SetBlock ( X , Y , Z , E_BLOCK_AIR , 0 ) ;
m_World - > SpawnPrimedTNT ( X , Y , Z ) ;
}
2014-04-26 19:45:39 -04:00
}
void cArrowEntity : : OnHitEntity ( cEntity & a_EntityHit , const Vector3d & a_HitPos )
2014-07-17 16:50:58 -04:00
{
2014-04-26 19:45:39 -04:00
int Damage = ( int ) ( GetSpeed ( ) . Length ( ) / 20 * m_DamageCoeff + 0.5 ) ;
if ( m_IsCritical )
{
Damage + = m_World - > GetTickRandomNumber ( Damage / 2 + 2 ) ;
}
2014-08-19 06:38:15 -04:00
2014-08-19 11:57:32 -04:00
int PowerLevel = m_CreatorData . m_Enchantments . GetLevel ( cEnchantments : : enchPower ) ;
2014-08-19 06:38:15 -04:00
if ( PowerLevel > 0 )
{
2014-09-01 08:29:13 -04:00
int ExtraDamage = ( int ) ceil ( 0.25 * ( PowerLevel + 1 ) ) ;
Damage + = ExtraDamage ;
2014-08-19 06:38:15 -04:00
}
2014-08-19 10:08:17 -04:00
int KnockbackAmount = 1 ;
2014-08-19 11:57:32 -04:00
int PunchLevel = m_CreatorData . m_Enchantments . GetLevel ( cEnchantments : : enchPunch ) ;
2014-08-19 10:08:17 -04:00
if ( PunchLevel > 0 )
{
2014-08-21 06:08:38 -04:00
Vector3d LookVector = GetLookVector ( ) ;
2014-08-19 10:08:17 -04:00
Vector3f FinalSpeed = Vector3f ( 0 , 0 , 0 ) ;
switch ( PunchLevel )
{
2014-08-21 06:08:38 -04:00
case 1 : FinalSpeed = LookVector * Vector3d ( 5 , 0.3 , 5 ) ; break ;
case 2 : FinalSpeed = LookVector * Vector3d ( 8 , 0.3 , 8 ) ; break ;
2014-08-19 10:08:17 -04:00
default : break ;
}
a_EntityHit . SetSpeed ( FinalSpeed ) ;
}
a_EntityHit . TakeDamage ( dtRangedAttack , this , Damage , KnockbackAmount ) ;
2014-04-26 19:45:39 -04:00
2014-08-21 06:08:38 -04:00
if ( IsOnFire ( ) & & ! a_EntityHit . IsSubmerged ( ) & & ! a_EntityHit . IsSwimming ( ) )
2014-08-19 06:38:15 -04:00
{
a_EntityHit . StartBurning ( 100 ) ;
}
2014-04-26 19:45:39 -04:00
// Broadcast successful hit sound
2014-07-12 20:08:02 -04:00
GetWorld ( ) - > BroadcastSoundEffect ( " random.successful_hit " , GetPosX ( ) , GetPosY ( ) , GetPosZ ( ) , 0.5 , ( float ) ( 0.75 + ( ( float ) ( ( GetUniqueID ( ) * 23 ) % 32 ) ) / 64 ) ) ;
2014-04-26 19:45:39 -04:00
Destroy ( ) ;
}
2014-10-15 13:01:55 -04:00
void cArrowEntity : : CollectedBy ( cPlayer & a_Dest )
2014-04-26 19:45:39 -04:00
{
2014-10-15 13:01:55 -04:00
if ( m_IsInGround & & ! m_bIsCollected & & CanPickup ( a_Dest ) )
2014-04-26 19:45:39 -04:00
{
2014-06-26 12:42:28 -04:00
// Do not add the arrow to the inventory when the player is in creative:
2014-10-15 13:01:55 -04:00
if ( ! a_Dest . IsGameModeCreative ( ) )
2014-04-26 19:45:39 -04:00
{
2014-10-15 13:01:55 -04:00
int NumAdded = a_Dest . GetInventory ( ) . AddItem ( E_ITEM_ARROW ) ;
2014-06-16 16:57:13 -04:00
if ( NumAdded = = 0 )
{
// No space in the inventory
return ;
}
2014-04-26 19:45:39 -04:00
}
2014-06-26 12:42:28 -04:00
2014-10-15 13:01:55 -04:00
GetWorld ( ) - > BroadcastCollectEntity ( * this , a_Dest ) ;
2014-07-12 20:08:02 -04:00
GetWorld ( ) - > BroadcastSoundEffect ( " random.pop " , GetPosX ( ) , GetPosY ( ) , GetPosZ ( ) , 0.5 , ( float ) ( 0.75 + ( ( float ) ( ( GetUniqueID ( ) * 23 ) % 32 ) ) / 64 ) ) ;
2014-06-16 16:57:13 -04:00
m_bIsCollected = true ;
2014-04-26 19:45:39 -04:00
}
}
2015-01-11 16:12:26 -05:00
void cArrowEntity : : Tick ( std : : chrono : : milliseconds a_Dt , cChunk & a_Chunk )
2014-04-26 19:45:39 -04:00
{
super : : Tick ( a_Dt , a_Chunk ) ;
2015-01-16 08:13:23 -05:00
m_Timer + = a_Dt ;
2014-04-26 19:45:39 -04:00
if ( m_bIsCollected )
{
2015-01-16 08:13:23 -05:00
if ( m_Timer > std : : chrono : : milliseconds ( 500 ) )
2014-04-26 19:45:39 -04:00
{
Destroy ( ) ;
return ;
}
}
2015-01-16 08:13:23 -05:00
else if ( m_Timer > std : : chrono : : minutes ( 5 ) )
2014-04-26 19:45:39 -04:00
{
Destroy ( ) ;
return ;
}
if ( m_IsInGround )
{
// When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL
// Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing)
// We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync
// Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position
2014-07-17 16:15:34 -04:00
if ( ! m_HasTeleported ) // Sent a teleport already, don't do again
2014-04-26 19:45:39 -04:00
{
2015-01-16 08:13:23 -05:00
if ( m_HitGroundTimer > std : : chrono : : milliseconds ( 500 ) )
2014-04-26 19:45:39 -04:00
{
m_World - > BroadcastTeleportEntity ( * this ) ;
m_HasTeleported = true ;
}
else
{
2015-01-16 08:13:23 -05:00
m_HitGroundTimer + = a_Dt ;
2014-04-26 19:45:39 -04:00
}
}
int RelPosX = m_HitBlockPos . x - a_Chunk . GetPosX ( ) * cChunkDef : : Width ;
int RelPosZ = m_HitBlockPos . z - a_Chunk . GetPosZ ( ) * cChunkDef : : Width ;
cChunk * Chunk = a_Chunk . GetRelNeighborChunkAdjustCoords ( RelPosX , RelPosZ ) ;
2014-10-20 16:55:07 -04:00
if ( Chunk = = nullptr )
2014-04-26 19:45:39 -04:00
{
// Inside an unloaded chunk, abort
return ;
}
2014-07-17 16:15:34 -04:00
if ( Chunk - > GetBlock ( RelPosX , m_HitBlockPos . y , RelPosZ ) = = E_BLOCK_AIR ) // Block attached to was destroyed?
2014-04-26 19:45:39 -04:00
{
2014-07-17 16:15:34 -04:00
m_IsInGround = false ; // Yes, begin simulating physics again
2014-04-26 19:45:39 -04:00
}
}
2014-04-27 12:42:31 -04:00
}
2014-10-15 13:01:55 -04:00