From c83bc038f30e51a1f1ab98ade4aac95e5a9b3d64 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Thu, 9 Feb 2017 01:32:26 -0600 Subject: [PATCH 01/27] Fix issue with installer pulling down the wrong opcodes for SOD+ clients on Linux Loginserver --- utils/scripts/eqemu_server.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index e0960847e..b81520a3c 100644 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -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"}; From 38651258fcf9025e45c5aeda9a6cb2f75681d295 Mon Sep 17 00:00:00 2001 From: Uleat Date: Thu, 9 Feb 2017 17:57:55 -0500 Subject: [PATCH 03/27] Updated npc spell types to 32-bit mask --- common/spdat.h | 47 ++++++++++--------- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + ...7_02_09_npc_spells_entries_type_update.sql | 1 + zone/bot.cpp | 2 +- zone/bot.h | 10 ++-- zone/botspellsai.cpp | 8 ++-- zone/entity.h | 4 +- zone/merc.cpp | 8 ++-- zone/merc.h | 10 ++-- zone/mob_ai.cpp | 4 +- zone/npc.h | 4 +- 12 files changed, 54 insertions(+), 47 deletions(-) create mode 100644 utils/sql/git/required/2017_02_09_npc_spells_entries_type_update.sql diff --git a/common/spdat.h b/common/spdat.h index 3344489df..4168b283c 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -43,29 +43,34 @@ 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), + + 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_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..7afe02279 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ 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 9104 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9008 #else diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 2c27d858d..98dc22fc1 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -357,6 +357,7 @@ 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 # 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/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/zone/bot.cpp b/zone/bot.cpp index d5f6ff814..56d8fd5d1 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7495,7 +7495,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; diff --git a/zone/bot.h b/zone/bot.h index bda693094..dda1c5f0e 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -235,7 +235,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); @@ -350,7 +350,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 +416,8 @@ 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 BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType); static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster); static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster); static BotSpell GetBestBotSpellForPercentageHeal(Bot* botCaster); @@ -467,7 +467,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()); } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index a35760535..c36e928bb 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; @@ -1417,7 +1417,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 +1444,7 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellTyp return result; } -BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) { +BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) { BotSpell result; result.SpellId = 0; @@ -3324,7 +3324,7 @@ void Bot::CalcChanceToCast() { _spellCastingChances[botStance][SpellType_CureIndex] = castChance; } -uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) { +uint8 Bot::GetChanceToCastBySpellType(uint32 spellType) { int index = 0; int botStance = (int)GetBotStance(); uint8 chance = 0; 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/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_ai.cpp b/zone/mob_ai.cpp index d2340581d..5034925d9 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 diff --git a/zone/npc.h b/zone/npc.h index 543725c28..2db110d98 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 @@ -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); From ed717add299c0a4d07e4183fc6bcd35cf71c2491 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 11 Feb 2017 18:33:58 -0500 Subject: [PATCH 04/27] Added a few more SpellType enumerations (uncoded) --- common/spdat.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/spdat.h b/common/spdat.h index 4168b283c..ba275f933 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -64,9 +64,12 @@ enum SpellTypes : uint32 SpellType_Debuff = (1 << 14), SpellType_Cure = (1 << 15), SpellType_Resurrect = (1 << 16), + SpellType_HateRedux = (1 << 17), + SpellType_InCombatBuffSong = (1 << 18), + SpellType_OutOfCombatBuffSong = (1 << 19), 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), + SpellTypes_Beneficial = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong), SpellType_Any = 0xFFFFFFFF }; From 955514c20ffdf75878995febe291164bd474945f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 12 Feb 2017 23:16:38 -0600 Subject: [PATCH 05/27] eqemu_server.pl Linux make routine compile with the amount of cores available [skip ci] --- utils/scripts/eqemu_server.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index b81520a3c..0fcfa9161 100644 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -334,8 +334,9 @@ sub build_linux_source { } print "Building EQEmu Server code. This will take a while."; - #::: Build - print `make`; + #::: Build + $processor_cores = `cat /proc/cpuinfo | grep -c ^processor /proc/cpuinfo`; + print `make -j$processor_cores`; chdir ($current_directory); From 0f32f780a9d9dab0ec54808be224b1e795db077b Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 12 Feb 2017 23:31:25 -0600 Subject: [PATCH 06/27] Revert previous change to keep installation memory safe and independent [skip ci] --- utils/scripts/eqemu_server.pl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 0fcfa9161..e9ba02099 100644 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -335,8 +335,7 @@ sub build_linux_source { print "Building EQEmu Server code. This will take a while."; #::: Build - $processor_cores = `cat /proc/cpuinfo | grep -c ^processor /proc/cpuinfo`; - print `make -j$processor_cores`; + print `make`; chdir ($current_directory); From 92d4468326026549c487a6cc84a4601773307ee1 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 13 Feb 2017 01:26:19 -0600 Subject: [PATCH 07/27] Put a category enabled filter on default switch case so we're not chewing up extra cpu cycles --- loginserver/client.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/loginserver/client.cpp b/loginserver/client.cpp index 9a6a81593..6f64b5899 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -99,9 +99,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); + } } } From fe215646592331b1a95b20b0d8e2dff5397609f3 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 13 Feb 2017 01:38:23 -0600 Subject: [PATCH 08/27] Apply KLS' tweaks to Log.Out (CPU saves) https://github.com/EQEmu/Server/commit/1d055b5364a4183a327683dfa13cf33954874616 --- common/eqemu_logsys.cpp | 57 ++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index 4c0ee1c9f..78a26c5dc 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -123,26 +123,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); @@ -160,14 +155,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); @@ -246,13 +233,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; @@ -273,12 +253,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); From ef165224731eeac848e6a5181d017b31ed228734 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 13 Feb 2017 02:16:40 -0600 Subject: [PATCH 09/27] Implement Rule Zone:GlobalLootMultiplier (Default 1) - Sets Global Loot drop multiplier for database based drops, useful for double, triple loot etc. --- common/ruletypes.h | 1 + zone/loottables.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 5323fda49..f972034f0 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -258,6 +258,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/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; From 5b8ad902ce367cf70d9ebc68eb9cf6c9ce46e718 Mon Sep 17 00:00:00 2001 From: JJ Date: Mon, 13 Feb 2017 16:00:39 -0500 Subject: [PATCH 10/27] Add character_tasks to the list when deleting a character. --- common/database.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/common/database.cpp b/common/database.cpp index 423bbac58..b06540cbd 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); From d043c38f71eef1436a8fc11b85e09b5a1480f7f4 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 14 Feb 2017 16:39:31 -0500 Subject: [PATCH 11/27] Make it so enraged NPCs can't be riposted This should prevent infinite loops --- zone/attack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 801242d45..826ae713b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -358,7 +358,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()) { From d62a449f9cf44505a149a6dff2c096ba816e789a Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 15 Feb 2017 07:03:43 -0500 Subject: [PATCH 12/27] Missed an npc spells type (or two..) --- zone/mob_ai.cpp | 4 ++-- zone/npc.h | 2 +- zone/zonedb.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 5034925d9..896709fc8 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -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) { @@ -2646,7 +2646,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 2db110d98..5dcc7eac9 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -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; } 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; From e3c8b7525924b82310851b0b6e441c52a0bc0420 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 15 Feb 2017 07:42:17 -0500 Subject: [PATCH 13/27] Moved bot spell entries out of npc spell entries (easier to manage bot changes by committing to non-bot manifest) --- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../2017_02_15_bot_spells_entries.sql | 31 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 utils/sql/git/required/2017_02_15_bot_spells_entries.sql diff --git a/common/version.h b/common/version.h index 7afe02279..88f986807 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9104 +#define CURRENT_BINARY_DATABASE_VERSION 9105 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9008 #else diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 98dc22fc1..c21f60f4b 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -358,6 +358,7 @@ 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/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 From 8177f7d9bb0a55738900d0be9854deea6945668d Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 15 Feb 2017 19:04:36 -0500 Subject: [PATCH 14/27] Activation of `bot_spells_entries` table --- common/version.h | 2 +- .../sql/git/bots/bots_db_update_manifest.txt | 1 + .../2017_02_15_bots_bot_spells_entries.sql | 29 +++++++++++++++++++ zone/mob_ai.cpp | 7 ++++- 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 utils/sql/git/bots/required/2017_02_15_bots_bot_spells_entries.sql diff --git a/common/version.h b/common/version.h index 88f986807..bda89dd8c 100644 --- a/common/version.h +++ b/common/version.h @@ -32,7 +32,7 @@ #define CURRENT_BINARY_DATABASE_VERSION 9105 #ifdef BOTS - #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9008 + #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9009 #else #define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0 #endif diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 3122545a0..d63c142c8 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -7,6 +7,7 @@ 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| # 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/zone/mob_ai.cpp b/zone/mob_ai.cpp index 896709fc8..294247d59 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -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()) From 9f4604ec3ebf8df4c1c1059a6a5865c576d77324 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 17 Feb 2017 21:04:48 -0500 Subject: [PATCH 15/27] Rework how XTarget auto haters work This should cause the auto haters to be shared with other toons who might be interested (group/raid) like live. There maybe some bugs since there is a lot of complex interactions here. --- zone/CMakeLists.txt | 2 + zone/attack.cpp | 6 +- zone/client.cpp | 194 +++++++++++++++++++++++++++---------- zone/client.h | 11 +++ zone/client_packet.cpp | 24 +++-- zone/client_process.cpp | 6 ++ zone/entity.cpp | 10 +- zone/groups.cpp | 38 +++++++- zone/groups.h | 8 +- zone/raids.cpp | 36 ++++++- zone/raids.h | 6 ++ zone/worldserver.cpp | 5 + zone/xtargetautohaters.cpp | 112 +++++++++++++++++++++ zone/xtargetautohaters.h | 48 +++++++++ 14 files changed, 435 insertions(+), 71 deletions(-) create mode 100644 zone/xtargetautohaters.cpp create mode 100644 zone/xtargetautohaters.h diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 532cc86cd..7bc96c3db 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -122,6 +122,7 @@ SET(zone_sources water_map_v2.cpp waypoints.cpp worldserver.cpp + xtargetautohaters.cpp zone.cpp zone_config.cpp zonedb.cpp @@ -215,6 +216,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/attack.cpp b/zone/attack.cpp index 826ae713b..b7cd49384 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2432,9 +2432,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 @@ -2510,7 +2510,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 @@ -2549,7 +2549,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/client.cpp b/zone/client.cpp index 1a5c02f1e..a3239eeb1 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -310,6 +310,8 @@ Client::Client(EQStreamInterface* ieqs) } MaxXTargets = 5; XTargetAutoAddHaters = true; + m_autohatermgr.SetOwner(this, nullptr, nullptr); + m_activeautohatermgr = &m_autohatermgr; LoadAccountFlags(); initial_respawn_selection = 0; @@ -4159,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); @@ -4234,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) @@ -7171,12 +7187,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) @@ -7195,60 +7211,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) @@ -7405,6 +7376,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 (!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) diff --git a/zone/client.h b/zone/client.h index 69fdddffa..ae6c2ef30 100644 --- a/zone/client.h +++ b/zone/client.h @@ -48,6 +48,7 @@ namespace EQEmu #include "../common/inventory_profile.h" #include "../common/guilds.h" //#include "../common/item_data.h" +#include "xtargetautohaters.h" #include "common.h" #include "merc.h" @@ -1122,6 +1123,13 @@ 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; } @@ -1546,8 +1554,11 @@ private: uint8 MaxXTargets; bool XTargetAutoAddHaters; + bool m_dirtyautohaters; struct XTarget_Struct XTargets[XTARGET_HARDCAP]; + XTargetAutoHaters m_autohatermgr; + XTargetAutoHaters *m_activeautohatermgr; Timer ItemTickTimer; Timer ItemQuestTimer; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f2e78adf7..721421b9e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -575,6 +575,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 +1553,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; } @@ -10806,7 +10811,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } } } - g->DisbandGroup(); + g->JoinRaidXTarget(r); + g->DisbandGroup(true); r->GroupUpdate(freeGroup); } else{ @@ -10871,7 +10877,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } } } - ig->DisbandGroup(); + ig->JoinRaidXTarget(r, true); + ig->DisbandGroup(true); r->GroupUpdate(groupFree); groupFree = r->GetFreeGroup(); } @@ -10924,10 +10931,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); @@ -10981,14 +10989,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(); @@ -14087,6 +14096,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_process.cpp b/zone/client_process.cpp index bb2db4777..b97f7b22e 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -681,6 +681,12 @@ bool Client::Process() { Message(0, "Your enemies have forgotten you!"); } + if (client_state == CLIENT_CONNECTED) { + if (m_dirtyautohaters) + ProcessXTargetAutoHaters(); + // aggro meter stuff should live here + } + 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/groups.cpp b/zone/groups.cpp index 2162e735e..24d31e74c 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() @@ -346,6 +349,9 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte database.SetGroupID(NewMemberName, GetID(), CharacterID, ismerc); } + if (newmember && newmember->IsClient()) + newmember->CastToClient()->JoinGroupXTargets(this); + safe_delete(outapp); return true; @@ -718,8 +724,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 +880,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 +907,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 +2302,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/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 84d4cb399..3251250ce 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -897,6 +897,9 @@ void WorldServer::Process() { 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) @@ -1011,6 +1014,7 @@ void WorldServer::Process() { group->SetGroupMentor(mentor_percent, mentoree_name); } + client->JoinGroupXTargets(group); } else if (client->GetMerc()) { @@ -1109,6 +1113,7 @@ void WorldServer::Process() { 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 */ + From 08c2f73e379066d4a2486c223282ba6daf72e6cb Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 18 Feb 2017 22:27:34 -0500 Subject: [PATCH 16/27] Implement aggro meter for RoF2 (RoF wasn't tested) I didn't test RoF, so it's disabled for now (change AggroMeterAvaliable if you want to test) Group member meters probably buggy ... but do later The "lock target" feature isn't working currently either --- common/emu_oplist.h | 3 + common/ruletypes.h | 1 + utils/patches/patch_RoF.conf | 3 + utils/patches/patch_RoF2.conf | 3 + zone/CMakeLists.txt | 2 + zone/aggromanager.cpp | 11 +++ zone/aggromanager.h | 80 +++++++++++++++++ zone/client.cpp | 165 ++++++++++++++++++++++++++++++++++ zone/client.h | 9 ++ zone/client_packet.cpp | 12 +++ zone/client_packet.h | 1 + zone/client_process.cpp | 3 +- zone/hate_list.cpp | 30 ++++++- zone/hate_list.h | 8 +- zone/mob.h | 2 + 15 files changed, 328 insertions(+), 5 deletions(-) create mode 100644 zone/aggromanager.cpp create mode 100644 zone/aggromanager.h 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/ruletypes.h b/common/ruletypes.h index f972034f0..a0bf01496 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, true) // 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. 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/zone/CMakeLists.txt b/zone/CMakeLists.txt index 7bc96c3db..deadd079e 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 @@ -132,6 +133,7 @@ SET(zone_sources SET(zone_headers aa.h aa_ability.h + aggromanager.h basic_functions.h beacon.h bot.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/client.cpp b/zone/client.cpp index a3239eeb1..9e02195c0 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(1000), 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), @@ -8771,3 +8772,167 @@ void Client::CheckRegionTypeChanges() else if (GetPVP()) SetPVP(false, false); } + +void Client::ProcessAggroMeter() +{ + if (!AggroMeterAvailable()) + 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 ae6c2ef30..6dc4d6e38 100644 --- a/zone/client.h +++ b/zone/client.h @@ -49,6 +49,7 @@ namespace EQEmu #include "../common/guilds.h" //#include "../common/item_data.h" #include "xtargetautohaters.h" +#include "aggromanager.h" #include "common.h" #include "merc.h" @@ -1133,6 +1134,11 @@ public: 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); @@ -1470,6 +1476,7 @@ private: Timer afk_toggle_timer; Timer helm_toggle_timer; Timer light_update_timer; + Timer aggro_meter_timer; glm::vec3 m_Proximity; @@ -1560,6 +1567,8 @@ private: XTargetAutoHaters m_autohatermgr; XTargetAutoHaters *m_activeautohatermgr; + AggroMeter m_aggrometer; + Timer ItemTickTimer; Timer ItemQuestTimer; std::map accountflags; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 721421b9e..535e5e98e 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; @@ -2400,6 +2401,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); 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 b97f7b22e..2bfa8a055 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -684,7 +684,8 @@ bool Client::Process() { if (client_state == CLIENT_CONNECTED) { if (m_dirtyautohaters) ProcessXTargetAutoHaters(); - // aggro meter stuff should live here + if (aggro_meter_timer.Check()) + ProcessAggroMeter(); } return ret; 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/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();} From f0f5c41c305668862dca717c04fb2392bec79d35 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 19 Feb 2017 21:12:18 -0600 Subject: [PATCH 17/27] Fixed an issue where clients would sell x1000 stacks of items where the price overflows data sizes, the code will now make sure not to sell too many items that go over this data size --- zone/client_packet.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 535e5e98e..67e085d0d 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12440,14 +12440,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 = 0; 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 = 0; 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()) From 46b19e8e6f41045b6411c274f59078a502565cc1 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 20 Feb 2017 18:41:17 -0500 Subject: [PATCH 18/27] Disable the aggro meter timer if it's not enabled --- zone/client.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index 9e02195c0..5f3d3ac81 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8775,8 +8775,10 @@ void Client::CheckRegionTypeChanges() void Client::ProcessAggroMeter() { - if (!AggroMeterAvailable()) + 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 From d9633dfee47dfa4970d1b76894a0268432cd9749 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 20 Feb 2017 21:41:15 -0500 Subject: [PATCH 19/27] Bard bot song twisting update --- changelog.txt | 3 + common/spdat.h | 4 +- common/version.h | 2 +- .../sql/git/bots/bots_db_update_manifest.txt | 1 + .../2017_02_20_bots_bard_spell_update.sql | 153 + zone/bot.cpp | 24 +- zone/bot.h | 15 +- zone/bot_command.cpp | 1 - zone/bot_structs.h | 4 + zone/botspellsai.cpp | 4458 ++++++++++++----- zone/groups.cpp | 25 +- 11 files changed, 3482 insertions(+), 1208 deletions(-) create mode 100644 utils/sql/git/bots/required/2017_02_20_bots_bard_spell_update.sql diff --git a/changelog.txt b/changelog.txt index 390c45cd9..23700766c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/20/2017 == +Uleat: Reworked bard bot spell twisting and updated their spell (song) list + == 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/spdat.h b/common/spdat.h index ba275f933..d4ccd9456 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -65,8 +65,8 @@ enum SpellTypes : uint32 SpellType_Cure = (1 << 15), SpellType_Resurrect = (1 << 16), SpellType_HateRedux = (1 << 17), - SpellType_InCombatBuffSong = (1 << 18), - SpellType_OutOfCombatBuffSong = (1 << 19), + 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), diff --git a/common/version.h b/common/version.h index bda89dd8c..40e50a0ac 100644 --- a/common/version.h +++ b/common/version.h @@ -32,7 +32,7 @@ #define CURRENT_BINARY_DATABASE_VERSION 9105 #ifdef BOTS - #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9009 + #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9010 #else #define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0 #endif diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index d63c142c8..44e68f037 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -8,6 +8,7 @@ 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 (`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_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/zone/bot.cpp b/zone/bot.cpp index 56d8fd5d1..240530237 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()); @@ -7639,6 +7637,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 +8033,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 +8047,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 dda1c5f0e..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 { @@ -296,7 +300,6 @@ public: bool GetNeedsHateRedux(Mob *tar); bool HasOrMayGetAggro(); void SetDefaultBotStance(); - void CalcChanceToCast(); inline virtual int32 GetMaxStat(); inline virtual int32 GetMaxResist(); @@ -417,6 +420,7 @@ public: static std::list GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect); static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType); 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); @@ -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 c36e928bb..74870fed3 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -96,14 +96,8 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 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, uint32 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, uint32 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, uint32 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); } } } @@ -748,35 +738,69 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 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 +816,28 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 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 +860,8 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 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 +929,100 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 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 +1079,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 +1179,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 +1207,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 +1400,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 +1417,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 +1495,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; } @@ -1444,6 +1584,37 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellTyp return result; } +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; @@ -1738,9 +1909,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 +2310,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 +2327,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 +2344,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 +2381,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 +2483,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(uint32 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/groups.cpp b/zone/groups.cpp index 2162e735e..37f1f9da1 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -333,13 +333,6 @@ 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 { @@ -496,16 +489,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 +649,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) From f851b1a3b4d92af68d247a6071d30da3a30ac832 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 20 Feb 2017 21:47:47 -0500 Subject: [PATCH 20/27] Added combat-prep song buffing note [skip ci] --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index 23700766c..b2e74a626 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ 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 == 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.) From 8f67df1f4feaf0bdf329ab11d242dde9054ac2c5 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 20 Feb 2017 22:50:43 -0500 Subject: [PATCH 21/27] Oops! (Won't affect any changes up to this point) [skip ci] --- utils/sql/git/bots/bots_db_update_manifest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 44e68f037..8418dd83b 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -8,7 +8,7 @@ 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 (`type` & 0xFFFF0000) = 0xFFFF0000|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 From a8846395349a31d5ee29c7fc98daf2f3c86081fa Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 21 Feb 2017 03:55:21 -0600 Subject: [PATCH 22/27] Reduce the initial telnet console prompt timer --- changelog.txt | 3 +++ world/console.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 390c45cd9..84b7dc67b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 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/world/console.cpp b/world/console.cpp index 5f1598654..b8a0b419a 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -68,7 +68,7 @@ void CatchSignal(int sig_num); Console::Console(EmuTCPConnection* itcpc) : WorldTCPConnection(), timeout_timer(RuleI(Console, SessionTimeOut)), - prompt_timer(1000) + prompt_timer(1) { tcpc = itcpc; tcpc->SetEcho(true); From 056725b9bd60e400aeb168a2ec8d71dcfc48ec1b Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 21 Feb 2017 13:38:00 -0500 Subject: [PATCH 23/27] Make timer for aggro meter not a magic number you can edit AGGRO_METER_UPDATE_MS in common/features.h if you want to see if a different number would work better --- common/features.h | 3 +++ zone/client.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/common/features.h b/common/features.h index 27390e745..4140014d8 100644 --- a/common/features.h +++ b/common/features.h @@ -235,6 +235,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/zone/client.cpp b/zone/client.cpp index 5f3d3ac81..842563d88 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -154,7 +154,7 @@ Client::Client(EQStreamInterface* ieqs) afk_toggle_timer(250), helm_toggle_timer(250), light_update_timer(600), - aggro_meter_timer(1000), + 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), From 7db82a3b142dd29cde1598230e207bc3f7bcd3ab Mon Sep 17 00:00:00 2001 From: Drajor Date: Wed, 22 Feb 2017 06:26:49 +1000 Subject: [PATCH 24/27] Fixes issue with calculating the value of items being sold. The loop was starting at 0 instead of 1. --- zone/client_packet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 67e085d0d..1c5e4ebfd 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12447,7 +12447,7 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) uint32 i; if (RuleB(Merchant, UsePriceMod)) { - for (i = 0; i < cost_quantity; i++) { + 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; @@ -12457,7 +12457,7 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) } } else { - for (i = 0; i < cost_quantity; i++) { + 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; From 9a157fa02870f671d02a4e3e392a95e4b0a04a4f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 21 Feb 2017 17:54:25 -0500 Subject: [PATCH 25/27] Turn aggro meter off by default until more tested Reports of it being too spammy and causing DCs --- common/ruletypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index a0bf01496..20430735a 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -114,7 +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, true) // Enable Aggro Meter, for users with RoF 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. From b423ad0d80189fd168861c529de0c2c05f1f0413 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 21 Feb 2017 18:02:58 -0500 Subject: [PATCH 26/27] Crash fix --- zone/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index 842563d88..40a919d2b 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7430,7 +7430,7 @@ void Client::ProcessXTargetAutoHaters() auto &haters = GetXTargetAutoMgr()->get_list(); for (auto &e : haters) { auto *mob = entity_list.GetMob(e.spawn_id); - if (!IsXTarget(mob)) { + if (mob && !IsXTarget(mob)) { auto slot = empty_slots.front(); empty_slots.pop(); XTargets[slot].dirty = true; From 9adfe5b9ff3f50afe27b294e000067adfbb1ba7e Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 21 Feb 2017 20:40:04 -0500 Subject: [PATCH 27/27] Fix for bot wizard repeating familiar casting when pet is dead and buff is active --- zone/bot.cpp | 19 ++++++++++++++++--- zone/botspellsai.cpp | 31 ++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 240530237..5da128aa6 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1634,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; @@ -1649,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; @@ -1693,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(); diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 74870fed3..f3b4ec83a 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -557,10 +557,35 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 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;