diff --git a/changelog.txt b/changelog.txt index 390c45cd9..865c82173 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/20/2017 == +Uleat: Reworked bard bot spell twisting and updated their spell (song) list +Uleat: Added ability to shift to pre-combat song buffing by selecting a non-pet npc target, eliminating the need to mix all bard buff songs together + +== 2/19/2017 == +Akkadius: Added a fix for limiting the amount of items sold in a stack when the resulting return coin is higher than the supporting struct for returning coin + == 01/31/2017 == Uleat: Modifed bot movement behavior in an attempt to 'normalize' it. This is a hack fix and will be revisited at some point. (Probably just need a follow function rather than use movement, when the leader of the follow chain is moving.) diff --git a/common/database.cpp b/common/database.cpp index 981b5592f..12809ee5c 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -307,6 +307,7 @@ bool Database::DeleteCharacter(char *name) { query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); QueryDatabase(query); query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); QueryDatabase(query); query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); QueryDatabase(query); query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); QueryDatabase(query); diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 6b7c58841..cf6f4ca28 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -25,6 +25,9 @@ N(OP_AdventureRequest), N(OP_AdventureStatsReply), N(OP_AdventureStatsRequest), N(OP_AdventureUpdate), +N(OP_AggroMeterLockTarget), +N(OP_AggroMeterTargetInfo), +N(OP_AggroMeterUpdate), N(OP_AltCurrency), N(OP_AltCurrencyMerchantReply), N(OP_AltCurrencyMerchantRequest), diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index 879d6cfd5..90176bd57 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -126,26 +126,21 @@ void EQEmuLogSys::LoadLogSettingsDefaults() std::string EQEmuLogSys::FormatOutMessageString(uint16 log_category, const std::string &in_message) { - std::string category_string; - if (log_category > 0 && Logs::LogCategoryName[log_category]) - category_string = StringFormat("[%s] ", Logs::LogCategoryName[log_category]); - return StringFormat("%s%s", category_string.c_str(), in_message.c_str()); + std::string ret; + ret.push_back('['); + ret.append(Logs::LogCategoryName[log_category]); + ret.push_back(']'); + ret.push_back(' '); + ret.append(in_message); + return ret; } void EQEmuLogSys::ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &message) { - /* Check if category enabled for process */ - if (log_settings[log_category].log_to_gmsay == 0) - return; - /* Enabling Netcode based GMSay output creates a feedback loop that ultimately ends in a crash */ if (log_category == Logs::LogCategory::Netcode) return; - /* Make sure the message inbound is at a debug level we're set at */ - if (log_settings[log_category].log_to_gmsay < debug_level) - return; - /* Check to see if the process that actually ran this is zone */ if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformZone) on_log_gmsay_hook(log_category, message); @@ -163,14 +158,6 @@ void EQEmuLogSys::ProcessLogWrite(uint16 debug_level, uint16 log_category, const crash_log.close(); } - /* Check if category enabled for process */ - if (log_settings[log_category].log_to_file == 0) - return; - - /* Make sure the message inbound is at a debug level we're set at */ - if (log_settings[log_category].log_to_file < debug_level) - return; - char time_stamp[80]; EQEmuLogSys::SetCurrentTimeStamp(time_stamp); @@ -249,13 +236,6 @@ uint16 EQEmuLogSys::GetGMSayColorFromCategory(uint16 log_category) { void EQEmuLogSys::ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message) { - /* Check if category enabled for process */ - if (log_settings[log_category].log_to_console == 0) - return; - - /* Make sure the message inbound is at a debug level we're set at */ - if (log_settings[log_category].log_to_console < debug_level) - return; #ifdef _WINDOWS HANDLE console_handle; @@ -276,12 +256,25 @@ void EQEmuLogSys::ProcessConsoleMessage(uint16 debug_level, uint16 log_category, void EQEmuLogSys::Out(Logs::DebugLevel debug_level, uint16 log_category, std::string message, ...) { - const bool log_to_console = log_settings[log_category].log_to_console > 0; - const bool log_to_file = log_settings[log_category].log_to_file > 0; - const bool log_to_gmsay = log_settings[log_category].log_to_gmsay > 0; - const bool nothing_to_log = !log_to_console && !log_to_file && !log_to_gmsay; - if (nothing_to_log) return; + bool log_to_console = true; + if (log_settings[log_category].log_to_console < debug_level) { + log_to_console = false; + } + + bool log_to_file = true; + if (log_settings[log_category].log_to_file < debug_level) { + log_to_file = false; + } + + bool log_to_gmsay = true; + if (log_settings[log_category].log_to_gmsay < debug_level) { + log_to_gmsay = false; + } + + const bool nothing_to_log = !log_to_console && !log_to_file && !log_to_gmsay; + if (nothing_to_log) + return; va_list args; va_start(args, message); diff --git a/common/features.h b/common/features.h index cf6b6fb0f..a6540ed56 100644 --- a/common/features.h +++ b/common/features.h @@ -234,6 +234,9 @@ enum { //some random constants #define ZONE_CONTROLLER_NPC_ID 10 +// Timer to update aggrometer +#define AGGRO_METER_UPDATE_MS 1000 + //Some hard coded statuses from commands and other places: enum { minStatusToBeGM = 40, diff --git a/common/ruletypes.h b/common/ruletypes.h index 5323fda49..20430735a 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -114,6 +114,7 @@ RULE_BOOL(Character, CheckCursorEmptyWhenLooting, true) // If true, a player can RULE_BOOL(Character, MaintainIntoxicationAcrossZones, true) // If true, alcohol effects are maintained across zoning and logging out/in. RULE_BOOL(Character, EnableDiscoveredItems, true) // If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps for the first time an item is discovered. RULE_BOOL(Character, EnableXTargetting, true) // Enable Extended Targetting Window, for users with UF and later clients. +RULE_BOOL(Character, EnableAggroMeter, false) // Enable Aggro Meter, for users with RoF and later clients. RULE_BOOL(Character, KeepLevelOverMax, false) // Don't delevel a character that has somehow gone over the level cap RULE_INT(Character, FoodLossPerUpdate, 35) // How much food/water you lose per stamina update RULE_INT(Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well. @@ -258,6 +259,7 @@ RULE_BOOL(Zone, EnableLoggedOffReplenishments, true) RULE_INT(Zone, MinOfflineTimeToReplenishments, 21600) // 21600 seconds is 6 Hours RULE_BOOL(Zone, UseZoneController, true) // Enables the ability to use persistent quest based zone controllers (zone_controller.pl/lua) RULE_BOOL(Zone, EnableZoneControllerGlobals, false) // Enables the ability to use quest globals with the zone controller NPC +RULE_INT(Zone, GlobalLootMultiplier, 1) // Sets Global Loot drop multiplier for database based drops, useful for double, triple loot etc. RULE_CATEGORY_END() RULE_CATEGORY(Map) diff --git a/common/spdat.h b/common/spdat.h index 3344489df..d4ccd9456 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -43,29 +43,37 @@ const int Z_AGGRO=10; -const int MobAISpellRange=100; // max range of buffs -const int SpellType_Nuke=1; -const int SpellType_Heal=2; -const int SpellType_Root=4; -const int SpellType_Buff=8; -const int SpellType_Escape=16; -const int SpellType_Pet=32; -const int SpellType_Lifetap=64; -const int SpellType_Snare=128; -const int SpellType_DOT=256; -const int SpellType_Dispel=512; -const int SpellType_InCombatBuff=1024; -const int SpellType_Mez=2048; -const int SpellType_Charm=4096; -const int SpellType_Slow = 8192; -const int SpellType_Debuff = 16384; -const int SpellType_Cure = 32768; -const int SpellType_Resurrect = 65536; +const uint32 MobAISpellRange=100; // max range of buffs -const int SpellTypes_Detrimental = SpellType_Nuke|SpellType_Root|SpellType_Lifetap|SpellType_Snare|SpellType_DOT|SpellType_Dispel|SpellType_Mez|SpellType_Charm|SpellType_Debuff|SpellType_Slow; -const int SpellTypes_Beneficial = SpellType_Heal|SpellType_Buff|SpellType_Escape|SpellType_Pet|SpellType_InCombatBuff|SpellType_Cure; +enum SpellTypes : uint32 +{ + SpellType_Nuke = (1 << 0), + SpellType_Heal = (1 << 1), + SpellType_Root = (1 << 2), + SpellType_Buff = (1 << 3), + SpellType_Escape = (1 << 4), + SpellType_Pet = (1 << 5), + SpellType_Lifetap = (1 << 6), + SpellType_Snare = (1 << 7), + SpellType_DOT = (1 << 8), + SpellType_Dispel = (1 << 9), + SpellType_InCombatBuff = (1 << 10), + SpellType_Mez = (1 << 11), + SpellType_Charm = (1 << 12), + SpellType_Slow = (1 << 13), + SpellType_Debuff = (1 << 14), + SpellType_Cure = (1 << 15), + SpellType_Resurrect = (1 << 16), + SpellType_HateRedux = (1 << 17), + SpellType_InCombatBuffSong = (1 << 18), // bard in-combat group/ae buffs + SpellType_OutOfCombatBuffSong = (1 << 19), // bard out-of-combat group/ae buffs + + SpellTypes_Detrimental = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow), + SpellTypes_Beneficial = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong), + + SpellType_Any = 0xFFFFFFFF +}; -#define SpellType_Any 0xFFFF // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only diff --git a/common/version.h b/common/version.h index f7f2ad05d..40e50a0ac 100644 --- a/common/version.h +++ b/common/version.h @@ -30,9 +30,9 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9103 +#define CURRENT_BINARY_DATABASE_VERSION 9105 #ifdef BOTS - #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9008 + #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9010 #else #define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0 #endif diff --git a/loginserver/client.cpp b/loginserver/client.cpp index 593bd5318..97a55c41e 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -110,9 +110,11 @@ bool Client::Process() } default: { - char dump[64]; - app->build_header_dump(dump); - Log.Out(Logs::General, Logs::Error, "Recieved unhandled application packet from the client: %s.", dump); + if (Log.log_settings[Logs::Client_Server_Packet_Unhandled].is_category_enabled == 1) { + char dump[64]; + app->build_header_dump(dump); + Log.Out(Logs::General, Logs::Error, "Recieved unhandled application packet from the client: %s.", dump); + } } } diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index aefcd8dbc..ad54b6341 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -359,6 +359,9 @@ OP_OpenContainer=0x654f OP_Marquee=0x288a OP_Fling=0x6b8e OP_CancelSneakHide=0x265f +OP_AggroMeterLockTarget=0x70b7 +OP_AggroMeterTargetInfo=0x18fe +OP_AggroMeterUpdate=0x75aa OP_DzQuit=0x5fc8 OP_DzListTimers=0x67b9 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 20111e850..9f588d18f 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -360,6 +360,9 @@ OP_ItemRecastDelay=0x15a9 OP_ResetAA=0x1669 OP_Fling=0x6f80 OP_CancelSneakHide=0x0927 +OP_AggroMeterLockTarget=0x1643 +OP_AggroMeterTargetInfo=0x16bc +OP_AggroMeterUpdate=0x1781 # Expeditions OP_DzAddPlayer=0x4701 diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index e0960847e..e9ba02099 100644 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -334,7 +334,7 @@ sub build_linux_source { } print "Building EQEmu Server code. This will take a while."; - #::: Build + #::: Build print `make`; chdir ($current_directory); @@ -1340,7 +1340,7 @@ sub do_linux_login_server_setup { get_remote_file($install_repository_request_url . "linux/login.ini", "login_template.ini"); get_remote_file($install_repository_request_url . "linux/login_opcodes.conf", "login_opcodes.conf"); - get_remote_file($install_repository_request_url . "linux/login_opcodes.conf", "login_opcodes_sod.conf"); + get_remote_file($install_repository_request_url . "linux/login_opcodes_sod.conf", "login_opcodes_sod.conf"); get_installation_variables(); my $db_name = $installation_variables{"mysql_eqemu_db_name"}; diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 2c27d858d..c21f60f4b 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -357,6 +357,8 @@ 9101|2016_12_01_pcnpc_only.sql|SHOW COLUMNS FROM `spells_new` LIKE 'pcnpc_only_flag'|empty| 9102|2017_01_10_book_languages.sql|SHOW COLUMNS FROM `books` LIKE 'language'|empty| 9103|2017_01_30_book_languages_fix.sql|SELECT `language` from `books` WHERE `language` IS NULL|not_empty| +9104|2017_02_09_npc_spells_entries_type_update.sql|SHOW COLUMNS IN `npc_spells_entries` LIKE `type`|contains|smallint(5) unsigned +9105|2017_02_15_bot_spells_entries.sql|SELECT `id` FROM `npc_spells_entries` WHERE `npc_spells_id` >= 701 AND `npc_spells_id` <= 712|not_empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 3122545a0..8418dd83b 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -7,6 +7,8 @@ 9006|2016_04_12_bots_inventory_window.sql|SELECT `bot_command` FROM `bot_command_settings` WHERE `bot_command` LIKE 'inventorywindow'|empty| 9007|2016_06_23_bots_camel_case_name_rule.sql|SELECT * FROM `rule_values` WHERE `rule_name` LIKE 'Bots:AllowCamelCaseNames'|empty| 9008|2016_06_28_bots_inventory_charges_update.sql|SELECT * FROM `information_schema`.`COLUMNS` isc WHERE isc.`TABLE_SCHEMA` = DATABASE() AND isc.`TABLE_NAME` = 'bot_inventories' AND isc.`COLUMN_NAME` = 'inst_charges' AND isc.`DATA_TYPE` = 'tinyint'|not_empty| +9009|2017_02_15_bots_bot_spells_entries.sql|SELECT `id` FROM `npc_spells_entries` WHERE `npc_spells_id` >= 701 AND `npc_spells_id` <= 712|not_empty| +9010|2017_02_20_bots_bard_spell_update.sql|SELECT * FROM `bot_spells_entries` WHERE `npc_spells_id` = 711 AND (`type` & 0xFFFF0000) = 0xFFFF0000|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/bots/required/2017_02_15_bots_bot_spells_entries.sql b/utils/sql/git/bots/required/2017_02_15_bots_bot_spells_entries.sql new file mode 100644 index 000000000..c7d5087e6 --- /dev/null +++ b/utils/sql/git/bots/required/2017_02_15_bots_bot_spells_entries.sql @@ -0,0 +1,29 @@ +-- Delete any existing `bots_spells_entries` table +DROP TABLE IF EXISTS `bots_spells_entries`; + +-- Create new bot spells entries table (new table does not have spells_id_spellid constraint) +CREATE TABLE `bot_spells_entries` ( + `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `npc_spells_id` INT(11) NOT NULL DEFAULT '0', + `spellid` SMALLINT(5) NOT NULL DEFAULT '0', + `type` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `minlevel` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `maxlevel` TINYINT(3) UNSIGNED NOT NULL DEFAULT '255', + `manacost` SMALLINT(5) NOT NULL DEFAULT '-1', + `recast_delay` INT(11) NOT NULL DEFAULT '-1', + `priority` SMALLINT(5) NOT NULL DEFAULT '0', + `resist_adjust` INT(11) NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB +AUTO_INCREMENT=1 +; + +-- Copy bots spells into new table +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`, `manacost`, `recast_delay`, `priority`, `resist_adjust`) +SELECT `npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`, `manacost`, `recast_delay`, `priority`, `resist_adjust` +FROM `npc_spells_entries` WHERE `npc_spells_id` >= '701' AND `npc_spells_id` <= '712'; + +-- Delete bot spells from old table +DELETE FROM `npc_spells_entries` WHERE `npc_spells_id` >= '701' AND `npc_spells_id` <= '712'; diff --git a/utils/sql/git/bots/required/2017_02_20_bots_bard_spell_update.sql b/utils/sql/git/bots/required/2017_02_20_bots_bard_spell_update.sql new file mode 100644 index 000000000..b0161209a --- /dev/null +++ b/utils/sql/git/bots/required/2017_02_20_bots_bard_spell_update.sql @@ -0,0 +1,153 @@ +DELETE FROM `bot_spells_entries` WHERE `npc_spells_id` = '711'; + +INSERT INTO `bot_spells_entries`(`npc_spells_id`,`spellid`,`type`,`minlevel`,`maxlevel`,`manacost`,`recast_delay`,`priority`,`resist_adjust`) VALUES + +-- versioning entry +('711', '0', '4294901760', '0', '0', '-1', '-1', '0', NULL), -- 0xFFFF0000 + +-- nuke +('711', '704', '1', '12', '54', '-1', '-1', '1', NULL), +('711', '1747', '1', '55', '127', '-1', '-1', '1', NULL), + +-- escape +('711', '1749', '16', '60', '127', '-1', '-1', '1', NULL), + +-- dot +('711', '743', '256', '38', '64', '-1', '-1', '1', NULL), +('711', '3367', '256', '65', '69', '-1', '-1', '1', NULL), +('711', '5385', '256', '70', '79', '-1', '-1', '1', NULL), +('711', '14074', '256', '80', '84', '-1', '-1', '1', NULL), +('711', '18059', '256', '85', '89', '-1', '-1', '1', NULL), +('711', '26084', '256', '90', '94', '-1', '-1', '1', NULL), +('711', '29182', '256', '95', '127', '-1', '-1', '1', NULL), +('711', '3566', '256', '50', '62', '-1', '-1', '2', NULL), +('711', '3370', '256', '63', '67', '-1', '-1', '2', NULL), +('711', '5378', '256', '68', '77', '-1', '-1', '2', NULL), +('711', '14071', '256', '78', '82', '-1', '-1', '2', NULL), +('711', '18056', '256', '83', '87', '-1', '-1', '2', NULL), +('711', '26033', '256', '88', '92', '-1', '-1', '2', NULL), +('711', '29128', '256', '93', '127', '-1', '-1', '2', NULL), +('711', '744', '256', '46', '62', '-1', '-1', '3', NULL), +('711', '3373', '256', '63', '66', '-1', '-1', '3', NULL), +('711', '5379', '256', '67', '76', '-1', '-1', '3', NULL), +('711', '14068', '256', '77', '81', '-1', '-1', '3', NULL), +('711', '18053', '256', '82', '86', '-1', '-1', '3', NULL), +('711', '26003', '256', '87', '91', '-1', '-1', '3', NULL), +('711', '29101', '256', '92', '127', '-1', '-1', '3', NULL), +('711', '3567', '256', '42', '60', '-1', '-1', '4', NULL), +('711', '3363', '256', '61', '65', '-1', '-1', '4', NULL), +('711', '5371', '256', '66', '75', '-1', '-1', '4', NULL), +('711', '14065', '256', '76', '80', '-1', '-1', '4', NULL), +('711', '18050', '256', '81', '85', '-1', '-1', '4', NULL), +('711', '25976', '256', '86', '90', '-1', '-1', '4', NULL), +('711', '29077', '256', '91', '127', '-1', '-1', '4', NULL), +('711', '707', '256', '30', '59', '-1', '-1', '5', NULL), +('711', '4210', '256', '60', '127', '-1', '-1', '5', NULL), + +-- slow +('711', '738', '8192', '23', '50', '-1', '-1', '1', NULL), +('711', '1751', '8192', '51', '59', '-1', '-1', '1', NULL), +('711', '1748', '8192', '60', '63', '-1', '-1', '1', NULL), +('711', '3066', '8192', '64', '127', '-1', '-1', '1', NULL), +('711', '738', '8192', '51', '63', '-1', '-1', '2', NULL), +('711', '1751', '8192', '64', '127', '-1', '-1', '2', NULL), +('711', '738', '8192', '64', '127', '-1', '-1', '3', NULL), + +-- cure +('711', '3682', '32768', '45', '85', '-1', '-1', '1', NULL), +('711', '25958', '32768', '86', '90', '-1', '-1', '1', NULL), +('711', '29059', '32768', '91', '127', '-1', '-1', '1', NULL), +('711', '3681', '32768', '52', '127', '-1', '-1', '2', NULL), +('711', '10448', '32768', '74', '78', '-1', '-1', '3', NULL), +('711', '14029', '32768', '79', '83', '-1', '-1', '3', NULL), +('711', '18023', '32768', '84', '127', '-1', '-1', '3', NULL), + +-- hate redux +('711', '1754', '131072', '53', '127', '-1', '-1', '1', NULL), +('711', '10436', '131072', '73', '127', '-1', '-1', '2', NULL), + +-- in-combat buff songs +('711', '2606', '262144', '52', '59', '-1', '-1', '1', NULL), +('711', '2610', '262144', '60', '127', '-1', '-1', '1', NULL), +('711', '700', '262144', '1', '9', '-1', '-1', '2', NULL), +('711', '701', '262144', '10', '35', '-1', '-1', '2', NULL), +('711', '740', '262144', '36', '41', '-1', '-1', '2', NULL), +('711', '702', '262144', '42', '49', '-1', '-1', '2', NULL), +('711', '747', '262144', '50', '61', '-1', '-1', '2', NULL), +('711', '3374', '262144', '62', '64', '-1', '-1', '2', NULL), +('711', '4871', '262144', '65', '67', '-1', '-1', '2', NULL), +('711', '5376', '262144', '68', '78', '-1', '-1', '2', NULL), +('711', '14080', '262144', '79', '83', '-1', '-1', '2', NULL), +('711', '18065', '262144', '84', '88', '-1', '-1', '2', NULL), +('711', '26042', '262144', '89', '93', '-1', '-1', '2', NULL), +('711', '29143', '262144', '94', '127', '-1', '-1', '2', NULL), +('711', '7', '262144', '6', '19', '-1', '-1', '2', NULL), +('711', '1287', '262144', '20', '31', '-1', '-1', '3', NULL), +('711', '723', '262144', '32', '33', '-1', '-1', '3', NULL), +('711', '1448', '262144', '34', '54', '-1', '-1', '3', NULL), +('711', '1759', '262144', '55', '61', '-1', '-1', '3', NULL), +('711', '3651', '262144', '62', '66', '-1', '-1', '3', NULL), +('711', '5377', '262144', '67', '70', '-1', '-1', '3', NULL), +('711', '10421', '262144', '71', '75', '-1', '-1', '3', NULL), +('711', '14008', '262144', '76', '80', '-1', '-1', '3', NULL), +('711', '18008', '262144', '81', '87', '-1', '-1', '3', NULL), +('711', '26015', '262144', '88', '92', '-1', '-1', '3', NULL), +('711', '29107', '262144', '93', '127', '-1', '-1', '3', NULL), +('711', '734', '262144', '7', '8', '-1', '-1', '4', NULL), +('711', '710', '262144', '9', '12', '-1', '-1', '4', NULL), +('711', '711', '262144', '13', '16', '-1', '-1', '4', NULL), +('711', '709', '262144', '17', '40', '-1', '-1', '4', NULL), +('711', '714', '262144', '41', '46', '-1', '-1', '4', NULL), +('711', '748', '262144', '47', '57', '-1', '-1', '4', NULL), +('711', '1763', '262144', '58', '72', '-1', '-1', '4', NULL), +('711', '11881', '262144', '73', '77', '-1', '-1', '4', NULL), +('711', '14056', '262144', '78', '82', '-1', '-1', '4', NULL), +('711', '18041', '262144', '83', '87', '-1', '-1', '4', NULL), +('711', '26027', '262144', '88', '92', '-1', '-1', '4', NULL), +('711', '29122', '262144', '93', '127', '-1', '-1', '4', NULL), +('711', '734', '262144', '9', '24', '-1', '-1', '5', NULL), +('711', '712', '262144', '25', '28', '-1', '-1', '5', NULL), +('711', '715', '262144', '29', '32', '-1', '-1', '5', NULL), +('711', '713', '262144', '33', '36', '-1', '-1', '5', NULL), +('711', '716', '262144', '37', '44', '-1', '-1', '5', NULL), +('711', '4083', '262144', '45', '52', '-1', '-1', '5', NULL), +('711', '4084', '262144', '53', '63', '-1', '-1', '5', NULL), +('711', '3362', '262144', '64', '64', '-1', '-1', '5', NULL), +('711', '4872', '262144', '65', '68', '-1', '-1', '5', NULL), +('711', '5382', '262144', '69', '75', '-1', '-1', '5', NULL), +('711', '14062', '262144', '76', '80', '-1', '-1', '5', NULL), +('711', '18047', '262144', '81', '85', '-1', '-1', '5', NULL), +('711', '25961', '262144', '86', '90', '-1', '-1', '5', NULL), +('711', '29062', '262144', '91', '127', '-1', '-1', '5', NULL), +('711', '734', '262144', '25', '43', '-1', '-1', '6', NULL), +('711', '4085', '262144', '44', '51', '-1', '-1', '6', NULL), +('711', '4086', '262144', '52', '62', '-1', '-1', '6', NULL), +('711', '4087', '262144', '63', '68', '-1', '-1', '6', NULL), +('711', '5374', '262144', '69', '71', '-1', '-1', '6', NULL), +('711', '10439', '262144', '72', '76', '-1', '-1', '6', NULL), +('711', '14020', '262144', '77', '81', '-1', '-1', '6', NULL), +('711', '18014', '262144', '82', '86', '-1', '-1', '6', NULL), +('711', '25991', '262144', '87', '127', '-1', '-1', '6', NULL), +('711', '734', '262144', '30', '82', '-1', '-1', '7', NULL), +('711', '18020', '262144', '83', '127', '-1', '-1', '7', NULL), +('711', '734', '262144', '83', '127', '-1', '-1', '8', NULL), +('711', '2603', '262144', '30', '127', '-1', '-1', '9', NULL), + +-- out-of-combat buff songs +('711', '7', '524288', '6', '19', '-1', '-1', '1', NULL), +('711', '1287', '524288', '20', '31', '-1', '-1', '1', NULL), +('711', '723', '524288', '32', '33', '-1', '-1', '1', NULL), +('711', '1448', '524288', '34', '54', '-1', '-1', '1', NULL), +('711', '1759', '524288', '55', '61', '-1', '-1', '1', NULL), +('711', '3651', '524288', '62', '66', '-1', '-1', '1', NULL), +('711', '5377', '524288', '67', '70', '-1', '-1', '1', NULL), +('711', '10421', '524288', '71', '75', '-1', '-1', '1', NULL), +('711', '14008', '524288', '76', '80', '-1', '-1', '1', NULL), +('711', '18008', '524288', '81', '87', '-1', '-1', '1', NULL), +('711', '26015', '524288', '88', '92', '-1', '-1', '1', NULL), +('711', '29107', '524288', '93', '127', '-1', '-1', '1', NULL), +('711', '717', '524288', '5', '29', '-1', '-1','2', NULL), +('711', '2603', '524288', '30', '127', '-1', '-1','2', NULL), +('711', '717', '524288', '30', '48', '-1', '-1','3', NULL), +('711', '2605', '524288', '49', '127', '-1', '-1','3', NULL), +('711', '2602', '524288', '15', '127', '-1', '-1','4', NULL); diff --git a/utils/sql/git/required/2017_02_09_npc_spells_entries_type_update.sql b/utils/sql/git/required/2017_02_09_npc_spells_entries_type_update.sql new file mode 100644 index 000000000..faa46e86e --- /dev/null +++ b/utils/sql/git/required/2017_02_09_npc_spells_entries_type_update.sql @@ -0,0 +1 @@ +ALTER TABLE `npc_spells_entries` MODIFY COLUMN `type` INT(10) UNSIGNED NOT NULL DEFAULT '0'; diff --git a/utils/sql/git/required/2017_02_15_bot_spells_entries.sql b/utils/sql/git/required/2017_02_15_bot_spells_entries.sql new file mode 100644 index 000000000..7f3a73d98 --- /dev/null +++ b/utils/sql/git/required/2017_02_15_bot_spells_entries.sql @@ -0,0 +1,31 @@ +-- Delete any existing `bots_spells_entries` table +DROP TABLE IF EXISTS `bots_spells_entries`; + +-- Create new bot spells entries table (new table does not have spells_id_spellid constraint) +CREATE TABLE `bot_spells_entries` ( + `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `npc_spells_id` INT(11) NOT NULL DEFAULT '0', + `spellid` SMALLINT(5) NOT NULL DEFAULT '0', + `type` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `minlevel` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `maxlevel` TINYINT(3) UNSIGNED NOT NULL DEFAULT '255', + `manacost` SMALLINT(5) NOT NULL DEFAULT '-1', + `recast_delay` INT(11) NOT NULL DEFAULT '-1', + `priority` SMALLINT(5) NOT NULL DEFAULT '0', + `resist_adjust` INT(11) NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB +AUTO_INCREMENT=1 +; + +-- Copy bots spells into new table +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`, `manacost`, `recast_delay`, `priority`, `resist_adjust`) +SELECT `npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`, `manacost`, `recast_delay`, `priority`, `resist_adjust` +FROM `npc_spells_entries` WHERE `npc_spells_id` >= '701' AND `npc_spells_id` <= '712'; + +-- Delete bot spells from old table +DELETE FROM `npc_spells_entries` WHERE `npc_spells_id` >= '701' AND `npc_spells_id` <= '712'; + +-- Admins can remove this new table if they are 100% certain they will never use bots diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 53e6edcd0..56e3a2dfe 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -4,6 +4,7 @@ SET(zone_sources aa.cpp aa_ability.cpp aggro.cpp + aggromanager.cpp attack.cpp beacon.cpp bonuses.cpp @@ -122,6 +123,7 @@ SET(zone_sources water_map_v2.cpp waypoints.cpp worldserver.cpp + xtargetautohaters.cpp zone.cpp zone_config.cpp zonedb.cpp @@ -131,6 +133,7 @@ SET(zone_sources SET(zone_headers aa.h aa_ability.h + aggromanager.h basic_functions.h beacon.h bot.h @@ -215,6 +218,7 @@ SET(zone_headers water_map_v1.h water_map_v2.h worldserver.h + xtargetautohaters.h zone.h zone_config.h zonedb.h diff --git a/zone/aggromanager.cpp b/zone/aggromanager.cpp new file mode 100644 index 000000000..8dd532c74 --- /dev/null +++ b/zone/aggromanager.cpp @@ -0,0 +1,11 @@ +#include "aggromanager.h" + +AggroMeter::AggroMeter() : lock_id(0), target_id(0), secondary_id(0), lock_changed(false) +{ + for (int i = 0; i < AT_Max; ++i) { + data[i].type = i; + data[i].pct = 0; + } +} + + diff --git a/zone/aggromanager.h b/zone/aggromanager.h new file mode 100644 index 000000000..b140b07d7 --- /dev/null +++ b/zone/aggromanager.h @@ -0,0 +1,80 @@ +#ifndef AGGROMANAGER_H +#define AGGROMANAGER_H + +#include "../common/types.h" +#include +#include + +class AggroMeter +{ +public: + enum AggroTypes { + AT_Player, + AT_Secondary, + AT_Group1, + AT_Group2, + AT_Group3, + AT_Group4, + AT_Group5, + AT_XTarget1, + AT_XTarget2, + AT_XTarget3, + AT_XTarget4, + AT_XTarget5, + AT_XTarget6, + AT_XTarget7, + AT_XTarget8, + AT_XTarget9, + AT_XTarget10, + AT_XTarget11, + AT_XTarget12, + AT_XTarget13, + AT_XTarget14, + AT_XTarget15, + AT_XTarget16, + AT_XTarget17, + AT_XTarget18, + AT_XTarget19, + AT_XTarget20, + AT_Max + }; + +private: + struct AggroData { + int16 type; + int16 pct; + }; + + AggroData data[AT_Max]; + int lock_id; // we set this + int target_id; // current target or if PC targeted, their Target + // so secondary depends on if we have aggro or not + // When we are the current target, this will be the 2nd person on list + // When we are not tanking, this will be the current tank + int secondary_id; + + // so we need some easy way to detect the client changing but still delaying the packet + bool lock_changed; +public: + AggroMeter(); + ~AggroMeter() {} + + inline void set_lock_id(int in) { lock_id = in; lock_changed = true; } + inline bool update_lock() { bool ret = lock_changed; lock_changed = false; return ret; } + inline void set_target_id(int in) { target_id = in; } + inline void set_secondary_id(int in) { secondary_id = in; } + // returns true when changed + inline bool set_pct(AggroTypes t, int pct) { assert(t >= AT_Player && t < AT_Max); if (data[t].pct == pct) return false; data[t].pct = pct; return true; } + + inline int get_lock_id() const { return lock_id; } + inline int get_target_id() const { return target_id; } + inline int get_secondary_id() const { return secondary_id; } + inline int get_pct(AggroTypes t) const { assert(t >= AT_Player && t < AT_Max); return data[t].pct; } + // the ID of the spawn for player entry depends on lock_id + inline int get_player_aggro_id() const { return lock_id ? lock_id : target_id; } + // fuck it, lets just use a buffer the size of the largest to work with + const inline size_t max_packet_size() const { return sizeof(uint8) + sizeof(uint32) + sizeof(uint8) + (sizeof(uint8) + sizeof(uint16)) * AT_Max; } +}; + + +#endif /* !AGGROMANAGER_H */ diff --git a/zone/attack.cpp b/zone/attack.cpp index 3b59d63a9..245388f1b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -359,7 +359,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit) } // riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo - bool ImmuneRipo = attacker->aabonuses.RiposteChance || attacker->spellbonuses.RiposteChance || attacker->itembonuses.RiposteChance; + bool ImmuneRipo = attacker->aabonuses.RiposteChance || attacker->spellbonuses.RiposteChance || attacker->itembonuses.RiposteChance || attacker->IsEnraged(); // Need to check if we have something in MainHand to actually attack with (or fists) if (hit.hand != EQEmu::inventory::slotRange && (CanThisClassRiposte() || IsEnraged()) && InFront && !ImmuneRipo) { if (IsEnraged()) { @@ -2433,9 +2433,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b Mob* mypet = this->GetPet(); Mob* myowner = this->GetOwner(); Mob* targetmob = this->GetTarget(); + bool on_hatelist = CheckAggro(other); if(other){ - bool on_hatelist = CheckAggro(other); AddRampage(other); if (on_hatelist) { // odd reason, if you're not on the hate list, subtlety etc don't apply! // Spell Casting Subtlety etc @@ -2511,7 +2511,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b hate_list.AddEntToHateList(other, hate, damage, bFrenzy, !iBuffTic); - if(other->IsClient()) + if(other->IsClient() && !on_hatelist) other->CastToClient()->AddAutoXTarget(this); #ifdef BOTS @@ -2550,7 +2550,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b if(!owner->GetSpecialAbility(IMMUNE_AGGRO)) { hate_list.AddEntToHateList(owner, 0, 0, false, !iBuffTic); - if(owner->IsClient()) + if(owner->IsClient() && !CheckAggro(owner)) owner->CastToClient()->AddAutoXTarget(this); } } diff --git a/zone/bot.cpp b/zone/bot.cpp index d5f6ff814..5da128aa6 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -76,7 +76,6 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm SetAltOutOfCombatBehavior(GetClass() == BARD); // will need to be updated if more classes make use of this flag SetShowHelm(true); SetPauseAI(false); - CalcChanceToCast(); rest_timer.Disable(); SetFollowDistance(BOT_DEFAULT_FOLLOW_DISTANCE); // Do this once and only in this constructor @@ -151,7 +150,6 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == BotStanceAggressive)); SetPauseAI(false); - CalcChanceToCast(); rest_timer.Disable(); SetFollowDistance(BOT_DEFAULT_FOLLOW_DISTANCE); strcpy(this->name, this->GetCleanName()); @@ -1636,6 +1634,19 @@ bool Bot::LoadPet() auto bot_owner = GetBotOwner(); if (!bot_owner) return false; + + if (GetClass() == WIZARD) { + auto buffs_max = GetMaxBuffSlots(); + auto my_buffs = GetBuffs(); + if (buffs_max && my_buffs) { + for (int index = 0; index < buffs_max; ++index) { + if (IsEffectInSpell(my_buffs[index].spellid, SE_Familiar)) { + MakePet(my_buffs[index].spellid, spells[my_buffs[index].spellid].teleport_zone); + return true; + } + } + } + } std::string error_message; @@ -1651,7 +1662,7 @@ bool Bot::LoadPet() if (!botdb.LoadPetSpellID(GetBotID(), saved_pet_spell_id)) { bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetSpellID(), GetCleanName()); } - if (!saved_pet_spell_id || saved_pet_spell_id > SPDAT_RECORDS) { + if (!IsValidSpell(saved_pet_spell_id)) { bot_owner->Message(13, "Invalid spell id for %s's pet", GetCleanName()); DeletePet(); return false; @@ -1695,11 +1706,11 @@ bool Bot::LoadPet() bool Bot::SavePet() { - if (!GetPet() /*|| dead*/) + if (!GetPet() || GetPet()->IsFamiliar() /*|| dead*/) return true; NPC *pet_inst = GetPet()->CastToNPC(); - if (pet_inst->IsFamiliar() || !pet_inst->GetPetSpellID() || pet_inst->GetPetSpellID() > SPDAT_RECORDS) + if (!pet_inst->GetPetSpellID() || !IsValidSpell(pet_inst->GetPetSpellID())) return false; auto bot_owner = GetBotOwner(); @@ -7495,7 +7506,7 @@ bool Bot::GroupHasClass(Group* group, uint8 classId) { return result; } -bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 iSpellTypes) { +bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { if((iSpellTypes&SpellTypes_Detrimental) != 0) { Log.Out(Logs::General, Logs::Error, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); return false; @@ -7639,6 +7650,24 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl } } } + + if (iSpellTypes == SpellType_HateRedux) { + if (!caster->IsEngaged()) + return false; + + if (caster->HasGroup()) { + Group *g = caster->GetGroup(); + if (g) { + for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (g->members[i] && caster->GetNeedsHateRedux(g->members[i])) { + if (caster->AICastSpell(g->members[i], caster->GetChanceToCastBySpellType(SpellType_HateRedux), SpellType_HateRedux)) + return true; + } + } + } + } + } + return false; } @@ -8017,7 +8046,7 @@ bool Bot::GetNeedsHateRedux(Mob *tar) { // This really should be a scalar function based in class Mob that returns 'this' state..but, is inline with current Bot coding... // TODO: Good starting point..but, can be refined.. // TODO: Still awaiting bot spell rework.. - if (!tar || !tar->HasTargetReflection()) + if (!tar || !tar->IsEngaged() || !tar->HasTargetReflection() || !tar->GetTarget()->IsNPC()) return false; if (tar->IsClient()) { @@ -8031,7 +8060,7 @@ bool Bot::GetNeedsHateRedux(Mob *tar) { else if (tar->IsBot()) { switch (tar->GetClass()) { case ROGUE: - if (tar->CastToBot()->evade_timer.Check(false)) + if (tar->CanFacestab() || tar->CastToBot()->evade_timer.Check(false)) return false; case CLERIC: case DRUID: diff --git a/zone/bot.h b/zone/bot.h index bda693094..97bf9fc1f 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -47,8 +47,6 @@ const int MaxSpellTimer = 15; const int MaxDisciplineTimer = 10; const int DisciplineReuseStart = MaxSpellTimer + 1; const int MaxTimer = MaxSpellTimer + MaxDisciplineTimer; -const int MaxStances = 7; -const int MaxSpellTypes = 16; enum BotStanceType { BotStancePassive, @@ -58,7 +56,8 @@ enum BotStanceType { BotStanceAggressive, BotStanceBurn, BotStanceBurnAE, - BotStanceUnknown + BotStanceUnknown, + MaxStances = BotStanceUnknown }; #define BOT_STANCE_COUNT 8 @@ -125,7 +124,12 @@ enum SpellTypeIndex { SpellType_CharmIndex, SpellType_SlowIndex, SpellType_DebuffIndex, - SpellType_CureIndex + SpellType_CureIndex, + SpellType_ResurrectIndex, + SpellType_HateReduxIndex, + SpellType_InCombatBuffSongIndex, + SpellType_OutOfCombatBuffSongIndex, + MaxSpellTypes }; class Bot : public NPC { @@ -235,7 +239,7 @@ public: virtual void Depop(); void CalcBotStats(bool showtext = true); uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; } - uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; } + uint32 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; } uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; } virtual float GetProcChances(float ProcBonus, uint16 hand); virtual int GetHandToHandDamage(void); @@ -296,7 +300,6 @@ public: bool GetNeedsHateRedux(Mob *tar); bool HasOrMayGetAggro(); void SetDefaultBotStance(); - void CalcChanceToCast(); inline virtual int32 GetMaxStat(); inline virtual int32 GetMaxResist(); @@ -350,7 +353,7 @@ public: void DoEnduranceUpkeep(); //does the endurance upkeep // AI Methods - virtual bool AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes); + virtual bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes); virtual bool AI_EngagedCastCheck(); virtual bool AI_PursueCastCheck(); virtual bool AI_IdleCastCheck(); @@ -416,8 +419,9 @@ public: static uint32 GetDisciplineRemainingTime(Bot *caster, int timer_index); static std::list GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect); static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType); - static std::list GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType); - static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType); + static std::list GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType); + static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint32 spellType); + static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType); static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster); static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster); static BotSpell GetBestBotSpellForPercentageHeal(Bot* botCaster); @@ -467,7 +471,7 @@ public: bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; } BotRoleType GetBotRole() { return _botRole; } BotStanceType GetBotStance() { return _botStance; } - uint8 GetChanceToCastBySpellType(uint16 spellType); + uint8 GetChanceToCastBySpellType(uint32 spellType); bool IsGroupPrimaryHealer(); bool IsGroupPrimarySlower(); bool IsBotCaster() { return IsCasterClass(GetClass()); } @@ -669,7 +673,6 @@ private: uint32 timers[MaxTimer]; bool _hasBeenSummoned; glm::vec3 m_PreSummonLocation; - uint8 _spellCastingChances[MaxStances][MaxSpellTypes]; Timer evade_timer; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index fa15dc7d2..ef995ce69 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -5117,7 +5117,6 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep) if (!current_flag) { bot_iter->SetBotStance(bst); - bot_iter->CalcChanceToCast(); bot_iter->Save(); } diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 996fbff41..a774bad70 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -56,6 +56,10 @@ struct BotSpell { int16 ManaCost; }; +struct BotSpell_wPriority : public BotSpell { + uint8 Priority; +}; + struct BotAA { uint32 aa_id; uint8 req_level; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index a35760535..f3b4ec83a 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -29,7 +29,7 @@ #define BotAI_DEBUG_Spells -1 #endif -bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { +bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { if (!tar) { return false; @@ -96,14 +96,8 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost); - if(castedSpell) { - char* gmsg = 0; - - MakeAnyLenString(&gmsg, "Attempting to mez %s.", addMob->GetCleanName()); - - if(gmsg) - BotGroupSay(this, gmsg); - } + if(castedSpell) + BotGroupSay(this, "Attempting to mez %s.", addMob->GetCleanName()); } break; } @@ -248,7 +242,6 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime); if(castedSpell) { - char* gmsg = 0; /*if(TempDontHealMeBeforeTime != tar->DontHealMeBefore()) tar->SetDontHealMeBefore(TempDontHealMeBeforeTime); @@ -270,7 +263,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { Group *g = this->GetGroup(); if(g) { - MakeAnyLenString(&gmsg, "Casting %s.", spells[botSpell.SpellId].name); + BotGroupSay(this, "Casting %s.", spells[botSpell.SpellId].name); for( int i = 0; imembers[i] && !g->members[i]->qglobal) { @@ -282,14 +275,11 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { } else { if(tar != this) //we don't need spam of bots healing themselves - MakeAnyLenString(&gmsg, "Casting %s on %s", spells[botSpell.SpellId].name, tar->GetCleanName()); + BotGroupSay(this, "Casting %s on %s", spells[botSpell.SpellId].name, tar->GetCleanName()); tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 2000); } } - - if(gmsg) - BotGroupSay(this, gmsg); } } } @@ -567,10 +557,35 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { case SpellType_Pet: { //keep mobs from recasting pets when they have them. if (!IsPet() && !GetPetID() && !IsBotCharmer()) { - if(botClass == MAGICIAN) - botSpell = GetBestBotMagicianPetSpell(this); - else + if (botClass == WIZARD) { + auto buffs_max = GetMaxBuffSlots(); + auto my_buffs = GetBuffs(); + int familiar_buff_slot = -1; + if (buffs_max && my_buffs) { + for (int index = 0; index < buffs_max; ++index) { + if (IsEffectInSpell(my_buffs[index].spellid, SE_Familiar)) { + MakePet(my_buffs[index].spellid, spells[my_buffs[index].spellid].teleport_zone); + familiar_buff_slot = index; + break; + } + } + } + if (GetPetID()) + break; + + if (familiar_buff_slot >= 0) { + BuffFadeBySlot(familiar_buff_slot); + break; + } + botSpell = GetFirstBotSpellBySpellType(this, SpellType_Pet); + } + else if (botClass == MAGICIAN) { + botSpell = GetBestBotMagicianPetSpell(this); + } + else { + botSpell = GetFirstBotSpellBySpellType(this, SpellType_Pet); + } if(botSpell.SpellId == 0) break; @@ -748,35 +763,69 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { checked_los = true; } - std::list dotList = GetBotSpellsBySpellType(this, SpellType_DOT); + if (GetClass() == BARD) { + std::list dotList = GetPrioritizedBotSpellsBySpellType(this, SpellType_DOT); - const int maxDotSelect = 5; - int dotSelectCounter = 0; + const int maxDotSelect = 5; + int dotSelectCounter = 0; - for(std::list::iterator itr = dotList.begin(); itr != dotList.end(); ++itr) { - BotSpell selectedBotSpell = *itr; + for (std::list::iterator itr = dotList.begin(); itr != dotList.end(); ++itr) { + BotSpell selectedBotSpell = *itr; - if(selectedBotSpell.SpellId == 0) - continue; - - if(CheckSpellRecastTimers(this, itr->SpellIndex)) - { - - if(!(!tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0)) + if (selectedBotSpell.SpellId == 0) continue; - uint32 TempDontDotMeBefore = tar->DontDotMeBefore(); + if (CheckSpellRecastTimers(this, itr->SpellIndex)) + { - castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontDotMeBefore); + if (!(!tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0)) + continue; - if(TempDontDotMeBefore != tar->DontDotMeBefore()) - tar->SetDontDotMeBefore(TempDontDotMeBefore); + uint32 TempDontDotMeBefore = tar->DontDotMeBefore(); + + castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontDotMeBefore); + + if (TempDontDotMeBefore != tar->DontDotMeBefore()) + tar->SetDontDotMeBefore(TempDontDotMeBefore); + } + + dotSelectCounter++; + + if ((dotSelectCounter == maxDotSelect) || castedSpell) + break; } + } + else { + std::list dotList = GetBotSpellsBySpellType(this, SpellType_DOT); - dotSelectCounter++; + const int maxDotSelect = 5; + int dotSelectCounter = 0; - if((dotSelectCounter == maxDotSelect) || castedSpell) - break; + for (std::list::iterator itr = dotList.begin(); itr != dotList.end(); ++itr) { + BotSpell selectedBotSpell = *itr; + + if (selectedBotSpell.SpellId == 0) + continue; + + if (CheckSpellRecastTimers(this, itr->SpellIndex)) + { + + if (!(!tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0)) + continue; + + uint32 TempDontDotMeBefore = tar->DontDotMeBefore(); + + castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontDotMeBefore); + + if (TempDontDotMeBefore != tar->DontDotMeBefore()) + tar->SetDontDotMeBefore(TempDontDotMeBefore); + } + + dotSelectCounter++; + + if ((dotSelectCounter == maxDotSelect) || castedSpell) + break; + } } } break; @@ -792,6 +841,28 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { } switch (botClass) { + case BARD: { + // probably needs attackable check + std::list botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_Slow); + for (auto iter : botSongList) { + if (!iter.SpellId) + continue; + if (!CheckSpellRecastTimers(this, iter.SpellIndex)) + continue; + if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index? + continue; + if (spells[iter.SpellId].targettype != ST_Target) + continue; + if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) + continue; + + castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); + if (castedSpell) + break; + } + + break; + } case ENCHANTER: { botSpell = GetBestBotSpellForMagicBasedSlow(this); break; @@ -814,14 +885,8 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); - if(castedSpell) { - char* gmsg = 0; - - MakeAnyLenString(&gmsg, "Attempting to slow %s.", tar->GetCleanName()); - - if(gmsg) - BotGroupSay(this, gmsg); - } + if(castedSpell && GetClass() != BARD) + BotGroupSay(this, "Attempting to slow %s.", tar->GetCleanName()); } break; } @@ -889,6 +954,100 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { } break; } + case SpellType_Resurrect: + break; + case SpellType_HateRedux: { + // assumed group member at this point + if (GetClass() == BARD) { + std::list botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_HateRedux); + for (auto iter : botSongList) { + if (!iter.SpellId) + continue; + if (!CheckSpellRecastTimers(this, iter.SpellIndex)) + continue; + if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index? + continue; + if (spells[iter.SpellId].targettype != ST_Target) + continue; + if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) + continue; + + castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); + if (castedSpell) { + BotGroupSay(this, "Attempting to reduce hate on %s.", tar->GetCleanName()); + break; + } + } + } + + break; + } + case SpellType_InCombatBuffSong: { + if (GetClass() != BARD || tar != this) // In-Combat songs can be cast Out-of-Combat in preparation for battle + break; + + std::list botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_InCombatBuffSong); + for (auto iter : botSongList) { + if (!iter.SpellId) + continue; + if (!CheckSpellRecastTimers(this, iter.SpellIndex)) + continue; + if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index? + continue; + switch (spells[iter.SpellId].targettype) { + case ST_AEBard: + case ST_AECaster: + case ST_GroupTeleport: + case ST_Group: + case ST_Self: + break; + default: + continue; + } + if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) + continue; + + castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); + if (castedSpell) + break; + } + + break; + } + case SpellType_OutOfCombatBuffSong: { + if (GetClass() != BARD || tar != this || IsEngaged()) // Out-of-Combat songs can not be cast in combat + break; + + std::list botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_OutOfCombatBuffSong); + for (auto iter : botSongList) { + if (!iter.SpellId) + continue; + if (!CheckSpellRecastTimers(this, iter.SpellIndex)) + continue; + if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index? + continue; + switch (spells[iter.SpellId].targettype) { + case ST_AEBard: + case ST_AECaster: + case ST_GroupTeleport: + case ST_Group: + case ST_Self: + break; + default: + continue; + } + if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) + continue; + + castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); + if (castedSpell) + break; + } + + break; + } + default: + break; } return castedSpell; @@ -945,22 +1104,18 @@ bool Bot::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain extraMana = false; } else { //handle spell recast and recast timers - if(GetClass() == BARD && IsGroupSpell(AIspells[i].spellid)) { - // Bard Clarity songs (spellids: 723 & 1287) are recast_time = 0 and buffduration = 0. - // This translates to an insta-recast in the above check and is essentially locking all idle bard songs - // between levels 20 & 33 to one of the clarity songs. + //if(GetClass() == BARD && IsGroupSpell(AIspells[i].spellid)) { + // // Bard buff songs have been moved to their own npc spell type.. + // // Buff stacking is now checked as opposed to manipulating the timer to avoid rapid casting + + // //AIspells[i].time_cancast = (spells[AIspells[i].spellid].recast_time > (spells[AIspells[i].spellid].buffduration * 6000)) ? Timer::GetCurrentTime() + spells[AIspells[i].spellid].recast_time : Timer::GetCurrentTime() + spells[AIspells[i].spellid].buffduration * 6000; + // //spellend_timer.Start(spells[AIspells[i].spellid].cast_time); + //} + //else + // AIspells[i].time_cancast = Timer::GetCurrentTime() + spells[AIspells[i].spellid].recast_time; + + AIspells[i].time_cancast = Timer::GetCurrentTime() + spells[AIspells[i].spellid].recast_time; - // Hard-coded fix of '0/0' bard songs..watch for issues in other song lines - if(spells[AIspells[i].spellid].recast_time == 0 && spells[AIspells[i].spellid].buffduration == 0 && spells[AIspells[i].spellid].goodEffect > 0) { - AIspells[i].time_cancast = Timer::GetCurrentTime() + (3 * 6000); // pseudo 'buffduration' of 3 (value matches others in bard 'heal' song line) - } - else { - AIspells[i].time_cancast = (spells[AIspells[i].spellid].recast_time > (spells[AIspells[i].spellid].buffduration * 6000)) ? Timer::GetCurrentTime() + spells[AIspells[i].spellid].recast_time : Timer::GetCurrentTime() + spells[AIspells[i].spellid].buffduration * 6000; - } - //spellend_timer.Start(spells[AIspells[i].spellid].cast_time); - } - else - AIspells[i].time_cancast = Timer::GetCurrentTime() + spells[AIspells[i].spellid].recast_time; if(spells[AIspells[i].spellid].EndurTimerIndex > 0) { SetSpellRecastTimer(spells[AIspells[i].spellid].EndurTimerIndex, spells[AIspells[i].spellid].recast_time); } @@ -1049,13 +1204,27 @@ bool Bot::AI_IdleCastCheck() { } else if(botClass == BARD) { // bard bots - if(!AICastSpell(this, 100, SpellType_Cure)) { - if(!AICastSpell(this, 100, SpellType_Heal)) { - if((!RuleB(Bots, BotBardUseOutOfCombatSongs) || !GetAltOutOfCombatBehavior()) || !AICastSpell(this, 100, SpellType_Buff)) { // skips if rule is false - if(!AICastSpell(this, 100, SpellType_InCombatBuff)) { // this tries to keep some combat buffs on the group until engaged code can pick up the buffing - // - } - } + bool battle_prep = false; + Mob* prep_against = nullptr; + + if (HasGroup() && GetGroup()->GetLeader() && GetGroup()->GetLeader()->IsClient()) + prep_against = GetGroup()->GetLeader(); + else if (GetOwner() && GetOwner()->IsClient()) + prep_against = GetOwner(); + + if (prep_against && prep_against->GetTarget() && prep_against->GetTarget()->IsNPC() && !prep_against->GetTarget()->IsPet()) + battle_prep = true; + + if (battle_prep) { + if (!AICastSpell(this, 100, SpellType_InCombatBuffSong)) { + // + } + } + else { + if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { + if (!AICastSpell(this, 100, SpellType_OutOfCombatBuffSong)) { + // + } } } @@ -1063,7 +1232,7 @@ bool Bot::AI_IdleCastCheck() { } if(!AIautocastspell_timer->Enabled()) - AIautocastspell_timer->Start(RandomTimer(1000, 5000), false); + AIautocastspell_timer->Start(RandomTimer(500, 2000), false); // avg human response is much less than 5 seconds..even for non-combat situations... } return result; @@ -1256,13 +1425,15 @@ bool Bot::AI_EngagedCastCheck() { } } else if(botClass == BARD) { - if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { - if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Dispel), SpellType_Dispel)) {// Bards will use their dispel songs - if(!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {// Bards will use their nuke songs - if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {// Bards will use their escape songs - // - failedToCast = true; + if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_HateRedux), BotAISpellRange, SpellType_HateRedux)) { + if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { + if (!AICastSpell(GetTarget(), mayGetAggro ? 0 : GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) {// Bards will use their dot songs + if (!AICastSpell(GetTarget(), mayGetAggro ? 0 : GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {// Bards will use their nuke songs + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {// Bards will use their escape songs + // + failedToCast = true; + } } } } @@ -1271,7 +1442,7 @@ bool Bot::AI_EngagedCastCheck() { } if(!AIautocastspell_timer->Enabled()) { - AIautocastspell_timer->Start(RandomTimer(100, 250), false); + AIautocastspell_timer->Start(RandomTimer(150, 300), false); } if(!failedToCast) @@ -1349,14 +1520,8 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime); - if(castedSpell) { - char* gmsg = 0; - - MakeAnyLenString(&gmsg, "Casting %s on %s, please stay in range!", spells[botSpell.SpellId].name, tar->GetCleanName()); - - if(gmsg) - Say(gmsg); - } + if(castedSpell) + Say("Casting %s on %s, please stay in range!", spells[botSpell.SpellId].name, tar->GetCleanName()); return castedSpell; } @@ -1417,7 +1582,7 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, return result; } -std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType) { +std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType) { std::list result; if(botCaster && botCaster->AI_HasSpells()) { @@ -1444,7 +1609,38 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellTyp return result; } -BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) { +std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint32 spellType) { + std::list result; + + if (botCaster && botCaster->AI_HasSpells()) { + std::vector botSpellList = botCaster->GetBotSpells(); + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if (botSpellList[i].type & spellType) { + BotSpell_wPriority botSpell; + botSpell.SpellId = botSpellList[i].spellid; + botSpell.SpellIndex = i; + botSpell.ManaCost = botSpellList[i].manacost; + botSpell.Priority = botSpellList[i].priority; + + result.push_back(botSpell); + } + } + + if (result.size() > 1) + result.sort([](BotSpell_wPriority& l, BotSpell_wPriority& r) { return l.Priority < r.Priority; }); + } + + return result; +} + +BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) { BotSpell result; result.SpellId = 0; @@ -1738,9 +1934,9 @@ BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) { if(botCaster) { std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed); - for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if(IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resisttype == RESIST_MAGIC && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + if (IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resisttype == RESIST_MAGIC && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; @@ -2139,7 +2335,7 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { } BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) { - BotSpell result; + BotSpell_wPriority result; bool spellSelected = false; result.SpellId = 0; @@ -2156,7 +2352,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) { bool isCorrupted = tar->FindType(SE_CorruptionCounter); if(botCaster && botCaster->AI_HasSpells()) { - std::list cureList = GetBotSpellsBySpellType(botCaster, SpellType_Cure); + std::list cureList = GetPrioritizedBotSpellsBySpellType(botCaster, SpellType_Cure); if(tar->HasGroup()) { Group *g = tar->GetGroup(); @@ -2173,7 +2369,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) { //Check for group cure first if(countNeedsCured > 2) { - for(std::list::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) { + for (std::list::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) { BotSpell selectedBotSpell = *itr; if(IsGroupSpell(itr->SpellId) && CheckSpellRecastTimers(botCaster, itr->SpellIndex)) { @@ -2210,7 +2406,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) { //no group cure for target- try to find single target spell if(!spellSelected) { - for(std::list::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) { + for(std::list::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) { BotSpell selectedBotSpell = *itr; if(CheckSpellRecastTimers(botCaster, itr->SpellIndex)) { @@ -2312,1099 +2508,3040 @@ bool Bot::CheckDisciplineRecastTimers(Bot *caster, int timer_index) { return false; } -void Bot::CalcChanceToCast() { - uint8 castChance = 0; - - for(int i=0; i < MaxStances; i++) { - for(int j=0; j < MaxSpellTypes; j++) { - _spellCastingChances[i][j] = 0; +uint8 Bot::GetChanceToCastBySpellType(uint32 spellType) +{ + enum : uint8 { nHS = 0, pH, pS, pHS, cntHS}; // negative Healer/Slower, positive Healer, postive Slower, positive Healer/Slower + + static const uint8 spell_casting_chances[MaxSpellTypes][PLAYER_CLASS_COUNT][MaxStances][cntHS] = { +// SpellType_NukeIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 15, 25, 15 }, // Balanced { nHS, pH, pS, pHS } + { 15, 0, 15, 0 }, // Efficient { nHS, pH, pS, pHS } + { 25, 15, 25, 15 }, // Reactive { nHS, pH, pS, pHS } + { 50, 15, 50, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 25, 50, 25 }, // Burn { nHS, pH, pS, pHS } + { 50, 25, 50, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 15, 25, 15 }, // Balanced { nHS, pH, pS, pHS } + { 15, 0, 15, 0 }, // Efficient { nHS, pH, pS, pHS } + { 25, 15, 25, 15 }, // Reactive { nHS, pH, pS, pHS } + { 50, 15, 50, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 25, 50, 25 }, // Burn { nHS, pH, pS, pHS } + { 50, 25, 50, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Balanced { nHS, pH, pS, pHS } + { 5, 5, 5, 5 }, // Efficient { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Burn { nHS, pH, pS, pHS } + { 50, 50, 50, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Balanced { nHS, pH, pS, pHS } + { 5, 5, 5, 5 }, // Efficient { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Burn { nHS, pH, pS, pHS } + { 50, 50, 50, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 15, 25, 15 }, // Balanced { nHS, pH, pS, pHS } + { 15, 0, 15, 0 }, // Efficient { nHS, pH, pS, pHS } + { 25, 15, 25, 15 }, // Reactive { nHS, pH, pS, pHS } + { 50, 15, 50, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 25, 50, 25 }, // Burn { nHS, pH, pS, pHS } + { 50, 25, 50, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Burn { nHS, pH, pS, pHS } + { 100, 100, 100, 100 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 10, 5, 0 }, // Balanced { nHS, pH, pS, pHS } + { 10, 5, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 15, 10, 5, 0 }, // Reactive { nHS, pH, pS, pHS } + { 25, 15, 15, 5 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 25, 25, 15 }, // Burn { nHS, pH, pS, pHS } + { 50, 25, 25, 15 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Balanced { nHS, pH, pS, pHS } + { 5, 5, 5, 5 }, // Efficient { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Burn { nHS, pH, pS, pHS } + { 50, 50, 50, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Reactive { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Aggressive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Burn { nHS, pH, pS, pHS } + { 100, 100, 100, 100 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Reactive { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Aggressive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Burn { nHS, pH, pS, pHS } + { 100, 100, 100, 100 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 15, 15 }, // Balanced { nHS, pH, pS, pHS } + { 15, 15, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 15, 15 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 15, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 50, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 50, 50, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 10, 5, 0 }, // Balanced { nHS, pH, pS, pHS } + { 10, 5, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 15, 10, 5, 0 }, // Reactive { nHS, pH, pS, pHS } + { 25, 15, 15, 5 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 25, 25, 15 }, // Burn { nHS, pH, pS, pHS } + { 50, 25, 25, 15 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_HealIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Efficient { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Burn { nHS, pH, pS, pHS } + { 50, 50, 50, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 100, 25, 100 }, // Balanced { nHS, pH, pS, pHS } + { 15, 75, 15, 75 }, // Efficient { nHS, pH, pS, pHS } + { 25, 100, 25, 100 }, // Reactive { nHS, pH, pS, pHS } + { 15, 75, 15, 75 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 50, 0, 50 }, // Burn { nHS, pH, pS, pHS } + { 0, 50, 0, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 100, 25, 100 }, // Balanced { nHS, pH, pS, pHS } + { 15, 75, 15, 75 }, // Efficient { nHS, pH, pS, pHS } + { 25, 100, 25, 100 }, // Reactive { nHS, pH, pS, pHS } + { 15, 75, 15, 75 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 50, 0, 50 }, // Burn { nHS, pH, pS, pHS } + { 0, 50, 0, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Burn { nHS, pH, pS, pHS } + { 15, 15, 15, 15 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 100, 25, 100 }, // Balanced { nHS, pH, pS, pHS } + { 15, 100, 15, 100 }, // Efficient { nHS, pH, pS, pHS } + { 25, 100, 25, 100 }, // Reactive { nHS, pH, pS, pHS } + { 25, 75, 25, 75 }, // Aggressive { nHS, pH, pS, pHS } + { 10, 50, 10, 50 }, // Burn { nHS, pH, pS, pHS } + { 10, 50, 10, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 100, 25, 100 }, // Balanced { nHS, pH, pS, pHS } + { 15, 100, 15, 100 }, // Efficient { nHS, pH, pS, pHS } + { 25, 100, 25, 100 }, // Reactive { nHS, pH, pS, pHS } + { 25, 75, 25, 75 }, // Aggressive { nHS, pH, pS, pHS } + { 10, 50, 10, 50 }, // Burn { nHS, pH, pS, pHS } + { 10, 50, 10, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 25, 25, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 25, 25, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 100, 25, 100 }, // Balanced { nHS, pH, pS, pHS } + { 15, 75, 15, 75 }, // Efficient { nHS, pH, pS, pHS } + { 25, 100, 25, 100 }, // Reactive { nHS, pH, pS, pHS } + { 15, 75, 15, 75 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 50, 0, 50 }, // Burn { nHS, pH, pS, pHS } + { 0, 50, 0, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_RootIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_BuffIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_EscapeIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Burn { nHS, pH, pS, pHS } + { 15, 15, 15, 15 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Efficient { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 25, 25, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Burn { nHS, pH, pS, pHS } + { 15, 15, 15, 15 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Burn { nHS, pH, pS, pHS } + { 15, 15, 15, 15 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Burn { nHS, pH, pS, pHS } + { 15, 15, 15, 15 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Burn { nHS, pH, pS, pHS } + { 15, 15, 15, 15 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Efficient { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 25, 25, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Burn { nHS, pH, pS, pHS } + { 15, 15, 15, 15 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Efficient { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 25, 25, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_PetIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Aggressive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Burn { nHS, pH, pS, pHS } + { 10, 10, 10, 10 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Aggressive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Burn { nHS, pH, pS, pHS } + { 10, 10, 10, 10 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Aggressive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Burn { nHS, pH, pS, pHS } + { 10, 10, 10, 10 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 25, 25, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 25, 25, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Aggressive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Burn { nHS, pH, pS, pHS } + { 10, 10, 10, 10 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 25, 25, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_LifetapIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Burn { nHS, pH, pS, pHS } + { 100, 100, 100, 100 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Burn { nHS, pH, pS, pHS } + { 100, 100, 100, 100 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_SnareIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_DOTIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 15, 10, 10 }, // Balanced { nHS, pH, pS, pHS } + { 10, 10, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 15, 15, 10, 10 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 50, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 50, 50, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 15, 10, 10 }, // Balanced { nHS, pH, pS, pHS } + { 10, 10, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 15, 15, 10, 10 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 50, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 50, 50, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 15, 50, 15 }, // Balanced { nHS, pH, pS, pHS } + { 25, 10, 25, 10 }, // Efficient { nHS, pH, pS, pHS } + { 50, 15, 50, 15 }, // Reactive { nHS, pH, pS, pHS } + { 50, 25, 50, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 25, 50, 25 }, // Burn { nHS, pH, pS, pHS } + { 50, 25, 50, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 25, 25, 15, 15 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 100, 100, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 100, 100, 50, 50 }, // Burn { nHS, pH, pS, pHS } + { 100, 100, 50, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 15, 15, 0 }, // Balanced { nHS, pH, pS, pHS } + { 15, 10, 10, 0 }, // Efficient { nHS, pH, pS, pHS } + { 25, 15, 15, 0 }, // Reactive { nHS, pH, pS, pHS } + { 50, 25, 50, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 25, 50, 15 }, // Burn { nHS, pH, pS, pHS } + { 50, 25, 50, 15 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Balanced { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Burn { nHS, pH, pS, pHS } + { 75, 75, 75, 75 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 15, 15 }, // Balanced { nHS, pH, pS, pHS } + { 25, 25, 10, 10 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 15, 15 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 15, 15, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 15, 15, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 15, 10, 10 }, // Balanced { nHS, pH, pS, pHS } + { 10, 10, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 15, 15, 10, 10 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 50, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 50, 50, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_DispelIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_InCombatBuffIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Reactive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Aggressive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Burn { nHS, pH, pS, pHS } + { 25, 25, 25, 25 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Burn { nHS, pH, pS, pHS } + { 50, 50, 50, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 75, 50, 75 }, // Balanced { nHS, pH, pS, pHS } + { 25, 50, 25, 50 }, // Efficient { nHS, pH, pS, pHS } + { 50, 75, 50, 75 }, // Reactive { nHS, pH, pS, pHS } + { 75, 100, 75, 100 }, // Aggressive { nHS, pH, pS, pHS } + { 75, 100, 75, 100 }, // Burn { nHS, pH, pS, pHS } + { 75, 100, 75, 100 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_MezIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_CharmIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_SlowIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 15, 15, 100, 100 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 50, 50 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 50, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 25, 25, 100, 100 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 15, 15, 50, 50 }, // Burn { nHS, pH, pS, pHS } + { 15, 15, 50, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 50, 50, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 25, 25, 100, 100 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 15, 15, 50, 50 }, // Burn { nHS, pH, pS, pHS } + { 15, 15, 50, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 15, 15, 100, 100 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 50, 50 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 50, 50 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_DebuffIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Balanced { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Efficient { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Reactive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Balanced { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Efficient { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Reactive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Efficient { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Reactive { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Balanced { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Efficient { nHS, pH, pS, pHS } + { 25, 25, 25, 25 }, // Reactive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Balanced { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Efficient { nHS, pH, pS, pHS } + { 15, 15, 15, 15 }, // Reactive { nHS, pH, pS, pHS } + { 10, 10, 10, 10 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_CureIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Balanced { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Efficient { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Reactive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Aggressive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Burn { nHS, pH, pS, pHS } + { 100, 100, 100, 100 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_ResurrectIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_HateReduxIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_InCombatBuffSongIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Balanced { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Efficient { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Reactive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Aggressive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Burn { nHS, pH, pS, pHS } + { 100, 100, 100, 100 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } + }, +// SpellType_OutOfCombatBuffSongIndex + { + {// WarriorIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ClericIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// PaladinIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RangerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShadowknightIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// DruidIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MonkIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Balanced { nHS, pH, pS, pHS } + { 50, 50, 50, 50 }, // Efficient { nHS, pH, pS, pHS } + { 75, 75, 75, 75 }, // Reactive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Aggressive { nHS, pH, pS, pHS } + { 100, 100, 100, 100 }, // Burn { nHS, pH, pS, pHS } + { 100, 100, 100, 100 } // BurnAE { nHS, pH, pS, pHS } + }, + {// RogueIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// ShamanIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// NecromancerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// WizardIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// MagicianIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// EnchanterIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BeastlordIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + }, + {// BerserkerIndex + { 0, 0, 0, 0 }, // Passive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Balanced { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Efficient { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Reactive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Aggressive { nHS, pH, pS, pHS } + { 0, 0, 0, 0 }, // Burn { nHS, pH, pS, pHS } + { 0, 0, 0, 0 } // BurnAE { nHS, pH, pS, pHS } + } } - } - - BotStanceType botStance = GetBotStance(); - uint8 botClass = GetClass(); - bool isPrimaryHealer = false; - bool isPrimarySlower = false; - - if(HasGroup()) { - isPrimaryHealer = IsGroupPrimaryHealer(); - isPrimarySlower = IsGroupPrimarySlower(); - } - - //Nuke - switch(botClass) - { - case WIZARD: - case MAGICIAN: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - case BotStanceAggressive: - castChance = 75; - break; - case BotStanceEfficient: - castChance = 50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 100; - break; - default: - castChance = 0; - break; - } - break; - case DRUID: - case CLERIC: - case PALADIN: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimaryHealer?15:25; - break; - case BotStanceEfficient: - castChance = isPrimaryHealer?0:15; - break; - case BotStanceAggressive: - castChance = isPrimaryHealer?15:50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimaryHealer?25:50; - break; - default: - castChance = 0; - break; - } - break; - case ENCHANTER: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimarySlower?15:25; - break; - case BotStanceEfficient: - castChance = isPrimarySlower?0:15; - break; - case BotStanceAggressive: - castChance = isPrimarySlower?15:50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimarySlower?25:50; - break; - default: - castChance = 0; - break; - } - break; - case BEASTLORD: - case SHAMAN: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimarySlower?isPrimaryHealer?0:5:isPrimaryHealer?10:15; - break; - case BotStanceEfficient: - castChance = isPrimarySlower?isPrimaryHealer?0:0:isPrimaryHealer?5:10; - break; - case BotStanceAggressive: - castChance = isPrimarySlower?isPrimaryHealer?5:15:isPrimaryHealer?15:25; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimarySlower?isPrimaryHealer?15:25:isPrimaryHealer?25:50; - break; - default: - castChance = 0; - break; - } - break; - case NECROMANCER: - case RANGER: - case SHADOWKNIGHT: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - case BotStanceAggressive: - castChance = 15; - break; - case BotStanceEfficient: - castChance = 5; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 50; - break; - default: - castChance = 0; - break; - } - break; - case BARD: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - case BotStanceAggressive: - castChance = 50; - break; - case BotStanceEfficient: - castChance = 25; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 100; - break; - default: - castChance = 0; - break; - } - break; - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_NukeIndex] = castChance; - - //Heal - switch(botClass) - { - case CLERIC: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceEfficient: - case BotStanceReactive: - castChance = 100; - break; - case BotStanceAggressive: - castChance = 75; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 50; - break; - default: - castChance = 0; - break; - } - break; - case DRUID: - case SHAMAN: - switch(botStance) - { - case BotStanceEfficient: - castChance = isPrimaryHealer?100:15; - break; - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimaryHealer?100:25; - break; - case BotStanceAggressive: - castChance = isPrimaryHealer?75:15; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimaryHealer?50:10; - break; - default: - castChance = 0; - break; - } - break; - case NECROMANCER: - case MAGICIAN: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 100; - break; - case BotStanceEfficient: - case BotStanceAggressive: - castChance = 50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 25; - break; - default: - castChance = 0; - break; - } - break; - case SHADOWKNIGHT: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 50; - break; - case BotStanceEfficient: - case BotStanceAggressive: - castChance = 25; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 15; - break; - default: - castChance = 0; - break; - } - break; - case BEASTLORD: - case PALADIN: - case RANGER: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimaryHealer?100:25; - break; - case BotStanceEfficient: - case BotStanceAggressive: - castChance = isPrimaryHealer?75:15; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimaryHealer?50:0; - break; - default: - castChance = 0; - break; - } - break; - case BARD: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 100; - break; - case BotStanceEfficient: - case BotStanceAggressive: - castChance = 50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 25; - break; - default: - castChance = 0; - break; - } - break; - case ENCHANTER: - case WIZARD: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_HealIndex] = castChance; - - //Root - castChance = 0; - _spellCastingChances[botStance][SpellType_RootIndex] = castChance; - - //Buff - switch(botClass) - { - case BARD: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 100; - break; - case BotStanceEfficient: - case BotStanceAggressive: - castChance = 50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 25; - break; - default: - castChance = 0; - break; - } - break; - case CLERIC: - case DRUID: - case SHAMAN: - case NECROMANCER: - case MAGICIAN: - case SHADOWKNIGHT: - case BEASTLORD: - case PALADIN: - case RANGER: - case ENCHANTER: - case WIZARD: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_BuffIndex] = castChance; - - //Escape - switch(botClass) - { - case ENCHANTER: - case WIZARD: - case RANGER: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - case BotStanceEfficient: - castChance = 100; - break; - case BotStanceAggressive: - castChance = 50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 25; - break; - default: - castChance = 0; - break; - } - break; - case CLERIC: - case DRUID: - case SHAMAN: - case NECROMANCER: - case MAGICIAN: - case BARD: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - case BotStanceEfficient: - castChance = 50; - break; - case BotStanceAggressive: - castChance = 25; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 15; - break; - default: - castChance = 0; - break; - } - break; - case SHADOWKNIGHT: - case PALADIN: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 25; - break; - case BotStanceEfficient: - castChance = 15; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - break; - case BEASTLORD: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_EscapeIndex] = castChance; - - //Pet - switch(botClass) - { - case MAGICIAN: - case NECROMANCER: - case BEASTLORD: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 100; - break; - case BotStanceEfficient: - case BotStanceAggressive: - castChance = 50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 25; - break; - default: - castChance = 0; - break; - } - break; - case DRUID: - case SHAMAN: - case ENCHANTER: - case SHADOWKNIGHT: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 25; - break; - case BotStanceEfficient: - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 10; - break; - default: - castChance = 0; - break; - } - break; - case BARD: - case WIZARD: - case CLERIC: - case RANGER: - case PALADIN: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_PetIndex] = castChance; - - //Lifetap - switch(botClass) - { - case NECROMANCER: - case SHADOWKNIGHT: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - case BotStanceEfficient: - case BotStanceAggressive: - castChance = 50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 100; - break; - default: - castChance = 0; - break; - } - break; - case MAGICIAN: - case BEASTLORD: - case DRUID: - case SHAMAN: - case ENCHANTER: - case BARD: - case WIZARD: - case CLERIC: - case RANGER: - case PALADIN: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_LifetapIndex] = castChance; - - //Snare - castChance = 0; - _spellCastingChances[botStance][SpellType_SnareIndex] = castChance; - - //DOT - switch(botClass) - { - case NECROMANCER: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - case BotStanceAggressive: - castChance = 50; - break; - case BotStanceEfficient: - castChance = 25; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 75; - break; - default: - castChance = 0; - break; - } - break; - case DRUID: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimaryHealer?15:50; - break; - case BotStanceEfficient: - castChance = isPrimaryHealer?10:25; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimaryHealer?25:50; - break; - default: - castChance = 0; - break; - } - break; - case ENCHANTER: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimarySlower?15:50; - break; - case BotStanceEfficient: - castChance = isPrimarySlower?10:25; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimarySlower?25:15; - break; - default: - castChance = 0; - break; - } - break; - case SHAMAN: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimarySlower?isPrimaryHealer?0:15:isPrimaryHealer?15:25; - break; - case BotStanceEfficient: - castChance = isPrimarySlower?isPrimaryHealer?0:10:isPrimaryHealer?10:15; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimarySlower?isPrimaryHealer?15:50:isPrimaryHealer?25:50; - break; - default: - castChance = 0; - break; - } - break; - case BEASTLORD: - case RANGER: - case SHADOWKNIGHT: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimarySlower?10:15; - break; - case BotStanceEfficient: - castChance = isPrimarySlower?0:10; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimarySlower?25:50; - break; - default: - castChance = 0; - break; - } - break; - case BARD: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimarySlower?25:50; - break; - case BotStanceEfficient: - castChance = isPrimarySlower?15:25; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimarySlower?50:100; - break; - default: - castChance = 0; - break; - } - break; - case MAGICIAN: - case PALADIN: - case CLERIC: - case WIZARD: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_DOTIndex] = castChance; - - //Dispel - switch(botClass) - { - case BARD: - case ENCHANTER: - case WIZARD: - case MAGICIAN: - case CLERIC: - case DRUID: - case SHAMAN: - case NECROMANCER: - case RANGER: - case SHADOWKNIGHT: - case PALADIN: - case BEASTLORD: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_DispelIndex] = castChance; - - //InCombatBuff - switch(botClass) - { - case CLERIC: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 15; - break; - case BotStanceEfficient: - castChance = 0; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 25; - break; - default: - castChance = 0; - break; - } - break; - case PALADIN: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 25; - break; - case BotStanceEfficient: - castChance = 0; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 50; - break; - default: - castChance = 0; - break; - } - break; - case SHAMAN: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimaryHealer?75:50; - break; - case BotStanceEfficient: - castChance = isPrimaryHealer?50:25; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimaryHealer?100:75; - break; - default: - castChance = 0; - break; - } - break; - case BARD: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 100; - break; - case BotStanceEfficient: - case BotStanceAggressive: - castChance = 50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 25; - break; - default: - castChance = 0; - break; - } - break; - case BEASTLORD: - case MAGICIAN: - case DRUID: - case ENCHANTER: - case WIZARD: - case NECROMANCER: - case SHADOWKNIGHT: - case RANGER: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_InCombatBuffIndex] = castChance; - - //Mez - switch(botClass) - { - case ENCHANTER: - case BARD: - castChance = 100; - break; - case WIZARD: - case CLERIC: - case DRUID: - case SHAMAN: - case NECROMANCER: - case MAGICIAN: - case RANGER: - case SHADOWKNIGHT: - case PALADIN: - case BEASTLORD: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_MezIndex] = castChance; - - //Charm - castChance = 0; - _spellCastingChances[botStance][SpellType_CharmIndex] = castChance; - - //Slow - switch(botClass) - { - case ENCHANTER: - case SHAMAN: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimarySlower?100:50; - break; - case BotStanceEfficient: - castChance = isPrimarySlower?100:25; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimarySlower?50:15; - break; - default: - castChance = 0; - break; - } - break; - case BARD: - case BEASTLORD: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = isPrimarySlower?100:25; - break; - case BotStanceEfficient: - castChance = isPrimarySlower?100:15; - break; - case BotStanceAggressive: - case BotStanceBurn: - case BotStanceBurnAE: - castChance = isPrimarySlower?50:0; - break; - default: - castChance = 0; - break; - } - break; - case MAGICIAN: - case DRUID: - case PALADIN: - case CLERIC: - case WIZARD: - case NECROMANCER: - case SHADOWKNIGHT: - case RANGER: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_SlowIndex] = castChance; - - //Debuff - switch(botClass) - { - case ENCHANTER: - case SHAMAN: - case MAGICIAN: - case DRUID: - case NECROMANCER: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 25; - break; - case BotStanceEfficient: - case BotStanceAggressive: - castChance = 15; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - break; - case BEASTLORD: - case RANGER: - case SHADOWKNIGHT: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceReactive: - castChance = 15; - break; - case BotStanceEfficient: - case BotStanceAggressive: - castChance = 10; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - break; - case BARD: - switch(botStance) - { - case BotStanceBalanced: - case BotStanceEfficient: - castChance = 25; - break; - case BotStanceReactive: - case BotStanceAggressive: - castChance = 50; - break; - case BotStanceBurn: - case BotStanceBurnAE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - break; - case CLERIC: - case PALADIN: - case WIZARD: - case WARRIOR: - case BERSERKER: - case MONK: - case ROGUE: - castChance = 0; - break; - default: - castChance = 0; - break; - } - _spellCastingChances[botStance][SpellType_DebuffIndex] = castChance; - - //Cure - castChance = 0; - _spellCastingChances[botStance][SpellType_CureIndex] = castChance; -} - -uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) { - int index = 0; - int botStance = (int)GetBotStance(); - uint8 chance = 0; - - if(GetBotStance() >= MaxStances) - return 0; - + }; + + uint8 spell_type_index = MaxSpellTypes; switch (spellType) { - case SpellType_Nuke: { - index = SpellType_NukeIndex; - break; - } - case SpellType_Heal: { - index = SpellType_HealIndex; - break; - } - case SpellType_Root: { - index = SpellType_RootIndex; - break; - } - case SpellType_Buff: { - index = SpellType_BuffIndex; - break; - } - case SpellType_Escape: { - index = SpellType_EscapeIndex; - break; - } - case SpellType_Pet: { - index = SpellType_PetIndex; - break; - } - case SpellType_Lifetap: { - index = SpellType_LifetapIndex; - break; - } - case SpellType_Snare: { - index = SpellType_SnareIndex; - break; - } - case SpellType_DOT: { - index = SpellType_DOTIndex; - break; - } - case SpellType_Dispel: { - index = SpellType_DispelIndex; - break; - } - case SpellType_InCombatBuff: { - index = SpellType_InCombatBuffIndex; - break; - } - case SpellType_Mez: { - index = SpellType_MezIndex; - break; - } - case SpellType_Charm: { - index = SpellType_CharmIndex; - break; - } - case SpellType_Slow: { - index = SpellType_SlowIndex; - break; - } - case SpellType_Debuff: { - index = SpellType_DebuffIndex; - break; - } - case SpellType_Cure: { - index = SpellType_CureIndex; - break; - } + case SpellType_Nuke: + spell_type_index = SpellType_NukeIndex; + break; + case SpellType_Heal: + spell_type_index = SpellType_HealIndex; + break; + case SpellType_Root: + spell_type_index = SpellType_RootIndex; + break; + case SpellType_Buff: + spell_type_index = SpellType_BuffIndex; + break; + case SpellType_Escape: + spell_type_index = SpellType_EscapeIndex; + break; + case SpellType_Pet: + spell_type_index = SpellType_PetIndex; + break; + case SpellType_Lifetap: + spell_type_index = SpellType_LifetapIndex; + break; + case SpellType_Snare: + spell_type_index = SpellType_SnareIndex; + break; + case SpellType_DOT: + spell_type_index = SpellType_DOTIndex; + break; + case SpellType_Dispel: + spell_type_index = SpellType_DispelIndex; + break; + case SpellType_InCombatBuff: + spell_type_index = SpellType_InCombatBuffIndex; + break; + case SpellType_Mez: + spell_type_index = SpellType_MezIndex; + break; + case SpellType_Charm: + spell_type_index = SpellType_CharmIndex; + break; + case SpellType_Slow: + spell_type_index = SpellType_SlowIndex; + break; + case SpellType_Debuff: + spell_type_index = SpellType_DebuffIndex; + break; + case SpellType_Cure: + spell_type_index = SpellType_CureIndex; + break; + case SpellType_Resurrect: + spell_type_index = SpellType_ResurrectIndex; + break; + case SpellType_HateRedux: + spell_type_index = SpellType_HateReduxIndex; + break; + case SpellType_InCombatBuffSong: + spell_type_index = SpellType_InCombatBuffSongIndex; + break; + case SpellType_OutOfCombatBuffSong: + spell_type_index = SpellType_OutOfCombatBuffSongIndex; + break; + default: + spell_type_index = MaxSpellTypes; + break; } - - if(index >= MaxSpellTypes) + if (spell_type_index >= MaxSpellTypes) return 0; - chance = _spellCastingChances[botStance][index]; + uint8 class_index = GetClass(); + if (class_index > BERSERKER || class_index < WARRIOR) + return 0; + --class_index; - return chance; + uint8 stance_index = (uint8)GetBotStance(); + if (stance_index >= MaxStances) + return 0; + + uint8 type_index = nHS; + if (HasGroup()) { + if (IsGroupPrimaryHealer()) + type_index |= pH; + if (IsGroupPrimarySlower()) + type_index |= pS; + } + + return spell_casting_chances[spell_type_index][class_index][stance_index][type_index]; } #endif diff --git a/zone/client.cpp b/zone/client.cpp index df63dca27..6ec0a019d 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -154,6 +154,7 @@ Client::Client(EQStreamInterface* ieqs) afk_toggle_timer(250), helm_toggle_timer(250), light_update_timer(600), + aggro_meter_timer(AGGRO_METER_UPDATE_MS), m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f), m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), @@ -310,6 +311,8 @@ Client::Client(EQStreamInterface* ieqs) } MaxXTargets = 5; XTargetAutoAddHaters = true; + m_autohatermgr.SetOwner(this, nullptr, nullptr); + m_activeautohatermgr = &m_autohatermgr; LoadAccountFlags(); initial_respawn_selection = 0; @@ -4158,6 +4161,18 @@ bool Client::GroupFollow(Client* inviter) { } if (raid->RaidCount() < MAX_RAID_MEMBERS) { + // okay, so we now have a single client (this) joining a group in a raid + // And they're not already in the raid (which is above and doesn't need xtarget shit) + if (!GetXTargetAutoMgr()->empty()) { + raid->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr()); + GetXTargetAutoMgr()->clear(); + RemoveAutoXTargets(); + } + + SetXTargetAutoMgr(GetXTargetAutoMgr()); + if (!GetXTargetAutoMgr()->empty()) + SetDirtyAutoHaters(); + if (raid->GroupCount(groupToUse) < 6) { raid->SendRaidCreate(this); @@ -4233,7 +4248,9 @@ bool Client::GroupFollow(Client* inviter) { inviter->SendGroupLeaderChangePacket(inviter->GetName()); inviter->SendGroupJoinAcknowledge(); } - + group->GetXTargetAutoMgr()->merge(*inviter->GetXTargetAutoMgr()); + inviter->GetXTargetAutoMgr()->clear(); + inviter->SetXTargetAutoMgr(group->GetXTargetAutoMgr()); } if (!group) @@ -7169,12 +7186,12 @@ void Client::UpdateClientXTarget(Client *c) } } +// IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO void Client::AddAutoXTarget(Mob *m, bool send) { - if(!XTargettingAvailable() || !XTargetAutoAddHaters) - return; + m_activeautohatermgr->increment_count(m); - if(IsXTarget(m)) + if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m)) return; for(int i = 0; i < GetMaxXTargets(); ++i) @@ -7193,60 +7210,15 @@ void Client::AddAutoXTarget(Mob *m, bool send) void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots) { - if (!XTargettingAvailable()) - return; - - bool HadFreeAutoSlotsBefore = false; - - int FreedAutoSlots = 0; - - if (m->GetID() == 0) - return; - + m_activeautohatermgr->decrement_count(m); + // now we may need to clean up our CurrentTargetNPC entries for (int i = 0; i < GetMaxXTargets(); ++i) { - if (OnlyAutoSlots && XTargets[i].Type != Auto) - continue; - - if (XTargets[i].ID == m->GetID()) { - if (XTargets[i].Type == CurrentTargetNPC) - XTargets[i].Type = Auto; - - if (XTargets[i].Type == Auto) - ++FreedAutoSlots; - + if (XTargets[i].Type == CurrentTargetNPC && XTargets[i].ID == m->GetID()) { + XTargets[i].Type = Auto; XTargets[i].ID = 0; XTargets[i].dirty = true; - } else { - if (XTargets[i].Type == Auto && XTargets[i].ID == 0) - HadFreeAutoSlotsBefore = true; } } - - // move shit up! If the removed NPC was in a CurrentTargetNPC slot it becomes Auto - // and we need to potentially fill it - std::queue empty_slots; - for (int i = 0; i < GetMaxXTargets(); ++i) { - if (XTargets[i].Type != Auto) - continue; - - if (XTargets[i].ID == 0) { - empty_slots.push(i); - continue; - } - - if (XTargets[i].ID != 0 && !empty_slots.empty()) { - int temp = empty_slots.front(); - std::swap(XTargets[i], XTargets[temp]); - XTargets[i].dirty = XTargets[temp].dirty = true; - empty_slots.pop(); - empty_slots.push(i); - } - } - // If there are more mobs aggro on us than we had auto-hate slots, add one of those haters into the slot(s) we - // just freed up. - if (!HadFreeAutoSlotsBefore && FreedAutoSlots) - entity_list.RefreshAutoXTargets(this); - SendXTargetUpdates(); } void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name) @@ -7403,6 +7375,123 @@ void Client::ShowXTargets(Client *c) for(int i = 0; i < GetMaxXTargets(); ++i) c->Message(0, "Xtarget Slot: %i, Type: %2i, ID: %4i, Name: %s", i, XTargets[i].Type, XTargets[i].ID, XTargets[i].Name); + auto &list = GetXTargetAutoMgr()->get_list(); + // yeah, I kept having to do something for debugging to tell if managers were the same object or not :P + // so lets use the address as an "ID" + c->Message(0, "XTargetAutoMgr ID %p size %d", GetXTargetAutoMgr(), list.size()); + int count = 0; + for (auto &e : list) { + c->Message(0, "spawn id %d count %d", e.spawn_id, e.count); + count++; + if (count == 20) { // lets not spam too many ... + c->Message(0, " ... "); + break; + } + } +} + +void Client::ProcessXTargetAutoHaters() +{ + if (!XTargettingAvailable()) + return; + + // move shit up! If the removed NPC was in a CurrentTargetNPC slot it becomes Auto + // and we need to potentially fill it + std::queue empty_slots; + for (int i = 0; i < GetMaxXTargets(); ++i) { + if (XTargets[i].Type != Auto) + continue; + + if (XTargets[i].ID != 0 && !GetXTargetAutoMgr()->contains_mob(XTargets[i].ID)) { + XTargets[i].ID = 0; + XTargets[i].dirty = true; + } + + if (XTargets[i].ID == 0) { + empty_slots.push(i); + continue; + } + + if (XTargets[i].ID != 0 && !empty_slots.empty()) { + int temp = empty_slots.front(); + std::swap(XTargets[i], XTargets[temp]); + XTargets[i].dirty = XTargets[temp].dirty = true; + empty_slots.pop(); + empty_slots.push(i); + } + } + // okay, now we need to check if we have any empty slots and if we have aggro + // We make the assumption that if we shuffled the NPCs up that they're still on the aggro + // list in the same order. We could probably do this better and try to calc if + // there are new NPCs for our empty slots on the manager, but ahhh fuck it. + if (!empty_slots.empty() && !GetXTargetAutoMgr()->empty() && XTargetAutoAddHaters) { + auto &haters = GetXTargetAutoMgr()->get_list(); + for (auto &e : haters) { + auto *mob = entity_list.GetMob(e.spawn_id); + if (mob && !IsXTarget(mob)) { + auto slot = empty_slots.front(); + empty_slots.pop(); + XTargets[slot].dirty = true; + XTargets[slot].ID = mob->GetID(); + strn0cpy(XTargets[slot].Name, mob->GetCleanName(), 64); + } + if (empty_slots.empty()) + break; + } + } + m_dirtyautohaters = false; + SendXTargetUpdates(); +} + +// This function is called when a client is added to a group +// Group leader joining isn't handled by this function +void Client::JoinGroupXTargets(Group *g) +{ + if (!g) + return; + + if (!GetXTargetAutoMgr()->empty()) { + g->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr()); + GetXTargetAutoMgr()->clear(); + RemoveAutoXTargets(); + } + + SetXTargetAutoMgr(g->GetXTargetAutoMgr()); + + if (!GetXTargetAutoMgr()->empty()) + SetDirtyAutoHaters(); +} + +// This function is called when a client leaves a group +void Client::LeaveGroupXTargets(Group *g) +{ + if (!g) + return; + + SetXTargetAutoMgr(nullptr); // this will set it back to our manager + RemoveAutoXTargets(); + entity_list.RefreshAutoXTargets(this); // this will probably break the temporal ordering, but whatever + // We now have a rebuilt, valid auto hater manager, so we need to demerge from the groups + if (!GetXTargetAutoMgr()->empty()) { + GetXTargetAutoMgr()->demerge(*g->GetXTargetAutoMgr()); // this will remove entries where we only had aggro + SetDirtyAutoHaters(); + } +} + +// This function is called when a client leaves a group +void Client::LeaveRaidXTargets(Raid *r) +{ + if (!r) + return; + + SetXTargetAutoMgr(nullptr); // this will set it back to our manager + RemoveAutoXTargets(); + entity_list.RefreshAutoXTargets(this); // this will probably break the temporal ordering, but whatever + // We now have a rebuilt, valid auto hater manager, so we need to demerge from the groups + if (!GetXTargetAutoMgr()->empty()) { + GetXTargetAutoMgr()->demerge(*r->GetXTargetAutoMgr()); // this will remove entries where we only had aggro + SetDirtyAutoHaters(); + } } void Client::SetMaxXTargets(uint8 NewMax) @@ -8681,3 +8770,169 @@ void Client::CheckRegionTypeChanges() else if (GetPVP()) SetPVP(false, false); } + +void Client::ProcessAggroMeter() +{ + if (!AggroMeterAvailable()) { + aggro_meter_timer.Disable(); + return; + } + + // we need to decide if we need to send OP_AggroMeterTargetInfo now + // This packet sends the current lock target ID and the current target ID + // target ID will be either our target or our target of target when we're targeting a PC + bool send_targetinfo = false; + auto cur_tar = GetTarget(); + + // probably should have PVP rules ... + if (cur_tar && cur_tar != this) { + if (cur_tar->IsNPC() && !cur_tar->IsPetOwnerClient() && cur_tar->GetID() != m_aggrometer.get_target_id()) { + m_aggrometer.set_target_id(cur_tar->GetID()); + send_targetinfo = true; + } else if ((cur_tar->IsPetOwnerClient() || cur_tar->IsClient()) && cur_tar->GetTarget() && cur_tar->GetTarget()->GetID() != m_aggrometer.get_target_id()) { + m_aggrometer.set_target_id(cur_tar->GetTarget()->GetID()); + send_targetinfo = true; + } + } else if (m_aggrometer.get_target_id()) { + m_aggrometer.set_target_id(0); + send_targetinfo = true; + } + + if (m_aggrometer.update_lock()) + send_targetinfo = true; + + if (send_targetinfo) { + auto app = new EQApplicationPacket(OP_AggroMeterTargetInfo, sizeof(uint32) * 2); + app->WriteUInt32(m_aggrometer.get_lock_id()); + app->WriteUInt32(m_aggrometer.get_target_id()); + FastQueuePacket(&app); + } + + // we could just calculate how big the packet would need to be ... but it's easier this way :P should be 87 bytes + auto app = new EQApplicationPacket(OP_AggroMeterUpdate, m_aggrometer.max_packet_size()); + + cur_tar = entity_list.GetMob(m_aggrometer.get_target_id()); + + // first we must check the secondary + // TODO: lock target should affect secondary as well + bool send = false; + Mob *secondary = nullptr; + bool has_aggro = false; + if (cur_tar) { + if (cur_tar->GetTarget() == this) {// we got aggro + secondary = cur_tar->GetSecondaryHate(this); + has_aggro = true; + } else { + secondary = cur_tar->GetTarget(); + } + } + + if (secondary && secondary->GetID() != m_aggrometer.get_secondary_id()) { + m_aggrometer.set_secondary_id(secondary->GetID()); + app->WriteUInt8(1); + app->WriteUInt32(m_aggrometer.get_secondary_id()); + send = true; + } else if (!secondary && m_aggrometer.get_secondary_id()) { + m_aggrometer.set_secondary_id(0); + app->WriteUInt8(1); + app->WriteUInt32(0); + send = true; + } else { // might not need to send in this case + app->WriteUInt8(0); + } + + auto count_offset = app->GetWritePosition(); + app->WriteUInt8(0); + + int count = 0; + auto add_entry = [&app, &count, this](AggroMeter::AggroTypes i) { + count++; + app->WriteUInt8(i); + app->WriteUInt16(m_aggrometer.get_pct(i)); + }; + // TODO: Player entry should either be lock or yourself, ignoring lock for now + // player, secondary, and group depend on your target/lock + if (cur_tar) { + if (m_aggrometer.set_pct(AggroMeter::AT_Player, cur_tar->GetHateRatio(cur_tar->GetTarget(), this))) + add_entry(AggroMeter::AT_Player); + + if (m_aggrometer.set_pct(AggroMeter::AT_Secondary, has_aggro ? cur_tar->GetHateRatio(this, secondary) : secondary ? 100 : 0)) + add_entry(AggroMeter::AT_Secondary); + + // fuuuuuuuuuuuuuuuuuuuuuuuucckkkkkkkkkkkkkkk raids + if (IsRaidGrouped()) { + auto raid = GetRaid(); + if (raid) { + auto gid = raid->GetGroup(this); + if (gid < 12) { + int at_id = AggroMeter::AT_Group1; + for (int i = 0; i < MAX_RAID_MEMBERS; ++i) { + if (raid->members[i].member && raid->members[i].member != this && raid->members[i].GroupNumber == gid) { + if (m_aggrometer.set_pct(static_cast(at_id), cur_tar->GetHateRatio(cur_tar->GetTarget(), raid->members[i].member))) + add_entry(static_cast(at_id)); + at_id++; + if (at_id > AggroMeter::AT_Group5) + break; + } + } + } + } + } else if (IsGrouped()) { + auto group = GetGroup(); + if (group) { + int at_id = AggroMeter::AT_Group1; + for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) { + if (group->members[i] && group->members[i] != this) { + if (m_aggrometer.set_pct(static_cast(at_id), cur_tar->GetHateRatio(cur_tar->GetTarget(), group->members[i]))) + add_entry(static_cast(at_id)); + at_id++; + } + } + } + } + } else { // we might need to clear out some data now + if (m_aggrometer.set_pct(AggroMeter::AT_Player, 0)) + add_entry(AggroMeter::AT_Player); + if (m_aggrometer.set_pct(AggroMeter::AT_Secondary, 0)) + add_entry(AggroMeter::AT_Secondary); + if (m_aggrometer.set_pct(AggroMeter::AT_Group1, 0)) + add_entry(AggroMeter::AT_Group1); + if (m_aggrometer.set_pct(AggroMeter::AT_Group2, 0)) + add_entry(AggroMeter::AT_Group2); + if (m_aggrometer.set_pct(AggroMeter::AT_Group3, 0)) + add_entry(AggroMeter::AT_Group3); + if (m_aggrometer.set_pct(AggroMeter::AT_Group4, 0)) + add_entry(AggroMeter::AT_Group5); + if (m_aggrometer.set_pct(AggroMeter::AT_Group5, 0)) + add_entry(AggroMeter::AT_Group5); + } + + // now to go over our xtargets + // if the entry is an NPC it's our hate relative to the NPCs current tank + // if it's a PC, it's their hate relative to our current target + for (int i = 0; i < GetMaxXTargets(); ++i) { + if (XTargets[i].ID) { + auto mob = entity_list.GetMob(XTargets[i].ID); + if (mob) { + int ratio = 0; + if (mob->IsNPC()) + ratio = mob->GetHateRatio(mob->GetTarget(), this); + else if (cur_tar) + ratio = cur_tar->GetHateRatio(cur_tar->GetTarget(), mob); + if (m_aggrometer.set_pct(static_cast(AggroMeter::AT_XTarget1 + i), ratio)) + add_entry(static_cast(AggroMeter::AT_XTarget1 + i)); + } + } + } + + if (send || count) { + app->size = app->GetWritePosition(); // this should be safe, although not recommended + // but this way we can have a smaller buffer created for the packet dispatched to the client w/o resizing this one + app->SetWritePosition(count_offset); + app->WriteUInt8(count); + FastQueuePacket(&app); + } else { + safe_delete(app); + } +} + diff --git a/zone/client.h b/zone/client.h index d30e1901e..61da18686 100644 --- a/zone/client.h +++ b/zone/client.h @@ -48,6 +48,8 @@ namespace EQEmu #include "../common/inventory_profile.h" #include "../common/guilds.h" //#include "../common/item_data.h" +#include "xtargetautohaters.h" +#include "aggromanager.h" #include "common.h" #include "merc.h" @@ -1122,9 +1124,21 @@ public: void RemoveGroupXTargets(); void RemoveAutoXTargets(); void ShowXTargets(Client *c); + inline XTargetAutoHaters *GetXTargetAutoMgr() { return m_activeautohatermgr; } // will be either raid or group or self + inline void SetXTargetAutoMgr(XTargetAutoHaters *in) { if (in) m_activeautohatermgr = in; else m_activeautohatermgr = &m_autohatermgr; } + inline void SetDirtyAutoHaters() { m_dirtyautohaters = true; } + void ProcessXTargetAutoHaters(); // fixes up our auto haters + void JoinGroupXTargets(Group *g); + void LeaveGroupXTargets(Group *g); + void LeaveRaidXTargets(Raid *r); bool GroupFollow(Client* inviter); inline bool GetRunMode() const { return runmode; } + inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQEmu::versions::bit_RoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested + inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); } + + void ProcessAggroMeter(); // builds packet and sends + void InitializeMercInfo(); bool CheckCanSpawnMerc(uint32 template_id); bool CheckCanHireMerc(Mob* merchant, uint32 template_id); @@ -1462,6 +1476,7 @@ private: Timer afk_toggle_timer; Timer helm_toggle_timer; Timer light_update_timer; + Timer aggro_meter_timer; glm::vec3 m_Proximity; @@ -1546,8 +1561,13 @@ private: uint8 MaxXTargets; bool XTargetAutoAddHaters; + bool m_dirtyautohaters; struct XTarget_Struct XTargets[XTARGET_HARDCAP]; + XTargetAutoHaters m_autohatermgr; + XTargetAutoHaters *m_activeautohatermgr; + + AggroMeter m_aggrometer; Timer ItemTickTimer; Timer ItemQuestTimer; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d80e0a91f..bb42ce3c4 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -120,6 +120,7 @@ void MapOpcodes() ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell; ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest; ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest; + ConnectedOpcodes[OP_AggroMeterLockTarget] = &Client::Handle_OP_AggroMeterLockTarget; ConnectedOpcodes[OP_AltCurrencyMerchantRequest] = &Client::Handle_OP_AltCurrencyMerchantRequest; ConnectedOpcodes[OP_AltCurrencyPurchase] = &Client::Handle_OP_AltCurrencyPurchase; ConnectedOpcodes[OP_AltCurrencyReclaim] = &Client::Handle_OP_AltCurrencyReclaim; @@ -575,6 +576,11 @@ void Client::CompleteConnect() } } raid->SendGroupLeadershipAA(this, grpID); // this may get sent an extra time ... + + SetXTargetAutoMgr(raid->GetXTargetAutoMgr()); + if (!GetXTargetAutoMgr()->empty()) + SetDirtyAutoHaters(); + if (raid->IsLocked()) raid->SendRaidLockTo(this); } @@ -1548,8 +1554,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) // we purchased a new one while out-of-zone. if (group->IsLeader(this)) group->SendLeadershipAAUpdate(); - } + JoinGroupXTargets(group); group->UpdatePlayer(this); LFG = false; } @@ -2394,6 +2400,17 @@ void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app) FastQueuePacket(&outapp); } +void Client::Handle_OP_AggroMeterLockTarget(const EQApplicationPacket *app) +{ + if (app->size < sizeof(uint32)) { + Log.Out(Logs::General, Logs::Error, "Handle_OP_AggroMeterLockTarget had a packet that was too small."); + return; + } + + SetAggroMeterLock(app->ReadUInt32(0)); + ProcessAggroMeter(); +} + void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app) { VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32); @@ -10804,7 +10821,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } } } - g->DisbandGroup(); + g->JoinRaidXTarget(r); + g->DisbandGroup(true); r->GroupUpdate(freeGroup); } else{ @@ -10869,7 +10887,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } } } - ig->DisbandGroup(); + ig->JoinRaidXTarget(r, true); + ig->DisbandGroup(true); r->GroupUpdate(groupFree); groupFree = r->GetFreeGroup(); } @@ -10922,10 +10941,11 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } } } - g->DisbandGroup(); + g->JoinRaidXTarget(r); + g->DisbandGroup(true); r->GroupUpdate(groupFree); } - else + else // target does not have a group { if (ig){ r = new Raid(i); @@ -10979,14 +10999,15 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) r->SendRaidCreate(this); r->SendMakeLeaderPacketTo(r->leadername, this); r->SendBulkRaid(this); + ig->JoinRaidXTarget(r, true); r->AddMember(this); - ig->DisbandGroup(); + ig->DisbandGroup(true); r->GroupUpdate(0); if (r->IsLocked()) { r->SendRaidLockTo(this); } } - else{ + else{ // neither has a group r = new Raid(i); entity_list.AddRaid(r); r->SetRaidDetails(); @@ -12416,14 +12437,33 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) return; } - int cost_quantity = mp->quantity; + uint32 cost_quantity = mp->quantity; if (inst->IsCharged()) - int cost_quantity = 1; + uint32 cost_quantity = 1; + + uint32 i; + + if (RuleB(Merchant, UsePriceMod)) { + for (i = 1; i <= cost_quantity; i++) { + price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price + if (price > 4000000000) { + cost_quantity = i; + mp->quantity = i; + break; + } + } + } + else { + for (i = 1; i <= cost_quantity; i++) { + price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod)) + 0.5); // need to round up, because client does it automatically when displaying price + if (price > 4000000000) { + cost_quantity = i; + mp->quantity = i; + break; + } + } + } - if (RuleB(Merchant, UsePriceMod)) - price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price - else - price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod)) + 0.5); AddMoneyToPP(price, false); if (inst->IsStackable() || inst->IsCharged()) @@ -14079,6 +14119,7 @@ void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) } XTargetAutoAddHaters = app->ReadUInt8(0); + SetDirtyAutoHaters(); } void Client::Handle_OP_XTargetOpen(const EQApplicationPacket *app) diff --git a/zone/client_packet.h b/zone/client_packet.h index 2635724dd..76a26e04e 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -32,6 +32,7 @@ void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app); void Handle_OP_AdventureRequest(const EQApplicationPacket *app); void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app); + void Handle_OP_AggroMeterLockTarget(const EQApplicationPacket *app); void Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app); void Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app); void Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index bb2db4777..2bfa8a055 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -681,6 +681,13 @@ bool Client::Process() { Message(0, "Your enemies have forgotten you!"); } + if (client_state == CLIENT_CONNECTED) { + if (m_dirtyautohaters) + ProcessXTargetAutoHaters(); + if (aggro_meter_timer.Check()) + ProcessAggroMeter(); + } + return ret; } diff --git a/zone/entity.cpp b/zone/entity.cpp index 2ff83980e..9f23f63a4 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1404,15 +1404,15 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets) if (!m) continue; - m->RemoveFromHateList(mob); - if (RemoveFromXTargets) { - if (m->IsClient()) + if (m->IsClient() && mob->CheckAggro(m)) m->CastToClient()->RemoveXTarget(mob, false); // FadingMemories calls this function passing the client. - else if (mob->IsClient()) + else if (mob->IsClient() && m->CheckAggro(mob)) mob->CastToClient()->RemoveXTarget(m, false); } + + m->RemoveFromHateList(mob); } } @@ -2557,6 +2557,8 @@ void EntityList::RemoveFromHateLists(Mob *mob, bool settoone) it->second->RemoveFromHateList(mob); else it->second->SetHateAmountOnEnt(mob, 1); + if (mob->IsClient()) + mob->CastToClient()->RemoveXTarget(it->second, false); // gotta do book keeping } ++it; } diff --git a/zone/entity.h b/zone/entity.h index 9a532cdb1..1ea9a4c03 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -413,7 +413,7 @@ public: Mob* AICheckCloseAggro(Mob* sender, float iAggroRange, float iAssistRange); int GetHatedCount(Mob *attacker, Mob *exclude); void AIYellForHelp(Mob* sender, Mob* attacker); - bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint16 iSpellTypes); + bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes); bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes); Mob* GetTargetForMez(Mob* caster); uint32 CheckNPCsClose(Mob *center); @@ -502,7 +502,7 @@ private: Bot* GetBotByBotName(std::string botName); std::list GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); - bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate + bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff private: std::list bot_list; diff --git a/zone/groups.cpp b/zone/groups.cpp index 2162e735e..94a2b0032 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -64,6 +64,8 @@ Group::Group(uint32 gid) MarkedNPCs[i] = 0; NPCMarkerID = 0; + + m_autohatermgr.SetOwner(nullptr, this, nullptr); } //creating a new group @@ -94,6 +96,7 @@ Group::Group(Mob* leader) MarkedNPCs[i] = 0; NPCMarkerID = 0; + m_autohatermgr.SetOwner(nullptr, this, nullptr); } Group::~Group() @@ -333,19 +336,15 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte database.SetGroupID(NewMemberName, GetID(), owner->CharacterID(), true); } } -#ifdef BOTS - for (i = 0;i < MAX_GROUP_MEMBERS; i++) { - if (members[i] != nullptr && members[i]->IsBot()) { - members[i]->CastToBot()->CalcChanceToCast(); - } - } -#endif //BOTS } else { database.SetGroupID(NewMemberName, GetID(), CharacterID, ismerc); } + if (newmember && newmember->IsClient()) + newmember->CastToClient()->JoinGroupXTargets(this); + safe_delete(outapp); return true; @@ -496,16 +495,11 @@ void Group::MemberZoned(Mob* removemob) { SetLeader(nullptr); for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] == removemob) { - members[i] = nullptr; - //should NOT clear the name, it is used for world communication. - break; - } -#ifdef BOTS - if (members[i] != nullptr && members[i]->IsBot()) { - members[i]->CastToBot()->CalcChanceToCast(); + if (members[i] == removemob) { + members[i] = nullptr; + //should NOT clear the name, it is used for world communication. + break; } -#endif //BOTS } if(removemob->IsClient() && HasRole(removemob, RoleAssist)) @@ -661,11 +655,6 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) if(members[i]->IsClient()) members[i]->CastToClient()->QueuePacket(outapp); } -#ifdef BOTS - if (members[i] != nullptr && members[i]->IsBot()) { - members[i]->CastToBot()->CalcChanceToCast(); - } -#endif //BOTS } if (!ignoresender) @@ -718,8 +707,10 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) if (oldmember->GetName() == mentoree_name) ClearGroupMentor(); - if(oldmember->IsClient()) + if(oldmember->IsClient()) { SendMarkedNPCsToMember(oldmember->CastToClient(), true); + oldmember->CastToClient()->LeaveGroupXTargets(this); + } if(GroupCount() < 3) { @@ -872,7 +863,7 @@ uint32 Group::GetTotalGroupDamage(Mob* other) { return total; } -void Group::DisbandGroup() { +void Group::DisbandGroup(bool joinraid) { auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate_Struct)); GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer; @@ -899,6 +890,8 @@ void Group::DisbandGroup() { database.SetGroupID(members[i]->GetCleanName(), 0, members[i]->CastToClient()->CharacterID(), false); members[i]->CastToClient()->QueuePacket(outapp); SendMarkedNPCsToMember(members[i]->CastToClient(), true); + if (!joinraid) + members[i]->CastToClient()->LeaveGroupXTargets(this); } if (members[i]->IsMerc()) @@ -2292,6 +2285,30 @@ void Group::UpdateXTargetMarkedNPC(uint32 Number, Mob *m) } +void Group::SetDirtyAutoHaters() +{ + for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) + if (members[i] && members[i]->IsClient()) + members[i]->CastToClient()->SetDirtyAutoHaters(); +} + +void Group::JoinRaidXTarget(Raid *raid, bool first) +{ + if (!GetXTargetAutoMgr()->empty()) + raid->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr()); + + for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) { + if (members[i] && members[i]->IsClient()) { + auto *client = members[i]->CastToClient(); + if (!first) + client->RemoveAutoXTargets(); + client->SetXTargetAutoMgr(raid->GetXTargetAutoMgr()); + if (!client->GetXTargetAutoMgr()->empty()) + client->SetDirtyAutoHaters(); + } + } +} + void Group::SetMainTank(const char *NewMainTankName) { MainTankName = NewMainTankName; diff --git a/zone/groups.h b/zone/groups.h index 3da2aae54..6de364d93 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -22,6 +22,7 @@ #include "../common/types.h" #include "mob.h" +#include "xtargetautohaters.h" class Client; class EQApplicationPacket; @@ -58,7 +59,7 @@ public: void SendWorldGroup(uint32 zone_id,Mob* zoningmember); bool DelMemberOOZ(const char *Name); bool DelMember(Mob* oldmember,bool ignoresender = false); - void DisbandGroup(); + void DisbandGroup(bool joinraid = false); void GetMemberList(std::list& member_list, bool clear_list = true); void GetClientList(std::list& client_list, bool clear_list = true); #ifdef BOTS @@ -140,6 +141,9 @@ public: void ChangeLeader(Mob* newleader); const char *GetClientNameByIndex(uint8 index); void UpdateXTargetMarkedNPC(uint32 Number, Mob *m); + void SetDirtyAutoHaters(); + inline XTargetAutoHaters *GetXTargetAutoMgr() { return &m_autohatermgr; } + void JoinRaidXTarget(Raid *raid, bool first = false); void SetGroupMentor(int percent, char *name); void ClearGroupMentor(); @@ -168,6 +172,8 @@ private: std::string mentoree_name; Client *mentoree; int mentor_percent; + + XTargetAutoHaters m_autohatermgr; }; #endif diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 34519b69f..225ff39ff 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "raids.h" #include "../common/rulesys.h" +#include "../common/data_verification.h" #include "hate_list.h" #include "quest_parser_collection.h" @@ -277,7 +278,24 @@ int HateList::GetSummonedPetCountOnHateList(Mob *hater) { return pet_count; } -Mob *HateList::GetEntWithMostHateOnList(Mob *center) +int HateList::GetHateRatio(Mob *top, Mob *other) +{ + auto other_entry = Find(other); + + if (!other_entry || other_entry->stored_hate_amount < 1) + return 0; + + auto top_entry = Find(top); + + if (!top_entry || top_entry->stored_hate_amount < 1) + return 999; // shouldn't happen if you call it right :P + + return EQEmu::Clamp(static_cast((other_entry->stored_hate_amount * 100) / top_entry->stored_hate_amount), 1, 999); +} + +// skip is used to ignore a certain mob on the list +// Currently used for getting 2nd on list for aggro meter +Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip) { // hack fix for zone shutdown crashes on some servers if (!zone->IsLoaded()) @@ -310,6 +328,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center) continue; } + if (cur->entity_on_hatelist == skip) { + ++iterator; + continue; + } + auto hateEntryPosition = glm::vec3(cur->entity_on_hatelist->GetX(), cur->entity_on_hatelist->GetY(), cur->entity_on_hatelist->GetZ()); if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { if (!zone->watermap->InLiquid(hateEntryPosition)) { @@ -436,6 +459,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center) while (iterator != list.end()) { struct_HateList *cur = (*iterator); + if (cur->entity_on_hatelist == skip) { + ++iterator; + continue; + } + if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { if(!zone->watermap->InLiquid(glm::vec3(cur->entity_on_hatelist->GetPosition()))) { skipped_count++; diff --git a/zone/hate_list.h b/zone/hate_list.h index dda8cb21d..f0e2b7618 100644 --- a/zone/hate_list.h +++ b/zone/hate_list.h @@ -41,9 +41,9 @@ public: Mob *GetClosestEntOnHateList(Mob *hater); Mob *GetDamageTopOnHateList(Mob *hater); - Mob *GetEntWithMostHateOnList(Mob *center); + Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr); Mob *GetRandomEntOnHateList(); - Mob* GetEntWithMostHateOnList(); + Mob *GetEntWithMostHateOnList(); bool IsEntOnHateList(Mob *mob); bool IsHateListEmpty(); @@ -51,6 +51,7 @@ public: int AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOptions *opts); int GetSummonedPetCountOnHateList(Mob *hater); + int GetHateRatio(Mob *top, Mob *other); int32 GetEntHateAmount(Mob *ent, bool in_damage = false); @@ -73,4 +74,5 @@ private: Mob *hate_owner; }; -#endif \ No newline at end of file +#endif + diff --git a/zone/loottables.cpp b/zone/loottables.cpp index 7f8e371bd..3297dec60 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -80,10 +80,11 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite *copper = cash; } + uint32 global_loot_multiplier = RuleI(Zone, GlobalLootMultiplier); // Do items for (uint32 i=0; iNumEntries; i++) { - for (uint32 k = 1; k <= lts->Entries[i].multiplier; k++) { + for (uint32 k = 1; k <= (lts->Entries[i].multiplier * global_loot_multiplier); k++) { uint8 droplimit = lts->Entries[i].droplimit; uint8 mindrop = lts->Entries[i].mindrop; diff --git a/zone/merc.cpp b/zone/merc.cpp index be8f75e60..3e3dfae93 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1985,7 +1985,7 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon return result; } -bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) { +bool Merc::AICastSpell(int8 iChance, uint32 iSpellTypes) { if(!AI_HasSpells()) return false; @@ -2746,7 +2746,7 @@ int32 Merc::GetActSpellCasttime(uint16 spell_id, int32 casttime) return casttime; } -int8 Merc::GetChanceToCastBySpellType(int16 spellType) { +int8 Merc::GetChanceToCastBySpellType(uint32 spellType) { int mercStance = (int)GetStance(); int8 mercClass = GetClass(); int8 chance = 0; @@ -2888,7 +2888,7 @@ bool Merc::CheckStance(int16 stance) { return false; } -std::list Merc::GetMercSpellsBySpellType(Merc* caster, int spellType) { +std::list Merc::GetMercSpellsBySpellType(Merc* caster, uint32 spellType) { std::list result; if(caster && caster->AI_HasSpells()) { @@ -2918,7 +2918,7 @@ std::list Merc::GetMercSpellsBySpellType(Merc* caster, int spellType) return result; } -MercSpell Merc::GetFirstMercSpellBySpellType(Merc* caster, int spellType) { +MercSpell Merc::GetFirstMercSpellBySpellType(Merc* caster, uint32 spellType) { MercSpell result; result.spellid = 0; diff --git a/zone/merc.h b/zone/merc.h index 7bd4c2b9b..e3eb6c029 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -78,8 +78,8 @@ public: virtual void AI_Stop(); virtual void AI_Process(); - //virtual bool AICastSpell(Mob* tar, int8 iChance, int16 iSpellTypes); - virtual bool AICastSpell(int8 iChance, int32 iSpellTypes); + //virtual bool AICastSpell(Mob* tar, int8 iChance, uint32 iSpellTypes); + virtual bool AICastSpell(int8 iChance, uint32 iSpellTypes); virtual bool AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); virtual bool AI_EngagedCastCheck(); //virtual bool AI_PursueCastCheck(); @@ -97,7 +97,7 @@ public: // Merc Spell Casting Methods virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); virtual int32 GetActSpellCost(uint16 spell_id, int32 cost); - int8 GetChanceToCastBySpellType(int16 spellType); + int8 GetChanceToCastBySpellType(uint32 spellType); void SetSpellRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay); void SetDisciplineRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay); void SetSpellTimeCanCast(uint16 spellid, uint32 recast_delay); @@ -108,8 +108,8 @@ public: static int32 GetDisciplineRemainingTime(Merc *caster, uint16 timer_id); static std::list GetMercSpellsForSpellEffect(Merc* caster, int spellEffect); static std::list GetMercSpellsForSpellEffectAndTargetType(Merc* caster, int spellEffect, SpellTargetType targetType); - static std::list GetMercSpellsBySpellType(Merc* caster, int spellType); - static MercSpell GetFirstMercSpellBySpellType(Merc* caster, int spellType); + static std::list GetMercSpellsBySpellType(Merc* caster, uint32 spellType); + static MercSpell GetFirstMercSpellBySpellType(Merc* caster, uint32 spellType); static MercSpell GetFirstMercSpellForSingleTargetHeal(Merc* caster); static MercSpell GetMercSpellBySpellID(Merc* caster, uint16 spellid); static MercSpell GetBestMercSpellForVeryFastHeal(Merc* caster); diff --git a/zone/mob.h b/zone/mob.h index fe76f949d..42672f046 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -539,7 +539,9 @@ public: void DoubleAggro(Mob *other) { uint32 in_hate = GetHateAmount(other); SetHateAmountOnEnt(other, (in_hate ? in_hate * 2 : 1)); } uint32 GetHateAmount(Mob* tmob, bool is_dam = false) { return hate_list.GetEntHateAmount(tmob,is_dam);} uint32 GetDamageAmount(Mob* tmob) { return hate_list.GetEntHateAmount(tmob, true);} + int GetHateRatio(Mob *first, Mob *with) { return hate_list.GetHateRatio(first, with); } Mob* GetHateTop() { return hate_list.GetEntWithMostHateOnList(this);} + Mob* GetSecondaryHate(Mob *skip) { return hate_list.GetEntWithMostHateOnList(this, skip); } Mob* GetHateDamageTop(Mob* other) { return hate_list.GetDamageTopOnHateList(other);} Mob* GetHateRandom() { return hate_list.GetRandomEntOnHateList();} Mob* GetHateMost() { return hate_list.GetEntWithMostHateOnList();} diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index d2340581d..294247d59 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -47,7 +47,7 @@ extern Zone *zone; #endif //NOTE: do NOT pass in beneficial and detrimental spell types into the same call here! -bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { +bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { if (!tar) return false; @@ -344,7 +344,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain return CastSpell(AIspells[i].spellid, tar->GetID(), EQEmu::CastingSlot::Gem2, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust)); } -bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint16 iSpellTypes) { +bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { if((iSpellTypes&SpellTypes_Detrimental) != 0) { //according to live, you can buff and heal through walls... //now with PCs, this only applies if you can TARGET the target, but @@ -2496,7 +2496,7 @@ bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID) { } // adds a spell to the list, taking into account priority and resorting list as needed. -void NPC::AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint16 iType, +void NPC::AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint32 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust) { @@ -2608,8 +2608,13 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) { query = StringFormat("SELECT spellid, type, minlevel, maxlevel, " "manacost, recast_delay, priority, resist_adjust " +#ifdef BOTS + "FROM %s " + "WHERE npc_spells_id=%d ORDER BY minlevel", (iDBSpellsID >= 701 && iDBSpellsID <= 712 ? "bot_spells_entries" : "npc_spells_entries"), iDBSpellsID); +#else "FROM npc_spells_entries " - "WHERE npc_spells_id=%d ORDER BY minlevel", iDBSpellsID); + "WHERE npc_spells_id=%d ORDER BY minlevel", iDBSpellsID); +#endif results = QueryDatabase(query); if (!results.Success()) @@ -2646,7 +2651,7 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) { { int spell_id = atoi(row[0]); npc_spells_cache[iDBSpellsID]->entries[entryIndex].spellid = spell_id; - npc_spells_cache[iDBSpellsID]->entries[entryIndex].type = atoi(row[1]); + npc_spells_cache[iDBSpellsID]->entries[entryIndex].type = atoul(row[1]); npc_spells_cache[iDBSpellsID]->entries[entryIndex].minlevel = atoi(row[2]); npc_spells_cache[iDBSpellsID]->entries[entryIndex].maxlevel = atoi(row[3]); npc_spells_cache[iDBSpellsID]->entries[entryIndex].manacost = atoi(row[4]); diff --git a/zone/npc.h b/zone/npc.h index 543725c28..5dcc7eac9 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -54,7 +54,7 @@ typedef struct { } NPCProximity; struct AISpells_Struct { - uint16 type; // 0 = never, must be one (and only one) of the defined values + uint32 type; // 0 = never, must be one (and only one) of the defined values uint16 spellid; // <= 0 = no spell int16 manacost; // -1 = use spdat, -2 = no cast time uint32 time_cancast; // when we can cast this spell next @@ -377,7 +377,7 @@ public: void NPCSlotTexture(uint8 slot, uint16 texture); // Sets new material values for slots uint32 GetAdventureTemplate() const { return adventure_template_id; } - void AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint16 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust); + void AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint32 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust); void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max); void RemoveSpellFromNPCList(int16 spell_id); Timer *GetRefaceTimer() const { return reface_timer; } @@ -453,7 +453,7 @@ protected: uint32* pDontCastBefore_casting_spell; std::vector AIspells; bool HasAISpell; - virtual bool AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes); + virtual bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes); virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); AISpellsVar_Struct AISpellVar; int16 GetFocusEffect(focusType type, uint16 spell_id); diff --git a/zone/raids.cpp b/zone/raids.cpp index ff9e84578..46dd4424e 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -43,6 +43,8 @@ Raid::Raid(uint32 raidID) memset(leadername, 0, 64); locked = false; LootType = 4; + + m_autohatermgr.SetOwner(nullptr, nullptr, this); } Raid::Raid(Client* nLeader) @@ -60,6 +62,8 @@ Raid::Raid(Client* nLeader) strn0cpy(leadername, nLeader->GetName(), 64); locked = false; LootType = 4; + + m_autohatermgr.SetOwner(nullptr, nullptr, this); } Raid::~Raid() @@ -121,6 +125,26 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo c->SetRaidGrouped(true); SendRaidMOTD(c); + // xtarget shit .......... + if (group == RAID_GROUPLESS) { + if (rleader) { + GetXTargetAutoMgr()->merge(*c->GetXTargetAutoMgr()); + c->GetXTargetAutoMgr()->clear(); + c->SetXTargetAutoMgr(GetXTargetAutoMgr()); + } else { + if (!c->GetXTargetAutoMgr()->empty()) { + GetXTargetAutoMgr()->merge(*c->GetXTargetAutoMgr()); + c->GetXTargetAutoMgr()->clear(); + c->RemoveAutoXTargets(); + } + + c->SetXTargetAutoMgr(GetXTargetAutoMgr()); + + if (!c->GetXTargetAutoMgr()->empty()) + c->SetDirtyAutoHaters(); + } + } + auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; rga->rid = GetID(); @@ -143,8 +167,10 @@ void Raid::RemoveMember(const char *characterName) LearnMembers(); VerifyRaid(); - if(client) + if(client) { client->SetRaidGrouped(false); + client->LeaveRaidXTargets(this); + } auto pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; @@ -1672,3 +1698,11 @@ void Raid::CheckGroupMentor(uint32 group_id, Client *c) group_mentor[group_id].mentoree = c; } +void Raid::SetDirtyAutoHaters() +{ + for (int i = 0; i < MAX_RAID_MEMBERS; ++i) + if (members[i].member) + members[i].member->SetDirtyAutoHaters(); + +} + diff --git a/zone/raids.h b/zone/raids.h index ca5d0ff9b..9e681153c 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -20,6 +20,7 @@ #include "../common/types.h" #include "groups.h" +#include "xtargetautohaters.h" class Client; class EQApplicationPacket; @@ -230,6 +231,9 @@ public: inline int GetMentorPercent(uint32 group_id) { return group_mentor[group_id].mentor_percent; } inline Client *GetMentoree(uint32 group_id) { return group_mentor[group_id].mentoree; } + void SetDirtyAutoHaters(); + inline XTargetAutoHaters *GetXTargetAutoMgr() { return &m_autohatermgr; } + RaidMember members[MAX_RAID_MEMBERS]; char leadername[64]; protected: @@ -244,6 +248,8 @@ protected: GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS]; GroupMentor group_mentor[MAX_RAID_GROUPS]; + + XTargetAutoHaters m_autohatermgr; }; diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 6d39f58f5..3b1c6122f 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -895,6 +895,10 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) Inviter->CastToClient()->SendGroupLeaderChangePacket(Inviter->GetName()); Inviter->CastToClient()->SendGroupJoinAcknowledge(); } + + group->GetXTargetAutoMgr()->merge(*Inviter->CastToClient()->GetXTargetAutoMgr()); + Inviter->CastToClient()->GetXTargetAutoMgr()->clear(); + Inviter->CastToClient()->SetXTargetAutoMgr(group->GetXTargetAutoMgr()); } if(!group) @@ -1007,7 +1011,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) group->SetNPCMarker(NPCMarkerName); group->SetGroupAAs(&GLAA); group->SetGroupMentor(mentor_percent, mentoree_name); - + client->JoinGroupXTargets(group); } } else if (client->GetMerc()) @@ -1107,6 +1111,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) r->SendRaidRemoveAll(rga->playername); Client *rem = entity_list.GetClientByName(rga->playername); if(rem){ + rem->LeaveRaidXTargets(r); r->SendRaidDisband(rem); } r->LearnMembers(); diff --git a/zone/xtargetautohaters.cpp b/zone/xtargetautohaters.cpp new file mode 100644 index 000000000..ab010d9db --- /dev/null +++ b/zone/xtargetautohaters.cpp @@ -0,0 +1,112 @@ +#include "xtargetautohaters.h" +#include "mob.h" +#include "client.h" +#include "raids.h" +#include "groups.h" + +#include + +void XTargetAutoHaters::increment_count(Mob *in) +{ + assert(in != nullptr); + auto it = std::find_if(m_haters.begin(), m_haters.end(), + [&in](const HatersCount &c) { return c.spawn_id == in->GetID(); }); + + // we are on the list, we just need to increment the count + if (it != m_haters.end()) { + it->count++; + return; + } + // We are not on the list + HatersCount c; + c.spawn_id = in->GetID(); + c.count = 1; + + m_haters.push_back(c); + // trigger event on owner + if (m_client) + m_client->SetDirtyAutoHaters(); + else if (m_group) + m_group->SetDirtyAutoHaters(); + else if (m_raid) + m_raid->SetDirtyAutoHaters(); +} + +void XTargetAutoHaters::decrement_count(Mob *in) +{ + assert(in != nullptr); + auto it = std::find_if(m_haters.begin(), m_haters.end(), + [&in](const HatersCount &c) { return c.spawn_id == in->GetID(); }); + + // we are not on the list ... shouldn't happen + if (it == m_haters.end()) + return; + it->count--; + if (it->count == 0) { + m_haters.erase(it); + if (m_client) + m_client->SetDirtyAutoHaters(); + else if (m_group) + m_group->SetDirtyAutoHaters(); + else if (m_raid) + m_raid->SetDirtyAutoHaters(); + } +} + +void XTargetAutoHaters::merge(XTargetAutoHaters &other) +{ + bool trigger = false; + for (auto &e : other.m_haters) { + auto it = std::find_if(m_haters.begin(), m_haters.end(), + [&e](const HatersCount &c) { return e.spawn_id == c.spawn_id; }); + if (it != m_haters.end()) { + it->count += e.count; + continue; + } + m_haters.push_back(e); + trigger = true; + } + + if (trigger) { + if (m_client) + m_client->SetDirtyAutoHaters(); + else if (m_group) + m_group->SetDirtyAutoHaters(); + else if (m_raid) + m_raid->SetDirtyAutoHaters(); + } +} + +// demerge this from other. other belongs to group/raid you just left +void XTargetAutoHaters::demerge(XTargetAutoHaters &other) +{ + bool trigger = false; + for (auto &e : m_haters) { + auto it = std::find_if(other.m_haters.begin(), other.m_haters.end(), + [&e](const HatersCount &c) { return e.spawn_id == c.spawn_id; }); + if (it != other.m_haters.end()) { + it->count -= e.count; + if (it->count == 0) { + trigger = true; + other.m_haters.erase(it); + } + } + } + + if (trigger) { + if (other.m_client) + other.m_client->SetDirtyAutoHaters(); + else if (other.m_group) + other.m_group->SetDirtyAutoHaters(); + else if (other.m_raid) + other.m_raid->SetDirtyAutoHaters(); + } +} + +bool XTargetAutoHaters::contains_mob(int spawn_id) +{ + auto it = std::find_if(m_haters.begin(), m_haters.end(), + [spawn_id](const HatersCount &c) { return c.spawn_id == spawn_id; }); + return it != m_haters.end(); +} + diff --git a/zone/xtargetautohaters.h b/zone/xtargetautohaters.h new file mode 100644 index 000000000..fd8ee4f21 --- /dev/null +++ b/zone/xtargetautohaters.h @@ -0,0 +1,48 @@ +#ifndef XTARGETAUTOHATERS_H +#define XTARGETAUTOHATERS_H + +#include + +class Mob; +class Client; +class Group; +class Raid; + +class XTargetAutoHaters { +struct HatersCount { + int spawn_id; + int count; +}; +public: + XTargetAutoHaters() : m_client(nullptr), m_group(nullptr), m_raid(nullptr) {} + XTargetAutoHaters(Client *co, Group *go, Raid *ro) : m_client(co), m_group(go), m_raid(ro) {} + ~XTargetAutoHaters() {} + + void merge(XTargetAutoHaters &other); + void demerge(XTargetAutoHaters &other); + void increment_count(Mob *in); + void decrement_count(Mob *in); + + bool contains_mob(int spawn_id); + + inline const std::vector &get_list() { return m_haters; } + inline void SetOwner(Client *c, Group *g, Raid *r) {m_client = c; m_group = g; m_raid = r; } + inline void clear() { m_haters.clear(); } + inline bool empty() { return m_haters.empty(); } + +private: + /* This will contain all of the mobs that are possible to fill in an autohater + * slot. This keeps track of ALL MOBS for a client or group or raid + * This list needs to be merged when you join group/raid/etc + */ + std::vector m_haters; + + // So this is the object that owns us ... only 1 shouldn't be null + Client *m_client; + Group *m_group; + Raid *m_raid; +}; + + +#endif /* !XTARGETAUTOHATERS_H */ + diff --git a/zone/zonedb.h b/zone/zonedb.h index 1c96ee298..46ce825e4 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -45,7 +45,7 @@ struct wplist { #pragma pack(1) struct DBnpcspells_entries_Struct { int16 spellid; - uint16 type; + uint32 type; uint8 minlevel; uint8 maxlevel; int16 manacost;