mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
3377 lines
95 KiB
C++
3377 lines
95 KiB
C++
#ifdef BOTS
|
|
|
|
#include "bot.h"
|
|
#include "../common/StringUtil.h"
|
|
|
|
bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
|
|
|
|
if (!tar) {
|
|
return false;
|
|
}
|
|
|
|
if(!AI_HasSpells())
|
|
return false;
|
|
|
|
if (iChance < 100) {
|
|
if (MakeRandomInt(0, 100) > iChance){
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(tar->GetAppearance() == eaDead) {
|
|
if((tar->IsClient() && tar->CastToClient()->GetFeigned()) || tar->IsBot()) {
|
|
// do nothing
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
uint8 botClass = GetClass();
|
|
uint8 botLevel = GetLevel();
|
|
|
|
bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once.
|
|
|
|
bool castedSpell = false;
|
|
|
|
BotSpell botSpell;
|
|
botSpell.SpellId = 0;
|
|
botSpell.SpellIndex = 0;
|
|
botSpell.ManaCost = 0;
|
|
|
|
switch (iSpellTypes) {
|
|
case SpellType_Mez: {
|
|
if (tar->GetBodyType() != BT_Giant) {
|
|
if(!checked_los) {
|
|
if(!CheckLosFN(tar))
|
|
break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
|
|
|
|
checked_los = true;
|
|
}
|
|
|
|
//TODO
|
|
//Check if single target or AoE mez is best
|
|
//if (TARGETS ON MT IS => 3 THEN botSpell = AoEMez)
|
|
//if (TARGETS ON MT IS <= 2 THEN botSpell = BestMez)
|
|
|
|
botSpell = GetBestBotSpellForMez(this);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
Mob* addMob = GetFirstIncomingMobToMez(this, botSpell);
|
|
|
|
if(!addMob){
|
|
//Say("!addMob.");
|
|
break;}
|
|
|
|
if(!(!addMob->IsImmuneToSpell(botSpell.SpellId, this) && addMob->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
|
|
break;
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost);
|
|
|
|
if(castedSpell) {
|
|
char* gmsg = 0;
|
|
|
|
MakeAnyLenString(&gmsg, "Attempting to mez %s.", addMob->GetCleanName());
|
|
|
|
if(gmsg && GetGroupMessagesOn())
|
|
BotGroupSay(this, gmsg);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Heal: {
|
|
if (tar->DontHealMeBefore() < Timer::GetCurrentTime()) {
|
|
uint8 hpr = (uint8)tar->GetHPRatio();
|
|
bool hasAggro = false;
|
|
bool isPrimaryHealer = false;
|
|
|
|
if(HasGroup()) {
|
|
isPrimaryHealer = IsGroupPrimaryHealer();
|
|
}
|
|
|
|
if(hpr < 95 || (tar->IsClient() && (hpr < 95)) || (botClass == BARD)) {
|
|
if(tar->GetClass() == NECROMANCER) {
|
|
// Give necromancers a chance to go lifetap something or cleric can spend too much mana on a necro
|
|
if(hpr >= 40) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(tar->GetClass() == SHAMAN) {
|
|
// Give shaman the chance to canni without wasting the cleric's mana
|
|
if(hpr >= 80) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Evaluate the situation
|
|
if((IsEngaged()) && ((botClass == CLERIC) || (botClass == DRUID) || (botClass == SHAMAN) || (botClass == PALADIN))) {
|
|
if(tar->GetTarget() && tar->GetTarget()->GetHateTop() && tar->GetTarget()->GetHateTop() == tar) {
|
|
hasAggro = true;
|
|
}
|
|
|
|
if(hpr < 35) {
|
|
botSpell = GetBestBotSpellForFastHeal(this);
|
|
}
|
|
else if(hpr >= 35 && hpr < 70){
|
|
if(GetNumberNeedingHealedInGroup(60, false) >= 3)
|
|
botSpell = GetBestBotSpellForGroupHeal(this);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetBestBotSpellForPercentageHeal(this);
|
|
}
|
|
else if(hpr >= 70 && hpr < 95){
|
|
if(GetNumberNeedingHealedInGroup(80, false) >= 3)
|
|
botSpell = GetBestBotSpellForGroupHealOverTime(this);
|
|
|
|
if(hasAggro)
|
|
botSpell = GetBestBotSpellForPercentageHeal(this);
|
|
}
|
|
else {
|
|
if(!tar->FindType(SE_HealOverTime))
|
|
botSpell = GetBestBotSpellForHealOverTime(this);
|
|
}
|
|
}
|
|
else if ((botClass == CLERIC) || (botClass == DRUID) || (botClass == SHAMAN) || (botClass == PALADIN)) {
|
|
if(GetNumberNeedingHealedInGroup(40, true) >= 2){
|
|
botSpell = GetBestBotSpellForGroupCompleteHeal(this);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetBestBotSpellForGroupHeal(this);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetBestBotSpellForGroupHealOverTime(this);
|
|
|
|
if(hpr < 40) {
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetBestBotSpellForPercentageHeal(this);
|
|
}
|
|
}
|
|
else if(GetNumberNeedingHealedInGroup(60, true) >= 2){
|
|
botSpell = GetBestBotSpellForGroupHeal(this);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetBestBotSpellForGroupHealOverTime(this);
|
|
|
|
if(hpr < 40) {
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetBestBotSpellForPercentageHeal(this);
|
|
}
|
|
}
|
|
else if(hpr < 40)
|
|
botSpell = GetBestBotSpellForPercentageHeal(this);
|
|
else if(hpr >= 40 && hpr < 75)
|
|
botSpell = GetBestBotSpellForRegularSingleTargetHeal(this);
|
|
else {
|
|
if(hpr < 90 && !tar->FindType(SE_HealOverTime))
|
|
botSpell = GetBestBotSpellForHealOverTime(this);
|
|
}
|
|
}
|
|
else {
|
|
float hpRatioToCast = 0.0f;
|
|
|
|
switch(this->GetBotStance())
|
|
{
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
hpRatioToCast = isPrimaryHealer?90.0f:50.0f;
|
|
break;
|
|
case BotStanceBalanced:
|
|
hpRatioToCast = isPrimaryHealer?95.0f:75.0f;
|
|
break;
|
|
case BotStanceReactive:
|
|
hpRatioToCast = isPrimaryHealer?100.0f:90.0f;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
hpRatioToCast = isPrimaryHealer?75.0f:25.0f;
|
|
break;
|
|
default:
|
|
hpRatioToCast = isPrimaryHealer?100.0f:0.0f;
|
|
break;
|
|
}
|
|
|
|
//If we're at specified mana % or below, don't heal as hybrid
|
|
if(tar->GetHPRatio() <= hpRatioToCast)
|
|
botSpell = GetBestBotSpellForRegularSingleTargetHeal(this);
|
|
}
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetBestBotSpellForRegularSingleTargetHeal(this);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetFirstBotSpellForSingleTargetHeal(this);
|
|
|
|
if(botSpell.SpellId == 0 && botClass == BARD){
|
|
botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal);
|
|
}
|
|
|
|
// If there is still no spell id, then there isn't going to be one so we are done
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
// Can we cast this spell on this target?
|
|
if(!(spells[botSpell.SpellId].targettype==ST_GroupTeleport || spells[botSpell.SpellId].targettype == ST_Target || tar == this)
|
|
&& !(tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
|
|
break;
|
|
|
|
uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore();
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
|
|
|
|
if(castedSpell) {
|
|
char* gmsg = 0;
|
|
/*if(TempDontHealMeBeforeTime != tar->DontHealMeBefore())
|
|
tar->SetDontHealMeBefore(TempDontHealMeBeforeTime);
|
|
|
|
// For non-HoT heals, do a 4 second delay
|
|
// TODO: Replace this code with logic that calculates the delay based on number of clerics in rotation
|
|
// and ignores heals for anyone except the main tank
|
|
if(!IsHealOverTimeSpell(botSpell.SpellId)) {
|
|
if(IsCompleteHealSpell(botSpell.SpellId)) {
|
|
// Complete Heal 4 second rotation
|
|
tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 4000);
|
|
}
|
|
else {
|
|
tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 1000);
|
|
}
|
|
}*/
|
|
if(botClass != BARD) {
|
|
if(IsGroupSpell(botSpell.SpellId)){
|
|
if(this->HasGroup()) {
|
|
Group *g = this->GetGroup();
|
|
|
|
if(g) {
|
|
MakeAnyLenString(&gmsg, "Casting %s.", spells[botSpell.SpellId].name);
|
|
|
|
for( int i = 0; i<MAX_GROUP_MEMBERS; i++) {
|
|
if(g->members[i] && !g->members[i]->qglobal) {
|
|
g->members[i]->SetDontHealMeBefore(Timer::GetCurrentTime() + 1000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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());
|
|
|
|
tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 2000);
|
|
}
|
|
}
|
|
|
|
if(gmsg && GetGroupMessagesOn())
|
|
BotGroupSay(this, gmsg);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Root: {
|
|
if (!tar->IsRooted() && tar->DontRootMeBefore() < Timer::GetCurrentTime()) {
|
|
if(!checked_los) {
|
|
if(!CheckLosFN(tar))
|
|
break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
|
|
|
|
checked_los = true;
|
|
}
|
|
|
|
// TODO: If there is a ranger in the group then don't allow root spells
|
|
|
|
botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
if(tar->CanBuffStack(botSpell.SpellId, botLevel, true) == 0)
|
|
break;
|
|
|
|
uint32 TempDontRootMeBefore = tar->DontRootMeBefore();
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontRootMeBefore);
|
|
|
|
if(TempDontRootMeBefore != tar->DontRootMeBefore())
|
|
tar->SetDontRootMeBefore(TempDontRootMeBefore);
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Buff: {
|
|
if (tar->DontBuffMeBefore() < Timer::GetCurrentTime()) {
|
|
std::list<BotSpell> buffSpellList = GetBotSpellsBySpellType(this, SpellType_Buff);
|
|
|
|
for(std::list<BotSpell>::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); itr++) {
|
|
BotSpell selectedBotSpell = *itr;
|
|
|
|
if(selectedBotSpell.SpellId == 0)
|
|
continue;
|
|
|
|
// no buffs with illusions.. use #bot command to cast illusions
|
|
if(IsEffectInSpell(selectedBotSpell.SpellId, SE_Illusion) && tar != this)
|
|
continue;
|
|
|
|
//no teleport spells use #bot command to cast teleports
|
|
if(IsEffectInSpell(selectedBotSpell.SpellId, SE_Teleport) || IsEffectInSpell(selectedBotSpell.SpellId, SE_Succor))
|
|
continue;
|
|
|
|
// can not cast buffs for your own pet only on another pet that isn't yours
|
|
if((spells[selectedBotSpell.SpellId].targettype == ST_Pet) && (tar != this->GetPet()))
|
|
continue;
|
|
|
|
// Validate target
|
|
|
|
if(!((spells[selectedBotSpell.SpellId].targettype == ST_Target || spells[selectedBotSpell.SpellId].targettype == ST_Pet || tar == this ||
|
|
spells[selectedBotSpell.SpellId].targettype == ST_Group || spells[selectedBotSpell.SpellId].targettype == ST_GroupTeleport ||
|
|
(botClass == BARD && spells[selectedBotSpell.SpellId].targettype == ST_AEBard))
|
|
&& !tar->IsImmuneToSpell(selectedBotSpell.SpellId, this)
|
|
&& (tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0))) {
|
|
continue;
|
|
}
|
|
|
|
// Put the zone levitate and movement check here since bots are able to bypass the client casting check
|
|
if((IsEffectInSpell(selectedBotSpell.SpellId, SE_Levitate) && !zone->CanLevitate())
|
|
|| (IsEffectInSpell(selectedBotSpell.SpellId, SE_MovementSpeed) && !zone->CanCastOutdoor())) {
|
|
if(botClass != BARD || !IsSpellUsableThisZoneType(selectedBotSpell.SpellId, zone->GetZoneType())){
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch(tar->GetArchetype())
|
|
{
|
|
case ARCHETYPE_CASTER:
|
|
//TODO: probably more caster specific spell effects in here
|
|
if(IsEffectInSpell(selectedBotSpell.SpellId, SE_AttackSpeed) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ATK) ||
|
|
IsEffectInSpell(selectedBotSpell.SpellId, SE_STR) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ReverseDS))
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
case ARCHETYPE_MELEE:
|
|
if(IsEffectInSpell(selectedBotSpell.SpellId, SE_IncreaseSpellHaste) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ManaPool) ||
|
|
IsEffectInSpell(selectedBotSpell.SpellId, SE_CastingLevel) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ManaRegen_v2) ||
|
|
IsEffectInSpell(selectedBotSpell.SpellId, SE_CurrentMana))
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
case ARCHETYPE_HYBRID:
|
|
//Hybrids get all buffs
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(botClass == ENCHANTER && IsEffectInSpell(selectedBotSpell.SpellId, SE_Rune))
|
|
{
|
|
float manaRatioToCast = 75.0f;
|
|
|
|
switch(this->GetBotStance())
|
|
{
|
|
case BotStanceEfficient:
|
|
manaRatioToCast = 90.0f;
|
|
break;
|
|
case BotStanceBalanced:
|
|
case BotStanceAggressive:
|
|
manaRatioToCast = 75.0f;
|
|
break;
|
|
case BotStanceReactive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
manaRatioToCast = 50.0f;
|
|
break;
|
|
default:
|
|
manaRatioToCast = 75.0f;
|
|
break;
|
|
}
|
|
|
|
//If we're at specified mana % or below, don't rune as enchanter
|
|
if(this->GetManaRatio() <= manaRatioToCast)
|
|
break;
|
|
}
|
|
|
|
if(CheckSpellRecastTimers(this, itr->SpellIndex))
|
|
{
|
|
|
|
uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore();
|
|
|
|
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontBuffMeBefore);
|
|
|
|
if(TempDontBuffMeBefore != tar->DontBuffMeBefore())
|
|
tar->SetDontBuffMeBefore(TempDontBuffMeBefore);
|
|
}
|
|
|
|
if(castedSpell)
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Escape: {
|
|
uint8 hpr = (uint8)GetHPRatio();
|
|
bool mayGetAggro = false;
|
|
|
|
#ifdef IPC
|
|
if (hpr <= 5 || (IsNPC() && CastToNPC()->IsInteractive() && tar != this) )
|
|
#else
|
|
if(hpr > 15 && ((botClass == WIZARD) || (botClass == ENCHANTER) || (botClass == RANGER)))
|
|
mayGetAggro = HasOrMayGetAggro(); //classes have hate reducing spells
|
|
|
|
if (hpr <= 15 || mayGetAggro)
|
|
#endif
|
|
{
|
|
botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
if(IsInvulnerabilitySpell(botSpell.SpellId))
|
|
tar = this; //target self for invul type spells
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Nuke: {
|
|
if((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER)))
|
|
{
|
|
if(!checked_los) {
|
|
if(!CheckLosFN(tar))
|
|
break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
|
|
|
|
checked_los = true;
|
|
}
|
|
|
|
if(botClass == CLERIC || botClass == ENCHANTER)
|
|
{
|
|
float manaRatioToCast = 75.0f;
|
|
|
|
switch(this->GetBotStance())
|
|
{
|
|
case BotStanceEfficient:
|
|
manaRatioToCast = 90.0f;
|
|
break;
|
|
case BotStanceBalanced:
|
|
manaRatioToCast = 75.0f;
|
|
break;
|
|
case BotStanceReactive:
|
|
case BotStanceAggressive:
|
|
manaRatioToCast = 50.0f;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
manaRatioToCast = 25.0f;
|
|
break;
|
|
default:
|
|
manaRatioToCast = 50.0f;
|
|
break;
|
|
}
|
|
|
|
//If we're at specified mana % or below, don't nuke as cleric or enchanter
|
|
if(this->GetManaRatio() <= manaRatioToCast)
|
|
break;
|
|
}
|
|
|
|
if(botClass == MAGICIAN || botClass == SHADOWKNIGHT || botClass == NECROMANCER || botClass == PALADIN || botClass == RANGER || botClass == DRUID || botClass == CLERIC) {
|
|
if(tar->GetBodyType() == BT_Undead || tar->GetBodyType() == BT_SummonedUndead || tar->GetBodyType() == BT_Vampire)
|
|
botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Undead);
|
|
else if(tar->GetBodyType() == BT_Summoned || tar->GetBodyType() == BT_Summoned2 || tar->GetBodyType() == BT_Summoned3)
|
|
botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Summoned);
|
|
}
|
|
|
|
if(botClass == PALADIN || botClass == DRUID || botClass == CLERIC || botClass == ENCHANTER || botClass == WIZARD) {
|
|
if(botSpell.SpellId == 0) {
|
|
uint8 stunChance = (tar->IsCasting() ? 30: 15);
|
|
|
|
if(botClass == PALADIN)
|
|
stunChance = 50;
|
|
|
|
if(!tar->GetSpecialAbility(UNSTUNABLE) && !tar->IsStunned() && (MakeRandomInt(1, 100) <= stunChance)) {
|
|
botSpell = GetBestBotSpellForStunByTargetType(this, ST_Target);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(botClass == WIZARD && botSpell.SpellId == 0) {
|
|
botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar);
|
|
}
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Target);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
if(!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)))
|
|
break;
|
|
|
|
if(IsFearSpell(botSpell.SpellId)) {
|
|
// don't let fear cast if the npc isn't snared or rooted
|
|
if(tar->GetSnaredAmount() == -1) {
|
|
if(!tar->IsRooted())
|
|
break;
|
|
}
|
|
}
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Dispel: {
|
|
if(tar->GetHPRatio() > 95.0f) {
|
|
if(!checked_los) {
|
|
if(!CheckLosFN(tar))
|
|
break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
|
|
|
|
checked_los = true;
|
|
}
|
|
|
|
botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
// TODO: Check target to see if there is anything to dispel
|
|
|
|
if(tar->CountDispellableBuffs() > 0) {
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Pet: {
|
|
//keep mobs from recasting pets when they have them.
|
|
if (!IsPet() && !GetPetID() && !IsBotCharmer()) {
|
|
if(botClass == MAGICIAN)
|
|
botSpell = GetBestBotMagicianPetSpell(this);
|
|
else
|
|
botSpell = GetFirstBotSpellBySpellType(this, SpellType_Pet);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_InCombatBuff: {
|
|
|
|
if(botClass == SHAMAN) {
|
|
checked_los = true;
|
|
|
|
std::list<BotSpell> inCombatBuffList = GetBotSpellsBySpellType(this, SpellType_InCombatBuff);
|
|
|
|
for(std::list<BotSpell>::iterator itr = inCombatBuffList.begin(); itr != inCombatBuffList.end(); itr++) {
|
|
BotSpell selectedBotSpell = *itr;
|
|
|
|
if(selectedBotSpell.SpellId == 0)
|
|
continue;
|
|
|
|
if(CheckSpellRecastTimers(this, itr->SpellIndex))
|
|
{
|
|
if(!(!tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && (spells[selectedBotSpell.SpellId].buffduration < 1 || tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0)))
|
|
continue;
|
|
|
|
//short duration buffs or other buffs only to be cast during combat.
|
|
if (IsSelfConversionSpell(selectedBotSpell.SpellId)) {
|
|
if(GetManaRatio() > 90.0f || GetHPRatio() < 50.0f || GetHPRatio() < (GetManaRatio() + 10.0f))
|
|
break; //don't cast if low hp, lots of mana, or if mana is higher than hps
|
|
}
|
|
|
|
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost);
|
|
}
|
|
|
|
if(castedSpell)
|
|
break;
|
|
}
|
|
}
|
|
else if(botClass == BARD) {
|
|
if (tar->DontBuffMeBefore() < Timer::GetCurrentTime()) {
|
|
std::list<BotSpell> inCombatBuffList = GetBotSpellsBySpellType(this, SpellType_InCombatBuff);
|
|
|
|
for(std::list<BotSpell>::iterator itr = inCombatBuffList.begin(); itr != inCombatBuffList.end(); itr++) {
|
|
BotSpell selectedBotSpell = *itr;
|
|
|
|
if(selectedBotSpell.SpellId == 0)
|
|
continue;
|
|
|
|
if(CheckSpellRecastTimers(this, itr->SpellIndex)) {
|
|
uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore();
|
|
|
|
// no buffs with illusions.. use #bot command to cast illusions
|
|
if(IsEffectInSpell(selectedBotSpell.SpellId, SE_Illusion) && tar != this)
|
|
continue;
|
|
|
|
//no teleport spells use #bot command to cast teleports
|
|
if(IsEffectInSpell(selectedBotSpell.SpellId, SE_Teleport) || IsEffectInSpell(selectedBotSpell.SpellId, SE_Succor))
|
|
continue;
|
|
|
|
// can not cast buffs for your own pet only on another pet that isn't yours
|
|
if((spells[selectedBotSpell.SpellId].targettype == ST_Pet) && (tar != this->GetPet()))
|
|
continue;
|
|
|
|
// Validate target
|
|
|
|
if(!((spells[selectedBotSpell.SpellId].targettype == ST_Target || spells[selectedBotSpell.SpellId].targettype == ST_Pet || tar == this ||
|
|
spells[selectedBotSpell.SpellId].targettype == ST_Group || spells[selectedBotSpell.SpellId].targettype == ST_GroupTeleport ||
|
|
(botClass == BARD && spells[selectedBotSpell.SpellId].targettype == ST_AEBard))
|
|
&& !tar->IsImmuneToSpell(selectedBotSpell.SpellId, this)
|
|
&& (tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0))) {
|
|
continue;
|
|
}
|
|
|
|
// Put the zone levitate and movement check here since bots are able to bypass the client casting check
|
|
if((IsEffectInSpell(selectedBotSpell.SpellId, SE_Levitate) && !zone->CanLevitate())
|
|
|| (IsEffectInSpell(selectedBotSpell.SpellId, SE_MovementSpeed) && !zone->CanCastOutdoor())) {
|
|
if(!IsSpellUsableThisZoneType(selectedBotSpell.SpellId, zone->GetZoneType())) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if(!IsGroupSpell(selectedBotSpell.SpellId)) {
|
|
//Only check archetype if song is not a group spell
|
|
switch(tar->GetArchetype()) {
|
|
case ARCHETYPE_CASTER:
|
|
//TODO: probably more caster specific spell effects in here
|
|
if(IsEffectInSpell(selectedBotSpell.SpellId, SE_AttackSpeed) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ATK) ||
|
|
IsEffectInSpell(selectedBotSpell.SpellId, SE_STR) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ReverseDS))
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
case ARCHETYPE_MELEE:
|
|
if(IsEffectInSpell(selectedBotSpell.SpellId, SE_IncreaseSpellHaste) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ManaPool) ||
|
|
IsEffectInSpell(selectedBotSpell.SpellId, SE_CastingLevel) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ManaRegen_v2) ||
|
|
IsEffectInSpell(selectedBotSpell.SpellId, SE_CurrentMana))
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
case ARCHETYPE_HYBRID:
|
|
//Hybrids get all buffs
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontBuffMeBefore);
|
|
|
|
if(TempDontBuffMeBefore != tar->DontBuffMeBefore())
|
|
tar->SetDontBuffMeBefore(TempDontBuffMeBefore);
|
|
}
|
|
|
|
if(castedSpell)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Lifetap: {
|
|
if (GetHPRatio() < 90.0f) {
|
|
if(!checked_los) {
|
|
if(!CheckLosFN(tar))
|
|
break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
|
|
|
|
checked_los = true;
|
|
}
|
|
|
|
botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
if(!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)))
|
|
break;
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Snare: {
|
|
if (tar->DontSnareMeBefore() < Timer::GetCurrentTime()) {
|
|
if(!checked_los) {
|
|
if(!CheckLosFN(tar))
|
|
break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
|
|
|
|
checked_los = true;
|
|
}
|
|
|
|
botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
if(!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
|
|
break;
|
|
|
|
uint32 TempDontSnareMeBefore = tar->DontSnareMeBefore();
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontSnareMeBefore);
|
|
|
|
if(TempDontSnareMeBefore != tar->DontSnareMeBefore())
|
|
tar->SetDontSnareMeBefore(TempDontSnareMeBefore);
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_DOT: {
|
|
if ((tar->GetHPRatio() <= 98.0f) && (tar->DontDotMeBefore() < Timer::GetCurrentTime()) && (tar->GetHPRatio() > 15.0f)) {
|
|
if(!checked_los) {
|
|
if(!CheckLosFN(tar))
|
|
break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
|
|
|
|
checked_los = true;
|
|
}
|
|
|
|
std::list<BotSpell> dotList = GetBotSpellsBySpellType(this, SpellType_DOT);
|
|
|
|
const int maxDotSelect = 5;
|
|
int dotSelectCounter = 0;
|
|
|
|
for(std::list<BotSpell>::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;
|
|
}
|
|
case SpellType_Slow: {
|
|
if (tar->GetHPRatio() <= 99.0f) {
|
|
|
|
if(!checked_los) {
|
|
if(!CheckLosFN(tar))
|
|
break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
|
|
|
|
checked_los = true;
|
|
}
|
|
|
|
switch (botClass) {
|
|
case ENCHANTER: {
|
|
botSpell = GetBestBotSpellForMagicBasedSlow(this);
|
|
break;
|
|
}
|
|
case SHAMAN:
|
|
case BEASTLORD: {
|
|
botSpell = GetBestBotSpellForDiseaseBasedSlow(this);
|
|
|
|
if(botSpell.SpellId == 0 || ((tar->GetMR() - 50) < (tar->GetDR() + spells[botSpell.SpellId].ResistDiff)))
|
|
botSpell = GetBestBotSpellForMagicBasedSlow(this);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
if(!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
|
|
break;
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
|
|
|
if(castedSpell) {
|
|
char* gmsg = 0;
|
|
|
|
MakeAnyLenString(&gmsg, "Attempting to slow %s.", tar->GetCleanName());
|
|
|
|
if(gmsg && GetGroupMessagesOn())
|
|
BotGroupSay(this, gmsg);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Debuff: {
|
|
if((tar->GetHPRatio() <= 99.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER) || (botClass == DRUID)) && (tar->GetHPRatio() > 40.0f))
|
|
{
|
|
if(!checked_los) {
|
|
if(!CheckLosFN(tar))
|
|
break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
|
|
|
|
checked_los = true;
|
|
}
|
|
|
|
botSpell = GetBestBotSpellForResistDebuff(this, tar);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetDebuffBotSpell(this, tar);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
if(!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)))
|
|
break;
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
|
}
|
|
break;
|
|
}
|
|
case SpellType_Cure: {
|
|
if(GetNeedsCured(tar) && (tar->DontCureMeBefore() < Timer::GetCurrentTime()) && !(GetNumberNeedingHealedInGroup(25, false) > 0) && !(GetNumberNeedingHealedInGroup(40, false) > 2))
|
|
{
|
|
botSpell = GetBestBotSpellForCure(this, tar);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
break;
|
|
|
|
uint32 TempDontCureMeBeforeTime = tar->DontCureMeBefore();
|
|
|
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontCureMeBeforeTime);
|
|
|
|
if(castedSpell) {
|
|
if(botClass != BARD) {
|
|
if(IsGroupSpell(botSpell.SpellId)){
|
|
Group *g;
|
|
|
|
if(this->HasGroup()) {
|
|
Group *g = this->GetGroup();
|
|
|
|
if(g) {
|
|
for( int i = 0; i<MAX_GROUP_MEMBERS; i++) {
|
|
if(g->members[i] && !g->members[i]->qglobal) {
|
|
if(TempDontCureMeBeforeTime != tar->DontCureMeBefore())
|
|
g->members[i]->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if(TempDontCureMeBeforeTime != tar->DontCureMeBefore())
|
|
tar->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return castedSpell;
|
|
}
|
|
|
|
bool Bot::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) {
|
|
bool result = false;
|
|
|
|
// manacost has special values, -1 is no mana cost, -2 is instant cast (no mana)
|
|
int32 manaCost = mana_cost;
|
|
|
|
if (manaCost == -1)
|
|
manaCost = spells[AIspells[i].spellid].mana;
|
|
else if (manaCost == -2)
|
|
manaCost = 0;
|
|
|
|
int32 extraMana = 0;
|
|
int32 hasMana = GetMana();
|
|
|
|
// Allow bots to cast buff spells even if they are out of mana
|
|
if(RuleB(Bots, BotFinishBuffing)) {
|
|
if(manaCost > hasMana) {
|
|
// Let's have the bots complete the buff time process
|
|
if(AIspells[i].type & SpellType_Buff) {
|
|
extraMana = manaCost - hasMana;
|
|
SetMana(manaCost);
|
|
}
|
|
}
|
|
}
|
|
|
|
float dist2 = 0;
|
|
|
|
if (AIspells[i].type & SpellType_Escape) {
|
|
dist2 = 0;
|
|
} else
|
|
dist2 = DistNoRoot(*tar);
|
|
|
|
if (((((spells[AIspells[i].spellid].targettype==ST_GroupTeleport && AIspells[i].type==2)
|
|
|| spells[AIspells[i].spellid].targettype==ST_AECaster
|
|
|| spells[AIspells[i].spellid].targettype==ST_Group
|
|
|| spells[AIspells[i].spellid].targettype==ST_AEBard)
|
|
&& dist2 <= spells[AIspells[i].spellid].aoerange*spells[AIspells[i].spellid].aoerange)
|
|
|| dist2 <= GetActSpellRange(AIspells[i].spellid, spells[AIspells[i].spellid].range)*GetActSpellRange(AIspells[i].spellid, spells[AIspells[i].spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana()))
|
|
{
|
|
result = NPC::AIDoSpellCast(i, tar, mana_cost, oDontDoAgainBefore);
|
|
|
|
if(IsCasting() && IsSitting())
|
|
Stand();
|
|
}
|
|
|
|
// if the spell wasn't casted, then take back any extra mana that was given to the bot to cast that spell
|
|
if(!result) {
|
|
SetMana(hasMana);
|
|
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.
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool Bot::AI_PursueCastCheck() {
|
|
bool result = false;
|
|
|
|
if (AIautocastspell_timer->Check(false)) {
|
|
|
|
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
|
|
|
mlog(AI__SPELLS, "Bot Engaged (pursuing) autocast check triggered. Trying to cast offensive spells.");
|
|
|
|
if(!AICastSpell(GetTarget(), 100, SpellType_Snare)) {
|
|
if(!AICastSpell(GetTarget(), 100, SpellType_Lifetap)) {
|
|
if(!AICastSpell(GetTarget(), 100, SpellType_Nuke)) {
|
|
/*AIautocastspell_timer->Start(RandomTimer(500, 2000), false);
|
|
result = true;*/
|
|
result = true;
|
|
}
|
|
|
|
result = true;
|
|
}
|
|
|
|
result = true;
|
|
}
|
|
|
|
if(!AIautocastspell_timer->Enabled())
|
|
AIautocastspell_timer->Start(RandomTimer(100, 250), false);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool Bot::AI_IdleCastCheck() {
|
|
bool result = false;
|
|
|
|
if (AIautocastspell_timer->Check(false)) {
|
|
#if MobAI_DEBUG_Spells >= 25
|
|
std::cout << "Non-Engaged autocast check triggered: " << this->GetCleanName() << std::endl; // cout undefine [CODEBUG]
|
|
#endif
|
|
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
|
|
|
//Ok, IdleCastCheck depends of class.
|
|
// Healers WITHOUT pets will check if a heal is needed before buffing.
|
|
uint8 botClass = GetClass();
|
|
|
|
if(botClass == CLERIC || botClass == PALADIN || botClass == RANGER) {
|
|
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
|
if (!AICastSpell(this, 100, SpellType_Heal)) {
|
|
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
|
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
|
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
|
//
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result = true;
|
|
}
|
|
// Pets class will first cast their pet, then buffs
|
|
else if(botClass == DRUID || botClass == MAGICIAN || botClass == SHADOWKNIGHT || botClass == SHAMAN || botClass == NECROMANCER || botClass == ENCHANTER || botClass == BEASTLORD || botClass == WIZARD) {
|
|
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
|
if (!AICastSpell(this, 100, SpellType_Pet)) {
|
|
if (!AICastSpell(this, 100, SpellType_Heal)) {
|
|
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
|
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
|
if (!AICastSpell(GetPet(), 100, SpellType_Heal)) {
|
|
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
|
//
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result = true;
|
|
}
|
|
else if(botClass == BARD) {
|
|
// bard bots
|
|
if(!AICastSpell(this, 100, SpellType_Cure)) {
|
|
if(!AICastSpell(this, 100, SpellType_Heal)) {
|
|
if((!RuleB(Bots, BotBardUseOutOfCombatSongs) || !GetBardUseOutOfCombatSongs()) || !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
|
|
//
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result = true;
|
|
}
|
|
|
|
if(!AIautocastspell_timer->Enabled())
|
|
AIautocastspell_timer->Start(RandomTimer(1000, 5000), false);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool Bot::AI_EngagedCastCheck() {
|
|
bool result = false;
|
|
bool failedToCast = false;
|
|
|
|
if (GetTarget() && AIautocastspell_timer->Check(false)) {
|
|
|
|
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
|
|
|
uint8 botClass = GetClass();
|
|
BotStanceType botStance = GetBotStance();
|
|
bool mayGetAggro = HasOrMayGetAggro();
|
|
|
|
mlog(AI__SPELLS, "Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells.");
|
|
|
|
if(botClass == CLERIC) {
|
|
if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
|
|
if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) {
|
|
if(!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) {
|
|
//AIautocastspell_timer->Start(RandomTimer(100, 250), false); // Do not give healer classes a lot of time off or your tank's die
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(botClass == DRUID) {
|
|
if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
|
|
if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) {
|
|
if(!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
//AIautocastspell_timer->Start(RandomTimer(100, 250), false); // Do not give healer classes a lot of time off or your tank's die
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(botClass == SHAMAN) {
|
|
if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) {
|
|
if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
|
|
if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) {
|
|
if(!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
//AIautocastspell_timer->Start(RandomTimer(100, 250), false); // Do not give healer classes a lot of time off or your tank's die
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(botClass == RANGER) {
|
|
if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
|
|
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) {
|
|
if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
//
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(botClass == BEASTLORD) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) {
|
|
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) {
|
|
if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
|
|
if(!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
//
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(botClass == WIZARD) {
|
|
if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
|
|
if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
//
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
else if(botClass == PALADIN) {
|
|
if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
|
|
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) {
|
|
//
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(botClass == SHADOWKNIGHT) {
|
|
if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
|
|
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Lifetap), SpellType_Lifetap)) {
|
|
if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
//
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(botClass == MAGICIAN) {
|
|
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) {
|
|
if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
|
|
if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
//
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(botClass == NECROMANCER) {
|
|
if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
|
|
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Lifetap), SpellType_Lifetap)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) {
|
|
if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
|
|
if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
//
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(botClass == ENCHANTER) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Mez), SpellType_Mez)) {
|
|
if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) {
|
|
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
|
|
if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) {
|
|
if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {
|
|
//
|
|
failedToCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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(!AIautocastspell_timer->Enabled()) {
|
|
AIautocastspell_timer->Start(RandomTimer(100, 250), false);
|
|
}
|
|
|
|
if(!failedToCast)
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) {
|
|
|
|
if (!tar) {
|
|
return false;
|
|
}
|
|
|
|
if(!AI_HasSpells())
|
|
return false;
|
|
|
|
if(tar->GetAppearance() == eaDead) {
|
|
if((tar->IsClient() && tar->CastToClient()->GetFeigned()) || tar->IsBot()) {
|
|
// do nothing
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
uint8 botLevel = GetLevel();
|
|
|
|
bool castedSpell = false;
|
|
|
|
BotSpell botSpell;
|
|
botSpell.SpellId = 0;
|
|
botSpell.SpellIndex = 0;
|
|
botSpell.ManaCost = 0;
|
|
|
|
if (useFastHeals) {
|
|
botSpell = GetBestBotSpellForRegularSingleTargetHeal(this);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetBestBotSpellForFastHeal(this);
|
|
}
|
|
else {
|
|
botSpell = GetBestBotSpellForPercentageHeal(this);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetBestBotSpellForRegularSingleTargetHeal(this);
|
|
|
|
if(botSpell.SpellId == 0)
|
|
botSpell = GetFirstBotSpellForSingleTargetHeal(this);
|
|
|
|
if(botSpell.SpellId == 0){
|
|
botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal);
|
|
}
|
|
}
|
|
|
|
// If there is still no spell id, then there isn't going to be one so we are done
|
|
if(botSpell.SpellId == 0)
|
|
return false;
|
|
|
|
// Can we cast this spell on this target?
|
|
if(!(spells[botSpell.SpellId].targettype==ST_GroupTeleport || spells[botSpell.SpellId].targettype == ST_Target || tar == this)
|
|
&& !(tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
|
|
return false;
|
|
|
|
uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore();
|
|
|
|
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);
|
|
}
|
|
|
|
return castedSpell;
|
|
}
|
|
|
|
std::list<BotSpell> Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect) {
|
|
std::list<BotSpell> result;
|
|
|
|
if(botCaster && botCaster->AI_HasSpells()) {
|
|
std::vector<AISpells_Struct> 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(IsEffectInSpell(botSpellList[i].spellid, spellEffect)) {
|
|
BotSpell botSpell;
|
|
botSpell.SpellId = botSpellList[i].spellid;
|
|
botSpell.SpellIndex = i;
|
|
botSpell.ManaCost = botSpellList[i].manacost;
|
|
|
|
result.push_back(botSpell);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::list<BotSpell> Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType) {
|
|
std::list<BotSpell> result;
|
|
|
|
if(botCaster && botCaster->AI_HasSpells()) {
|
|
std::vector<AISpells_Struct> 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(IsEffectInSpell(botSpellList[i].spellid, spellEffect)) {
|
|
if(spells[botSpellList[i].spellid].targettype == targetType) {
|
|
BotSpell botSpell;
|
|
botSpell.SpellId = botSpellList[i].spellid;
|
|
botSpell.SpellIndex = i;
|
|
botSpell.ManaCost = botSpellList[i].manacost;
|
|
|
|
result.push_back(botSpell);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::list<BotSpell> Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType) {
|
|
std::list<BotSpell> result;
|
|
|
|
if(botCaster && botCaster->AI_HasSpells()) {
|
|
std::vector<AISpells_Struct> 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 botSpell;
|
|
botSpell.SpellId = botSpellList[i].spellid;
|
|
botSpell.SpellIndex = i;
|
|
botSpell.ManaCost = botSpellList[i].manacost;
|
|
|
|
result.push_back(botSpell);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster && botCaster->AI_HasSpells()) {
|
|
std::vector<AISpells_Struct> 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) && CheckSpellRecastTimers(botCaster, i)) {
|
|
result.SpellId = botSpellList[i].spellid;
|
|
result.SpellIndex = i;
|
|
result.ManaCost = botSpellList[i].manacost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP);
|
|
|
|
for(std::list<BotSpell>::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(IsFastHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botHoTSpellList = GetBotSpellsForSpellEffect(botCaster, SE_HealOverTime);
|
|
std::vector<AISpells_Struct> botSpellList = botCaster->GetBotSpells();
|
|
|
|
for(std::list<BotSpell>::iterator botSpellListItr = botHoTSpellList.begin(); botSpellListItr != botHoTSpellList.end(); botSpellListItr++) {
|
|
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
if(IsHealOverTimeSpell(botSpellListItr->SpellId)) {
|
|
|
|
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].spellid == botSpellListItr->SpellId && (botSpellList[i].type & SpellType_Heal) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster && botCaster->AI_HasSpells()) {
|
|
std::vector<AISpells_Struct> 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(IsCompleteHealSpell(botSpellList[i].spellid) && CheckSpellRecastTimers(botCaster, i)) {
|
|
result.SpellId = botSpellList[i].spellid;
|
|
result.SpellIndex = i;
|
|
result.ManaCost = botSpellList[i].manacost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP);
|
|
|
|
for(std::list<BotSpell>::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(IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP);
|
|
|
|
for(std::list<BotSpell>::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((IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) || IsFastHealSpell(botSpellListItr->SpellId)) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP);
|
|
|
|
for(std::list<BotSpell>::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(IsRegularGroupHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botHoTSpellList = GetBotSpellsForSpellEffect(botCaster, SE_HealOverTime);
|
|
std::vector<AISpells_Struct> botSpellList = botCaster->GetBotSpells();
|
|
|
|
for(std::list<BotSpell>::iterator botSpellListItr = botHoTSpellList.begin(); botSpellListItr != botHoTSpellList.end(); botSpellListItr++) {
|
|
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
if(IsGroupHealOverTimeSpell(botSpellListItr->SpellId)) {
|
|
|
|
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].spellid == botSpellListItr->SpellId && (botSpellList[i].type & SpellType_Heal) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CompleteHeal);
|
|
|
|
for(std::list<BotSpell>::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(IsGroupCompleteHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_Mez);
|
|
|
|
for(std::list<BotSpell>::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(IsMezSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed);
|
|
|
|
for(std::list<BotSpell>::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)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed);
|
|
|
|
for(std::list<BotSpell>::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_DISEASE && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell) {
|
|
Mob* result = 0;
|
|
|
|
if(botCaster && IsMezSpell(botSpell.SpellId)) {
|
|
|
|
std::list<NPC*> npc_list;
|
|
entity_list.GetNPCList(npc_list);
|
|
|
|
for(std::list<NPC*>::iterator itr = npc_list.begin(); itr != npc_list.end(); itr++) {
|
|
NPC* npc = *itr;
|
|
|
|
if(npc->DistNoRootNoZ(*botCaster) <= botCaster->GetActSpellRange(botSpell.SpellId, spells[botSpell.SpellId].range)) {
|
|
if(!npc->IsMezzed()) {
|
|
if(botCaster->HasGroup()) {
|
|
Group* g = botCaster->GetGroup();
|
|
|
|
if(g) {
|
|
for(int counter = 0; counter < g->GroupCount(); counter++) {
|
|
if(npc->IsOnHatelist(g->members[counter]) && g->members[counter]->GetTarget() != npc && g->members[counter]->IsEngaged()) {
|
|
result = npc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(result)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_SummonPet);
|
|
|
|
std::string petType = GetBotMagicianPetType(botCaster);
|
|
|
|
for(std::list<BotSpell>::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(IsSummonPetSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
if(!strncmp(spells[botSpellListItr->SpellId].teleport_zone, petType.c_str(), petType.length())) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::string Bot::GetBotMagicianPetType(Bot* botCaster) {
|
|
std::string result;
|
|
|
|
if(botCaster) {
|
|
if(botCaster->IsPetChooser()) {
|
|
switch(botCaster->GetPetChooserID()) {
|
|
case 0:
|
|
result = std::string("SumWater");
|
|
break;
|
|
case 1:
|
|
result = std::string("SumFire");
|
|
break;
|
|
case 2:
|
|
result = std::string("SumAir");
|
|
break;
|
|
case 3:
|
|
result = std::string("SumEarth");
|
|
break;
|
|
default:
|
|
result = std::string("MonsterSum");
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if(botCaster->GetLevel() == 2)
|
|
result = std::string("SumWater");
|
|
else if(botCaster->GetLevel() == 3)
|
|
result = std::string("SumFire");
|
|
else if(botCaster->GetLevel() == 4)
|
|
result = std::string("SumAir");
|
|
else if(botCaster->GetLevel() == 5)
|
|
result = std::string("SumEarth");
|
|
else if(botCaster->GetLevel() < 30) {
|
|
// Under level 30
|
|
int counter = MakeRandomInt(0, 3);
|
|
|
|
switch(counter) {
|
|
case 0:
|
|
result = std::string("SumWater");
|
|
break;
|
|
case 1:
|
|
result = std::string("SumFire");
|
|
break;
|
|
case 2:
|
|
result = std::string("SumAir");
|
|
break;
|
|
case 3:
|
|
result = std::string("SumEarth");
|
|
break;
|
|
default:
|
|
result = std::string("MonsterSum");
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
// Over level 30
|
|
int counter = MakeRandomInt(0, 4);
|
|
|
|
switch(counter) {
|
|
case 0:
|
|
result = std::string("SumWater");
|
|
break;
|
|
case 1:
|
|
result = std::string("SumFire");
|
|
break;
|
|
case 2:
|
|
result = std::string("SumAir");
|
|
break;
|
|
case 3:
|
|
result = std::string("SumEarth");
|
|
break;
|
|
default:
|
|
result = std::string("MonsterSum");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster) {
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_CurrentHP, targetType);
|
|
|
|
for(std::list<BotSpell>::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(IsPureNukeSpell(botSpellListItr->SpellId) && IsDamageSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType)
|
|
{
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster)
|
|
{
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_Stun, targetType);
|
|
|
|
for(std::list<BotSpell>::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(IsStunSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex))
|
|
{
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(botCaster && target) {
|
|
const int lureResisValue = -100;
|
|
const int maxTargetResistValue = 300;
|
|
bool selectLureNuke = false;
|
|
|
|
if((target->GetMR() > maxTargetResistValue) && (target->GetCR() > maxTargetResistValue) && (target->GetFR() > maxTargetResistValue))
|
|
selectLureNuke = true;
|
|
|
|
|
|
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_CurrentHP, ST_Target);
|
|
|
|
BotSpell firstWizardMagicNukeSpellFound;
|
|
firstWizardMagicNukeSpellFound.SpellId = 0;
|
|
firstWizardMagicNukeSpellFound.SpellIndex = 0;
|
|
firstWizardMagicNukeSpellFound.ManaCost = 0;
|
|
|
|
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) {
|
|
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
bool spellSelected = false;
|
|
|
|
if(CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
|
if(selectLureNuke && (spells[botSpellListItr->SpellId].ResistDiff < lureResisValue)) {
|
|
spellSelected = true;
|
|
}
|
|
else if(IsPureNukeSpell(botSpellListItr->SpellId)) {
|
|
if(((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC)
|
|
&& (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue))
|
|
{
|
|
spellSelected = true;
|
|
}
|
|
else if(((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_COLD)
|
|
&& (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue))
|
|
{
|
|
spellSelected = true;
|
|
}
|
|
else if(((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_FIRE)
|
|
&& (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue))
|
|
{
|
|
spellSelected = true;
|
|
}
|
|
else if((GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue) && !IsStunSpell(botSpellListItr->SpellId)) {
|
|
firstWizardMagicNukeSpellFound.SpellId = botSpellListItr->SpellId;
|
|
firstWizardMagicNukeSpellFound.SpellIndex = botSpellListItr->SpellIndex;
|
|
firstWizardMagicNukeSpellFound.ManaCost = botSpellListItr->ManaCost;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(spellSelected) {
|
|
result.SpellId = botSpellListItr->SpellId;
|
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
|
result.ManaCost = botSpellListItr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(result.SpellId == 0) {
|
|
result = firstWizardMagicNukeSpellFound;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(!tar || !botCaster)
|
|
return result;
|
|
|
|
if(botCaster && botCaster->AI_HasSpells()) {
|
|
std::vector<AISpells_Struct> 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_Debuff) || IsDebuffSpell(botSpellList[i].spellid))
|
|
&& (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster)
|
|
&& tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0)
|
|
&& CheckSpellRecastTimers(botCaster, i)) {
|
|
result.SpellId = botSpellList[i].spellid;
|
|
result.SpellIndex = i;
|
|
result.ManaCost = botSpellList[i].manacost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) {
|
|
BotSpell result;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(!tar)
|
|
return result;
|
|
|
|
int level_mod = (tar->GetLevel() - botCaster->GetLevel())* (tar->GetLevel() - botCaster->GetLevel()) / 2;
|
|
if(tar->GetLevel() - botCaster->GetLevel() < 0)
|
|
{
|
|
level_mod = -level_mod;
|
|
}
|
|
bool needsMagicResistDebuff = (tar->GetMR() + level_mod) > 100 ? true: false;
|
|
bool needsColdResistDebuff = (tar->GetCR() + level_mod) > 100 ? true: false;
|
|
bool needsFireResistDebuff = (tar->GetFR() + level_mod) > 100 ? true: false;
|
|
bool needsPoisonResistDebuff = (tar->GetPR() + level_mod) > 100 ? true: false;
|
|
bool needsDiseaseResistDebuff = (tar->GetDR() + level_mod) > 100 ? true: false;
|
|
|
|
if(botCaster && botCaster->AI_HasSpells()) {
|
|
std::vector<AISpells_Struct> 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_Debuff) || IsResistDebuffSpell(botSpellList[i].spellid))
|
|
&& ((needsMagicResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistMagic)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll))
|
|
|| (needsColdResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistCold)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll))
|
|
|| (needsFireResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistFire)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll))
|
|
|| (needsPoisonResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistPoison)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll))
|
|
|| (needsDiseaseResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistDisease)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)))
|
|
&& (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster)
|
|
&& tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0)
|
|
&& CheckSpellRecastTimers(botCaster, i)) {
|
|
result.SpellId = botSpellList[i].spellid;
|
|
result.SpellIndex = i;
|
|
result.ManaCost = botSpellList[i].manacost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) {
|
|
BotSpell result;
|
|
bool spellSelected = false;
|
|
|
|
result.SpellId = 0;
|
|
result.SpellIndex = 0;
|
|
result.ManaCost = 0;
|
|
|
|
if(!tar)
|
|
return result;
|
|
|
|
int countNeedsCured = 0;
|
|
bool isPoisoned = tar->FindType(SE_PoisonCounter);
|
|
bool isDiseased = tar->FindType(SE_DiseaseCounter);
|
|
bool isCursed = tar->FindType(SE_CurseCounter);
|
|
bool isCorrupted = tar->FindType(SE_CorruptionCounter);
|
|
|
|
if(botCaster && botCaster->AI_HasSpells()) {
|
|
std::list<BotSpell> cureList = GetBotSpellsBySpellType(botCaster, SpellType_Cure);
|
|
|
|
if(tar->HasGroup()) {
|
|
Group *g = tar->GetGroup();
|
|
|
|
if(g) {
|
|
for( int i = 0; i<MAX_GROUP_MEMBERS; i++) {
|
|
if(g->members[i] && !g->members[i]->qglobal) {
|
|
if(botCaster->GetNeedsCured(g->members[i]))
|
|
countNeedsCured++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check for group cure first
|
|
if(countNeedsCured > 2) {
|
|
for(std::list<BotSpell>::iterator itr = cureList.begin(); itr != cureList.end(); itr++) {
|
|
BotSpell selectedBotSpell = *itr;
|
|
|
|
if(IsGroupSpell(itr->SpellId) && CheckSpellRecastTimers(botCaster, itr->SpellIndex)) {
|
|
if(selectedBotSpell.SpellId == 0)
|
|
continue;
|
|
|
|
if(isPoisoned && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) {
|
|
spellSelected = true;
|
|
}
|
|
else if(isDiseased && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) {
|
|
spellSelected = true;
|
|
}
|
|
else if(isCursed && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) {
|
|
spellSelected = true;
|
|
}
|
|
else if(isCorrupted && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) {
|
|
spellSelected = true;
|
|
}
|
|
else if(IsEffectInSpell(itr->SpellId, SE_DispelDetrimental)) {
|
|
spellSelected = true;
|
|
}
|
|
|
|
if(spellSelected)
|
|
{
|
|
result.SpellId = itr->SpellId;
|
|
result.SpellIndex = itr->SpellIndex;
|
|
result.ManaCost = itr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//no group cure for target- try to find single target spell
|
|
if(!spellSelected) {
|
|
for(std::list<BotSpell>::iterator itr = cureList.begin(); itr != cureList.end(); itr++) {
|
|
BotSpell selectedBotSpell = *itr;
|
|
|
|
if(CheckSpellRecastTimers(botCaster, itr->SpellIndex)) {
|
|
if(selectedBotSpell.SpellId == 0)
|
|
continue;
|
|
|
|
if(isPoisoned && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) {
|
|
spellSelected = true;
|
|
}
|
|
else if(isDiseased && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) {
|
|
spellSelected = true;
|
|
}
|
|
else if(isCursed && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) {
|
|
spellSelected = true;
|
|
}
|
|
else if(isCorrupted && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) {
|
|
spellSelected = true;
|
|
}
|
|
else if(IsEffectInSpell(itr->SpellId, SE_DispelDetrimental)) {
|
|
spellSelected = true;
|
|
}
|
|
|
|
if(spellSelected)
|
|
{
|
|
result.SpellId = itr->SpellId;
|
|
result.SpellIndex = itr->SpellIndex;
|
|
result.ManaCost = itr->ManaCost;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void Bot::SetSpellRecastTimer(int timer_index, int32 recast_delay) {
|
|
if(timer_index > 0 && timer_index <= MaxSpellTimer) {
|
|
timers[timer_index - 1] = Timer::GetCurrentTime() + recast_delay;
|
|
}
|
|
}
|
|
|
|
int32 Bot::GetSpellRecastTimer(Bot *caster, int timer_index) {
|
|
int32 result = 0;
|
|
if(caster) {
|
|
if(timer_index > 0 && timer_index <= MaxSpellTimer) {
|
|
result = caster->timers[timer_index - 1];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool Bot::CheckSpellRecastTimers(Bot *caster, int SpellIndex) {
|
|
if(caster) {
|
|
if(caster->AIspells[SpellIndex].time_cancast < Timer::GetCurrentTime()) { //checks spell recast
|
|
if(GetSpellRecastTimer(caster, spells[caster->AIspells[SpellIndex].spellid].EndurTimerIndex) < Timer::GetCurrentTime()) { //checks for spells on the same timer
|
|
return true; //can cast spell
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Bot::SetDisciplineRecastTimer(int timer_index, int32 recast_delay) {
|
|
if(timer_index > 0 && timer_index <= MaxDisciplineTimer) {
|
|
timers[DisciplineReuseStart + timer_index - 1] = Timer::GetCurrentTime() + recast_delay;
|
|
}
|
|
}
|
|
|
|
int32 Bot::GetDisciplineRecastTimer(Bot *caster, int timer_index) {
|
|
int32 result = 0;
|
|
if(caster) {
|
|
if(timer_index > 0 && timer_index <= MaxDisciplineTimer) {
|
|
result = caster->timers[DisciplineReuseStart + timer_index - 1];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint32 Bot::GetDisciplineRemainingTime(Bot *caster, int timer_index) {
|
|
int32 result = 0;
|
|
if(caster) {
|
|
if(timer_index > 0 && timer_index <= MaxDisciplineTimer) {
|
|
if(GetDisciplineRecastTimer(caster, timer_index) > Timer::GetCurrentTime())
|
|
result = GetDisciplineRecastTimer(caster, timer_index) - Timer::GetCurrentTime();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool Bot::CheckDisciplineRecastTimers(Bot *caster, int timer_index) {
|
|
if(caster) {
|
|
if(GetDisciplineRecastTimer(caster, timer_index) < Timer::GetCurrentTime()) { //checks for spells on the same timer
|
|
return true; //can cast spell
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
BotStanceType botStance = GetBotStance();
|
|
uint8 botClass = GetClass();
|
|
bool isPrimaryHealer = false;
|
|
bool isPrimarySlower = false;
|
|
|
|
if(HasGroup()) {
|
|
isPrimaryHealer = IsGroupPrimaryHealer();
|
|
isPrimarySlower = IsGroupPrimarySlower();
|
|
}
|
|
|
|
//Nuke
|
|
switch(botClass)
|
|
{
|
|
case WIZARD:
|
|
case MAGICIAN:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
case BotStanceAggressive:
|
|
castChance = 75;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 100;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case DRUID:
|
|
case CLERIC:
|
|
case PALADIN:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimaryHealer?15:25;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimaryHealer?0:15;
|
|
break;
|
|
case BotStanceAggressive:
|
|
castChance = isPrimaryHealer?15:50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimaryHealer?25:50;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case ENCHANTER:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimarySlower?15:25;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimarySlower?0:15;
|
|
break;
|
|
case BotStanceAggressive:
|
|
castChance = isPrimarySlower?15:50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimarySlower?25:50;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BEASTLORD:
|
|
case SHAMAN:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimarySlower?isPrimaryHealer?0:5:isPrimaryHealer?10:15;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimarySlower?isPrimaryHealer?0:0:isPrimaryHealer?5:10;
|
|
break;
|
|
case BotStanceAggressive:
|
|
castChance = isPrimarySlower?isPrimaryHealer?5:15:isPrimaryHealer?15:25;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimarySlower?isPrimaryHealer?15:25:isPrimaryHealer?25:50;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case NECROMANCER:
|
|
case RANGER:
|
|
case SHADOWKNIGHT:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
case BotStanceAggressive:
|
|
castChance = 15;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = 5;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 50;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BARD:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
case BotStanceAggressive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = 25;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 100;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_NukeIndex] = castChance;
|
|
|
|
//Heal
|
|
switch(botClass)
|
|
{
|
|
case CLERIC:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceEfficient:
|
|
case BotStanceReactive:
|
|
castChance = 100;
|
|
break;
|
|
case BotStanceAggressive:
|
|
castChance = 75;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 50;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case DRUID:
|
|
case SHAMAN:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceEfficient:
|
|
castChance = isPrimaryHealer?100:15;
|
|
break;
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimaryHealer?100:25;
|
|
break;
|
|
case BotStanceAggressive:
|
|
castChance = isPrimaryHealer?75:15;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimaryHealer?50:10;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case NECROMANCER:
|
|
case MAGICIAN:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 100;
|
|
break;
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 25;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case SHADOWKNIGHT:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
castChance = 25;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 15;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BEASTLORD:
|
|
case PALADIN:
|
|
case RANGER:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimaryHealer?100:25;
|
|
break;
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
castChance = isPrimaryHealer?75:15;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimaryHealer?50:0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BARD:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 100;
|
|
break;
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 25;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case ENCHANTER:
|
|
case WIZARD:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_HealIndex] = castChance;
|
|
|
|
//Root
|
|
castChance = 0;
|
|
_spellCastingChances[botStance][SpellType_RootIndex] = castChance;
|
|
|
|
//Buff
|
|
switch(botClass)
|
|
{
|
|
case BARD:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 100;
|
|
break;
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 25;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case CLERIC:
|
|
case DRUID:
|
|
case SHAMAN:
|
|
case NECROMANCER:
|
|
case MAGICIAN:
|
|
case SHADOWKNIGHT:
|
|
case BEASTLORD:
|
|
case PALADIN:
|
|
case RANGER:
|
|
case ENCHANTER:
|
|
case WIZARD:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_BuffIndex] = castChance;
|
|
|
|
//Escape
|
|
switch(botClass)
|
|
{
|
|
case ENCHANTER:
|
|
case WIZARD:
|
|
case RANGER:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
case BotStanceEfficient:
|
|
castChance = 100;
|
|
break;
|
|
case BotStanceAggressive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 25;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case CLERIC:
|
|
case DRUID:
|
|
case SHAMAN:
|
|
case NECROMANCER:
|
|
case MAGICIAN:
|
|
case BARD:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
case BotStanceEfficient:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceAggressive:
|
|
castChance = 25;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 15;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case SHADOWKNIGHT:
|
|
case PALADIN:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 25;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = 15;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BEASTLORD:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_EscapeIndex] = castChance;
|
|
|
|
//Pet
|
|
switch(botClass)
|
|
{
|
|
case MAGICIAN:
|
|
case NECROMANCER:
|
|
case BEASTLORD:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 100;
|
|
break;
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 25;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case DRUID:
|
|
case SHAMAN:
|
|
case ENCHANTER:
|
|
case SHADOWKNIGHT:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 25;
|
|
break;
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 10;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BARD:
|
|
case WIZARD:
|
|
case CLERIC:
|
|
case RANGER:
|
|
case PALADIN:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_PetIndex] = castChance;
|
|
|
|
//Lifetap
|
|
switch(botClass)
|
|
{
|
|
case NECROMANCER:
|
|
case SHADOWKNIGHT:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 100;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case MAGICIAN:
|
|
case BEASTLORD:
|
|
case DRUID:
|
|
case SHAMAN:
|
|
case ENCHANTER:
|
|
case BARD:
|
|
case WIZARD:
|
|
case CLERIC:
|
|
case RANGER:
|
|
case PALADIN:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_LifetapIndex] = castChance;
|
|
|
|
//Snare
|
|
castChance = 0;
|
|
_spellCastingChances[botStance][SpellType_SnareIndex] = castChance;
|
|
|
|
//DOT
|
|
switch(botClass)
|
|
{
|
|
case NECROMANCER:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
case BotStanceAggressive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = 25;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 75;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case DRUID:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimaryHealer?15:50;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimaryHealer?10:25;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimaryHealer?25:50;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case ENCHANTER:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimarySlower?15:50;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimarySlower?10:25;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimarySlower?25:15;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case SHAMAN:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimarySlower?isPrimaryHealer?0:15:isPrimaryHealer?15:25;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimarySlower?isPrimaryHealer?0:10:isPrimaryHealer?10:15;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimarySlower?isPrimaryHealer?15:50:isPrimaryHealer?25:50;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BEASTLORD:
|
|
case RANGER:
|
|
case SHADOWKNIGHT:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimarySlower?10:15;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimarySlower?0:10;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimarySlower?25:50;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BARD:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimarySlower?25:50;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimarySlower?15:25;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimarySlower?50:100;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case MAGICIAN:
|
|
case PALADIN:
|
|
case CLERIC:
|
|
case WIZARD:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_DOTIndex] = castChance;
|
|
|
|
//Dispel
|
|
switch(botClass)
|
|
{
|
|
case BARD:
|
|
case ENCHANTER:
|
|
case WIZARD:
|
|
case MAGICIAN:
|
|
case CLERIC:
|
|
case DRUID:
|
|
case SHAMAN:
|
|
case NECROMANCER:
|
|
case RANGER:
|
|
case SHADOWKNIGHT:
|
|
case PALADIN:
|
|
case BEASTLORD:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_DispelIndex] = castChance;
|
|
|
|
//InCombatBuff
|
|
switch(botClass)
|
|
{
|
|
case CLERIC:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 15;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = 0;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 25;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case PALADIN:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 25;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = 0;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 50;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case SHAMAN:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimaryHealer?75:50;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimaryHealer?50:25;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimaryHealer?100:75;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BARD:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 100;
|
|
break;
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 25;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BEASTLORD:
|
|
case MAGICIAN:
|
|
case DRUID:
|
|
case ENCHANTER:
|
|
case WIZARD:
|
|
case NECROMANCER:
|
|
case SHADOWKNIGHT:
|
|
case RANGER:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_InCombatBuffIndex] = castChance;
|
|
|
|
//Mez
|
|
switch(botClass)
|
|
{
|
|
case ENCHANTER:
|
|
case BARD:
|
|
castChance = 100;
|
|
break;
|
|
case WIZARD:
|
|
case CLERIC:
|
|
case DRUID:
|
|
case SHAMAN:
|
|
case NECROMANCER:
|
|
case MAGICIAN:
|
|
case RANGER:
|
|
case SHADOWKNIGHT:
|
|
case PALADIN:
|
|
case BEASTLORD:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_MezIndex] = castChance;
|
|
|
|
//Charm
|
|
castChance = 0;
|
|
_spellCastingChances[botStance][SpellType_CharmIndex] = castChance;
|
|
|
|
//Slow
|
|
switch(botClass)
|
|
{
|
|
case ENCHANTER:
|
|
case SHAMAN:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimarySlower?100:50;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimarySlower?100:25;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimarySlower?50:15;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BARD:
|
|
case BEASTLORD:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = isPrimarySlower?100:25;
|
|
break;
|
|
case BotStanceEfficient:
|
|
castChance = isPrimarySlower?100:15;
|
|
break;
|
|
case BotStanceAggressive:
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = isPrimarySlower?50:0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case MAGICIAN:
|
|
case DRUID:
|
|
case PALADIN:
|
|
case CLERIC:
|
|
case WIZARD:
|
|
case NECROMANCER:
|
|
case SHADOWKNIGHT:
|
|
case RANGER:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_SlowIndex] = castChance;
|
|
|
|
//Debuff
|
|
switch(botClass)
|
|
{
|
|
case ENCHANTER:
|
|
case SHAMAN:
|
|
case MAGICIAN:
|
|
case DRUID:
|
|
case NECROMANCER:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 25;
|
|
break;
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
castChance = 15;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BEASTLORD:
|
|
case RANGER:
|
|
case SHADOWKNIGHT:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceReactive:
|
|
castChance = 15;
|
|
break;
|
|
case BotStanceEfficient:
|
|
case BotStanceAggressive:
|
|
castChance = 10;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case BARD:
|
|
switch(botStance)
|
|
{
|
|
case BotStanceBalanced:
|
|
case BotStanceEfficient:
|
|
castChance = 25;
|
|
break;
|
|
case BotStanceReactive:
|
|
case BotStanceAggressive:
|
|
castChance = 50;
|
|
break;
|
|
case BotStanceBurn:
|
|
case BotStanceBurnAE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case CLERIC:
|
|
case PALADIN:
|
|
case WIZARD:
|
|
case WARRIOR:
|
|
case BERSERKER:
|
|
case MONK:
|
|
case ROGUE:
|
|
castChance = 0;
|
|
break;
|
|
default:
|
|
castChance = 0;
|
|
break;
|
|
}
|
|
_spellCastingChances[botStance][SpellType_DebuffIndex] = castChance;
|
|
|
|
//Cure
|
|
castChance = 0;
|
|
_spellCastingChances[botStance][SpellType_CureIndex] = castChance;
|
|
}
|
|
|
|
uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) {
|
|
int index = 0;
|
|
int botStance = (int)GetBotStance();
|
|
uint8 chance = 0;
|
|
|
|
if(GetBotStance() >= MaxStances)
|
|
return 0;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if(index >= MaxSpellTypes)
|
|
return 0;
|
|
|
|
chance = _spellCastingChances[botStance][index];
|
|
|
|
return chance;
|
|
}
|
|
|
|
#endif
|