2008-10-11 14:16:25 -05:00
/*
2009-02-04 12:42:26 +01:00
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
2008-10-14 11:57:03 -05:00
*
2009-02-04 12:04:12 +01:00
* Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/>
2008-10-11 14:16:25 -05:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
2008-11-04 09:23:06 -06:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2008-10-11 14:16:25 -05:00
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
2008-11-04 09:23:06 -06:00
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2008-10-11 14:16:25 -05:00
*/
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "WorldPacket.h"
#include "World.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "Creature.h"
#include "QuestDef.h"
#include "GossipDef.h"
#include "Player.h"
2009-02-19 18:44:20 -06:00
#include "PoolHandler.h"
2008-10-11 14:16:25 -05:00
#include "Opcodes.h"
#include "Log.h"
#include "LootMgr.h"
#include "MapManager.h"
#include "CreatureAI.h"
#include "CreatureAISelector.h"
#include "Formulas.h"
#include "WaypointMovementGenerator.h"
#include "InstanceData.h"
2009-02-09 22:08:06 -06:00
#include "BattleGroundMgr.h"
2008-10-11 14:16:25 -05:00
#include "Util.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
2008-10-14 11:57:03 -05:00
#include "OutdoorPvPMgr.h"
2009-03-06 19:36:47 -06:00
#include "GameEventMgr.h"
2008-12-15 00:07:21 +02:00
#include "CreatureGroups.h"
2008-10-11 14:16:25 -05:00
// apply implementation of the singletons
#include "Policies/SingletonImp.h"
TrainerSpell const * TrainerSpellData :: Find ( uint32 spell_id ) const
{
2009-02-16 20:10:04 -06:00
TrainerSpellMap :: const_iterator itr = spellList . find ( spell_id );
if ( itr != spellList . end ())
return & itr -> second ;
2008-10-11 14:16:25 -05:00
return NULL ;
}
bool VendorItemData :: RemoveItem ( uint32 item_id )
{
for ( VendorItemList :: iterator i = m_items . begin (); i != m_items . end (); ++ i )
{
if (( * i ) -> item == item_id )
{
m_items . erase ( i );
return true ;
}
}
return false ;
}
size_t VendorItemData :: FindItemSlot ( uint32 item_id ) const
{
for ( size_t i = 0 ; i < m_items . size (); ++ i )
if ( m_items [ i ] -> item == item_id )
return i ;
return m_items . size ();
}
VendorItem const * VendorItemData :: FindItem ( uint32 item_id ) const
{
for ( VendorItemList :: const_iterator i = m_items . begin (); i != m_items . end (); ++ i )
if (( * i ) -> item == item_id )
return * i ;
return NULL ;
}
2008-10-27 11:48:45 -05:00
uint32 CreatureInfo :: GetRandomValidModelId () const
{
uint32 c = 0 ;
uint32 modelIDs [ 4 ];
2009-06-15 18:02:12 -05:00
if ( DisplayID_A [ 0 ]) modelIDs [ c ++ ] = DisplayID_A [ 0 ];
if ( DisplayID_A [ 1 ]) modelIDs [ c ++ ] = DisplayID_A [ 1 ];
if ( DisplayID_H [ 0 ]) modelIDs [ c ++ ] = DisplayID_H [ 0 ];
if ( DisplayID_H [ 1 ]) modelIDs [ c ++ ] = DisplayID_H [ 1 ];
2008-10-27 11:48:45 -05:00
return (( c > 0 ) ? modelIDs [ urand ( 0 , c - 1 )] : 0 );
}
uint32 CreatureInfo :: GetFirstValidModelId () const
{
2009-06-15 18:02:12 -05:00
if ( DisplayID_A [ 0 ]) return DisplayID_A [ 0 ];
if ( DisplayID_A [ 1 ]) return DisplayID_A [ 1 ];
if ( DisplayID_H [ 0 ]) return DisplayID_H [ 0 ];
if ( DisplayID_H [ 1 ]) return DisplayID_H [ 1 ];
2008-10-27 11:48:45 -05:00
return 0 ;
}
2008-11-25 16:36:50 -06:00
bool AssistDelayEvent :: Execute ( uint64 /*e_time*/ , uint32 /*p_time*/ )
{
2009-05-21 10:15:09 -05:00
if ( Unit * victim = Unit :: GetUnit ( m_owner , m_victim ))
2008-11-25 16:36:50 -06:00
{
while ( ! m_assistants . empty ())
{
2009-04-11 13:42:34 -05:00
Creature * assistant = Unit :: GetCreature ( m_owner , * m_assistants . begin ());
2008-11-25 16:36:50 -06:00
m_assistants . pop_front ();
if ( assistant && assistant -> CanAssistTo ( & m_owner , victim ))
{
2008-12-01 12:59:39 -06:00
assistant -> SetNoCallAssistance ( true );
2008-11-25 16:36:50 -06:00
assistant -> CombatStart ( victim );
2009-03-05 18:48:56 -06:00
if ( assistant -> IsAIEnabled )
2008-11-25 16:36:50 -06:00
assistant -> AI () -> AttackStart ( victim );
}
}
}
return true ;
}
2008-10-11 14:16:25 -05:00
Creature :: Creature () :
2009-03-12 14:15:44 -06:00
Unit (),
2008-10-11 14:16:25 -05:00
lootForPickPocketed ( false ), lootForBody ( false ), m_groupLootTimer ( 0 ), lootingGroupLeaderGUID ( 0 ),
m_lootMoney ( 0 ), m_lootRecipient ( 0 ),
m_deathTimer ( 0 ), m_respawnTime ( 0 ), m_respawnDelay ( 25 ), m_corpseDelay ( 60 ), m_respawnradius ( 0.0f ),
2009-04-13 17:21:17 -05:00
m_gossipOptionLoaded ( false ),
2009-02-24 11:14:28 -06:00
m_defaultMovementType ( IDLE_MOTION_TYPE ), m_DBTableGuid ( 0 ), m_equipmentId ( 0 ), m_AlreadyCallAssistance ( false ),
m_regenHealth ( true ), m_AI_locked ( false ), m_isDeadByDefault ( false ), m_meleeDamageSchoolMask ( SPELL_SCHOOL_MASK_NORMAL ),
2009-08-23 22:09:43 -05:00
m_creatureInfo ( NULL ), m_reactState ( REACT_AGGRESSIVE ), m_formation ( NULL )
2009-05-21 10:48:00 -05:00
, m_AlreadySearchedAssistance ( false )
2009-06-10 10:13:45 -05:00
, m_creatureData ( NULL ), m_PlayerDamageReq ( 0 )
2008-10-11 14:16:25 -05:00
{
2008-12-24 09:58:26 -06:00
m_regenTimer = 200 ;
2008-10-11 14:16:25 -05:00
m_valuesCount = UNIT_END ;
2009-06-18 17:21:25 +02:00
for ( uint8 i = 0 ; i < CREATURE_MAX_SPELLS ; ++ i )
2008-10-11 14:16:25 -05:00
m_spells [ i ] = 0 ;
m_CreatureSpellCooldowns . clear ();
m_CreatureCategoryCooldowns . clear ();
m_GlobalCooldown = 0 ;
2009-04-10 18:11:09 +02:00
DisableReputationGain = false ;
2009-06-11 00:45:59 -05:00
//m_unit_movement_flags = MONSTER_MOVE_WALK;
2009-08-23 14:16:03 -05:00
m_SightDistance = sWorld . getConfig ( CONFIG_SIGHT_MONSTER );
2009-08-23 16:27:00 -05:00
m_CombatDistance = 0 ; //MELEE_RANGE;
2008-10-11 14:16:25 -05:00
}
Creature ::~ Creature ()
{
m_vendorItemCounts . clear ();
2009-03-24 22:25:22 -06:00
if ( i_AI )
{
delete i_AI ;
i_AI = NULL ;
}
2009-05-09 22:31:53 -05:00
//if(m_uint32Values)
// sLog.outDetail("Deconstruct Creature Entry = %u", GetEntry());
2008-10-11 14:16:25 -05:00
}
void Creature :: AddToWorld ()
{
///- Register the creature for guid lookup
2009-03-23 20:13:37 -06:00
if ( ! IsInWorld ())
{
2009-05-30 22:15:05 -05:00
if ( m_zoneScript )
m_zoneScript -> OnCreatureCreate ( this , true );
2009-03-23 20:13:37 -06:00
ObjectAccessor :: Instance (). AddObject ( this );
Unit :: AddToWorld ();
2009-05-11 13:27:10 -05:00
SearchFormationAndPath ();
2009-04-06 20:33:52 -05:00
AIM_Initialize ();
2009-03-23 20:13:37 -06:00
}
2008-10-11 14:16:25 -05:00
}
void Creature :: RemoveFromWorld ()
{
2009-03-23 20:13:37 -06:00
if ( IsInWorld ())
{
2009-05-30 22:15:05 -05:00
if ( m_zoneScript )
m_zoneScript -> OnCreatureCreate ( this , false );
2009-04-06 20:33:52 -05:00
if ( m_formation )
formation_mgr . RemoveCreatureFromGroup ( m_formation , this );
2009-03-23 20:13:37 -06:00
Unit :: RemoveFromWorld ();
2009-03-28 16:04:37 -06:00
ObjectAccessor :: Instance (). RemoveObject ( this );
2009-03-23 20:13:37 -06:00
}
2008-10-11 14:16:25 -05:00
}
2009-08-14 22:26:07 -05:00
void Creature :: DisappearAndDie ()
{
//DestroyForNearbyPlayers();
SetVisibility ( VISIBILITY_OFF );
ObjectAccessor :: UpdateObjectVisibility ( this );
2009-08-26 19:28:21 -05:00
if ( isAlive ())
setDeathState ( JUST_DIED );
2009-08-14 22:26:07 -05:00
}
2009-05-11 13:27:10 -05:00
void Creature :: SearchFormationAndPath ()
2008-12-15 00:07:21 +02:00
{
2009-04-06 20:33:52 -05:00
if ( isSummon ())
2009-04-04 15:04:24 -06:00
return ;
2009-02-09 08:16:34 -05:00
uint32 lowguid = GetDBTableGUIDLow ();
2009-04-06 13:27:42 -05:00
if ( ! lowguid )
return ;
2009-02-04 10:33:18 -06:00
2009-05-11 13:27:10 -05:00
bool usePath = ( GetDefaultMovementType () == WAYPOINT_MOTION_TYPE );
2009-04-06 20:33:52 -05:00
CreatureGroupInfoType :: iterator frmdata = CreatureGroupMap . find ( lowguid );
2009-04-06 13:27:42 -05:00
if ( frmdata != CreatureGroupMap . end ())
2009-05-11 13:27:10 -05:00
{
if ( usePath && lowguid != frmdata -> second -> leaderGUID )
{
SetDefaultMovementType ( IDLE_MOTION_TYPE );
usePath = false ;
}
2009-04-06 20:33:52 -05:00
formation_mgr . AddCreatureToGroup ( frmdata -> second -> leaderGUID , this );
2009-05-11 13:27:10 -05:00
}
if ( usePath )
{
if ( WaypointMgr . GetPath ( lowguid * 10 ))
SetWaypointPathId ( lowguid * 10 );
else
{
sLog . outErrorDb ( "Creature DBGUID %u has waypoint motion type, but it does not have a waypoint path!" , lowguid );
SetDefaultMovementType ( IDLE_MOTION_TYPE );
}
}
2008-12-15 00:07:21 +02:00
}
2008-10-11 14:16:25 -05:00
void Creature :: RemoveCorpse ()
{
if ( getDeathState () != CORPSE && ! m_isDeadByDefault || getDeathState () != ALIVE && m_isDeadByDefault )
return ;
m_deathTimer = 0 ;
setDeathState ( DEAD );
ObjectAccessor :: UpdateObjectVisibility ( this );
loot . clear ();
m_respawnTime = time ( NULL ) + m_respawnDelay ;
float x , y , z , o ;
GetRespawnCoord ( x , y , z , & o );
2009-02-09 08:16:34 -05:00
SetHomePosition ( x , y , z , o );
GetMap () -> CreatureRelocation ( this , x , y , z , o );
2008-10-11 14:16:25 -05:00
}
/**
* change the entry of creature until respawn
*/
bool Creature :: InitEntry ( uint32 Entry , uint32 team , const CreatureData * data )
{
CreatureInfo const * normalInfo = objmgr . GetCreatureTemplate ( Entry );
if ( ! normalInfo )
{
sLog . outErrorDb ( "Creature::UpdateEntry creature entry %u does not exist." , Entry );
return false ;
}
// get heroic mode entry
uint32 actualEntry = Entry ;
CreatureInfo const * cinfo = normalInfo ;
if ( normalInfo -> HeroicEntry )
{
2009-06-16 11:19:59 -05:00
//we already have valid Map pointer for current creature!
if ( GetMap () -> IsHeroic ())
2008-10-11 14:16:25 -05:00
{
cinfo = objmgr . GetCreatureTemplate ( normalInfo -> HeroicEntry );
if ( ! cinfo )
{
sLog . outErrorDb ( "Creature::UpdateEntry creature heroic entry %u does not exist." , actualEntry );
return false ;
}
}
}
2008-11-20 16:16:57 -06:00
SetEntry ( Entry ); // normal entry always
2008-10-11 14:16:25 -05:00
m_creatureInfo = cinfo ; // map mode related always
2009-05-29 21:24:23 -05:00
// equal to player Race field, but creature does not have race
SetByteValue ( UNIT_FIELD_BYTES_0 , 0 , 0 );
// known valid are: CLASS_WARRIOR,CLASS_PALADIN,CLASS_ROGUE,CLASS_MAGE
SetByteValue ( UNIT_FIELD_BYTES_0 , 1 , uint8 ( cinfo -> unit_class ));
2008-10-27 11:48:45 -05:00
// Cancel load if no model defined
if ( ! ( cinfo -> GetFirstValidModelId ()))
2008-10-11 14:16:25 -05:00
{
2008-10-27 11:48:45 -05:00
sLog . outErrorDb ( "Creature (Entry: %u) has no model defined in table `creature_template`, can't load. " , Entry );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-06-15 18:02:12 -05:00
uint32 display_id = objmgr . ChooseDisplayId ( 0 , GetCreatureInfo (), data );
2008-10-11 14:16:25 -05:00
CreatureModelInfo const * minfo = objmgr . GetCreatureModelRandomGender ( display_id );
2009-06-15 18:02:12 -05:00
if ( ! minfo ) // Cancel load if no model defined
2008-10-11 14:16:25 -05:00
{
2009-06-15 18:02:12 -05:00
sLog . outErrorDb ( "Creature (Entry: %u) has no model defined in table `creature_template`, can't load. " , Entry );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-06-15 18:02:12 -05:00
display_id = minfo -> modelid ; // it can be different (for another gender)
2008-10-11 14:16:25 -05:00
SetDisplayId ( display_id );
SetNativeDisplayId ( display_id );
SetByteValue ( UNIT_FIELD_BYTES_0 , 2 , minfo -> gender );
// Load creature equipment
if ( ! data || data -> equipmentId == 0 )
{ // use default from the template
LoadEquipment ( cinfo -> equipmentId );
}
else if ( data && data -> equipmentId != - 1 )
{ // override, -1 means no equipment
LoadEquipment ( data -> equipmentId );
}
SetName ( normalInfo -> Name ); // at normal entry always
SetFloatValue ( UNIT_FIELD_BOUNDINGRADIUS , minfo -> bounding_radius );
SetFloatValue ( UNIT_FIELD_COMBATREACH , minfo -> combat_reach );
SetFloatValue ( UNIT_MOD_CAST_SPEED , 1.0f );
SetSpeed ( MOVE_WALK , cinfo -> speed );
SetSpeed ( MOVE_RUN , cinfo -> speed );
SetSpeed ( MOVE_SWIM , cinfo -> speed );
SetFloatValue ( OBJECT_FIELD_SCALE_X , cinfo -> scale );
// checked at loading
m_defaultMovementType = MovementGeneratorType ( cinfo -> MovementType );
if ( ! m_respawnradius && m_defaultMovementType == RANDOM_MOTION_TYPE )
m_defaultMovementType = IDLE_MOTION_TYPE ;
2009-06-18 17:21:25 +02:00
for ( uint8 i = 0 ; i < CREATURE_MAX_SPELLS ; ++ i )
2009-03-14 09:24:59 -06:00
m_spells [ i ] = GetCreatureInfo () -> spells [ i ];
2009-03-14 09:18:55 -06:00
2008-10-11 14:16:25 -05:00
return true ;
}
bool Creature :: UpdateEntry ( uint32 Entry , uint32 team , const CreatureData * data )
{
if ( ! InitEntry ( Entry , team , data ))
return false ;
m_regenHealth = GetCreatureInfo () -> RegenHealth ;
// creatures always have melee weapon ready if any
2009-05-28 18:43:57 -05:00
SetSheath ( SHEATH_STATE_MELEE );
2008-10-11 14:16:25 -05:00
SelectLevel ( GetCreatureInfo ());
if ( team == HORDE )
2009-06-03 19:29:55 -05:00
setFaction ( GetCreatureInfo () -> faction_H );
2008-10-11 14:16:25 -05:00
else
2009-06-03 19:29:55 -05:00
setFaction ( GetCreatureInfo () -> faction_A );
2008-10-11 14:16:25 -05:00
2008-10-14 11:57:03 -05:00
if ( GetCreatureInfo () -> flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT )
SetUInt32Value ( UNIT_NPC_FLAGS , GetCreatureInfo () -> npcflag | gameeventmgr . GetNPCFlag ( this ));
else
SetUInt32Value ( UNIT_NPC_FLAGS , GetCreatureInfo () -> npcflag );
2008-10-11 14:16:25 -05:00
SetAttackTime ( BASE_ATTACK , GetCreatureInfo () -> baseattacktime );
SetAttackTime ( OFF_ATTACK , GetCreatureInfo () -> baseattacktime );
SetAttackTime ( RANGED_ATTACK , GetCreatureInfo () -> rangeattacktime );
2008-11-05 09:22:56 -06:00
SetUInt32Value ( UNIT_FIELD_FLAGS , GetCreatureInfo () -> unit_flags );
2008-10-11 14:16:25 -05:00
SetUInt32Value ( UNIT_DYNAMIC_FLAGS , GetCreatureInfo () -> dynamicflags );
2009-01-28 00:25:06 -06:00
RemoveFlag ( UNIT_FIELD_FLAGS , UNIT_FLAG_IN_COMBAT );
2008-11-20 18:54:50 -06:00
SetMeleeDamageSchool ( SpellSchools ( GetCreatureInfo () -> dmgschool ));
2008-10-11 14:16:25 -05:00
SetModifierValue ( UNIT_MOD_ARMOR , BASE_VALUE , float ( GetCreatureInfo () -> armor ));
SetModifierValue ( UNIT_MOD_RESISTANCE_HOLY , BASE_VALUE , float ( GetCreatureInfo () -> resistance1 ));
SetModifierValue ( UNIT_MOD_RESISTANCE_FIRE , BASE_VALUE , float ( GetCreatureInfo () -> resistance2 ));
SetModifierValue ( UNIT_MOD_RESISTANCE_NATURE , BASE_VALUE , float ( GetCreatureInfo () -> resistance3 ));
SetModifierValue ( UNIT_MOD_RESISTANCE_FROST , BASE_VALUE , float ( GetCreatureInfo () -> resistance4 ));
SetModifierValue ( UNIT_MOD_RESISTANCE_SHADOW , BASE_VALUE , float ( GetCreatureInfo () -> resistance5 ));
SetModifierValue ( UNIT_MOD_RESISTANCE_ARCANE , BASE_VALUE , float ( GetCreatureInfo () -> resistance6 ));
SetCanModifyStats ( true );
UpdateAllStats ();
FactionTemplateEntry const * factionTemplate = sFactionTemplateStore . LookupEntry ( GetCreatureInfo () -> faction_A );
if ( factionTemplate ) // check and error show at loading templates
{
FactionEntry const * factionEntry = sFactionStore . LookupEntry ( factionTemplate -> faction );
if ( factionEntry )
if ( ! ( GetCreatureInfo () -> flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN ) &&
( factionEntry -> team == ALLIANCE || factionEntry -> team == HORDE ) )
SetPvP ( true );
}
2008-11-06 10:27:58 -06:00
// HACK: trigger creature is always not selectable
2008-12-10 20:25:47 -06:00
if ( isTrigger ())
2008-11-06 10:27:58 -06:00
SetFlag ( UNIT_FIELD_FLAGS , UNIT_FLAG_NOT_SELECTABLE );
2008-12-10 20:25:47 -06:00
if ( isTotem () || isTrigger ()
2008-11-20 18:54:50 -06:00
|| GetCreatureType () == CREATURE_TYPE_CRITTER )
2008-12-07 09:21:12 -06:00
SetReactState ( REACT_PASSIVE );
2009-03-21 23:29:18 -06:00
/*else if(isCivilian())
SetReactState(REACT_DEFENSIVE);*/
2008-11-20 18:54:50 -06:00
else
2008-12-07 09:21:12 -06:00
SetReactState ( REACT_AGGRESSIVE );
2008-11-20 18:54:50 -06:00
2009-01-27 15:23:26 -06:00
if ( GetCreatureInfo () -> flags_extra & CREATURE_FLAG_EXTRA_NO_TAUNT )
{
ApplySpellImmune ( 0 , IMMUNITY_STATE , SPELL_AURA_MOD_TAUNT , true );
ApplySpellImmune ( 0 , IMMUNITY_EFFECT , SPELL_EFFECT_ATTACK_ME , true );
}
2009-08-26 13:38:09 -05:00
if ( GetCreatureInfo () -> InhabitType & INHABIT_AIR )
AddUnitMovementFlag ( MOVEMENTFLAG_FLY_MODE + MOVEMENTFLAG_FLYING );
2008-10-11 14:16:25 -05:00
return true ;
}
void Creature :: Update ( uint32 diff )
{
if ( m_GlobalCooldown <= diff )
m_GlobalCooldown = 0 ;
else
m_GlobalCooldown -= diff ;
switch ( m_deathState )
{
case JUST_ALIVED :
2008-11-14 16:28:45 -06:00
// Don't must be called, see Creature::setDeathState JUST_ALIVED -> ALIVE promoting.
2008-10-11 14:16:25 -05:00
sLog . outError ( "Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_ALIVED (4)" , GetGUIDLow (), GetEntry ());
break ;
case JUST_DIED :
2008-11-14 16:28:45 -06:00
// Don't must be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
2008-10-11 14:16:25 -05:00
sLog . outError ( "Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_DEAD (1)" , GetGUIDLow (), GetEntry ());
break ;
case DEAD :
{
if ( m_respawnTime <= time ( NULL ) )
{
2009-04-14 14:33:48 +02:00
if ( ! GetLinkedCreatureRespawnTime ()) // Can respawn
Respawn ();
else // the master is dead
2008-10-11 14:16:25 -05:00
{
2009-04-14 14:33:48 +02:00
if ( uint32 targetGuid = objmgr . GetLinkedRespawnGuid ( m_DBTableGuid ))
{
if ( targetGuid == m_DBTableGuid ) // if linking self, never respawn (check delayed to next day)
SetRespawnTime ( DAY );
else
2009-04-14 15:41:59 +02:00
m_respawnTime = ( time ( NULL ) > GetLinkedCreatureRespawnTime () ? time ( NULL ) : GetLinkedCreatureRespawnTime ()) + urand ( 5 , MINUTE ); // else copy time from master and add a little
2009-04-14 14:33:48 +02:00
SaveRespawnTime (); // also save to DB immediately
}
else
Respawn ();
2008-10-11 14:16:25 -05:00
}
}
break ;
}
case CORPSE :
{
if ( m_isDeadByDefault )
break ;
if ( m_deathTimer <= diff )
{
RemoveCorpse ();
DEBUG_LOG ( "Removing corpse... %u " , GetUInt32Value ( OBJECT_FIELD_ENTRY ));
}
else
{
2009-05-13 10:53:31 -05:00
// for delayed spells
m_Events . Update ( diff );
2008-10-11 14:16:25 -05:00
m_deathTimer -= diff ;
if ( m_groupLootTimer && lootingGroupLeaderGUID )
{
if ( diff <= m_groupLootTimer )
{
m_groupLootTimer -= diff ;
}
else
{
Group * group = objmgr . GetGroupByLeader ( lootingGroupLeaderGUID );
if ( group )
group -> EndRoll ();
m_groupLootTimer = 0 ;
lootingGroupLeaderGUID = 0 ;
}
}
}
break ;
}
case ALIVE :
{
if ( m_isDeadByDefault )
{
if ( m_deathTimer <= diff )
{
RemoveCorpse ();
DEBUG_LOG ( "Removing alive corpse... %u " , GetUInt32Value ( OBJECT_FIELD_ENTRY ));
}
else
{
m_deathTimer -= diff ;
}
}
Unit :: Update ( diff );
// creature can be dead after Unit::Update call
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
if ( ! isAlive ())
break ;
2009-03-12 14:15:44 -06:00
// if creature is charmed, switch to charmed AI
2009-03-13 17:48:57 -06:00
if ( NeedChangeAI )
{
UpdateCharmAI ();
NeedChangeAI = false ;
IsAIEnabled = true ;
}
2009-03-12 14:15:44 -06:00
2009-03-05 18:48:56 -06:00
if ( ! IsInEvadeMode () && IsAIEnabled )
2008-10-11 14:16:25 -05:00
{
// do not allow the AI to be changed during update
m_AI_locked = true ;
2008-11-20 16:16:57 -06:00
i_AI -> UpdateAI ( diff );
2008-10-11 14:16:25 -05:00
m_AI_locked = false ;
}
// creature can be dead after UpdateAI call
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
if ( ! isAlive ())
break ;
2009-08-23 22:36:51 -05:00
if ( m_regenTimer > diff )
m_regenTimer -= diff ;
else
2009-02-09 08:16:34 -05:00
{
2009-08-23 22:36:51 -05:00
if ( ! isInCombat () || IsPolymorphed ())
2009-02-09 08:16:34 -05:00
RegenerateHealth ();
2008-10-11 14:16:25 -05:00
2009-08-23 22:36:51 -05:00
if ( getPowerType () == POWER_ENERGY )
Regenerate ( POWER_ENERGY );
else
RegenerateMana ();
m_regenTimer += 2000 - diff ;
}
2008-10-11 14:16:25 -05:00
break ;
}
2008-12-08 16:34:03 -06:00
case DEAD_FALLING :
2009-08-26 22:56:20 -05:00
GetMotionMaster () -> UpdateMotion ( diff );
break ;
2008-10-11 14:16:25 -05:00
default :
break ;
}
}
void Creature :: RegenerateMana ()
{
uint32 curValue = GetPower ( POWER_MANA );
uint32 maxValue = GetMaxPower ( POWER_MANA );
if ( curValue >= maxValue )
return ;
uint32 addvalue = 0 ;
// Combat and any controlled creature
if ( isInCombat () || GetCharmerOrOwnerGUID ())
{
if ( ! IsUnderLastManaUseEffect ())
{
float ManaIncreaseRate = sWorld . getRate ( RATE_POWER_MANA );
float Spirit = GetStat ( STAT_SPIRIT );
addvalue = uint32 (( Spirit / 5.0f + 17.0f ) * ManaIncreaseRate );
}
}
else
addvalue = maxValue / 3 ;
ModifyPower ( POWER_MANA , addvalue );
}
void Creature :: RegenerateHealth ()
{
if ( ! isRegeneratingHealth ())
return ;
uint32 curValue = GetHealth ();
uint32 maxValue = GetMaxHealth ();
if ( curValue >= maxValue )
return ;
uint32 addvalue = 0 ;
2008-11-14 16:28:45 -06:00
// Not only pet, but any controlled creature
2008-10-11 14:16:25 -05:00
if ( GetCharmerOrOwnerGUID ())
{
float HealthIncreaseRate = sWorld . getRate ( RATE_HEALTH );
float Spirit = GetStat ( STAT_SPIRIT );
if ( GetPower ( POWER_MANA ) > 0 )
addvalue = uint32 ( Spirit * 0.25 * HealthIncreaseRate );
else
addvalue = uint32 ( Spirit * 0.80 * HealthIncreaseRate );
}
else
addvalue = maxValue / 3 ;
ModifyHealth ( addvalue );
}
2009-05-21 10:48:00 -05:00
void Creature :: DoFleeToGetAssistance ()
{
if ( ! getVictim ())
return ;
if ( HasAuraType ( SPELL_AURA_PREVENTS_FLEEING ))
return ;
float radius = sWorld . getConfig ( CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS );
if ( radius > 0 )
{
Creature * pCreature = NULL ;
CellPair p ( MaNGOS :: ComputeCellPair ( GetPositionX (), GetPositionY ()));
Cell cell ( p );
cell . data . Part . reserved = ALL_DISTRICT ;
cell . SetNoCreate ();
MaNGOS :: NearestAssistCreatureInCreatureRangeCheck u_check ( this , getVictim (), radius );
MaNGOS :: CreatureLastSearcher < MaNGOS :: NearestAssistCreatureInCreatureRangeCheck > searcher ( this , pCreature , u_check );
TypeContainerVisitor < MaNGOS :: CreatureLastSearcher < MaNGOS :: NearestAssistCreatureInCreatureRangeCheck > , GridTypeMapContainer > grid_creature_searcher ( searcher );
CellLock < GridReadGuard > cell_lock ( cell , p );
cell_lock -> Visit ( cell_lock , grid_creature_searcher , * GetMap ());
SetNoSearchAssistance ( true );
if ( ! pCreature )
//SetFeared(true, getVictim()->GetGUID(), 0 ,sWorld.getConfig(CONFIG_CREATURE_FAMILY_FLEE_DELAY));
//TODO: use 31365
SetControlled ( true , UNIT_STAT_FLEEING );
else
GetMotionMaster () -> MoveSeekAssistance ( pCreature -> GetPositionX (), pCreature -> GetPositionY (), pCreature -> GetPositionZ ());
}
}
2008-12-10 20:25:47 -06:00
bool Creature :: AIM_Initialize ( CreatureAI * ai )
2008-10-11 14:16:25 -05:00
{
// make sure nothing can change the AI during AI update
if ( m_AI_locked )
{
sLog . outDebug ( "AIM_Initialize: failed to init, locked." );
return false ;
}
2009-03-20 14:01:46 -06:00
UnitAI * oldAI = i_AI ;
2009-04-06 20:33:52 -05:00
Motion_Initialize ();
2009-04-06 13:27:42 -05:00
2009-04-06 20:33:52 -05:00
i_AI = ai ? ai : FactorySelector :: selectAI ( this );
if ( oldAI ) delete oldAI ;
IsAIEnabled = true ;
2009-04-13 17:53:09 -05:00
i_AI -> InitializeAI ();
2009-04-06 20:33:52 -05:00
return true ;
}
void Creature :: Motion_Initialize ()
{
2009-04-07 10:51:45 +02:00
if ( ! m_formation )
i_motionMaster . Initialize ();
else if ( m_formation -> getLeader () == this )
{
m_formation -> FormationReset ( false );
i_motionMaster . Initialize ();
}
else if ( m_formation -> isFormed ())
2009-04-06 20:33:52 -05:00
i_motionMaster . MoveIdle ( MOTION_SLOT_IDLE ); //wait the order of leader
else
i_motionMaster . Initialize ();
2008-10-11 14:16:25 -05:00
}
2009-08-23 22:09:43 -05:00
bool Creature :: Create ( uint32 guidlow , Map * map , uint32 phaseMask , uint32 Entry , uint32 vehId , uint32 team , float x , float y , float z , float ang , const CreatureData * data )
2008-10-11 14:16:25 -05:00
{
2009-08-01 00:20:32 +02:00
ASSERT ( map );
SetMap ( map );
SetPhaseMask ( phaseMask , false );
2009-05-30 22:15:05 -05:00
Relocate ( x , y , z , ang );
2009-08-01 00:20:32 +02:00
2009-05-30 22:15:05 -05:00
if ( ! IsPositionValid ())
{
sLog . outError ( "Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)" , guidlow , Entry , x , y );
return false ;
}
2008-10-11 14:16:25 -05:00
//oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0;
2009-08-23 22:09:43 -05:00
const bool bResult = CreateFromProto ( guidlow , Entry , vehId , team , data );
2008-10-11 14:16:25 -05:00
if ( bResult )
{
switch ( GetCreatureInfo () -> rank )
{
case CREATURE_ELITE_RARE :
m_corpseDelay = sWorld . getConfig ( CONFIG_CORPSE_DECAY_RARE );
break ;
case CREATURE_ELITE_ELITE :
m_corpseDelay = sWorld . getConfig ( CONFIG_CORPSE_DECAY_ELITE );
break ;
case CREATURE_ELITE_RAREELITE :
m_corpseDelay = sWorld . getConfig ( CONFIG_CORPSE_DECAY_RAREELITE );
break ;
case CREATURE_ELITE_WORLDBOSS :
m_corpseDelay = sWorld . getConfig ( CONFIG_CORPSE_DECAY_WORLDBOSS );
break ;
default :
m_corpseDelay = sWorld . getConfig ( CONFIG_CORPSE_DECAY_NORMAL );
break ;
}
LoadCreaturesAddon ();
}
return bResult ;
}
bool Creature :: isCanTrainingOf ( Player * pPlayer , bool msg ) const
{
if ( ! isTrainer ())
return false ;
TrainerSpellData const * trainer_spells = GetTrainerSpells ();
if ( ! trainer_spells || trainer_spells -> spellList . empty ())
{
sLog . outErrorDb ( "Creature %u (Entry: %u) have UNIT_NPC_FLAG_TRAINER but have empty trainer spell list." ,
GetGUIDLow (), GetEntry ());
return false ;
}
switch ( GetCreatureInfo () -> trainer_type )
{
case TRAINER_TYPE_CLASS :
2009-05-27 16:36:09 -05:00
if ( pPlayer -> getClass () != GetCreatureInfo () -> trainer_class )
2008-10-11 14:16:25 -05:00
{
if ( msg )
{
pPlayer -> PlayerTalkClass -> ClearMenus ();
2009-05-27 16:36:09 -05:00
switch ( GetCreatureInfo () -> trainer_class )
2008-10-11 14:16:25 -05:00
{
case CLASS_DRUID : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 4913 , GetGUID ()); break ;
case CLASS_HUNTER : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 10090 , GetGUID ()); break ;
case CLASS_MAGE : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 328 , GetGUID ()); break ;
case CLASS_PALADIN : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 1635 , GetGUID ()); break ;
case CLASS_PRIEST : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 4436 , GetGUID ()); break ;
case CLASS_ROGUE : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 4797 , GetGUID ()); break ;
case CLASS_SHAMAN : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 5003 , GetGUID ()); break ;
case CLASS_WARLOCK : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 5836 , GetGUID ()); break ;
case CLASS_WARRIOR : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 4985 , GetGUID ()); break ;
}
}
return false ;
}
break ;
case TRAINER_TYPE_PETS :
if ( pPlayer -> getClass () != CLASS_HUNTER )
{
pPlayer -> PlayerTalkClass -> ClearMenus ();
pPlayer -> PlayerTalkClass -> SendGossipMenu ( 3620 , GetGUID ());
return false ;
}
break ;
case TRAINER_TYPE_MOUNTS :
2009-05-27 16:36:09 -05:00
if ( GetCreatureInfo () -> trainer_race && pPlayer -> getRace () != GetCreatureInfo () -> trainer_race )
2008-10-11 14:16:25 -05:00
{
if ( msg )
{
pPlayer -> PlayerTalkClass -> ClearMenus ();
2009-05-27 16:36:09 -05:00
switch ( GetCreatureInfo () -> trainer_class )
2008-10-11 14:16:25 -05:00
{
case RACE_DWARF : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 5865 , GetGUID ()); break ;
case RACE_GNOME : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 4881 , GetGUID ()); break ;
case RACE_HUMAN : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 5861 , GetGUID ()); break ;
case RACE_NIGHTELF : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 5862 , GetGUID ()); break ;
case RACE_ORC : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 5863 , GetGUID ()); break ;
case RACE_TAUREN : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 5864 , GetGUID ()); break ;
case RACE_TROLL : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 5816 , GetGUID ()); break ;
case RACE_UNDEAD_PLAYER : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 624 , GetGUID ()); break ;
case RACE_BLOODELF : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 5862 , GetGUID ()); break ;
case RACE_DRAENEI : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 5864 , GetGUID ()); break ;
}
}
return false ;
}
break ;
case TRAINER_TYPE_TRADESKILLS :
if ( GetCreatureInfo () -> trainer_spell && ! pPlayer -> HasSpell ( GetCreatureInfo () -> trainer_spell ))
{
if ( msg )
{
pPlayer -> PlayerTalkClass -> ClearMenus ();
pPlayer -> PlayerTalkClass -> SendGossipMenu ( 11031 , GetGUID ());
}
return false ;
}
break ;
default :
return false ; // checked and error output at creature_template loading
}
return true ;
}
2008-12-23 10:54:13 +01:00
bool Creature :: isCanInteractWithBattleMaster ( Player * pPlayer , bool msg ) const
2008-10-11 14:16:25 -05:00
{
if ( ! isBattleMaster ())
return false ;
2009-02-09 22:08:06 -06:00
BattleGroundTypeId bgTypeId = sBattleGroundMgr . GetBattleMasterBG ( GetEntry ());
2008-10-11 14:16:25 -05:00
if ( ! msg )
return pPlayer -> GetBGAccessByLevel ( bgTypeId );
if ( ! pPlayer -> GetBGAccessByLevel ( bgTypeId ))
{
pPlayer -> PlayerTalkClass -> ClearMenus ();
switch ( bgTypeId )
{
case BATTLEGROUND_AV : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 7616 , GetGUID ()); break ;
case BATTLEGROUND_WS : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 7599 , GetGUID ()); break ;
case BATTLEGROUND_AB : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 7642 , GetGUID ()); break ;
case BATTLEGROUND_EY :
case BATTLEGROUND_NA :
case BATTLEGROUND_BE :
case BATTLEGROUND_AA :
2008-12-24 09:58:26 -06:00
case BATTLEGROUND_RL :
case BATTLEGROUND_SA :
case BATTLEGROUND_DS :
case BATTLEGROUND_RV : pPlayer -> PlayerTalkClass -> SendGossipMenu ( 10024 , GetGUID ()); break ;
default : break ;
2008-10-11 14:16:25 -05:00
}
return false ;
}
return true ;
}
bool Creature :: isCanTrainingAndResetTalentsOf ( Player * pPlayer ) const
{
return pPlayer -> getLevel () >= 10
&& GetCreatureInfo () -> trainer_type == TRAINER_TYPE_CLASS
2009-05-27 16:36:09 -05:00
&& pPlayer -> getClass () == GetCreatureInfo () -> trainer_class ;
2008-10-11 14:16:25 -05:00
}
void Creature :: prepareGossipMenu ( Player * pPlayer , uint32 gossipid )
{
2009-02-09 08:06:43 -05:00
//Prevent gossip from NPCs that are possessed.
Unit * Charmed = Unit :: GetCharmer ();
if ( Charmed )
return ;
2008-10-11 14:16:25 -05:00
PlayerMenu * pm = pPlayer -> PlayerTalkClass ;
pm -> ClearMenus ();
// lazy loading single time at use
LoadGossipOptions ();
2008-12-08 16:34:03 -06:00
for ( GossipOptionList :: iterator i = m_goptions . begin ( ); i != m_goptions . end ( ); ++ i )
2008-10-11 14:16:25 -05:00
{
2008-11-05 09:22:56 -06:00
GossipOption * gso =&* i ;
2008-10-11 14:16:25 -05:00
if ( gso -> GossipId == gossipid )
{
bool cantalking = true ;
if ( gso -> Id == 1 )
{
uint32 textid = GetNpcTextId ();
2009-02-16 20:12:55 -06:00
GossipText const * gossiptext = objmgr . GetGossipText ( textid );
2008-10-11 14:16:25 -05:00
if ( ! gossiptext )
cantalking = false ;
}
else
{
switch ( gso -> Action )
{
case GOSSIP_OPTION_QUESTGIVER :
pPlayer -> PrepareQuestMenu ( GetGUID ());
//if (pm->GetQuestMenu()->MenuItemCount() == 0)
cantalking = false ;
//pm->GetQuestMenu()->ClearMenu();
break ;
case GOSSIP_OPTION_ARMORER :
cantalking = false ; // added in special mode
break ;
case GOSSIP_OPTION_SPIRITHEALER :
if ( ! pPlayer -> isDead () )
cantalking = false ;
break ;
case GOSSIP_OPTION_VENDOR :
{
VendorItemData const * vItems = GetVendorItems ();
if ( ! vItems || vItems -> Empty ())
{
sLog . outErrorDb ( "Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list." ,
GetGUIDLow (), GetEntry ());
cantalking = false ;
}
break ;
}
case GOSSIP_OPTION_TRAINER :
if ( ! isCanTrainingOf ( pPlayer , false ))
cantalking = false ;
break ;
2009-08-14 17:24:52 -07:00
case GOSSIP_OPTION_LEARNDUALSPEC :
2009-08-15 13:39:40 -07:00
if ( ! ( pPlayer -> GetSpecsCount () == 1 && isCanTrainingAndResetTalentsOf ( pPlayer ) && ! ( pPlayer -> getLevel () < sWorld . getConfig ( CONFIG_MIN_DUALSPEC_LEVEL ))))
2009-08-14 17:24:52 -07:00
cantalking = false ;
break ;
2008-10-11 14:16:25 -05:00
case GOSSIP_OPTION_UNLEARNTALENTS :
if ( ! isCanTrainingAndResetTalentsOf ( pPlayer ))
cantalking = false ;
break ;
case GOSSIP_OPTION_UNLEARNPETSKILLS :
2009-05-27 16:36:09 -05:00
if ( ! pPlayer -> GetPet () || pPlayer -> GetPet () -> getPetType () != HUNTER_PET || pPlayer -> GetPet () -> m_spells . size () <= 1 || GetCreatureInfo () -> trainer_type != TRAINER_TYPE_PETS || GetCreatureInfo () -> trainer_class != CLASS_HUNTER )
2008-10-11 14:16:25 -05:00
cantalking = false ;
break ;
case GOSSIP_OPTION_TAXIVENDOR :
if ( pPlayer -> GetSession () -> SendLearnNewTaxiNode ( this ) )
return ;
break ;
case GOSSIP_OPTION_BATTLEFIELD :
2008-12-23 10:54:13 +01:00
if ( ! isCanInteractWithBattleMaster ( pPlayer , false ))
2008-10-11 14:16:25 -05:00
cantalking = false ;
break ;
case GOSSIP_OPTION_SPIRITGUIDE :
case GOSSIP_OPTION_INNKEEPER :
case GOSSIP_OPTION_BANKER :
case GOSSIP_OPTION_PETITIONER :
case GOSSIP_OPTION_STABLEPET :
case GOSSIP_OPTION_TABARDDESIGNER :
case GOSSIP_OPTION_AUCTIONEER :
break ; // no checks
2008-10-14 11:57:03 -05:00
case GOSSIP_OPTION_OUTDOORPVP :
if ( ! sOutdoorPvPMgr . CanTalkTo ( pPlayer , this ,( * gso )) )
cantalking = false ;
break ;
2008-10-11 14:16:25 -05:00
default :
2008-11-05 09:22:56 -06:00
sLog . outErrorDb ( "Creature %u (entry: %u) have unknown gossip option %u" , GetDBTableGUIDLow (), GetEntry (), gso -> Action );
2008-10-11 14:16:25 -05:00
break ;
}
}
2008-11-05 09:22:56 -06:00
//note for future dev: should have database fields for BoxMessage & BoxMoney
if ( ! gso -> OptionText . empty () && cantalking )
{
std :: string OptionText = gso -> OptionText ;
std :: string BoxText = gso -> BoxText ;
int loc_idx = pPlayer -> GetSession () -> GetSessionDbLocaleIndex ();
if ( loc_idx >= 0 )
{
NpcOptionLocale const * no = objmgr . GetNpcOptionLocale ( gso -> Id );
if ( no )
{
if ( no -> OptionText . size () > loc_idx && ! no -> OptionText [ loc_idx ]. empty ())
OptionText = no -> OptionText [ loc_idx ];
if ( no -> BoxText . size () > loc_idx && ! no -> BoxText [ loc_idx ]. empty ())
BoxText = no -> BoxText [ loc_idx ];
}
}
pm -> GetGossipMenu (). AddMenuItem (( uint8 ) gso -> Icon , OptionText , gossipid , gso -> Action , BoxText , gso -> BoxMoney , gso -> Coded );
2008-10-11 14:16:25 -05:00
}
}
}
///some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
if ( pm -> Empty ())
{
if ( HasFlag ( UNIT_NPC_FLAGS , UNIT_NPC_FLAG_TRAINER ))
{
isCanTrainingOf ( pPlayer , true ); // output error message if need
}
if ( HasFlag ( UNIT_NPC_FLAGS , UNIT_NPC_FLAG_BATTLEMASTER ))
{
2008-12-23 10:54:13 +01:00
isCanInteractWithBattleMaster ( pPlayer , true ); // output error message if need
2008-10-11 14:16:25 -05:00
}
}
}
void Creature :: sendPreparedGossip ( Player * player )
{
if ( ! player )
return ;
2008-10-14 11:57:03 -05:00
if ( GetCreatureInfo () -> flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT ) // if world event npc then
gameeventmgr . HandleWorldEventGossip ( player , this ); // update world state with progress
2009-02-15 12:00:37 -06:00
// in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
if ( ! HasFlag ( UNIT_NPC_FLAGS , UNIT_NPC_FLAG_GOSSIP ) && ! player -> PlayerTalkClass -> GetQuestMenu (). Empty ())
2008-10-11 14:16:25 -05:00
{
player -> SendPreparedQuest ( GetGUID ());
return ;
}
// in case non empty gossip menu (that not included quests list size) show it
2008-11-14 16:28:45 -06:00
// (quest entries from quest menu will be included in list)
2008-10-11 14:16:25 -05:00
player -> PlayerTalkClass -> SendGossipMenu ( GetNpcTextId (), GetGUID ());
}
void Creature :: OnGossipSelect ( Player * player , uint32 option )
{
GossipMenu & gossipmenu = player -> PlayerTalkClass -> GetGossipMenu ();
if ( option >= gossipmenu . MenuItemCount ())
return ;
uint32 action = gossipmenu . GetItem ( option ). m_gAction ;
uint32 zoneid = GetZoneId ();
uint64 guid = GetGUID ();
2008-11-04 09:23:06 -06:00
2008-10-11 14:16:25 -05:00
GossipOption const * gossip = GetGossipOption ( action );
if ( ! gossip )
{
zoneid = 0 ;
gossip = GetGossipOption ( action );
if ( ! gossip )
return ;
}
switch ( gossip -> Action )
{
case GOSSIP_OPTION_GOSSIP :
2008-11-05 20:10:19 -06:00
{
uint32 textid = GetGossipTextId ( action , zoneid );
if ( textid == 0 )
textid = GetNpcTextId ();
2008-10-11 14:16:25 -05:00
player -> PlayerTalkClass -> CloseGossip ();
2008-11-04 09:23:06 -06:00
player -> PlayerTalkClass -> SendTalking ( textid );
2008-10-11 14:16:25 -05:00
break ;
2009-02-09 08:16:34 -05:00
}
2008-10-14 11:57:03 -05:00
case GOSSIP_OPTION_OUTDOORPVP :
sOutdoorPvPMgr . HandleGossipOption ( player , GetGUID (), option );
break ;
2008-10-11 14:16:25 -05:00
case GOSSIP_OPTION_SPIRITHEALER :
2008-11-04 09:23:06 -06:00
if ( player -> isDead ())
2008-10-11 14:16:25 -05:00
CastSpell ( this , 17251 , true , NULL , NULL , player -> GetGUID ());
break ;
case GOSSIP_OPTION_QUESTGIVER :
player -> PrepareQuestMenu ( guid );
player -> SendPreparedQuest ( guid );
break ;
case GOSSIP_OPTION_VENDOR :
case GOSSIP_OPTION_ARMORER :
player -> GetSession () -> SendListInventory ( guid );
break ;
case GOSSIP_OPTION_STABLEPET :
player -> GetSession () -> SendStablePet ( guid );
break ;
case GOSSIP_OPTION_TRAINER :
player -> GetSession () -> SendTrainerList ( guid );
break ;
case GOSSIP_OPTION_UNLEARNTALENTS :
player -> PlayerTalkClass -> CloseGossip ();
player -> SendTalentWipeConfirm ( guid );
break ;
2009-08-14 17:24:52 -07:00
case GOSSIP_OPTION_LEARNDUALSPEC :
2009-08-15 13:39:40 -07:00
if ( player -> GetSpecsCount () == 1 && ! ( player -> getLevel () < sWorld . getConfig ( CONFIG_MIN_DUALSPEC_LEVEL )))
2009-08-14 17:24:52 -07:00
{
if ( player -> GetMoney () < 10000000 )
{
player -> SendBuyError ( BUY_ERR_NOT_ENOUGHT_MONEY , 0 , 0 , 0 );
player -> PlayerTalkClass -> CloseGossip ();
break ;
}
else
{
player -> ModifyMoney ( - 10000000 );
// Cast spells that teach dual spec
// Both are also ImplicitTarget self and must be cast by player
player -> CastSpell ( player , 63680 , true , NULL , NULL , player -> GetGUID ());
player -> CastSpell ( player , 63624 , true , NULL , NULL , player -> GetGUID ());
// Should show another Gossip text with "Congratulations..."
player -> PlayerTalkClass -> CloseGossip ();
}
}
break ;
2008-10-11 14:16:25 -05:00
case GOSSIP_OPTION_UNLEARNPETSKILLS :
player -> PlayerTalkClass -> CloseGossip ();
player -> SendPetSkillWipeConfirm ();
break ;
case GOSSIP_OPTION_TAXIVENDOR :
player -> GetSession () -> SendTaxiMenu ( this );
break ;
case GOSSIP_OPTION_INNKEEPER :
player -> PlayerTalkClass -> CloseGossip ();
player -> SetBindPoint ( guid );
break ;
case GOSSIP_OPTION_BANKER :
player -> GetSession () -> SendShowBank ( guid );
break ;
case GOSSIP_OPTION_PETITIONER :
player -> PlayerTalkClass -> CloseGossip ();
player -> GetSession () -> SendPetitionShowList ( guid );
break ;
case GOSSIP_OPTION_TABARDDESIGNER :
player -> PlayerTalkClass -> CloseGossip ();
player -> GetSession () -> SendTabardVendorActivate ( guid );
break ;
case GOSSIP_OPTION_AUCTIONEER :
player -> GetSession () -> SendAuctionHello ( guid , this );
break ;
case GOSSIP_OPTION_SPIRITGUIDE :
case GOSSIP_GUARD_SPELLTRAINER :
case GOSSIP_GUARD_SKILLTRAINER :
prepareGossipMenu ( player , gossip -> Id );
sendPreparedGossip ( player );
break ;
case GOSSIP_OPTION_BATTLEFIELD :
{
2009-02-09 22:08:06 -06:00
BattleGroundTypeId bgTypeId = sBattleGroundMgr . GetBattleMasterBG ( GetEntry ());
2008-10-11 14:16:25 -05:00
player -> GetSession () -> SendBattlegGroundList ( GetGUID (), bgTypeId );
break ;
}
default :
OnPoiSelect ( player , gossip );
break ;
}
}
void Creature :: OnPoiSelect ( Player * player , GossipOption const * gossip )
{
if ( gossip -> GossipId == GOSSIP_GUARD_SPELLTRAINER || gossip -> GossipId == GOSSIP_GUARD_SKILLTRAINER )
{
2009-02-19 18:57:32 -06:00
Poi_Icon icon = ICON_POI_BLANK ;
2008-10-11 14:16:25 -05:00
//need add more case.
switch ( gossip -> Action )
{
case GOSSIP_GUARD_BANK :
2009-02-19 18:57:32 -06:00
icon = ICON_POI_SMALL_HOUSE ;
2008-10-11 14:16:25 -05:00
break ;
case GOSSIP_GUARD_RIDE :
icon = ICON_POI_RWHORSE ;
break ;
case GOSSIP_GUARD_GUILD :
icon = ICON_POI_BLUETOWER ;
break ;
default :
2009-02-19 18:57:32 -06:00
icon = ICON_POI_GREYTOWER ;
2008-10-11 14:16:25 -05:00
break ;
}
2008-12-12 11:21:28 -06:00
uint32 textid = GetGossipTextId ( gossip -> Action , GetZoneId () );
player -> PlayerTalkClass -> SendTalking ( textid );
// std::string areaname= gossip->OptionText;
2008-10-11 14:16:25 -05:00
// how this could worked player->PlayerTalkClass->SendPointOfInterest( x, y, icon, 2, 15, areaname.c_str() );
}
}
uint32 Creature :: GetGossipTextId ( uint32 action , uint32 zoneid )
{
QueryResult * result = WorldDatabase . PQuery ( "SELECT textid FROM npc_gossip_textid WHERE action = '%u' AND zoneid ='%u'" , action , zoneid );
if ( ! result )
return 0 ;
Field * fields = result -> Fetch ();
uint32 id = fields [ 0 ]. GetUInt32 ();
delete result ;
return id ;
}
uint32 Creature :: GetNpcTextId ()
{
2008-10-14 11:57:03 -05:00
// don't cache / use cache in case it's a world event announcer
if ( GetCreatureInfo () -> flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT )
if ( uint32 textid = gameeventmgr . GetNpcTextId ( m_DBTableGuid ))
return textid ;
2008-10-11 14:16:25 -05:00
if ( ! m_DBTableGuid )
return DEFAULT_GOSSIP_MESSAGE ;
if ( uint32 pos = objmgr . GetNpcGossip ( m_DBTableGuid ))
return pos ;
return DEFAULT_GOSSIP_MESSAGE ;
}
GossipOption const * Creature :: GetGossipOption ( uint32 id ) const
{
2008-12-08 16:34:03 -06:00
for ( GossipOptionList :: const_iterator i = m_goptions . begin ( ); i != m_goptions . end ( ); ++ i )
2008-10-11 14:16:25 -05:00
{
if ( i -> Action == id )
return &* i ;
}
return NULL ;
}
2008-10-14 11:57:03 -05:00
void Creature :: ResetGossipOptions ()
{
m_gossipOptionLoaded = false ;
m_goptions . clear ();
}
2008-10-11 14:16:25 -05:00
void Creature :: LoadGossipOptions ()
{
if ( m_gossipOptionLoaded )
return ;
uint32 npcflags = GetUInt32Value ( UNIT_NPC_FLAGS );
2008-11-05 09:22:56 -06:00
CacheNpcOptionList const & noList = objmgr . GetNpcOptions ();
for ( CacheNpcOptionList :: const_iterator i = noList . begin (); i != noList . end (); ++ i )
if ( i -> NpcFlag & npcflags )
addGossipOption ( * i );
2008-10-11 14:16:25 -05:00
m_gossipOptionLoaded = true ;
}
void Creature :: AI_SendMoveToPacket ( float x , float y , float z , uint32 time , uint32 MovementFlags , uint8 type )
{
/* uint32 timeElap = getMSTime();
if ((timeElap - m_startMove) < m_moveTime)
{
oX = (dX - oX) * ( (timeElap - m_startMove) / m_moveTime );
oY = (dY - oY) * ( (timeElap - m_startMove) / m_moveTime );
}
else
{
oX = dX;
oY = dY;
}
dX = x;
dY = y;
m_orientation = atan2((oY - dY), (oX - dX));
m_startMove = getMSTime();
m_moveTime = time;*/
2009-02-23 13:14:04 -06:00
SendMonsterMove ( x , y , z , time );
2008-10-11 14:16:25 -05:00
}
Player * Creature :: GetLootRecipient () const
{
if ( ! m_lootRecipient ) return NULL ;
else return ObjectAccessor :: FindPlayer ( m_lootRecipient );
}
void Creature :: SetLootRecipient ( Unit * unit )
{
// set the player whose group should receive the right
// to loot the creature after it dies
// should be set to NULL after the loot disappears
if ( ! unit )
{
m_lootRecipient = 0 ;
2009-01-08 14:41:01 -06:00
RemoveFlag ( UNIT_DYNAMIC_FLAGS , UNIT_DYNFLAG_LOOTABLE );
2008-10-11 14:16:25 -05:00
RemoveFlag ( UNIT_DYNAMIC_FLAGS , UNIT_DYNFLAG_OTHER_TAGGER );
return ;
}
Player * player = unit -> GetCharmerOrOwnerPlayerOrPlayerItself ();
if ( ! player ) // normal creature, no player involved
return ;
m_lootRecipient = player -> GetGUID ();
SetFlag ( UNIT_DYNAMIC_FLAGS , UNIT_DYNFLAG_OTHER_TAGGER );
}
void Creature :: SaveToDB ()
{
// this should only be used when the creature has already been loaded
2008-11-14 16:28:45 -06:00
// preferably after adding to map, because mapid may not be valid otherwise
2008-10-11 14:16:25 -05:00
CreatureData const * data = objmgr . GetCreatureData ( m_DBTableGuid );
if ( ! data )
{
sLog . outError ( "Creature::SaveToDB failed, cannot get creature data!" );
return ;
}
2009-01-31 16:38:50 -06:00
SaveToDB ( GetMapId (), data -> spawnMask , GetPhaseMask ());
2008-10-11 14:16:25 -05:00
}
2009-01-31 16:38:50 -06:00
void Creature :: SaveToDB ( uint32 mapid , uint8 spawnMask , uint32 phaseMask )
2008-10-11 14:16:25 -05:00
{
// update in loaded data
if ( ! m_DBTableGuid )
m_DBTableGuid = GetGUIDLow ();
CreatureData & data = objmgr . NewOrExistCreatureData ( m_DBTableGuid );
uint32 displayId = GetNativeDisplayId ();
// check if it's a custom model and if not, use 0 for displayId
CreatureInfo const * cinfo = GetCreatureInfo ();
2009-06-15 18:02:12 -05:00
if ( cinfo )
2008-10-11 14:16:25 -05:00
{
2009-06-15 18:02:12 -05:00
if ( displayId != cinfo -> DisplayID_A [ 0 ] && displayId != cinfo -> DisplayID_A [ 1 ] &&
displayId != cinfo -> DisplayID_H [ 0 ] && displayId != cinfo -> DisplayID_H [ 1 ])
{
if ( cinfo -> DisplayID_A [ 0 ])
if ( CreatureModelInfo const * minfo = objmgr . GetCreatureModelInfo ( cinfo -> DisplayID_A [ 0 ]))
if ( displayId == minfo -> modelid_other_gender )
displayId = 0 ;
if ( displayId && cinfo -> DisplayID_A [ 1 ])
if ( CreatureModelInfo const * minfo = objmgr . GetCreatureModelInfo ( cinfo -> DisplayID_A [ 1 ]))
if ( displayId == minfo -> modelid_other_gender )
displayId = 0 ;
if ( displayId && cinfo -> DisplayID_H [ 0 ])
if ( CreatureModelInfo const * minfo = objmgr . GetCreatureModelInfo ( cinfo -> DisplayID_H [ 0 ]))
if ( displayId == minfo -> modelid_other_gender )
displayId = 0 ;
if ( displayId && cinfo -> DisplayID_H [ 1 ])
if ( CreatureModelInfo const * minfo = objmgr . GetCreatureModelInfo ( cinfo -> DisplayID_H [ 1 ]))
if ( displayId == minfo -> modelid_other_gender )
displayId = 0 ;
}
else
displayId = 0 ;
2008-10-11 14:16:25 -05:00
}
// data->guid = guid don't must be update at save
data . id = GetEntry ();
data . mapid = mapid ;
2009-01-31 16:38:50 -06:00
data . phaseMask = phaseMask ;
2008-10-11 14:16:25 -05:00
data . displayid = displayId ;
data . equipmentId = GetEquipmentId ();
data . posX = GetPositionX ();
data . posY = GetPositionY ();
data . posZ = GetPositionZ ();
data . orientation = GetOrientation ();
data . spawntimesecs = m_respawnDelay ;
// prevent add data integrity problems
data . spawndist = GetDefaultMovementType () == IDLE_MOTION_TYPE ? 0 : m_respawnradius ;
data . currentwaypoint = 0 ;
data . curhealth = GetHealth ();
data . curmana = GetPower ( POWER_MANA );
data . is_dead = m_isDeadByDefault ;
// prevent add data integrity problems
data . movementType = ! m_respawnradius && GetDefaultMovementType () == RANDOM_MOTION_TYPE
? IDLE_MOTION_TYPE : GetDefaultMovementType ();
data . spawnMask = spawnMask ;
// updated in DB
WorldDatabase . BeginTransaction ();
WorldDatabase . PExecuteLog ( "DELETE FROM creature WHERE guid = '%u'" , m_DBTableGuid );
std :: ostringstream ss ;
ss << "INSERT INTO creature VALUES ("
<< m_DBTableGuid << ","
<< GetEntry () << ","
<< mapid << ","
2009-02-27 12:26:40 -06:00
<< uint32 ( spawnMask ) << "," // cast to prevent save as symbol
<< uint16 ( GetPhaseMask ()) << "," // prevent out of range error
2008-10-11 14:16:25 -05:00
<< displayId << ","
<< GetEquipmentId () << ","
<< GetPositionX () << ","
<< GetPositionY () << ","
<< GetPositionZ () << ","
<< GetOrientation () << ","
<< m_respawnDelay << "," //respawn time
<< ( float ) m_respawnradius << "," //spawn distance (float)
<< ( uint32 ) ( 0 ) << "," //currentwaypoint
<< GetHealth () << "," //curhealth
<< GetPower ( POWER_MANA ) << "," //curmana
<< ( m_isDeadByDefault ? 1 : 0 ) << "," //is_dead
<< GetDefaultMovementType () << ")" ; //default movement generator type
WorldDatabase . PExecuteLog ( ss . str ( ). c_str ( ) );
WorldDatabase . CommitTransaction ();
}
void Creature :: SelectLevel ( const CreatureInfo * cinfo )
{
uint32 rank = isPet () ? 0 : cinfo -> rank ;
// level
uint32 minlevel = std :: min ( cinfo -> maxlevel , cinfo -> minlevel );
uint32 maxlevel = std :: max ( cinfo -> maxlevel , cinfo -> minlevel );
uint32 level = minlevel == maxlevel ? minlevel : urand ( minlevel , maxlevel );
SetLevel ( level );
float rellevel = maxlevel == minlevel ? 0 : ( float ( level - minlevel )) / ( maxlevel - minlevel );
// health
float healthmod = _GetHealthMod ( rank );
uint32 minhealth = std :: min ( cinfo -> maxhealth , cinfo -> minhealth );
uint32 maxhealth = std :: max ( cinfo -> maxhealth , cinfo -> minhealth );
uint32 health = uint32 ( healthmod * ( minhealth + uint32 ( rellevel * ( maxhealth - minhealth ))));
SetCreateHealth ( health );
SetMaxHealth ( health );
SetHealth ( health );
2009-06-11 17:54:59 +02:00
ResetPlayerDamageReq ();
2008-10-11 14:16:25 -05:00
// mana
uint32 minmana = std :: min ( cinfo -> maxmana , cinfo -> minmana );
uint32 maxmana = std :: max ( cinfo -> maxmana , cinfo -> minmana );
uint32 mana = minmana + uint32 ( rellevel * ( maxmana - minmana ));
SetCreateMana ( mana );
SetMaxPower ( POWER_MANA , mana ); //MAX Mana
SetPower ( POWER_MANA , mana );
2009-05-29 21:24:23 -05:00
// TODO: set UNIT_FIELD_POWER*, for some creature class case (energy, etc)
2008-10-11 14:16:25 -05:00
SetModifierValue ( UNIT_MOD_HEALTH , BASE_VALUE , health );
SetModifierValue ( UNIT_MOD_MANA , BASE_VALUE , mana );
2009-06-22 23:12:53 +02:00
//damage
2009-08-06 10:38:46 -05:00
float damagemod = 1.0f ; //_GetDamageMod(rank);
2009-06-22 23:12:53 +02:00
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , cinfo -> mindmg * damagemod );
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , cinfo -> maxdmg * damagemod );
SetFloatValue ( UNIT_FIELD_MINRANGEDDAMAGE , cinfo -> minrangedmg * damagemod );
SetFloatValue ( UNIT_FIELD_MAXRANGEDDAMAGE , cinfo -> maxrangedmg * damagemod );
SetModifierValue ( UNIT_MOD_ATTACK_POWER , BASE_VALUE , cinfo -> attackpower * damagemod );
2008-10-11 14:16:25 -05:00
}
float Creature :: _GetHealthMod ( int32 Rank )
{
switch ( Rank ) // define rates for each elite rank
{
case CREATURE_ELITE_NORMAL :
return sWorld . getRate ( RATE_CREATURE_NORMAL_HP );
case CREATURE_ELITE_ELITE :
return sWorld . getRate ( RATE_CREATURE_ELITE_ELITE_HP );
case CREATURE_ELITE_RAREELITE :
return sWorld . getRate ( RATE_CREATURE_ELITE_RAREELITE_HP );
case CREATURE_ELITE_WORLDBOSS :
return sWorld . getRate ( RATE_CREATURE_ELITE_WORLDBOSS_HP );
case CREATURE_ELITE_RARE :
return sWorld . getRate ( RATE_CREATURE_ELITE_RARE_HP );
default :
return sWorld . getRate ( RATE_CREATURE_ELITE_ELITE_HP );
}
}
float Creature :: _GetDamageMod ( int32 Rank )
{
switch ( Rank ) // define rates for each elite rank
{
case CREATURE_ELITE_NORMAL :
return sWorld . getRate ( RATE_CREATURE_NORMAL_DAMAGE );
case CREATURE_ELITE_ELITE :
return sWorld . getRate ( RATE_CREATURE_ELITE_ELITE_DAMAGE );
case CREATURE_ELITE_RAREELITE :
return sWorld . getRate ( RATE_CREATURE_ELITE_RAREELITE_DAMAGE );
case CREATURE_ELITE_WORLDBOSS :
return sWorld . getRate ( RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE );
case CREATURE_ELITE_RARE :
return sWorld . getRate ( RATE_CREATURE_ELITE_RARE_DAMAGE );
default :
return sWorld . getRate ( RATE_CREATURE_ELITE_ELITE_DAMAGE );
}
}
float Creature :: GetSpellDamageMod ( int32 Rank )
{
switch ( Rank ) // define rates for each elite rank
{
case CREATURE_ELITE_NORMAL :
return sWorld . getRate ( RATE_CREATURE_NORMAL_SPELLDAMAGE );
case CREATURE_ELITE_ELITE :
return sWorld . getRate ( RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE );
case CREATURE_ELITE_RAREELITE :
return sWorld . getRate ( RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE );
case CREATURE_ELITE_WORLDBOSS :
return sWorld . getRate ( RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE );
case CREATURE_ELITE_RARE :
return sWorld . getRate ( RATE_CREATURE_ELITE_RARE_SPELLDAMAGE );
default :
return sWorld . getRate ( RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE );
}
}
2009-08-23 22:09:43 -05:00
bool Creature :: CreateFromProto ( uint32 guidlow , uint32 Entry , uint32 vehId , uint32 team , const CreatureData * data )
2008-10-11 14:16:25 -05:00
{
2009-05-30 22:15:05 -05:00
SetZoneScript ();
2009-06-03 21:47:38 -05:00
if ( m_zoneScript && data )
2009-05-30 22:15:05 -05:00
{
2009-06-03 21:47:38 -05:00
Entry = m_zoneScript -> GetCreatureEntry ( guidlow , data );
2009-05-30 22:15:05 -05:00
if ( ! Entry )
return false ;
}
2008-10-11 14:16:25 -05:00
CreatureInfo const * cinfo = objmgr . GetCreatureTemplate ( Entry );
if ( ! cinfo )
{
2009-03-21 14:28:02 -06:00
sLog . outErrorDb ( "Creature entry %u does not exist." , Entry );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-05-30 22:15:05 -05:00
2009-05-31 22:45:32 -05:00
SetOriginalEntry ( Entry );
2008-10-11 14:16:25 -05:00
2009-08-23 22:09:43 -05:00
if ( ! vehId )
vehId = cinfo -> VehicleId ;
if ( vehId && ! CreateVehicleKit ( vehId ))
vehId = 0 ;
Object :: _Create ( guidlow , Entry , vehId ? HIGHGUID_VEHICLE : HIGHGUID_UNIT );
2008-10-11 14:16:25 -05:00
if ( ! UpdateEntry ( Entry , team , data ))
return false ;
return true ;
}
bool Creature :: LoadFromDB ( uint32 guid , Map * map )
{
CreatureData const * data = objmgr . GetCreatureData ( guid );
if ( ! data )
{
sLog . outErrorDb ( "Creature (GUID: %u) not found in table `creature`, can't load. " , guid );
return false ;
}
m_DBTableGuid = guid ;
if ( map -> GetInstanceId () != 0 ) guid = objmgr . GenerateLowGuid ( HIGHGUID_UNIT );
uint16 team = 0 ;
2009-08-23 22:09:43 -05:00
if ( ! Create ( guid , map , data -> phaseMask , data -> id , team , 0 , data -> posX , data -> posY , data -> posZ , data -> orientation , data ))
2008-10-11 14:16:25 -05:00
return false ;
2009-02-09 08:16:34 -05:00
//We should set first home position, because then AI calls home movement
SetHomePosition ( data -> posX , data -> posY , data -> posZ , data -> orientation );
2009-02-04 10:33:18 -06:00
2009-02-09 08:16:34 -05:00
m_respawnradius = data -> spawndist ;
2008-10-11 14:16:25 -05:00
m_respawnDelay = data -> spawntimesecs ;
m_isDeadByDefault = data -> is_dead ;
m_deathState = m_isDeadByDefault ? DEAD : ALIVE ;
m_respawnTime = objmgr . GetCreatureRespawnTime ( m_DBTableGuid , GetInstanceId ());
2009-04-14 14:33:48 +02:00
if ( m_respawnTime ) // respawn on Update
2008-12-08 16:34:03 -06:00
{
2008-10-11 14:16:25 -05:00
m_deathState = DEAD ;
2008-12-08 16:34:03 -06:00
if ( canFly ())
{
float tz = GetMap () -> GetHeight ( data -> posX , data -> posY , data -> posZ , false );
if ( data -> posZ - tz > 0.1 )
Relocate ( data -> posX , data -> posY , tz );
}
}
2008-10-11 14:16:25 -05:00
uint32 curhealth = data -> curhealth ;
if ( curhealth )
{
curhealth = uint32 ( curhealth * _GetHealthMod ( GetCreatureInfo () -> rank ));
if ( curhealth < 1 )
curhealth = 1 ;
}
SetHealth ( m_deathState == ALIVE ? curhealth : 0 );
SetPower ( POWER_MANA , data -> curmana );
// checked at creature_template loading
m_defaultMovementType = MovementGeneratorType ( data -> movementType );
2009-05-31 21:04:04 -05:00
m_creatureData = data ;
2009-05-31 14:26:57 -05:00
2008-10-11 14:16:25 -05:00
return true ;
}
void Creature :: LoadEquipment ( uint32 equip_entry , bool force )
{
if ( equip_entry == 0 )
{
if ( force )
{
2009-04-29 00:26:07 -05:00
for ( uint8 i = 0 ; i < 3 ; ++ i )
2008-12-24 09:58:26 -06:00
SetUInt32Value ( UNIT_VIRTUAL_ITEM_SLOT_ID + i , 0 );
2008-10-11 14:16:25 -05:00
m_equipmentId = 0 ;
}
return ;
}
EquipmentInfo const * einfo = objmgr . GetEquipmentInfo ( equip_entry );
if ( ! einfo )
return ;
m_equipmentId = equip_entry ;
2009-04-29 00:26:07 -05:00
for ( uint8 i = 0 ; i < 3 ; ++ i )
2008-12-24 09:58:26 -06:00
SetUInt32Value ( UNIT_VIRTUAL_ITEM_SLOT_ID + i , einfo -> equipentry [ i ]);
2008-10-11 14:16:25 -05:00
}
bool Creature :: hasQuest ( uint32 quest_id ) const
{
QuestRelations const & qr = objmgr . mCreatureQuestRelations ;
for ( QuestRelations :: const_iterator itr = qr . lower_bound ( GetEntry ()); itr != qr . upper_bound ( GetEntry ()); ++ itr )
{
if ( itr -> second == quest_id )
return true ;
}
return false ;
}
bool Creature :: hasInvolvedQuest ( uint32 quest_id ) const
{
QuestRelations const & qr = objmgr . mCreatureQuestInvolvedRelations ;
for ( QuestRelations :: const_iterator itr = qr . lower_bound ( GetEntry ()); itr != qr . upper_bound ( GetEntry ()); ++ itr )
{
if ( itr -> second == quest_id )
return true ;
}
return false ;
}
void Creature :: DeleteFromDB ()
{
if ( ! m_DBTableGuid )
{
sLog . outDebug ( "Trying to delete not saved creature!" );
return ;
}
objmgr . SaveCreatureRespawnTime ( m_DBTableGuid , GetInstanceId (), 0 );
objmgr . DeleteCreatureData ( m_DBTableGuid );
WorldDatabase . BeginTransaction ();
WorldDatabase . PExecuteLog ( "DELETE FROM creature WHERE guid = '%u'" , m_DBTableGuid );
WorldDatabase . PExecuteLog ( "DELETE FROM creature_addon WHERE guid = '%u'" , m_DBTableGuid );
WorldDatabase . PExecuteLog ( "DELETE FROM game_event_creature WHERE guid = '%u'" , m_DBTableGuid );
WorldDatabase . PExecuteLog ( "DELETE FROM game_event_model_equip WHERE guid = '%u'" , m_DBTableGuid );
WorldDatabase . CommitTransaction ();
}
2008-12-06 14:01:44 -06:00
bool Creature :: canSeeOrDetect ( Unit const * u , bool detect , bool inVisibleList , bool is3dDistance ) const
2008-11-06 10:27:58 -06:00
{
// not in world
if ( ! IsInWorld () || ! u -> IsInWorld ())
return false ;
// all dead creatures/players not visible for any creatures
if ( ! u -> isAlive () || ! isAlive ())
return false ;
// Always can see self
if ( u == this )
return true ;
2009-01-31 16:38:50 -06:00
// phased visibility (both must phased in same way)
if ( ! InSamePhase ( u ))
return false ;
2008-11-06 10:27:58 -06:00
// always seen by owner
if ( GetGUID () == u -> GetCharmerOrOwnerGUID ())
return true ;
if ( u -> GetVisibility () == VISIBILITY_OFF ) //GM
return false ;
// invisible aura
if (( m_invisibilityMask || u -> m_invisibilityMask ) && ! canDetectInvisibilityOf ( u ))
return false ;
// unit got in stealth in this moment and must ignore old detected state
//if (m_Visibility == VISIBILITY_GROUP_NO_DETECT)
// return false;
// GM invisibility checks early, invisibility if any detectable, so if not stealth then visible
if ( u -> GetVisibility () == VISIBILITY_GROUP_STEALTH )
{
//do not know what is the use of this detect
if ( ! detect || ! canDetectStealthOf ( u , GetDistance ( u )))
return false ;
}
// Now check is target visible with LoS
2008-11-08 09:03:56 -06:00
//return u->IsWithinLOS(GetPositionX(),GetPositionY(),GetPositionZ());
return true ;
2008-11-06 10:27:58 -06:00
}
2009-06-09 19:55:15 -05:00
bool Creature :: canStartAttack ( Unit const * who , bool force ) const
2008-11-21 10:07:11 -06:00
{
2009-08-15 14:30:14 -05:00
if ( isCivilian ())
2009-06-15 12:02:55 -05:00
return false ;
2009-08-23 16:27:00 -05:00
if ( ! canFly () && ( GetDistanceZ ( who ) > CREATURE_Z_ATTACK_RANGE + m_CombatDistance ))
2009-06-15 12:02:55 -05:00
//|| who->IsControlledByPlayer() && who->IsFlying()))
// we cannot check flying for other creatures, too much map/vmap calculation
// TODO: should switch to range attack
2008-11-21 10:07:11 -06:00
return false ;
2009-08-23 16:27:00 -05:00
if ( ! force && ( IsNeutralToAll () || ! IsWithinDistInMap ( who , GetAttackDistance ( who ) + m_CombatDistance )))
2009-06-09 19:55:15 -05:00
return false ;
2009-08-15 14:30:14 -05:00
if ( ! canCreatureAttack ( who , force ))
2008-11-21 10:07:11 -06:00
return false ;
return IsWithinLOSInMap ( who );
}
2008-10-11 14:16:25 -05:00
float Creature :: GetAttackDistance ( Unit const * pl ) const
{
float aggroRate = sWorld . getRate ( RATE_CREATURE_AGGRO );
if ( aggroRate == 0 )
return 0.0f ;
int32 playerlevel = pl -> getLevelForTarget ( this );
int32 creaturelevel = getLevelForTarget ( pl );
int32 leveldif = playerlevel - creaturelevel ;
// "The maximum Aggro Radius has a cap of 25 levels under. Example: A level 30 char has the same Aggro Radius of a level 5 char on a level 60 mob."
if ( leveldif < - 25 )
leveldif = - 25 ;
// "The aggro radius of a mob having the same level as the player is roughly 20 yards"
float RetDistance = 20 ;
2008-11-14 16:28:45 -06:00
// "Aggro Radius varies with level difference at a rate of roughly 1 yard/level"
2008-10-11 14:16:25 -05:00
// radius grow if playlevel < creaturelevel
RetDistance -= ( float ) leveldif ;
if ( creaturelevel + 5 <= sWorld . getConfig ( CONFIG_MAX_PLAYER_LEVEL ))
{
// detect range auras
RetDistance += GetTotalAuraModifier ( SPELL_AURA_MOD_DETECT_RANGE );
// detected range auras
RetDistance += pl -> GetTotalAuraModifier ( SPELL_AURA_MOD_DETECTED_RANGE );
}
// "Minimum Aggro Radius for a mob seems to be combat range (5 yards)"
if ( RetDistance < 5 )
RetDistance = 5 ;
return ( RetDistance * aggroRate );
}
void Creature :: setDeathState ( DeathState s )
{
if (( s == JUST_DIED && ! m_isDeadByDefault ) || ( s == JUST_ALIVED && m_isDeadByDefault ))
{
2009-03-02 16:53:50 -06:00
m_deathTimer = m_corpseDelay * IN_MILISECONDS ;
2008-10-11 14:16:25 -05:00
// always save boss respawn time at death to prevent crash cheating
2008-11-14 16:28:45 -06:00
if ( sWorld . getConfig ( CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY ) || isWorldBoss ())
2008-10-11 14:16:25 -05:00
SaveRespawnTime ();
}
Unit :: setDeathState ( s );
if ( s == JUST_DIED )
{
2009-06-03 19:29:55 -05:00
SetUInt64Value ( UNIT_FIELD_TARGET , 0 ); // remove target selection in any cases (can be set at aura remove in Unit::setDeathState)
SetUInt32Value ( UNIT_NPC_FLAGS , UNIT_NPC_FLAG_NONE );
setActive ( false );
2008-10-11 14:16:25 -05:00
if ( ! isPet () && GetCreatureInfo () -> SkinLootId )
if ( LootTemplates_Skinning . HaveLootFor ( GetCreatureInfo () -> SkinLootId ) )
SetFlag ( UNIT_FIELD_FLAGS , UNIT_FLAG_SKINNABLE );
2009-05-21 10:48:00 -05:00
SetNoSearchAssistance ( false );
2009-04-06 13:27:42 -05:00
2009-04-07 10:51:45 +02:00
//Dismiss group if is leader
if ( m_formation && m_formation -> getLeader () == this )
m_formation -> FormationReset ( true );
2009-08-26 19:28:21 -05:00
if ( canFly () && FallGround ())
return ;
Unit :: setDeathState ( CORPSE );
2009-04-07 10:51:45 +02:00
}
2009-08-26 19:28:21 -05:00
else if ( s == JUST_ALIVED )
2008-10-11 14:16:25 -05:00
{
2009-02-25 17:53:05 -06:00
//if(isPet())
// setActive(true);
2008-10-11 14:16:25 -05:00
SetHealth ( GetMaxHealth ());
SetLootRecipient ( NULL );
2009-06-11 17:54:59 +02:00
ResetPlayerDamageReq ();
2008-10-11 14:16:25 -05:00
CreatureInfo const * cinfo = GetCreatureInfo ();
AddUnitMovementFlag ( MOVEMENTFLAG_WALK_MODE );
SetUInt32Value ( UNIT_NPC_FLAGS , cinfo -> npcflag );
clearUnitState ( UNIT_STAT_ALL_STATE );
SetMeleeDamageSchool ( SpellSchools ( cinfo -> dmgschool ));
LoadCreaturesAddon ( true );
2009-04-06 20:33:52 -05:00
Motion_Initialize ();
2009-06-03 11:07:01 -05:00
if ( GetCreatureData () && GetPhaseMask () != GetCreatureData () -> phaseMask )
2009-06-17 01:01:39 -05:00
SetPhaseMask ( GetCreatureData () -> phaseMask , false );
2009-08-25 00:56:23 -05:00
Unit :: setDeathState ( ALIVE );
2009-04-07 10:51:45 +02:00
}
2008-10-11 14:16:25 -05:00
}
2008-12-08 16:34:03 -06:00
bool Creature :: FallGround ()
{
// Let's abort after we called this function one time
if ( getDeathState () == DEAD_FALLING )
return false ;
2009-02-09 08:16:34 -05:00
float x , y , z ;
GetPosition ( x , y , z );
float ground_Z = GetMap () -> GetVmapHeight ( x , y , z , true );
2009-01-16 19:05:30 -06:00
if ( fabs ( ground_Z - z ) < 0.1f )
2008-12-08 16:34:03 -06:00
return false ;
2009-08-26 22:56:20 -05:00
RemoveUnitMovementFlag ( MOVEMENTFLAG_WALK_MODE );
2009-08-26 19:28:21 -05:00
GetMotionMaster () -> MovePoint ( EVENT_FALL_GROUND , x , y , ground_Z );
2008-12-08 16:34:03 -06:00
Unit :: setDeathState ( DEAD_FALLING );
return true ;
}
2009-06-17 01:01:39 -05:00
void Creature :: Respawn ( bool force )
2008-10-11 14:16:25 -05:00
{
2009-06-17 01:01:39 -05:00
DestroyForNearbyPlayers ();
2008-10-11 14:16:25 -05:00
2009-06-17 01:01:39 -05:00
if ( force )
{
if ( isAlive ())
setDeathState ( JUST_DIED );
else if ( getDeathState () != CORPSE )
setDeathState ( CORPSE );
}
RemoveCorpse ();
2008-10-11 14:16:25 -05:00
if ( getDeathState () == DEAD )
{
if ( m_DBTableGuid )
objmgr . SaveCreatureRespawnTime ( m_DBTableGuid , GetInstanceId (), 0 );
2009-04-14 14:33:48 +02:00
DEBUG_LOG ( "Respawning..." );
m_respawnTime = 0 ;
lootForPickPocketed = false ;
lootForBody = false ;
if ( m_originalEntry != GetEntry ())
UpdateEntry ( m_originalEntry );
CreatureInfo const * cinfo = GetCreatureInfo ();
SelectLevel ( cinfo );
if ( m_isDeadByDefault )
{
setDeathState ( JUST_DIED );
SetHealth ( 0 );
i_motionMaster . Clear ();
clearUnitState ( UNIT_STAT_ALL_STATE );
LoadCreaturesAddon ( true );
}
else
setDeathState ( JUST_ALIVED );
//Call AI respawn virtual function
AI () -> JustRespawned ();
2009-04-15 17:06:38 -05:00
uint16 poolid = poolhandler . IsPartOfAPool ( GetGUIDLow (), GetTypeId ());
if ( poolid )
poolhandler . UpdatePool ( poolid , GetGUIDLow (), GetTypeId ());
2008-10-11 14:16:25 -05:00
}
2009-06-17 01:01:39 -05:00
SetToNotify ();
2008-10-11 14:16:25 -05:00
}
2009-06-18 17:05:01 -05:00
void Creature :: ForcedDespawn ()
{
setDeathState ( JUST_DIED );
RemoveCorpse ();
SetHealth ( 0 ); // just for nice GM-mode view
}
2009-01-04 16:45:32 -06:00
bool Creature :: IsImmunedToSpell ( SpellEntry const * spellInfo )
2008-10-11 14:16:25 -05:00
{
if ( ! spellInfo )
return false ;
if ( GetCreatureInfo () -> MechanicImmuneMask & ( 1 << ( spellInfo -> Mechanic - 1 )))
return true ;
2009-01-04 16:45:32 -06:00
return Unit :: IsImmunedToSpell ( spellInfo );
2008-10-11 14:16:25 -05:00
}
2009-01-18 19:01:41 -06:00
bool Creature :: IsImmunedToSpellEffect ( SpellEntry const * spellInfo , uint32 index ) const
2008-10-11 14:16:25 -05:00
{
2009-01-18 19:01:41 -06:00
if ( GetCreatureInfo () -> MechanicImmuneMask & ( 1 << ( spellInfo -> EffectMechanic [ index ] - 1 )))
2008-10-11 14:16:25 -05:00
return true ;
2009-01-18 19:01:41 -06:00
return Unit :: IsImmunedToSpellEffect ( spellInfo , index );
2008-10-11 14:16:25 -05:00
}
SpellEntry const * Creature :: reachWithSpellAttack ( Unit * pVictim )
{
if ( ! pVictim )
return NULL ;
2009-04-29 00:26:07 -05:00
for ( uint32 i = 0 ; i < CREATURE_MAX_SPELLS ; ++ i )
2008-10-11 14:16:25 -05:00
{
if ( ! m_spells [ i ])
continue ;
SpellEntry const * spellInfo = sSpellStore . LookupEntry ( m_spells [ i ] );
if ( ! spellInfo )
{
2009-03-09 17:56:51 -06:00
sLog . outError ( "WORLD: unknown spell id %i" , m_spells [ i ]);
2008-10-11 14:16:25 -05:00
continue ;
}
bool bcontinue = true ;
for ( uint32 j = 0 ; j < 3 ; j ++ )
{
if ( ( spellInfo -> Effect [ j ] == SPELL_EFFECT_SCHOOL_DAMAGE ) ||
( spellInfo -> Effect [ j ] == SPELL_EFFECT_INSTAKILL ) ||
( spellInfo -> Effect [ j ] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE ) ||
( spellInfo -> Effect [ j ] == SPELL_EFFECT_HEALTH_LEECH )
)
{
bcontinue = false ;
break ;
}
}
if ( bcontinue ) continue ;
if ( spellInfo -> manaCost > GetPower ( POWER_MANA ))
continue ;
SpellRangeEntry const * srange = sSpellRangeStore . LookupEntry ( spellInfo -> rangeIndex );
2009-02-14 15:56:04 +01:00
float range = GetSpellMaxRangeForHostile ( srange );
float minrange = GetSpellMinRangeForHostile ( srange );
2008-10-11 14:16:25 -05:00
float dist = GetDistance ( pVictim );
//if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
// continue;
if ( dist > range || dist < minrange )
continue ;
2009-01-07 11:56:57 -06:00
if ( spellInfo -> PreventionType == SPELL_PREVENTION_TYPE_SILENCE && HasFlag ( UNIT_FIELD_FLAGS , UNIT_FLAG_SILENCED ))
continue ;
if ( spellInfo -> PreventionType == SPELL_PREVENTION_TYPE_PACIFY && HasFlag ( UNIT_FIELD_FLAGS , UNIT_FLAG_PACIFIED ))
2008-10-11 14:16:25 -05:00
continue ;
return spellInfo ;
}
return NULL ;
}
SpellEntry const * Creature :: reachWithSpellCure ( Unit * pVictim )
{
if ( ! pVictim )
return NULL ;
2009-04-29 00:26:07 -05:00
for ( uint32 i = 0 ; i < CREATURE_MAX_SPELLS ; ++ i )
2008-10-11 14:16:25 -05:00
{
if ( ! m_spells [ i ])
continue ;
SpellEntry const * spellInfo = sSpellStore . LookupEntry ( m_spells [ i ] );
if ( ! spellInfo )
{
2009-03-09 17:56:51 -06:00
sLog . outError ( "WORLD: unknown spell id %i" , m_spells [ i ]);
2008-10-11 14:16:25 -05:00
continue ;
}
bool bcontinue = true ;
for ( uint32 j = 0 ; j < 3 ; j ++ )
{
if ( ( spellInfo -> Effect [ j ] == SPELL_EFFECT_HEAL ) )
{
bcontinue = false ;
break ;
}
}
if ( bcontinue ) continue ;
if ( spellInfo -> manaCost > GetPower ( POWER_MANA ))
continue ;
SpellRangeEntry const * srange = sSpellRangeStore . LookupEntry ( spellInfo -> rangeIndex );
2009-02-14 15:56:04 +01:00
float range = GetSpellMaxRangeForFriend ( srange );
float minrange = GetSpellMinRangeForFriend ( srange );
2008-10-11 14:16:25 -05:00
float dist = GetDistance ( pVictim );
//if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
// continue;
if ( dist > range || dist < minrange )
continue ;
2009-01-07 11:56:57 -06:00
if ( spellInfo -> PreventionType == SPELL_PREVENTION_TYPE_SILENCE && HasFlag ( UNIT_FIELD_FLAGS , UNIT_FLAG_SILENCED ))
continue ;
if ( spellInfo -> PreventionType == SPELL_PREVENTION_TYPE_PACIFY && HasFlag ( UNIT_FIELD_FLAGS , UNIT_FLAG_PACIFIED ))
2008-10-11 14:16:25 -05:00
continue ;
return spellInfo ;
}
return NULL ;
}
2008-11-06 10:27:58 -06:00
bool Creature :: IsVisibleInGridForPlayer ( Player const * pl ) const
2008-10-11 14:16:25 -05:00
{
// gamemaster in GM mode see all, including ghosts
if ( pl -> isGameMaster ())
return true ;
// Live player (or with not release body see live creatures or death creatures with corpse disappearing time > 0
if ( pl -> isAlive () || pl -> GetDeathTimer () > 0 )
{
if ( GetEntry () == VISUAL_WAYPOINT && ! pl -> isGameMaster () )
return false ;
return isAlive () || m_deathTimer > 0 || m_isDeadByDefault && m_deathState == CORPSE ;
}
// Dead player see live creatures near own corpse
if ( isAlive ())
{
Corpse * corpse = pl -> GetCorpse ();
if ( corpse )
{
// 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level
if ( corpse -> IsWithinDistInMap ( this ,( 20 + 25 ) * sWorld . getRate ( RATE_CREATURE_AGGRO )))
return true ;
}
}
// Dead player see Spirit Healer or Spirit Guide
if ( isSpiritService ())
return true ;
// and not see any other
return false ;
}
2008-11-20 20:28:17 -06:00
Unit * Creature :: SelectNearestTarget ( float dist ) const
{
2008-11-21 10:07:11 -06:00
CellPair p ( Trinity :: ComputeCellPair ( GetPositionX (), GetPositionY ()));
2008-11-20 20:28:17 -06:00
Cell cell ( p );
cell . data . Part . reserved = ALL_DISTRICT ;
cell . SetNoCreate ();
2008-11-21 10:07:11 -06:00
Unit * target = NULL ;
2008-11-20 20:28:17 -06:00
{
Trinity :: NearestHostileUnitInAttackDistanceCheck u_check ( this , dist );
2009-01-31 16:38:50 -06:00
Trinity :: UnitLastSearcher < Trinity :: NearestHostileUnitInAttackDistanceCheck > searcher ( this , target , u_check );
2008-11-20 20:28:17 -06:00
TypeContainerVisitor < Trinity :: UnitLastSearcher < Trinity :: NearestHostileUnitInAttackDistanceCheck > , WorldTypeMapContainer > world_unit_searcher ( searcher );
TypeContainerVisitor < Trinity :: UnitLastSearcher < Trinity :: NearestHostileUnitInAttackDistanceCheck > , GridTypeMapContainer > grid_unit_searcher ( searcher );
CellLock < GridReadGuard > cell_lock ( cell , p );
2008-11-21 10:07:11 -06:00
cell_lock -> Visit ( cell_lock , world_unit_searcher , * GetMap ());
cell_lock -> Visit ( cell_lock , grid_unit_searcher , * GetMap ());
}
return target ;
2008-11-20 20:28:17 -06:00
}
2009-05-21 10:48:00 -05:00
void Creature :: CallAssistance ()
2008-10-11 14:16:25 -05:00
{
2008-11-25 16:36:50 -06:00
if ( ! m_AlreadyCallAssistance && getVictim () && ! isPet () && ! isCharmed ())
2008-10-11 14:16:25 -05:00
{
2008-11-25 16:36:50 -06:00
SetNoCallAssistance ( true );
2008-10-11 14:16:25 -05:00
2009-05-21 10:48:00 -05:00
float radius = sWorld . getConfig ( CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS );
2009-04-25 09:15:40 -05:00
2008-10-11 14:16:25 -05:00
if ( radius > 0 )
{
std :: list < Creature *> assistList ;
{
2008-10-14 11:57:03 -05:00
CellPair p ( Trinity :: ComputeCellPair ( GetPositionX (), GetPositionY ()));
2008-10-11 14:16:25 -05:00
Cell cell ( p );
cell . data . Part . reserved = ALL_DISTRICT ;
cell . SetNoCreate ();
2009-01-31 16:38:50 -06:00
MaNGOS :: AnyAssistCreatureInRangeCheck u_check ( this , getVictim (), radius );
MaNGOS :: CreatureListSearcher < MaNGOS :: AnyAssistCreatureInRangeCheck > searcher ( this , assistList , u_check );
2008-10-11 14:16:25 -05:00
2008-10-14 11:57:03 -05:00
TypeContainerVisitor < Trinity :: CreatureListSearcher < Trinity :: AnyAssistCreatureInRangeCheck > , GridTypeMapContainer > grid_creature_searcher ( searcher );
2008-10-11 14:16:25 -05:00
CellLock < GridReadGuard > cell_lock ( cell , p );
2009-07-16 11:49:00 +08:00
cell_lock -> Visit ( cell_lock , grid_creature_searcher , * GetMap ());
2008-10-11 14:16:25 -05:00
}
2008-11-25 16:36:50 -06:00
if ( ! assistList . empty ())
2008-10-11 14:16:25 -05:00
{
2008-11-25 16:36:50 -06:00
AssistDelayEvent * e = new AssistDelayEvent ( getVictim () -> GetGUID (), * this );
while ( ! assistList . empty ())
{
// Pushing guids because in delay can happen some creature gets despawned => invalid pointer
e -> AddAssistant (( * assistList . begin ()) -> GetGUID ());
assistList . pop_front ();
}
m_Events . AddEvent ( e , m_Events . CalculateTime ( sWorld . getConfig ( CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY )));
2008-10-11 14:16:25 -05:00
}
}
}
}
2009-06-04 16:10:26 -05:00
void Creature :: CallForHelp ( float fRadius )
{
if ( fRadius <= 0.0f || ! getVictim () || isPet () || isCharmed ())
return ;
CellPair p ( MaNGOS :: ComputeCellPair ( GetPositionX (), GetPositionY ()));
Cell cell ( p );
cell . data . Part . reserved = ALL_DISTRICT ;
cell . SetNoCreate ();
MaNGOS :: CallOfHelpCreatureInRangeDo u_do ( this , getVictim (), fRadius );
MaNGOS :: CreatureWorker < MaNGOS :: CallOfHelpCreatureInRangeDo > worker ( this , u_do );
TypeContainerVisitor < MaNGOS :: CreatureWorker < MaNGOS :: CallOfHelpCreatureInRangeDo > , GridTypeMapContainer > grid_creature_searcher ( worker );
CellLock < GridReadGuard > cell_lock ( cell , p );
cell_lock -> Visit ( cell_lock , grid_creature_searcher , * GetMap ());
}
2009-05-21 10:15:09 -05:00
bool Creature :: CanAssistTo ( const Unit * u , const Unit * enemy , bool checkfaction /*= true*/ ) const
2008-11-25 16:36:50 -06:00
{
2008-12-07 09:21:12 -06:00
// is it true?
if ( ! HasReactState ( REACT_AGGRESSIVE ))
2008-11-25 16:36:50 -06:00
return false ;
// we don't need help from zombies :)
2009-05-21 10:15:09 -05:00
if ( ! isAlive ())
2008-11-25 16:36:50 -06:00
return false ;
2009-07-24 09:15:52 +08:00
// we don't need help from non-combatant ;)
if ( isCivilian ())
return false ;
2008-11-25 16:36:50 -06:00
// skip fighting creature
2009-05-21 10:15:09 -05:00
if ( isInCombat ())
2008-11-25 16:36:50 -06:00
return false ;
// only free creature
2009-05-21 10:15:09 -05:00
if ( GetCharmerOrOwnerGUID ())
2008-11-25 16:36:50 -06:00
return false ;
2009-05-21 10:15:09 -05:00
// only from same creature faction
if ( checkfaction )
{
if ( getFaction () != u -> getFaction ())
return false ;
}
else
{
if ( ! IsFriendlyTo ( u ))
return false ;
}
2008-11-25 16:36:50 -06:00
// skip non hostile to caster enemy creatures
2009-05-21 10:15:09 -05:00
if ( ! IsHostileTo ( enemy ))
2008-11-25 16:36:50 -06:00
return false ;
return true ;
}
2008-10-11 14:16:25 -05:00
void Creature :: SaveRespawnTime ()
{
2009-05-31 21:04:04 -05:00
if ( isSummon () || ! m_DBTableGuid || m_creatureData && ! m_creatureData -> dbData )
2008-10-11 14:16:25 -05:00
return ;
if ( m_respawnTime > time ( NULL )) // dead (no corpse)
objmgr . SaveCreatureRespawnTime ( m_DBTableGuid , GetInstanceId (), m_respawnTime );
else if ( m_deathTimer > 0 ) // dead (corpse)
2009-03-02 16:53:50 -06:00
objmgr . SaveCreatureRespawnTime ( m_DBTableGuid , GetInstanceId (), time ( NULL ) + m_respawnDelay + m_deathTimer / IN_MILISECONDS );
2008-10-11 14:16:25 -05:00
}
2009-08-15 14:30:14 -05:00
// this should not be called by petAI or
bool Creature :: canCreatureAttack ( Unit const * pVictim , bool force ) const
2008-10-11 14:16:25 -05:00
{
if ( ! pVictim -> IsInMap ( this ))
2009-08-15 14:30:14 -05:00
return false ;
2008-10-11 14:16:25 -05:00
2009-08-15 14:30:14 -05:00
if ( ! canAttack ( pVictim , force ))
return false ;
2008-10-11 14:16:25 -05:00
2008-11-14 16:28:45 -06:00
if ( ! pVictim -> isInAccessiblePlaceFor ( this ))
2009-08-15 14:30:14 -05:00
return false ;
2008-10-11 14:16:25 -05:00
2009-08-26 16:54:59 -05:00
if ( ! AI () -> CanAIAttack ( pVictim ))
return false ;
2008-11-30 12:47:26 -06:00
if ( sMapStore . LookupEntry ( GetMapId ()) -> IsDungeon ())
2009-08-15 14:30:14 -05:00
return true ;
2008-10-11 14:16:25 -05:00
2008-11-14 16:28:45 -06:00
//Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and out of combat every update tick.
2009-08-23 16:27:00 -05:00
float dist = std :: max ( GetAttackDistance ( pVictim ), ( float ) sWorld . getConfig ( CONFIG_THREAT_RADIUS )) + m_CombatDistance ;
2009-08-15 14:30:14 -05:00
if ( Unit * unit = GetCharmerOrOwner ())
2009-08-23 16:27:00 -05:00
return pVictim -> IsWithinDist ( unit , dist );
2009-08-15 14:30:14 -05:00
else
2009-08-23 16:27:00 -05:00
return pVictim -> IsWithinDist3d ( mHome_X , mHome_Y , mHome_Z , dist );
2008-10-11 14:16:25 -05:00
}
CreatureDataAddon const * Creature :: GetCreatureAddon () const
{
if ( m_DBTableGuid )
{
if ( CreatureDataAddon const * addon = ObjectMgr :: GetCreatureAddon ( m_DBTableGuid ))
return addon ;
}
// dependent from heroic mode entry
return ObjectMgr :: GetCreatureTemplateAddon ( GetCreatureInfo () -> Entry );
}
//creature_addon table
bool Creature :: LoadCreaturesAddon ( bool reload )
{
CreatureDataAddon const * cainfo = GetCreatureAddon ();
if ( ! cainfo )
return false ;
if ( cainfo -> mount != 0 )
Mount ( cainfo -> mount );
if ( cainfo -> bytes1 != 0 )
2009-05-31 15:58:58 -05:00
{
// 0 StandState
// 1 FreeTalentPoints Pet only, so always 0 for default creature
// 2 StandFlags
// 3 StandMiscFlags
SetByteValue ( UNIT_FIELD_BYTES_1 , 0 , uint8 ( cainfo -> bytes1 & 0xFF ));
//SetByteValue(UNIT_FIELD_BYTES_1, 1, uint8((cainfo->bytes1 >> 8) & 0xFF));
SetByteValue ( UNIT_FIELD_BYTES_1 , 1 , 0 );
SetByteValue ( UNIT_FIELD_BYTES_1 , 2 , uint8 (( cainfo -> bytes1 >> 16 ) & 0xFF ));
SetByteValue ( UNIT_FIELD_BYTES_1 , 3 , uint8 (( cainfo -> bytes1 >> 24 ) & 0xFF ));
}
2008-10-11 14:16:25 -05:00
if ( cainfo -> bytes2 != 0 )
2009-05-31 15:58:58 -05:00
{
// 0 SheathState
// 1 Bytes2Flags
// 2 UnitRename Pet only, so always 0 for default creature
// 3 ShapeshiftForm Must be determined/set by shapeshift spell/aura
SetByteValue ( UNIT_FIELD_BYTES_2 , 0 , uint8 ( cainfo -> bytes2 & 0xFF ));
SetByteValue ( UNIT_FIELD_BYTES_2 , 1 , uint8 (( cainfo -> bytes2 >> 8 ) & 0xFF ));
//SetByteValue(UNIT_FIELD_BYTES_2, 2, uint8((cainfo->bytes2 >> 16) & 0xFF));
SetByteValue ( UNIT_FIELD_BYTES_2 , 2 , 0 );
//SetByteValue(UNIT_FIELD_BYTES_2, 3, uint8((cainfo->bytes2 >> 24) & 0xFF));
SetByteValue ( UNIT_FIELD_BYTES_2 , 3 , 0 );
}
2008-10-11 14:16:25 -05:00
if ( cainfo -> emote != 0 )
SetUInt32Value ( UNIT_NPC_EMOTESTATE , cainfo -> emote );
if ( cainfo -> move_flags != 0 )
SetUnitMovementFlags ( cainfo -> move_flags );
if ( cainfo -> auras )
{
for ( CreatureDataAddonAura const * cAura = cainfo -> auras ; cAura -> spell_id ; ++ cAura )
{
SpellEntry const * AdditionalSpellInfo = sSpellStore . LookupEntry ( cAura -> spell_id );
if ( ! AdditionalSpellInfo )
{
sLog . outErrorDb ( "Creature (GUIDLow: %u Entry: %u ) has wrong spell %u defined in `auras` field." , GetGUIDLow (), GetEntry (), cAura -> spell_id );
continue ;
}
// skip already applied aura
2009-04-20 16:48:35 +02:00
if ( HasAuraEffect ( cAura -> spell_id , cAura -> effect_idx ))
2008-10-11 14:16:25 -05:00
{
if ( ! reload )
sLog . outErrorDb ( "Creature (GUIDLow: %u Entry: %u ) has duplicate aura (spell %u effect %u) in `auras` field." , GetGUIDLow (), GetEntry (), cAura -> spell_id , cAura -> effect_idx );
continue ;
}
2009-08-24 22:31:54 -05:00
AddAuraEffect ( AdditionalSpellInfo , cAura -> effect_idx , this , this );
2009-04-06 13:31:14 +02:00
sLog . outDebug ( "Spell: %u with Aura %u added to creature (GUIDLow: %u Entry: %u )" , cAura -> spell_id , AdditionalSpellInfo -> EffectApplyAuraName [ cAura -> effect_idx ], GetGUIDLow (), GetEntry ());
2008-10-11 14:16:25 -05:00
}
}
return true ;
}
2008-11-14 16:28:45 -06:00
/// Send a message to LocalDefense channel for players opposition team in the zone
2008-10-11 14:16:25 -05:00
void Creature :: SendZoneUnderAttackMessage ( Player * attacker )
{
uint32 enemy_team = attacker -> GetTeam ();
WorldPacket data ( SMSG_ZONE_UNDER_ATTACK , 4 );
2009-08-12 07:16:20 -05:00
data << ( uint32 ) GetAreaId ();
2008-10-11 14:16:25 -05:00
sWorld . SendGlobalMessage ( & data , NULL ,( enemy_team == ALLIANCE ? HORDE : ALLIANCE ));
}
2009-06-27 15:37:33 -05:00
void Creature :: SetInCombatWithZone ()
{
if ( ! CanHaveThreatList ())
{
2009-06-27 15:38:09 -05:00
sLog . outError ( "Creature entry %u call SetInCombatWithZone but creature cannot have threat list." , GetEntry ());
2009-06-27 15:37:33 -05:00
return ;
}
Map * pMap = GetMap ();
if ( ! pMap -> IsDungeon ())
{
2009-06-27 15:38:09 -05:00
sLog . outError ( "Creature entry %u call SetInCombatWithZone for map (id: %u) that isn't an instance." , GetEntry (), pMap -> GetId ());
2009-06-27 15:37:33 -05:00
return ;
}
Map :: PlayerList const & PlList = pMap -> GetPlayers ();
if ( PlList . isEmpty ())
return ;
for ( Map :: PlayerList :: const_iterator i = PlList . begin (); i != PlList . end (); ++ i )
{
if ( Player * pPlayer = i -> getSource ())
{
if ( pPlayer -> isGameMaster ())
continue ;
if ( pPlayer -> isAlive ())
{
pPlayer -> SetInCombatWith ( this );
AddThreat ( pPlayer , 0.0f );
}
}
}
}
2008-10-11 14:16:25 -05:00
void Creature :: _AddCreatureSpellCooldown ( uint32 spell_id , time_t end_time )
{
m_CreatureSpellCooldowns [ spell_id ] = end_time ;
}
void Creature :: _AddCreatureCategoryCooldown ( uint32 category , time_t apply_time )
{
m_CreatureCategoryCooldowns [ category ] = apply_time ;
}
void Creature :: AddCreatureSpellCooldown ( uint32 spellid )
{
SpellEntry const * spellInfo = sSpellStore . LookupEntry ( spellid );
if ( ! spellInfo )
return ;
uint32 cooldown = GetSpellRecoveryTime ( spellInfo );
2009-08-17 14:36:36 +02:00
if ( Player * modOwner = GetSpellModOwner ())
modOwner -> ApplySpellMod ( spellid , SPELLMOD_COOLDOWN , cooldown );
2008-10-11 14:16:25 -05:00
if ( cooldown )
2009-03-02 16:53:50 -06:00
_AddCreatureSpellCooldown ( spellid , time ( NULL ) + cooldown / IN_MILISECONDS );
2008-10-11 14:16:25 -05:00
if ( spellInfo -> Category )
_AddCreatureCategoryCooldown ( spellInfo -> Category , time ( NULL ));
m_GlobalCooldown = spellInfo -> StartRecoveryTime ;
}
bool Creature :: HasCategoryCooldown ( uint32 spell_id ) const
{
SpellEntry const * spellInfo = sSpellStore . LookupEntry ( spell_id );
if ( ! spellInfo )
return false ;
// check global cooldown if spell affected by it
if ( spellInfo -> StartRecoveryCategory > 0 && m_GlobalCooldown > 0 )
return true ;
CreatureSpellCooldowns :: const_iterator itr = m_CreatureCategoryCooldowns . find ( spellInfo -> Category );
2009-03-02 16:53:50 -06:00
return ( itr != m_CreatureCategoryCooldowns . end () && time_t ( itr -> second + ( spellInfo -> CategoryRecoveryTime / IN_MILISECONDS )) > time ( NULL ));
2008-10-11 14:16:25 -05:00
}
bool Creature :: HasSpellCooldown ( uint32 spell_id ) const
{
CreatureSpellCooldowns :: const_iterator itr = m_CreatureSpellCooldowns . find ( spell_id );
return ( itr != m_CreatureSpellCooldowns . end () && itr -> second > time ( NULL )) || HasCategoryCooldown ( spell_id );
}
bool Creature :: IsInEvadeMode () const
{
2009-01-17 00:00:48 -06:00
return /*!i_motionMaster.empty() &&*/ i_motionMaster . GetCurrentMovementGeneratorType () == HOME_MOTION_TYPE ;
2008-10-11 14:16:25 -05:00
}
bool Creature :: HasSpell ( uint32 spellID ) const
{
uint8 i ;
for ( i = 0 ; i < CREATURE_MAX_SPELLS ; ++ i )
if ( spellID == m_spells [ i ])
break ;
return i < CREATURE_MAX_SPELLS ; //broke before end of iteration of known spells
}
time_t Creature :: GetRespawnTimeEx () const
{
time_t now = time ( NULL );
if ( m_respawnTime > now ) // dead (no corpse)
return m_respawnTime ;
else if ( m_deathTimer > 0 ) // dead (corpse)
2009-03-02 16:53:50 -06:00
return now + m_respawnDelay + m_deathTimer / IN_MILISECONDS ;
2008-10-11 14:16:25 -05:00
else
return now ;
}
void Creature :: GetRespawnCoord ( float & x , float & y , float & z , float * ori , float * dist ) const
{
if ( m_DBTableGuid )
{
if ( CreatureData const * data = objmgr . GetCreatureData ( GetDBTableGUIDLow ()))
{
x = data -> posX ;
y = data -> posY ;
z = data -> posZ ;
if ( ori )
* ori = data -> orientation ;
if ( dist )
* dist = data -> spawndist ;
return ;
}
}
x = GetPositionX ();
y = GetPositionY ();
z = GetPositionZ ();
if ( ori )
* ori = GetOrientation ();
if ( dist )
* dist = 0 ;
}
void Creature :: AllLootRemovedFromCorpse ()
{
if ( ! HasFlag ( UNIT_FIELD_FLAGS , UNIT_FLAG_SKINNABLE ))
{
uint32 nDeathTimer ;
CreatureInfo const * cinfo = GetCreatureInfo ();
// corpse was not skinnable -> apply corpse looted timer
if ( ! cinfo || ! cinfo -> SkinLootId )
2009-03-02 16:53:50 -06:00
nDeathTimer = ( uint32 )(( m_corpseDelay * IN_MILISECONDS ) * sWorld . getRate ( RATE_CORPSE_DECAY_LOOTED ));
2008-10-11 14:16:25 -05:00
// corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
else
nDeathTimer = 0 ;
// update death timer only if looted timer is shorter
if ( m_deathTimer > nDeathTimer )
m_deathTimer = nDeathTimer ;
}
}
uint32 Creature :: getLevelForTarget ( Unit const * target ) const
{
if ( ! isWorldBoss ())
return Unit :: getLevelForTarget ( target );
uint32 level = target -> getLevel () + sWorld . getConfig ( CONFIG_WORLD_BOSS_LEVEL_DIFF );
if ( level < 1 )
return 1 ;
if ( level > 255 )
return 255 ;
return level ;
}
2009-04-14 18:31:31 -05:00
std :: string Creature :: GetAIName () const
{
return ObjectMgr :: GetCreatureTemplate ( GetEntry ()) -> AIName ;
}
std :: string Creature :: GetScriptName () const
2008-10-11 14:16:25 -05:00
{
2008-11-20 16:16:57 -06:00
return objmgr . GetScriptName ( GetScriptId ());
}
2009-04-14 18:31:31 -05:00
uint32 Creature :: GetScriptId () const
2008-11-20 16:16:57 -06:00
{
return ObjectMgr :: GetCreatureTemplate ( GetEntry ()) -> ScriptID ;
2008-10-11 14:16:25 -05:00
}
VendorItemData const * Creature :: GetVendorItems () const
{
return objmgr . GetNpcVendorItemList ( GetEntry ());
}
uint32 Creature :: GetVendorItemCurrentCount ( VendorItem const * vItem )
{
if ( ! vItem -> maxcount )
return vItem -> maxcount ;
VendorItemCounts :: iterator itr = m_vendorItemCounts . begin ();
for (; itr != m_vendorItemCounts . end (); ++ itr )
if ( itr -> itemId == vItem -> item )
break ;
if ( itr == m_vendorItemCounts . end ())
return vItem -> maxcount ;
VendorItemCount * vCount = &* itr ;
time_t ptime = time ( NULL );
if ( vCount -> lastIncrementTime + vItem -> incrtime <= ptime )
{
ItemPrototype const * pProto = objmgr . GetItemPrototype ( vItem -> item );
uint32 diff = uint32 (( ptime - vCount -> lastIncrementTime ) / vItem -> incrtime );
if (( vCount -> count + diff * pProto -> BuyCount ) >= vItem -> maxcount )
{
2008-11-04 09:23:06 -06:00
m_vendorItemCounts . erase ( itr );
2008-10-11 14:16:25 -05:00
return vItem -> maxcount ;
}
vCount -> count += diff * pProto -> BuyCount ;
vCount -> lastIncrementTime = ptime ;
}
return vCount -> count ;
}
uint32 Creature :: UpdateVendorItemCurrentCount ( VendorItem const * vItem , uint32 used_count )
{
if ( ! vItem -> maxcount )
return 0 ;
VendorItemCounts :: iterator itr = m_vendorItemCounts . begin ();
for (; itr != m_vendorItemCounts . end (); ++ itr )
if ( itr -> itemId == vItem -> item )
break ;
if ( itr == m_vendorItemCounts . end ())
{
uint32 new_count = vItem -> maxcount > used_count ? vItem -> maxcount - used_count : 0 ;
m_vendorItemCounts . push_back ( VendorItemCount ( vItem -> item , new_count ));
return new_count ;
}
VendorItemCount * vCount = &* itr ;
time_t ptime = time ( NULL );
if ( vCount -> lastIncrementTime + vItem -> incrtime <= ptime )
{
ItemPrototype const * pProto = objmgr . GetItemPrototype ( vItem -> item );
uint32 diff = uint32 (( ptime - vCount -> lastIncrementTime ) / vItem -> incrtime );
if (( vCount -> count + diff * pProto -> BuyCount ) < vItem -> maxcount )
vCount -> count += diff * pProto -> BuyCount ;
else
vCount -> count = vItem -> maxcount ;
}
vCount -> count = vCount -> count > used_count ? vCount -> count - used_count : 0 ;
vCount -> lastIncrementTime = ptime ;
return vCount -> count ;
}
TrainerSpellData const * Creature :: GetTrainerSpells () const
{
return objmgr . GetNpcTrainerSpells ( GetEntry ());
}
2008-11-14 17:03:03 -06:00
// overwrite WorldObject function for proper name localization
const char * Creature :: GetNameForLocaleIdx ( int32 loc_idx ) const
{
if ( loc_idx >= 0 )
{
CreatureLocale const * cl = objmgr . GetCreatureLocale ( GetEntry ());
if ( cl )
{
if ( cl -> Name . size () > loc_idx && ! cl -> Name [ loc_idx ]. empty ())
return cl -> Name [ loc_idx ]. c_str ();
}
}
return GetName ();
}
2009-02-17 20:07:49 -06:00
2009-04-14 14:33:48 +02:00
const CreatureData * Creature :: GetLinkedRespawnCreatureData () const
{
if ( ! m_DBTableGuid ) // only hard-spawned creatures from DB can have a linked master
return NULL ;
if ( uint32 targetGuid = objmgr . GetLinkedRespawnGuid ( m_DBTableGuid ))
return objmgr . GetCreatureData ( targetGuid );
return NULL ;
}
// returns master's remaining respawn time if any
time_t Creature :: GetLinkedCreatureRespawnTime () const
{
if ( ! m_DBTableGuid ) // only hard-spawned creatures from DB can have a linked master
return 0 ;
if ( uint32 targetGuid = objmgr . GetLinkedRespawnGuid ( m_DBTableGuid ))
{
Map * targetMap = NULL ;
if ( const CreatureData * data = objmgr . GetCreatureData ( targetGuid ))
{
if ( data -> mapid == GetMapId ()) // look up on the same map
targetMap = GetMap ();
else // it shouldn't be instanceable map here
targetMap = MapManager :: Instance (). FindMap ( data -> mapid );
}
if ( targetMap )
return objmgr . GetCreatureRespawnTime ( targetGuid , targetMap -> GetInstanceId ());
}
return 0 ;
2009-06-15 12:02:55 -05:00
}
2009-07-22 17:10:30 +02:00