2009-04-07 19:38:09 -05:00
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/>
*
* 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
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* 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
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "CreatureEventAI.h"
#include "CreatureEventAIMgr.h"
#include "ObjectMgr.h"
#include "Spell.h"
#include "World.h"
#include "Cell.h"
#include "CellImpl.h"
#include "GameEventMgr.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "WorldPacket.h"
#include "InstanceData.h"
2009-05-21 10:15:09 -05:00
namespace MaNGOS
{
class CallOfHelpCreatureInRangeDo // do attack at call of help to friendly crearture
{
public :
CallOfHelpCreatureInRangeDo ( Unit * funit , Unit * enemy , float range )
: i_funit ( funit ), i_enemy ( enemy ), i_range ( range )
{}
void operator ()( Creature * u )
{
if ( u == i_funit )
return ;
if ( ! u -> CanAssistTo ( i_funit , i_enemy , false ))
return ;
// too far
if ( ! i_funit -> IsWithinDistInMap ( u , i_range ) )
return ;
// only if see assisted creature
if ( ! i_funit -> IsWithinLOSInMap ( u ) )
return ;
if ( u -> AI ())
u -> AI () -> AttackStart ( i_enemy );
}
private :
Unit * const i_funit ;
Unit * const i_enemy ;
float i_range ;
};
}
2009-05-19 15:38:36 -05:00
bool CreatureEventAIHolder :: UpdateRepeatTimer ( Creature * creature , uint32 repeatMin , uint32 repeatMax )
{
if ( repeatMin == repeatMax )
Time = repeatMin ;
else if ( repeatMax > repeatMin )
Time = urand ( repeatMin , repeatMax );
else
{
sLog . outErrorDb ( "CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled." , creature -> GetEntry (), Event . event_id , Event . event_type );
Enabled = false ;
return false ;
}
return true ;
}
2009-04-07 19:38:09 -05:00
int CreatureEventAI :: Permissible ( const Creature * creature )
{
2009-04-14 18:31:31 -05:00
if ( creature -> GetAIName () == "EventAI" )
2009-04-07 19:38:09 -05:00
return PERMIT_BASE_SPECIAL ;
return PERMIT_BASE_NO ;
}
2009-04-24 11:57:36 -05:00
CreatureEventAI :: CreatureEventAI ( Creature * c ) : CreatureAI ( c )
2009-04-07 19:38:09 -05:00
{
2009-05-18 15:09:34 -05:00
// Need make copy for filter unneeded steps and safe in case table reload
2009-04-27 18:43:39 -05:00
CreatureEventAI_Event_Map :: const_iterator CreatureEvents = CreatureEAI_Mgr . GetCreatureEventAIMap (). find ( m_creature -> GetEntry ());
2009-04-07 19:38:09 -05:00
if ( CreatureEvents != CreatureEAI_Mgr . GetCreatureEventAIMap (). end ())
{
2009-04-27 18:43:39 -05:00
std :: vector < CreatureEventAI_Event >:: const_iterator i ;
2009-04-07 19:38:09 -05:00
for ( i = ( * CreatureEvents ). second . begin (); i != ( * CreatureEvents ). second . end (); ++ i )
{
//Debug check
2009-05-04 13:13:07 -05:00
#ifndef TRINITY_DEBUG
2009-04-07 19:38:09 -05:00
if (( * i ). event_flags & EFLAG_DEBUG_ONLY )
continue ;
#endif
2009-04-14 19:54:49 -05:00
if ((( * i ). event_flags & ( EFLAG_HEROIC | EFLAG_NORMAL )) && m_creature -> GetMap () -> IsDungeon () )
2009-04-07 19:38:09 -05:00
{
2009-04-14 19:54:49 -05:00
if ( ( m_creature -> GetMap () -> IsHeroic () && ( * i ). event_flags & EFLAG_HEROIC ) ||
( ! m_creature -> GetMap () -> IsHeroic () && ( * i ). event_flags & EFLAG_NORMAL ))
2009-04-07 19:38:09 -05:00
{
//event flagged for instance mode
CreatureEventAIList . push_back ( CreatureEventAIHolder ( * i ));
}
continue ;
}
CreatureEventAIList . push_back ( CreatureEventAIHolder ( * i ));
}
//EventMap had events but they were not added because they must be for instance
if ( CreatureEventAIList . empty ())
2009-04-29 00:31:03 -05:00
sLog . outError ( "CreatureEventAI: Creature %u has events but no events added to list because of instance flags." , m_creature -> GetEntry ());
2009-04-07 19:38:09 -05:00
}
else
2009-04-14 19:54:49 -05:00
sLog . outError ( "CreatureEventAI: EventMap for Creature %u is empty but creature is using CreatureEventAI." , m_creature -> GetEntry ());
2009-04-07 19:38:09 -05:00
bEmptyList = CreatureEventAIList . empty ();
Phase = 0 ;
CombatMovementEnabled = true ;
MeleeEnabled = true ;
AttackDistance = 0 ;
AttackAngle = 0.0f ;
//Handle Spawned Events
if ( ! bEmptyList )
{
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
2009-05-21 10:52:05 -05:00
if ( SpawnedEventConditionsCheck (( * i ). Event ))
2009-04-07 19:38:09 -05:00
ProcessEvent ( * i );
}
}
bool CreatureEventAI :: ProcessEvent ( CreatureEventAIHolder & pHolder , Unit * pActionInvoker )
{
if ( ! pHolder . Enabled || pHolder . Time )
return false ;
//Check the inverse phase mask (event doesn't trigger if current phase bit is set in mask)
if ( pHolder . Event . event_inverse_phase_mask & ( 1 << Phase ))
return false ;
//Store random here so that all random actions match up
uint32 rnd = rand ();
//Return if chance for event is not met
if ( pHolder . Event . event_chance <= rnd % 100 )
return false ;
2009-05-19 15:38:36 -05:00
CreatureEventAI_Event const & event = pHolder . Event ;
2009-04-07 19:38:09 -05:00
//Check event conditions based on the event type, also reset events
2009-05-19 15:38:36 -05:00
switch ( event . event_type )
2009-04-07 19:38:09 -05:00
{
case EVENT_T_TIMER :
2009-04-24 11:57:36 -05:00
if ( ! m_creature -> isInCombat ())
2009-04-07 19:38:09 -05:00
return false ;
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . timer . repeatMin , event . timer . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
case EVENT_T_TIMER_OOC :
2009-04-24 11:57:36 -05:00
if ( m_creature -> isInCombat ())
2009-04-07 19:38:09 -05:00
return false ;
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . timer . repeatMin , event . timer . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
case EVENT_T_HP :
{
2009-04-24 11:57:36 -05:00
if ( ! m_creature -> isInCombat () || ! m_creature -> GetMaxHealth ())
2009-04-07 19:38:09 -05:00
return false ;
2009-04-14 19:54:49 -05:00
uint32 perc = ( m_creature -> GetHealth () * 100 ) / m_creature -> GetMaxHealth ();
2009-04-07 19:38:09 -05:00
2009-05-19 15:38:36 -05:00
if ( perc > event . percent_range . percentMax || perc < event . percent_range . percentMin )
2009-04-07 19:38:09 -05:00
return false ;
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . percent_range . repeatMin , event . percent_range . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
}
case EVENT_T_MANA :
{
2009-04-24 11:57:36 -05:00
if ( ! m_creature -> isInCombat () || ! m_creature -> GetMaxPower ( POWER_MANA ))
2009-04-07 19:38:09 -05:00
return false ;
2009-04-14 19:54:49 -05:00
uint32 perc = ( m_creature -> GetPower ( POWER_MANA ) * 100 ) / m_creature -> GetMaxPower ( POWER_MANA );
2009-04-07 19:38:09 -05:00
2009-05-19 15:38:36 -05:00
if ( perc > event . percent_range . percentMax || perc < event . percent_range . percentMin )
2009-04-07 19:38:09 -05:00
return false ;
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . percent_range . repeatMin , event . percent_range . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
}
case EVENT_T_AGGRO :
2009-05-19 15:38:36 -05:00
break ;
2009-04-07 19:38:09 -05:00
case EVENT_T_KILL :
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . kill . repeatMin , event . kill . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
case EVENT_T_DEATH :
case EVENT_T_EVADE :
2009-05-19 15:38:36 -05:00
break ;
2009-04-07 19:38:09 -05:00
case EVENT_T_SPELLHIT :
//Spell hit is special case, param1 and param2 handled within CreatureEventAI::SpellHit
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . spell_hit . repeatMin , event . spell_hit . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
case EVENT_T_RANGE :
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . range . repeatMin , event . range . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
case EVENT_T_OOC_LOS :
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . ooc_los . repeatMin , event . ooc_los . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
case EVENT_T_SPAWNED :
2009-05-19 15:38:36 -05:00
break ;
2009-04-07 19:38:09 -05:00
case EVENT_T_TARGET_HP :
{
2009-04-24 11:57:36 -05:00
if ( ! m_creature -> isInCombat () || ! m_creature -> getVictim () || ! m_creature -> getVictim () -> GetMaxHealth ())
2009-04-07 19:38:09 -05:00
return false ;
2009-04-14 19:54:49 -05:00
uint32 perc = ( m_creature -> getVictim () -> GetHealth () * 100 ) / m_creature -> getVictim () -> GetMaxHealth ();
2009-04-07 19:38:09 -05:00
2009-05-19 15:38:36 -05:00
if ( perc > event . percent_range . percentMax || perc < event . percent_range . percentMin )
2009-04-07 19:38:09 -05:00
return false ;
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . percent_range . repeatMin , event . percent_range . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
}
case EVENT_T_TARGET_CASTING :
2009-04-24 11:57:36 -05:00
if ( ! m_creature -> isInCombat () || ! m_creature -> getVictim () || ! m_creature -> getVictim () -> IsNonMeleeSpellCasted ( false , false , true ))
2009-04-07 19:38:09 -05:00
return false ;
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . target_casting . repeatMin , event . target_casting . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
case EVENT_T_FRIENDLY_HP :
{
2009-04-24 11:57:36 -05:00
if ( ! m_creature -> isInCombat ())
2009-04-07 19:38:09 -05:00
return false ;
2009-05-19 15:38:36 -05:00
Unit * pUnit = DoSelectLowestHpFriendly ( event . friendly_hp . radius , event . friendly_hp . hpDeficit );
2009-04-07 19:38:09 -05:00
if ( ! pUnit )
return false ;
pActionInvoker = pUnit ;
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . friendly_hp . repeatMin , event . friendly_hp . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
}
case EVENT_T_FRIENDLY_IS_CC :
{
2009-04-24 11:57:36 -05:00
if ( ! m_creature -> isInCombat ())
2009-04-07 19:38:09 -05:00
return false ;
std :: list < Creature *> pList ;
2009-05-19 15:38:36 -05:00
DoFindFriendlyCC ( pList , event . friendly_is_cc . radius );
2009-04-07 19:38:09 -05:00
//List is empty
if ( pList . empty ())
return false ;
//We don't really care about the whole list, just return first available
pActionInvoker = * ( pList . begin ());
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . friendly_is_cc . repeatMin , event . friendly_is_cc . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
}
case EVENT_T_FRIENDLY_MISSING_BUFF :
{
std :: list < Creature *> pList ;
2009-05-19 15:38:36 -05:00
DoFindFriendlyMissingBuff ( pList , event . friendly_buff . radius , event . friendly_buff . spellId );
2009-04-07 19:38:09 -05:00
//List is empty
if ( pList . empty ())
return false ;
//We don't really care about the whole list, just return first available
pActionInvoker = * ( pList . begin ());
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . friendly_buff . repeatMin , event . friendly_buff . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
}
case EVENT_T_SUMMONED_UNIT :
{
//Prevent event from occuring on no unit or non creatures
if ( ! pActionInvoker || pActionInvoker -> GetTypeId () != TYPEID_UNIT )
return false ;
//Creature id doesn't match up
2009-05-19 15:38:36 -05:00
if ((( Creature * ) pActionInvoker ) -> GetEntry () != event . summon_unit . creatureId )
2009-04-07 19:38:09 -05:00
return false ;
//Repeat Timers
2009-05-19 15:38:36 -05:00
pHolder . UpdateRepeatTimer ( m_creature , event . summon_unit . repeatMin , event . summon_unit . repeatMax );
2009-04-07 19:38:09 -05:00
}
break ;
2009-05-19 15:38:36 -05:00
case EVENT_T_TARGET_MANA :
2009-04-07 19:38:09 -05:00
{
2009-05-19 15:38:36 -05:00
if ( ! m_creature -> isInCombat () || ! m_creature -> getVictim () || ! m_creature -> getVictim () -> GetMaxPower ( POWER_MANA ))
return false ;
uint32 perc = ( m_creature -> getVictim () -> GetPower ( POWER_MANA ) * 100 ) / m_creature -> getVictim () -> GetMaxPower ( POWER_MANA );
if ( perc > event . percent_range . percentMax || perc < event . percent_range . percentMin )
return false ;
//Repeat Timers
pHolder . UpdateRepeatTimer ( m_creature , event . percent_range . repeatMin , event . percent_range . repeatMax );
break ;
2009-04-07 19:38:09 -05:00
}
2009-05-19 15:38:36 -05:00
case EVENT_T_REACHED_HOME :
2009-04-07 19:38:09 -05:00
case EVENT_T_RECEIVE_EMOTE :
2009-05-19 15:38:36 -05:00
break ;
2009-04-07 19:38:09 -05:00
default :
2009-04-14 19:54:49 -05:00
sLog . outErrorDb ( "CreatureEventAI: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch." , m_creature -> GetEntry (), pHolder . Event . event_id , pHolder . Event . event_type );
2009-04-07 19:38:09 -05:00
break ;
}
//Disable non-repeatable events
if ( ! ( pHolder . Event . event_flags & EFLAG_REPEATABLE ))
pHolder . Enabled = false ;
//Process actions
for ( uint32 j = 0 ; j < MAX_ACTIONS ; j ++ )
2009-05-18 15:14:27 -05:00
ProcessAction ( pHolder . Event . action [ j ], rnd , pHolder . Event . event_id , pActionInvoker );
2009-04-07 19:38:09 -05:00
return true ;
}
2009-05-18 15:14:27 -05:00
void CreatureEventAI :: ProcessAction ( CreatureEventAI_Action const & action , uint32 rnd , uint32 EventId , Unit * pActionInvoker )
2009-04-07 19:38:09 -05:00
{
2009-05-18 15:14:27 -05:00
switch ( action . type )
2009-04-07 19:38:09 -05:00
{
case ACTION_T_TEXT :
{
2009-05-18 15:14:27 -05:00
if ( ! action . text . TextId1 )
2009-04-07 19:38:09 -05:00
return ;
2009-05-18 15:14:27 -05:00
int32 temp = 0 ;
2009-04-07 19:38:09 -05:00
2009-05-18 15:14:27 -05:00
if ( action . text . TextId2 && action . text . TextId3 )
2009-04-07 19:38:09 -05:00
{
switch ( rand () % 3 )
{
2009-05-18 15:14:27 -05:00
case 0 : temp = action . text . TextId1 ; break ;
2009-05-19 15:42:06 -05:00
case 1 : temp = action . text . TextId2 ; break ;
case 2 : temp = action . text . TextId3 ; break ;
2009-04-07 19:38:09 -05:00
}
}
2009-05-18 15:14:27 -05:00
else if ( action . text . TextId2 && urand ( 0 , 1 ))
temp = action . text . TextId2 ;
else
temp = action . text . TextId1 ;
2009-04-07 19:38:09 -05:00
if ( temp )
{
Unit * target = NULL ;
if ( pActionInvoker )
{
if ( pActionInvoker -> GetTypeId () == TYPEID_PLAYER )
target = pActionInvoker ;
2009-04-29 00:31:03 -05:00
else if ( Unit * owner = pActionInvoker -> GetOwner ())
2009-04-07 19:38:09 -05:00
{
if ( owner -> GetTypeId () == TYPEID_PLAYER )
target = owner ;
}
}
2009-04-14 19:54:49 -05:00
else if ( target = m_creature -> getVictim ())
2009-04-07 19:38:09 -05:00
{
if ( target -> GetTypeId () != TYPEID_PLAYER )
2009-04-29 00:31:03 -05:00
if ( Unit * owner = target -> GetOwner ())
2009-04-07 19:38:09 -05:00
if ( owner -> GetTypeId () == TYPEID_PLAYER )
target = owner ;
}
2009-04-14 19:54:49 -05:00
DoScriptText ( temp , m_creature , target );
2009-04-07 19:38:09 -05:00
}
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_SET_FACTION :
{
2009-05-18 15:14:27 -05:00
if ( action . set_faction . factionId )
m_creature -> setFaction ( action . set_faction . factionId );
2009-04-07 19:38:09 -05:00
else
{
2009-04-14 19:54:49 -05:00
if ( CreatureInfo const * ci = GetCreatureTemplateStore ( m_creature -> GetEntry ()))
2009-04-07 19:38:09 -05:00
{
//if no id provided, assume reset and then use default
2009-04-14 19:54:49 -05:00
if ( m_creature -> getFaction () != ci -> faction_A )
m_creature -> setFaction ( ci -> faction_A );
2009-04-07 19:38:09 -05:00
}
}
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_MORPH_TO_ENTRY_OR_MODEL :
{
2009-05-18 15:14:27 -05:00
if ( action . morph . creatireId || action . morph . modelId )
2009-04-07 19:38:09 -05:00
{
//set model based on entry from creature_template
2009-05-18 15:14:27 -05:00
if ( action . morph . creatireId )
2009-04-07 19:38:09 -05:00
{
2009-05-18 15:14:27 -05:00
if ( CreatureInfo const * ci = GetCreatureTemplateStore ( action . morph . creatireId ))
2009-04-07 19:38:09 -05:00
{
//use default display
2009-04-17 16:07:54 -05:00
if ( ci -> Modelid_A1 )
m_creature -> SetDisplayId ( ci -> Modelid_A1 );
2009-04-07 19:38:09 -05:00
}
}
//if no param1, then use value from param2 (modelId)
else
2009-05-18 15:14:27 -05:00
m_creature -> SetDisplayId ( action . morph . modelId );
2009-04-07 19:38:09 -05:00
}
else
2009-04-14 19:54:49 -05:00
m_creature -> DeMorph ();
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_SOUND :
2009-05-18 15:14:27 -05:00
m_creature -> PlayDirectSound ( action . sound . soundId );
2009-04-07 19:38:09 -05:00
break ;
case ACTION_T_EMOTE :
2009-05-18 15:14:27 -05:00
m_creature -> HandleEmoteCommand ( action . emote . emoteId );
2009-04-07 19:38:09 -05:00
break ;
case ACTION_T_RANDOM_SOUND :
{
2009-05-18 15:14:27 -05:00
int32 temp = GetRandActionParam ( rnd , action . random_sound . soundId1 , action . random_sound . soundId2 , action . random_sound . soundId3 );
if ( temp >= 0 )
m_creature -> PlayDirectSound ( temp );
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_RANDOM_EMOTE :
{
2009-05-18 15:14:27 -05:00
int32 temp = GetRandActionParam ( rnd , action . random_emote . emoteId1 , action . random_emote . emoteId2 , action . random_emote . emoteId3 );
if ( temp >= 0 )
2009-04-14 19:54:49 -05:00
m_creature -> HandleEmoteCommand ( temp );
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_CAST :
{
2009-05-18 15:14:27 -05:00
Unit * target = GetTargetByType ( action . cast . target , pActionInvoker );
2009-04-14 19:54:49 -05:00
Unit * caster = m_creature ;
2009-04-07 19:38:09 -05:00
if ( ! target )
return ;
2009-05-18 15:14:27 -05:00
if ( action . cast . castFlags & CAST_FORCE_TARGET_SELF )
2009-04-07 19:38:09 -05:00
caster = target ;
//Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered
2009-05-18 15:14:27 -05:00
bool canCast = ! caster -> IsNonMeleeSpellCasted ( false ) || ( action . cast . castFlags & ( CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS ));
2009-04-07 19:38:09 -05:00
// If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them
2009-05-18 15:14:27 -05:00
if ( action . cast . castFlags & CAST_AURA_NOT_PRESENT )
2009-04-07 19:38:09 -05:00
{
2009-05-18 15:14:27 -05:00
if ( target -> HasAura ( action . cast . spellId ))
2009-04-07 19:38:09 -05:00
return ;
}
if ( canCast )
{
2009-05-18 15:14:27 -05:00
const SpellEntry * tSpell = GetSpellStore () -> LookupEntry ( action . cast . spellId );
2009-04-07 19:38:09 -05:00
//Verify that spell exists
if ( tSpell )
{
//Check if cannot cast spell
2009-05-18 15:14:27 -05:00
if ( ! ( action . cast . castFlags & ( CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST )) &&
! CanCast ( target , tSpell , ( action . cast . castFlags & CAST_TRIGGERED )))
2009-04-07 19:38:09 -05:00
{
//Melee current victim if flag not set
2009-05-18 15:14:27 -05:00
if ( ! ( action . cast . castFlags & CAST_NO_MELEE_IF_OOM ))
2009-04-07 19:38:09 -05:00
{
2009-04-14 19:54:49 -05:00
if ( m_creature -> GetMotionMaster () -> GetCurrentMovementGeneratorType () == TARGETED_MOTION_TYPE )
2009-04-07 19:38:09 -05:00
{
AttackDistance = 0 ;
AttackAngle = 0 ;
2009-04-14 19:54:49 -05:00
m_creature -> GetMotionMaster () -> MoveChase ( m_creature -> getVictim (), AttackDistance , AttackAngle );
2009-04-07 19:38:09 -05:00
}
}
}
else
{
//Interrupt any previous spell
2009-05-18 15:14:27 -05:00
if ( caster -> IsNonMeleeSpellCasted ( false ) && action . cast . castFlags & CAST_INTURRUPT_PREVIOUS )
2009-04-07 19:38:09 -05:00
caster -> InterruptNonMeleeSpells ( false );
2009-05-18 15:14:27 -05:00
caster -> CastSpell ( target , action . cast . spellId , ( action . cast . castFlags & CAST_TRIGGERED ));
2009-04-07 19:38:09 -05:00
}
2009-05-18 15:14:27 -05:00
}
else
sLog . outErrorDb ( "CreatureEventAI: event %d creature %d attempt to cast spell that doesn't exist %d" , EventId , m_creature -> GetEntry (), action . cast . spellId );
2009-04-07 19:38:09 -05:00
}
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_SUMMON :
{
2009-05-18 15:14:27 -05:00
Unit * target = GetTargetByType ( action . summon . target , pActionInvoker );
2009-04-07 19:38:09 -05:00
Creature * pCreature = NULL ;
2009-05-18 15:14:27 -05:00
if ( action . summon . duration )
pCreature = m_creature -> SummonCreature ( action . summon . creatured , 0.0f , 0.0f , 0.0f , 0.0f , TEMPSUMMON_TIMED_OR_DEAD_DESPAWN , action . summon . duration );
2009-04-07 19:38:09 -05:00
else
2009-05-18 15:14:27 -05:00
pCreature = m_creature -> SummonCreature ( action . summon . creatured , 0.0f , 0.0f , 0.0f , 0.0f , TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT , 0 );
2009-04-07 19:38:09 -05:00
if ( ! pCreature )
2009-05-18 15:14:27 -05:00
sLog . outErrorDb ( "CreatureEventAI: failed to spawn creature %u. Spawn event %d is on creature %d" , action . summon . creatured , EventId , m_creature -> GetEntry ());
else if ( action . summon . target != TARGET_T_SELF && target )
2009-04-07 19:38:09 -05:00
pCreature -> AI () -> AttackStart ( target );
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_THREAT_SINGLE_PCT :
2009-05-18 15:14:27 -05:00
if ( Unit * target = GetTargetByType ( action . threat_single_pct . target , pActionInvoker ))
m_creature -> getThreatManager (). modifyThreatPercent ( target , action . threat_single_pct . percent );
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_THREAT_ALL_PCT :
{
2009-05-18 15:14:27 -05:00
std :: list < HostilReference *>& threatList = m_creature -> getThreatManager (). getThreatList ();
for ( std :: list < HostilReference *>:: iterator i = threatList . begin (); i != threatList . end (); ++ i )
if ( Unit * Temp = Unit :: GetUnit ( * m_creature ,( * i ) -> getUnitGuid ()))
m_creature -> getThreatManager (). modifyThreatPercent ( Temp , action . threat_all_pct . percent );
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_QUEST_EVENT :
2009-05-18 15:14:27 -05:00
if ( Unit * target = GetTargetByType ( action . quest_event . target , pActionInvoker ))
if ( target -> GetTypeId () == TYPEID_PLAYER )
(( Player * ) target ) -> AreaExploredOrEventHappens ( action . quest_event . questId );
break ;
case ACTION_T_CAST_EVENT :
if ( Unit * target = GetTargetByType ( action . cast_event . target , pActionInvoker ))
if ( target -> GetTypeId () == TYPEID_PLAYER )
(( Player * ) target ) -> CastedCreatureOrGO ( action . cast_event . creatureId , m_creature -> GetGUID (), action . cast_event . spellId );
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_SET_UNIT_FIELD :
{
2009-05-18 15:14:27 -05:00
Unit * target = GetTargetByType ( action . set_unit_field . target , pActionInvoker );
2009-04-07 19:38:09 -05:00
2009-05-18 15:14:27 -05:00
// not allow modify important for integrity object fields
if ( action . set_unit_field . field < OBJECT_END || action . set_unit_field . field >= UNIT_END )
2009-04-07 19:38:09 -05:00
return ;
if ( target )
2009-05-18 15:14:27 -05:00
target -> SetUInt32Value ( action . set_unit_field . field , action . set_unit_field . value );
2009-04-07 19:38:09 -05:00
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
}
2009-05-18 15:14:27 -05:00
case ACTION_T_SET_UNIT_FLAG :
if ( Unit * target = GetTargetByType ( action . unit_flag . target , pActionInvoker ))
target -> SetFlag ( UNIT_FIELD_FLAGS , action . unit_flag . value );
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_REMOVE_UNIT_FLAG :
2009-05-18 15:14:27 -05:00
if ( Unit * target = GetTargetByType ( action . unit_flag . target , pActionInvoker ))
target -> RemoveFlag ( UNIT_FIELD_FLAGS , action . unit_flag . value );
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_AUTO_ATTACK :
2009-05-18 15:14:27 -05:00
MeleeEnabled = action . auto_attack . state != 0 ;
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_COMBAT_MOVEMENT :
2009-05-18 15:14:27 -05:00
CombatMovementEnabled = action . combat_movement . state != 0 ;
2009-04-07 19:38:09 -05:00
//Allow movement (create new targeted movement gen only if idle)
if ( CombatMovementEnabled )
{
2009-04-14 19:54:49 -05:00
m_creature -> GetMotionMaster () -> MoveChase ( m_creature -> getVictim (), AttackDistance , AttackAngle );
2009-04-07 19:38:09 -05:00
}
else
{
2009-04-14 19:54:49 -05:00
m_creature -> GetMotionMaster () -> MoveIdle ();
2009-04-07 19:38:09 -05:00
}
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_SET_PHASE :
2009-05-18 15:14:27 -05:00
Phase = action . set_phase . phase ;
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_INC_PHASE :
{
2009-05-18 15:14:27 -05:00
int32 new_phase = int32 ( Phase ) + action . set_inc_phase . step ;
if ( new_phase < 0 )
{
sLog . outErrorDb ( "CreatureEventAI: Event %d decrease Phase under 0. CreatureEntry = %d" , EventId , m_creature -> GetEntry ());
Phase = 0 ;
}
else if ( new_phase >= MAX_PHASE )
{
sLog . outErrorDb ( "CreatureEventAI: Event %d incremented Phase above %u. Phase mask cannot be used with phases past %u. CreatureEntry = %d" , EventId , MAX_PHASE - 1 , MAX_PHASE - 1 , m_creature -> GetEntry ());
Phase = MAX_PHASE - 1 ;
}
else
Phase = new_phase ;
2009-04-07 19:38:09 -05:00
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_EVADE :
EnterEvadeMode ();
2009-05-18 15:14:27 -05:00
break ;
2009-05-21 10:48:00 -05:00
case ACTION_T_FLEE_FOR_ASSIST :
m_creature -> DoFleeToGetAssistance ();
2009-04-07 19:38:09 -05:00
break ;
case ACTION_T_QUEST_EVENT_ALL :
2009-05-18 15:14:27 -05:00
if ( pActionInvoker && pActionInvoker -> GetTypeId () == TYPEID_PLAYER )
2009-04-07 19:38:09 -05:00
{
2009-05-18 15:14:27 -05:00
if ( Unit * Temp = Unit :: GetUnit ( * m_creature , pActionInvoker -> GetGUID ()))
if ( Temp -> GetTypeId () == TYPEID_PLAYER )
(( Player * ) Temp ) -> GroupEventHappens ( action . quest_event_all . questId , m_creature );
2009-04-07 19:38:09 -05:00
}
2009-05-18 15:14:27 -05:00
break ;
case ACTION_T_CAST_EVENT_ALL :
2009-04-07 19:38:09 -05:00
{
2009-05-18 15:14:27 -05:00
std :: list < HostilReference *>& threatList = m_creature -> getThreatManager (). getThreatList ();
for ( std :: list < HostilReference *>:: iterator i = threatList . begin (); i != threatList . end (); ++ i )
if ( Unit * Temp = Unit :: GetUnit ( * m_creature ,( * i ) -> getUnitGuid ()))
if ( Temp -> GetTypeId () == TYPEID_PLAYER )
(( Player * ) Temp ) -> CastedCreatureOrGO ( action . cast_event_all . creatureId , m_creature -> GetGUID (), action . cast_event_all . spellId );
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_REMOVEAURASFROMSPELL :
2009-05-18 15:14:27 -05:00
if ( Unit * target = GetTargetByType ( action . remove_aura . target , pActionInvoker ))
target -> RemoveAurasDueToSpell ( action . remove_aura . spellId );
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_RANGED_MOVEMENT :
2009-05-18 15:14:27 -05:00
AttackDistance = action . ranged_movement . distance ;
AttackAngle = (( float ) action . ranged_movement . angle / 180 ) * M_PI ;
2009-04-07 19:38:09 -05:00
if ( CombatMovementEnabled )
{
2009-04-14 19:54:49 -05:00
m_creature -> GetMotionMaster () -> MoveChase ( m_creature -> getVictim (), AttackDistance , AttackAngle );
2009-04-07 19:38:09 -05:00
}
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_RANDOM_PHASE :
2009-05-18 15:14:27 -05:00
Phase = GetRandActionParam ( rnd , action . random_phase . phase1 , action . random_phase . phase2 , action . random_phase . phase3 );
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_RANDOM_PHASE_RANGE :
2009-05-18 15:14:27 -05:00
if ( action . random_phase_range . phaseMax > action . random_phase_range . phaseMin )
Phase = action . random_phase_range . phaseMin + ( rnd % ( action . random_phase_range . phaseMax - action . random_phase_range . phaseMin ));
2009-04-07 19:38:09 -05:00
else
2009-04-14 19:54:49 -05:00
sLog . outErrorDb ( "CreatureEventAI: ACTION_T_RANDOM_PHASE_RANGE cannot have Param2 <= Param1. Divide by Zero. Event = %d. CreatureEntry = %d" , EventId , m_creature -> GetEntry ());
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_SUMMON_ID :
{
2009-05-18 15:14:27 -05:00
Unit * target = GetTargetByType ( action . summon_id . target , pActionInvoker );
2009-04-07 19:38:09 -05:00
2009-05-18 15:14:27 -05:00
CreatureEventAI_Summon_Map :: const_iterator i = CreatureEAI_Mgr . GetCreatureEventAISummonMap (). find ( action . summon_id . spawnId );
2009-04-07 19:38:09 -05:00
if ( i == CreatureEAI_Mgr . GetCreatureEventAISummonMap (). end ())
{
2009-05-18 15:14:27 -05:00
sLog . outErrorDb ( "CreatureEventAI: failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d" , action . summon_id . creatureId , action . summon_id . spawnId , EventId , m_creature -> GetEntry ());
2009-04-07 19:38:09 -05:00
return ;
}
2009-05-18 15:14:27 -05:00
Creature * pCreature = NULL ;
2009-04-07 19:38:09 -05:00
if (( * i ). second . SpawnTimeSecs )
2009-05-18 15:14:27 -05:00
pCreature = m_creature -> SummonCreature ( action . summon_id . creatureId , ( * i ). second . position_x , ( * i ). second . position_y , ( * i ). second . position_z , ( * i ). second . orientation , TEMPSUMMON_TIMED_OR_DEAD_DESPAWN , ( * i ). second . SpawnTimeSecs );
else
pCreature = m_creature -> SummonCreature ( action . summon_id . creatureId , ( * i ). second . position_x , ( * i ). second . position_y , ( * i ). second . position_z , ( * i ). second . orientation , TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT , 0 );
2009-04-07 19:38:09 -05:00
if ( ! pCreature )
2009-05-18 15:14:27 -05:00
sLog . outErrorDb ( "CreatureEventAI: failed to spawn creature %u. EventId %d.Creature %d" , action . summon_id . creatureId , EventId , m_creature -> GetEntry ());
else if ( action . summon_id . target != TARGET_T_SELF && target )
2009-04-07 19:38:09 -05:00
pCreature -> AI () -> AttackStart ( target );
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_KILLED_MONSTER :
//first attempt player who tapped creature
2009-04-14 19:54:49 -05:00
if ( Player * pPlayer = m_creature -> GetLootRecipient ())
2009-05-18 15:14:27 -05:00
pPlayer -> RewardPlayerAndGroupAtEvent ( action . killed_monster . creatureId , m_creature );
2009-04-07 19:38:09 -05:00
else
{
//if not available, use pActionInvoker
2009-05-18 15:14:27 -05:00
if ( Unit * pTarget = GetTargetByType ( action . killed_monster . target , pActionInvoker ))
2009-04-27 18:42:35 -05:00
if ( Player * pPlayer2 = pTarget -> GetCharmerOrOwnerPlayerOrPlayerItself ())
2009-05-18 15:14:27 -05:00
pPlayer2 -> RewardPlayerAndGroupAtEvent ( action . killed_monster . creatureId , m_creature );
2009-04-07 19:38:09 -05:00
}
2009-05-18 15:14:27 -05:00
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_SET_INST_DATA :
{
2009-04-14 19:54:49 -05:00
InstanceData * pInst = ( InstanceData * ) m_creature -> GetInstanceData ();
2009-04-07 19:38:09 -05:00
if ( ! pInst )
{
2009-04-14 19:54:49 -05:00
sLog . outErrorDb ( "CreatureEventAI: Event %d attempt to set instance data without instance script. Creature %d" , EventId , m_creature -> GetEntry ());
2009-04-07 19:38:09 -05:00
return ;
}
2009-05-18 15:14:27 -05:00
pInst -> SetData ( action . set_inst_data . field , action . set_inst_data . value );
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_SET_INST_DATA64 :
{
2009-05-18 15:14:27 -05:00
Unit * target = GetTargetByType ( action . set_inst_data64 . target , pActionInvoker );
2009-04-07 19:38:09 -05:00
if ( ! target )
{
2009-04-14 19:54:49 -05:00
sLog . outErrorDb ( "CreatureEventAI: Event %d attempt to set instance data64 but Target == NULL. Creature %d" , EventId , m_creature -> GetEntry ());
2009-04-07 19:38:09 -05:00
return ;
}
2009-04-14 19:54:49 -05:00
InstanceData * pInst = ( InstanceData * ) m_creature -> GetInstanceData ();
2009-04-07 19:38:09 -05:00
if ( ! pInst )
{
2009-04-14 19:54:49 -05:00
sLog . outErrorDb ( "CreatureEventAI: Event %d attempt to set instance data64 without instance script. Creature %d" , EventId , m_creature -> GetEntry ());
2009-04-07 19:38:09 -05:00
return ;
}
2009-05-18 15:14:27 -05:00
pInst -> SetData64 ( action . set_inst_data64 . field , target -> GetGUID ());
break ;
2009-04-07 19:38:09 -05:00
}
case ACTION_T_UPDATE_TEMPLATE :
2009-05-18 15:14:27 -05:00
if ( m_creature -> GetEntry () == action . update_template . creatureId )
2009-04-07 19:38:09 -05:00
{
2009-04-14 19:54:49 -05:00
sLog . outErrorDb ( "CreatureEventAI: Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d" , EventId , m_creature -> GetEntry ());
2009-04-07 19:38:09 -05:00
return ;
}
2009-05-18 15:14:27 -05:00
m_creature -> UpdateEntry ( action . update_template . creatureId , action . update_template . team ? HORDE : ALLIANCE );
2009-05-21 10:15:09 -05:00
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_DIE :
2009-04-14 19:54:49 -05:00
if ( m_creature -> isDead ())
2009-04-07 19:38:09 -05:00
{
2009-04-14 19:54:49 -05:00
sLog . outErrorDb ( "CreatureEventAI: Event %d ACTION_T_DIE on dead creature. Creature %d" , EventId , m_creature -> GetEntry ());
2009-04-07 19:38:09 -05:00
return ;
}
2009-04-14 19:54:49 -05:00
m_creature -> DealDamage ( m_creature , m_creature -> GetMaxHealth (), NULL , DIRECT_DAMAGE , SPELL_SCHOOL_MASK_NORMAL , NULL , false );
2009-05-21 10:15:09 -05:00
break ;
2009-04-07 19:38:09 -05:00
case ACTION_T_ZONE_COMBAT_PULSE :
2009-04-14 19:54:49 -05:00
if ( ! m_creature -> isInCombat () || ! m_creature -> GetMap () -> IsDungeon ())
2009-04-07 19:38:09 -05:00
{
2009-04-14 19:54:49 -05:00
sLog . outErrorDb ( "CreatureEventAI: Event %d ACTION_T_ZONE_COMBAT_PULSE on creature out of combat or in non-dungeon map. Creature %d" , EventId , m_creature -> GetEntry ());
2009-04-07 19:38:09 -05:00
return ;
}
2009-04-14 19:54:49 -05:00
DoZoneInCombat ( m_creature );
2009-05-21 10:15:09 -05:00
break ;
case ACTION_T_CALL_FOR_HELP :
{
if ( ! m_creature -> getVictim ())
return ;
CellPair p ( MaNGOS :: ComputeCellPair ( m_creature -> GetPositionX (), m_creature -> GetPositionY ()));
Cell cell ( p );
cell . data . Part . reserved = ALL_DISTRICT ;
cell . SetNoCreate ();
MaNGOS :: CallOfHelpCreatureInRangeDo u_do ( m_creature , m_creature -> getVictim (), action . call_for_help . radius );
MaNGOS :: CreatureWorker < MaNGOS :: CallOfHelpCreatureInRangeDo > worker ( m_creature , 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 , * m_creature -> GetMap ());
break ;
2009-04-07 19:38:09 -05:00
}
break ;
// TRINITY ONLY
case ACTION_T_SET_ACTIVE :
2009-05-18 15:14:27 -05:00
me -> setActive ( action . raw . param1 ? true : false );
2009-04-07 19:38:09 -05:00
break ;
case ACTION_T_SET_AGGRESSIVE :
2009-05-18 15:14:27 -05:00
me -> SetReactState ( ReactStates ( action . raw . param1 ));
2009-04-07 19:38:09 -05:00
break ;
case ACTION_T_ATTACK_START_PULSE :
2009-05-18 15:14:27 -05:00
AttackStart ( me -> SelectNearestTarget (( float ) action . raw . param1 ));
2009-04-07 19:38:09 -05:00
break ;
2009-04-17 15:36:10 -05:00
case ACTION_T_SUMMON_GO :
{
GameObject * pObject = NULL ;
float x , y , z ;
m_creature -> GetPosition ( x , y , z );
2009-05-18 15:14:27 -05:00
pObject = m_creature -> SummonGameObject ( action . raw . param1 , x , y , z , 0 , 0 , 0 , 0 , 0 , action . raw . param2 );
2009-04-17 15:36:10 -05:00
if ( ! pObject )
{
2009-05-18 15:14:27 -05:00
sLog . outErrorDb ( "TSCR: EventAI failed to spawn object %u. Spawn event %d is on creature %d" , action . raw . param1 , EventId , m_creature -> GetEntry ());
2009-04-17 15:36:10 -05:00
}
2009-04-25 09:15:40 -05:00
break ;
2009-04-17 15:36:10 -05:00
}
2009-05-28 18:43:57 -05:00
case ACTION_T_SET_SHEATH :
{
m_creature -> SetSheath ( SheathState ( action . set_sheath . sheath ));
break ;
}
2009-04-07 19:38:09 -05:00
}
}
void CreatureEventAI :: JustRespawned ()
{
Reset ();
if ( bEmptyList )
return ;
//Handle Spawned Events
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
2009-05-21 10:52:05 -05:00
if ( SpawnedEventConditionsCheck (( * i ). Event ))
2009-04-07 19:38:09 -05:00
ProcessEvent ( * i );
}
void CreatureEventAI :: Reset ()
{
EventUpdateTime = EVENT_UPDATE_TIME ;
EventDiff = 0 ;
if ( bEmptyList )
return ;
//Reset all events to enabled
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
{
2009-05-19 15:38:36 -05:00
CreatureEventAI_Event const & event = ( * i ). Event ;
switch ( event . event_type )
2009-04-07 19:38:09 -05:00
{
//Reset all out of combat timers
case EVENT_T_TIMER_OOC :
{
2009-05-19 15:38:36 -05:00
if (( * i ). UpdateRepeatTimer ( m_creature , event . timer . initialMin , event . timer . initialMax ))
2009-04-07 19:38:09 -05:00
( * i ). Enabled = true ;
2009-05-19 15:38:36 -05:00
break ;
2009-04-07 19:38:09 -05:00
}
//default:
2009-04-27 21:23:42 -05:00
//TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void EnterCombat()
2009-04-07 19:38:09 -05:00
//(*i).Enabled = true;
//(*i).Time = 0;
//break;
}
}
}
void CreatureEventAI :: JustReachedHome ()
{
2009-04-14 19:54:49 -05:00
m_creature -> LoadCreaturesAddon ();
2009-04-07 19:38:09 -05:00
if ( ! bEmptyList )
{
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
{
if (( * i ). Event . event_type == EVENT_T_REACHED_HOME )
ProcessEvent ( * i );
}
}
Reset ();
}
void CreatureEventAI :: EnterEvadeMode ()
{
2009-05-12 10:11:54 -05:00
CreatureAI :: EnterEvadeMode ();
2009-04-07 19:38:09 -05:00
if ( bEmptyList )
return ;
//Handle Evade events
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
{
if (( * i ). Event . event_type == EVENT_T_EVADE )
ProcessEvent ( * i );
}
}
void CreatureEventAI :: JustDied ( Unit * killer )
{
Reset ();
if ( bEmptyList )
return ;
//Handle Evade events
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
{
if (( * i ). Event . event_type == EVENT_T_DEATH )
ProcessEvent ( * i , killer );
}
}
void CreatureEventAI :: KilledUnit ( Unit * victim )
{
if ( bEmptyList || victim -> GetTypeId () != TYPEID_PLAYER )
return ;
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
{
if (( * i ). Event . event_type == EVENT_T_KILL )
ProcessEvent ( * i , victim );
}
}
void CreatureEventAI :: JustSummoned ( Creature * pUnit )
{
if ( bEmptyList || ! pUnit )
return ;
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
{
if (( * i ). Event . event_type == EVENT_T_SUMMONED_UNIT )
ProcessEvent ( * i , pUnit );
}
}
2009-04-27 19:14:37 -05:00
void CreatureEventAI :: EnterCombat ( Unit * enemy )
2009-04-07 19:38:09 -05:00
{
//Check for on combat start events
if ( ! bEmptyList )
{
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
{
2009-05-19 15:38:36 -05:00
CreatureEventAI_Event const & event = ( * i ). Event ;
switch ( event . event_type )
2009-04-07 19:38:09 -05:00
{
case EVENT_T_AGGRO :
( * i ). Enabled = true ;
2009-04-27 19:14:37 -05:00
ProcessEvent ( * i , enemy );
2009-04-07 19:38:09 -05:00
break ;
//Reset all in combat timers
case EVENT_T_TIMER :
2009-05-19 15:38:36 -05:00
if (( * i ). UpdateRepeatTimer ( m_creature , event . timer . initialMin , event . timer . initialMax ))
2009-04-07 19:38:09 -05:00
( * i ). Enabled = true ;
break ;
//All normal events need to be re-enabled and their time set to 0
default :
( * i ). Enabled = true ;
( * i ). Time = 0 ;
break ;
}
}
}
EventUpdateTime = EVENT_UPDATE_TIME ;
EventDiff = 0 ;
}
void CreatureEventAI :: AttackStart ( Unit * who )
{
if ( ! who )
return ;
2009-04-14 19:54:49 -05:00
if ( m_creature -> Attack ( who , MeleeEnabled ))
2009-04-07 19:38:09 -05:00
{
if ( CombatMovementEnabled )
{
2009-04-14 19:54:49 -05:00
m_creature -> GetMotionMaster () -> MoveChase ( who , AttackDistance , AttackAngle );
2009-04-07 19:38:09 -05:00
}
else
{
2009-04-14 19:54:49 -05:00
m_creature -> GetMotionMaster () -> MoveIdle ();
2009-04-07 19:38:09 -05:00
}
}
}
void CreatureEventAI :: MoveInLineOfSight ( Unit * who )
{
2009-05-03 16:58:44 -05:00
if ( me -> getVictim ())
2009-04-07 19:38:09 -05:00
return ;
//Check for OOC LOS Event
2009-05-03 16:58:44 -05:00
if ( ! bEmptyList )
2009-04-07 19:38:09 -05:00
{
for ( std :: list < CreatureEventAIHolder >:: iterator itr = CreatureEventAIList . begin (); itr != CreatureEventAIList . end (); ++ itr )
{
if (( * itr ). Event . event_type == EVENT_T_OOC_LOS )
{
//can trigger if closer than fMaxAllowedRange
2009-05-19 15:38:36 -05:00
float fMaxAllowedRange = ( * itr ). Event . ooc_los . maxRange ;
2009-04-07 19:38:09 -05:00
//if range is ok and we are actually in LOS
2009-04-14 19:54:49 -05:00
if ( m_creature -> IsWithinDistInMap ( who , fMaxAllowedRange ) && m_creature -> IsWithinLOSInMap ( who ))
2009-04-07 19:38:09 -05:00
{
//if friendly event&&who is not hostile OR hostile event&&who is hostile
2009-05-19 15:38:36 -05:00
if ((( * itr ). Event . ooc_los . noHostile && ! m_creature -> IsHostileTo ( who )) ||
(( ! ( * itr ). Event . ooc_los . noHostile ) && m_creature -> IsHostileTo ( who )))
2009-04-07 19:38:09 -05:00
ProcessEvent ( * itr , who );
}
}
}
}
2009-04-14 19:54:49 -05:00
//if (m_creature->isCivilian() && m_creature->IsNeutralToAll())
2009-04-07 19:38:09 -05:00
// return;
if ( me -> canStartAttack ( who ))
AttackStart ( who );
}
void CreatureEventAI :: SpellHit ( Unit * pUnit , const SpellEntry * pSpell )
{
if ( bEmptyList )
return ;
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
if (( * i ). Event . event_type == EVENT_T_SPELLHIT )
//If spell id matches (or no spell id) & if spell school matches (or no spell school)
2009-05-19 15:38:36 -05:00
if ( ! ( * i ). Event . spell_hit . spellId || pSpell -> Id == ( * i ). Event . spell_hit . spellId )
if ( pSpell -> SchoolMask & ( * i ). Event . spell_hit . schoolMask )
2009-04-07 19:38:09 -05:00
ProcessEvent ( * i , pUnit );
}
void CreatureEventAI :: UpdateAI ( const uint32 diff )
{
//Check if we are in combat (also updates calls threat update code)
2009-04-24 11:57:36 -05:00
bool Combat = UpdateVictim ();
2009-04-07 19:38:09 -05:00
if ( ! bEmptyList )
{
//Events are only updated once every EVENT_UPDATE_TIME ms to prevent lag with large amount of events
if ( EventUpdateTime < diff )
{
EventDiff += diff ;
//Check for time based events
for ( std :: list < CreatureEventAIHolder >:: iterator i = CreatureEventAIList . begin (); i != CreatureEventAIList . end (); ++ i )
{
//Decrement Timers
if (( * i ). Time )
{
if (( * i ). Time > EventDiff )
{
//Do not decrement timers if event cannot trigger in this phase
if ( ! (( * i ). Event . event_inverse_phase_mask & ( 1 << Phase )))
( * i ). Time -= EventDiff ;
//Skip processing of events that have time remaining
continue ;
}
else ( * i ). Time = 0 ;
}
//Events that are updated every EVENT_UPDATE_TIME
switch (( * i ). Event . event_type )
{
case EVENT_T_TIMER_OOC :
ProcessEvent ( * i );
break ;
case EVENT_T_TIMER :
case EVENT_T_MANA :
case EVENT_T_HP :
case EVENT_T_TARGET_HP :
case EVENT_T_TARGET_CASTING :
case EVENT_T_FRIENDLY_HP :
2009-05-15 14:23:05 -05:00
if ( me -> getVictim ())
2009-04-07 19:38:09 -05:00
ProcessEvent ( * i );
break ;
case EVENT_T_RANGE :
2009-05-15 14:23:05 -05:00
if ( me -> getVictim ())
2009-05-14 16:50:47 -05:00
if ( m_creature -> IsInMap ( m_creature -> getVictim ()))
2009-05-19 15:38:36 -05:00
if ( m_creature -> IsInRange ( m_creature -> getVictim (),( float )( * i ). Event . range . minDist ,( float )( * i ). Event . range . maxDist ))
2009-04-07 19:38:09 -05:00
ProcessEvent ( * i );
break ;
}
}
EventDiff = 0 ;
EventUpdateTime = EVENT_UPDATE_TIME ;
}
else
{
EventDiff += diff ;
EventUpdateTime -= diff ;
}
}
//Melee Auto-Attack
if ( Combat && MeleeEnabled )
DoMeleeAttackIfReady ();
}
inline Unit * CreatureEventAI :: SelectUnit ( AttackingTarget target , uint32 position )
{
//ThreatList m_threatlist;
2009-04-14 19:54:49 -05:00
std :: list < HostilReference *>& m_threatlist = m_creature -> getThreatManager (). getThreatList ();
2009-04-07 19:38:09 -05:00
std :: list < HostilReference *>:: iterator i = m_threatlist . begin ();
std :: list < HostilReference *>:: reverse_iterator r = m_threatlist . rbegin ();
if ( position >= m_threatlist . size () || ! m_threatlist . size ())
return NULL ;
switch ( target )
{
case ATTACKING_TARGET_RANDOM :
{
advance ( i , position + ( rand () % ( m_threatlist . size () - position ) ));
2009-04-14 19:54:49 -05:00
return Unit :: GetUnit ( * m_creature ,( * i ) -> getUnitGuid ());
2009-04-07 19:38:09 -05:00
}
case ATTACKING_TARGET_TOPAGGRO :
{
advance ( i , position );
2009-04-14 19:54:49 -05:00
return Unit :: GetUnit ( * m_creature ,( * i ) -> getUnitGuid ());
2009-04-07 19:38:09 -05:00
}
case ATTACKING_TARGET_BOTTOMAGGRO :
{
advance ( r , position );
2009-04-14 19:54:49 -05:00
return Unit :: GetUnit ( * m_creature ,( * r ) -> getUnitGuid ());
2009-04-07 19:38:09 -05:00
}
}
return NULL ;
}
inline uint32 CreatureEventAI :: GetRandActionParam ( uint32 rnd , uint32 param1 , uint32 param2 , uint32 param3 )
{
switch ( rnd % 3 )
{
2009-05-18 15:14:27 -05:00
case 0 : return param1 ;
case 1 : return param2 ;
case 2 : return param3 ;
}
return 0 ;
}
inline int32 CreatureEventAI :: GetRandActionParam ( uint32 rnd , int32 param1 , int32 param2 , int32 param3 )
{
switch ( rnd % 3 )
{
case 0 : return param1 ;
case 1 : return param2 ;
case 2 : return param3 ;
2009-04-07 19:38:09 -05:00
}
return 0 ;
}
inline Unit * CreatureEventAI :: GetTargetByType ( uint32 Target , Unit * pActionInvoker )
{
switch ( Target )
{
case TARGET_T_SELF :
2009-04-14 19:54:49 -05:00
return m_creature ;
2009-04-07 19:38:09 -05:00
case TARGET_T_HOSTILE :
2009-04-14 19:54:49 -05:00
return m_creature -> getVictim ();
2009-04-07 19:38:09 -05:00
case TARGET_T_HOSTILE_SECOND_AGGRO :
return SelectUnit ( ATTACKING_TARGET_TOPAGGRO , 1 );
case TARGET_T_HOSTILE_LAST_AGGRO :
return SelectUnit ( ATTACKING_TARGET_BOTTOMAGGRO , 0 );
case TARGET_T_HOSTILE_RANDOM :
return SelectUnit ( ATTACKING_TARGET_RANDOM , 0 );
case TARGET_T_HOSTILE_RANDOM_NOT_TOP :
return SelectUnit ( ATTACKING_TARGET_RANDOM , 1 );
case TARGET_T_ACTION_INVOKER :
return pActionInvoker ;
default :
return NULL ;
};
}
Unit * CreatureEventAI :: DoSelectLowestHpFriendly ( float range , uint32 MinHPDiff )
{
2009-04-14 19:54:49 -05:00
CellPair p ( MaNGOS :: ComputeCellPair ( m_creature -> GetPositionX (), m_creature -> GetPositionY ()));
2009-04-07 19:38:09 -05:00
Cell cell ( p );
cell . data . Part . reserved = ALL_DISTRICT ;
cell . SetNoCreate ();
Unit * pUnit = NULL ;
2009-04-14 19:54:49 -05:00
MaNGOS :: MostHPMissingInRange u_check ( m_creature , range , MinHPDiff );
MaNGOS :: UnitLastSearcher < MaNGOS :: MostHPMissingInRange > searcher ( m_creature , pUnit , u_check );
2009-04-07 19:38:09 -05:00
/*
typedef TYPELIST_4(GameObject, Creature*except pets*, DynamicObject, Corpse*Bones*) AllGridObjectTypes;
This means that if we only search grid then we cannot possibly return pets or players so this is safe
*/
TypeContainerVisitor < MaNGOS :: UnitLastSearcher < MaNGOS :: MostHPMissingInRange > , GridTypeMapContainer > grid_unit_searcher ( searcher );
CellLock < GridReadGuard > cell_lock ( cell , p );
2009-04-14 19:54:49 -05:00
cell_lock -> Visit ( cell_lock , grid_unit_searcher , * m_creature -> GetMap ());
2009-04-07 19:38:09 -05:00
return pUnit ;
}
void CreatureEventAI :: DoFindFriendlyCC ( std :: list < Creature *>& _list , float range )
{
2009-04-14 19:54:49 -05:00
CellPair p ( MaNGOS :: ComputeCellPair ( m_creature -> GetPositionX (), m_creature -> GetPositionY ()));
2009-04-07 19:38:09 -05:00
Cell cell ( p );
cell . data . Part . reserved = ALL_DISTRICT ;
cell . SetNoCreate ();
2009-04-14 19:54:49 -05:00
MaNGOS :: FriendlyCCedInRange u_check ( m_creature , range );
MaNGOS :: CreatureListSearcher < MaNGOS :: FriendlyCCedInRange > searcher ( m_creature , _list , u_check );
2009-04-07 19:38:09 -05:00
TypeContainerVisitor < MaNGOS :: CreatureListSearcher < MaNGOS :: FriendlyCCedInRange > , GridTypeMapContainer > grid_creature_searcher ( searcher );
CellLock < GridReadGuard > cell_lock ( cell , p );
2009-04-14 19:54:49 -05:00
cell_lock -> Visit ( cell_lock , grid_creature_searcher , * m_creature -> GetMap ());
2009-04-07 19:38:09 -05:00
}
void CreatureEventAI :: DoFindFriendlyMissingBuff ( std :: list < Creature *>& _list , float range , uint32 spellid )
{
2009-04-14 19:54:49 -05:00
CellPair p ( MaNGOS :: ComputeCellPair ( m_creature -> GetPositionX (), m_creature -> GetPositionY ()));
2009-04-07 19:38:09 -05:00
Cell cell ( p );
cell . data . Part . reserved = ALL_DISTRICT ;
cell . SetNoCreate ();
2009-04-14 19:54:49 -05:00
MaNGOS :: FriendlyMissingBuffInRange u_check ( m_creature , range , spellid );
MaNGOS :: CreatureListSearcher < MaNGOS :: FriendlyMissingBuffInRange > searcher ( m_creature , _list , u_check );
2009-04-07 19:38:09 -05:00
TypeContainerVisitor < MaNGOS :: CreatureListSearcher < MaNGOS :: FriendlyMissingBuffInRange > , GridTypeMapContainer > grid_creature_searcher ( searcher );
CellLock < GridReadGuard > cell_lock ( cell , p );
2009-04-14 19:54:49 -05:00
cell_lock -> Visit ( cell_lock , grid_creature_searcher , * m_creature -> GetMap ());
2009-04-07 19:38:09 -05:00
}
//*********************************
//*** Functions used globally ***
void CreatureEventAI :: DoScriptText ( int32 textEntry , WorldObject * pSource , Unit * target )
{
if ( ! pSource )
{
sLog . outErrorDb ( "CreatureEventAI: DoScriptText entry %i, invalid Source pointer." , textEntry );
return ;
}
if ( textEntry >= 0 )
{
sLog . outErrorDb ( "CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) attempts to process text entry %i, but text entry must be negative." , pSource -> GetEntry (), pSource -> GetTypeId (), pSource -> GetGUIDLow (), textEntry );
return ;
}
CreatureEventAI_TextMap :: const_iterator i = CreatureEAI_Mgr . GetCreatureEventAITextMap (). find ( textEntry );
if ( i == CreatureEAI_Mgr . GetCreatureEventAITextMap (). end ())
{
sLog . outErrorDb ( "CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) could not find text entry %i." , pSource -> GetEntry (), pSource -> GetTypeId (), pSource -> GetGUIDLow (), textEntry );
return ;
}
sLog . outDebug ( "CreatureEventAI: DoScriptText: text entry=%i, Sound=%u, Type=%u, Language=%u, Emote=%u" , textEntry ,( * i ). second . SoundId ,( * i ). second . Type ,( * i ). second . Language ,( * i ). second . Emote );
if (( * i ). second . SoundId )
{
if ( GetSoundEntriesStore () -> LookupEntry (( * i ). second . SoundId ))
pSource -> PlayDirectSound (( * i ). second . SoundId );
else
sLog . outErrorDb ( "CreatureEventAI: DoScriptText entry %i tried to process invalid sound id %u." , textEntry ,( * i ). second . SoundId );
}
if (( * i ). second . Emote )
{
if ( pSource -> GetTypeId () == TYPEID_UNIT || pSource -> GetTypeId () == TYPEID_PLAYER )
{
(( Unit * ) pSource ) -> HandleEmoteCommand (( * i ). second . Emote );
}
else
sLog . outErrorDb ( "CreatureEventAI: DoScriptText entry %i tried to process emote for invalid TypeId (%u)." , textEntry , pSource -> GetTypeId ());
}
switch (( * i ). second . Type )
{
case CHAT_TYPE_SAY :
pSource -> MonsterSay ( textEntry , ( * i ). second . Language , target ? target -> GetGUID () : 0 );
break ;
case CHAT_TYPE_YELL :
pSource -> MonsterYell ( textEntry , ( * i ). second . Language , target ? target -> GetGUID () : 0 );
break ;
case CHAT_TYPE_TEXT_EMOTE :
pSource -> MonsterTextEmote ( textEntry , target ? target -> GetGUID () : 0 );
break ;
case CHAT_TYPE_BOSS_EMOTE :
pSource -> MonsterTextEmote ( textEntry , target ? target -> GetGUID () : 0 , true );
break ;
case CHAT_TYPE_WHISPER :
{
if ( target && target -> GetTypeId () == TYPEID_PLAYER )
pSource -> MonsterWhisper ( textEntry , target -> GetGUID ());
else sLog . outErrorDb ( "CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER)." , textEntry );
} break ;
case CHAT_TYPE_BOSS_WHISPER :
{
if ( target && target -> GetTypeId () == TYPEID_PLAYER )
pSource -> MonsterWhisper ( textEntry , target -> GetGUID (), true );
else sLog . outErrorDb ( "CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER)." , textEntry );
} break ;
case CHAT_TYPE_ZONE_YELL :
pSource -> MonsterYellToZone ( textEntry , ( * i ). second . Language , target ? target -> GetGUID () : 0 );
break ;
}
}
bool CreatureEventAI :: CanCast ( Unit * Target , SpellEntry const * Spell , bool Triggered )
{
//No target so we can't cast
if ( ! Target || ! Spell )
return false ;
//Silenced so we can't cast
if ( ! Triggered && me -> HasFlag ( UNIT_FIELD_FLAGS , UNIT_FLAG_SILENCED ))
return false ;
//Check for power
if ( ! Triggered && me -> GetPower (( Powers ) Spell -> powerType ) < Spell -> manaCost )
return false ;
SpellRangeEntry const * TempRange = NULL ;
TempRange = GetSpellRangeStore () -> LookupEntry ( Spell -> rangeIndex );
//Spell has invalid range store so we can't use it
if ( ! TempRange )
return false ;
//Unit is out of range of this spell
2009-05-14 16:50:47 -05:00
if ( ! m_creature -> IsInRange ( Target , TempRange -> minRangeHostile , TempRange -> maxRangeHostile ))
2009-04-07 19:38:09 -05:00
return false ;
return true ;
}
2009-04-15 17:29:33 -05:00
void CreatureEventAI :: ReceiveEmote ( Player * pPlayer , uint32 text_emote )
2009-04-07 19:38:09 -05:00
{
2009-04-15 17:29:33 -05:00
if ( bEmptyList )
return ;
2009-04-07 19:38:09 -05:00
2009-04-15 17:29:33 -05:00
for ( std :: list < CreatureEventAIHolder >:: iterator itr = CreatureEventAIList . begin (); itr != CreatureEventAIList . end (); ++ itr )
2009-04-07 19:38:09 -05:00
{
if (( * itr ). Event . event_type == EVENT_T_RECEIVE_EMOTE )
{
2009-05-19 15:38:36 -05:00
if (( * itr ). Event . receive_emote . emoteId != text_emote )
2009-04-15 17:29:33 -05:00
return ;
2009-04-07 19:38:09 -05:00
2009-05-19 15:38:36 -05:00
PlayerCondition pcon (( * itr ). Event . receive_emote . condition ,( * itr ). Event . receive_emote . conditionValue1 ,( * itr ). Event . receive_emote . conditionValue2 );
if ( pcon . Meets ( pPlayer ))
2009-04-07 19:38:09 -05:00
{
sLog . outDebug ( "CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing" );
2009-04-15 17:29:33 -05:00
ProcessEvent ( * itr , pPlayer );
2009-04-07 19:38:09 -05:00
}
}
}
}
2009-05-21 10:52:05 -05:00
bool CreatureEventAI :: SpawnedEventConditionsCheck ( CreatureEventAI_Event const & event )
{
if ( event . event_type != EVENT_T_SPAWNED )
return false ;
switch ( event . spawned . condition )
{
case SPAWNED_EVENT_ALWAY :
// always
return true ;
case SPAWNED_EVENT_MAP :
// map ID check
return m_creature -> GetMapId () == event . spawned . conditionValue1 ;
case SPAWNED_EVENT_ZONE :
{
// zone ID check
uint32 zone , area ;
m_creature -> GetZoneAndAreaId ( zone , area );
return zone == event . spawned . conditionValue1 || area == event . spawned . conditionValue1 ;
}
default :
break ;
}
return false ;
}