2014-04-10 10:52:00 -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
2013-10-20 07:25:56 -04:00
# include "IncludeAllMonsters.h"
2012-09-23 18:09:57 -04:00
# include "../Root.h"
# include "../Server.h"
# include "../ClientHandle.h"
# include "../World.h"
2013-08-19 05:39:13 -04:00
# include "../Entities/Player.h"
2013-11-25 14:04:39 -05:00
# include "../Entities/ExpOrb.h"
2012-09-23 18:09:57 -04:00
# include "../MonsterConfig.h"
2012-09-23 16:53:08 -04:00
2013-09-05 16:40:08 -04:00
# include "../Chunk.h"
2013-10-20 07:25:56 -04:00
# include "../FastRandom.h"
2013-04-22 03:18:03 -04:00
2015-04-29 12:24:14 -04:00
# include "Path.h"
2012-06-14 09:06:06 -04:00
2013-10-20 07:25:56 -04:00
/** Map for eType <-> string
Needs to be alpha - sorted by the strings , because binary search is used in StringToMobType ( )
The strings need to be lowercase ( for more efficient comparisons in StringToMobType ( ) )
2014-12-01 08:58:13 -05:00
m_VanillaName is the name that vanilla use for this mob .
2013-10-20 07:25:56 -04:00
*/
static const struct
{
2014-09-17 13:40:10 -04:00
eMonsterType m_Type ;
2013-10-20 07:25:56 -04:00
const char * m_lcName ;
2014-11-29 09:20:44 -05:00
const char * m_VanillaName ;
2013-10-20 07:25:56 -04:00
} g_MobTypeNames [ ] =
{
2014-11-29 09:20:44 -05:00
{ mtBat , " bat " , " Bat " } ,
{ mtBlaze , " blaze " , " Blaze " } ,
{ mtCaveSpider , " cavespider " , " CaveSpider " } ,
{ mtChicken , " chicken " , " Chicken " } ,
{ mtCow , " cow " , " Cow " } ,
{ mtCreeper , " creeper " , " Creeper " } ,
{ mtEnderman , " enderman " , " Enderman " } ,
{ mtEnderDragon , " enderdragon " , " EnderDragon " } ,
{ mtGhast , " ghast " , " Ghast " } ,
2015-04-17 12:33:34 -04:00
{ mtGiant , " giant " , " Giant " } ,
2014-12-18 13:30:32 -05:00
{ mtGuardian , " guardian " , " Guardian " } ,
2014-11-29 09:20:44 -05:00
{ mtHorse , " horse " , " EntityHorse " } ,
{ mtIronGolem , " irongolem " , " VillagerGolem " } ,
{ mtMagmaCube , " magmacube " , " LavaSlime " } ,
{ mtMooshroom , " mooshroom " , " MushroomCow " } ,
{ mtOcelot , " ocelot " , " Ozelot " } ,
{ mtPig , " pig " , " Pig " } ,
2014-12-20 04:31:34 -05:00
{ mtRabbit , " rabbit " , " Rabbit " } ,
2014-11-29 09:20:44 -05:00
{ mtSheep , " sheep " , " Sheep " } ,
{ mtSilverfish , " silverfish " , " Silverfish " } ,
{ mtSkeleton , " skeleton " , " Skeleton " } ,
{ mtSlime , " slime " , " Slime " } ,
{ mtSnowGolem , " snowgolem " , " SnowMan " } ,
{ mtSpider , " spider " , " Spider " } ,
{ mtSquid , " squid " , " Squid " } ,
{ mtVillager , " villager " , " Villager " } ,
{ mtWitch , " witch " , " Witch " } ,
{ mtWither , " wither " , " WitherBoss " } ,
{ mtWolf , " wolf " , " Wolf " } ,
{ mtZombie , " zombie " , " Zombie " } ,
{ mtZombiePigman , " zombiepigman " , " PigZombie " } ,
2013-10-20 07:25:56 -04:00
} ;
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-10-20 07:25:56 -04:00
// cMonster:
2012-06-14 09:06:06 -04:00
2014-09-17 13:40:10 -04:00
cMonster : : cMonster ( const AString & a_ConfigName , eMonsterType a_MobType , const AString & a_SoundHurt , const AString & a_SoundDeath , double a_Width , double a_Height )
2013-10-13 07:47:55 -04:00
: super ( etMonster , a_Width , a_Height )
2013-12-20 10:39:20 -05:00
, m_EMState ( IDLE )
, m_EMPersonality ( AGGRESSIVE )
2014-10-20 16:55:07 -04:00
, m_Target ( nullptr )
2015-04-29 12:24:14 -04:00
, m_Path ( nullptr )
, m_IsFollowingPath ( false )
, m_GiveUpCounter ( 0 )
2015-05-05 03:04:41 -04:00
, m_TicksSinceLastPathReset ( 1000 )
2014-02-05 12:43:49 -05:00
, m_LastGroundHeight ( POSY_TOINT )
2015-05-02 14:22:28 -04:00
, m_JumpCoolDown ( 0 )
2014-02-05 12:43:49 -05:00
, m_IdleInterval ( 0 )
2014-01-24 16:55:04 -05:00
, m_DestroyTimer ( 0 )
2013-10-20 04:23:30 -04:00
, m_MobType ( a_MobType )
2014-09-01 14:12:56 -04:00
, m_CustomName ( " " )
, m_CustomNameAlwaysVisible ( false )
2012-12-22 04:39:13 -05:00
, m_SoundHurt ( a_SoundHurt )
, m_SoundDeath ( a_SoundDeath )
2014-02-05 12:43:49 -05:00
, m_AttackRate ( 3 )
2014-01-24 16:55:04 -05:00
, m_AttackDamage ( 1 )
, m_AttackRange ( 2 )
2012-06-14 09:06:06 -04:00
, m_AttackInterval ( 0 )
2014-02-24 09:38:38 -05:00
, m_SightDistance ( 25 )
2014-03-16 17:00:28 -04:00
, m_DropChanceWeapon ( 0.085f )
, m_DropChanceHelmet ( 0.085f )
, m_DropChanceChestplate ( 0.085f )
, m_DropChanceLeggings ( 0.085f )
, m_DropChanceBoots ( 0.085f )
2014-02-23 13:44:58 -05:00
, m_CanPickUpLoot ( true )
2015-04-29 12:24:14 -04:00
, m_TicksSinceLastDamaged ( 100 )
2013-09-07 10:42:34 -04:00
, m_BurnsInDaylight ( false )
2015-04-29 12:24:14 -04:00
, m_RelativeWalkSpeed ( 1 )
2012-06-14 09:06:06 -04:00
{
2012-12-22 04:39:13 -05:00
if ( ! a_ConfigName . empty ( ) )
{
GetMonsterConfig ( a_ConfigName ) ;
}
2012-06-14 09:06:06 -04:00
}
2012-08-19 15:42:32 -04:00
2012-08-24 03:58:26 -04:00
void cMonster : : SpawnOn ( cClientHandle & a_Client )
2012-06-14 09:06:06 -04:00
{
2012-08-24 03:58:26 -04:00
a_Client . SendSpawnMob ( * this ) ;
2012-06-14 09:06:06 -04:00
}
2015-05-03 13:56:37 -04:00
bool cMonster : : TickPathFinding ( cChunk & a_Chunk )
2012-06-14 09:06:06 -04:00
{
2015-05-03 13:56:37 -04:00
if ( ! m_IsFollowingPath )
2014-03-31 17:37:05 -04:00
{
2015-05-03 13:56:37 -04:00
return false ;
}
2015-05-05 03:04:41 -04:00
if ( m_TicksSinceLastPathReset < 1000 )
{
// No need to count beyond 1000. 1000 is arbitary here.
+ + m_TicksSinceLastPathReset ;
}
2015-04-29 12:24:14 -04:00
2015-05-03 13:56:37 -04:00
if ( ReachedFinalDestination ( ) )
{
StopMovingToPosition ( ) ;
return false ;
2015-04-29 12:24:14 -04:00
}
2015-05-03 13:56:37 -04:00
2015-05-05 03:04:41 -04:00
if ( ( m_FinalDestination - m_PathFinderDestination ) . Length ( ) > 0.25 ) // if the distance between where we're going and where we should go is too big.
{
/* If we reached the last path waypoint,
Or if we haven ' t re - calculated for too long .
2015-05-05 23:46:10 -04:00
Interval is proportional to distance squared , and its minimum is 10.
( Recalculate lots when close , calculate rarely when far ) */
2015-05-05 03:04:41 -04:00
if (
( ( GetPosition ( ) - m_PathFinderDestination ) . Length ( ) < 0.25 ) | |
2015-05-05 23:46:10 -04:00
( ( m_TicksSinceLastPathReset > 10 ) & & ( m_TicksSinceLastPathReset > ( 0.15 * ( m_FinalDestination - GetPosition ( ) ) . SqrLength ( ) ) ) )
2015-05-05 03:04:41 -04:00
)
{
ResetPathFinding ( ) ;
}
}
2015-05-03 13:56:37 -04:00
if ( m_Path = = nullptr )
2014-01-24 14:57:32 -05:00
{
2015-05-06 10:23:07 -04:00
if ( ! EnsureProperDestination ( a_Chunk ) )
{
StopMovingToPosition ( ) ; // Invalid chunks, probably world is loading or something, cancel movement.
return false ;
}
2015-05-05 03:04:41 -04:00
m_PathFinderDestination = m_FinalDestination ;
m_Path = new cPath ( a_Chunk , GetPosition ( ) . Floor ( ) , m_PathFinderDestination . Floor ( ) , 20 ) ;
2015-05-03 13:56:37 -04:00
}
2014-01-24 14:57:32 -05:00
2015-05-03 13:56:37 -04:00
switch ( m_Path - > Step ( a_Chunk ) )
{
2015-04-29 12:24:14 -04:00
case ePathFinderStatus : : PATH_NOT_FOUND :
2014-01-24 14:57:32 -05:00
{
2015-05-05 03:04:41 -04:00
StopMovingToPosition ( ) ; // Give up pathfinding to that destination.
2015-04-29 12:24:14 -04:00
break ;
2014-01-24 14:57:32 -05:00
}
2015-04-29 12:24:14 -04:00
case ePathFinderStatus : : CALCULATING :
2014-01-24 14:57:32 -05:00
{
2015-05-03 13:56:37 -04:00
// Pathfinder needs more time
2015-04-29 12:24:14 -04:00
break ;
2014-01-24 14:57:32 -05:00
}
2015-04-29 12:24:14 -04:00
case ePathFinderStatus : : PATH_FOUND :
2014-01-24 14:57:32 -05:00
{
2015-05-03 13:56:37 -04:00
if ( - - m_GiveUpCounter = = 0 )
2014-01-24 14:57:32 -05:00
{
2015-05-05 03:04:41 -04:00
ResetPathFinding ( ) ; // Try to calculate a path again.
2015-05-03 13:56:37 -04:00
return false ;
2014-01-24 14:57:32 -05:00
}
2015-05-05 03:04:41 -04:00
else if ( ! m_Path - > IsLastPoint ( ) & & ( m_Path - > IsFirstPoint ( ) | | ReachedNextWaypoint ( ) ) ) // Have we arrived at the next cell, as denoted by m_NextWayPointPosition?
2015-04-29 12:24:14 -04:00
{
2015-05-05 03:04:41 -04:00
m_NextWayPointPosition = Vector3d ( 0.5 , 0 , 0.5 ) + m_Path - > GetNextPoint ( ) ;
m_GiveUpCounter = 40 ; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition.
2015-04-29 12:24:14 -04:00
}
2015-05-03 13:56:37 -04:00
return true ;
2015-04-29 12:24:14 -04:00
}
2014-01-24 14:57:32 -05:00
}
2015-05-03 13:56:37 -04:00
return false ;
2014-02-03 14:52:11 -05:00
}
2014-01-24 14:57:32 -05:00
2015-04-29 12:24:14 -04:00
2015-05-03 13:56:37 -04:00
void cMonster : : MoveToWayPoint ( cChunk & a_Chunk )
2015-04-29 12:24:14 -04:00
{
2015-05-03 13:56:37 -04:00
if ( m_JumpCoolDown = = 0 )
{
2015-05-06 10:23:07 -04:00
if ( DoesPosYRequireJump ( FloorC ( m_NextWayPointPosition . y ) ) )
2015-05-03 13:56:37 -04:00
{
2015-05-06 10:23:07 -04:00
if (
( IsOnGround ( ) & & ( GetSpeedX ( ) = = 0 ) & & ( GetSpeedY ( ) = = 0 ) ) | |
( IsSwimming ( ) & & ( m_GiveUpCounter < 15 ) )
)
2015-05-03 13:56:37 -04:00
{
m_bOnGround = false ;
m_JumpCoolDown = 20 ;
// TODO: Change to AddSpeedY once collision detection is fixed - currently, mobs will go into blocks attempting to jump without a teleport
AddPosY ( 1.6 ) ; // Jump!!
2015-05-05 03:04:41 -04:00
SetSpeedX ( 3.2 * ( m_NextWayPointPosition . x - GetPosition ( ) . x ) ) ; // Move forward in a preset speed.
SetSpeedZ ( 3.2 * ( m_NextWayPointPosition . z - GetPosition ( ) . z ) ) ; // The numbers were picked based on trial and error and 1.6 and 3.2 are perfect.
2015-05-03 13:56:37 -04:00
}
}
}
else
{
- - m_JumpCoolDown ;
}
2015-04-29 12:24:14 -04:00
2015-05-05 03:04:41 -04:00
Vector3d Distance = m_NextWayPointPosition - GetPosition ( ) ;
2015-05-03 13:56:37 -04:00
if ( ( Distance . x ! = 0 ) | | ( Distance . z ! = 0 ) )
{
Distance . y = 0 ;
Distance . Normalize ( ) ;
2015-04-29 12:24:14 -04:00
2015-05-03 13:56:37 -04:00
if ( m_bOnGround )
{
Distance * = 2.5f ;
}
else if ( IsSwimming ( ) )
{
Distance * = 1.3f ;
}
else
{
// Don't let the mob move too much if he's falling.
Distance * = 0.25f ;
}
// Apply walk speed:
Distance * = m_RelativeWalkSpeed ;
/* Reduced default speed.
2015-05-05 03:04:41 -04:00
Close to Vanilla , easier for mobs to follow m_NextWayPointPositions , hence
2015-05-03 13:56:37 -04:00
better pathfinding . */
Distance * = 0.5 ;
AddSpeedX ( Distance . x ) ;
AddSpeedZ ( Distance . z ) ;
}
2012-06-14 09:06:06 -04:00
}
2012-08-19 15:42:32 -04:00
2015-05-06 10:23:07 -04:00
bool cMonster : : EnsureProperDestination ( cChunk & a_Chunk )
{
2015-05-08 18:32:02 -04:00
cChunk * Chunk = a_Chunk . GetNeighborChunk ( FloorC ( m_FinalDestination . x ) , FloorC ( m_FinalDestination . z ) ) ;
2015-05-06 10:23:07 -04:00
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
2015-05-08 18:20:22 -04:00
2015-05-06 10:23:07 -04:00
if ( ( Chunk = = nullptr ) | | ! Chunk - > IsValid ( ) )
{
return false ;
}
2015-05-08 18:20:22 -04:00
2015-05-10 07:16:20 -04:00
int RelX = FloorC ( m_FinalDestination . x ) - Chunk - > GetPosX ( ) * cChunkDef : : Width ;
int RelZ = FloorC ( m_FinalDestination . z ) - Chunk - > GetPosZ ( ) * cChunkDef : : Width ;
2015-05-06 10:23:07 -04:00
// If destination in the air, go down to the lowest air block.
while ( m_FinalDestination . y > 0 )
{
2015-05-08 18:32:02 -04:00
Chunk - > GetBlockTypeMeta ( RelX , FloorC ( m_FinalDestination . y ) - 1 , RelZ , BlockType , BlockMeta ) ;
2015-05-06 10:23:07 -04:00
if ( cBlockInfo : : IsSolid ( BlockType ) )
{
break ;
}
m_FinalDestination . y - = 1 ;
}
// If destination in water, go up to the highest water block.
// If destination in solid, go up to first air block.
bool InWater = false ;
while ( m_FinalDestination . y < cChunkDef : : Height )
{
2015-05-08 18:32:02 -04:00
Chunk - > GetBlockTypeMeta ( RelX , FloorC ( m_FinalDestination . y ) , RelZ , BlockType , BlockMeta ) ;
2015-05-06 10:23:07 -04:00
if ( BlockType = = E_BLOCK_STATIONARY_WATER )
{
InWater = true ;
}
else if ( cBlockInfo : : IsSolid ( BlockType ) )
{
InWater = false ;
}
else
{
break ;
}
m_FinalDestination . y + = 1 ;
}
if ( InWater )
{
m_FinalDestination . y - = 1 ;
}
return true ;
}
2015-05-03 13:56:37 -04:00
void cMonster : : MoveToPosition ( const Vector3d & a_Position )
2015-04-29 12:24:14 -04:00
{
2015-05-03 13:56:37 -04:00
m_FinalDestination = a_Position ;
m_IsFollowingPath = true ;
2015-04-29 12:24:14 -04:00
}
2015-05-03 13:56:37 -04:00
void cMonster : : StopMovingToPosition ( )
2012-06-14 09:06:06 -04:00
{
2015-05-03 13:56:37 -04:00
m_IsFollowingPath = false ;
2014-01-24 14:57:32 -05:00
}
2012-06-14 09:06:06 -04:00
2014-01-24 14:57:32 -05:00
2015-04-29 12:24:14 -04:00
2015-05-03 13:56:37 -04:00
void cMonster : : ResetPathFinding ( void )
2014-01-24 14:57:32 -05:00
{
2015-05-05 03:04:41 -04:00
m_TicksSinceLastPathReset = 0 ;
2015-05-03 13:56:37 -04:00
if ( m_Path ! = nullptr )
2014-01-24 14:57:32 -05:00
{
2015-05-03 13:56:37 -04:00
delete m_Path ;
m_Path = nullptr ;
2014-01-24 14:57:32 -05:00
}
2012-06-14 09:06:06 -04:00
}
2015-01-11 16:12:26 -05:00
void cMonster : : Tick ( std : : chrono : : milliseconds a_Dt , cChunk & a_Chunk )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
super : : Tick ( a_Dt , a_Chunk ) ;
2015-05-01 13:34:24 -04:00
GET_AND_VERIFY_CURRENT_CHUNK ( Chunk , POSX_TOINT , POSZ_TOINT ) ;
2012-06-14 09:06:06 -04:00
2012-12-22 05:15:53 -05:00
if ( m_Health < = 0 )
2012-06-14 09:06:06 -04:00
{
2015-05-06 10:23:07 -04:00
// The mob is dead, but we're still animating the "puff" they leave when they die.
2015-01-16 09:38:21 -05:00
m_DestroyTimer + = a_Dt ;
if ( m_DestroyTimer > std : : chrono : : seconds ( 1 ) )
2012-06-14 09:06:06 -04:00
{
2013-06-25 02:36:59 -04:00
Destroy ( true ) ;
2012-06-14 09:06:06 -04:00
}
return ;
}
2015-04-29 12:24:14 -04:00
if ( m_TicksSinceLastDamaged < 100 )
{
+ + m_TicksSinceLastDamaged ;
}
2014-10-20 16:55:07 -04:00
if ( ( m_Target ! = nullptr ) & & m_Target - > IsDestroyed ( ) )
2014-12-05 10:59:11 -05:00
{
2014-10-20 16:55:07 -04:00
m_Target = nullptr ;
2014-12-05 10:59:11 -05:00
}
2014-01-24 18:56:19 -05:00
2015-05-06 10:23:07 -04:00
// Process the undead burning in daylight.
2015-05-03 13:56:37 -04:00
HandleDaylightBurning ( * Chunk , WouldBurnAt ( GetPosition ( ) , * Chunk ) ) ;
if ( TickPathFinding ( * Chunk ) )
2012-06-14 09:06:06 -04:00
{
2015-05-06 09:52:37 -04:00
/* If I burn in daylight, and I won't burn where I'm standing, and I'll burn in my next position, and at least one of those is true:
1. I am idle
2. I was not hurt by a player recently .
Then STOP . */
if (
2015-05-06 10:02:50 -04:00
m_BurnsInDaylight & & ( ( m_TicksSinceLastDamaged > = 100 ) | | ( m_EMState = = IDLE ) ) & &
WouldBurnAt ( m_NextWayPointPosition , * Chunk ) & &
! WouldBurnAt ( GetPosition ( ) , * Chunk )
2015-05-06 09:52:37 -04:00
)
2014-01-25 09:42:26 -05:00
{
2015-05-03 13:56:37 -04:00
// If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently:
StopMovingToPosition ( ) ;
2015-05-05 03:04:41 -04:00
m_GiveUpCounter = 40 ; // This doesn't count as giving up, keep the giveup timer as is.
2012-06-14 09:06:06 -04:00
}
2015-05-03 13:56:37 -04:00
else
2012-06-14 09:06:06 -04:00
{
2015-05-03 13:56:37 -04:00
MoveToWayPoint ( * Chunk ) ;
2012-06-14 09:06:06 -04:00
}
}
2013-03-23 00:33:47 -04:00
2014-01-24 14:57:32 -05:00
SetPitchAndYawFromDestination ( ) ;
2014-01-25 14:02:13 -05:00
HandleFalling ( ) ;
2013-03-22 02:33:10 -04:00
2012-12-22 05:15:53 -05:00
switch ( m_EMState )
2012-08-19 15:42:32 -04:00
{
2012-12-22 05:15:53 -05:00
case IDLE :
{
2015-04-29 12:24:14 -04:00
// If enemy passive we ignore checks for player visibility.
2015-01-16 09:38:21 -05:00
InStateIdle ( a_Dt ) ;
2012-12-22 05:15:53 -05:00
break ;
2014-07-17 16:50:58 -04:00
}
2012-12-22 05:15:53 -05:00
case CHASING :
{
2015-04-29 12:24:14 -04:00
// If we do not see a player anymore skip chasing action.
2015-01-16 09:38:21 -05:00
InStateChasing ( a_Dt ) ;
2012-12-22 05:15:53 -05:00
break ;
2014-07-17 16:50:58 -04:00
}
2012-12-22 05:15:53 -05:00
case ESCAPING :
{
2015-01-16 09:38:21 -05:00
InStateEscaping ( a_Dt ) ;
2012-12-22 05:15:53 -05:00
break ;
}
2014-05-11 19:25:21 -04:00
case ATTACKING : break ;
2012-12-22 05:15:53 -05:00
} // switch (m_EMState)
2014-01-24 14:57:32 -05:00
BroadcastMovementUpdate ( ) ;
2015-05-03 13:56:37 -04:00
}
2015-04-29 12:24:14 -04:00
2014-01-24 14:57:32 -05:00
void cMonster : : SetPitchAndYawFromDestination ( )
{
Vector3d FinalDestination = m_FinalDestination ;
2014-10-20 16:55:07 -04:00
if ( m_Target ! = nullptr )
2014-01-24 14:57:32 -05:00
{
if ( m_Target - > IsPlayer ( ) )
{
2015-05-03 13:56:37 -04:00
FinalDestination . y = static_cast < cPlayer * > ( m_Target ) - > GetStance ( ) - 1 ;
2014-01-24 14:57:32 -05:00
}
else
{
2015-05-03 13:56:37 -04:00
FinalDestination . y = m_Target - > GetPosY ( ) + GetHeight ( ) ;
2014-01-24 14:57:32 -05:00
}
}
Vector3d Distance = FinalDestination - GetPosition ( ) ;
{
2015-05-03 13:56:37 -04:00
double Rotation , Pitch ;
Distance . Normalize ( ) ;
VectorToEuler ( Distance . x , Distance . y , Distance . z , Rotation , Pitch ) ;
SetHeadYaw ( Rotation ) ;
SetPitch ( - Pitch ) ;
}
2014-01-24 14:57:32 -05:00
2015-05-03 13:56:37 -04:00
{
2015-05-05 03:04:41 -04:00
Vector3d BodyDistance = m_NextWayPointPosition - GetPosition ( ) ;
2015-05-03 13:56:37 -04:00
double Rotation , Pitch ;
BodyDistance . Normalize ( ) ;
VectorToEuler ( BodyDistance . x , BodyDistance . y , BodyDistance . z , Rotation , Pitch ) ;
SetYaw ( Rotation ) ;
2014-01-24 14:57:32 -05:00
}
}
2015-04-29 12:24:14 -04:00
2014-01-25 14:02:13 -05:00
void cMonster : : HandleFalling ( )
{
if ( m_bOnGround )
{
int Damage = ( m_LastGroundHeight - POSY_TOINT ) - 3 ;
if ( Damage > 0 )
{
2014-10-20 16:55:07 -04:00
TakeDamage ( dtFalling , nullptr , Damage , Damage , 0 ) ;
2014-01-25 14:02:13 -05:00
// Fall particles
GetWorld ( ) - > BroadcastSoundParticleEffect ( 2006 , POSX_TOINT , POSY_TOINT - 1 , POSZ_TOINT , Damage /* Used as particle effect speed modifier */ ) ;
}
2014-04-17 13:50:25 -04:00
m_LastGroundHeight = POSY_TOINT ;
2014-01-25 14:02:13 -05:00
}
}
2014-06-08 21:44:20 -04:00
2014-01-24 14:57:32 -05:00
int cMonster : : FindFirstNonAirBlockPosition ( double a_PosX , double a_PosZ )
{
2014-04-17 13:50:25 -04:00
int PosY = POSY_TOINT ;
2014-07-19 04:40:29 -04:00
PosY = Clamp ( PosY , 0 , cChunkDef : : Height ) ;
2014-01-25 09:42:26 -05:00
2014-03-01 14:34:19 -05:00
if ( ! cBlockInfo : : IsSolid ( m_World - > GetBlock ( ( int ) floor ( a_PosX ) , PosY , ( int ) floor ( a_PosZ ) ) ) )
2014-01-24 14:57:32 -05:00
{
2014-03-01 14:34:19 -05:00
while ( ! cBlockInfo : : IsSolid ( m_World - > GetBlock ( ( int ) floor ( a_PosX ) , PosY , ( int ) floor ( a_PosZ ) ) ) & & ( PosY > 0 ) )
2014-01-24 14:57:32 -05:00
{
PosY - - ;
}
return PosY + 1 ;
}
else
{
2014-11-25 18:03:33 -05:00
while ( ( PosY < cChunkDef : : Height ) & & cBlockInfo : : IsSolid ( m_World - > GetBlock ( ( int ) floor ( a_PosX ) , PosY , ( int ) floor ( a_PosZ ) ) ) )
2014-01-24 14:57:32 -05:00
{
PosY + + ;
}
return PosY ;
}
2012-06-14 09:06:06 -04:00
}
2012-08-19 15:42:32 -04:00
2014-04-25 18:32:30 -04:00
bool cMonster : : DoTakeDamage ( TakeDamageInfo & a_TDI )
2012-06-14 09:06:06 -04:00
{
2014-04-25 18:32:30 -04:00
if ( ! super : : DoTakeDamage ( a_TDI ) )
{
return false ;
}
2014-01-24 14:57:32 -05:00
2014-05-13 07:53:15 -04:00
if ( ! m_SoundHurt . empty ( ) & & ( m_Health > 0 ) )
2014-05-12 14:38:52 -04:00
{
2014-07-12 20:08:02 -04:00
m_World - > BroadcastSoundEffect ( m_SoundHurt , GetPosX ( ) , GetPosY ( ) , GetPosZ ( ) , 1.0f , 0.8f ) ;
2014-05-12 14:38:52 -04:00
}
2014-01-24 14:57:32 -05:00
2014-10-20 16:55:07 -04:00
if ( a_TDI . Attacker ! = nullptr )
2012-12-21 06:04:08 -05:00
{
m_Target = a_TDI . Attacker ;
2015-04-29 12:24:14 -04:00
m_TicksSinceLastDamaged = 0 ;
2012-12-21 06:04:08 -05:00
}
2014-04-25 18:32:30 -04:00
return true ;
2012-06-14 09:06:06 -04:00
}
2012-08-19 15:42:32 -04:00
2014-07-04 05:55:09 -04:00
void cMonster : : KilledBy ( TakeDamageInfo & a_TDI )
2012-06-14 09:06:06 -04:00
{
2014-07-04 05:55:09 -04:00
super : : KilledBy ( a_TDI ) ;
2013-07-01 06:39:56 -04:00
if ( m_SoundHurt ! = " " )
{
2014-07-12 20:08:02 -04:00
m_World - > BroadcastSoundEffect ( m_SoundDeath , GetPosX ( ) , GetPosY ( ) , GetPosZ ( ) , 1.0f , 0.8f ) ;
2013-07-01 06:39:56 -04:00
}
2013-11-25 15:43:43 -05:00
int Reward ;
2013-11-25 15:03:26 -05:00
switch ( m_MobType )
{
// Animals
2014-09-17 13:40:10 -04:00
case mtChicken :
case mtCow :
case mtHorse :
case mtPig :
2014-12-20 04:31:34 -05:00
case mtRabbit :
2014-09-17 13:40:10 -04:00
case mtSheep :
case mtSquid :
case mtMooshroom :
case mtOcelot :
case mtWolf :
2013-11-25 15:03:26 -05:00
{
2013-11-25 15:43:43 -05:00
Reward = m_World - > GetTickRandomNumber ( 2 ) + 1 ;
2013-12-14 06:50:08 -05:00
break ;
2013-11-25 15:03:26 -05:00
}
// Monsters
2014-09-17 13:40:10 -04:00
case mtCaveSpider :
case mtCreeper :
case mtEnderman :
case mtGhast :
2014-12-18 13:30:32 -05:00
case mtGuardian :
2014-09-17 13:40:10 -04:00
case mtSilverfish :
case mtSkeleton :
case mtSpider :
case mtWitch :
case mtZombie :
case mtZombiePigman :
case mtSlime :
case mtMagmaCube :
2013-11-25 15:03:26 -05:00
{
2013-11-25 15:43:43 -05:00
Reward = 6 + ( m_World - > GetTickRandomNumber ( 2 ) ) ;
2013-12-14 06:50:08 -05:00
break ;
2013-11-25 15:43:43 -05:00
}
2014-09-17 13:40:10 -04:00
case mtBlaze :
2013-11-25 15:43:43 -05:00
{
Reward = 10 ;
2013-12-14 06:50:08 -05:00
break ;
2013-11-25 15:03:26 -05:00
}
// Bosses
2014-09-17 13:40:10 -04:00
case mtEnderDragon :
2013-11-25 15:03:26 -05:00
{
2013-11-25 15:43:43 -05:00
Reward = 12000 ;
2013-12-14 06:50:08 -05:00
break ;
2013-11-25 15:03:26 -05:00
}
2014-09-17 13:40:10 -04:00
case mtWither :
2013-11-25 15:03:26 -05:00
{
2013-11-25 15:43:43 -05:00
Reward = 50 ;
2013-12-14 06:50:08 -05:00
break ;
2013-11-25 15:03:26 -05:00
}
default :
{
2013-11-25 15:43:43 -05:00
Reward = 0 ;
2013-12-14 06:50:08 -05:00
break ;
2013-11-25 15:03:26 -05:00
}
}
2014-10-20 16:55:07 -04:00
if ( ( a_TDI . Attacker ! = nullptr ) & & ( ! IsBaby ( ) ) )
2014-04-10 10:50:43 -04:00
{
m_World - > SpawnExperienceOrb ( GetPosX ( ) , GetPosY ( ) , GetPosZ ( ) , Reward ) ;
}
2015-01-16 09:38:21 -05:00
m_DestroyTimer = std : : chrono : : milliseconds ( 0 ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-19 15:42:32 -04:00
2014-09-01 15:05:45 -04:00
void cMonster : : OnRightClicked ( cPlayer & a_Player )
{
super : : OnRightClicked ( a_Player ) ;
const cItem & EquippedItem = a_Player . GetEquippedItem ( ) ;
if ( ( EquippedItem . m_ItemType = = E_ITEM_NAME_TAG ) & & ! EquippedItem . m_CustomName . empty ( ) )
{
SetCustomName ( EquippedItem . m_CustomName ) ;
if ( ! a_Player . IsGameModeCreative ( ) )
{
a_Player . GetInventory ( ) . RemoveOneEquippedItem ( ) ;
}
}
}
2014-07-17 16:15:34 -04:00
// Checks to see if EventSeePlayer should be fired
// monster sez: Do I see the player
2013-04-13 17:02:10 -04:00
void cMonster : : CheckEventSeePlayer ( void )
2012-06-14 09:06:06 -04:00
{
2012-12-22 05:15:53 -05:00
// TODO: Rewrite this to use cWorld's DoWithPlayers()
2014-01-25 09:42:26 -05:00
cPlayer * Closest = m_World - > FindClosestPlayer ( GetPosition ( ) , ( float ) m_SightDistance , false ) ;
2012-06-14 09:06:06 -04:00
2014-10-20 16:55:07 -04:00
if ( Closest ! = nullptr )
2012-06-14 09:06:06 -04:00
{
2013-04-13 17:02:10 -04:00
EventSeePlayer ( Closest ) ;
2012-06-14 09:06:06 -04:00
}
}
2012-08-19 15:42:32 -04:00
2013-04-13 17:02:10 -04:00
void cMonster : : CheckEventLostPlayer ( void )
2014-07-17 16:50:58 -04:00
{
2014-10-20 16:55:07 -04:00
if ( m_Target ! = nullptr )
2012-08-19 15:42:32 -04:00
{
2014-01-24 15:46:22 -05:00
if ( ( m_Target - > GetPosition ( ) - GetPosition ( ) ) . Length ( ) > m_SightDistance )
2012-06-14 09:06:06 -04:00
{
EventLosePlayer ( ) ;
}
2012-08-19 15:42:32 -04:00
}
else
{
2012-06-14 09:06:06 -04:00
EventLosePlayer ( ) ;
}
}
2012-08-19 15:42:32 -04:00
// What to do if player is seen
// default to change state to chasing
2013-04-13 17:02:10 -04:00
void cMonster : : EventSeePlayer ( cEntity * a_SeenPlayer )
2012-06-14 09:06:06 -04:00
{
m_Target = a_SeenPlayer ;
}
2012-08-19 15:42:32 -04:00
2012-12-22 05:15:53 -05:00
void cMonster : : EventLosePlayer ( void )
2012-08-19 15:42:32 -04:00
{
2014-10-20 16:55:07 -04:00
m_Target = nullptr ;
2012-06-14 09:06:06 -04:00
m_EMState = IDLE ;
}
2012-08-19 15:42:32 -04:00
2015-01-16 09:38:21 -05:00
void cMonster : : InStateIdle ( std : : chrono : : milliseconds a_Dt )
2012-08-19 15:42:32 -04:00
{
2015-05-03 13:56:37 -04:00
if ( m_IsFollowingPath )
2014-01-25 09:42:26 -05:00
{
2014-07-17 16:15:34 -04:00
return ; // Still getting there
2014-01-25 09:42:26 -05:00
}
2013-12-20 10:39:20 -05:00
m_IdleInterval + = a_Dt ;
2014-01-24 14:57:32 -05:00
2015-01-16 09:38:21 -05:00
if ( m_IdleInterval > std : : chrono : : seconds ( 1 ) )
2012-08-19 15:42:32 -04:00
{
2014-01-24 14:57:32 -05:00
// At this interval the results are predictable
2013-04-13 17:02:10 -04:00
int rem = m_World - > GetTickRandomNumber ( 6 ) + 1 ;
2015-01-16 09:38:21 -05:00
m_IdleInterval - = std : : chrono : : seconds ( 1 ) ; // So nothing gets dropped when the server hangs for a few seconds
2014-01-24 14:57:32 -05:00
Vector3d Dist ;
2014-01-25 09:42:26 -05:00
Dist . x = ( double ) m_World - > GetTickRandomNumber ( 10 ) - 5 ;
Dist . z = ( double ) m_World - > GetTickRandomNumber ( 10 ) - 5 ;
2014-01-24 14:57:32 -05:00
2012-12-22 05:15:53 -05:00
if ( ( Dist . SqrLength ( ) > 2 ) & & ( rem > = 3 ) )
2012-06-14 09:06:06 -04:00
{
2014-01-25 09:42:26 -05:00
Vector3d Destination ( GetPosX ( ) + Dist . x , 0 , GetPosZ ( ) + Dist . z ) ;
2015-05-03 13:56:37 -04:00
Destination . y = FindFirstNonAirBlockPosition ( Destination . x , Destination . z ) ;
MoveToPosition ( Destination ) ;
2012-06-14 09:06:06 -04:00
}
}
}
2012-08-19 15:42:32 -04:00
// What to do if in Chasing State
// This state should always be defined in each child class
2015-01-16 09:38:21 -05:00
void cMonster : : InStateChasing ( std : : chrono : : milliseconds a_Dt )
2012-08-19 15:42:32 -04:00
{
UNUSED ( a_Dt ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-19 15:42:32 -04:00
// What to do if in Escaping State
2015-01-16 09:38:21 -05:00
void cMonster : : InStateEscaping ( std : : chrono : : milliseconds a_Dt )
2012-08-19 15:42:32 -04:00
{
2012-12-22 05:15:53 -05:00
UNUSED ( a_Dt ) ;
2015-04-29 12:24:14 -04:00
2014-10-20 16:55:07 -04:00
if ( m_Target ! = nullptr )
2012-08-19 15:42:32 -04:00
{
2013-03-22 02:33:10 -04:00
Vector3d newloc = GetPosition ( ) ;
2012-06-14 09:06:06 -04:00
newloc . x = ( m_Target - > GetPosition ( ) . x < newloc . x ) ? ( newloc . x + m_SightDistance ) : ( newloc . x - m_SightDistance ) ;
newloc . z = ( m_Target - > GetPosition ( ) . z < newloc . z ) ? ( newloc . z + m_SightDistance ) : ( newloc . z - m_SightDistance ) ;
MoveToPosition ( newloc ) ;
}
2012-08-19 15:42:32 -04:00
else
{
2012-12-22 05:15:53 -05:00
m_EMState = IDLE ; // This shouldnt be required but just to be safe
2012-06-14 09:06:06 -04:00
}
}
2014-09-01 14:12:56 -04:00
void cMonster : : SetCustomName ( const AString & a_CustomName )
{
m_CustomName = a_CustomName ;
// The maximal length is 64
if ( a_CustomName . length ( ) > 64 )
{
m_CustomName = a_CustomName . substr ( 0 , 64 ) ;
}
2014-10-20 16:55:07 -04:00
if ( m_World ! = nullptr )
2014-09-02 13:34:58 -04:00
{
m_World - > BroadcastEntityMetadata ( * this ) ;
}
2014-09-01 14:12:56 -04:00
}
void cMonster : : SetCustomNameAlwaysVisible ( bool a_CustomNameAlwaysVisible )
{
m_CustomNameAlwaysVisible = a_CustomNameAlwaysVisible ;
2014-10-20 16:55:07 -04:00
if ( m_World ! = nullptr )
2014-09-02 13:34:58 -04:00
{
m_World - > BroadcastEntityMetadata ( * this ) ;
}
2014-09-01 14:12:56 -04:00
}
2012-12-22 04:39:13 -05:00
void cMonster : : GetMonsterConfig ( const AString & a_Name )
2012-06-14 09:06:06 -04:00
{
2012-12-22 04:39:13 -05:00
cRoot : : Get ( ) - > GetMonsterConfig ( ) - > AssignAttributes ( this , a_Name ) ;
2012-06-14 09:06:06 -04:00
}
2014-06-08 21:44:20 -04:00
bool cMonster : : IsUndead ( void )
{
return false ;
}
2014-09-17 13:40:10 -04:00
AString cMonster : : MobTypeToString ( eMonsterType a_MobType )
2013-10-20 07:25:56 -04:00
{
// Mob types aren't sorted, so we need to search linearly:
2013-12-20 10:01:34 -05:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( g_MobTypeNames ) ; i + + )
2013-10-20 07:25:56 -04:00
{
if ( g_MobTypeNames [ i ] . m_Type = = a_MobType )
{
return g_MobTypeNames [ i ] . m_lcName ;
}
}
2015-04-29 12:24:14 -04:00
2013-10-20 07:25:56 -04:00
// Not found:
return " " ;
}
2014-11-29 09:20:44 -05:00
AString cMonster : : MobTypeToVanillaName ( eMonsterType a_MobType )
2013-10-20 07:25:56 -04:00
{
2014-11-29 09:20:44 -05:00
// Mob types aren't sorted, so we need to search linearly:
for ( size_t i = 0 ; i < ARRAYCOUNT ( g_MobTypeNames ) ; i + + )
2013-10-20 07:25:56 -04:00
{
2014-11-29 09:20:44 -05:00
if ( g_MobTypeNames [ i ] . m_Type = = a_MobType )
2013-10-20 07:25:56 -04:00
{
2014-11-29 09:20:44 -05:00
return g_MobTypeNames [ i ] . m_VanillaName ;
2013-10-20 07:25:56 -04:00
}
}
2014-11-29 09:20:44 -05:00
// Not found:
return " " ;
}
eMonsterType cMonster : : StringToMobType ( const AString & a_Name )
{
AString lcName = StrToLower ( a_Name ) ;
// Search MCServer name:
for ( size_t i = 0 ; i < ARRAYCOUNT ( g_MobTypeNames ) ; i + + )
2013-10-20 07:25:56 -04:00
{
2014-11-29 09:20:44 -05:00
if ( strcmp ( g_MobTypeNames [ i ] . m_lcName , lcName . c_str ( ) ) = = 0 )
{
return g_MobTypeNames [ i ] . m_Type ;
}
2013-10-20 07:25:56 -04:00
}
2014-11-29 09:20:44 -05:00
// Not found. Search Vanilla name:
for ( size_t i = 0 ; i < ARRAYCOUNT ( g_MobTypeNames ) ; i + + )
2013-10-20 07:25:56 -04:00
{
2014-11-29 09:20:44 -05:00
if ( strcmp ( StrToLower ( g_MobTypeNames [ i ] . m_VanillaName ) . c_str ( ) , lcName . c_str ( ) ) = = 0 )
{
return g_MobTypeNames [ i ] . m_Type ;
}
2013-10-20 07:25:56 -04:00
}
2014-11-29 09:20:44 -05:00
2013-10-20 07:25:56 -04:00
// Not found:
return mtInvalidType ;
}
2014-09-17 13:40:10 -04:00
cMonster : : eFamily cMonster : : FamilyFromType ( eMonsterType a_Type )
2013-10-20 07:25:56 -04:00
{
2014-01-24 14:57:32 -05:00
// Passive-agressive mobs are counted in mob spawning code as passive
2013-10-20 08:15:55 -04:00
switch ( a_Type )
2013-10-20 07:25:56 -04:00
{
2013-10-20 08:15:55 -04:00
case mtBat : return mfAmbient ;
case mtBlaze : return mfHostile ;
case mtCaveSpider : return mfHostile ;
case mtChicken : return mfPassive ;
case mtCow : return mfPassive ;
case mtCreeper : return mfHostile ;
2014-04-25 23:49:55 -04:00
case mtEnderDragon : return mfNoSpawn ;
2013-10-20 08:15:55 -04:00
case mtEnderman : return mfHostile ;
case mtGhast : return mfHostile ;
2014-04-25 23:49:55 -04:00
case mtGiant : return mfNoSpawn ;
2014-12-24 18:44:09 -05:00
case mtGuardian : return mfWater ; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited!
2013-10-20 08:15:55 -04:00
case mtHorse : return mfPassive ;
2013-12-25 11:07:52 -05:00
case mtIronGolem : return mfPassive ;
2013-10-20 08:15:55 -04:00
case mtMagmaCube : return mfHostile ;
case mtMooshroom : return mfHostile ;
2013-12-25 11:07:52 -05:00
case mtOcelot : return mfPassive ;
2013-10-20 08:15:55 -04:00
case mtPig : return mfPassive ;
2014-12-20 04:31:34 -05:00
case mtRabbit : return mfPassive ;
2013-10-20 08:15:55 -04:00
case mtSheep : return mfPassive ;
case mtSilverfish : return mfHostile ;
case mtSkeleton : return mfHostile ;
case mtSlime : return mfHostile ;
2014-04-25 23:49:55 -04:00
case mtSnowGolem : return mfNoSpawn ;
2013-10-20 08:15:55 -04:00
case mtSpider : return mfHostile ;
case mtSquid : return mfWater ;
case mtVillager : return mfPassive ;
case mtWitch : return mfHostile ;
2014-04-25 23:49:55 -04:00
case mtWither : return mfNoSpawn ;
2013-10-20 08:15:55 -04:00
case mtWolf : return mfHostile ;
case mtZombie : return mfHostile ;
case mtZombiePigman : return mfHostile ;
2015-04-29 12:24:14 -04:00
2014-04-24 21:11:11 -04:00
case mtInvalidType : break ;
}
2013-10-20 08:15:55 -04:00
ASSERT ( ! " Unhandled mob type " ) ;
2014-04-24 21:11:11 -04:00
return mfUnhandled ;
2013-10-20 07:25:56 -04:00
}
2013-10-24 10:45:13 -04:00
int cMonster : : GetSpawnDelay ( cMonster : : eFamily a_MobFamily )
2013-10-20 08:00:45 -04:00
{
switch ( a_MobFamily )
{
2014-04-24 21:11:11 -04:00
case mfHostile : return 40 ;
case mfPassive : return 40 ;
case mfAmbient : return 40 ;
case mfWater : return 400 ;
2014-04-25 23:49:55 -04:00
case mfNoSpawn : return - 1 ;
2014-04-24 21:11:11 -04:00
case mfUnhandled : break ;
2013-10-20 08:00:45 -04:00
}
ASSERT ( ! " Unhandled mob family " ) ;
return - 1 ;
}
2014-09-17 13:40:10 -04:00
cMonster * cMonster : : NewMonsterFromType ( eMonsterType a_MobType )
2013-10-20 07:25:56 -04:00
{
cFastRandom Random ;
2014-10-20 16:55:07 -04:00
cMonster * toReturn = nullptr ;
2013-10-20 07:25:56 -04:00
2013-11-10 15:48:12 -05:00
// Create the mob entity
2013-10-20 07:25:56 -04:00
switch ( a_MobType )
{
case mtMagmaCube :
2014-05-02 13:17:22 -04:00
{
2015-05-06 23:29:36 -04:00
toReturn = new cMagmaCube ( 1 < < Random . NextInt ( 3 ) ) ; // Size 1, 2 or 4
2014-05-02 13:17:22 -04:00
break ;
}
2013-10-20 07:25:56 -04:00
case mtSlime :
{
2014-07-18 17:20:42 -04:00
toReturn = new cSlime ( 1 < < Random . NextInt ( 3 ) ) ; // Size 1, 2 or 4
2013-11-13 05:08:51 -05:00
break ;
}
2013-11-10 17:20:25 -05:00
case mtSkeleton :
{
// TODO: Actual detection of spawning in Nether
2014-07-18 17:20:42 -04:00
toReturn = new cSkeleton ( ( Random . NextInt ( 1 ) = = 0 ) ? false : true ) ;
2013-11-10 17:20:25 -05:00
break ;
}
2013-11-10 15:48:12 -05:00
case mtVillager :
2013-10-20 07:25:56 -04:00
{
2013-11-13 05:08:51 -05:00
int VillagerType = Random . NextInt ( 6 ) ;
if ( VillagerType = = 6 )
2013-10-20 07:25:56 -04:00
{
2013-11-13 05:08:51 -05:00
// Give farmers a better chance of spawning
VillagerType = 0 ;
2013-10-20 07:25:56 -04:00
}
2013-11-10 15:48:12 -05:00
2013-11-13 05:08:51 -05:00
toReturn = new cVillager ( ( cVillager : : eVillagerType ) VillagerType ) ;
2013-11-10 15:48:12 -05:00
break ;
}
case mtHorse :
{
// Horses take a type (species), a colour, and a style (dots, stripes, etc.)
2013-11-13 05:08:51 -05:00
int HorseType = Random . NextInt ( 7 ) ;
int HorseColor = Random . NextInt ( 6 ) ;
int HorseStyle = Random . NextInt ( 6 ) ;
int HorseTameTimes = Random . NextInt ( 6 ) + 1 ;
2013-11-10 15:48:12 -05:00
2013-11-13 05:08:51 -05:00
if ( ( HorseType = = 5 ) | | ( HorseType = = 6 ) | | ( HorseType = = 7 ) )
2013-10-20 07:25:56 -04:00
{
2013-11-13 05:08:51 -05:00
// Increase chances of normal horse (zero)
HorseType = 0 ;
2013-10-20 07:25:56 -04:00
}
2013-11-10 15:48:12 -05:00
2013-11-13 05:08:51 -05:00
toReturn = new cHorse ( HorseType , HorseColor , HorseStyle , HorseTameTimes ) ;
2013-10-20 07:25:56 -04:00
break ;
}
2013-11-13 05:08:51 -05:00
case mtBat : toReturn = new cBat ( ) ; break ;
case mtBlaze : toReturn = new cBlaze ( ) ; break ;
2014-04-24 15:00:27 -04:00
case mtCaveSpider : toReturn = new cCaveSpider ( ) ; break ;
2013-11-13 05:08:51 -05:00
case mtChicken : toReturn = new cChicken ( ) ; break ;
case mtCow : toReturn = new cCow ( ) ; break ;
case mtCreeper : toReturn = new cCreeper ( ) ; break ;
2013-12-22 14:15:09 -05:00
case mtEnderDragon : toReturn = new cEnderDragon ( ) ; break ;
2013-11-13 05:08:51 -05:00
case mtEnderman : toReturn = new cEnderman ( ) ; break ;
case mtGhast : toReturn = new cGhast ( ) ; break ;
2014-04-24 21:11:11 -04:00
case mtGiant : toReturn = new cGiant ( ) ; break ;
2014-12-18 13:30:32 -05:00
case mtGuardian : toReturn = new cGuardian ( ) ; break ;
2013-12-23 04:32:49 -05:00
case mtIronGolem : toReturn = new cIronGolem ( ) ; break ;
2013-11-13 05:08:51 -05:00
case mtMooshroom : toReturn = new cMooshroom ( ) ; break ;
case mtOcelot : toReturn = new cOcelot ( ) ; break ;
case mtPig : toReturn = new cPig ( ) ; break ;
2014-12-20 04:31:34 -05:00
case mtRabbit : toReturn = new cRabbit ( ) ; break ;
2014-06-28 06:59:09 -04:00
case mtSheep : toReturn = new cSheep ( ) ; break ;
2013-11-13 05:08:51 -05:00
case mtSilverfish : toReturn = new cSilverfish ( ) ; break ;
2013-12-22 14:15:09 -05:00
case mtSnowGolem : toReturn = new cSnowGolem ( ) ; break ;
2013-11-13 05:08:51 -05:00
case mtSpider : toReturn = new cSpider ( ) ; break ;
case mtSquid : toReturn = new cSquid ( ) ; break ;
case mtWitch : toReturn = new cWitch ( ) ; break ;
2013-12-23 04:32:49 -05:00
case mtWither : toReturn = new cWither ( ) ; break ;
2013-11-13 05:08:51 -05:00
case mtWolf : toReturn = new cWolf ( ) ; break ;
2014-07-17 16:15:34 -04:00
case mtZombie : toReturn = new cZombie ( false ) ; break ; // TODO: Infected zombie parameter
2013-11-13 05:08:51 -05:00
case mtZombiePigman : toReturn = new cZombiePigman ( ) ; break ;
2013-10-20 07:25:56 -04:00
default :
{
2013-11-10 15:48:12 -05:00
ASSERT ( ! " Unhandled mob type whilst trying to spawn mob! " ) ;
2013-10-20 07:25:56 -04:00
}
}
return toReturn ;
}
2012-08-19 15:42:32 -04:00
void cMonster : : AddRandomDropItem ( cItems & a_Drops , unsigned int a_Min , unsigned int a_Max , short a_Item , short a_ItemHealth )
2012-06-14 09:06:06 -04:00
{
MTRand r1 ;
int Count = r1 . randInt ( ) % ( a_Max + 1 - a_Min ) + a_Min ;
if ( Count > 0 )
{
a_Drops . push_back ( cItem ( a_Item , Count , a_ItemHealth ) ) ;
}
}
2013-09-05 16:40:08 -04:00
2014-02-23 13:44:58 -05:00
void cMonster : : AddRandomUncommonDropItem ( cItems & a_Drops , float a_Chance , short a_Item , short a_ItemHealth )
{
MTRand r1 ;
int Count = r1 . randInt ( ) % 1000 ;
if ( Count < ( a_Chance * 10 ) )
{
a_Drops . push_back ( cItem ( a_Item , 1 , a_ItemHealth ) ) ;
}
}
void cMonster : : AddRandomRareDropItem ( cItems & a_Drops , cItems & a_Items , short a_LootingLevel )
{
MTRand r1 ;
int Count = r1 . randInt ( ) % 200 ;
if ( Count < ( 5 + a_LootingLevel ) )
{
int Rare = r1 . randInt ( ) % a_Items . Size ( ) ;
a_Drops . push_back ( a_Items . at ( Rare ) ) ;
}
}
void cMonster : : AddRandomArmorDropItem ( cItems & a_Drops , short a_LootingLevel )
{
MTRand r1 ;
if ( r1 . randInt ( ) % 200 < ( ( m_DropChanceHelmet * 200 ) + ( a_LootingLevel * 2 ) ) )
{
2014-12-05 10:59:11 -05:00
if ( ! GetEquippedHelmet ( ) . IsEmpty ( ) )
{
a_Drops . push_back ( GetEquippedHelmet ( ) ) ;
}
2014-02-23 13:44:58 -05:00
}
2015-04-29 12:24:14 -04:00
2014-02-23 13:44:58 -05:00
if ( r1 . randInt ( ) % 200 < ( ( m_DropChanceChestplate * 200 ) + ( a_LootingLevel * 2 ) ) )
{
2014-12-05 10:59:11 -05:00
if ( ! GetEquippedChestplate ( ) . IsEmpty ( ) )
{
a_Drops . push_back ( GetEquippedChestplate ( ) ) ;
}
2014-02-23 13:44:58 -05:00
}
2015-04-29 12:24:14 -04:00
2014-02-23 13:44:58 -05:00
if ( r1 . randInt ( ) % 200 < ( ( m_DropChanceLeggings * 200 ) + ( a_LootingLevel * 2 ) ) )
{
2014-12-05 10:59:11 -05:00
if ( ! GetEquippedLeggings ( ) . IsEmpty ( ) )
{
a_Drops . push_back ( GetEquippedLeggings ( ) ) ;
}
2014-02-23 13:44:58 -05:00
}
2015-04-29 12:24:14 -04:00
2014-02-23 13:44:58 -05:00
if ( r1 . randInt ( ) % 200 < ( ( m_DropChanceBoots * 200 ) + ( a_LootingLevel * 2 ) ) )
{
2014-12-05 10:59:11 -05:00
if ( ! GetEquippedBoots ( ) . IsEmpty ( ) )
{
a_Drops . push_back ( GetEquippedBoots ( ) ) ;
}
2014-02-23 13:44:58 -05:00
}
}
void cMonster : : AddRandomWeaponDropItem ( cItems & a_Drops , short a_LootingLevel )
{
MTRand r1 ;
if ( r1 . randInt ( ) % 200 < ( ( m_DropChanceWeapon * 200 ) + ( a_LootingLevel * 2 ) ) )
{
2014-12-05 10:59:11 -05:00
if ( ! GetEquippedWeapon ( ) . IsEmpty ( ) )
{
a_Drops . push_back ( GetEquippedWeapon ( ) ) ;
}
2014-02-23 13:44:58 -05:00
}
}
2015-05-01 13:34:24 -04:00
void cMonster : : HandleDaylightBurning ( cChunk & a_Chunk , bool WouldBurn )
2013-09-05 16:40:08 -04:00
{
if ( ! m_BurnsInDaylight )
{
return ;
}
2015-04-29 12:24:14 -04:00
2014-04-17 13:50:25 -04:00
int RelY = POSY_TOINT ;
2013-09-05 16:40:08 -04:00
if ( ( RelY < 0 ) | | ( RelY > = cChunkDef : : Height ) )
{
// Outside the world
return ;
}
2014-01-24 14:57:32 -05:00
if ( ! a_Chunk . IsLightValid ( ) )
{
m_World - > QueueLightChunk ( GetChunkX ( ) , GetChunkZ ( ) ) ;
return ;
}
2015-05-01 13:34:24 -04:00
if ( ! IsOnFire ( ) & & WouldBurn )
2015-04-29 12:24:14 -04:00
{
// Burn for 100 ticks, then decide again
StartBurning ( 100 ) ;
}
}
bool cMonster : : WouldBurnAt ( Vector3d a_Location , cChunk & a_Chunk )
{
2015-05-08 20:51:25 -04:00
cChunk * Chunk = a_Chunk . GetNeighborChunk ( FloorC ( a_Location . x ) , FloorC ( a_Location . z ) ) ;
2015-05-06 10:02:50 -04:00
if ( ( Chunk = = nullptr ) | | ( ! Chunk - > IsValid ( ) ) )
{
return false ;
}
2015-05-08 20:51:25 -04:00
int RelX = FloorC ( a_Location . x ) - Chunk - > GetPosX ( ) * cChunkDef : : Width ;
2015-04-29 12:24:14 -04:00
int RelY = FloorC ( a_Location . y ) ;
2015-05-08 20:51:25 -04:00
int RelZ = FloorC ( a_Location . z ) - Chunk - > GetPosZ ( ) * cChunkDef : : Width ;
2013-09-05 16:40:08 -04:00
if (
2015-05-08 20:51:25 -04:00
( Chunk - > GetSkyLight ( RelX , RelY , RelZ ) = = 15 ) & & // In the daylight
( Chunk - > GetBlock ( RelX , RelY , RelZ ) ! = E_BLOCK_SOULSAND ) & & // Not on soulsand
2013-09-05 16:40:08 -04:00
( GetWorld ( ) - > GetTimeOfDay ( ) < ( 12000 + 1000 ) ) & & // It is nighttime
2014-08-27 18:01:01 -04:00
GetWorld ( ) - > IsWeatherSunnyAt ( POSX_TOINT , POSZ_TOINT ) // Not raining
2013-09-05 16:40:08 -04:00
)
{
2015-04-29 12:24:14 -04:00
return true ;
2013-09-05 16:40:08 -04:00
}
2015-04-29 12:24:14 -04:00
return false ;
2013-09-05 16:40:08 -04:00
}
2015-04-29 12:24:14 -04:00
2013-09-10 09:09:45 -04:00
cMonster : : eFamily cMonster : : GetMobFamily ( void ) const
{
2013-10-20 07:25:56 -04:00
return FamilyFromType ( m_MobType ) ;
2013-09-10 09:09:45 -04:00
}