aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/mangos.sql101
-rw-r--r--sql/updates/7622_01_mangos_creature_ai_scripts.sql30
-rw-r--r--sql/updates/7622_02_mangos_creature_ai_summons.sql14
-rw-r--r--sql/updates/7622_03_mangos_creature_ai_texts.sql22
-rw-r--r--src/bindings/scripts/ScriptMgr.cpp649
-rw-r--r--src/bindings/scripts/include/sc_creature.cpp40
-rw-r--r--src/bindings/scripts/include/sc_creature.h3
-rw-r--r--src/bindings/scripts/scripts/creature/mob_event_ai.cpp1524
-rw-r--r--src/bindings/scripts/scripts/creature/mob_event_ai.h219
-rw-r--r--src/game/Creature.h23
-rw-r--r--src/game/CreatureAI.cpp32
-rw-r--r--src/game/CreatureAI.h2
-rw-r--r--src/game/CreatureAIRegistry.cpp2
-rw-r--r--src/game/CreatureEventAI.cpp1661
-rw-r--r--src/game/CreatureEventAI.h309
-rw-r--r--src/game/CreatureEventAIMgr.cpp560
-rw-r--r--src/game/CreatureEventAIMgr.h46
-rw-r--r--src/game/CreatureGroups.cpp1
-rw-r--r--src/game/GridNotifiers.h113
-rw-r--r--src/game/InstanceData.h4
-rw-r--r--src/game/Makefile.am267
-rw-r--r--src/game/Object.h2
-rw-r--r--src/game/ObjectGridLoader.cpp1
-rw-r--r--src/game/Unit.h1
-rw-r--r--src/game/World.cpp10
-rw-r--r--win/VC71/game.vcproj12
-rw-r--r--win/VC80/game.vcproj16
-rw-r--r--win/VC90/game.vcproj16
28 files changed, 3190 insertions, 2490 deletions
diff --git a/sql/mangos.sql b/sql/mangos.sql
index b752d0d76ed..83ac68521e4 100644
--- a/sql/mangos.sql
+++ b/sql/mangos.sql
@@ -22,7 +22,7 @@
DROP TABLE IF EXISTS `db_version`;
CREATE TABLE `db_version` (
`version` varchar(120) default NULL,
- `required_7616_02_mangos_command` bit(1) default NULL
+ `required_7622_03_mangos_creature_ai_texts` bit(1) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes';
--
@@ -920,6 +920,105 @@ LOCK TABLES `disenchant_loot_template` WRITE;
UNLOCK TABLES;
--
+-- Table structure for table `creature_ai_scripts`
+--
+
+DROP TABLE IF EXISTS `creature_ai_scripts`;
+CREATE TABLE `creature_ai_scripts` (
+ `id` int(11) unsigned NOT NULL COMMENT 'Identifier' AUTO_INCREMENT,
+ `creature_id` int(11) unsigned NOT NULL default '0' COMMENT 'Creature Template Identifier',
+ `event_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Event Type',
+ `event_inverse_phase_mask` int(11) signed NOT NULL default '0' COMMENT 'Mask which phases this event will not trigger in',
+ `event_chance` int(3) unsigned NOT NULL default '100',
+ `event_flags` int(3) unsigned NOT NULL default '0',
+ `event_param1` int(11) signed NOT NULL default '0',
+ `event_param2` int(11) signed NOT NULL default '0',
+ `event_param3` int(11) signed NOT NULL default '0',
+ `event_param4` int(11) signed NOT NULL default '0',
+ `action1_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
+ `action1_param1` int(11) signed NOT NULL default '0',
+ `action1_param2` int(11) signed NOT NULL default '0',
+ `action1_param3` int(11) signed NOT NULL default '0',
+ `action2_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
+ `action2_param1` int(11) signed NOT NULL default '0',
+ `action2_param2` int(11) signed NOT NULL default '0',
+ `action2_param3` int(11) signed NOT NULL default '0',
+ `action3_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
+ `action3_param1` int(11) signed NOT NULL default '0',
+ `action3_param2` int(11) signed NOT NULL default '0',
+ `action3_param3` int(11) signed NOT NULL default '0',
+ `comment` varchar(255) NOT NULL default '' COMMENT 'Event Comment',
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='EventAI Scripts';
+
+--
+-- Dumping data for table `creature_ai_scripts`
+--
+
+LOCK TABLES `creature_ai_scripts` WRITE;
+/*!40000 ALTER TABLE `creature_ai_scripts` DISABLE KEYS */;
+/*!40000 ALTER TABLE `creature_ai_scripts` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `creature_ai_summons`
+--
+
+DROP TABLE IF EXISTS `creature_ai_summons`;
+CREATE TABLE `creature_ai_summons` (
+ `id` int(11) unsigned NOT NULL COMMENT 'Location Identifier' AUTO_INCREMENT,
+ `position_x` float NOT NULL default '0',
+ `position_y` float NOT NULL default '0',
+ `position_z` float NOT NULL default '0',
+ `orientation` float NOT NULL default '0',
+ `spawntimesecs` int(11) unsigned NOT NULL default '120',
+ `comment` varchar(255) NOT NULL default '' COMMENT 'Summon Comment',
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='EventAI Summoning Locations';
+
+--
+-- Dumping data for table `creature_ai_summons`
+--
+
+LOCK TABLES `creature_ai_summons` WRITE;
+/*!40000 ALTER TABLE `creature_ai_summons` DISABLE KEYS */;
+/*!40000 ALTER TABLE `creature_ai_summons` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `creature_ai_texts`
+--
+
+DROP TABLE IF EXISTS `creature_ai_texts`;
+CREATE TABLE `creature_ai_texts` (
+ `entry` mediumint(8) NOT NULL,
+ `content_default` text NOT NULL,
+ `content_loc1` text,
+ `content_loc2` text,
+ `content_loc3` text,
+ `content_loc4` text,
+ `content_loc5` text,
+ `content_loc6` text,
+ `content_loc7` text,
+ `content_loc8` text,
+ `sound` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ `type` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `language` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `emote` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `comment` text,
+ PRIMARY KEY (`entry`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Script Texts';
+
+--
+-- Dumping data for table `creature_ai_texts`
+--
+
+LOCK TABLES `creature_ai_texts` WRITE;
+/*!40000 ALTER TABLE `creature_ai_texts` DISABLE KEYS */;
+/*!40000 ALTER TABLE `creature_ai_texts` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
-- Table structure for table `event_scripts`
--
diff --git a/sql/updates/7622_01_mangos_creature_ai_scripts.sql b/sql/updates/7622_01_mangos_creature_ai_scripts.sql
new file mode 100644
index 00000000000..6050a8e0232
--- /dev/null
+++ b/sql/updates/7622_01_mangos_creature_ai_scripts.sql
@@ -0,0 +1,30 @@
+ALTER TABLE db_version CHANGE COLUMN required_7616_02_mangos_command required_7622_01_mangos_creature_ai_scripts bit;
+
+
+DROP TABLE IF EXISTS `creature_ai_scripts`;
+CREATE TABLE `creature_ai_scripts` (
+ `id` int(11) unsigned NOT NULL COMMENT 'Identifier' AUTO_INCREMENT,
+ `creature_id` int(11) unsigned NOT NULL default '0' COMMENT 'Creature Template Identifier',
+ `event_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Event Type',
+ `event_inverse_phase_mask` int(11) signed NOT NULL default '0' COMMENT 'Mask which phases this event will not trigger in',
+ `event_chance` int(3) unsigned NOT NULL default '100',
+ `event_flags` int(3) unsigned NOT NULL default '0',
+ `event_param1` int(11) signed NOT NULL default '0',
+ `event_param2` int(11) signed NOT NULL default '0',
+ `event_param3` int(11) signed NOT NULL default '0',
+ `event_param4` int(11) signed NOT NULL default '0',
+ `action1_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
+ `action1_param1` int(11) signed NOT NULL default '0',
+ `action1_param2` int(11) signed NOT NULL default '0',
+ `action1_param3` int(11) signed NOT NULL default '0',
+ `action2_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
+ `action2_param1` int(11) signed NOT NULL default '0',
+ `action2_param2` int(11) signed NOT NULL default '0',
+ `action2_param3` int(11) signed NOT NULL default '0',
+ `action3_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
+ `action3_param1` int(11) signed NOT NULL default '0',
+ `action3_param2` int(11) signed NOT NULL default '0',
+ `action3_param3` int(11) signed NOT NULL default '0',
+ `comment` varchar(255) NOT NULL default '' COMMENT 'Event Comment',
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='EventAI Scripts'; \ No newline at end of file
diff --git a/sql/updates/7622_02_mangos_creature_ai_summons.sql b/sql/updates/7622_02_mangos_creature_ai_summons.sql
new file mode 100644
index 00000000000..a46d933df91
--- /dev/null
+++ b/sql/updates/7622_02_mangos_creature_ai_summons.sql
@@ -0,0 +1,14 @@
+ALTER TABLE db_version CHANGE COLUMN required_7622_01_mangos_creature_ai_scripts required_7622_02_mangos_creature_ai_summons bit;
+
+
+DROP TABLE IF EXISTS `creature_ai_summons`;
+CREATE TABLE `creature_ai_summons` (
+ `id` int(11) unsigned NOT NULL COMMENT 'Location Identifier' AUTO_INCREMENT,
+ `position_x` float NOT NULL default '0',
+ `position_y` float NOT NULL default '0',
+ `position_z` float NOT NULL default '0',
+ `orientation` float NOT NULL default '0',
+ `spawntimesecs` int(11) unsigned NOT NULL default '120',
+ `comment` varchar(255) NOT NULL default '' COMMENT 'Summon Comment',
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='EventAI Summoning Locations'; \ No newline at end of file
diff --git a/sql/updates/7622_03_mangos_creature_ai_texts.sql b/sql/updates/7622_03_mangos_creature_ai_texts.sql
new file mode 100644
index 00000000000..d8cb6ccbeeb
--- /dev/null
+++ b/sql/updates/7622_03_mangos_creature_ai_texts.sql
@@ -0,0 +1,22 @@
+ALTER TABLE db_version CHANGE COLUMN required_7622_02_mangos_creature_ai_summons required_7622_03_mangos_creature_ai_texts bit;
+
+
+DROP TABLE IF EXISTS `creature_ai_texts`;
+CREATE TABLE `creature_ai_texts` (
+ `entry` mediumint(8) NOT NULL,
+ `content_default` text NOT NULL,
+ `content_loc1` text,
+ `content_loc2` text,
+ `content_loc3` text,
+ `content_loc4` text,
+ `content_loc5` text,
+ `content_loc6` text,
+ `content_loc7` text,
+ `content_loc8` text,
+ `sound` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ `type` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `language` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `emote` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `comment` text,
+ PRIMARY KEY (`entry`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Script Texts';
diff --git a/src/bindings/scripts/ScriptMgr.cpp b/src/bindings/scripts/ScriptMgr.cpp
index 70974f9ea11..8b7b8af72c9 100644
--- a/src/bindings/scripts/ScriptMgr.cpp
+++ b/src/bindings/scripts/ScriptMgr.cpp
@@ -8,7 +8,6 @@
#include "DBCStores.h"
#include "ObjectMgr.h"
#include "ProgressBar.h"
-#include "scripts/creature/mob_event_ai.h"
#define _FULLVERSION "TrinityScript"
@@ -31,18 +30,6 @@ struct StringTextData
uint32 Emote;
};
-// Enums used by StringTextData::Type
-enum ChatType
-{
- CHAT_TYPE_SAY = 0,
- CHAT_TYPE_YELL = 1,
- CHAT_TYPE_TEXT_EMOTE = 2,
- CHAT_TYPE_BOSS_EMOTE = 3,
- CHAT_TYPE_WHISPER = 4,
- CHAT_TYPE_BOSS_WHISPER = 5,
- CHAT_TYPE_ZONE_YELL = 6
-};
-
#define TEXT_SOURCE_RANGE -1000000 //the amount of entries each text source has available
// Text Maps
@@ -51,14 +38,6 @@ UNORDERED_MAP<int32, StringTextData> TextMap;
// Waypoint lists
std::list<PointMovement> PointMovementList;
-//Event AI structure. Used exclusivly by mob_event_ai.cpp (60 bytes each)
-UNORDERED_MAP<uint32, std::vector<EventAI_Event> > EventAI_Event_Map;
-
-//Event AI summon structure. Used exclusivly by mob_event_ai.cpp.
-UNORDERED_MAP<uint32, EventAI_Summon> EventAI_Summon_Map;
-
-uint32 EAI_ErrorLevel;
-
void FillSpellSummary();
void LoadOverridenSQLData();
void LoadOverridenDBCData();
@@ -74,7 +53,6 @@ extern void AddSC_boss_taerar();
extern void AddSC_boss_ysondre();
// -- Creature --
-extern void AddSC_mob_event();
extern void AddSC_generic_creature();
// -- Custom --
@@ -916,613 +894,6 @@ void LoadDatabase()
outstring_log(">> Loaded 0 Script Waypoints. DB table `script_waypoint` is empty.");
}
- //Gather additional data for EventAI
- result = TScriptDB.PQuery("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM eventai_summons");
-
- //Drop Existing EventSummon Map
- EventAI_Summon_Map.clear();
-
- outstring_log("TSCR: Loading EventAI Summons...");
- if (result)
- {
- barGoLink bar(result->GetRowCount());
- uint32 Count = 0;
-
- do
- {
- bar.step();
- Field *fields = result->Fetch();
-
- EventAI_Summon temp;
-
- uint32 i = fields[0].GetUInt32();
- temp.position_x = fields[1].GetFloat();
- temp.position_y = fields[2].GetFloat();
- temp.position_z = fields[3].GetFloat();
- temp.orientation = fields[4].GetFloat();
- temp.SpawnTimeSecs = fields[5].GetUInt32();
-
- //Add to map
- EventAI_Summon_Map[i] = temp;
- ++Count;
- }while (result->NextRow());
-
- delete result;
-
- outstring_log("");
- outstring_log(">> Loaded %u EventAI summon definitions", Count);
- }else
- {
- barGoLink bar(1);
- bar.step();
- outstring_log("");
- outstring_log(">> Loaded 0 EventAI Summon definitions. DB table `eventai_summons` is empty.");
- }
-
- //Drop Existing EventAI List
- EventAI_Event_Map.clear();
- uint64 uiEAICreatureCount = 0;
-
- result = TScriptDB.PQuery("SELECT COUNT(creature_id) FROM eventai_scripts GROUP BY creature_id");
- if (result)
- {
- uiEAICreatureCount = result->GetRowCount();
- delete result;
- }
-
- outstring_log("SD2: Loading EventAI scripts for %u creature(s)...", uiEAICreatureCount);
-
- //Gather event data
- result = TScriptDB.PQuery("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, "
- "event_param1, event_param2, event_param3, event_param4, "
- "action1_type, action1_param1, action1_param2, action1_param3, "
- "action2_type, action2_param1, action2_param2, action2_param3, "
- "action3_type, action3_param1, action3_param2, action3_param3 "
- "FROM eventai_scripts");
-
- outstring_log("SD2: Loading EventAI scripts for %u creature(s)...", uiEAICreatureCount);
- if (result)
- {
- barGoLink bar(result->GetRowCount());
- uint32 Count = 0;
-
- do
- {
- bar.step();
- Field *fields = result->Fetch();
-
- EventAI_Event temp;
-
- temp.event_id = fields[0].GetUInt32();
- uint32 i = temp.event_id;
- temp.creature_id = fields[1].GetUInt32();
- uint32 creature_id = temp.creature_id;
- temp.event_type = fields[2].GetUInt16();
- temp.event_inverse_phase_mask = fields[3].GetUInt32();
- temp.event_chance = fields[4].GetUInt8();
- temp.event_flags = fields[5].GetUInt8();
- temp.event_param1 = fields[6].GetUInt32();
- temp.event_param2 = fields[7].GetUInt32();
- temp.event_param3 = fields[8].GetUInt32();
- temp.event_param4 = fields[9].GetUInt32();
-
- //Creature does not exist in database
- if (!GetCreatureTemplateStore(temp.creature_id))
- {
- error_db_log("TSCR: Event %u has script for non-existing creature.", i);
- continue;
- }
-
- //Report any errors in event
- if (temp.event_type >= EVENT_T_END)
- {
- error_db_log("TSCR: Event %u has incorrect event type. Maybe DB requires updated version of SD2.", i);
- continue;
- }
-
- //No chance of this event occuring
- if (temp.event_chance == 0)
- error_db_log("TSCR: Event %u has 0 percent chance. Event will never trigger!", i);
-
- //Chance above 100, force it to be 100
- if (temp.event_chance > 100)
- {
- error_db_log("TSCR: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i);
- temp.event_chance = 100;
- }
-
- //Individual event checks
- switch (temp.event_type)
- {
- case EVENT_T_HP:
- case EVENT_T_MANA:
- case EVENT_T_TARGET_HP:
- {
- if (temp.event_param2 > 100)
- error_db_log("TSCR: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i);
-
- if (temp.event_param1 <= temp.event_param2)
- error_db_log("TSCR: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i);
-
- if (temp.event_flags & EFLAG_REPEATABLE && !temp.event_param3 && !temp.event_param4)
- {
- error_db_log("TSCR: 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);
- temp.event_flags &= ~EFLAG_REPEATABLE;
- }
- }
- break;
-
- case EVENT_T_SPELLHIT:
- {
- if (temp.event_param1)
- {
- SpellEntry const* pSpell = GetSpellStore()->LookupEntry(temp.event_param1);
- if (!pSpell)
- {
- error_db_log("TSCR: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.event_param1, i);
- continue;
- }
-
- if (temp.event_param2_s != -1 && temp.event_param2 != pSpell->SchoolMask)
- error_db_log("TSCR: 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.event_param1, i);
- }
-
- //TODO: fix this system with SPELL_SCHOOL_MASK. Current complicate things, using int32(-1) instead of just 0
- //SPELL_SCHOOL_MASK_NONE = 0 and does not exist, thus it can not ever trigger or be used in SpellHit()
- if (temp.event_param2_s != -1 && temp.event_param2_s > SPELL_SCHOOL_MASK_ALL)
- error_db_log("TSCR: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.event_param2, i);
-
- if (temp.event_param4 < temp.event_param3)
- error_db_log("TSCR: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
- }
- break;
-
- case EVENT_T_OOC_LOS:
- {
- if (temp.event_param2 > VISIBLE_RANGE || temp.event_param2 <= 0)
- {
- error_db_log("SD2: Creature %u are using event(%u), but param2 (MaxAllowedRange=%u) are not within allowed range.", temp.creature_id, i, temp.event_param2);
- temp.event_param2 = VISIBLE_RANGE;
- }
-
- if (temp.event_param3 == 0 && temp.event_param4 == 0 && temp.event_flags & EFLAG_REPEATABLE)
- {
- error_db_log("SD2: Creature %u are using event(%u) with EFLAG_REPEATABLE, but param3(RepeatMin) and param4(RepeatMax) are 0. Repeatable disabled.", temp.creature_id, i);
- temp.event_flags &= ~EFLAG_REPEATABLE;
- }
-
- if (temp.event_param4 < temp.event_param3)
- error_db_log("SD2: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
- }
- break;
-
- case EVENT_T_RANGE:
- case EVENT_T_FRIENDLY_HP:
- case EVENT_T_FRIENDLY_IS_CC:
- {
- if (temp.event_param4 < temp.event_param3)
- error_db_log("SD2: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
- }
- break;
-
- case EVENT_T_FRIENDLY_MISSING_BUFF:
- {
- if (!GetSpellStore()->LookupEntry(temp.event_param1))
- {
- error_db_log("SD2: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.event_param1, i);
- continue;
- }
-
- if (temp.event_param4 < temp.event_param3)
- error_db_log("TSCR: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
- }
- break;
-
- case EVENT_T_TIMER:
- case EVENT_T_TIMER_OOC:
- {
- if (temp.event_param2 < temp.event_param1)
- error_db_log("TSCR: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i);
-
- if (temp.event_param4 < temp.event_param3)
- error_db_log("TSCR: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
- }
- break;
-
- case EVENT_T_KILL:
- case EVENT_T_TARGET_CASTING:
- {
- if (temp.event_param2 < temp.event_param1)
- error_db_log("TSCR: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
- }
- break;
-
- case EVENT_T_SUMMONED_UNIT:
- {
- if (!GetCreatureTemplateStore(temp.event_param1))
- {
- error_db_log("SD2: Creature %u has non-existant creature entry (%u) defined in event %u.", temp.creature_id, temp.event_param1, i);
- continue;
- }
- }
- break;
-
- case EVENT_T_AGGRO:
- case EVENT_T_DEATH:
- case EVENT_T_EVADE:
- case EVENT_T_SPAWNED:
- case EVENT_T_REACHED_HOME:
- {
- if (temp.event_flags & EFLAG_REPEATABLE)
- {
- error_db_log("TSCR: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i);
- temp.event_flags &= ~EFLAG_REPEATABLE;
- }
- }
- break;
-
- case EVENT_T_RECEIVE_EMOTE:
- {
- //no good way to check for valid textEmote (enum TextEmotes)
-
- switch(temp.event_param2)
- {
- case CONDITION_AURA:
- if (!GetSpellStore()->LookupEntry(temp.event_param3))
- {
- error_db_log("SD2: Creature %u using event %u: param3 (CondValue1: %u) are not valid.",temp.creature_id, i, temp.event_param3);
- continue;
- }
- break;
- case CONDITION_TEAM:
- if (temp.event_param3 != HORDE || temp.event_param3 != ALLIANCE)
- {
- error_db_log("SD2: Creature %u using event %u: param3 (CondValue1: %u) are not valid.",temp.creature_id, i, temp.event_param3);
- continue;
- }
- break;
- case CONDITION_QUESTREWARDED:
- case CONDITION_QUESTTAKEN:
- if (!GetQuestTemplateStore(temp.event_param3))
- {
- error_db_log("SD2: Creature %u using event %u: param3 (CondValue1: %u) are not valid.",temp.creature_id, i, temp.event_param3);
- continue;
- }
- break;
- case CONDITION_ACTIVE_EVENT:
- if (temp.event_param3 !=
- (HOLIDAY_FIREWORKS_SPECTACULAR | HOLIDAY_FEAST_OF_WINTER_VEIL | HOLIDAY_NOBLEGARDEN |
- HOLIDAY_CHILDRENS_WEEK | HOLIDAY_CALL_TO_ARMS_AV | HOLIDAY_CALL_TO_ARMS_WG |
- HOLIDAY_CALL_TO_ARMS_AB | HOLIDAY_FISHING_EXTRAVAGANZA | HOLIDAY_HARVEST_FESTIVAL |
- HOLIDAY_HALLOWS_END | HOLIDAY_LUNAR_FESTIVAL | HOLIDAY_LOVE_IS_IN_THE_AIR |
- HOLIDAY_FIRE_FESTIVAL | HOLIDAY_CALL_TO_ARMS_ES | HOLIDAY_BREWFEST |
- HOLIDAY_DARKMOON_FAIRE_ELWYNN | HOLIDAY_DARKMOON_FAIRE_THUNDER | HOLIDAY_DARKMOON_FAIRE_SHATTRATH |
- HOLIDAY_CALL_TO_ARMS_SA | HOLIDAY_WOTLK_LAUNCH))
- {
- error_db_log("SD2: Creature %u using event %u: param3 (CondValue1: %u) are not valid.",temp.creature_id, i, temp.event_param3);
- continue;
- }
- break;
- case CONDITION_REPUTATION_RANK:
- if (!temp.event_param3)
- {
- error_db_log("SD2: Creature %u using event %u: param3 (CondValue1: %u) are missing.",temp.creature_id, i, temp.event_param3);
- continue;
- }
- if (temp.event_param4 > REP_EXALTED)
- {
- error_db_log("SD2: Creature %u using event %u: param4 (CondValue2: %u) are not valid.",temp.creature_id, i, temp.event_param4);
- continue;
- }
- break;
- case CONDITION_ITEM:
- case CONDITION_ITEM_EQUIPPED:
- case CONDITION_SKILL:
- if (!temp.event_param3)
- {
- error_db_log("SD2: Creature %u using event %u: param3 (CondValue1: %u) are missing.",temp.creature_id, i, temp.event_param3);
- continue;
- }
- if (!temp.event_param4)
- {
- error_db_log("SD2: Creature %u using event %u: param4 (CondValue2: %u) are missing.",temp.creature_id, i, temp.event_param4);
- continue;
- }
- break;
- case CONDITION_ZONEID:
- if (!temp.event_param3)
- {
- error_db_log("SD2: Creature %u using event %u: param3 (CondValue1: %u) are missing.",temp.creature_id, i, temp.event_param3);
- continue;
- }
- break;
- case CONDITION_NONE:
- break;
- default:
- {
- error_db_log("SD2: Creature %u using event %u: param2 (Condition: %u) are not valid/not implemented for script.",temp.creature_id, i, temp.event_param3);
- continue;
- }
- }
-
- if (!(temp.event_flags & EFLAG_REPEATABLE))
- {
- error_db_log("SD2: Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp.creature_id, i);
- temp.event_flags |= EFLAG_REPEATABLE;
- }
-
- }
- break;
-
- case EVENT_T_QUEST_ACCEPT:
- case EVENT_T_QUEST_COMPLETE:
- {
- error_db_log("SD2: Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i);
- continue;
- }
- break;
- }
-
- for (uint32 j = 0; j < MAX_ACTIONS; j++)
- {
- temp.action[j].type = fields[10+(j*4)].GetUInt16();
- temp.action[j].param1 = fields[11+(j*4)].GetUInt32();
- temp.action[j].param2 = fields[12+(j*4)].GetUInt32();
- temp.action[j].param3 = fields[13+(j*4)].GetUInt32();
-
- //Report any errors in actions
- switch (temp.action[j].type)
- {
- case ACTION_T_TEXT:
- {
- if (temp.action[j].param1_s < 0)
- {
- if (TextMap.find(temp.action[j].param1_s) == TextMap.end())
- error_db_log("TSCR: Event %u Action %u param1 refrences non-existing entry in texts table.", i, j+1);
- }
- if (temp.action[j].param2_s < 0)
- {
- if (TextMap.find(temp.action[j].param2_s) == TextMap.end())
- error_db_log("TSCR: Event %u Action %u param2 refrences non-existing entry in texts table.", i, j+1);
-
- if (!temp.action[j].param1_s)
- error_db_log("TSCR: Event %u Action %u has param2, but param1 is not set. Required for randomized text.", i, j+1);
- }
- if (temp.action[j].param3_s < 0)
- {
- if (TextMap.find(temp.action[j].param3_s) == TextMap.end())
- error_db_log("TSCR: Event %u Action %u param3 refrences non-existing entry in texts table.", i, j+1);
-
- if (!temp.action[j].param1_s || !temp.action[j].param2_s)
- error_db_log("TSCR: Event %u Action %u has param3, but param1 and/or param2 is not set. Required for randomized text.", i, j+1);
- }
- }
- break;
- case ACTION_T_SET_FACTION:
- if (temp.action[j].param1 !=0 && !GetFactionStore()->LookupEntry(temp.action[j].param1))
- {
- error_db_log("TSCR: Event %u Action %u uses non-existant FactionId %u.", i, j+1, temp.action[j].param1);
- temp.action[j].param1 = 0;
- }
- break;
- case ACTION_T_MORPH_TO_ENTRY_OR_MODEL:
- if (temp.action[j].param1 !=0 || temp.action[j].param2 !=0)
- {
- if (temp.action[j].param1 && !GetCreatureTemplateStore(temp.action[j].param1))
- {
- error_db_log("TSCR: Event %u Action %u uses non-existant Creature entry %u.", i, j+1, temp.action[j].param1);
- temp.action[j].param1 = 0;
- }
-
- if (temp.action[j].param2 && !GetCreatureDisplayStore()->LookupEntry(temp.action[j].param2))
- {
- error_db_log("TSCR: Event %u Action %u uses non-existant ModelId %u.", i, j+1, temp.action[j].param2);
- temp.action[j].param2 = 0;
- }
- }
- break;
- case ACTION_T_SOUND:
- if (!GetSoundEntriesStore()->LookupEntry(temp.action[j].param1))
- error_db_log("TSCR: Event %u Action %u uses non-existant SoundID %u.", i, j+1, temp.action[j].param1);
- break;
-
- /*case ACTION_T_RANDOM_SOUND:
- {
- if(!GetSoundEntriesStore()->LookupEntry(temp.action[j].param1))
- error_db_log("TSCR: Event %u Action %u param1 uses non-existant SoundID %u.", i, j+1, temp.action[j].param1);
- if(!GetSoundEntriesStore()->LookupEntry(temp.action[j].param2))
- error_db_log("TSCR: Event %u Action %u param2 uses non-existant SoundID %u.", i, j+1, temp.action[j].param2);
- if(!GetSoundEntriesStore()->LookupEntry(temp.action[j].param3))
- error_db_log("TSCR: Event %u Action %u param3 uses non-existant SoundID %u.", i, j+1, temp.action[j].param3);
- }
- break;*/
-
- case ACTION_T_CAST:
- {
- const SpellEntry *spell = GetSpellStore()->LookupEntry(temp.action[j].param1);
- if (!spell)
- error_db_log("SD2: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param1);
- else
- {
- if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE)
- {
- //output as debug for now, also because there's no general rule all spells have RecoveryTime
- if (temp.event_param3 < spell->RecoveryTime)
- debug_log("TSCR: Event %u Action %u uses SpellID %u but cooldown is longer(%u) than minumum defined in event param3(%u).", i, j+1,temp.action[j].param1, spell->RecoveryTime, temp.event_param3);
- }
- }
-
- if (temp.action[j].param2 >= TARGET_T_END)
- error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
- }
- break;
- case ACTION_T_REMOVEAURASFROMSPELL:
- {
- if (!GetSpellStore()->LookupEntry(temp.action[j].param2))
- error_db_log("TSCR: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
-
- if (temp.action[j].param1 >= TARGET_T_END)
- error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
- }
- break;
- case ACTION_T_QUEST_EVENT:
- {
- if (Quest const* qid = GetQuestTemplateStore(temp.action[j].param1))
- {
- if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
- error_db_log("TSCR: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, temp.action[j].param1);
- }
- else
- error_db_log("TSCR: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1);
-
- if (temp.action[j].param2 >= TARGET_T_END)
- error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
- }
- break;
- case ACTION_T_QUEST_EVENT_ALL:
- {
- if (Quest const* qid = GetQuestTemplateStore(temp.action[j].param1))
- {
- if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
- error_db_log("TSCR: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, temp.action[j].param1);
- }
- else
- error_db_log("TSCR: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1);
- }
- break;
- case ACTION_T_CASTCREATUREGO:
- {
- if (!GetCreatureTemplateStore(temp.action[j].param1))
- error_db_log("TSCR: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
-
- if (!GetSpellStore()->LookupEntry(temp.action[j].param2))
- error_db_log("TSCR: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
-
- if (temp.action[j].param3 >= TARGET_T_END)
- error_db_log("SD2: Event %u Action %u uses incorrect Target type", i, j+1);
- }
- break;
- case ACTION_T_CASTCREATUREGO_ALL:
- {
- if (!GetQuestTemplateStore(temp.action[j].param1))
- error_db_log("TSCR: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1);
-
- if (!GetSpellStore()->LookupEntry(temp.action[j].param2))
- error_db_log("TSCR: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
- }
- break;
-
- //2nd param target
- case ACTION_T_SUMMON_ID:
- {
- if (!GetCreatureTemplateStore(temp.action[j].param1))
- error_db_log("TSCR: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
-
- if (EventAI_Summon_Map.find(temp.action[j].param3) == EventAI_Summon_Map.end())
- error_db_log("TSCR: Event %u Action %u summons missing EventAI_Summon %u", i, j+1, temp.action[j].param3);
-
- if (temp.action[j].param2 >= TARGET_T_END)
- error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
- }
- break;
- case ACTION_T_KILLED_MONSTER:
- {
- if (!GetCreatureTemplateStore(temp.action[j].param1))
- error_db_log("TSCR: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
-
- if (temp.action[j].param2 >= TARGET_T_END)
- error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
- }
- break;
- case ACTION_T_SUMMON:
- {
- if (!GetCreatureTemplateStore(temp.action[j].param1))
- error_db_log("TSCR: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
-
- if (temp.action[j].param2 >= TARGET_T_END)
- error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
- }
- break;
- case ACTION_T_THREAT_SINGLE_PCT:
- case ACTION_T_SET_UNIT_FLAG:
- case ACTION_T_REMOVE_UNIT_FLAG:
- if (temp.action[j].param2 >= TARGET_T_END)
- error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
- break;
-
- //3rd param target
- case ACTION_T_SET_UNIT_FIELD:
- if (temp.action[j].param1 < OBJECT_END || temp.action[j].param1 >= UNIT_END)
- error_db_log("TSCR: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j+1);
- if (temp.action[j].param3 >= TARGET_T_END)
- error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
- break;
-
- case ACTION_T_SET_PHASE:
- if (temp.action[j].param1 > 31)
- error_db_log("TSCR: Event %u Action %u attempts to set phase > 31. Phase mask cannot be used past phase 31.", i, j+1);
- break;
-
- case ACTION_T_INC_PHASE:
- if (!temp.action[j].param1)
- error_db_log("SD2: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j+1);
- break;
-
- case ACTION_T_SET_INST_DATA:
- {
- if (!(temp.event_flags & EFLAG_NORMAL) && !(temp.event_flags & EFLAG_HEROIC))
- error_db_log("TSCR: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i, j+1);
-
- if (temp.action[j].param2 > SPECIAL)
- error_db_log("TSCR: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j+1);
- }
- break;
- case ACTION_T_SET_INST_DATA64:
- {
- if (!(temp.event_flags & EFLAG_NORMAL) && !(temp.event_flags & EFLAG_HEROIC))
- error_db_log("TSCR: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i, j+1);
-
- if (temp.action[j].param2 >= TARGET_T_END)
- error_db_log("TSCR: Event %u Action %u uses incorrect Target type", i, j+1);
- }
- break;
- case ACTION_T_UPDATE_TEMPLATE:
- {
- if (!GetCreatureTemplateStore(temp.action[j].param1))
- error_db_log("TSCR: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
- }
- break;
- case ACTION_T_RANDOM_SAY:
- case ACTION_T_RANDOM_YELL:
- case ACTION_T_RANDOM_TEXTEMOTE:
- error_db_log("TSCR: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j+1);
- break;
-
- default:
- if (temp.action[j].type >= ACTION_T_END)
- error_db_log("TSCR: Event %u Action %u has incorrect action type. Maybe DB requires updated version of SD2.", i, j+1);
- break;
- }
- }
-
- //Add to list
- EventAI_Event_Map[creature_id].push_back(temp);
-
- ++Count;
- } while (result->NextRow());
-
- delete result;
-
- outstring_log("");
- outstring_log(">> Loaded %u EventAI scripts", Count);
- }else
- {
- barGoLink bar(1);
- bar.step();
- outstring_log("");
- outstring_log(">> Loaded 0 EventAI scripts. DB table `eventai_scripts` is empty.");
- }
-
//Free database thread and resources
TScriptDB.HaltDelayThread();
@@ -1569,25 +940,6 @@ void ScriptsInit()
}
else outstring_log("TSCR: Using configuration file %s",_TRINITY_SCRIPT_CONFIG);
- EAI_ErrorLevel = TScriptConfig.GetIntDefault("EAIErrorLevel", 1);
-
- switch (EAI_ErrorLevel)
- {
- case 0:
- outstring_log("TSCR: EventAI Error Reporting level set to 0 (Startup Errors only)");
- break;
- case 1:
- outstring_log("TSCR: EventAI Error Reporting level set to 1 (Startup errors and Runtime event errors)");
- break;
- case 2:
- outstring_log("TSCR: EventAI Error Reporting level set to 2 (Startup errors, Runtime event errors, and Creation errors)");
- break;
- default:
- outstring_log("TSCR: Unknown EventAI Error Reporting level. Defaulting to 1 (Startup errors and Runtime event errors)");
- EAI_ErrorLevel = 1;
- break;
- }
-
outstring_log("");
//Load database (must be called after TScriptConfig.SetSource). In case it failed, no need to even try load.
@@ -1615,7 +967,6 @@ void ScriptsInit()
AddSC_boss_ysondre();
// -- Creature --
- AddSC_mob_event();
AddSC_generic_creature();
// -- Custom --
diff --git a/src/bindings/scripts/include/sc_creature.cpp b/src/bindings/scripts/include/sc_creature.cpp
index 59019348043..2c0ab471bda 100644
--- a/src/bindings/scripts/include/sc_creature.cpp
+++ b/src/bindings/scripts/include/sc_creature.cpp
@@ -519,11 +519,11 @@ bool ScriptedAI::CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered)
return false;
//Silenced so we can't cast
- if (!Triggered && m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
+ if (!Triggered && me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
return false;
//Check for power
- if (!Triggered && m_creature->GetPower((Powers)Spell->powerType) < Spell->manaCost)
+ if (!Triggered && me->GetPower((Powers)Spell->powerType) < Spell->manaCost)
return false;
SpellRangeEntry const *TempRange = NULL;
@@ -535,7 +535,8 @@ bool ScriptedAI::CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered)
return false;
//Unit is out of range of this spell
- if (m_creature->GetDistance(Target) > m_creature->GetSpellMaxRangeForTarget(Target, TempRange) || m_creature->GetDistance(Target) < m_creature->GetSpellMinRangeForTarget(Target, TempRange))
+ if (me->GetDistance(Target) > me->GetSpellMaxRangeForTarget(Target, TempRange)
+ || me->GetDistance(Target) < me->GetSpellMinRangeForTarget(Target, TempRange))
return false;
return true;
@@ -636,39 +637,6 @@ void FillSpellSummary()
}
}
-void ScriptedAI::DoZoneInCombat(Unit* pUnit)
-{
- if (!pUnit)
- pUnit = m_creature;
-
- Map *map = pUnit->GetMap();
-
- if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated
- {
- error_log("SD2: DoZoneInCombat call for map that isn't an instance (pUnit entry = %d)", pUnit->GetTypeId() == TYPEID_UNIT ? ((Creature*)pUnit)->GetEntry() : 0);
- return;
- }
-
- if (!pUnit->CanHaveThreatList() || pUnit->getThreatManager().isThreatListEmpty())
- {
- error_log("SD2: DoZoneInCombat called for creature that either cannot have threat list or has empty threat list (pUnit entry = %d)", pUnit->GetTypeId() == TYPEID_UNIT ? ((Creature*)pUnit)->GetEntry() : 0);
-
- return;
- }
-
- Map::PlayerList const &PlayerList = map->GetPlayers();
- for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
- {
- if (Player* i_pl = i->getSource())
- if (i_pl->isAlive())
- {
- pUnit->SetInCombatWith(i_pl);
- i_pl->SetInCombatWith(pUnit);
- pUnit->AddThreat(i_pl, 0.0f);
- }
- }
-}
-
void ScriptedAI::DoResetThreat()
{
if (!m_creature->CanHaveThreatList() || m_creature->getThreatManager().isThreatListEmpty())
diff --git a/src/bindings/scripts/include/sc_creature.h b/src/bindings/scripts/include/sc_creature.h
index 5cb9b2c9f81..61a82bb6dd5 100644
--- a/src/bindings/scripts/include/sc_creature.h
+++ b/src/bindings/scripts/include/sc_creature.h
@@ -148,9 +148,6 @@ struct TRINITY_DLL_DECL ScriptedAI : public CreatureAI
//Plays a sound to all nearby players
void DoPlaySoundToSet(Unit* unit, uint32 sound);
- //Places the entire map into combat with creature
- void DoZoneInCombat(Unit* pUnit = 0);
-
//Drops all threat to 0%. Does not remove players from the threat list
void DoResetThreat();
diff --git a/src/bindings/scripts/scripts/creature/mob_event_ai.cpp b/src/bindings/scripts/scripts/creature/mob_event_ai.cpp
index ed0e8d2a0a7..5f656a45da8 100644
--- a/src/bindings/scripts/scripts/creature/mob_event_ai.cpp
+++ b/src/bindings/scripts/scripts/creature/mob_event_ai.cpp
@@ -1,1525 +1 @@
-/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/* ScriptData
-SDName: Mob_Event_AI
-SD%Complete: 90
-SDComment: Database Event AI
-SDCategory: Creatures
-EndScriptData */
-
#include "precompiled.h"
-#include "mob_event_ai.h"
-#include "ObjectMgr.h"
-#include "GameEventMgr.h"
-
-#define EVENT_UPDATE_TIME 500
-#define SPELL_RUN_AWAY 8225
-
-struct EventHolder
-{
- EventHolder(EventAI_Event p) : Event(p), Time(0), Enabled(true){}
-
- EventAI_Event Event;
- uint32 Time;
- bool Enabled;
-};
-
-struct TRINITY_DLL_DECL Mob_EventAI : public ScriptedAI
-{
- Mob_EventAI(Creature *c, std::list<EventHolder> pEventList) : ScriptedAI(c)
- {
- EventList = pEventList;
- bEmptyList = pEventList.empty();
- Phase = 0;
- CombatMovementEnabled = true;
- MeleeEnabled = true;
- AttackDistance = 0;
- AttackAngle = 0.0f;
-
- //Handle Spawned Events
- if (!bEmptyList)
- {
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- if ((*i).Event.event_type == EVENT_T_SPAWNED)
- ProcessEvent(*i);
- }
- }
- }
-
- ~Mob_EventAI()
- {
- EventList.clear();
- }
-
- //Variables used by EventAI for handling events
- std::list<EventHolder> EventList; //Holder for events (stores enabled, time, and eventid)
- uint32 EventUpdateTime; //Time between event updates
- uint32 EventDiff; //Time between the last event call
- bool bEmptyList;
-
- //Variables used by Events themselves
- uint8 Phase; //Current phase, max 32 phases
- bool CombatMovementEnabled; //If we allow targeted movment gen (movement twoards top threat)
- bool MeleeEnabled; //If we allow melee auto attack
- uint32 AttackDistance; //Distance to attack from
- float AttackAngle; //Angle of attack
- uint32 TimetoFleeLeft; //For fleeing
-
- bool ProcessEvent(EventHolder& pHolder, Unit* pActionInvoker = NULL)
- {
- if (!pHolder.Enabled || pHolder.Time)
- return false;
-
- //Check the inverse phase mask (event doesn't trigger if current phase bit is set in mask)
- if (pHolder.Event.event_inverse_phase_mask & (1 << Phase))
- return false;
-
- //Store random here so that all random actions match up
- uint32 rnd = rand();
-
- //Return if chance for event is not met
- if (pHolder.Event.event_chance <= rnd % 100)
- return false;
-
- union
- {
- uint32 param1;
- int32 param1_s;
- };
-
- union
- {
- uint32 param2;
- int32 param2_s;
- };
-
- union
- {
- uint32 param3;
- int32 param3_s;
- };
-
- union
- {
- uint32 param4;
- int32 param4_s;
- };
-
- param1 = pHolder.Event.event_param1;
- param2 = pHolder.Event.event_param2;
- param3 = pHolder.Event.event_param3;
- param4 = pHolder.Event.event_param4;
-
- //Check event conditions based on the event type, also reset events
- switch (pHolder.Event.event_type)
- {
- case EVENT_T_TIMER:
- {
- if (!InCombat)
- return false;
-
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_TIMER_OOC:
- {
- if (InCombat)
- return false;
-
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_HP:
- {
- if (!InCombat || !m_creature->GetMaxHealth())
- return false;
-
- uint32 perc = (m_creature->GetHealth()*100) / m_creature->GetMaxHealth();
-
- if (perc > param1 || perc < param2)
- return false;
-
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_MANA:
- {
- if (!InCombat || !m_creature->GetMaxPower(POWER_MANA))
- return false;
-
- uint32 perc = (m_creature->GetPower(POWER_MANA)*100) / m_creature->GetMaxPower(POWER_MANA);
-
- if (perc > param1 || perc < param2)
- return false;
-
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_AGGRO:
- {
- }
- break;
- case EVENT_T_KILL:
- {
- //Repeat Timers
- if (param1 == param2)
- {
- pHolder.Time = param1;
-
- }else if (param2 > param1)
- pHolder.Time = urand(param1, param2);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- case EVENT_T_DEATH:
- {
- }
- break;
- case EVENT_T_EVADE:
- {
- }
- break;
- case EVENT_T_SPELLHIT:
- {
- //Spell hit is special case, param1 and param2 handled within EventAI::SpellHit
-
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_RANGE:
- {
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_OOC_LOS:
- {
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_SPAWNED:
- {
- }
- break;
- case EVENT_T_TARGET_HP:
- {
- if (!InCombat || !m_creature->getVictim() || !m_creature->getVictim()->GetMaxHealth())
- return false;
-
- uint32 perc = (m_creature->getVictim()->GetHealth()*100) / m_creature->getVictim()->GetMaxHealth();
-
- if (perc > param1 || perc < param2)
- return false;
-
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_TARGET_CASTING:
- {
- if (!InCombat || !m_creature->getVictim() || !m_creature->getVictim()->IsNonMeleeSpellCasted(false, false, true))
- return false;
-
- //Repeat Timers
- if (param1 == param2)
- {
- pHolder.Time = param1;
-
- }else if (param2 > param1)
- pHolder.Time = urand(param1, param2);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_FRIENDLY_HP:
- {
- if (!InCombat)
- return false;
-
- Unit* pUnit = DoSelectLowestHpFriendly(param2, param1);
-
- if (!pUnit)
- return false;
-
- pActionInvoker = pUnit;
-
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_FRIENDLY_IS_CC:
- {
- if (!InCombat)
- return false;
-
- std::list<Creature*> pList = DoFindFriendlyCC(param2);
-
- //List is empty
- if (pList.empty())
- return false;
-
- //We don't really care about the whole list, just return first available
- pActionInvoker = *(pList.begin());
-
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_FRIENDLY_MISSING_BUFF:
- {
- std::list<Creature*> pList = DoFindFriendlyMissingBuff(param2, param1);
-
- //List is empty
- if (pList.empty())
- return false;
-
- //We don't really care about the whole list, just return first available
- pActionInvoker = *(pList.begin());
-
- //Repeat Timers
- if (param3 == param4)
- {
- pHolder.Time = param3;
-
- }else if (param4 > param3)
- pHolder.Time = urand(param3, param4);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_SUMMONED_UNIT:
- {
- //Prevent event from occuring on no unit or non creatures
- if (!pActionInvoker || pActionInvoker->GetTypeId()!=TYPEID_UNIT)
- return false;
-
- //Creature id doesn't match up
- if (param1 && ((Creature*)pActionInvoker)->GetEntry() != param1)
- return false;
-
- //Repeat Timers
- if (param2 == param3)
- {
- pHolder.Time = param2;
-
- }else if (param3 > param2)
- pHolder.Time = urand(param2, param3);
- else
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- pHolder.Enabled = false;
- }
- }
- break;
- case EVENT_T_REACHED_HOME:
- {
- }
- break;
- case EVENT_T_RECEIVE_EMOTE:
- {
- }
- break;
- default:
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
- break;
- }
-
- //Disable non-repeatable events
- if (!(pHolder.Event.event_flags & EFLAG_REPEATABLE))
- pHolder.Enabled = false;
-
- //Process actions
- for (uint32 j = 0; j < MAX_ACTIONS; j++)
- ProcessAction(pHolder.Event.action[j].type, pHolder.Event.action[j].param1, pHolder.Event.action[j].param2, pHolder.Event.action[j].param3, rnd, pHolder.Event.event_id, pActionInvoker);
-
- return true;
- }
-
- inline uint32 GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3)
- {
- switch (rnd % 3)
- {
- case 0:
- return param1;
- break;
- case 1:
- return param2;
- break;
- case 2:
- return param3;
- break;
- }
- return 0;
- }
-
- inline Unit* GetTargetByType(uint32 Target, Unit* pActionInvoker)
- {
- switch (Target)
- {
- case TARGET_T_SELF:
- return m_creature;
- break;
- case TARGET_T_HOSTILE:
- return m_creature->getVictim();
- break;
- case TARGET_T_HOSTILE_SECOND_AGGRO:
- return SelectUnit(SELECT_TARGET_TOPAGGRO,1);
- break;
- case TARGET_T_HOSTILE_LAST_AGGRO:
- return SelectUnit(SELECT_TARGET_BOTTOMAGGRO,0);
- break;
- case TARGET_T_HOSTILE_RANDOM:
- return SelectUnit(SELECT_TARGET_RANDOM,0);
- break;
- case TARGET_T_HOSTILE_RANDOM_NOT_TOP:
- return SelectUnit(SELECT_TARGET_RANDOM,1);
- break;
- case TARGET_T_ACTION_INVOKER:
- return pActionInvoker;
- break;
- default:
- return NULL;
- break;
- };
- }
-
- void ProcessAction(uint16 type, uint32 param1, uint32 param2, uint32 param3, uint32 rnd, uint32 EventId, Unit* pActionInvoker)
- {
- switch (type)
- {
- case ACTION_T_TEXT:
- {
- if (!param1)
- return;
-
- uint32 temp = 0;
-
- if (param2 && param3)
- {
- switch( rand()%3 )
- {
- case 0: temp = param1; break;
- case 2: temp = param2; break;
- case 3: temp = param3; break;
- }
- }else if ( param2 && urand(0,1) )
- {
- temp = param2;
- }else
- {
- temp = param1;
- }
-
- if (temp)
- {
- Unit* target = NULL;
- Unit* owner = NULL;
-
- if (pActionInvoker)
- {
- if (pActionInvoker->GetTypeId() == TYPEID_PLAYER)
- target = pActionInvoker;
- else if (owner = pActionInvoker->GetOwner())
- {
- if (owner->GetTypeId() == TYPEID_PLAYER)
- target = owner;
- }
- }
- else if (target = m_creature->getVictim())
- {
- if (target->GetTypeId() != TYPEID_PLAYER)
- {
- if (owner = target->GetOwner())
- {
- if (owner->GetTypeId() == TYPEID_PLAYER)
- target = owner;
- }
- }
- }
-
- DoScriptText(temp, m_creature, target);
- }
- }
- break;
- case ACTION_T_SET_FACTION:
- {
- if (param1)
- m_creature->setFaction(param1);
- else
- {
- if (CreatureInfo const* ci = GetCreatureTemplateStore(m_creature->GetEntry()))
- {
- //if no id provided, assume reset and then use default
- if (m_creature->getFaction() != ci->faction_A)
- m_creature->setFaction(ci->faction_A);
- }
- }
- }
- break;
- case ACTION_T_MORPH_TO_ENTRY_OR_MODEL:
- {
- if (param1 || param2)
- {
- //set model based on entry from creature_template
- if (param1)
- {
- if (CreatureInfo const* ci = GetCreatureTemplateStore(param1))
- {
- //use default display
- if (ci->Modelid1)
- m_creature->SetDisplayId(ci->Modelid1);
- }
- }
- //if no param1, then use value from param2 (modelId)
- else
- m_creature->SetDisplayId(param2);
- }
- else
- m_creature->DeMorph();
- }
- break;
- case ACTION_T_SOUND:
- DoPlaySoundToSet(m_creature, param1);
- break;
- case ACTION_T_EMOTE:
- m_creature->HandleEmoteCommand(param1);
- break;
- case ACTION_T_RANDOM_SOUND:
- {
- uint32 temp = GetRandActionParam(rnd, param1, param2, param3);
-
- if (temp != 0xffffffff)
- DoPlaySoundToSet(m_creature, temp);
- }
- break;
- case ACTION_T_RANDOM_EMOTE:
- {
- uint32 temp = GetRandActionParam(rnd, param1, param2, param3);
-
- if (temp != 0xffffffff)
- m_creature->HandleEmoteCommand(temp);
- }
- break;
- case ACTION_T_CAST:
- {
- Unit* target = GetTargetByType(param2, pActionInvoker);
- Unit* caster = m_creature;
-
- if (!target)
- return;
-
- //Cast is always triggered if target is forced to cast on self
- if (param3 & CAST_FORCE_TARGET_SELF)
- {
- param3 |= CAST_TRIGGERED;
- caster = target;
- }
-
- //Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered
- bool canCast = !(caster->IsNonMeleeSpellCasted(false) && (param3 & CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS));
-
- // If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them
- if(param3 & CAST_AURA_NOT_PRESENT)
- {
- if(target->HasAura(param1))
- return;
- }
-
- if (canCast)
- {
- const SpellEntry* tSpell = GetSpellStore()->LookupEntry(param1);
-
- //Verify that spell exists
- if (tSpell)
- {
- //Check if cannot cast spell
- if (!(param3 & (CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST)) &&
- !CanCast(target, tSpell, (param3 & CAST_TRIGGERED)))
- {
- //Melee current victim if flag not set
- if (!(param3 & CAST_NO_MELEE_IF_OOM))
- {
- if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
- {
- AttackDistance = 0;
- AttackAngle = 0;
-
- m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle);
- }
- }
-
- }
- else
- {
- //Interrupt any previous spell
- if (caster->IsNonMeleeSpellCasted(false) && param3 & CAST_INTURRUPT_PREVIOUS)
- caster->InterruptNonMeleeSpells(false);
-
- caster->CastSpell(target, param1, (param3 & CAST_TRIGGERED));
- }
-
- }else if (EAI_ErrorLevel > 0)
- error_db_log("SD2: EventAI event %d creature %d attempt to cast spell that doesn't exist %d", EventId, m_creature->GetEntry(), param1);
- }
- }
- break;
- case ACTION_T_SUMMON:
- {
- Unit* target = GetTargetByType(param2, pActionInvoker);
-
- Creature* pCreature = NULL;
-
- if (param3)
- pCreature = DoSpawnCreature(param1, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, param3);
- else pCreature = pCreature = DoSpawnCreature(param1, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
-
- if (!pCreature)
- {
- if (EAI_ErrorLevel > 0)
- error_db_log( "SD2: EventAI failed to spawn creature %u. Spawn event %d is on creature %d", param1, EventId, m_creature->GetEntry());
- }
- else if (param2 != TARGET_T_SELF && target)
- pCreature->AI()->AttackStart(target);
- }
- break;
- case ACTION_T_THREAT_SINGLE_PCT:
- {
- Unit* target = GetTargetByType(param2, pActionInvoker);
-
- if (target)
- DoModifyThreatPercent(target, param1);
- }
- break;
- case ACTION_T_THREAT_ALL_PCT:
- {
- Unit* Temp = NULL;
-
- std::list<HostilReference*>::iterator i = m_creature->getThreatManager().getThreatList().begin();
- for (; i != m_creature->getThreatManager().getThreatList().end(); ++i)
- {
- Temp = Unit::GetUnit((*m_creature),(*i)->getUnitGuid());
- if (Temp)
- DoModifyThreatPercent(Temp, param1);
- }
- }
- break;
- case ACTION_T_QUEST_EVENT:
- {
- Unit* target = GetTargetByType(param2, pActionInvoker);
-
- if (target && target->GetTypeId() == TYPEID_PLAYER)
- ((Player*)target)->AreaExploredOrEventHappens(param1);
- }
- break;
- case ACTION_T_CASTCREATUREGO:
- {
- Unit* target = GetTargetByType(param3, pActionInvoker);
-
- if (target && target->GetTypeId() == TYPEID_PLAYER)
- ((Player*)target)->CastedCreatureOrGO(param1, m_creature->GetGUID(), param2);
- }
- break;
- case ACTION_T_SET_UNIT_FIELD:
- {
- Unit* target = GetTargetByType(param3, pActionInvoker);
-
- if (param1 < OBJECT_END || param1 >= UNIT_END)
- return;
-
- if (target)
- target->SetUInt32Value(param1, param2);
- }
- break;
- case ACTION_T_SET_UNIT_FLAG:
- {
- Unit* target = GetTargetByType(param2, pActionInvoker);
-
- if (target)
- target->SetFlag(UNIT_FIELD_FLAGS, param1);
- }
- break;
- case ACTION_T_REMOVE_UNIT_FLAG:
- {
- Unit* target = GetTargetByType(param2, pActionInvoker);
-
- if (target)
- target->RemoveFlag(UNIT_FIELD_FLAGS, param1);
- }
- break;
- case ACTION_T_AUTO_ATTACK:
- {
- if (param1)
- MeleeEnabled = true;
- else MeleeEnabled = false;
- }
- break;
- case ACTION_T_COMBAT_MOVEMENT:
- {
- CombatMovementEnabled = param1;
-
- //Allow movement (create new targeted movement gen only if idle)
- if (CombatMovementEnabled)
- {
- m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle);
- }
- else
- {
- m_creature->GetMotionMaster()->MoveIdle();
- }
- }
- break;
- case ACTION_T_SET_PHASE:
- {
- Phase = param1;
- }
- break;
- case ACTION_T_INC_PHASE:
- {
- Phase += param1;
-
- if (Phase > 31)
- if (EAI_ErrorLevel > 0)
- error_db_log( "SD2: Event %d incremented Phase above 31. Phase mask cannot be used with phases past 31. CreatureEntry = %d", EventId, m_creature->GetEntry());
- }
- break;
- case ACTION_T_EVADE:
- {
- EnterEvadeMode();
- }
- break;
- case ACTION_T_FLEE:
- {
- if(m_creature->HasAuraType(SPELL_AURA_PREVENTS_FLEEING))
- break;
- TimetoFleeLeft = 8000;
- m_creature->DoFleeToGetAssistance();
- IsFleeing = true;
- }
- break;
- case ACTION_T_QUEST_EVENT_ALL:
- {
- Unit* Temp = NULL;
- if( pActionInvoker && pActionInvoker->GetTypeId() == TYPEID_PLAYER )
- {
- Temp = Unit::GetUnit((*m_creature),pActionInvoker->GetGUID());
- if( Temp )
- ((Player*)Temp)->GroupEventHappens(param1,m_creature);
- }
- }
- break;
- case ACTION_T_CASTCREATUREGO_ALL:
- {
- Unit* Temp = NULL;
-
- std::list<HostilReference*>::iterator i = m_creature->getThreatManager().getThreatList().begin();
- for (; i != m_creature->getThreatManager().getThreatList().end(); ++i)
- {
- Temp = Unit::GetUnit((*m_creature),(*i)->getUnitGuid());
- if (Temp && Temp->GetTypeId() == TYPEID_PLAYER)
- ((Player*)Temp)->CastedCreatureOrGO(param1, m_creature->GetGUID(), param2);
- }
- }
- break;
- case ACTION_T_REMOVEAURASFROMSPELL:
- {
- Unit* target = GetTargetByType(param1, pActionInvoker);
-
- if (target)
- target->RemoveAurasDueToSpell(param2);
- }
- break;
- case ACTION_T_RANGED_MOVEMENT:
- {
- AttackDistance = param1;
- AttackAngle = ((float)param2/180)*M_PI;
-
- if (CombatMovementEnabled)
- {
- m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle);
- }
- }
- break;
- case ACTION_T_RANDOM_PHASE:
- {
- uint32 temp = GetRandActionParam(rnd, param1, param2, param3);
-
- Phase = temp;
- }
- break;
- case ACTION_T_RANDOM_PHASE_RANGE:
- {
- if (param2 > param1)
- {
- Phase = param1 + (rnd % (param2 - param1));
- }
- else if (EAI_ErrorLevel > 0)
- error_db_log( "SD2: ACTION_T_RANDOM_PHASE_RANGE cannot have Param2 <= Param1. Divide by Zero. Event = %d. CreatureEntry = %d", EventId, m_creature->GetEntry());
- }
- break;
- case ACTION_T_SUMMON_ID:
- {
- Unit* target = GetTargetByType(param2, pActionInvoker);
-
- //Duration
- Creature* pCreature = NULL;
-
- UNORDERED_MAP<uint32, EventAI_Summon>::iterator i = EventAI_Summon_Map.find(param3);
-
- if (i == EventAI_Summon_Map.end())
- {
- if (EAI_ErrorLevel > 0)
- error_db_log( "SD2: EventAI failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", param1, param3, EventId, m_creature->GetEntry());
- return;
- }
-
- if ((*i).second.SpawnTimeSecs)
- pCreature = m_creature->SummonCreature(param1, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs);
- else pCreature = m_creature->SummonCreature(param1, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
-
- if (!pCreature)
- {
- if (EAI_ErrorLevel > 0)
- error_db_log( "SD2: EventAI failed to spawn creature %u. EventId %d.Creature %d", param1, EventId, m_creature->GetEntry());
- }
- else if (param2 != TARGET_T_SELF && target)
- pCreature->AI()->AttackStart(target);
- }
- break;
- case ACTION_T_KILLED_MONSTER:
- {
- //first attempt player who tapped creature
- if (Player* pPlayer = m_creature->GetLootRecipient())
- pPlayer->RewardPlayerAndGroupAtEvent(param1,m_creature);
- else
- {
- //if not available, use pActionInvoker
- Unit* pTarget = GetTargetByType(param2, pActionInvoker);
-
- if (Player* pPlayer = pTarget->GetCharmerOrOwnerPlayerOrPlayerItself())
- pPlayer->RewardPlayerAndGroupAtEvent(param1,m_creature);
- }
- }
- break;
- case ACTION_T_SET_INST_DATA:
- {
- ScriptedInstance* pInst = (ScriptedInstance*)m_creature->GetInstanceData();
- if (!pInst)
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Event %d attempt to set instance data without instance script. Creature %d", EventId, m_creature->GetEntry());
- return;
- }
-
- pInst->SetData(param1, param2);
- }
- break;
- case ACTION_T_SET_INST_DATA64:
- {
- Unit* target = GetTargetByType(param2, pActionInvoker);
-
- if (!target)
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Event %d attempt to set instance data64 but Target == NULL. Creature %d", EventId, m_creature->GetEntry());
- return;
- }
-
- ScriptedInstance* pInst = (ScriptedInstance*)m_creature->GetInstanceData();
-
- if (!pInst)
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Event %d attempt to set instance data64 without instance script. Creature %d", EventId, m_creature->GetEntry());
- return;
- }
-
- pInst->SetData64(param1, target->GetGUID());
- }
- break;
- case ACTION_T_UPDATE_TEMPLATE:
- {
- if (m_creature->GetEntry() == param1)
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d", EventId, m_creature->GetEntry());
- return;
- }
-
- m_creature->UpdateEntry(param1, param2 ? HORDE : ALLIANCE);
- }
- break;
- case ACTION_T_DIE:
- {
- if (m_creature->isDead())
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Event %d ACTION_T_DIE on dead creature. Creature %d", EventId, m_creature->GetEntry());
- return;
- }
- m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(),NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
- }
- break;
- case ACTION_T_ZONE_COMBAT_PULSE:
- {
- if (!m_creature->isInCombat() || !m_creature->GetMap()->IsDungeon())
- {
- if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Event %d ACTION_T_ZONE_COMBAT_PULSE on creature out of combat or in non-dungeon map. Creature %d", EventId, m_creature->GetEntry());
- return;
- }
-
- DoZoneInCombat();
- }
- break;
-
- // TRINITY ONLY
- case ACTION_T_SET_ACTIVE:
- m_creature->setActive(param1 ? true : false);
- break;
- case ACTION_T_SET_AGGRESSIVE:
- m_creature->SetReactState(ReactStates(param1));
- break;
- case ACTION_T_ATTACK_START_PULSE:
- AttackStart(m_creature->SelectNearestTarget((float)param1));
- break;
- }
- }
-
- void JustRespawned()
- {
- InCombat = false;
- IsFleeing = false;
- Reset();
-
- if (bEmptyList)
- return;
-
- //Handle Spawned Events
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- if ((*i).Event.event_type == EVENT_T_SPAWNED)
- ProcessEvent(*i);
- }
- }
-
- void Reset()
- {
- EventUpdateTime = EVENT_UPDATE_TIME;
- EventDiff = 0;
-
- if (bEmptyList)
- return;
-
- //Reset all events to enabled
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- switch ((*i).Event.event_type)
- {
- //Reset all out of combat timers
- case EVENT_T_TIMER_OOC:
- {
- if ((*i).Event.event_param2 == (*i).Event.event_param1)
- {
- (*i).Time = (*i).Event.event_param1;
- (*i).Enabled = true;
- }
- else if ((*i).Event.event_param2 > (*i).Event.event_param1)
- {
- (*i).Time = urand((*i).Event.event_param1, (*i).Event.event_param2);
- (*i).Enabled = true;
- }
- else if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature->GetEntry(), (*i).Event.event_id, (*i).Event.event_type);
- }
- break;
- //default:
- //TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void Aggro()
- //(*i).Enabled = true;
- //(*i).Time = 0;
- //break;
- }
- }
- }
-
- //when creature reach home after EnterEvadeMode
- void JustReachedHome()
- {
- m_creature->LoadCreaturesAddon();
-
- if (!bEmptyList)
- {
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- if ((*i).Event.event_type == EVENT_T_REACHED_HOME)
- ProcessEvent(*i);
- }
- }
-
- Reset();
- }
-
- void EnterEvadeMode()
- {
- m_creature->InterruptNonMeleeSpells(true);
- m_creature->RemoveAllAuras();
- m_creature->DeleteThreatList();
- m_creature->CombatStop();
-
- if (m_creature->isAlive())
- m_creature->GetMotionMaster()->MoveTargetedHome();
-
- m_creature->SetLootRecipient(NULL);
-
- InCombat = false;
-
- if (bEmptyList)
- return;
-
- //Handle Evade events
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- if ((*i).Event.event_type == EVENT_T_EVADE)
- ProcessEvent(*i);
- }
- }
-
- void JustDied(Unit* killer)
- {
- InCombat = false;
- IsFleeing = false;
- Reset();
-
- if (bEmptyList)
- return;
-
- //Handle Evade events
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- if ((*i).Event.event_type == EVENT_T_DEATH)
- ProcessEvent(*i, killer);
- }
- }
-
- void KilledUnit(Unit* victim)
- {
- if (bEmptyList || victim->GetTypeId() != TYPEID_PLAYER)
- return;
-
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- if ((*i).Event.event_type == EVENT_T_KILL)
- ProcessEvent(*i, victim);
- }
- }
-
- void JustSummoned(Creature* pUnit)
- {
- if (bEmptyList || !pUnit)
- return;
-
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- if ((*i).Event.event_type == EVENT_T_SUMMONED_UNIT)
- ProcessEvent(*i, pUnit);
- }
- }
-
- void Aggro(Unit *who)
- {
- //Check for on combat start events
- if (!bEmptyList)
- {
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- switch ((*i).Event.event_type)
- {
- case EVENT_T_AGGRO:
- (*i).Enabled = true;
- ProcessEvent(*i, who);
- break;
- //Reset all in combat timers
- case EVENT_T_TIMER:
- if ((*i).Event.event_param2 == (*i).Event.event_param1)
- {
- (*i).Time = (*i).Event.event_param1;
- (*i).Enabled = true;
- }
- else if ((*i).Event.event_param2 > (*i).Event.event_param1)
- {
- (*i).Time = urand((*i).Event.event_param1, (*i).Event.event_param2);
- (*i).Enabled = true;
- }
- else if (EAI_ErrorLevel > 0)
- error_db_log("SD2: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature->GetEntry(), (*i).Event.event_id, (*i).Event.event_type);
- break;
- //All normal events need to be re-enabled and their time set to 0
- default:
- (*i).Enabled = true;
- (*i).Time = 0;
- break;
- }
- }
- }
-
- EventUpdateTime = EVENT_UPDATE_TIME;
- EventDiff = 0;
- }
-
- void AttackStart(Unit *who)
- {
- if (!who)
- return;
-
- if (m_creature->Attack(who, MeleeEnabled))
- {
- //Begin melee attack if we are within range
-
- if (!InCombat)
- {
- InCombat = true;
- Aggro(who);
- }
-
- if (CombatMovementEnabled)
- {
- m_creature->GetMotionMaster()->MoveChase(who, AttackDistance, AttackAngle);
- }
- else
- {
- m_creature->GetMotionMaster()->MoveIdle();
- }
- }
- }
-
- void MoveInLineOfSight(Unit *who)
- {
- if (!who || InCombat)
- return;
-
- //Check for OOC LOS Event
- if (!bEmptyList && !m_creature->getVictim())
- {
- for (std::list<EventHolder>::iterator itr = EventList.begin(); itr != EventList.end(); ++itr)
- {
- if ((*itr).Event.event_type == EVENT_T_OOC_LOS)
- {
- //can trigger if closer than fMaxAllowedRange
- float fMaxAllowedRange = (*itr).Event.event_param2;
-
- //if range is ok and we are actually in LOS
- if (m_creature->IsWithinDistInMap(who, fMaxAllowedRange) && m_creature->IsWithinLOSInMap(who))
- {
- //if friendly event&&who is not hostile OR hostile event&&who is hostile
- if (((*itr).Event.event_param1 && !m_creature->IsHostileTo(who)) ||
- ((!(*itr).Event.event_param1) && m_creature->IsHostileTo(who)))
- ProcessEvent(*itr, who);
- }
- }
- }
- }
-
- // do we need this?
- //if (m_creature->isCivilian() && m_creature->IsNeutralToAll())
- // return;
-
- if(m_creature->canStartAttack(who))
- AttackStart(who);
- }
-
- void SpellHit(Unit* pUnit, const SpellEntry* pSpell)
- {
- if (bEmptyList)
- return;
-
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- if ((*i).Event.event_type == EVENT_T_SPELLHIT)
- {
- //If spell id matches (or no spell id) & if spell school matches (or no spell school)
- if (!(*i).Event.event_param1 || pSpell->Id == (*i).Event.event_param1)
- {
- if ((*i).Event.event_param2_s == -1 || pSpell->SchoolMask == (*i).Event.event_param2)
- ProcessEvent(*i, pUnit);
- }
- }
- }
- }
-
- void UpdateAI(const uint32 diff)
- {
- //Check if we are in combat (also updates calls threat update code)
- bool Combat = InCombat ? UpdateVictim() : false;
-
- //Must return if creature isn't alive. Normally select hostil target and get victim prevent this
- if (!m_creature->isAlive())
- return;
-
- if (IsFleeing)
- {
- if(TimetoFleeLeft < diff)
- {
- m_creature->SetControlled(false, UNIT_STAT_FLEEING);
- m_creature->SetNoCallAssistance(false);
- m_creature->CallAssistance();
- if(m_creature->getVictim())
- m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
- IsFleeing = false;
- }
- else
- TimetoFleeLeft -= diff;
-
- return;
- }
-
- //Events are only updated once every EVENT_UPDATE_TIME ms to prevent lag with large amount of events
- if (!bEmptyList)
- {
- if (EventUpdateTime < diff)
- {
- EventDiff += diff;
-
- //Check for time based events
- for (std::list<EventHolder>::iterator i = EventList.begin(); i != EventList.end(); ++i)
- {
- //Decrement Timers
- if ((*i).Time)
- {
- if ((*i).Time > EventDiff)
- {
- //Do not decrement timers if event cannot trigger in this phase
- if (!((*i).Event.event_inverse_phase_mask & (1 << Phase)))
- (*i).Time -= EventDiff;
-
- //Skip processing of events that have time remaining
- continue;
- }
- else (*i).Time = 0;
- }
-
- //Events that are updated every EVENT_UPDATE_TIME
- switch ((*i).Event.event_type)
- {
- case EVENT_T_TIMER_OOC:
- ProcessEvent(*i);
- break;
- case EVENT_T_TIMER:
- case EVENT_T_MANA:
- case EVENT_T_HP:
- case EVENT_T_TARGET_HP:
- case EVENT_T_TARGET_CASTING:
- case EVENT_T_FRIENDLY_HP:
- if (Combat)
- ProcessEvent(*i);
- break;
- case EVENT_T_RANGE:
- if (Combat)
- {
- if (m_creature->IsWithinDistInMap(m_creature->getVictim(),(float)(*i).Event.event_param2))
- {
- if (m_creature->GetDistance(m_creature->getVictim()) >= (float)(*i).Event.event_param1)
- ProcessEvent(*i);
- }
- }
- break;
- }
- }
-
- EventDiff = 0;
- EventUpdateTime = EVENT_UPDATE_TIME;
- }
- else
- {
- EventDiff += diff;
- EventUpdateTime -= diff;
- }
- }
-
- //Melee Auto-Attack
- if (Combat && MeleeEnabled)
- DoMeleeAttackIfReady();
-
- }
-};
-
-CreatureAI* GetAI_mob_eventai(Creature* pCreature)
-{
- //Select events by creature id
- std::list<EventHolder> EventList;
-
- //Find creature id in the Event map
- UNORDERED_MAP<uint32, std::vector<EventAI_Event> >::iterator CreatureEvents = EventAI_Event_Map.find(pCreature->GetEntry());
-
- if (CreatureEvents != EventAI_Event_Map.end())
- {
- std::vector<EventAI_Event>::iterator i;
-
- for (i = (*CreatureEvents).second.begin(); i != (*CreatureEvents).second.end(); ++i)
- {
- //Debug check
-#ifndef _DEBUG
- if ((*i).event_flags & EFLAG_DEBUG_ONLY)
- continue;
-#endif
- if (pCreature->GetMap()->IsDungeon())
- {
- if ((pCreature->GetMap()->IsHeroic() && (*i).event_flags & EFLAG_HEROIC) ||
- (!pCreature->GetMap()->IsHeroic() && (*i).event_flags & EFLAG_NORMAL))
- {
- //event flagged for instance mode
- EventList.push_back(EventHolder(*i));
- }
- continue;
- }
-
- EventList.push_back(EventHolder(*i));
- }
-
- //EventMap had events but they were not added because they must be for instance
- if (EventList.empty())
- {
- if (EAI_ErrorLevel > 1)
- error_db_log("SD2: CreatureId has events but no events added to list because of instance flags.", pCreature->GetEntry());
- }
- }
- else
- {
- if (EAI_ErrorLevel > 1)
- error_db_log("SD2: EventMap for Creature %u is empty but creature is using Mob_EventAI.", pCreature->GetEntry());
- }
-
- return new Mob_EventAI(pCreature, EventList);
-}
-
-bool ReceiveEmote_mob_eventai(Player* pPlayer, Creature* pCreature, uint32 uiEmote)
-{
- if(pCreature->isCharmed())
- return true;
-
- Mob_EventAI* pTmpCreature = (Mob_EventAI*)(pCreature->AI());
-
- if (pTmpCreature->bEmptyList)
- return true;
-
- for (std::list<EventHolder>::iterator itr = pTmpCreature->EventList.begin(); itr != pTmpCreature->EventList.end(); ++itr)
- {
- if ((*itr).Event.event_type == EVENT_T_RECEIVE_EMOTE)
- {
- if ((*itr).Event.event_param1 != uiEmote)
- return true;
-
- bool bProcess = false;
-
- switch((*itr).Event.event_param2)
- {
- //enum ConditionType
- case CONDITION_NONE: // 0 0
- bProcess = true;
- break;
- case CONDITION_AURA: // spell_id effindex
- if (pPlayer->HasAura((*itr).Event.event_param3,(*itr).Event.event_param4))
- bProcess = true;
- break;
- case CONDITION_ITEM: // item_id count
- if (pPlayer->HasItemCount((*itr).Event.event_param3,(*itr).Event.event_param4))
- bProcess = true;
- break;
- case CONDITION_ITEM_EQUIPPED: // item_id count
- if (pPlayer->HasItemOrGemWithIdEquipped((*itr).Event.event_param3,(*itr).Event.event_param4))
- bProcess = true;
- break;
- case CONDITION_ZONEID: // zone_id 0
- if (pPlayer->GetZoneId() == (*itr).Event.event_param3)
- bProcess = true;
- break;
- case CONDITION_REPUTATION_RANK: // faction_id min_rank
- if (pPlayer->GetReputationRank((*itr).Event.event_param3) >= (*itr).Event.event_param4)
- bProcess = true;
- break;
- case CONDITION_TEAM: // player_team 0, (469 - Alliance 67 - Horde)
- if (pPlayer->GetTeam() == (*itr).Event.event_param3)
- bProcess = true;
- break;
- case CONDITION_SKILL: // skill_id min skill_value
- if (pPlayer->HasSkill((*itr).Event.event_param3) && pPlayer->GetSkillValue((*itr).Event.event_param3) >= (*itr).Event.event_param4)
- bProcess = true;
- break;
- case CONDITION_QUESTREWARDED: // quest_id 0
- if (pPlayer->GetQuestRewardStatus((*itr).Event.event_param3))
- bProcess = true;
- break;
- case CONDITION_QUESTTAKEN: // quest_id 0, for condition true while quest active.
- if (pPlayer->GetQuestStatus((*itr).Event.event_param3) == QUEST_STATUS_INCOMPLETE)
- bProcess = true;
- break;
- case CONDITION_ACTIVE_EVENT: // event_id 0
- if (IsHolidayActive(HolidayIds((*itr).Event.event_param3)))
- bProcess = true;
- break;
- }
-
- if (bProcess)
- {
- debug_log("SD2: ReceiveEmote EventAI: Condition ok, processing");
- pTmpCreature->ProcessEvent(*itr, pPlayer);
- }
- }
- }
-
- return true;
-}
-
-void AddSC_mob_event()
-{
- Script *newscript;
- newscript = new Script;
- newscript->Name = "mob_eventai";
- newscript->GetAI = &GetAI_mob_eventai;
- newscript->pReceiveEmote = &ReceiveEmote_mob_eventai;
- newscript->RegisterSelf();
-}
-
diff --git a/src/bindings/scripts/scripts/creature/mob_event_ai.h b/src/bindings/scripts/scripts/creature/mob_event_ai.h
index 7c56b7932dc..f6c9b663ce0 100644
--- a/src/bindings/scripts/scripts/creature/mob_event_ai.h
+++ b/src/bindings/scripts/scripts/creature/mob_event_ai.h
@@ -1,223 +1,4 @@
-/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
- * This program is free software licensed under GPL version 2
- * Please see the included DOCS/LICENSE.TXT for more information */
-
#ifndef SC_EVENTAI_H
#define SC_EVENTAI_H
-
-#define MAX_ACTIONS 3
-
-enum Event_Types
-{
- EVENT_T_TIMER = 0, //InitialMin, InitialMax, RepeatMin, RepeatMax
- EVENT_T_TIMER_OOC = 1, //InitialMin, InitialMax, RepeatMin, RepeatMax
- EVENT_T_HP = 2, //HPMax%, HPMin%, RepeatMin, RepeatMax
- EVENT_T_MANA = 3, //ManaMax%,ManaMin% RepeatMin, RepeatMax
- EVENT_T_AGGRO = 4, //NONE
- EVENT_T_KILL = 5, //RepeatMin, RepeatMax
- EVENT_T_DEATH = 6, //NONE
- EVENT_T_EVADE = 7, //NONE
- EVENT_T_SPELLHIT = 8, //SpellID, School, RepeatMin, RepeatMax
- EVENT_T_RANGE = 9, //MinDist, MaxDist, RepeatMin, RepeatMax
- EVENT_T_OOC_LOS = 10, //NoHostile, NoFriendly, RepeatMin, RepeatMax
- EVENT_T_SPAWNED = 11, //NONE
- EVENT_T_TARGET_HP = 12, //HPMax%, HPMin%, RepeatMin, RepeatMax
- EVENT_T_TARGET_CASTING = 13, //RepeatMin, RepeatMax
- EVENT_T_FRIENDLY_HP = 14, //HPDeficit, Radius, RepeatMin, RepeatMax
- EVENT_T_FRIENDLY_IS_CC = 15, //DispelType, Radius, RepeatMin, RepeatMax
- EVENT_T_FRIENDLY_MISSING_BUFF = 16, //SpellId, Radius, RepeatMin, RepeatMax
- EVENT_T_SUMMONED_UNIT = 17, //CreatureId, RepeatMin, RepeatMax
- EVENT_T_TARGET_MANA = 18, //ManaMax%, ManaMin%, RepeatMin, RepeatMax
- EVENT_T_QUEST_ACCEPT = 19, //QuestID
- EVENT_T_QUEST_COMPLETE = 20, //
- EVENT_T_REACHED_HOME = 21, //NONE
- EVENT_T_RECEIVE_EMOTE = 22, //EmoteId, Condition, CondValue1, CondValue2
-
- EVENT_T_END,
-};
-
-enum Action_Types
-{
- ACTION_T_NONE = 0, //No action
- ACTION_T_TEXT = 1, //-TextId1, optionally -TextId2, optionally -TextId3(if -TextId2 exist). If more than just -TextId1 is defined, randomize. Negative values.
- ACTION_T_SET_FACTION = 2, //FactionId (or 0 for default)
- ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3, //Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph)
- ACTION_T_SOUND = 4, //SoundId
- ACTION_T_EMOTE = 5, //EmoteId
- ACTION_T_RANDOM_SAY = 6, //UNUSED
- ACTION_T_RANDOM_YELL = 7, //UNUSED
- ACTION_T_RANDOM_TEXTEMOTE = 8, //UNUSED
- ACTION_T_RANDOM_SOUND = 9, //SoundId1, SoundId2, SoundId3 (-1 in any field means no output if randomed that field)
- ACTION_T_RANDOM_EMOTE = 10, //EmoteId1, EmoteId2, EmoteId3 (-1 in any field means no output if randomed that field)
- ACTION_T_CAST = 11, //SpellId, Target, CastFlags
- ACTION_T_SUMMON = 12, //CreatureID, Target, Duration in ms
- ACTION_T_THREAT_SINGLE_PCT = 13, //Threat%, Target
- ACTION_T_THREAT_ALL_PCT = 14, //Threat%
- ACTION_T_QUEST_EVENT = 15, //QuestID, Target
- ACTION_T_CASTCREATUREGO = 16, //QuestID, SpellId, Target
- ACTION_T_SET_UNIT_FIELD = 17, //Field_Number, Value, Target
- ACTION_T_SET_UNIT_FLAG = 18, //Flags (may be more than one field OR'd together), Target
- ACTION_T_REMOVE_UNIT_FLAG = 19, //Flags (may be more than one field OR'd together), Target
- ACTION_T_AUTO_ATTACK = 20, //AllowAttackState (0 = stop attack, anything else means continue attacking)
- ACTION_T_COMBAT_MOVEMENT = 21, //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking)
- ACTION_T_SET_PHASE = 22, //Phase
- ACTION_T_INC_PHASE = 23, //Value (may be negative to decrement phase, should not be 0)
- ACTION_T_EVADE = 24, //No Params
- ACTION_T_FLEE = 25, //No Params
- ACTION_T_QUEST_EVENT_ALL = 26, //QuestID
- ACTION_T_CASTCREATUREGO_ALL = 27, //QuestId, SpellId
- ACTION_T_REMOVEAURASFROMSPELL = 28, //Target, Spellid
- ACTION_T_RANGED_MOVEMENT = 29, //Distance, Angle
- ACTION_T_RANDOM_PHASE = 30, //PhaseId1, PhaseId2, PhaseId3
- ACTION_T_RANDOM_PHASE_RANGE = 31, //PhaseMin, PhaseMax
- ACTION_T_SUMMON_ID = 32, //CreatureId, Target, SpawnId
- ACTION_T_KILLED_MONSTER = 33, //CreatureId, Target
- ACTION_T_SET_INST_DATA = 34, //Field, Data
- ACTION_T_SET_INST_DATA64 = 35, //Field, Target
- ACTION_T_UPDATE_TEMPLATE = 36, //Entry, Team
- ACTION_T_DIE = 37, //No Params
- ACTION_T_ZONE_COMBAT_PULSE = 38, //No Params
-
- ACTION_T_SET_ACTIVE = 101, //Apply
- ACTION_T_SET_AGGRESSIVE = 102, //Apply
- ACTION_T_ATTACK_START_PULSE = 103, //Distance
-
- ACTION_T_END,
-};
-
-enum Target
-{
- //Self (m_creature)
- TARGET_T_SELF = 0, //Self cast
-
- //Hostile targets (if pet then returns pet owner)
- TARGET_T_HOSTILE, //Our current target (ie: highest aggro)
- TARGET_T_HOSTILE_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks)
- TARGET_T_HOSTILE_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for)
- TARGET_T_HOSTILE_RANDOM, //Just any random target on our threat list
- TARGET_T_HOSTILE_RANDOM_NOT_TOP, //Any random target except top threat
-
- //Invoker targets (if pet then returns pet owner)
- TARGET_T_ACTION_INVOKER, //Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF)
-
- //Hostile targets (including pets)
- TARGET_T_HOSTILE_WPET, //Current target (can be a pet)
- TARGET_T_HOSTILE_WPET_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks)
- TARGET_T_HOSTILE_WPET_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for)
- TARGET_T_HOSTILE_WPET_RANDOM, //Just any random target on our threat list
- TARGET_T_HOSTILE_WPET_RANDOM_NOT_TOP, //Any random target except top threat
-
- TARGET_T_ACTION_INVOKER_WPET,
-
- TARGET_T_END
-};
-
-enum CastFlags
-{
- CAST_INTURRUPT_PREVIOUS = 0x01, //Interrupt any spell casting
- CAST_TRIGGERED = 0x02, //Triggered (this makes spell cost zero mana and have no cast time)
- CAST_FORCE_CAST = 0x04, //Forces cast even if creature is out of mana or out of range
- CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range
- CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself
- CAST_AURA_NOT_PRESENT = 0x20, //Only casts the spell if the target does not have an aura from the spell
-};
-
-enum EventFlags
-{
- EFLAG_REPEATABLE = 0x01, //Event repeats
- EFLAG_NORMAL = 0x02, //Event only occurs in Normal instance difficulty
- EFLAG_HEROIC = 0x04, //Event only occurs in Heroic instance difficulty
- EFLAG_RESERVED_3 = 0x08,
- EFLAG_RESERVED_4 = 0x10,
- EFLAG_RESERVED_5 = 0x20,
- EFLAG_RESERVED_6 = 0x40,
- EFLAG_DEBUG_ONLY = 0x80, //Event only occurs in debug build of SD2 only
-};
-
-struct EventAI_Event
-{
- uint32 event_id;
-
- uint32 creature_id;
-
- uint16 event_type;
- uint32 event_inverse_phase_mask;
- uint8 event_chance;
- uint8 event_flags;
- union
- {
- uint32 event_param1;
- int32 event_param1_s;
- };
- union
- {
- uint32 event_param2;
- int32 event_param2_s;
- };
- union
- {
- uint32 event_param3;
- int32 event_param3_s;
- };
- union
- {
- uint32 event_param4;
- int32 event_param4_s;
- };
-
- struct _action
- {
- uint16 type;
- union
- {
- uint32 param1;
- int32 param1_s;
- };
- union
- {
- uint32 param2;
- int32 param2_s;
- };
- union
- {
- uint32 param3;
- int32 param3_s;
- };
- }action[MAX_ACTIONS];
-};
-
-//Event_Map
-extern UNORDERED_MAP<uint32, std::vector<EventAI_Event> > EventAI_Event_Map;
-
-struct EventAI_Summon
-{
- uint32 id;
-
- float position_x;
- float position_y;
- float position_z;
- float orientation;
- uint32 SpawnTimeSecs;
-};
-
-//EventSummon_Map
-extern UNORDERED_MAP<uint32, EventAI_Summon> EventAI_Summon_Map;
-
-//EventAI Error handling
-extern uint32 EAI_ErrorLevel;
-/*
-
-struct EventAI_CreatureError
-{
- bool ListEmpty;
- bool NoInstance;
-};
-
-//Error prevention list
-extern UNORDERED_MAP<uint32, EventAI_CreatureError> EventAI_CreatureErrorPreventionList;
-
-//Defines
-#define EVENTAI_EMPTY_EVENTLIST "SD2: Eventlist for Creature %i is empty but creature is using Mob_EventAI. Preventing EventAI on this creature."
-*/
#endif
diff --git a/src/game/Creature.h b/src/game/Creature.h
index 02111ea9f70..0b09a00b84f 100644
--- a/src/game/Creature.h
+++ b/src/game/Creature.h
@@ -325,6 +325,29 @@ enum InhabitTypeValues
INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR
};
+// Enums used by StringTextData::Type (CreatureEventAI)
+enum ChatType
+{
+ CHAT_TYPE_SAY = 0,
+ CHAT_TYPE_YELL = 1,
+ CHAT_TYPE_TEXT_EMOTE = 2,
+ CHAT_TYPE_BOSS_EMOTE = 3,
+ CHAT_TYPE_WHISPER = 4,
+ CHAT_TYPE_BOSS_WHISPER = 5,
+ CHAT_TYPE_ZONE_YELL = 6
+};
+
+//Selection method used by SelectTarget (CreatureEventAI)
+enum AttackingTarget
+{
+ ATTACKING_TARGET_RANDOM = 0, //Just selects a random target
+ ATTACKING_TARGET_TOPAGGRO, //Selects targes from top aggro to bottom
+ ATTACKING_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top
+ ATTACKING_TARGET_RANDOM_PLAYER, //Just selects a random target (player only)
+ ATTACKING_TARGET_TOPAGGRO_PLAYER, //Selects targes from top aggro to bottom (player only)
+ ATTACKING_TARGET_BOTTOMAGGRO_PLAYER, //Selects targets from bottom aggro to top (player only)
+};
+
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
#if defined( __GNUC__ )
#pragma pack()
diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp
index 45608ef317e..e7043c6042f 100644
--- a/src/game/CreatureAI.cpp
+++ b/src/game/CreatureAI.cpp
@@ -70,6 +70,38 @@ void CreatureAI::OnCharmed(bool apply)
me->IsAIEnabled = false;
}
+void CreatureAI::DoZoneInCombat(Unit* pUnit)
+{
+ if (!pUnit)
+ pUnit = me;
+
+ Map *map = pUnit->GetMap();
+
+ if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated
+ {
+ sLog.outError("DoZoneInCombat call for map that isn't an instance (pUnit entry = %d)", pUnit->GetTypeId() == TYPEID_UNIT ? ((Creature*)pUnit)->GetEntry() : 0);
+ return;
+ }
+
+ if (!pUnit->CanHaveThreatList() || pUnit->getThreatManager().isThreatListEmpty())
+ {
+ sLog.outError("DoZoneInCombat called for creature that either cannot have threat list or has empty threat list (pUnit entry = %d)", pUnit->GetTypeId() == TYPEID_UNIT ? ((Creature*)pUnit)->GetEntry() : 0);
+ return;
+ }
+
+ Map::PlayerList const &PlayerList = map->GetPlayers();
+ for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ {
+ if (Player* i_pl = i->getSource())
+ if (i_pl->isAlive())
+ {
+ pUnit->SetInCombatWith(i_pl);
+ i_pl->SetInCombatWith(pUnit);
+ pUnit->AddThreat(i_pl, 0.0f);
+ }
+ }
+}
+
void CreatureAI::MoveInLineOfSight(Unit *who)
{
if(!me->getVictim() && me->canStartAttack(who))
diff --git a/src/game/CreatureAI.h b/src/game/CreatureAI.h
index 7388aab786c..11d5bd372fd 100644
--- a/src/game/CreatureAI.h
+++ b/src/game/CreatureAI.h
@@ -158,6 +158,8 @@ class TRINITY_DLL_SPEC CreatureAI : public UnitAI
// Called at reaching home after evade
virtual void JustReachedHome() {}
+
+ void DoZoneInCombat(Unit* pUnit = NULL);
};
struct SelectableAI : public FactoryHolder<CreatureAI>, public Permissible<Creature>
diff --git a/src/game/CreatureAIRegistry.cpp b/src/game/CreatureAIRegistry.cpp
index 3d7fe1848fb..6253c06b8bc 100644
--- a/src/game/CreatureAIRegistry.cpp
+++ b/src/game/CreatureAIRegistry.cpp
@@ -26,6 +26,7 @@
#include "PossessedAI.h"
#include "TotemAI.h"
#include "OutdoorPvPObjectiveAI.h"
+#include "CreatureEventAI.h"
#include "RandomMovementGenerator.h"
#include "CreatureAIImpl.h"
#include "MovementGeneratorImpl.h"
@@ -46,6 +47,7 @@ namespace AIRegistry
(new CreatureAIFactory<TotemAI>("TotemAI"))->RegisterSelf();
(new CreatureAIFactory<OutdoorPvPObjectiveAI>("OutdoorPvPObjectiveAI"))->RegisterSelf();
(new CreatureAIFactory<PossessedAI>("PossessedAI"))->RegisterSelf();
+ (new CreatureAIFactory<CreatureEventAI>("EventAI"))->RegisterSelf();
(new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf();
(new MovementGeneratorFactory<WaypointMovementGenerator<Creature> >(WAYPOINT_MOTION_TYPE))->RegisterSelf();
diff --git a/src/game/CreatureEventAI.cpp b/src/game/CreatureEventAI.cpp
new file mode 100644
index 00000000000..daf5c74ca72
--- /dev/null
+++ b/src/game/CreatureEventAI.cpp
@@ -0,0 +1,1661 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Common.h"
+#include "CreatureEventAI.h"
+#include "CreatureEventAIMgr.h"
+#include "ObjectMgr.h"
+#include "Spell.h"
+#include "World.h"
+#include "Cell.h"
+#include "CellImpl.h"
+#include "GameEventMgr.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "WorldPacket.h"
+#include "InstanceData.h"
+
+int CreatureEventAI::Permissible(const Creature *creature)
+{
+ if( creature->GetCreatureInfo()->AIName == "EventAI" )
+ return PERMIT_BASE_SPECIAL;
+ return PERMIT_BASE_NO;
+}
+
+CreatureEventAI::CreatureEventAI(Creature *c) : CreatureAI(c), m_creature(*c), InCombat(false)
+{
+ CreatureEventAI_Event_Map::iterator CreatureEvents = CreatureEAI_Mgr.GetCreatureEventAIMap().find(m_creature.GetEntry());
+ if (CreatureEvents != CreatureEAI_Mgr.GetCreatureEventAIMap().end())
+ {
+ std::vector<CreatureEventAI_Event>::iterator i;
+ for (i = (*CreatureEvents).second.begin(); i != (*CreatureEvents).second.end(); ++i)
+ {
+
+ //Debug check
+ #ifndef _DEBUG
+ if ((*i).event_flags & EFLAG_DEBUG_ONLY)
+ continue;
+ #endif
+ if(((*i).event_flags & (EFLAG_HEROIC | EFLAG_NORMAL)) && m_creature.GetMap()->IsDungeon() )
+ {
+ if( (m_creature.GetMap()->IsHeroic() && (*i).event_flags & EFLAG_HEROIC) ||
+ (!m_creature.GetMap()->IsHeroic() && (*i).event_flags & EFLAG_NORMAL))
+ {
+ //event flagged for instance mode
+ CreatureEventAIList.push_back(CreatureEventAIHolder(*i));
+ }
+ continue;
+ }
+ CreatureEventAIList.push_back(CreatureEventAIHolder(*i));
+ }
+ //EventMap had events but they were not added because they must be for instance
+ if (CreatureEventAIList.empty())
+ sLog.outError("CreatureEventAI: CreatureId has events but no events added to list because of instance flags.", m_creature.GetEntry());
+ }
+ else
+ sLog.outError("CreatureEventAI: EventMap for Creature %u is empty but creature is using CreatureEventAI.", m_creature.GetEntry());
+
+ bEmptyList = CreatureEventAIList.empty();
+ Phase = 0;
+ CombatMovementEnabled = true;
+ MeleeEnabled = true;
+ AttackDistance = 0;
+ AttackAngle = 0.0f;
+
+ //Handle Spawned Events
+ if (!bEmptyList)
+ {
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ if ((*i).Event.event_type == EVENT_T_SPAWNED)
+ ProcessEvent(*i);
+ }
+ }
+}
+
+bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker)
+{
+ if (!pHolder.Enabled || pHolder.Time)
+ return false;
+
+ //Check the inverse phase mask (event doesn't trigger if current phase bit is set in mask)
+ if (pHolder.Event.event_inverse_phase_mask & (1 << Phase))
+ return false;
+
+ //Store random here so that all random actions match up
+ uint32 rnd = rand();
+
+ //Return if chance for event is not met
+ if (pHolder.Event.event_chance <= rnd % 100)
+ return false;
+
+ union
+ {
+ uint32 param1;
+ int32 param1_s;
+ };
+
+ union
+ {
+ uint32 param2;
+ int32 param2_s;
+ };
+
+ union
+ {
+ uint32 param3;
+ int32 param3_s;
+ };
+
+ union
+ {
+ uint32 param4;
+ int32 param4_s;
+ };
+
+ param1 = pHolder.Event.event_param1;
+ param2 = pHolder.Event.event_param2;
+ param3 = pHolder.Event.event_param3;
+ param4 = pHolder.Event.event_param4;
+
+ //Check event conditions based on the event type, also reset events
+ switch (pHolder.Event.event_type)
+ {
+ case EVENT_T_TIMER:
+ {
+ if (!InCombat)
+ return false;
+
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_TIMER_OOC:
+ {
+ if (InCombat)
+ return false;
+
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_HP:
+ {
+ if (!InCombat || !m_creature.GetMaxHealth())
+ return false;
+
+ uint32 perc = (m_creature.GetHealth()*100) / m_creature.GetMaxHealth();
+
+ if (perc > param1 || perc < param2)
+ return false;
+
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_MANA:
+ {
+ if (!InCombat || !m_creature.GetMaxPower(POWER_MANA))
+ return false;
+
+ uint32 perc = (m_creature.GetPower(POWER_MANA)*100) / m_creature.GetMaxPower(POWER_MANA);
+
+ if (perc > param1 || perc < param2)
+ return false;
+
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_AGGRO:
+ {
+ }
+ break;
+ case EVENT_T_KILL:
+ {
+ //Repeat Timers
+ if (param1 == param2)
+ {
+ pHolder.Time = param1;
+
+ }else if (param2 > param1)
+ pHolder.Time = urand(param1, param2);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ case EVENT_T_DEATH:
+ {
+ }
+ break;
+ case EVENT_T_EVADE:
+ {
+ }
+ break;
+ case EVENT_T_SPELLHIT:
+ {
+ //Spell hit is special case, param1 and param2 handled within CreatureEventAI::SpellHit
+
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_RANGE:
+ {
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_OOC_LOS:
+ {
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_SPAWNED:
+ {
+ }
+ break;
+ case EVENT_T_TARGET_HP:
+ {
+ if (!InCombat || !m_creature.getVictim() || !m_creature.getVictim()->GetMaxHealth())
+ return false;
+
+ uint32 perc = (m_creature.getVictim()->GetHealth()*100) / m_creature.getVictim()->GetMaxHealth();
+
+ if (perc > param1 || perc < param2)
+ return false;
+
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_TARGET_CASTING:
+ {
+ if (!InCombat || !m_creature.getVictim() || !m_creature.getVictim()->IsNonMeleeSpellCasted(false, false, true))
+ return false;
+
+ //Repeat Timers
+ if (param1 == param2)
+ {
+ pHolder.Time = param1;
+
+ }else if (param2 > param1)
+ pHolder.Time = urand(param1, param2);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_FRIENDLY_HP:
+ {
+ if (!InCombat)
+ return false;
+
+ Unit* pUnit = DoSelectLowestHpFriendly(param2, param1);
+
+ if (!pUnit)
+ return false;
+
+ pActionInvoker = pUnit;
+
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_FRIENDLY_IS_CC:
+ {
+ if (!InCombat)
+ return false;
+
+ std::list<Creature*> pList;
+ DoFindFriendlyCC(pList, param2);
+
+ //List is empty
+ if (pList.empty())
+ return false;
+
+ //We don't really care about the whole list, just return first available
+ pActionInvoker = *(pList.begin());
+
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_FRIENDLY_MISSING_BUFF:
+ {
+ std::list<Creature*> pList;
+ DoFindFriendlyMissingBuff(pList, param2, param1);
+
+ //List is empty
+ if (pList.empty())
+ return false;
+
+ //We don't really care about the whole list, just return first available
+ pActionInvoker = *(pList.begin());
+
+ //Repeat Timers
+ if (param3 == param4)
+ {
+ pHolder.Time = param3;
+
+ }else if (param4 > param3)
+ pHolder.Time = urand(param3, param4);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_SUMMONED_UNIT:
+ {
+ //Prevent event from occuring on no unit or non creatures
+ if (!pActionInvoker || pActionInvoker->GetTypeId()!=TYPEID_UNIT)
+ return false;
+
+ //Creature id doesn't match up
+ if (param1 && ((Creature*)pActionInvoker)->GetEntry() != param1)
+ return false;
+
+ //Repeat Timers
+ if (param2 == param3)
+ {
+ pHolder.Time = param2;
+
+ }else if (param3 > param2)
+ pHolder.Time = urand(param2, param3);
+ else
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ pHolder.Enabled = false;
+ }
+ }
+ break;
+ case EVENT_T_REACHED_HOME:
+ {
+ }
+ break;
+ case EVENT_T_RECEIVE_EMOTE:
+ {
+ }
+ break;
+ default:
+
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature.GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
+ break;
+ }
+
+ //Disable non-repeatable events
+ if (!(pHolder.Event.event_flags & EFLAG_REPEATABLE))
+ pHolder.Enabled = false;
+
+ //Process actions
+ for (uint32 j = 0; j < MAX_ACTIONS; j++)
+ ProcessAction(pHolder.Event.action[j].type, pHolder.Event.action[j].param1, pHolder.Event.action[j].param2, pHolder.Event.action[j].param3, rnd, pHolder.Event.event_id, pActionInvoker);
+
+ return true;
+}
+
+void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, uint32 param3, uint32 rnd, uint32 EventId, Unit* pActionInvoker)
+{
+ switch (type)
+ {
+ case ACTION_T_TEXT:
+ {
+ if (!param1)
+ return;
+
+ uint32 temp = 0;
+
+ if (param2 && param3)
+ {
+ switch( rand()%3 )
+ {
+ case 0: temp = param1; break;
+ case 2: temp = param2; break;
+ case 3: temp = param3; break;
+ }
+ }else if ( param2 && urand(0,1) )
+ {
+ temp = param2;
+ }else
+ {
+ temp = param1;
+ }
+
+ if (temp)
+ {
+ Unit* target = NULL;
+ Unit* owner = NULL;
+
+ if (pActionInvoker)
+ {
+ if (pActionInvoker->GetTypeId() == TYPEID_PLAYER)
+ target = pActionInvoker;
+ else if (owner = pActionInvoker->GetOwner())
+ {
+ if (owner->GetTypeId() == TYPEID_PLAYER)
+ target = owner;
+ }
+ }
+ else if (target = m_creature.getVictim())
+ {
+ if (target->GetTypeId() != TYPEID_PLAYER)
+ {
+ if (owner = target->GetOwner())
+ {
+ if (owner->GetTypeId() == TYPEID_PLAYER)
+ target = owner;
+ }
+ }
+ }
+
+ DoScriptText(temp, &m_creature, target);
+ }
+ }
+ break;
+ case ACTION_T_SET_FACTION:
+ {
+ if (param1)
+ m_creature.setFaction(param1);
+ else
+ {
+ if (CreatureInfo const* ci = GetCreatureTemplateStore(m_creature.GetEntry()))
+ {
+ //if no id provided, assume reset and then use default
+ if (m_creature.getFaction() != ci->faction_A)
+ m_creature.setFaction(ci->faction_A);
+ }
+ }
+ }
+ break;
+ case ACTION_T_MORPH_TO_ENTRY_OR_MODEL:
+ {
+ if (param1 || param2)
+ {
+ //set model based on entry from creature_template
+ if (param1)
+ {
+ if (CreatureInfo const* ci = GetCreatureTemplateStore(param1))
+ {
+ //use default display
+ if (ci->Modelid1)
+ m_creature.SetDisplayId(ci->Modelid1);
+ }
+ }
+ //if no param1, then use value from param2 (modelId)
+ else
+ m_creature.SetDisplayId(param2);
+ }
+ else
+ m_creature.DeMorph();
+ }
+ break;
+ case ACTION_T_SOUND:
+ m_creature.PlayDirectSound(param1);
+ break;
+ case ACTION_T_EMOTE:
+ m_creature.HandleEmoteCommand(param1);
+ break;
+ case ACTION_T_RANDOM_SOUND:
+ {
+ uint32 temp = GetRandActionParam(rnd, param1, param2, param3);
+
+ if (temp != uint32(0xffffffff))
+ m_creature.PlayDirectSound( temp );
+ }
+ break;
+ case ACTION_T_RANDOM_EMOTE:
+ {
+ uint32 temp = GetRandActionParam(rnd, param1, param2, param3);
+
+ if (temp != uint32(0xffffffff))
+ m_creature.HandleEmoteCommand(temp);
+ }
+ break;
+ case ACTION_T_CAST:
+ {
+ Unit* target = GetTargetByType(param2, pActionInvoker);
+ Unit* caster = &m_creature;
+
+ if (!target)
+ return;
+
+ //Cast is always triggered if target is forced to cast on self
+ if (param3 & CAST_FORCE_TARGET_SELF)
+ {
+ param3 |= CAST_TRIGGERED;
+ caster = target;
+ }
+
+ //Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered
+ bool canCast = !(caster->IsNonMeleeSpellCasted(false) && (param3 & CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS));
+
+ // If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them
+ if(param3 & CAST_AURA_NOT_PRESENT)
+ {
+ if(target->HasAura(param1))
+ return;
+ }
+
+ if (canCast)
+ {
+ const SpellEntry* tSpell = GetSpellStore()->LookupEntry(param1);
+
+ //Verify that spell exists
+ if (tSpell)
+ {
+ //Check if cannot cast spell
+ if (!(param3 & (CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST)) &&
+ !CanCast(target, tSpell, (param3 & CAST_TRIGGERED)))
+ {
+ //Melee current victim if flag not set
+ if (!(param3 & CAST_NO_MELEE_IF_OOM))
+ {
+ if (m_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
+ {
+ AttackDistance = 0;
+ AttackAngle = 0;
+
+ m_creature.GetMotionMaster()->MoveChase(m_creature.getVictim(), AttackDistance, AttackAngle);
+ }
+ }
+
+ }
+ else
+ {
+ //Interrupt any previous spell
+ if (caster->IsNonMeleeSpellCasted(false) && param3 & CAST_INTURRUPT_PREVIOUS)
+ caster->InterruptNonMeleeSpells(false);
+
+ caster->CastSpell(target, param1, (param3 & CAST_TRIGGERED));
+ }
+
+ }else
+ sLog.outErrorDb("CreatureEventAI: event %d creature %d attempt to cast spell that doesn't exist %d", EventId, m_creature.GetEntry(), param1);
+ }
+ }
+ break;
+ case ACTION_T_SUMMON:
+ {
+ Unit* target = GetTargetByType(param2, pActionInvoker);
+
+ Creature* pCreature = NULL;
+
+ if (param3)
+ pCreature = m_creature.SummonCreature(param1, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, param3);
+ else
+ pCreature = m_creature.SummonCreature(param1, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
+
+ if (!pCreature)
+ {
+
+ sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. Spawn event %d is on creature %d", param1, EventId, m_creature.GetEntry());
+ }
+ else if (param2 != TARGET_T_SELF && target)
+ pCreature->AI()->AttackStart(target);
+ }
+ break;
+ case ACTION_T_THREAT_SINGLE_PCT:
+ {
+ Unit* target = GetTargetByType(param2, pActionInvoker);
+
+ if (target)
+ m_creature.getThreatManager().modifyThreatPercent(target, param1);
+ }
+ break;
+ case ACTION_T_THREAT_ALL_PCT:
+ {
+ Unit* Temp = NULL;
+
+ std::list<HostilReference*>::iterator i = m_creature.getThreatManager().getThreatList().begin();
+ for (; i != m_creature.getThreatManager().getThreatList().end(); ++i)
+ {
+ Temp = Unit::GetUnit(m_creature,(*i)->getUnitGuid());
+ if (Temp)
+ m_creature.getThreatManager().modifyThreatPercent(Temp, param1);
+ }
+ }
+ break;
+ case ACTION_T_QUEST_EVENT:
+ {
+ Unit* target = GetTargetByType(param2, pActionInvoker);
+
+ if (target && target->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)target)->AreaExploredOrEventHappens(param1);
+ }
+ break;
+ case ACTION_T_CASTCREATUREGO:
+ {
+ Unit* target = GetTargetByType(param3, pActionInvoker);
+
+ if (target && target->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)target)->CastedCreatureOrGO(param1, m_creature.GetGUID(), param2);
+ }
+ break;
+ case ACTION_T_SET_UNIT_FIELD:
+ {
+ Unit* target = GetTargetByType(param3, pActionInvoker);
+
+ if (param1 < OBJECT_END || param1 >= UNIT_END)
+ return;
+
+ if (target)
+ target->SetUInt32Value(param1, param2);
+ }
+ break;
+ case ACTION_T_SET_UNIT_FLAG:
+ {
+ Unit* target = GetTargetByType(param2, pActionInvoker);
+
+ if (target)
+ target->SetFlag(UNIT_FIELD_FLAGS, param1);
+ }
+ break;
+ case ACTION_T_REMOVE_UNIT_FLAG:
+ {
+ Unit* target = GetTargetByType(param2, pActionInvoker);
+
+ if (target)
+ target->RemoveFlag(UNIT_FIELD_FLAGS, param1);
+ }
+ break;
+ case ACTION_T_AUTO_ATTACK:
+ {
+ if (param1)
+ MeleeEnabled = true;
+ else MeleeEnabled = false;
+ }
+ break;
+ case ACTION_T_COMBAT_MOVEMENT:
+ {
+ CombatMovementEnabled = param1;
+
+ //Allow movement (create new targeted movement gen only if idle)
+ if (CombatMovementEnabled)
+ {
+ m_creature.GetMotionMaster()->MoveChase(m_creature.getVictim(), AttackDistance, AttackAngle);
+ }
+ else
+ {
+ m_creature.GetMotionMaster()->MoveIdle();
+ }
+ }
+ break;
+ case ACTION_T_SET_PHASE:
+ {
+ Phase = param1;
+ }
+ break;
+ case ACTION_T_INC_PHASE:
+ {
+ Phase += param1;
+
+ if (Phase > 31)
+
+ sLog.outErrorDb( "CreatureEventAI: Event %d incremented Phase above 31. Phase mask cannot be used with phases past 31. CreatureEntry = %d", EventId, m_creature.GetEntry());
+ }
+ break;
+ case ACTION_T_EVADE:
+ {
+ EnterEvadeMode();
+ }
+ break;
+ case ACTION_T_FLEE:
+ {
+ if(m_creature.HasAuraType(SPELL_AURA_PREVENTS_FLEEING))
+ break;
+ TimetoFleeLeft = 8000;
+ m_creature.DoFleeToGetAssistance();
+ IsFleeing = true;
+ }
+ break;
+ case ACTION_T_QUEST_EVENT_ALL:
+ {
+ Unit* Temp = NULL;
+ if( pActionInvoker && pActionInvoker->GetTypeId() == TYPEID_PLAYER )
+ {
+ Temp = Unit::GetUnit(m_creature,pActionInvoker->GetGUID());
+ if( Temp )
+ ((Player*)Temp)->GroupEventHappens(param1,&m_creature);
+ }
+ }
+ break;
+ case ACTION_T_CASTCREATUREGO_ALL:
+ {
+ Unit* Temp = NULL;
+
+ std::list<HostilReference*>::iterator i = m_creature.getThreatManager().getThreatList().begin();
+ for (; i != m_creature.getThreatManager().getThreatList().end(); ++i)
+ {
+ Temp = Unit::GetUnit(m_creature,(*i)->getUnitGuid());
+ if (Temp && Temp->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)Temp)->CastedCreatureOrGO(param1, m_creature.GetGUID(), param2);
+ }
+ }
+ break;
+ case ACTION_T_REMOVEAURASFROMSPELL:
+ {
+ Unit* target = GetTargetByType(param1, pActionInvoker);
+
+ if (target)
+ target->RemoveAurasDueToSpell(param2);
+ }
+ break;
+ case ACTION_T_RANGED_MOVEMENT:
+ {
+ AttackDistance = param1;
+ AttackAngle = ((float)param2/180)*M_PI;
+
+ if (CombatMovementEnabled)
+ {
+ m_creature.GetMotionMaster()->MoveChase(m_creature.getVictim(), AttackDistance, AttackAngle);
+ }
+ }
+ break;
+ case ACTION_T_RANDOM_PHASE:
+ {
+ uint32 temp = GetRandActionParam(rnd, param1, param2, param3);
+
+ Phase = temp;
+ }
+ break;
+ case ACTION_T_RANDOM_PHASE_RANGE:
+ {
+ if (param2 > param1)
+ {
+ Phase = param1 + (rnd % (param2 - param1));
+ }
+ else
+ sLog.outErrorDb( "CreatureEventAI: ACTION_T_RANDOM_PHASE_RANGE cannot have Param2 <= Param1. Divide by Zero. Event = %d. CreatureEntry = %d", EventId, m_creature.GetEntry());
+ }
+ break;
+ case ACTION_T_SUMMON_ID:
+ {
+ Unit* target = GetTargetByType(param2, pActionInvoker);
+
+ //Duration
+ Creature* pCreature = NULL;
+
+ CreatureEventAI_Summon_Map::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAISummonMap().find(param3);
+ if (i == CreatureEAI_Mgr.GetCreatureEventAISummonMap().end())
+ {
+
+ sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", param1, param3, EventId, m_creature.GetEntry());
+ return;
+ }
+
+ if ((*i).second.SpawnTimeSecs)
+ pCreature = m_creature.SummonCreature(param1, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs);
+ else pCreature = m_creature.SummonCreature(param1, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
+
+ if (!pCreature)
+ {
+
+ sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. EventId %d.Creature %d", param1, EventId, m_creature.GetEntry());
+ }
+ else if (param2 != TARGET_T_SELF && target)
+ pCreature->AI()->AttackStart(target);
+ }
+ break;
+ case ACTION_T_KILLED_MONSTER:
+ {
+ //first attempt player who tapped creature
+ if (Player* pPlayer = m_creature.GetLootRecipient())
+ pPlayer->RewardPlayerAndGroupAtEvent(param1, &m_creature);
+ else
+ {
+ //if not available, use pActionInvoker
+ Unit* pTarget = GetTargetByType(param2, pActionInvoker);
+
+ if (Player* pPlayer = pTarget->GetCharmerOrOwnerPlayerOrPlayerItself())
+ pPlayer->RewardPlayerAndGroupAtEvent(param1, &m_creature);
+ }
+ }
+ break;
+ case ACTION_T_SET_INST_DATA:
+ {
+ InstanceData* pInst = (InstanceData*)m_creature.GetInstanceData();
+ if (!pInst)
+ {
+ sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data without instance script. Creature %d", EventId, m_creature.GetEntry());
+ return;
+ }
+
+ pInst->SetData(param1, param2);
+ }
+ break;
+ case ACTION_T_SET_INST_DATA64:
+ {
+ Unit* target = GetTargetByType(param2, pActionInvoker);
+ if (!target)
+ {
+ sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 but Target == NULL. Creature %d", EventId, m_creature.GetEntry());
+ return;
+ }
+
+ InstanceData* pInst = (InstanceData*)m_creature.GetInstanceData();
+ if (!pInst)
+ {
+ sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 without instance script. Creature %d", EventId, m_creature.GetEntry());
+ return;
+ }
+
+ pInst->SetData64(param1, target->GetGUID());
+ }
+ break;
+ case ACTION_T_UPDATE_TEMPLATE:
+ {
+ if (m_creature.GetEntry() == param1)
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d", EventId, m_creature.GetEntry());
+ return;
+ }
+
+ m_creature.UpdateEntry(param1, param2 ? HORDE : ALLIANCE);
+ }
+ break;
+ case ACTION_T_DIE:
+ {
+ if (m_creature.isDead())
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_DIE on dead creature. Creature %d", EventId, m_creature.GetEntry());
+ return;
+ }
+ m_creature.DealDamage(&m_creature, m_creature.GetMaxHealth(),NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ }
+ break;
+ case ACTION_T_ZONE_COMBAT_PULSE:
+ {
+ if (!m_creature.isInCombat() || !m_creature.GetMap()->IsDungeon())
+ {
+
+ sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_ZONE_COMBAT_PULSE on creature out of combat or in non-dungeon map. Creature %d", EventId, m_creature.GetEntry());
+ return;
+ }
+
+ DoZoneInCombat(&m_creature);
+ }
+ break;
+
+ // TRINITY ONLY
+ case ACTION_T_SET_ACTIVE:
+ me->setActive(param1 ? true : false);
+ break;
+ case ACTION_T_SET_AGGRESSIVE:
+ me->SetReactState(ReactStates(param1));
+ break;
+ case ACTION_T_ATTACK_START_PULSE:
+ AttackStart(me->SelectNearestTarget((float)param1));
+ break;
+ }
+}
+
+void CreatureEventAI::JustRespawned()
+{
+ InCombat = false;
+ IsFleeing = false;
+ Reset();
+
+ if (bEmptyList)
+ return;
+
+ //Handle Spawned Events
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ if ((*i).Event.event_type == EVENT_T_SPAWNED)
+ ProcessEvent(*i);
+ }
+}
+
+void CreatureEventAI::Reset()
+{
+ EventUpdateTime = EVENT_UPDATE_TIME;
+ EventDiff = 0;
+
+ TimetoFleeLeft = 0;
+ IsFleeing = false;
+
+ if (bEmptyList)
+ return;
+
+ //Reset all events to enabled
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ switch ((*i).Event.event_type)
+ {
+ //Reset all out of combat timers
+ case EVENT_T_TIMER_OOC:
+ {
+ if ((*i).Event.event_param2 == (*i).Event.event_param1)
+ {
+ (*i).Time = (*i).Event.event_param1;
+ (*i).Enabled = true;
+ }
+ else if ((*i).Event.event_param2 > (*i).Event.event_param1)
+ {
+ (*i).Time = urand((*i).Event.event_param1, (*i).Event.event_param2);
+ (*i).Enabled = true;
+ }
+ else
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature.GetEntry(), (*i).Event.event_id, (*i).Event.event_type);
+ }
+ break;
+ //default:
+ //TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void Aggro()
+ //(*i).Enabled = true;
+ //(*i).Time = 0;
+ //break;
+ }
+ }
+}
+
+void CreatureEventAI::JustReachedHome()
+{
+ m_creature.LoadCreaturesAddon();
+
+ if (!bEmptyList)
+ {
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ if ((*i).Event.event_type == EVENT_T_REACHED_HOME)
+ ProcessEvent(*i);
+ }
+ }
+
+ Reset();
+}
+
+void CreatureEventAI::EnterEvadeMode()
+{
+ m_creature.InterruptNonMeleeSpells(true);
+ m_creature.RemoveAllAuras();
+ m_creature.DeleteThreatList();
+ m_creature.CombatStop();
+
+ if (m_creature.isAlive())
+ m_creature.GetMotionMaster()->MoveTargetedHome();
+
+ m_creature.SetLootRecipient(NULL);
+
+ InCombat = false;
+
+ if (bEmptyList)
+ return;
+
+ //Handle Evade events
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ if ((*i).Event.event_type == EVENT_T_EVADE)
+ ProcessEvent(*i);
+ }
+}
+
+void CreatureEventAI::JustDied(Unit* killer)
+{
+ InCombat = false;
+ IsFleeing = false;
+ Reset();
+
+ if (bEmptyList)
+ return;
+
+ //Handle Evade events
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ if ((*i).Event.event_type == EVENT_T_DEATH)
+ ProcessEvent(*i, killer);
+ }
+}
+
+void CreatureEventAI::KilledUnit(Unit* victim)
+{
+ if (bEmptyList || victim->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ if ((*i).Event.event_type == EVENT_T_KILL)
+ ProcessEvent(*i, victim);
+ }
+}
+
+void CreatureEventAI::JustSummoned(Creature* pUnit)
+{
+ if (bEmptyList || !pUnit)
+ return;
+
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ if ((*i).Event.event_type == EVENT_T_SUMMONED_UNIT)
+ ProcessEvent(*i, pUnit);
+ }
+}
+
+void CreatureEventAI::Aggro(Unit *who)
+{
+ //Check for on combat start events
+ if (!bEmptyList)
+ {
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ switch ((*i).Event.event_type)
+ {
+ case EVENT_T_AGGRO:
+ (*i).Enabled = true;
+ ProcessEvent(*i, who);
+ break;
+ //Reset all in combat timers
+ case EVENT_T_TIMER:
+ if ((*i).Event.event_param2 == (*i).Event.event_param1)
+ {
+ (*i).Time = (*i).Event.event_param1;
+ (*i).Enabled = true;
+ }
+ else if ((*i).Event.event_param2 > (*i).Event.event_param1)
+ {
+ (*i).Time = urand((*i).Event.event_param1, (*i).Event.event_param2);
+ (*i).Enabled = true;
+ }
+ else
+ sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature.GetEntry(), (*i).Event.event_id, (*i).Event.event_type);
+ break;
+ //All normal events need to be re-enabled and their time set to 0
+ default:
+ (*i).Enabled = true;
+ (*i).Time = 0;
+ break;
+ }
+ }
+ }
+
+ EventUpdateTime = EVENT_UPDATE_TIME;
+ EventDiff = 0;
+}
+
+void CreatureEventAI::AttackStart(Unit *who)
+{
+ if (!who)
+ return;
+
+ if (m_creature.Attack(who, MeleeEnabled))
+ {
+ if (!InCombat)
+ {
+ InCombat = true;
+ Aggro(who);
+ }
+
+ if (CombatMovementEnabled)
+ {
+ m_creature.GetMotionMaster()->MoveChase(who, AttackDistance, AttackAngle);
+ }
+ else
+ {
+ m_creature.GetMotionMaster()->MoveIdle();
+ }
+ }
+}
+
+void CreatureEventAI::MoveInLineOfSight(Unit *who)
+{
+ if (!who || InCombat)
+ return;
+
+ //Check for OOC LOS Event
+ if (!bEmptyList && !m_creature.getVictim())
+ {
+ for (std::list<CreatureEventAIHolder>::iterator itr = CreatureEventAIList.begin(); itr != CreatureEventAIList.end(); ++itr)
+ {
+ if ((*itr).Event.event_type == EVENT_T_OOC_LOS)
+ {
+ //can trigger if closer than fMaxAllowedRange
+ float fMaxAllowedRange = (*itr).Event.event_param2;
+
+ //if range is ok and we are actually in LOS
+ if (m_creature.IsWithinDistInMap(who, fMaxAllowedRange) && m_creature.IsWithinLOSInMap(who))
+ {
+ //if friendly event&&who is not hostile OR hostile event&&who is hostile
+ if (((*itr).Event.event_param1 && !m_creature.IsHostileTo(who)) ||
+ ((!(*itr).Event.event_param1) && m_creature.IsHostileTo(who)))
+ ProcessEvent(*itr, who);
+ }
+ }
+ }
+ }
+
+ //if (m_creature.isCivilian() && m_creature.IsNeutralToAll())
+ // return;
+
+ if(me->canStartAttack(who))
+ AttackStart(who);
+}
+
+void CreatureEventAI::SpellHit(Unit* pUnit, const SpellEntry* pSpell)
+{
+
+ if (bEmptyList)
+ return;
+
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ if ((*i).Event.event_type == EVENT_T_SPELLHIT)
+ {
+ //If spell id matches (or no spell id) & if spell school matches (or no spell school)
+ if (!(*i).Event.event_param1 || pSpell->Id == (*i).Event.event_param1)
+ {
+ if ((*i).Event.event_param2_s == -1 || pSpell->SchoolMask == (*i).Event.event_param2)
+ ProcessEvent(*i, pUnit);
+ }
+ }
+ }
+}
+
+void CreatureEventAI::UpdateAI(const uint32 diff)
+{
+ //Check if we are in combat (also updates calls threat update code)
+ bool Combat = InCombat ? UpdateVictim() : false;
+
+ //Must return if creature isn't alive. Normally select hostil target and get victim prevent this
+ if (!m_creature.isAlive())
+ return;
+
+ if (IsFleeing)
+ {
+ if(TimetoFleeLeft < diff)
+ {
+ me->SetControlled(false, UNIT_STAT_FLEEING);
+ me->SetNoCallAssistance(false);
+ me->CallAssistance();
+ if(me->getVictim())
+ me->GetMotionMaster()->MoveChase(me->getVictim());
+ IsFleeing = false;
+ }
+ else
+ TimetoFleeLeft -= diff;
+
+ return;
+ }
+
+ if (!bEmptyList)
+ {
+ //Events are only updated once every EVENT_UPDATE_TIME ms to prevent lag with large amount of events
+ if (EventUpdateTime < diff)
+ {
+ EventDiff += diff;
+
+ //Check for time based events
+ for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
+ {
+ //Decrement Timers
+ if ((*i).Time)
+ {
+ if ((*i).Time > EventDiff)
+ {
+ //Do not decrement timers if event cannot trigger in this phase
+ if (!((*i).Event.event_inverse_phase_mask & (1 << Phase)))
+ (*i).Time -= EventDiff;
+
+ //Skip processing of events that have time remaining
+ continue;
+ }
+ else (*i).Time = 0;
+ }
+
+ //Events that are updated every EVENT_UPDATE_TIME
+ switch ((*i).Event.event_type)
+ {
+ case EVENT_T_TIMER_OOC:
+ ProcessEvent(*i);
+ break;
+ case EVENT_T_TIMER:
+ case EVENT_T_MANA:
+ case EVENT_T_HP:
+ case EVENT_T_TARGET_HP:
+ case EVENT_T_TARGET_CASTING:
+ case EVENT_T_FRIENDLY_HP:
+ if (Combat)
+ ProcessEvent(*i);
+ break;
+ case EVENT_T_RANGE:
+ if (Combat)
+ {
+ if (m_creature.IsWithinDistInMap(m_creature.getVictim(),(float)(*i).Event.event_param2))
+ {
+ if (m_creature.GetDistance(m_creature.getVictim()) >= (float)(*i).Event.event_param1)
+ ProcessEvent(*i);
+ }
+ }
+ break;
+ }
+ }
+
+ EventDiff = 0;
+ EventUpdateTime = EVENT_UPDATE_TIME;
+ }
+ else
+ {
+ EventDiff += diff;
+ EventUpdateTime -= diff;
+ }
+ }
+
+ //Melee Auto-Attack
+ if (Combat && MeleeEnabled)
+ DoMeleeAttackIfReady();
+}
+
+inline Unit* CreatureEventAI::SelectUnit(AttackingTarget target, uint32 position)
+{
+ //ThreatList m_threatlist;
+ std::list<HostilReference*>& m_threatlist = m_creature.getThreatManager().getThreatList();
+ std::list<HostilReference*>::iterator i = m_threatlist.begin();
+ std::list<HostilReference*>::reverse_iterator r = m_threatlist.rbegin();
+
+ if (position >= m_threatlist.size() || !m_threatlist.size())
+ return NULL;
+
+ switch (target)
+ {
+ case ATTACKING_TARGET_RANDOM:
+ {
+ advance ( i , position + (rand() % (m_threatlist.size() - position ) ));
+ return Unit::GetUnit(m_creature,(*i)->getUnitGuid());
+ }
+ case ATTACKING_TARGET_TOPAGGRO:
+ {
+ advance ( i , position);
+ return Unit::GetUnit(m_creature,(*i)->getUnitGuid());
+ }
+ case ATTACKING_TARGET_BOTTOMAGGRO:
+ {
+ advance ( r , position);
+ return Unit::GetUnit(m_creature,(*r)->getUnitGuid());
+ }
+ }
+ return NULL;
+}
+
+inline uint32 CreatureEventAI::GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3)
+{
+ switch (rnd % 3)
+ {
+ case 0:
+ return param1;
+ break;
+ case 1:
+ return param2;
+ break;
+ case 2:
+ return param3;
+ break;
+ }
+ return 0;
+}
+
+inline Unit* CreatureEventAI::GetTargetByType(uint32 Target, Unit* pActionInvoker)
+{
+ switch (Target)
+ {
+ case TARGET_T_SELF:
+ return &m_creature;
+ break;
+ case TARGET_T_HOSTILE:
+ return m_creature.getVictim();
+ break;
+ case TARGET_T_HOSTILE_SECOND_AGGRO:
+ return SelectUnit(ATTACKING_TARGET_TOPAGGRO,1);
+ break;
+ case TARGET_T_HOSTILE_LAST_AGGRO:
+ return SelectUnit(ATTACKING_TARGET_BOTTOMAGGRO,0);
+ break;
+ case TARGET_T_HOSTILE_RANDOM:
+ return SelectUnit(ATTACKING_TARGET_RANDOM,0);
+ break;
+ case TARGET_T_HOSTILE_RANDOM_NOT_TOP:
+ return SelectUnit(ATTACKING_TARGET_RANDOM,1);
+ break;
+ case TARGET_T_ACTION_INVOKER:
+ return pActionInvoker;
+ break;
+ default:
+ return NULL;
+ break;
+ };
+}
+
+Unit* CreatureEventAI::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff)
+{
+ CellPair p(MaNGOS::ComputeCellPair(m_creature.GetPositionX(), m_creature.GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ Unit* pUnit = NULL;
+
+ MaNGOS::MostHPMissingInRange u_check(&m_creature, range, MinHPDiff);
+ MaNGOS::UnitLastSearcher<MaNGOS::MostHPMissingInRange> searcher(&m_creature, pUnit, u_check);
+
+ /*
+ typedef TYPELIST_4(GameObject, Creature*except pets*, DynamicObject, Corpse*Bones*) AllGridObjectTypes;
+ This means that if we only search grid then we cannot possibly return pets or players so this is safe
+ */
+ TypeContainerVisitor<MaNGOS::UnitLastSearcher<MaNGOS::MostHPMissingInRange>, GridTypeMapContainer > grid_unit_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *m_creature.GetMap());
+ return pUnit;
+}
+
+void CreatureEventAI::DoFindFriendlyCC(std::list<Creature*>& _list, float range)
+{
+ CellPair p(MaNGOS::ComputeCellPair(m_creature.GetPositionX(), m_creature.GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::FriendlyCCedInRange u_check(&m_creature, range);
+ MaNGOS::CreatureListSearcher<MaNGOS::FriendlyCCedInRange> searcher(&m_creature, _list, u_check);
+
+ TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::FriendlyCCedInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_creature_searcher, *m_creature.GetMap());
+}
+
+void CreatureEventAI::DoFindFriendlyMissingBuff(std::list<Creature*>& _list, float range, uint32 spellid)
+{
+ CellPair p(MaNGOS::ComputeCellPair(m_creature.GetPositionX(), m_creature.GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::FriendlyMissingBuffInRange u_check(&m_creature, range, spellid);
+ MaNGOS::CreatureListSearcher<MaNGOS::FriendlyMissingBuffInRange> searcher(&m_creature, _list, u_check);
+
+ TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::FriendlyMissingBuffInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_creature_searcher, *m_creature.GetMap());
+}
+
+//*********************************
+//*** Functions used globally ***
+
+void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target)
+{
+ if (!pSource)
+ {
+ sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i, invalid Source pointer.",textEntry);
+ return;
+ }
+
+ if (textEntry >= 0)
+ {
+ sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) attempts to process text entry %i, but text entry must be negative.",pSource->GetEntry(),pSource->GetTypeId(),pSource->GetGUIDLow(),textEntry);
+ return;
+ }
+
+ CreatureEventAI_TextMap::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAITextMap().find(textEntry);
+
+ if (i == CreatureEAI_Mgr.GetCreatureEventAITextMap().end())
+ {
+ sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) could not find text entry %i.",pSource->GetEntry(),pSource->GetTypeId(),pSource->GetGUIDLow(),textEntry);
+ return;
+ }
+
+ sLog.outDebug("CreatureEventAI: DoScriptText: text entry=%i, Sound=%u, Type=%u, Language=%u, Emote=%u",textEntry,(*i).second.SoundId,(*i).second.Type,(*i).second.Language,(*i).second.Emote);
+
+ if((*i).second.SoundId)
+ {
+ if (GetSoundEntriesStore()->LookupEntry((*i).second.SoundId))
+ pSource->PlayDirectSound((*i).second.SoundId);
+ else
+ sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process invalid sound id %u.",textEntry,(*i).second.SoundId);
+ }
+
+ if((*i).second.Emote)
+ {
+ if (pSource->GetTypeId() == TYPEID_UNIT || pSource->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Unit*)pSource)->HandleEmoteCommand((*i).second.Emote);
+ }
+ else
+ sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process emote for invalid TypeId (%u).",textEntry,pSource->GetTypeId());
+ }
+
+ switch((*i).second.Type)
+ {
+ case CHAT_TYPE_SAY:
+ pSource->MonsterSay(textEntry, (*i).second.Language, target ? target->GetGUID() : 0);
+ break;
+ case CHAT_TYPE_YELL:
+ pSource->MonsterYell(textEntry, (*i).second.Language, target ? target->GetGUID() : 0);
+ break;
+ case CHAT_TYPE_TEXT_EMOTE:
+ pSource->MonsterTextEmote(textEntry, target ? target->GetGUID() : 0);
+ break;
+ case CHAT_TYPE_BOSS_EMOTE:
+ pSource->MonsterTextEmote(textEntry, target ? target->GetGUID() : 0, true);
+ break;
+ case CHAT_TYPE_WHISPER:
+ {
+ if (target && target->GetTypeId() == TYPEID_PLAYER)
+ pSource->MonsterWhisper(textEntry, target->GetGUID());
+ else sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry);
+ }break;
+ case CHAT_TYPE_BOSS_WHISPER:
+ {
+ if (target && target->GetTypeId() == TYPEID_PLAYER)
+ pSource->MonsterWhisper(textEntry, target->GetGUID(), true);
+ else sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry);
+ }break;
+ case CHAT_TYPE_ZONE_YELL:
+ pSource->MonsterYellToZone(textEntry, (*i).second.Language, target ? target->GetGUID() : 0);
+ break;
+ }
+}
+
+bool CreatureEventAI::CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered)
+{
+ //No target so we can't cast
+ if (!Target || !Spell)
+ return false;
+
+ //Silenced so we can't cast
+ if (!Triggered && me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
+ return false;
+
+ //Check for power
+ if (!Triggered && me->GetPower((Powers)Spell->powerType) < Spell->manaCost)
+ return false;
+
+ SpellRangeEntry const *TempRange = NULL;
+
+ TempRange = GetSpellRangeStore()->LookupEntry(Spell->rangeIndex);
+
+ //Spell has invalid range store so we can't use it
+ if (!TempRange)
+ return false;
+
+ //Unit is out of range of this spell
+ if (me->GetDistance(Target) > me->GetSpellMaxRangeForTarget(Target, TempRange)
+ || me->GetDistance(Target) < me->GetSpellMinRangeForTarget(Target, TempRange))
+ return false;
+
+ return true;
+}
+
+bool CreatureEventAI::ReceiveEmote(Player* pPlayer, Creature* pCreature, uint32 uiEmote)
+{
+ if(pCreature->isCharmed())
+ return true;
+
+ CreatureEventAI* pTmpCreature = (CreatureEventAI*)(pCreature->AI());
+
+ if (pTmpCreature->bEmptyList)
+ return true;
+
+ for (std::list<CreatureEventAIHolder>::iterator itr = pTmpCreature->CreatureEventAIList.begin(); itr != pTmpCreature->CreatureEventAIList.end(); ++itr)
+ {
+ if ((*itr).Event.event_type == EVENT_T_RECEIVE_EMOTE)
+ {
+ if ((*itr).Event.event_param1 != uiEmote)
+ return true;
+
+ bool bProcess = false;
+
+ switch((*itr).Event.event_param2)
+ {
+ //enum ConditionType
+ case CONDITION_NONE: // 0 0
+ bProcess = true;
+ break;
+ case CONDITION_AURA: // spell_id effindex
+ if (pPlayer->HasAura((*itr).Event.event_param3,(*itr).Event.event_param4))
+ bProcess = true;
+ break;
+ case CONDITION_ITEM: // item_id count
+ if (pPlayer->HasItemCount((*itr).Event.event_param3,(*itr).Event.event_param4))
+ bProcess = true;
+ break;
+ case CONDITION_ITEM_EQUIPPED: // item_id count
+ if (pPlayer->HasItemOrGemWithIdEquipped((*itr).Event.event_param3,(*itr).Event.event_param4))
+ bProcess = true;
+ break;
+ case CONDITION_ZONEID: // zone_id 0
+ if (pPlayer->GetZoneId() == (*itr).Event.event_param3)
+ bProcess = true;
+ break;
+ case CONDITION_REPUTATION_RANK: // faction_id min_rank
+ if (pPlayer->GetReputationRank((*itr).Event.event_param3) >= (*itr).Event.event_param4)
+ bProcess = true;
+ break;
+ case CONDITION_TEAM: // player_team 0, (469 - Alliance 67 - Horde)
+ if (pPlayer->GetTeam() == (*itr).Event.event_param3)
+ bProcess = true;
+ break;
+ case CONDITION_SKILL: // skill_id min skill_value
+ if (pPlayer->HasSkill((*itr).Event.event_param3) && pPlayer->GetSkillValue((*itr).Event.event_param3) >= (*itr).Event.event_param4)
+ bProcess = true;
+ break;
+ case CONDITION_QUESTREWARDED: // quest_id 0
+ if (pPlayer->GetQuestRewardStatus((*itr).Event.event_param3))
+ bProcess = true;
+ break;
+ case CONDITION_QUESTTAKEN: // quest_id 0, for condition true while quest active.
+ if (pPlayer->GetQuestStatus((*itr).Event.event_param3) == QUEST_STATUS_INCOMPLETE)
+ bProcess = true;
+ break;
+ case CONDITION_ACTIVE_EVENT: // event_id 0
+ if (IsHolidayActive(HolidayIds((*itr).Event.event_param3)))
+ bProcess = true;
+ break;
+ }
+
+ if (bProcess)
+ {
+ sLog.outDebug("CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing");
+ pTmpCreature->ProcessEvent(*itr, pPlayer);
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/src/game/CreatureEventAI.h b/src/game/CreatureEventAI.h
new file mode 100644
index 00000000000..74ea7a40a6a
--- /dev/null
+++ b/src/game/CreatureEventAI.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MANGOS_CREATURE_EAI_H
+#define MANGOS_CREATURE_EAI_H
+
+#include "Common.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "Unit.h"
+
+class Player;
+class WorldObject;
+
+#define EVENT_UPDATE_TIME 500
+#define SPELL_RUN_AWAY 8225
+#define MAX_ACTIONS 3
+#define TEXT_SOURCE_RANGE -1000000 //the amount of entries each text source has available
+
+enum Event_Types
+{
+ EVENT_T_TIMER = 0, //InitialMin, InitialMax, RepeatMin, RepeatMax
+ EVENT_T_TIMER_OOC = 1, //InitialMin, InitialMax, RepeatMin, RepeatMax
+ EVENT_T_HP = 2, //HPMax%, HPMin%, RepeatMin, RepeatMax
+ EVENT_T_MANA = 3, //ManaMax%,ManaMin% RepeatMin, RepeatMax
+ EVENT_T_AGGRO = 4, //NONE
+ EVENT_T_KILL = 5, //RepeatMin, RepeatMax
+ EVENT_T_DEATH = 6, //NONE
+ EVENT_T_EVADE = 7, //NONE
+ EVENT_T_SPELLHIT = 8, //SpellID, School, RepeatMin, RepeatMax
+ EVENT_T_RANGE = 9, //MinDist, MaxDist, RepeatMin, RepeatMax
+ EVENT_T_OOC_LOS = 10, //NoHostile, NoFriendly, RepeatMin, RepeatMax
+ EVENT_T_SPAWNED = 11, //NONE
+ EVENT_T_TARGET_HP = 12, //HPMax%, HPMin%, RepeatMin, RepeatMax
+ EVENT_T_TARGET_CASTING = 13, //RepeatMin, RepeatMax
+ EVENT_T_FRIENDLY_HP = 14, //HPDeficit, Radius, RepeatMin, RepeatMax
+ EVENT_T_FRIENDLY_IS_CC = 15, //DispelType, Radius, RepeatMin, RepeatMax
+ EVENT_T_FRIENDLY_MISSING_BUFF = 16, //SpellId, Radius, RepeatMin, RepeatMax
+ EVENT_T_SUMMONED_UNIT = 17, //CreatureId, RepeatMin, RepeatMax
+ EVENT_T_TARGET_MANA = 18, //ManaMax%, ManaMin%, RepeatMin, RepeatMax
+ EVENT_T_QUEST_ACCEPT = 19, //QuestID
+ EVENT_T_QUEST_COMPLETE = 20, //
+ EVENT_T_REACHED_HOME = 21, //NONE
+ EVENT_T_RECEIVE_EMOTE = 22, //EmoteId, Condition, CondValue1, CondValue2
+
+ EVENT_T_END,
+};
+
+enum Action_Types
+{
+ ACTION_T_NONE = 0, //No action
+ ACTION_T_TEXT = 1, //-TextId1, optionally -TextId2, optionally -TextId3(if -TextId2 exist). If more than just -TextId1 is defined, randomize. Negative values.
+ ACTION_T_SET_FACTION = 2, //FactionId (or 0 for default)
+ ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3, //Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph)
+ ACTION_T_SOUND = 4, //SoundId
+ ACTION_T_EMOTE = 5, //EmoteId
+ ACTION_T_RANDOM_SAY = 6, //UNUSED
+ ACTION_T_RANDOM_YELL = 7, //UNUSED
+ ACTION_T_RANDOM_TEXTEMOTE = 8, //UNUSED
+ ACTION_T_RANDOM_SOUND = 9, //SoundId1, SoundId2, SoundId3 (-1 in any field means no output if randomed that field)
+ ACTION_T_RANDOM_EMOTE = 10, //EmoteId1, EmoteId2, EmoteId3 (-1 in any field means no output if randomed that field)
+ ACTION_T_CAST = 11, //SpellId, Target, CastFlags
+ ACTION_T_SUMMON = 12, //CreatureID, Target, Duration in ms
+ ACTION_T_THREAT_SINGLE_PCT = 13, //Threat%, Target
+ ACTION_T_THREAT_ALL_PCT = 14, //Threat%
+ ACTION_T_QUEST_EVENT = 15, //QuestID, Target
+ ACTION_T_CASTCREATUREGO = 16, //QuestID, SpellId, Target
+ ACTION_T_SET_UNIT_FIELD = 17, //Field_Number, Value, Target
+ ACTION_T_SET_UNIT_FLAG = 18, //Flags (may be more than one field OR'd together), Target
+ ACTION_T_REMOVE_UNIT_FLAG = 19, //Flags (may be more than one field OR'd together), Target
+ ACTION_T_AUTO_ATTACK = 20, //AllowAttackState (0 = stop attack, anything else means continue attacking)
+ ACTION_T_COMBAT_MOVEMENT = 21, //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking)
+ ACTION_T_SET_PHASE = 22, //Phase
+ ACTION_T_INC_PHASE = 23, //Value (may be negative to decrement phase, should not be 0)
+ ACTION_T_EVADE = 24, //No Params
+ ACTION_T_FLEE = 25, //No Params
+ ACTION_T_QUEST_EVENT_ALL = 26, //QuestID
+ ACTION_T_CASTCREATUREGO_ALL = 27, //QuestId, SpellId
+ ACTION_T_REMOVEAURASFROMSPELL = 28, //Target, Spellid
+ ACTION_T_RANGED_MOVEMENT = 29, //Distance, Angle
+ ACTION_T_RANDOM_PHASE = 30, //PhaseId1, PhaseId2, PhaseId3
+ ACTION_T_RANDOM_PHASE_RANGE = 31, //PhaseMin, PhaseMax
+ ACTION_T_SUMMON_ID = 32, //CreatureId, Target, SpawnId
+ ACTION_T_KILLED_MONSTER = 33, //CreatureId, Target
+ ACTION_T_SET_INST_DATA = 34, //Field, Data
+ ACTION_T_SET_INST_DATA64 = 35, //Field, Target
+ ACTION_T_UPDATE_TEMPLATE = 36, //Entry, Team
+ ACTION_T_DIE = 37, //No Params
+ ACTION_T_ZONE_COMBAT_PULSE = 38, //No Params
+
+ ACTION_T_SET_ACTIVE = 101, //Apply
+ ACTION_T_SET_AGGRESSIVE = 102, //Apply
+ ACTION_T_ATTACK_START_PULSE = 103, //Distance
+
+ ACTION_T_END,
+};
+
+enum Target
+{
+ //Self (m_creature)
+ TARGET_T_SELF = 0, //Self cast
+
+ //Hostile targets (if pet then returns pet owner)
+ TARGET_T_HOSTILE, //Our current target (ie: highest aggro)
+ TARGET_T_HOSTILE_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks)
+ TARGET_T_HOSTILE_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for)
+ TARGET_T_HOSTILE_RANDOM, //Just any random target on our threat list
+ TARGET_T_HOSTILE_RANDOM_NOT_TOP, //Any random target except top threat
+
+ //Invoker targets (if pet then returns pet owner)
+ TARGET_T_ACTION_INVOKER, //Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF)
+
+ //Hostile targets (including pets)
+ TARGET_T_HOSTILE_WPET, //Current target (can be a pet)
+ TARGET_T_HOSTILE_WPET_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks)
+ TARGET_T_HOSTILE_WPET_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for)
+ TARGET_T_HOSTILE_WPET_RANDOM, //Just any random target on our threat list
+ TARGET_T_HOSTILE_WPET_RANDOM_NOT_TOP, //Any random target except top threat
+
+ TARGET_T_ACTION_INVOKER_WPET,
+
+ TARGET_T_END
+};
+
+enum CastFlags
+{
+ CAST_INTURRUPT_PREVIOUS = 0x01, //Interrupt any spell casting
+ CAST_TRIGGERED = 0x02, //Triggered (this makes spell cost zero mana and have no cast time)
+ CAST_FORCE_CAST = 0x04, //Forces cast even if creature is out of mana or out of range
+ CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range
+ CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself
+ CAST_AURA_NOT_PRESENT = 0x20, //Only casts the spell if the target does not have an aura from the spell
+};
+
+enum EventFlags
+{
+ EFLAG_REPEATABLE = 0x01, //Event repeats
+ EFLAG_NORMAL = 0x02, //Event only occurs in Normal instance difficulty
+ EFLAG_HEROIC = 0x04, //Event only occurs in Heroic instance difficulty
+ EFLAG_RESERVED_3 = 0x08,
+ EFLAG_RESERVED_4 = 0x10,
+ EFLAG_RESERVED_5 = 0x20,
+ EFLAG_RESERVED_6 = 0x40,
+ EFLAG_DEBUG_ONLY = 0x80, //Event only occurs in debug build of SD2 only
+};
+
+// String text additional data, used in (CreatureEventAI)
+struct StringTextData
+{
+ uint32 SoundId;
+ uint8 Type;
+ uint32 Language;
+ uint32 Emote;
+};
+// Text Maps
+typedef UNORDERED_MAP<int32, StringTextData> CreatureEventAI_TextMap;
+
+struct CreatureEventAI_Event
+{
+ uint32 event_id;
+
+ uint32 creature_id;
+
+ uint16 event_type;
+ uint32 event_inverse_phase_mask;
+ uint8 event_chance;
+ uint8 event_flags;
+ union
+ {
+ uint32 event_param1;
+ int32 event_param1_s;
+ };
+ union
+ {
+ uint32 event_param2;
+ int32 event_param2_s;
+ };
+ union
+ {
+ uint32 event_param3;
+ int32 event_param3_s;
+ };
+ union
+ {
+ uint32 event_param4;
+ int32 event_param4_s;
+ };
+
+ struct _action
+ {
+ uint16 type;
+ union
+ {
+ uint32 param1;
+ int32 param1_s;
+ };
+ union
+ {
+ uint32 param2;
+ int32 param2_s;
+ };
+ union
+ {
+ uint32 param3;
+ int32 param3_s;
+ };
+ }action[MAX_ACTIONS];
+};
+//Event_Map
+typedef UNORDERED_MAP<uint32, std::vector<CreatureEventAI_Event> > CreatureEventAI_Event_Map;
+
+struct CreatureEventAI_Summon
+{
+ uint32 id;
+
+ float position_x;
+ float position_y;
+ float position_z;
+ float orientation;
+ uint32 SpawnTimeSecs;
+};
+
+//EventSummon_Map
+typedef UNORDERED_MAP<uint32, CreatureEventAI_Summon> CreatureEventAI_Summon_Map;
+
+struct CreatureEventAIHolder
+{
+ CreatureEventAIHolder(CreatureEventAI_Event p) : Event(p), Time(0), Enabled(true){}
+
+ CreatureEventAI_Event Event;
+ uint32 Time;
+ bool Enabled;
+};
+
+class TRINITY_DLL_SPEC CreatureEventAI : public CreatureAI
+{
+
+ public:
+ CreatureEventAI(Creature *c);
+ ~CreatureEventAI()
+ {
+ CreatureEventAIList.clear();
+ }
+ void JustRespawned();
+ void Reset();
+ void JustReachedHome();
+ void EnterEvadeMode();
+ void JustDied(Unit* killer);
+ void KilledUnit(Unit* victim);
+ void JustSummoned(Creature* pUnit);
+ void Aggro(Unit *who);
+ void AttackStart(Unit *who);
+ void MoveInLineOfSight(Unit *who);
+ void SpellHit(Unit* pUnit, const SpellEntry* pSpell);
+ void UpdateAI(const uint32 diff);
+ static int Permissible(const Creature *);
+
+ bool ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker = NULL);
+ void ProcessAction(uint16 type, uint32 param1, uint32 param2, uint32 param3, uint32 rnd, uint32 EventId, Unit* pActionInvoker);
+ inline uint32 GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3);
+ inline Unit* GetTargetByType(uint32 Target, Unit* pActionInvoker);
+ inline Unit* SelectUnit(AttackingTarget target, uint32 position);
+
+ void DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target);
+ bool CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered);
+ bool ReceiveEmote(Player* pPlayer, Creature* pCreature, uint32 uiEmote);
+
+ Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff);
+ void DoFindFriendlyMissingBuff(std::list<Creature*>& _list, float range, uint32 spellid);
+ void DoFindFriendlyCC(std::list<Creature*>& _list, float range);
+
+ //Pointer to creature we are manipulating
+ Creature& m_creature;
+
+ //Bool for if we are in combat or not
+ bool InCombat;
+
+ //Holder for events (stores enabled, time, and eventid)
+ std::list<CreatureEventAIHolder> CreatureEventAIList;
+ uint32 EventUpdateTime; //Time between event updates
+ uint32 EventDiff; //Time between the last event call
+ bool bEmptyList;
+
+ //Variables used by Events themselves
+ uint8 Phase; //Current phase, max 32 phases
+ bool CombatMovementEnabled; //If we allow targeted movment gen (movement twoards top threat)
+ bool MeleeEnabled; //If we allow melee auto attack
+ uint32 AttackDistance; //Distance to attack from
+ float AttackAngle; //Angle of attack
+
+ uint32 TimetoFleeLeft;
+ bool IsFleeing;
+};
+#endif
diff --git a/src/game/CreatureEventAIMgr.cpp b/src/game/CreatureEventAIMgr.cpp
new file mode 100644
index 00000000000..3e6c2822759
--- /dev/null
+++ b/src/game/CreatureEventAIMgr.cpp
@@ -0,0 +1,560 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Database/SQLStorage.h"
+#include "CreatureEventAI.h"
+#include "CreatureEventAIMgr.h"
+#include "ObjectMgr.h"
+#include "ProgressBar.h"
+#include "Policies/SingletonImp.h"
+#include "ObjectDefines.h"
+
+INSTANTIATE_SINGLETON_1(CreatureEventAIMgr);
+
+// -------------------
+void CreatureEventAIMgr::LoadCreatureEventAI_Texts()
+{
+ // Drop Existing Text Map, only done once and we are ready to add data from multiple sources.
+ m_CreatureEventAI_TextMap.clear();
+
+ // Load EventAI Text
+ LoadTrinityStrings(WorldDatabase,"creature_ai_texts",-1,1+(TEXT_SOURCE_RANGE));
+
+ // Gather Additional data from EventAI Texts
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry, sound, type, language, emote FROM creature_ai_texts");
+
+ sLog.outString("Loading EventAI Texts additional data...");
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+ uint32 count = 0;
+
+ do
+ {
+ bar.step();
+ Field* fields = result->Fetch();
+ StringTextData temp;
+
+ int32 i = fields[0].GetInt32();
+ temp.SoundId = fields[1].GetInt32();
+ temp.Type = fields[2].GetInt32();
+ temp.Language = fields[3].GetInt32();
+ temp.Emote = fields[4].GetInt32();
+
+ if (i >= 0)
+ {
+ sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` is not a negative value.",i);
+ continue;
+ }
+
+ if (i <= TEXT_SOURCE_RANGE)
+ {
+ sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` is out of accepted entry range for table.",i);
+ continue;
+ }
+
+ if (temp.SoundId)
+ {
+ if (!GetSoundEntriesStore()->LookupEntry(temp.SoundId))
+ sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has soundId %u but sound does not exist.",i,temp.SoundId);
+ }
+
+ if (!GetLanguageDescByID(temp.Language))
+ sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` using Language %u but Language does not exist.",i,temp.Language);
+
+ if (temp.Type > CHAT_TYPE_BOSS_WHISPER)
+ sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Type %u but this Chat Type does not exist.",i,temp.Type);
+
+ m_CreatureEventAI_TextMap[i] = temp;
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u additional CreatureEventAI Texts data.", count);
+ }else
+ {
+ barGoLink bar(1);
+ bar.step();
+ sLog.outString();
+ sLog.outString(">> Loaded 0 additional CreatureEventAI Texts data. DB table `creature_ai_texts` is empty.");
+ }
+
+}
+
+// -------------------
+void CreatureEventAIMgr::LoadCreatureEventAI_Summons()
+{
+
+ //Drop Existing EventSummon Map
+ m_CreatureEventAI_Summon_Map.clear();
+
+ //Gather additional data for EventAI
+ QueryResult *result = WorldDatabase.PQuery("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM creature_ai_summons");
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+ uint32 Count = 0;
+
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+
+ CreatureEventAI_Summon temp;
+
+ uint32 i = fields[0].GetUInt32();
+ temp.position_x = fields[1].GetFloat();
+ temp.position_y = fields[2].GetFloat();
+ temp.position_z = fields[3].GetFloat();
+ temp.orientation = fields[4].GetFloat();
+ temp.SpawnTimeSecs = fields[5].GetUInt32();
+
+ //Add to map
+ m_CreatureEventAI_Summon_Map[i] = temp;
+ ++Count;
+ }while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u CreatureEventAI summon definitions", Count);
+ }else
+ {
+ barGoLink bar(1);
+ bar.step();
+ sLog.outString();
+ sLog.outString(">> Loaded 0 CreatureEventAI Summon definitions. DB table `creature_ai_summons` is empty.");
+ }
+
+}
+
+// -------------------
+void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
+{
+ //Drop Existing EventAI List
+ m_CreatureEventAI_Event_Map.clear();
+
+ //Gather event data
+ QueryResult *result = WorldDatabase.PQuery("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, "
+ "event_param1, event_param2, event_param3, event_param4, "
+ "action1_type, action1_param1, action1_param2, action1_param3, "
+ "action2_type, action2_param1, action2_param2, action2_param3, "
+ "action3_type, action3_param1, action3_param2, action3_param3 "
+ "FROM creature_ai_scripts");
+ if (result)
+ {
+ barGoLink bar(result->GetRowCount());
+ uint32 Count = 0;
+
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+
+ CreatureEventAI_Event temp;
+
+ temp.event_id = fields[0].GetUInt32();
+ uint32 i = temp.event_id;
+ temp.creature_id = fields[1].GetUInt32();
+ uint32 creature_id = temp.creature_id;
+ temp.event_type = fields[2].GetUInt16();
+ temp.event_inverse_phase_mask = fields[3].GetUInt32();
+ temp.event_chance = fields[4].GetUInt8();
+ temp.event_flags = fields[5].GetUInt8();
+ temp.event_param1 = fields[6].GetUInt32();
+ temp.event_param2 = fields[7].GetUInt32();
+ temp.event_param3 = fields[8].GetUInt32();
+ temp.event_param4 = fields[9].GetUInt32();
+
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id);
+ //Creature does not exist in database
+ if (!cInfo)
+ {
+ sLog.outErrorDb("CreatureEventAI: Event %u has script for non-existing creature.", i);
+ continue;
+ }
+
+ //Report any errors in event
+ if (temp.event_type >= EVENT_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u has incorrect event type. Maybe DB requires updated version of SD2.", i);
+
+ //No chance of this event occuring
+ if (temp.event_chance == 0)
+ sLog.outErrorDb("CreatureEventAI: Event %u has 0 percent chance. Event will never trigger!", i);
+
+ //Chance above 100, force it to be 100
+ if (temp.event_chance > 100)
+ {
+ sLog.outErrorDb("CreatureEventAI: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i);
+ temp.event_chance = 100;
+ }
+
+ //Individual event checks
+ switch (temp.event_type)
+ {
+ case EVENT_T_HP:
+ case EVENT_T_MANA:
+ case EVENT_T_TARGET_HP:
+ {
+ if (temp.event_param2 > 100)
+ sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i);
+
+ if (temp.event_param1 <= temp.event_param2)
+ sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i);
+
+ if (temp.event_flags & EFLAG_REPEATABLE && !temp.event_param3 && !temp.event_param4)
+ {
+ 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);
+ temp.event_flags &= ~EFLAG_REPEATABLE;
+ }
+ }
+ break;
+
+ case EVENT_T_SPELLHIT:
+ {
+ if (temp.event_param1)
+ {
+ SpellEntry const* pSpell = GetSpellStore()->LookupEntry(temp.event_param1);
+ if (!pSpell)
+ {
+ sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.event_param1, i);
+ continue;
+ }
+
+ if (temp.event_param2_s != -1 && temp.event_param2 != pSpell->SchoolMask)
+ 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.event_param1, i);
+ }
+
+ //TODO: fix this system with SPELL_SCHOOL_MASK. Current complicate things, using int32(-1) instead of just 0
+ //SPELL_SCHOOL_MASK_NONE = 0 and does not exist, thus it can not ever trigger or be used in SpellHit()
+ if (temp.event_param2_s != -1 && temp.event_param2_s > SPELL_SCHOOL_MASK_ALL)
+ sLog.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.event_param2, i);
+
+ if (temp.event_param4 < temp.event_param3)
+ sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
+ }
+ break;
+
+ case EVENT_T_RANGE:
+ case EVENT_T_OOC_LOS:
+ case EVENT_T_FRIENDLY_HP:
+ case EVENT_T_FRIENDLY_IS_CC:
+ case EVENT_T_FRIENDLY_MISSING_BUFF:
+ {
+ if (temp.event_param4 < temp.event_param3)
+ sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
+ }
+ break;
+
+ case EVENT_T_TIMER:
+ case EVENT_T_TIMER_OOC:
+ {
+ if (temp.event_param2 < temp.event_param1)
+ sLog.outErrorDb("CreatureEventAI: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i);
+
+ if (temp.event_param4 < temp.event_param3)
+ sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
+ }
+ break;
+
+ case EVENT_T_KILL:
+ case EVENT_T_TARGET_CASTING:
+ {
+ if (temp.event_param2 < temp.event_param1)
+ sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
+ }
+ break;
+
+ case EVENT_T_AGGRO:
+ case EVENT_T_DEATH:
+ case EVENT_T_EVADE:
+ case EVENT_T_SPAWNED:
+ case EVENT_T_REACHED_HOME:
+ {
+ if (temp.event_flags & EFLAG_REPEATABLE)
+ {
+ sLog.outErrorDb("CreatureEventAI: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i);
+ temp.event_flags &= ~EFLAG_REPEATABLE;
+ }
+ }
+ break;
+ }
+
+ for (uint32 j = 0; j < MAX_ACTIONS; j++)
+ {
+ temp.action[j].type = fields[10+(j*4)].GetUInt16();
+ temp.action[j].param1 = fields[11+(j*4)].GetUInt32();
+ temp.action[j].param2 = fields[12+(j*4)].GetUInt32();
+ temp.action[j].param3 = fields[13+(j*4)].GetUInt32();
+
+ //Report any errors in actions
+ switch (temp.action[j].type)
+ {
+ case ACTION_T_TEXT:
+ {
+ if (temp.action[j].param1_s < 0)
+ {
+ if (m_CreatureEventAI_TextMap.find(temp.action[j].param1_s) == m_CreatureEventAI_TextMap.end())
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 refrences non-existing entry in texts table.", i, j+1);
+ }
+ if (temp.action[j].param2_s < 0)
+ {
+ if (m_CreatureEventAI_TextMap.find(temp.action[j].param2_s) == m_CreatureEventAI_TextMap.end())
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 refrences non-existing entry in texts table.", i, j+1);
+
+ if (!temp.action[j].param1_s)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param2, but param1 is not set. Required for randomized text.", i, j+1);
+ }
+ if (temp.action[j].param3_s < 0)
+ {
+ if (m_CreatureEventAI_TextMap.find(temp.action[j].param3_s) == m_CreatureEventAI_TextMap.end())
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 refrences non-existing entry in texts table.", i, j+1);
+
+ if (!temp.action[j].param1_s || !temp.action[j].param2_s)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param3, but param1 and/or param2 is not set. Required for randomized text.", i, j+1);
+ }
+ }
+ break;
+ case ACTION_T_SET_FACTION:
+ if (temp.action[j].param1 !=0 && !GetFactionStore()->LookupEntry(temp.action[j].param1))
+ {
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant FactionId %u.", i, j+1, temp.action[j].param1);
+ temp.action[j].param1 = 0;
+ }
+ break;
+ case ACTION_T_MORPH_TO_ENTRY_OR_MODEL:
+ if (temp.action[j].param1 !=0 || temp.action[j].param2 !=0)
+ {
+ if (temp.action[j].param1 && !GetCreatureTemplateStore(temp.action[j].param1))
+ {
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i, j+1, temp.action[j].param1);
+ temp.action[j].param1 = 0;
+ }
+
+ if (temp.action[j].param2 && !GetCreatureDisplayStore()->LookupEntry(temp.action[j].param2))
+ {
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant ModelId %u.", i, j+1, temp.action[j].param2);
+ temp.action[j].param2 = 0;
+ }
+ }
+ break;
+ case ACTION_T_SOUND:
+ if (!GetSoundEntriesStore()->LookupEntry(temp.action[j].param1))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i, j+1, temp.action[j].param1);
+ break;
+/*
+ case ACTION_T_RANDOM_SOUND:
+ {
+ if(!GetSoundEntriesStore()->LookupEntry(temp.action[j].param1))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses non-existant SoundID %u.", i, j+1, temp.action[j].param1);
+ if(!GetSoundEntriesStore()->LookupEntry(temp.action[j].param2))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses non-existant SoundID %u.", i, j+1, temp.action[j].param2);
+ if(!GetSoundEntriesStore()->LookupEntry(temp.action[j].param3))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses non-existant SoundID %u.", i, j+1, temp.action[j].param3);
+ }
+ break;
+ */
+ case ACTION_T_CAST:
+ {
+ const SpellEntry *spell = GetSpellStore()->LookupEntry(temp.action[j].param1);
+ if (!spell)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param1);
+ else
+ {
+ if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE)
+ {
+ //output as debug for now, also because there's no general rule all spells have RecoveryTime
+ if (temp.event_param3 < spell->RecoveryTime)
+ debug_log("CreatureEventAI: Event %u Action %u uses SpellID %u but cooldown is longer(%u) than minumum defined in event param3(%u).", i, j+1,temp.action[j].param1, spell->RecoveryTime, temp.event_param3);
+ }
+ }
+
+ if (temp.action[j].param2 >= TARGET_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+ case ACTION_T_REMOVEAURASFROMSPELL:
+ {
+ if (!GetSpellStore()->LookupEntry(temp.action[j].param2))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
+
+ if (temp.action[j].param1 >= TARGET_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+ case ACTION_T_QUEST_EVENT:
+ {
+ if (Quest const* qid = GetQuestTemplateStore(temp.action[j].param1))
+ {
+ if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
+ 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, temp.action[j].param1);
+ }
+ else
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1);
+
+ if (temp.action[j].param2 >= TARGET_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+ case ACTION_T_QUEST_EVENT_ALL:
+ {
+ if (Quest const* qid = GetQuestTemplateStore(temp.action[j].param1))
+ {
+ if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
+ 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, temp.action[j].param1);
+ }
+ else
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1);
+ }
+ break;
+ case ACTION_T_CASTCREATUREGO:
+ {
+ if (!GetCreatureTemplateStore(temp.action[j].param1))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
+
+ if (!GetSpellStore()->LookupEntry(temp.action[j].param2))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
+
+ if (temp.action[j].param3 >= TARGET_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+ case ACTION_T_CASTCREATUREGO_ALL:
+ {
+ if (!GetQuestTemplateStore(temp.action[j].param1))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1);
+
+ if (!GetSpellStore()->LookupEntry(temp.action[j].param2))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
+ }
+ break;
+
+ //2nd param target
+ case ACTION_T_SUMMON_ID:
+ {
+ if (!GetCreatureTemplateStore(temp.action[j].param1))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
+
+ if (m_CreatureEventAI_Summon_Map.find(temp.action[j].param3) == m_CreatureEventAI_Summon_Map.end())
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j+1, temp.action[j].param3);
+
+ if (temp.action[j].param2 >= TARGET_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+ case ACTION_T_KILLED_MONSTER:
+ {
+ if (!GetCreatureTemplateStore(temp.action[j].param1))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
+
+ if (temp.action[j].param2 >= TARGET_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+ case ACTION_T_SUMMON:
+ {
+ if (!GetCreatureTemplateStore(temp.action[j].param1))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
+
+ if (temp.action[j].param2 >= TARGET_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+ case ACTION_T_THREAT_SINGLE_PCT:
+ case ACTION_T_SET_UNIT_FLAG:
+ case ACTION_T_REMOVE_UNIT_FLAG:
+ if (temp.action[j].param2 >= TARGET_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+ break;
+
+ //3rd param target
+ case ACTION_T_SET_UNIT_FIELD:
+ if (temp.action[j].param1 < OBJECT_END || temp.action[j].param1 >= UNIT_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j+1);
+ if (temp.action[j].param3 >= TARGET_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+ break;
+
+ case ACTION_T_SET_PHASE:
+ if (temp.action[j].param1 > 31)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase > 31. Phase mask cannot be used past phase 31.", i, j+1);
+ break;
+
+ case ACTION_T_INC_PHASE:
+ if (!temp.action[j].param1)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j+1);
+ break;
+
+ case ACTION_T_SET_INST_DATA:
+ {
+ if (!(temp.event_flags & EFLAG_NORMAL) && !(temp.event_flags & EFLAG_HEROIC))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i, j+1);
+
+ if (temp.action[j].param2 > 4/*SPECIAL*/)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j+1);
+ }
+ break;
+ case ACTION_T_SET_INST_DATA64:
+ {
+ if (!(temp.event_flags & EFLAG_NORMAL) && !(temp.event_flags & EFLAG_HEROIC))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i, j+1);
+
+ if (temp.action[j].param2 >= TARGET_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+ }
+ break;
+ case ACTION_T_UPDATE_TEMPLATE:
+ {
+ if (!GetCreatureTemplateStore(temp.action[j].param1))
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
+ }
+ break;
+ case ACTION_T_RANDOM_SAY:
+ case ACTION_T_RANDOM_YELL:
+ case ACTION_T_RANDOM_TEXTEMOTE:
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j+1);
+ break;
+
+ default:
+ if (temp.action[j].type >= ACTION_T_END)
+ sLog.outErrorDb("CreatureEventAI: Event %u Action %u has incorrect action type. Maybe DB requires updated version of SD2.", i, j+1);
+ break;
+ }
+ }
+
+ //Add to list
+ m_CreatureEventAI_Event_Map[creature_id].push_back(temp);
+ ++Count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u CreatureEventAI scripts", Count);
+ }else
+ {
+ barGoLink bar(1);
+ bar.step();
+ sLog.outString();
+ sLog.outString(">> Loaded 0 CreatureEventAI scripts. DB table `creature_ai_scripts` is empty.");
+ }
+}
diff --git a/src/game/CreatureEventAIMgr.h b/src/game/CreatureEventAIMgr.h
new file mode 100644
index 00000000000..ea5989a74ed
--- /dev/null
+++ b/src/game/CreatureEventAIMgr.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MANGOS_CREATURE_EAI_MGR_H
+#define MANGOS_CREATURE_EAI_MGR_H
+
+#include "Common.h"
+#include "CreatureEventAI.h"
+
+class CreatureEventAIMgr
+{
+ public:
+ CreatureEventAIMgr(){};
+ ~CreatureEventAIMgr(){};
+
+ void LoadCreatureEventAI_Texts();
+ void LoadCreatureEventAI_Summons();
+ void LoadCreatureEventAI_Scripts();
+
+ CreatureEventAI_Event_Map& GetCreatureEventAIMap() { return m_CreatureEventAI_Event_Map; }
+ CreatureEventAI_Summon_Map& GetCreatureEventAISummonMap() { return m_CreatureEventAI_Summon_Map; }
+ CreatureEventAI_TextMap& GetCreatureEventAITextMap() { return m_CreatureEventAI_TextMap; }
+
+ private:
+ CreatureEventAI_Event_Map m_CreatureEventAI_Event_Map;
+ CreatureEventAI_Summon_Map m_CreatureEventAI_Summon_Map;
+ CreatureEventAI_TextMap m_CreatureEventAI_TextMap;
+};
+
+#define CreatureEAI_Mgr MaNGOS::Singleton<CreatureEventAIMgr>::Instance()
+#endif
diff --git a/src/game/CreatureGroups.cpp b/src/game/CreatureGroups.cpp
index ed0fde19eb0..23f72d89816 100644
--- a/src/game/CreatureGroups.cpp
+++ b/src/game/CreatureGroups.cpp
@@ -23,6 +23,7 @@
#include "ObjectMgr.h"
#include "ProgressBar.h"
#include "Policies/SingletonImp.h"
+#include "CreatureAI.h"
#define MAX_DESYNC 5.0f
diff --git a/src/game/GridNotifiers.h b/src/game/GridNotifiers.h
index a7b8c753782..521a543c08c 100644
--- a/src/game/GridNotifiers.h
+++ b/src/game/GridNotifiers.h
@@ -628,6 +628,62 @@ namespace Trinity
// Unit checks
+ class MostHPMissingInRange
+ {
+ public:
+ MostHPMissingInRange(Unit const* obj, float range, uint32 hp) : i_obj(obj), i_range(range), i_hp(hp) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && u->GetMaxHealth() - u->GetHealth() > i_hp)
+ {
+ i_hp = u->GetMaxHealth() - u->GetHealth();
+ return true;
+ }
+ return false;
+ }
+ private:
+ Unit const* i_obj;
+ float i_range;
+ uint32 i_hp;
+ };
+
+ class FriendlyCCedInRange
+ {
+ public:
+ FriendlyCCedInRange(Unit const* obj, float range) : i_obj(obj), i_range(range) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) &&
+ (u->isFeared() || u->isCharmed() || u->isFrozen() || u->hasUnitState(UNIT_STAT_STUNNED) || u->hasUnitState(UNIT_STAT_CONFUSED)))
+ {
+ return true;
+ }
+ return false;
+ }
+ private:
+ Unit const* i_obj;
+ float i_range;
+ };
+
+ class FriendlyMissingBuffInRange
+ {
+ public:
+ FriendlyMissingBuffInRange(Unit const* obj, float range, uint32 spellid) : i_obj(obj), i_range(range), i_spell(spellid) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) &&
+ !(u->HasAura(i_spell, 0) || u->HasAura(i_spell, 1) || u->HasAura(i_spell, 2)))
+ {
+ return true;
+ }
+ return false;
+ }
+ private:
+ Unit const* i_obj;
+ float i_range;
+ uint32 i_spell;
+ };
+
class AnyUnfriendlyUnitInObjectRangeCheck
{
public:
@@ -915,63 +971,6 @@ namespace Trinity
float i_range;
};
- // Searchers used by ScriptedAI
- class MostHPMissingInRange
- {
- public:
- MostHPMissingInRange(Unit const* obj, float range, uint32 hp) : i_obj(obj), i_range(range), i_hp(hp) {}
- bool operator()(Unit* u)
- {
- if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && u->GetMaxHealth() - u->GetHealth() > i_hp)
- {
- i_hp = u->GetMaxHealth() - u->GetHealth();
- return true;
- }
- return false;
- }
- private:
- Unit const* i_obj;
- float i_range;
- uint32 i_hp;
- };
-
- class FriendlyCCedInRange
- {
- public:
- FriendlyCCedInRange(Unit const* obj, float range) : i_obj(obj), i_range(range) {}
- bool operator()(Unit* u)
- {
- if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) &&
- (u->isFeared() || u->isCharmed() || u->isFrozen() || u->hasUnitState(UNIT_STAT_STUNNED) || u->hasUnitState(UNIT_STAT_CONFUSED)))
- {
- return true;
- }
- return false;
- }
- private:
- Unit const* i_obj;
- float i_range;
- };
-
- class FriendlyMissingBuffInRange
- {
- public:
- FriendlyMissingBuffInRange(Unit const* obj, float range, uint32 spellid) : i_obj(obj), i_range(range), i_spell(spellid) {}
- bool operator()(Unit* u)
- {
- if(u->isAlive() && u->isInCombat() && /*!i_obj->IsHostileTo(u)*/ i_obj->IsFriendlyTo(u) && i_obj->IsWithinDistInMap(u, i_range) &&
- !(u->HasAura(i_spell)))
- {
- return true;
- }
- return false;
- }
- private:
- Unit const* i_obj;
- float i_range;
- uint32 i_spell;
- };
-
class AllFriendlyCreaturesInGrid
{
public:
diff --git a/src/game/InstanceData.h b/src/game/InstanceData.h
index b2571de66bd..c33beccfffb 100644
--- a/src/game/InstanceData.h
+++ b/src/game/InstanceData.h
@@ -67,6 +67,10 @@ class TRINITY_DLL_SPEC InstanceData
//called on creature creation
virtual void OnCreatureCreate(Creature * /*creature*/, uint32 /*creature_entry*/) {}
+ //All-purpose data storage 64 bit
+ virtual uint64 GetData64(uint32 /*Data*/) { return 0; }
+ virtual void SetData64(uint32 /*Data*/, uint64 /*Value*/) { }
+
//All-purpose data storage 32 bit
virtual uint32 GetData(uint32) { return 0; }
virtual void SetData(uint32, uint32 data) {}
diff --git a/src/game/Makefile.am b/src/game/Makefile.am
index de27815594a..47c072a1188 100644
--- a/src/game/Makefile.am
+++ b/src/game/Makefile.am
@@ -31,6 +31,7 @@ noinst_LIBRARIES = libmangosgame.a
# libmangossgame library will later be reused by ...
libmangosgame_a_SOURCES = \
<<<<<<< HEAD:src/game/Makefile.am
+<<<<<<< HEAD:src/game/Makefile.am
AccountMgr.cpp \
AccountMgr.h \
AchievementMgr.h \
@@ -572,6 +573,272 @@ libmangosgame_a_SOURCES = \
GroupReference.h \
GroupRefManager.h
>>>>>>> 2429aaf2276d689e101ed88285f18449dbe4280d:src/game/Makefile.am
+=======
+ AccountMgr.cpp \
+ AccountMgr.h \
+ AchievementMgr.h \
+ AchievementMgr.cpp \
+ AggressorAI.cpp \
+ AggressorAI.h \
+ AnimalRandomMovementGenerator.h \
+ ArenaTeam.cpp \
+ ArenaTeam.h \
+ ArenaTeamHandler.cpp \
+ AuctionHouseHandler.cpp \
+ AuctionHouseMgr.cpp \
+ AuctionHouseMgr.h \
+ Bag.cpp \
+ Bag.h \
+ BattleGround.cpp \
+ BattleGroundAA.cpp \
+ BattleGroundAB.cpp \
+ BattleGroundAV.cpp \
+ BattleGroundBE.cpp \
+ BattleGroundDS.cpp \
+ BattleGroundEY.cpp \
+ BattleGroundNA.cpp \
+ BattleGroundRL.cpp \
+ BattleGroundRV.cpp \
+ BattleGroundSA.cpp \
+ BattleGroundWS.cpp \
+ BattleGround.h \
+ BattleGroundAA.h \
+ BattleGroundAB.h \
+ BattleGroundAV.h \
+ BattleGroundBE.h \
+ BattleGroundDS.h \
+ BattleGroundEY.h \
+ BattleGroundNA.h \
+ BattleGroundRL.h \
+ BattleGroundRV.h \
+ BattleGroundSA.h \
+ BattleGroundWS.h \
+ BattleGroundHandler.cpp \
+ BattleGroundMgr.cpp \
+ BattleGroundMgr.h \
+ Calendar.cpp \
+ Calendar.h \
+ CalendarHandler.cpp \
+ Cell.h \
+ CellImpl.h \
+ Channel.cpp \
+ Channel.h \
+ ChannelHandler.cpp \
+ ChannelMgr.h \
+ CharacterHandler.cpp \
+ Chat.cpp \
+ Chat.h \
+ ChatHandler.cpp \
+ CombatHandler.cpp \
+ ConfusedMovementGenerator.cpp \
+ ConfusedMovementGenerator.h \
+ Corpse.cpp \
+ Corpse.h \
+ CreatureAI.cpp \
+ CreatureAI.h \
+ CreatureAIImpl.h \
+ CreatureAIRegistry.cpp \
+ CreatureAIRegistry.h \
+ CreatureAISelector.cpp \
+ CreatureAISelector.h \
+ CreatureEventAI.cpp \
+ CreatureEventAI.h \
+ CreatureEventAIMgr.cpp \
+ CreatureEventAIMgr.h \
+ Creature.cpp \
+ Creature.h \
+ DBCEnums.h \
+ DBCfmt.h \
+ DBCStores.cpp \
+ DBCStores.h \
+ DBCStructure.h \
+ debugcmds.cpp \
+ DestinationHolder.cpp \
+ DestinationHolder.h \
+ DestinationHolderImp.h \
+ DuelHandler.cpp \
+ DynamicObject.cpp \
+ DynamicObject.h \
+ FleeingMovementGenerator.cpp \
+ FleeingMovementGenerator.h \
+ Formulas.h \
+ GameEventMgr.cpp \
+ GameEventMgr.h \
+ GameObject.cpp \
+ GameObject.h \
+ GlobalEvents.cpp \
+ GlobalEvents.h \
+ GMTicketHandler.cpp \
+ GMTicketMgr.cpp \
+ GMTicketMgr.h \
+ GossipDef.cpp \
+ GossipDef.h \
+ GridDefines.h \
+ GridNotifiers.cpp \
+ GridNotifiers.h \
+ GridNotifiersImpl.h \
+ GridStates.cpp \
+ GridStates.h \
+ Group.cpp \
+ Group.h \
+ GroupHandler.cpp \
+ GuardAI.cpp \
+ GuardAI.h \
+ Guild.cpp \
+ Guild.h \
+ GuildHandler.cpp \
+ HomeMovementGenerator.cpp \
+ HomeMovementGenerator.h \
+ HostilRefManager.cpp \
+ HostilRefManager.h \
+ IdleMovementGenerator.cpp \
+ IdleMovementGenerator.h \
+ InstanceData.cpp \
+ InstanceData.h \
+ InstanceSaveMgr.cpp \
+ InstanceSaveMgr.h \
+ Item.cpp \
+ Item.h \
+ ItemEnchantmentMgr.cpp \
+ ItemEnchantmentMgr.h \
+ ItemHandler.cpp \
+ ItemPrototype.h \
+ Language.h \
+ Level0.cpp \
+ Level1.cpp \
+ Level2.cpp \
+ Level3.cpp \
+ LFGHandler.cpp \
+ LootHandler.cpp \
+ LootMgr.cpp \
+ LootMgr.h \
+ Mail.cpp \
+ Mail.h \
+ Map.cpp \
+ Map.h \
+ MapInstanced.cpp \
+ MapInstanced.h \
+ MapManager.cpp \
+ MapManager.h \
+ MapReference.h \
+ MapRefManager.h \
+ MiscHandler.cpp \
+ MotionMaster.cpp \
+ MotionMaster.h \
+ MovementGenerator.cpp \
+ MovementGenerator.h \
+ MovementGeneratorImpl.h \
+ MovementHandler.cpp \
+ NPCHandler.cpp \
+ NPCHandler.h \
+ NullCreatureAI.cpp \
+ NullCreatureAI.h \
+ ObjectAccessor.cpp \
+ ObjectAccessor.h \
+ Object.cpp \
+ ObjectDefines.h \
+ ObjectGridLoader.cpp \
+ ObjectGridLoader.h \
+ Object.h \
+ ObjectMgr.cpp \
+ ObjectMgr.h \
+ ObjectPosSelector.cpp \
+ ObjectPosSelector.h \
+ Opcodes.cpp \
+ Opcodes.h \
+ Path.h \
+ PetAI.cpp \
+ PetAI.h \
+ Pet.cpp \
+ Pet.h \
+ PetHandler.cpp \
+ PetitionsHandler.cpp \
+ Player.cpp \
+ Player.h \
+ PlayerDump.cpp \
+ PlayerDump.h \
+ PointMovementGenerator.cpp \
+ PointMovementGenerator.h \
+ PoolHandler.cpp \
+ PoolHandler.h \
+ QueryHandler.cpp \
+ QuestDef.cpp \
+ QuestDef.h \
+ QuestHandler.cpp \
+ RandomMovementGenerator.cpp \
+ RandomMovementGenerator.h \
+ ReactorAI.cpp \
+ ReactorAI.h \
+ ReputationMgr.cpp \
+ ReputationMgr.h \
+ ScriptCalls.cpp \
+ ScriptCalls.h \
+ SharedDefines.h \
+ SkillHandler.cpp \
+ SpellAuraDefines.h \
+ SpellAuras.cpp \
+ SpellAuras.h \
+ Spell.cpp \
+ SpellEffects.cpp \
+ Spell.h \
+ SkillDiscovery.cpp \
+ SkillDiscovery.h \
+ SkillExtraItems.cpp \
+ SkillExtraItems.h \
+ SpellHandler.cpp \
+ SocialMgr.cpp \
+ SocialMgr.h \
+ SpellMgr.cpp \
+ SpellMgr.h \
+ StatSystem.cpp \
+ TargetedMovementGenerator.cpp \
+ TargetedMovementGenerator.h \
+ TaxiHandler.cpp \
+ TemporarySummon.cpp \
+ TemporarySummon.h \
+ TotemAI.cpp \
+ TotemAI.h \
+ Totem.cpp \
+ Totem.h \
+ TradeHandler.cpp \
+ Transports.cpp \
+ Transports.h \
+ ThreatManager.cpp \
+ ThreatManager.h \
+ Traveller.h \
+ Unit.cpp \
+ Unit.h \
+ UnitEvents.h \
+ UpdateData.cpp \
+ UpdateData.h \
+ UpdateFields.h \
+ UpdateMask.h \
+ Vehicle.cpp \
+ Vehicle.h \
+ VoiceChatHandler.cpp \
+ WaypointManager.cpp \
+ WaypointManager.h \
+ WaypointMovementGenerator.cpp \
+ WaypointMovementGenerator.h \
+ Weather.cpp \
+ Weather.h \
+ World.cpp \
+ World.h \
+ WorldLog.cpp \
+ WorldLog.h \
+ WorldSession.cpp \
+ WorldSession.h \
+ WorldSocket.cpp \
+ WorldSocket.h \
+ WorldSocketMgr.cpp \
+ WorldSocketMgr.h \
+ FollowerReference.cpp \
+ FollowerReference.h \
+ FollowerRefManager.h \
+ GroupReference.cpp \
+ GroupReference.h \
+ GroupRefManager.h
+>>>>>>> 5a6594330caefc0dc00a5fe792dcb0e344b457cb:src/game/Makefile.am
## Link against shared library
libmangosgame_a_LIBADD = ../shared/libmangosshared.a ../shared/Auth/libmangosauth.a ../shared/Config/libmangosconfig.a ../shared/Database/libmangosdatabase.a ../shared/vmap/libmangosvmaps.a
diff --git a/src/game/Object.h b/src/game/Object.h
index a4b7506ca30..43add24d98d 100644
--- a/src/game/Object.h
+++ b/src/game/Object.h
@@ -28,7 +28,6 @@
#include "GameSystem/GridReference.h"
#include "ObjectDefines.h"
#include "GridDefines.h"
-#include "CreatureAI.h"
#include "Map.h"
#include <set>
@@ -106,6 +105,7 @@ class InstanceData;
class GameObject;
class TempSummon;
class Vehicle;
+class CreatureAI;
typedef UNORDERED_MAP<Player*, UpdateData> UpdateDataMapType;
diff --git a/src/game/ObjectGridLoader.cpp b/src/game/ObjectGridLoader.cpp
index 78727fc7c0a..be9ac9f53db 100644
--- a/src/game/ObjectGridLoader.cpp
+++ b/src/game/ObjectGridLoader.cpp
@@ -28,6 +28,7 @@
#include "Corpse.h"
#include "World.h"
#include "CellImpl.h"
+#include "CreatureAI.h"
class TRINITY_DLL_DECL ObjectGridRespawnMover
{
diff --git a/src/game/Unit.h b/src/game/Unit.h
index 0d1c01e5bd1..79e98f0e92c 100644
--- a/src/game/Unit.h
+++ b/src/game/Unit.h
@@ -295,6 +295,7 @@ class Pet;
class Path;
class PetAura;
class Guardian;
+class UnitAI;
struct SpellImmune
{
diff --git a/src/game/World.cpp b/src/game/World.cpp
index d138c4c1bb4..4bf272a21c8 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -40,6 +40,7 @@
#include "AchievementMgr.h"
#include "AuctionHouseMgr.h"
#include "ObjectMgr.h"
+#include "CreatureEventAIMgr.h"
#include "SpellMgr.h"
#include "Chat.h"
#include "DBCStores.h"
@@ -1447,6 +1448,15 @@ void World::SetInitialWorldSettings()
sLog.outString( "Loading Scripts text locales..." ); // must be after Load*Scripts calls
objmgr.LoadDbScriptStrings();
+ sLog.outString( "Loading CreatureEventAI Texts...");
+ CreatureEAI_Mgr.LoadCreatureEventAI_Texts();
+
+ sLog.outString( "Loading CreatureEventAI Summons...");
+ CreatureEAI_Mgr.LoadCreatureEventAI_Summons();
+
+ sLog.outString( "Loading CreatureEventAI Scripts...");
+ CreatureEAI_Mgr.LoadCreatureEventAI_Scripts();
+
sLog.outString( "Initializing Scripts..." );
if(!LoadScriptingModule())
exit(1);
diff --git a/win/VC71/game.vcproj b/win/VC71/game.vcproj
index 9431fa87980..3f0809d900f 100644
--- a/win/VC71/game.vcproj
+++ b/win/VC71/game.vcproj
@@ -660,6 +660,18 @@
RelativePath="..\..\src\game\DynamicObject.h">
</File>
<File
+ RelativePath="..\..\src\game\CreatureEventAI.cpp">
+ </File>
+ <File
+ RelativePath="..\..\src\game\CreatureEventAI.h">
+ </File>
+ <File
+ RelativePath="..\..\src\game\CreatureEventAIMgr.cpp">
+ </File>
+ <File
+ RelativePath="..\..\src\game\CreatureEventAIMgr.h">
+ </File>
+ <File
RelativePath="..\..\src\game\FleeingMovementGenerator.cpp">
</File>
<File
diff --git a/win/VC80/game.vcproj b/win/VC80/game.vcproj
index 1a43ce4d14b..2eda92302b9 100644
--- a/win/VC80/game.vcproj
+++ b/win/VC80/game.vcproj
@@ -1024,6 +1024,22 @@
>
</File>
<File
+ RelativePath="..\..\src\game\CreatureEventAI.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\game\CreatureEventAI.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\game\CreatureEventAIMgr.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\game\CreatureEventAIMgr.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\game\FleeingMovementGenerator.cpp"
>
</File>
diff --git a/win/VC90/game.vcproj b/win/VC90/game.vcproj
index 85a0e553e36..466cc355a41 100644
--- a/win/VC90/game.vcproj
+++ b/win/VC90/game.vcproj
@@ -1025,6 +1025,22 @@
>
</File>
<File
+ RelativePath="..\..\src\game\CreatureEventAI.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\game\CreatureEventAI.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\game\CreatureEventAIMgr.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\game\CreatureEventAIMgr.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\game\FleeingMovementGenerator.cpp"
>
</File>