2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include "Database/DatabaseEnv.h"
21 #include "Database/SQLStorage.h"
22 #include "CreatureEventAI.h"
23 #include "CreatureEventAIMgr.h"
24 #include "ObjectMgr.h"
25 #include "ProgressBar.h"
26 #include "Policies/SingletonImp.h"
27 #include "ObjectDefines.h"
28 #include "GridDefines.h"
30 INSTANTIATE_SINGLETON_1(CreatureEventAIMgr
);
32 // -------------------
33 void CreatureEventAIMgr::LoadCreatureEventAI_Texts()
35 // Drop Existing Text Map, only done once and we are ready to add data from multiple sources.
36 m_CreatureEventAI_TextMap
.clear();
39 objmgr
.LoadMangosStrings(WorldDatabase
,"creature_ai_texts",MIN_CREATURE_AI_TEXT_STRING_ID
,MAX_CREATURE_AI_TEXT_STRING_ID
);
41 // Gather Additional data from EventAI Texts
42 QueryResult
*result
= WorldDatabase
.PQuery("SELECT entry, sound, type, language, emote FROM creature_ai_texts");
44 sLog
.outString("Loading EventAI Texts additional data...");
47 barGoLink
bar(result
->GetRowCount());
53 Field
* fields
= result
->Fetch();
56 int32 i
= fields
[0].GetInt32();
57 temp
.SoundId
= fields
[1].GetInt32();
58 temp
.Type
= fields
[2].GetInt32();
59 temp
.Language
= fields
[3].GetInt32();
60 temp
.Emote
= fields
[4].GetInt32();
63 if (i
> MIN_CREATURE_AI_TEXT_STRING_ID
|| i
<= MAX_CREATURE_AI_TEXT_STRING_ID
)
65 sLog
.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` is not in valid range(%d-%d)",i
,MIN_CREATURE_AI_TEXT_STRING_ID
,MAX_CREATURE_AI_TEXT_STRING_ID
);
69 // range negative (don't must be happen, loaded from same table)
70 if (!objmgr
.GetMangosStringLocale(i
))
72 sLog
.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` not found",i
);
78 if (!sSoundEntriesStore
.LookupEntry(temp
.SoundId
))
79 sLog
.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Sound %u but sound does not exist.",i
,temp
.SoundId
);
82 if (!GetLanguageDescByID(temp
.Language
))
83 sLog
.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` using Language %u but Language does not exist.",i
,temp
.Language
);
85 if (temp
.Type
> CHAT_TYPE_ZONE_YELL
)
86 sLog
.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Type %u but this Chat Type does not exist.",i
,temp
.Type
);
90 if (!sEmotesStore
.LookupEntry(temp
.Emote
))
91 sLog
.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Emote %u but emote does not exist.",i
,temp
.Emote
);
94 m_CreatureEventAI_TextMap
[i
] = temp
;
96 } while (result
->NextRow());
101 sLog
.outString(">> Loaded %u additional CreatureEventAI Texts data.", count
);
108 sLog
.outString(">> Loaded 0 additional CreatureEventAI Texts data. DB table `creature_ai_texts` is empty.");
113 // -------------------
114 void CreatureEventAIMgr::LoadCreatureEventAI_Summons()
117 //Drop Existing EventSummon Map
118 m_CreatureEventAI_Summon_Map
.clear();
120 //Gather additional data for EventAI
121 QueryResult
*result
= WorldDatabase
.PQuery("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM creature_ai_summons");
124 barGoLink
bar(result
->GetRowCount());
130 Field
*fields
= result
->Fetch();
132 CreatureEventAI_Summon temp
;
134 uint32 i
= fields
[0].GetUInt32();
135 temp
.position_x
= fields
[1].GetFloat();
136 temp
.position_y
= fields
[2].GetFloat();
137 temp
.position_z
= fields
[3].GetFloat();
138 temp
.orientation
= fields
[4].GetFloat();
139 temp
.SpawnTimeSecs
= fields
[5].GetUInt32();
141 if(!MaNGOS::IsValidMapCoord(temp
.position_x
,temp
.position_y
,temp
.position_z
,temp
.orientation
))
143 sLog
.outErrorDb("CreatureEventAI: Summon id %u have wrong coordinates (%f,%f,%f,%f), skipping.", i
,temp
.position_x
,temp
.position_y
,temp
.position_z
,temp
.orientation
);
148 m_CreatureEventAI_Summon_Map
[i
] = temp
;
150 }while (result
->NextRow());
155 sLog
.outString(">> Loaded %u CreatureEventAI summon definitions", Count
);
161 sLog
.outString(">> Loaded 0 CreatureEventAI Summon definitions. DB table `creature_ai_summons` is empty.");
166 // -------------------
167 void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
169 //Drop Existing EventAI List
170 m_CreatureEventAI_Event_Map
.clear();
173 QueryResult
*result
= WorldDatabase
.PQuery("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, "
174 "event_param1, event_param2, event_param3, event_param4, "
175 "action1_type, action1_param1, action1_param2, action1_param3, "
176 "action2_type, action2_param1, action2_param2, action2_param3, "
177 "action3_type, action3_param1, action3_param2, action3_param3 "
178 "FROM creature_ai_scripts");
181 barGoLink
bar(result
->GetRowCount());
187 Field
*fields
= result
->Fetch();
189 CreatureEventAI_Event temp
;
190 temp
.event_id
= EventAI_Type(fields
[0].GetUInt32());
191 uint32 i
= temp
.event_id
;
193 temp
.creature_id
= fields
[1].GetUInt32();
194 uint32 creature_id
= temp
.creature_id
;
196 uint32 e_type
= fields
[2].GetUInt32();
197 //Report any errors in event
198 if (e_type
>= EVENT_T_END
)
200 sLog
.outErrorDb("CreatureEventAI: Event %u have wrong type (%u), skipping.", i
,e_type
);
203 temp
.event_type
= EventAI_Type(e_type
);
205 temp
.event_inverse_phase_mask
= fields
[3].GetUInt32();
206 temp
.event_chance
= fields
[4].GetUInt8();
207 temp
.event_flags
= fields
[5].GetUInt8();
208 temp
.raw
.param1
= fields
[6].GetUInt32();
209 temp
.raw
.param2
= fields
[7].GetUInt32();
210 temp
.raw
.param3
= fields
[8].GetUInt32();
211 temp
.raw
.param4
= fields
[9].GetUInt32();
213 //Creature does not exist in database
214 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(temp
.creature_id
))
216 sLog
.outErrorDb("CreatureEventAI: Event %u has script for non-existing creature entry (%u), skipping.", i
, temp
.creature_id
);
220 //No chance of this event occuring
221 if (temp
.event_chance
== 0)
222 sLog
.outErrorDb("CreatureEventAI: Event %u has 0 percent chance. Event will never trigger!", i
);
223 //Chance above 100, force it to be 100
224 else if (temp
.event_chance
> 100)
226 sLog
.outErrorDb("CreatureEventAI: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp
.creature_id
, i
);
227 temp
.event_chance
= 100;
230 //Individual event checks
231 switch (temp
.event_type
)
234 case EVENT_T_TIMER_OOC
:
235 if (temp
.timer
.initialMax
< temp
.timer
.initialMin
)
236 sLog
.outErrorDb("CreatureEventAI: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp
.creature_id
, i
);
237 if (temp
.timer
.repeatMax
< temp
.timer
.repeatMin
)
238 sLog
.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
242 case EVENT_T_TARGET_HP
:
243 case EVENT_T_TARGET_MANA
:
244 if (temp
.percent_range
.percentMax
> 100)
245 sLog
.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp
.creature_id
, i
);
247 if (temp
.percent_range
.percentMax
<= temp
.percent_range
.percentMin
)
248 sLog
.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp
.creature_id
, i
);
250 if (temp
.event_flags
& EFLAG_REPEATABLE
&& !temp
.percent_range
.repeatMin
&& !temp
.percent_range
.repeatMax
)
252 sLog
.outErrorDb("CreatureEventAI: Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp
.creature_id
, i
);
253 temp
.event_flags
&= ~EFLAG_REPEATABLE
;
256 case EVENT_T_SPELLHIT
:
257 if (temp
.spell_hit
.spellId
)
259 SpellEntry
const* pSpell
= sSpellStore
.LookupEntry(temp
.spell_hit
.spellId
);
262 sLog
.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp
.creature_id
, temp
.spell_hit
.spellId
, i
);
266 if ((temp
.spell_hit
.schoolMask
& pSpell
->SchoolMask
) != pSpell
->SchoolMask
)
267 sLog
.outErrorDb("CreatureEventAI: Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp
.creature_id
, temp
.spell_hit
.schoolMask
, i
);
270 if (!temp
.spell_hit
.schoolMask
)
271 sLog
.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp
.creature_id
, temp
.spell_hit
.schoolMask
, i
);
273 if (temp
.spell_hit
.repeatMax
< temp
.spell_hit
.repeatMin
)
274 sLog
.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
277 if (temp
.range
.maxDist
< temp
.range
.minDist
)
278 sLog
.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (MaxDist < MinDist). Event will never repeat.", temp
.creature_id
, i
);
279 if (temp
.range
.repeatMax
< temp
.range
.repeatMin
)
280 sLog
.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
282 case EVENT_T_OOC_LOS
:
283 if (temp
.ooc_los
.repeatMax
< temp
.ooc_los
.repeatMin
)
284 sLog
.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
286 case EVENT_T_SPAWNED
:
287 switch(temp
.spawned
.condition
)
289 case SPAWNED_EVENT_ALWAY
:
291 case SPAWNED_EVENT_MAP
:
292 if(!sMapStore
.LookupEntry(temp
.spawned
.conditionValue1
))
293 sLog
.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'map specific' but with not existed map (%u) in param2. Event will never repeat.", temp
.creature_id
, i
, temp
.spawned
.condition
, temp
.spawned
.conditionValue1
);
295 case SPAWNED_EVENT_ZONE
:
296 if(!GetAreaEntryByAreaID(temp
.spawned
.conditionValue1
))
297 sLog
.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'area specific' but with not existed area (%u) in param2. Event will never repeat.", temp
.creature_id
, i
, temp
.spawned
.condition
, temp
.spawned
.conditionValue1
);
299 sLog
.outErrorDb("CreatureEventAI: Creature %u are using invalid spawned event %u mode (%u) in param1", temp
.creature_id
, i
, temp
.spawned
.condition
);
303 case EVENT_T_FRIENDLY_HP
:
304 if (temp
.friendly_hp
.repeatMax
< temp
.friendly_hp
.repeatMin
)
305 sLog
.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
307 case EVENT_T_FRIENDLY_IS_CC
:
308 if (temp
.friendly_is_cc
.repeatMax
< temp
.friendly_is_cc
.repeatMin
)
309 sLog
.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
311 case EVENT_T_FRIENDLY_MISSING_BUFF
:
313 SpellEntry
const* pSpell
= sSpellStore
.LookupEntry(temp
.spell_hit
.spellId
);
316 sLog
.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp
.creature_id
, temp
.spell_hit
.spellId
, i
);
319 if (temp
.friendly_buff
.repeatMax
< temp
.friendly_buff
.repeatMin
)
320 sLog
.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
324 if (temp
.kill
.repeatMax
< temp
.kill
.repeatMin
)
325 sLog
.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
327 case EVENT_T_TARGET_CASTING
:
328 if (temp
.target_casting
.repeatMax
< temp
.target_casting
.repeatMin
)
329 sLog
.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
331 case EVENT_T_SUMMONED_UNIT
:
332 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(temp
.summon_unit
.creatureId
))
333 sLog
.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed creature template id (%u) in param1, skipped.", temp
.creature_id
, i
, temp
.summon_unit
.creatureId
);
334 if (temp
.summon_unit
.repeatMax
< temp
.summon_unit
.repeatMin
)
335 sLog
.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
337 case EVENT_T_QUEST_ACCEPT
:
338 case EVENT_T_QUEST_COMPLETE
:
339 if (!objmgr
.GetQuestTemplate(temp
.quest
.questId
))
340 sLog
.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed qyest id (%u) in param1, skipped.", temp
.creature_id
, i
, temp
.quest
.questId
);
341 sLog
.outErrorDb("CreatureEventAI: Creature %u using not implemented event (%u) in event %u.", temp
.creature_id
, temp
.event_id
, i
);
347 case EVENT_T_REACHED_HOME
:
349 if (temp
.event_flags
& EFLAG_REPEATABLE
)
351 sLog
.outErrorDb("CreatureEventAI: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp
.creature_id
, i
);
352 temp
.event_flags
&= ~EFLAG_REPEATABLE
;
358 case EVENT_T_RECEIVE_EMOTE
:
360 if (!sEmotesTextStore
.LookupEntry(temp
.receive_emote
.emoteId
))
362 sLog
.outErrorDb("CreatureEventAI: Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.",temp
.creature_id
, i
, temp
.receive_emote
.emoteId
);
366 if (!PlayerCondition::IsValid(ConditionType(temp
.receive_emote
.condition
), temp
.receive_emote
.conditionValue1
, temp
.receive_emote
.conditionValue2
))
368 sLog
.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp
.creature_id
, i
, temp
.receive_emote
.condition
);
372 if (!(temp
.event_flags
& EFLAG_REPEATABLE
))
374 sLog
.outErrorDb("CreatureEventAI: Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp
.creature_id
, i
);
375 temp
.event_flags
|= EFLAG_REPEATABLE
;
382 sLog
.outErrorDb("CreatureEventAI: Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp
.creature_id
, temp
.event_id
, i
);
386 for (uint32 j
= 0; j
< MAX_ACTIONS
; j
++)
388 uint16 action_type
= fields
[10+(j
*4)].GetUInt16();
389 if (action_type
>= ACTION_T_END
)
391 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u has incorrect action type (%u), replace by ACTION_T_NONE.", i
, j
+1, action_type
);
392 temp
.action
[j
].type
= ACTION_T_NONE
;
396 CreatureEventAI_Action
& action
= temp
.action
[j
];
398 action
.type
= EventAI_ActionType(action_type
);
399 action
.raw
.param1
= fields
[11+(j
*4)].GetUInt32();
400 action
.raw
.param2
= fields
[12+(j
*4)].GetUInt32();
401 action
.raw
.param3
= fields
[13+(j
*4)].GetUInt32();
403 //Report any errors in actions
410 if (action
.text
.TextId1
< 0)
412 if (m_CreatureEventAI_TextMap
.find(action
.text
.TextId1
) == m_CreatureEventAI_TextMap
.end())
413 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param1 refrences non-existing entry in texts table.", i
, j
+1);
415 if (action
.text
.TextId2
< 0)
417 if (m_CreatureEventAI_TextMap
.find(action
.text
.TextId2
) == m_CreatureEventAI_TextMap
.end())
418 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param2 refrences non-existing entry in texts table.", i
, j
+1);
420 if (!action
.text
.TextId1
)
421 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u has param2, but param1 is not set. Required for randomized text.", i
, j
+1);
423 if (action
.text
.TextId3
< 0)
425 if (m_CreatureEventAI_TextMap
.find(action
.text
.TextId3
) == m_CreatureEventAI_TextMap
.end())
426 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param3 refrences non-existing entry in texts table.", i
, j
+1);
428 if (!action
.text
.TextId1
|| !action
.text
.TextId2
)
429 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u has param3, but param1 and/or param2 is not set. Required for randomized text.", i
, j
+1);
433 case ACTION_T_SET_FACTION
:
434 if (action
.set_faction
.factionId
!=0 && !sFactionStore
.LookupEntry(action
.set_faction
.factionId
))
436 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent FactionId %u.", i
, j
+1, action
.set_faction
.factionId
);
437 action
.set_faction
.factionId
= 0;
440 case ACTION_T_MORPH_TO_ENTRY_OR_MODEL
:
441 if (action
.morph
.creatureId
!=0 || action
.morph
.modelId
!=0)
443 if (action
.morph
.creatureId
&& !sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.morph
.creatureId
))
445 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i
, j
+1, action
.morph
.creatureId
);
446 action
.morph
.creatureId
= 0;
449 if (action
.morph
.modelId
)
451 if (action
.morph
.creatureId
)
453 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u have unused ModelId %u with also set creature id %u.", i
, j
+1, action
.morph
.modelId
,action
.morph
.creatureId
);
454 action
.morph
.modelId
= 0;
456 else if (!sCreatureDisplayInfoStore
.LookupEntry(action
.morph
.modelId
))
458 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant ModelId %u.", i
, j
+1, action
.morph
.modelId
);
459 action
.morph
.modelId
= 0;
466 if (!sSoundEntriesStore
.LookupEntry(action
.sound
.soundId
))
467 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i
, j
+1, action
.sound
.soundId
);
470 if (!sEmotesStore
.LookupEntry(action
.emote
.emoteId
))
471 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i
, j
+1, action
.emote
.emoteId
);
473 case ACTION_T_RANDOM_SOUND
:
474 if (!sSoundEntriesStore
.LookupEntry(action
.random_sound
.soundId1
))
475 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses non-existant SoundID %u.", i
, j
+1, action
.random_sound
.soundId1
);
476 if (action
.random_sound
.soundId2
>= 0 && !sSoundEntriesStore
.LookupEntry(action
.random_sound
.soundId2
))
477 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses non-existant SoundID %u.", i
, j
+1, action
.random_sound
.soundId2
);
478 if (action
.random_sound
.soundId3
>= 0 && !sSoundEntriesStore
.LookupEntry(action
.random_sound
.soundId3
))
479 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses non-existant SoundID %u.", i
, j
+1, action
.random_sound
.soundId3
);
481 case ACTION_T_RANDOM_EMOTE
:
482 if (!sEmotesStore
.LookupEntry(action
.random_emote
.emoteId1
))
483 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i
, j
+1, action
.random_emote
.emoteId1
);
484 if (action
.random_emote
.emoteId2
>= 0 && !sEmotesStore
.LookupEntry(action
.random_emote
.emoteId2
))
485 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param2 (EmoteId: %u) are not valid.", i
, j
+1, action
.random_emote
.emoteId2
);
486 if (action
.random_emote
.emoteId3
>= 0 && !sEmotesStore
.LookupEntry(action
.random_emote
.emoteId3
))
487 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param3 (EmoteId: %u) are not valid.", i
, j
+1, action
.random_emote
.emoteId3
);
491 const SpellEntry
*spell
= sSpellStore
.LookupEntry(action
.cast
.spellId
);
493 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i
, j
+1, action
.cast
.spellId
);
494 /* FIXME: temp.raw.param3 not have event tipes with recovery time in it....
497 if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE)
499 //output as debug for now, also because there's no general rule all spells have RecoveryTime
500 if (temp.event_param3 < spell->RecoveryTime)
501 sLog.outDebug("CreatureEventAI: Event %u Action %u uses SpellID %u but cooldown is longer(%u) than minumum defined in event param3(%u).", i, j+1,action.cast.spellId, spell->RecoveryTime, temp.event_param3);
506 //Cast is always triggered if target is forced to cast on self
507 if (action
.cast
.castFlags
& CAST_FORCE_TARGET_SELF
)
508 action
.cast
.castFlags
|= CAST_TRIGGERED
;
510 if (action
.cast
.target
>= TARGET_T_END
)
511 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
514 case ACTION_T_SUMMON
:
515 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.summon
.creatureId
))
516 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i
, j
+1, action
.summon
.creatureId
);
518 if (action
.summon
.target
>= TARGET_T_END
)
519 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
521 case ACTION_T_THREAT_SINGLE_PCT
:
522 if (std::abs(action
.threat_single_pct
.percent
) > 100)
523 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i
, j
+1, action
.threat_single_pct
.percent
);
524 if (action
.threat_single_pct
.target
>= TARGET_T_END
)
525 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
527 case ACTION_T_THREAT_ALL_PCT
:
528 if (std::abs(action
.threat_all_pct
.percent
) > 100)
529 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i
, j
+1, action
.threat_all_pct
.percent
);
531 case ACTION_T_QUEST_EVENT
:
532 if (Quest
const* qid
= objmgr
.GetQuestTemplate(action
.quest_event
.questId
))
534 if (!qid
->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT
))
535 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i
, j
+1, action
.quest_event
.questId
);
538 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i
, j
+1, action
.quest_event
.questId
);
540 if (action
.quest_event
.target
>= TARGET_T_END
)
541 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
544 case ACTION_T_CAST_EVENT
:
545 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.cast_event
.creatureId
))
546 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i
, j
+1, action
.cast_event
.creatureId
);
547 if (!sSpellStore
.LookupEntry(action
.cast_event
.spellId
))
548 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i
, j
+1, action
.cast_event
.spellId
);
549 if (action
.cast_event
.target
>= TARGET_T_END
)
550 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
552 case ACTION_T_SET_UNIT_FIELD
:
553 if (action
.set_unit_field
.field
< OBJECT_END
|| action
.set_unit_field
.field
>= UNIT_END
)
554 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i
, j
+1);
555 if (action
.set_unit_field
.target
>= TARGET_T_END
)
556 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
558 case ACTION_T_SET_UNIT_FLAG
:
559 case ACTION_T_REMOVE_UNIT_FLAG
:
560 if (action
.unit_flag
.target
>= TARGET_T_END
)
561 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
563 case ACTION_T_SET_PHASE
:
564 if (action
.set_phase
.phase
>= MAX_PHASE
)
565 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase >= %u. Phase mask cannot be used past phase %u.", i
, j
+1, MAX_PHASE
, MAX_PHASE
-1);
567 case ACTION_T_INC_PHASE
:
568 if (action
.set_inc_phase
.step
== 0)
569 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u is incrementing phase by 0. Was this intended?", i
, j
+1);
570 else if (std::abs(action
.set_inc_phase
.step
) > MAX_PHASE
-1)
571 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u is change phase by too large for any use %i.", i
, j
+1, action
.set_inc_phase
.step
);
573 case ACTION_T_QUEST_EVENT_ALL
:
574 if (Quest
const* qid
= objmgr
.GetQuestTemplate(action
.quest_event_all
.questId
))
576 if (!qid
->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT
))
577 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i
, j
+1, action
.quest_event_all
.questId
);
580 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i
, j
+1, action
.quest_event_all
.questId
);
582 case ACTION_T_CAST_EVENT_ALL
:
583 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.cast_event_all
.creatureId
))
584 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i
, j
+1, action
.cast_event_all
.creatureId
);
585 if (!sSpellStore
.LookupEntry(action
.cast_event_all
.spellId
))
586 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i
, j
+1, action
.cast_event_all
.spellId
);
588 case ACTION_T_REMOVEAURASFROMSPELL
:
589 if (!sSpellStore
.LookupEntry(action
.remove_aura
.spellId
))
590 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i
, j
+1, action
.remove_aura
.spellId
);
591 if (action
.remove_aura
.target
>= TARGET_T_END
)
592 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
594 case ACTION_T_RANDOM_PHASE
: //PhaseId1, PhaseId2, PhaseId3
595 if (action
.random_phase
.phase1
>= MAX_PHASE
)
596 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase1 >= %u. Phase mask cannot be used past phase %u.", i
, j
+1, MAX_PHASE
, MAX_PHASE
-1);
597 if (action
.random_phase
.phase2
>= MAX_PHASE
)
598 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase2 >= %u. Phase mask cannot be used past phase %u.", i
, j
+1, MAX_PHASE
, MAX_PHASE
-1);
599 if (action
.random_phase
.phase3
>= MAX_PHASE
)
600 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase3 >= %u. Phase mask cannot be used past phase %u.", i
, j
+1, MAX_PHASE
, MAX_PHASE
-1);
602 case ACTION_T_RANDOM_PHASE_RANGE
: //PhaseMin, PhaseMax
603 if (action
.random_phase_range
.phaseMin
>= MAX_PHASE
)
604 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMin >= %u. Phase mask cannot be used past phase %u.", i
, j
+1, MAX_PHASE
, MAX_PHASE
-1);
605 if (action
.random_phase_range
.phaseMin
>= MAX_PHASE
)
606 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax >= %u. Phase mask cannot be used past phase %u.", i
, j
+1, MAX_PHASE
, MAX_PHASE
-1);
607 if (action
.random_phase_range
.phaseMin
>= action
.random_phase_range
.phaseMax
)
609 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax <= phaseMin.", i
, j
+1);
610 std::swap(action
.random_phase_range
.phaseMin
,action
.random_phase_range
.phaseMax
);
611 // equal case processed at call
614 case ACTION_T_SUMMON_ID
:
615 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.summon_id
.creatureId
))
616 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i
, j
+1, action
.summon_id
.creatureId
);
617 if (action
.summon_id
.target
>= TARGET_T_END
)
618 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
619 if (m_CreatureEventAI_Summon_Map
.find(action
.summon_id
.spawnId
) == m_CreatureEventAI_Summon_Map
.end())
620 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i
, j
+1, action
.summon_id
.spawnId
);
622 case ACTION_T_KILLED_MONSTER
:
623 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.killed_monster
.creatureId
))
624 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i
, j
+1, action
.killed_monster
.creatureId
);
625 if (action
.killed_monster
.target
>= TARGET_T_END
)
626 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
628 case ACTION_T_SET_INST_DATA
:
629 if (!(temp
.event_flags
& EFLAG_NORMAL
) && !(temp
.event_flags
& EFLAG_HEROIC
))
630 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i
, j
+1);
631 if (action
.set_inst_data
.value
> 4/*SPECIAL*/)
632 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i
, j
+1);
634 case ACTION_T_SET_INST_DATA64
:
635 if (!(temp
.event_flags
& EFLAG_NORMAL
) && !(temp
.event_flags
& EFLAG_HEROIC
))
636 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i
, j
+1);
637 if (action
.set_inst_data64
.target
>= TARGET_T_END
)
638 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
640 case ACTION_T_UPDATE_TEMPLATE
:
641 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.update_template
.creatureId
))
642 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i
, j
+1, action
.update_template
.creatureId
);
644 case ACTION_T_SET_SHEATH
:
645 if (action
.set_sheath
.sheath
>= MAX_SHEATH_STATE
)
647 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong sheath state %u.", i
, j
+1, action
.set_sheath
.sheath
);
648 action
.set_sheath
.sheath
= SHEATH_STATE_UNARMED
;
651 case ACTION_T_EVADE
: //No Params
652 case ACTION_T_FLEE_FOR_ASSIST
: //No Params
653 case ACTION_T_DIE
: //No Params
654 case ACTION_T_ZONE_COMBAT_PULSE
: //No Params
655 case ACTION_T_AUTO_ATTACK
: //AllowAttackState (0 = stop attack, anything else means continue attacking)
656 case ACTION_T_COMBAT_MOVEMENT
: //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking)
657 case ACTION_T_RANGED_MOVEMENT
: //Distance, Angle
658 case ACTION_T_CALL_FOR_HELP
: //Distance
661 case ACTION_T_RANDOM_SAY
:
662 case ACTION_T_RANDOM_YELL
:
663 case ACTION_T_RANDOM_TEXTEMOTE
:
664 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i
, j
+1);
667 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u have currently not checked at load action type (%u). Need check code update?", i
, j
+1, temp
.action
[j
].type
);
673 m_CreatureEventAI_Event_Map
[creature_id
].push_back(temp
);
675 } while (result
->NextRow());
680 sLog
.outString(">> Loaded %u CreatureEventAI scripts", Count
);
686 sLog
.outString(">> Loaded 0 CreatureEventAI scripts. DB table `creature_ai_scripts` is empty.");