diff options
author | megamage <none@none> | 2009-04-07 19:38:09 -0500 |
---|---|---|
committer | megamage <none@none> | 2009-04-07 19:38:09 -0500 |
commit | f59c4debf6d413fefb7f55d422263fbb44772054 (patch) | |
tree | a65e607067e5caf81a2b10b0eaecbaa78d9cf062 | |
parent | 24dede78d339f348ab67d4e7235ed12d93802b69 (diff) |
[7622] Added creatureAI with related database tables. Author: AlexDereka
*Note: three tables are renamed.
--HG--
branch : trunk
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> |