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
.Query("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
.Query("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
.Query("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 case EVENT_T_TARGET_BUFFED
:
384 SpellEntry
const* pSpell
= sSpellStore
.LookupEntry(temp
.buffed
.spellId
);
387 sLog
.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp
.creature_id
, temp
.spell_hit
.spellId
, i
);
390 if (temp
.buffed
.repeatMax
< temp
.buffed
.repeatMin
)
391 sLog
.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp
.creature_id
, i
);
396 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
);
400 for (uint32 j
= 0; j
< MAX_ACTIONS
; j
++)
402 uint16 action_type
= fields
[10+(j
*4)].GetUInt16();
403 if (action_type
>= ACTION_T_END
)
405 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u has incorrect action type (%u), replace by ACTION_T_NONE.", i
, j
+1, action_type
);
406 temp
.action
[j
].type
= ACTION_T_NONE
;
410 CreatureEventAI_Action
& action
= temp
.action
[j
];
412 action
.type
= EventAI_ActionType(action_type
);
413 action
.raw
.param1
= fields
[11+(j
*4)].GetUInt32();
414 action
.raw
.param2
= fields
[12+(j
*4)].GetUInt32();
415 action
.raw
.param3
= fields
[13+(j
*4)].GetUInt32();
417 //Report any errors in actions
424 if (action
.text
.TextId1
< 0)
426 if (m_CreatureEventAI_TextMap
.find(action
.text
.TextId1
) == m_CreatureEventAI_TextMap
.end())
427 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param1 refrences non-existing entry in texts table.", i
, j
+1);
429 if (action
.text
.TextId2
< 0)
431 if (m_CreatureEventAI_TextMap
.find(action
.text
.TextId2
) == m_CreatureEventAI_TextMap
.end())
432 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param2 refrences non-existing entry in texts table.", i
, j
+1);
434 if (!action
.text
.TextId1
)
435 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u has param2, but param1 is not set. Required for randomized text.", i
, j
+1);
437 if (action
.text
.TextId3
< 0)
439 if (m_CreatureEventAI_TextMap
.find(action
.text
.TextId3
) == m_CreatureEventAI_TextMap
.end())
440 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param3 refrences non-existing entry in texts table.", i
, j
+1);
442 if (!action
.text
.TextId1
|| !action
.text
.TextId2
)
443 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u has param3, but param1 and/or param2 is not set. Required for randomized text.", i
, j
+1);
447 case ACTION_T_SET_FACTION
:
448 if (action
.set_faction
.factionId
!=0 && !sFactionStore
.LookupEntry(action
.set_faction
.factionId
))
450 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent FactionId %u.", i
, j
+1, action
.set_faction
.factionId
);
451 action
.set_faction
.factionId
= 0;
454 case ACTION_T_MORPH_TO_ENTRY_OR_MODEL
:
455 if (action
.morph
.creatureId
!=0 || action
.morph
.modelId
!=0)
457 if (action
.morph
.creatureId
&& !sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.morph
.creatureId
))
459 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i
, j
+1, action
.morph
.creatureId
);
460 action
.morph
.creatureId
= 0;
463 if (action
.morph
.modelId
)
465 if (action
.morph
.creatureId
)
467 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
);
468 action
.morph
.modelId
= 0;
470 else if (!sCreatureDisplayInfoStore
.LookupEntry(action
.morph
.modelId
))
472 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant ModelId %u.", i
, j
+1, action
.morph
.modelId
);
473 action
.morph
.modelId
= 0;
479 if (!sSoundEntriesStore
.LookupEntry(action
.sound
.soundId
))
480 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i
, j
+1, action
.sound
.soundId
);
483 if (!sEmotesStore
.LookupEntry(action
.emote
.emoteId
))
484 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i
, j
+1, action
.emote
.emoteId
);
486 case ACTION_T_RANDOM_SOUND
:
487 if (!sSoundEntriesStore
.LookupEntry(action
.random_sound
.soundId1
))
488 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses non-existant SoundID %u.", i
, j
+1, action
.random_sound
.soundId1
);
489 if (action
.random_sound
.soundId2
>= 0 && !sSoundEntriesStore
.LookupEntry(action
.random_sound
.soundId2
))
490 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses non-existant SoundID %u.", i
, j
+1, action
.random_sound
.soundId2
);
491 if (action
.random_sound
.soundId3
>= 0 && !sSoundEntriesStore
.LookupEntry(action
.random_sound
.soundId3
))
492 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses non-existant SoundID %u.", i
, j
+1, action
.random_sound
.soundId3
);
494 case ACTION_T_RANDOM_EMOTE
:
495 if (!sEmotesStore
.LookupEntry(action
.random_emote
.emoteId1
))
496 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i
, j
+1, action
.random_emote
.emoteId1
);
497 if (action
.random_emote
.emoteId2
>= 0 && !sEmotesStore
.LookupEntry(action
.random_emote
.emoteId2
))
498 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param2 (EmoteId: %u) are not valid.", i
, j
+1, action
.random_emote
.emoteId2
);
499 if (action
.random_emote
.emoteId3
>= 0 && !sEmotesStore
.LookupEntry(action
.random_emote
.emoteId3
))
500 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param3 (EmoteId: %u) are not valid.", i
, j
+1, action
.random_emote
.emoteId3
);
504 const SpellEntry
*spell
= sSpellStore
.LookupEntry(action
.cast
.spellId
);
506 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i
, j
+1, action
.cast
.spellId
);
507 /* FIXME: temp.raw.param3 not have event tipes with recovery time in it....
510 if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE)
512 //output as debug for now, also because there's no general rule all spells have RecoveryTime
513 if (temp.event_param3 < spell->RecoveryTime)
514 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);
519 //Cast is always triggered if target is forced to cast on self
520 if (action
.cast
.castFlags
& CAST_FORCE_TARGET_SELF
)
521 action
.cast
.castFlags
|= CAST_TRIGGERED
;
523 if (action
.cast
.target
>= TARGET_T_END
)
524 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
527 case ACTION_T_SUMMON
:
528 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.summon
.creatureId
))
529 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i
, j
+1, action
.summon
.creatureId
);
531 if (action
.summon
.target
>= TARGET_T_END
)
532 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
534 case ACTION_T_THREAT_SINGLE_PCT
:
535 if (std::abs(action
.threat_single_pct
.percent
) > 100)
536 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i
, j
+1, action
.threat_single_pct
.percent
);
537 if (action
.threat_single_pct
.target
>= TARGET_T_END
)
538 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
540 case ACTION_T_THREAT_ALL_PCT
:
541 if (std::abs(action
.threat_all_pct
.percent
) > 100)
542 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i
, j
+1, action
.threat_all_pct
.percent
);
544 case ACTION_T_QUEST_EVENT
:
545 if (Quest
const* qid
= objmgr
.GetQuestTemplate(action
.quest_event
.questId
))
547 if (!qid
->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT
))
548 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
);
551 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i
, j
+1, action
.quest_event
.questId
);
553 if (action
.quest_event
.target
>= TARGET_T_END
)
554 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
557 case ACTION_T_CAST_EVENT
:
558 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.cast_event
.creatureId
))
559 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i
, j
+1, action
.cast_event
.creatureId
);
560 if (!sSpellStore
.LookupEntry(action
.cast_event
.spellId
))
561 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i
, j
+1, action
.cast_event
.spellId
);
562 if (action
.cast_event
.target
>= TARGET_T_END
)
563 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
565 case ACTION_T_SET_UNIT_FIELD
:
566 if (action
.set_unit_field
.field
< OBJECT_END
|| action
.set_unit_field
.field
>= UNIT_END
)
567 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i
, j
+1);
568 if (action
.set_unit_field
.target
>= TARGET_T_END
)
569 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
571 case ACTION_T_SET_UNIT_FLAG
:
572 case ACTION_T_REMOVE_UNIT_FLAG
:
573 if (action
.unit_flag
.target
>= TARGET_T_END
)
574 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
576 case ACTION_T_SET_PHASE
:
577 if (action
.set_phase
.phase
>= MAX_PHASE
)
578 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);
580 case ACTION_T_INC_PHASE
:
581 if (action
.set_inc_phase
.step
== 0)
582 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u is incrementing phase by 0. Was this intended?", i
, j
+1);
583 else if (std::abs(action
.set_inc_phase
.step
) > MAX_PHASE
-1)
584 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
);
586 case ACTION_T_QUEST_EVENT_ALL
:
587 if (Quest
const* qid
= objmgr
.GetQuestTemplate(action
.quest_event_all
.questId
))
589 if (!qid
->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT
))
590 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
);
593 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i
, j
+1, action
.quest_event_all
.questId
);
595 case ACTION_T_CAST_EVENT_ALL
:
596 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.cast_event_all
.creatureId
))
597 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i
, j
+1, action
.cast_event_all
.creatureId
);
598 if (!sSpellStore
.LookupEntry(action
.cast_event_all
.spellId
))
599 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i
, j
+1, action
.cast_event_all
.spellId
);
601 case ACTION_T_REMOVEAURASFROMSPELL
:
602 if (!sSpellStore
.LookupEntry(action
.remove_aura
.spellId
))
603 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i
, j
+1, action
.remove_aura
.spellId
);
604 if (action
.remove_aura
.target
>= TARGET_T_END
)
605 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
607 case ACTION_T_RANDOM_PHASE
: //PhaseId1, PhaseId2, PhaseId3
608 if (action
.random_phase
.phase1
>= MAX_PHASE
)
609 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);
610 if (action
.random_phase
.phase2
>= MAX_PHASE
)
611 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);
612 if (action
.random_phase
.phase3
>= MAX_PHASE
)
613 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);
615 case ACTION_T_RANDOM_PHASE_RANGE
: //PhaseMin, PhaseMax
616 if (action
.random_phase_range
.phaseMin
>= MAX_PHASE
)
617 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);
618 if (action
.random_phase_range
.phaseMin
>= MAX_PHASE
)
619 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);
620 if (action
.random_phase_range
.phaseMin
>= action
.random_phase_range
.phaseMax
)
622 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax <= phaseMin.", i
, j
+1);
623 std::swap(action
.random_phase_range
.phaseMin
,action
.random_phase_range
.phaseMax
);
624 // equal case processed at call
627 case ACTION_T_SUMMON_ID
:
628 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.summon_id
.creatureId
))
629 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i
, j
+1, action
.summon_id
.creatureId
);
630 if (action
.summon_id
.target
>= TARGET_T_END
)
631 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
632 if (m_CreatureEventAI_Summon_Map
.find(action
.summon_id
.spawnId
) == m_CreatureEventAI_Summon_Map
.end())
633 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i
, j
+1, action
.summon_id
.spawnId
);
635 case ACTION_T_KILLED_MONSTER
:
636 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.killed_monster
.creatureId
))
637 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i
, j
+1, action
.killed_monster
.creatureId
);
638 if (action
.killed_monster
.target
>= TARGET_T_END
)
639 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
641 case ACTION_T_SET_INST_DATA
:
642 if (!(temp
.event_flags
& EFLAG_NORMAL
) && !(temp
.event_flags
& EFLAG_HEROIC
))
643 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i
, j
+1);
644 if (action
.set_inst_data
.value
> 4/*SPECIAL*/)
645 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i
, j
+1);
647 case ACTION_T_SET_INST_DATA64
:
648 if (!(temp
.event_flags
& EFLAG_NORMAL
) && !(temp
.event_flags
& EFLAG_HEROIC
))
649 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i
, j
+1);
650 if (action
.set_inst_data64
.target
>= TARGET_T_END
)
651 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i
, j
+1);
653 case ACTION_T_UPDATE_TEMPLATE
:
654 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(action
.update_template
.creatureId
))
655 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i
, j
+1, action
.update_template
.creatureId
);
657 case ACTION_T_SET_SHEATH
:
658 if (action
.set_sheath
.sheath
>= MAX_SHEATH_STATE
)
660 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong sheath state %u.", i
, j
+1, action
.set_sheath
.sheath
);
661 action
.set_sheath
.sheath
= SHEATH_STATE_UNARMED
;
664 case ACTION_T_SET_INVINCIBILITY_HP_LEVEL
:
665 if(action
.invincibility_hp_level
.is_percent
)
667 if(action
.invincibility_hp_level
.hp_level
> 100)
669 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong percent value %u.", i
, j
+1, action
.invincibility_hp_level
.hp_level
);
670 action
.invincibility_hp_level
.hp_level
= 100;
674 case ACTION_T_EVADE
: //No Params
675 case ACTION_T_FLEE_FOR_ASSIST
: //No Params
676 case ACTION_T_DIE
: //No Params
677 case ACTION_T_ZONE_COMBAT_PULSE
: //No Params
678 case ACTION_T_FORCE_DESPAWN
: //No Params
679 case ACTION_T_AUTO_ATTACK
: //AllowAttackState (0 = stop attack, anything else means continue attacking)
680 case ACTION_T_COMBAT_MOVEMENT
: //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking)
681 case ACTION_T_RANGED_MOVEMENT
: //Distance, Angle
682 case ACTION_T_CALL_FOR_HELP
: //Distance
685 case ACTION_T_RANDOM_SAY
:
686 case ACTION_T_RANDOM_YELL
:
687 case ACTION_T_RANDOM_TEXTEMOTE
:
688 sLog
.outErrorDb("CreatureEventAI: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i
, j
+1);
691 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
);
697 m_CreatureEventAI_Event_Map
[creature_id
].push_back(temp
);
699 } while (result
->NextRow());
704 sLog
.outString(">> Loaded %u CreatureEventAI scripts", Count
);
710 sLog
.outString(">> Loaded 0 CreatureEventAI scripts. DB table `creature_ai_scripts` is empty.");