[9581] Fixed apply damage reduction to melee/ranged damage.
[getmangos.git] / src / game / CreatureEventAIMgr.cpp
blobb3b85e16f961a633e0052729119ed576279817d1
1 /*
2 * Copyright (C) 2005-2010 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
19 #include "Common.h"
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 "ObjectGuid.h"
28 #include "GridDefines.h"
30 INSTANTIATE_SINGLETON_1(CreatureEventAIMgr);
32 // -------------------
33 void CreatureEventAIMgr::LoadCreatureEventAI_Texts(bool check_entry_use)
35 // Drop Existing Text Map, only done once and we are ready to add data from multiple sources.
36 m_CreatureEventAI_TextMap.clear();
38 // Load EventAI Text
39 sObjectMgr.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...");
45 if (result)
47 barGoLink bar((int)result->GetRowCount());
48 uint32 count = 0;
52 bar.step();
53 Field* fields = result->Fetch();
54 StringTextData temp;
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();
62 // range negative
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);
66 continue;
69 // range negative (don't must be happen, loaded from same table)
70 if (!sObjectMgr.GetMangosStringLocale(i))
72 sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` not found",i);
73 continue;
76 if (temp.SoundId)
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);
88 if (temp.Emote)
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;
95 ++count;
96 } while (result->NextRow());
98 delete result;
100 if(check_entry_use)
101 CheckUnusedAITexts();
103 sLog.outString();
104 sLog.outString(">> Loaded %u additional CreatureEventAI Texts data.", count);
106 else
108 barGoLink bar(1);
109 bar.step();
110 sLog.outString();
111 sLog.outString(">> Loaded 0 additional CreatureEventAI Texts data. DB table `creature_ai_texts` is empty.");
115 void CreatureEventAIMgr::CheckUnusedAITexts()
117 std::set<int32> idx_set;
118 // check not used strings this is negative range
119 for(CreatureEventAI_TextMap::const_iterator itr = m_CreatureEventAI_TextMap.begin(); itr != m_CreatureEventAI_TextMap.end(); ++itr)
120 idx_set.insert(itr->first);
122 for(CreatureEventAI_Event_Map::const_iterator itr = m_CreatureEventAI_Event_Map.begin(); itr != m_CreatureEventAI_Event_Map.end(); ++itr)
124 for(size_t i = 0; i < itr->second.size(); ++i)
126 CreatureEventAI_Event const& event = itr->second[i];
128 for(int j = 0; j < MAX_ACTIONS; ++j)
130 CreatureEventAI_Action const& action = event.action[j];
131 switch(action.type)
133 case ACTION_T_TEXT:
135 for(int k = 0; k < 3; ++k)
136 if (action.text.TextId[k])
137 idx_set.erase(action.text.TextId[k]);
138 break;
140 default: break;
147 for(std::set<int32>::const_iterator itr = idx_set.begin(); itr != idx_set.end(); ++itr)
148 sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` but not used in EventAI scripts.",*itr);
151 // -------------------
152 void CreatureEventAIMgr::LoadCreatureEventAI_Summons(bool check_entry_use)
155 //Drop Existing EventSummon Map
156 m_CreatureEventAI_Summon_Map.clear();
158 // Gather additional data for EventAI
159 QueryResult *result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM creature_ai_summons");
160 if (result)
162 barGoLink bar((int)result->GetRowCount());
163 uint32 Count = 0;
167 bar.step();
168 Field *fields = result->Fetch();
170 CreatureEventAI_Summon temp;
172 uint32 i = fields[0].GetUInt32();
173 temp.position_x = fields[1].GetFloat();
174 temp.position_y = fields[2].GetFloat();
175 temp.position_z = fields[3].GetFloat();
176 temp.orientation = fields[4].GetFloat();
177 temp.SpawnTimeSecs = fields[5].GetUInt32();
179 if(!MaNGOS::IsValidMapCoord(temp.position_x,temp.position_y,temp.position_z,temp.orientation))
181 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);
182 continue;
185 //Add to map
186 m_CreatureEventAI_Summon_Map[i] = temp;
187 ++Count;
188 }while (result->NextRow());
190 delete result;
192 if(check_entry_use)
193 CheckUnusedAISummons();
195 sLog.outString();
196 sLog.outString(">> Loaded %u CreatureEventAI summon definitions", Count);
197 }else
199 barGoLink bar(1);
200 bar.step();
201 sLog.outString();
202 sLog.outString(">> Loaded 0 CreatureEventAI Summon definitions. DB table `creature_ai_summons` is empty.");
206 void CreatureEventAIMgr::CheckUnusedAISummons()
208 std::set<int32> idx_set;
209 // check not used strings this is negative range
210 for(CreatureEventAI_Summon_Map::const_iterator itr = m_CreatureEventAI_Summon_Map.begin(); itr != m_CreatureEventAI_Summon_Map.end(); ++itr)
211 idx_set.insert(itr->first);
213 for(CreatureEventAI_Event_Map::const_iterator itr = m_CreatureEventAI_Event_Map.begin(); itr != m_CreatureEventAI_Event_Map.end(); ++itr)
215 for(size_t i = 0; i < itr->second.size(); ++i)
217 CreatureEventAI_Event const& event = itr->second[i];
219 for(int j = 0; j < MAX_ACTIONS; ++j)
221 CreatureEventAI_Action const& action = event.action[j];
222 switch(action.type)
224 case ACTION_T_SUMMON_ID:
226 if (action.summon_id.spawnId)
227 idx_set.erase(action.summon_id.spawnId);
228 break;
230 default: break;
237 for(std::set<int32>::const_iterator itr = idx_set.begin(); itr != idx_set.end(); ++itr)
238 sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_summons` but not used in EventAI scripts.",*itr);
241 // -------------------
242 void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
244 //Drop Existing EventAI List
245 m_CreatureEventAI_Event_Map.clear();
247 // Gather event data
248 QueryResult *result = WorldDatabase.Query("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, "
249 "event_param1, event_param2, event_param3, event_param4, "
250 "action1_type, action1_param1, action1_param2, action1_param3, "
251 "action2_type, action2_param1, action2_param2, action2_param3, "
252 "action3_type, action3_param1, action3_param2, action3_param3 "
253 "FROM creature_ai_scripts");
254 if (result)
256 barGoLink bar((int)result->GetRowCount());
257 uint32 Count = 0;
261 bar.step();
262 Field *fields = result->Fetch();
264 CreatureEventAI_Event temp;
265 temp.event_id = EventAI_Type(fields[0].GetUInt32());
266 uint32 i = temp.event_id;
268 temp.creature_id = fields[1].GetUInt32();
269 uint32 creature_id = temp.creature_id;
271 uint32 e_type = fields[2].GetUInt32();
272 //Report any errors in event
273 if (e_type >= EVENT_T_END)
275 sLog.outErrorDb("CreatureEventAI: Event %u have wrong type (%u), skipping.", i,e_type);
276 continue;
278 temp.event_type = EventAI_Type(e_type);
280 temp.event_inverse_phase_mask = fields[3].GetUInt32();
281 temp.event_chance = fields[4].GetUInt8();
282 temp.event_flags = fields[5].GetUInt8();
283 temp.raw.param1 = fields[6].GetUInt32();
284 temp.raw.param2 = fields[7].GetUInt32();
285 temp.raw.param3 = fields[8].GetUInt32();
286 temp.raw.param4 = fields[9].GetUInt32();
288 //Creature does not exist in database
289 if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id))
291 sLog.outErrorDb("CreatureEventAI: Event %u has script for non-existing creature entry (%u), skipping.", i, temp.creature_id);
292 continue;
295 //No chance of this event occuring
296 if (temp.event_chance == 0)
297 sLog.outErrorDb("CreatureEventAI: Event %u has 0 percent chance. Event will never trigger!", i);
298 //Chance above 100, force it to be 100
299 else if (temp.event_chance > 100)
301 sLog.outErrorDb("CreatureEventAI: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i);
302 temp.event_chance = 100;
305 //Individual event checks
306 switch (temp.event_type)
308 case EVENT_T_TIMER:
309 case EVENT_T_TIMER_OOC:
310 if (temp.timer.initialMax < temp.timer.initialMin)
311 sLog.outErrorDb("CreatureEventAI: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i);
312 if (temp.timer.repeatMax < temp.timer.repeatMin)
313 sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
314 break;
315 case EVENT_T_HP:
316 case EVENT_T_MANA:
317 case EVENT_T_TARGET_HP:
318 case EVENT_T_TARGET_MANA:
319 if (temp.percent_range.percentMax > 100)
320 sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i);
322 if (temp.percent_range.percentMax <= temp.percent_range.percentMin)
323 sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i);
325 if (temp.event_flags & EFLAG_REPEATABLE && !temp.percent_range.repeatMin && !temp.percent_range.repeatMax)
327 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);
328 temp.event_flags &= ~EFLAG_REPEATABLE;
330 break;
331 case EVENT_T_SPELLHIT:
332 if (temp.spell_hit.spellId)
334 SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId);
335 if (!pSpell)
337 sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i);
338 continue;
341 if ((temp.spell_hit.schoolMask & pSpell->SchoolMask) != pSpell->SchoolMask)
342 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);
345 if (!temp.spell_hit.schoolMask)
346 sLog.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.spell_hit.schoolMask, i);
348 if (temp.spell_hit.repeatMax < temp.spell_hit.repeatMin)
349 sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
350 break;
351 case EVENT_T_RANGE:
352 if (temp.range.maxDist < temp.range.minDist)
353 sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (MaxDist < MinDist). Event will never repeat.", temp.creature_id, i);
354 if (temp.range.repeatMax < temp.range.repeatMin)
355 sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
356 break;
357 case EVENT_T_OOC_LOS:
358 if (temp.ooc_los.repeatMax < temp.ooc_los.repeatMin)
359 sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
360 break;
361 case EVENT_T_SPAWNED:
362 switch(temp.spawned.condition)
364 case SPAWNED_EVENT_ALWAY:
365 break;
366 case SPAWNED_EVENT_MAP:
367 if(!sMapStore.LookupEntry(temp.spawned.conditionValue1))
368 sLog.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'map specific' but map (param2: %u) does not exist. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1);
369 break;
370 case SPAWNED_EVENT_ZONE:
371 if(!GetAreaEntryByAreaID(temp.spawned.conditionValue1))
372 sLog.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'area specific' but area (param2: %u) does not exist. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1);
373 default:
374 sLog.outErrorDb("CreatureEventAI: Creature %u are using invalid spawned event %u mode (%u) in param1", temp.creature_id, i, temp.spawned.condition);
375 break;
377 break;
378 case EVENT_T_FRIENDLY_HP:
379 if (temp.friendly_hp.repeatMax < temp.friendly_hp.repeatMin)
380 sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
381 break;
382 case EVENT_T_FRIENDLY_IS_CC:
383 if (temp.friendly_is_cc.repeatMax < temp.friendly_is_cc.repeatMin)
384 sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
385 break;
386 case EVENT_T_FRIENDLY_MISSING_BUFF:
388 SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId);
389 if (!pSpell)
391 sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i);
392 continue;
394 if (temp.friendly_buff.repeatMax < temp.friendly_buff.repeatMin)
395 sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
396 break;
398 case EVENT_T_KILL:
399 if (temp.kill.repeatMax < temp.kill.repeatMin)
400 sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
401 break;
402 case EVENT_T_TARGET_CASTING:
403 if (temp.target_casting.repeatMax < temp.target_casting.repeatMin)
404 sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
405 break;
406 case EVENT_T_SUMMONED_UNIT:
407 case EVENT_T_SUMMONED_JUST_DIED:
408 case EVENT_T_SUMMONED_JUST_DESPAWN:
409 if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.summoned.creatureId))
410 sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed creature template id (%u) in param1, skipped.", temp.creature_id, i, temp.summoned.creatureId);
411 if (temp.summoned.repeatMax < temp.summoned.repeatMin)
412 sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
413 break;
414 case EVENT_T_QUEST_ACCEPT:
415 case EVENT_T_QUEST_COMPLETE:
416 if (!sObjectMgr.GetQuestTemplate(temp.quest.questId))
417 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);
418 sLog.outErrorDb("CreatureEventAI: Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i);
419 continue;
421 case EVENT_T_AGGRO:
422 case EVENT_T_DEATH:
423 case EVENT_T_EVADE:
424 case EVENT_T_REACHED_HOME:
426 if (temp.event_flags & EFLAG_REPEATABLE)
428 sLog.outErrorDb("CreatureEventAI: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i);
429 temp.event_flags &= ~EFLAG_REPEATABLE;
432 break;
435 case EVENT_T_RECEIVE_EMOTE:
437 if (!sEmotesTextStore.LookupEntry(temp.receive_emote.emoteId))
439 sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.",temp.creature_id, i, temp.receive_emote.emoteId);
440 continue;
443 if (!PlayerCondition::IsValid(ConditionType(temp.receive_emote.condition), temp.receive_emote.conditionValue1, temp.receive_emote.conditionValue2))
445 sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp.creature_id, i, temp.receive_emote.condition);
446 continue;
449 if (!(temp.event_flags & EFLAG_REPEATABLE))
451 sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp.creature_id, i);
452 temp.event_flags |= EFLAG_REPEATABLE;
455 break;
458 case EVENT_T_BUFFED:
459 case EVENT_T_TARGET_BUFFED:
461 SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.buffed.spellId);
462 if (!pSpell)
464 sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i);
465 continue;
467 if (temp.buffed.repeatMax < temp.buffed.repeatMin)
468 sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
469 break;
472 default:
473 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);
474 break;
477 for (uint32 j = 0; j < MAX_ACTIONS; j++)
479 uint16 action_type = fields[10+(j*4)].GetUInt16();
480 if (action_type >= ACTION_T_END)
482 sLog.outErrorDb("CreatureEventAI: Event %u Action %u has incorrect action type (%u), replace by ACTION_T_NONE.", i, j+1, action_type);
483 temp.action[j].type = ACTION_T_NONE;
484 continue;
487 CreatureEventAI_Action& action = temp.action[j];
489 action.type = EventAI_ActionType(action_type);
490 action.raw.param1 = fields[11+(j*4)].GetUInt32();
491 action.raw.param2 = fields[12+(j*4)].GetUInt32();
492 action.raw.param3 = fields[13+(j*4)].GetUInt32();
494 //Report any errors in actions
495 switch (action.type)
497 case ACTION_T_NONE:
498 break;
499 case ACTION_T_TEXT:
501 bool not_set = false;
502 for(int k = 0; k < 3; ++k)
504 if (action.text.TextId[k])
506 if (k > 0 && not_set)
507 sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param%d, but it follow after not set param. Required for randomized text.", i, j+1, k+1);
509 if(!action.text.TextId[k])
510 not_set = true;
511 // range negative
512 else if(action.text.TextId[k] > MIN_CREATURE_AI_TEXT_STRING_ID || action.text.TextId[k] <= MAX_CREATURE_AI_TEXT_STRING_ID)
514 sLog.outErrorDb("CreatureEventAI: Event %u Action %u param%d references out-of-range entry (%i) in texts table.", i, j+1, k+1, action.text.TextId[k]);
515 action.text.TextId[k] = 0;
517 else if (m_CreatureEventAI_TextMap.find(action.text.TextId[k]) == m_CreatureEventAI_TextMap.end())
519 sLog.outErrorDb("CreatureEventAI: Event %u Action %u param%d references non-existing entry (%i) in texts table.", i, j+1, k+1, action.text.TextId[k]);
520 action.text.TextId[k] = 0;
524 break;
526 case ACTION_T_SET_FACTION:
527 if (action.set_faction.factionId !=0 && !sFactionStore.LookupEntry(action.set_faction.factionId))
529 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent FactionId %u.", i, j+1, action.set_faction.factionId);
530 action.set_faction.factionId = 0;
532 break;
533 case ACTION_T_MORPH_TO_ENTRY_OR_MODEL:
534 if (action.morph.creatureId !=0 || action.morph.modelId !=0)
536 if (action.morph.creatureId && !sCreatureStorage.LookupEntry<CreatureInfo>(action.morph.creatureId))
538 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i, j+1, action.morph.creatureId);
539 action.morph.creatureId = 0;
542 if (action.morph.modelId)
544 if (action.morph.creatureId)
546 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);
547 action.morph.modelId = 0;
549 else if (!sCreatureDisplayInfoStore.LookupEntry(action.morph.modelId))
551 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant ModelId %u.", i, j+1, action.morph.modelId);
552 action.morph.modelId = 0;
556 break;
557 case ACTION_T_SOUND:
558 if (!sSoundEntriesStore.LookupEntry(action.sound.soundId))
559 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i, j+1, action.sound.soundId);
560 break;
561 case ACTION_T_EMOTE:
562 if (!sEmotesStore.LookupEntry(action.emote.emoteId))
563 sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, action.emote.emoteId);
564 break;
565 case ACTION_T_RANDOM_SOUND:
566 if (!sSoundEntriesStore.LookupEntry(action.random_sound.soundId1))
567 sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId1);
568 if (action.random_sound.soundId2 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId2))
569 sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId2);
570 if (action.random_sound.soundId3 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId3))
571 sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId3);
572 break;
573 case ACTION_T_RANDOM_EMOTE:
574 if (!sEmotesStore.LookupEntry(action.random_emote.emoteId1))
575 sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId1);
576 if (action.random_emote.emoteId2 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId2))
577 sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId2);
578 if (action.random_emote.emoteId3 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId3))
579 sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId3);
580 break;
581 case ACTION_T_CAST:
583 const SpellEntry *spell = sSpellStore.LookupEntry(action.cast.spellId);
584 if (!spell)
585 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast.spellId);
586 /* FIXME: temp.raw.param3 not have event tipes with recovery time in it....
587 else
589 if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE)
591 //output as debug for now, also because there's no general rule all spells have RecoveryTime
592 if (temp.event_param3 < spell->RecoveryTime)
593 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);
598 //Cast is always triggered if target is forced to cast on self
599 if (action.cast.castFlags & CAST_FORCE_TARGET_SELF)
600 action.cast.castFlags |= CAST_TRIGGERED;
602 if (action.cast.target >= TARGET_T_END)
603 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
604 break;
606 case ACTION_T_SUMMON:
607 if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon.creatureId))
608 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.summon.creatureId);
610 if (action.summon.target >= TARGET_T_END)
611 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
612 break;
613 case ACTION_T_THREAT_SINGLE_PCT:
614 if (std::abs(action.threat_single_pct.percent) > 100)
615 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, action.threat_single_pct.percent);
616 if (action.threat_single_pct.target >= TARGET_T_END)
617 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
618 break;
619 case ACTION_T_THREAT_ALL_PCT:
620 if (std::abs(action.threat_all_pct.percent) > 100)
621 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, action.threat_all_pct.percent);
622 break;
623 case ACTION_T_QUEST_EVENT:
624 if (Quest const* qid = sObjectMgr.GetQuestTemplate(action.quest_event.questId))
626 if (!qid->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
627 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);
629 else
630 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event.questId);
632 if (action.quest_event.target >= TARGET_T_END)
633 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
635 break;
636 case ACTION_T_CAST_EVENT:
637 if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event.creatureId))
638 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.cast_event.creatureId);
639 if (!sSpellStore.LookupEntry(action.cast_event.spellId))
640 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast_event.spellId);
641 if (action.cast_event.target >= TARGET_T_END)
642 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
643 break;
644 case ACTION_T_SET_UNIT_FIELD:
645 if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END)
646 sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j+1);
647 if (action.set_unit_field.target >= TARGET_T_END)
648 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
649 break;
650 case ACTION_T_SET_UNIT_FLAG:
651 case ACTION_T_REMOVE_UNIT_FLAG:
652 if (action.unit_flag.target >= TARGET_T_END)
653 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
654 break;
655 case ACTION_T_SET_PHASE:
656 if (action.set_phase.phase >= MAX_PHASE)
657 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);
658 break;
659 case ACTION_T_INC_PHASE:
660 if (action.set_inc_phase.step == 0)
661 sLog.outErrorDb("CreatureEventAI: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j+1);
662 else if (std::abs(action.set_inc_phase.step) > MAX_PHASE-1)
663 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);
664 break;
665 case ACTION_T_QUEST_EVENT_ALL:
666 if (Quest const* qid = sObjectMgr.GetQuestTemplate(action.quest_event_all.questId))
668 if (!qid->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
669 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);
671 else
672 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event_all.questId);
673 break;
674 case ACTION_T_CAST_EVENT_ALL:
675 if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event_all.creatureId))
676 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.cast_event_all.creatureId);
677 if (!sSpellStore.LookupEntry(action.cast_event_all.spellId))
678 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast_event_all.spellId);
679 break;
680 case ACTION_T_REMOVEAURASFROMSPELL:
681 if (!sSpellStore.LookupEntry(action.remove_aura.spellId))
682 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.remove_aura.spellId);
683 if (action.remove_aura.target >= TARGET_T_END)
684 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
685 break;
686 case ACTION_T_RANDOM_PHASE: //PhaseId1, PhaseId2, PhaseId3
687 if (action.random_phase.phase1 >= MAX_PHASE)
688 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);
689 if (action.random_phase.phase2 >= MAX_PHASE)
690 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);
691 if (action.random_phase.phase3 >= MAX_PHASE)
692 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);
693 break;
694 case ACTION_T_RANDOM_PHASE_RANGE: //PhaseMin, PhaseMax
695 if (action.random_phase_range.phaseMin >= MAX_PHASE)
696 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);
697 if (action.random_phase_range.phaseMin >= MAX_PHASE)
698 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);
699 if (action.random_phase_range.phaseMin >= action.random_phase_range.phaseMax)
701 sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax <= phaseMin.", i, j+1);
702 std::swap(action.random_phase_range.phaseMin,action.random_phase_range.phaseMax);
703 // equal case processed at call
705 break;
706 case ACTION_T_SUMMON_ID:
707 if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon_id.creatureId))
708 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.summon_id.creatureId);
709 if (action.summon_id.target >= TARGET_T_END)
710 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
711 if (m_CreatureEventAI_Summon_Map.find(action.summon_id.spawnId) == m_CreatureEventAI_Summon_Map.end())
712 sLog.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j+1, action.summon_id.spawnId);
713 break;
714 case ACTION_T_KILLED_MONSTER:
715 if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.killed_monster.creatureId))
716 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.killed_monster.creatureId);
717 if (action.killed_monster.target >= TARGET_T_END)
718 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
719 break;
720 case ACTION_T_SET_INST_DATA:
721 if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL))
722 sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j+1);
723 if (action.set_inst_data.value > 4/*SPECIAL*/)
724 sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j+1);
725 break;
726 case ACTION_T_SET_INST_DATA64:
727 if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL))
728 sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j+1);
729 if (action.set_inst_data64.target >= TARGET_T_END)
730 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
731 break;
732 case ACTION_T_UPDATE_TEMPLATE:
733 if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.update_template.creatureId))
734 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.update_template.creatureId);
735 break;
736 case ACTION_T_SET_SHEATH:
737 if (action.set_sheath.sheath >= MAX_SHEATH_STATE)
739 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong sheath state %u.", i, j+1, action.set_sheath.sheath);
740 action.set_sheath.sheath = SHEATH_STATE_UNARMED;
742 break;
743 case ACTION_T_SET_INVINCIBILITY_HP_LEVEL:
744 if(action.invincibility_hp_level.is_percent)
746 if(action.invincibility_hp_level.hp_level > 100)
748 sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong percent value %u.", i, j+1, action.invincibility_hp_level.hp_level);
749 action.invincibility_hp_level.hp_level = 100;
752 break;
753 case ACTION_T_EVADE: //No Params
754 case ACTION_T_FLEE_FOR_ASSIST: //No Params
755 case ACTION_T_DIE: //No Params
756 case ACTION_T_ZONE_COMBAT_PULSE: //No Params
757 case ACTION_T_FORCE_DESPAWN: //No Params
758 case ACTION_T_AUTO_ATTACK: //AllowAttackState (0 = stop attack, anything else means continue attacking)
759 case ACTION_T_COMBAT_MOVEMENT: //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking)
760 case ACTION_T_RANGED_MOVEMENT: //Distance, Angle
761 case ACTION_T_CALL_FOR_HELP: //Distance
762 break;
764 case ACTION_T_RANDOM_SAY:
765 case ACTION_T_RANDOM_YELL:
766 case ACTION_T_RANDOM_TEXTEMOTE:
767 sLog.outErrorDb("CreatureEventAI: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j+1);
768 break;
769 default:
770 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);
771 break;
775 //Add to list
776 m_CreatureEventAI_Event_Map[creature_id].push_back(temp);
777 ++Count;
778 } while (result->NextRow());
780 delete result;
782 CheckUnusedAITexts();
783 CheckUnusedAISummons();
785 sLog.outString();
786 sLog.outString(">> Loaded %u CreatureEventAI scripts", Count);
787 }else
789 barGoLink bar(1);
790 bar.step();
791 sLog.outString();
792 sLog.outString(">> Loaded 0 CreatureEventAI scripts. DB table `creature_ai_scripts` is empty.");