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 ) ;
2015-03-30 19:42:32 -04:00
SetGravity ( - 20.0f ) ;
SetAirDrag ( 0.2f ) ;
2014-04-26 19:45:39 -04:00
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 ;
}
2015-03-30 19:42:32 -04:00
SetGravity ( - 20.0f ) ;
2015-03-31 11:03:35 -04:00
SetAirDrag ( 0.01f ) ;
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 ( ) ;
}
2018-02-04 18:07:12 -05:00
UNREACHABLE ( " Unsupported arrow pickup state " ) ;
2014-04-26 19:45:39 -04:00
}
2014-08-19 11:57:32 -04:00
2017-09-07 04:25:34 -04:00
void cArrowEntity : : OnHitSolidBlock ( Vector3d a_HitPos , eBlockFace a_HitFace )
2014-07-17 16:50:58 -04:00
{
2014-06-22 15:44:01 -04:00
Vector3d Hit = a_HitPos ;
2015-03-20 18:32:32 -04:00
Hit + = GetSpeed ( ) . NormalizeCopy ( ) / 100000 ; // Make arrow sink into block a bit so it lodges (TODO: investigate how to stop them going so far so that they become 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 ) ;
2015-05-28 10:45:47 -04:00
2014-04-26 19:45:39 -04:00
// Broadcast arrow hit sound
2017-09-19 10:12:54 -04:00
m_World - > BroadcastSoundEffect ( " entity.arrow.hit " , BlockHit , 0.5f , static_cast < float > ( 0.75 + ( static_cast < 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 ) ;
2017-09-07 04:25:34 -04:00
m_World - > SpawnPrimedTNT ( Vector3d ( BlockHit ) ) ;
2014-08-19 06:38:15 -04:00
}
2014-04-26 19:45:39 -04:00
}
2017-09-07 04:25:34 -04:00
void cArrowEntity : : OnHitEntity ( cEntity & a_EntityHit , Vector3d a_HitPos )
2014-07-17 16:50:58 -04:00
{
2016-01-11 14:34:41 -05:00
super : : OnHitEntity ( a_EntityHit , a_HitPos ) ;
2015-07-29 11:04:03 -04:00
int Damage = static_cast < int > ( GetSpeed ( ) . Length ( ) / 20 * m_DamageCoeff + 0.5 ) ;
2014-04-26 19:45:39 -04:00
if ( m_IsCritical )
{
Damage + = m_World - > GetTickRandomNumber ( Damage / 2 + 2 ) ;
}
2014-08-19 06:38:15 -04:00
2015-05-19 14:32:10 -04:00
unsigned int PowerLevel = m_CreatorData . m_Enchantments . GetLevel ( cEnchantments : : enchPower ) ;
2014-08-19 06:38:15 -04:00
if ( PowerLevel > 0 )
{
2015-07-29 11:04:03 -04:00
int ExtraDamage = static_cast < int > ( ceil ( 0.25 * ( PowerLevel + 1 ) ) ) ;
2014-09-01 08:29:13 -04:00
Damage + = ExtraDamage ;
2014-08-19 06:38:15 -04:00
}
2014-08-19 10:08:17 -04:00
2015-05-19 14:32:10 -04:00
unsigned int PunchLevel = m_CreatorData . m_Enchantments . GetLevel ( cEnchantments : : enchPunch ) ;
2017-08-13 07:40:23 -04:00
double KnockbackAmount = 11 + 10 * PunchLevel ;
a_EntityHit . TakeDamage ( dtRangedAttack , GetCreatorUniqueID ( ) , Damage , KnockbackAmount ) ;
2015-05-28 10:45:47 -04:00
2018-01-14 13:44:45 -05:00
if ( IsOnFire ( ) & & ! a_EntityHit . IsInWater ( ) )
2014-08-19 06:38:15 -04:00
{
a_EntityHit . StartBurning ( 100 ) ;
}
2014-04-26 19:45:39 -04:00
// Broadcast successful hit sound
2017-09-19 10:12:54 -04:00
GetWorld ( ) - > BroadcastSoundEffect ( " entity.arrow.hit_player " , GetPosition ( ) , 0.5 , static_cast < float > ( 0.75 + ( static_cast < float > ( ( GetUniqueID ( ) * 23 ) % 32 ) ) / 64 ) ) ;
2016-01-11 14:34:41 -05:00
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
2016-12-15 14:21:43 -05:00
GetWorld ( ) - > BroadcastCollectEntity ( * this , a_Dest , 1 ) ;
2017-09-19 10:12:54 -04:00
GetWorld ( ) - > BroadcastSoundEffect ( " entity.item.pickup " , GetPosition ( ) , 0.5 , static_cast < float > ( 0.75 + ( static_cast < 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 ) ;
2016-09-03 07:31:27 -04:00
if ( ! IsTicking ( ) )
{
// The base class tick destroyed us
return ;
}
2015-01-16 08:13:23 -05:00
m_Timer + = a_Dt ;
2016-01-11 14:34:41 -05:00
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 ;
}
2016-01-11 14:34:41 -05:00
2014-04-26 19:45:39 -04:00
if ( m_IsInGround )
2015-03-21 09:07:16 -04:00
{
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
}
}
2016-01-11 14:34:41 -05:00
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 ) ;
2016-01-11 14:34:41 -05:00
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 ;
}
2016-01-11 14:34:41 -05:00
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
}