#include "BotBrain.h" #include "../Combat.h" #include "../Spells.h" #include "../../common/Log.h" #include "../Rules/Rules.h" extern RuleManager rule_manager; BotBrain::BotBrain(Bot* body) : Brain(body) { Body = body; } BotBrain::~BotBrain() { } void BotBrain::Think() { // No ownder do nothing, probably despawn as owner should never be empty for bots if (!m_body->GetOwner()) return; // Not in a group yet then do nothing if (!m_body->GetGroupMemberInfo()) return; if (!Body->Alive()) return; if (Body->IsMezzedOrStunned()) return; // If combat was processed we can return out if (ProcessCombat()) return; // Combat failed to process so do out of combat tasks like follow the player if (ProcessOutOfCombatSpells()) return; // put htis here so bots don't try to follow the owner while in combat if (Body->EngagedInCombat()) return; // Set target to owner Spawn* target = GetBody()->GetFollowTarget(); if(target) { // Get distance from the owner float distance = GetBody()->GetDistance(target); // If out of melee range then move closer if (distance > rule_manager.GetZoneRule(GetBody()->GetZoneID(), R_Combat, MaxCombatRange)->GetFloat()) MoveCloser(target); } } bool BotBrain::ProcessCombat() { SetTarget(); if (Body->GetTarget() && Body->EngagedInCombat()) { if (Body->GetTarget() && Body->GetTarget()->IsEntity() && Body->AttackAllowed((Entity*)Body->GetTarget())) { Entity* target = (Entity*)Body->GetTarget(); float distance = Body->GetDistance(target); if (!ProcessSpell(target, distance)) { if (Body->ShouldMelee()) ProcessMelee(target, distance); } NPC* pet = (NPC*)Body->GetPet(); if (pet) { if (pet->Brain()->GetHate(target) == 0) pet->AddHate(target, 1); } } return true; } return false; } void BotBrain::SetTarget() { // The target issued from /bot attack if (Body->GetCombatTarget() && Body->GetCombatTarget()->Alive()) { Body->SetTarget(Body->GetCombatTarget()); Body->InCombat(true); return; } // Assist Entity* owner = Body->GetOwner(); if (owner && owner->EngagedInCombat()) { if (owner->GetTarget() && owner->GetTarget()->IsEntity() && owner->GetTarget()->Alive() && owner->AttackAllowed((Entity*)owner->GetTarget())) { Body->SetTarget(owner->GetTarget()); Body->InCombat(true); // Add some hate to keep the bot attacking if // the player toggles combat off if (GetHate((Entity*)Body->GetTarget()) == 0) AddHate((Entity*)Body->GetTarget(), 1); return; } } // Most hated Entity* hated = GetMostHated(); if (hated && hated->Alive()) { if (hated == Body->GetOwner()) { ClearHate(hated); } else { Body->SetTarget(hated); Body->InCombat(true); return; } } // None of the above true so clear target and turn combat off Body->SetTarget(0); Body->InCombat(false); } bool BotBrain::ProcessSpell(Entity* target, float distance) { if (Body->IsStifled() || Body->IsFeared()) return false; if (Body->IsCasting()) return false; if (!HasRecovered()) return false; Spell* spell = Body->SelectSpellToCast(distance); if (spell) { // Target can change (heals for example) so recalculate distance and if out of range move closer float distance = Body->GetDistance(Body->GetTarget()); if (distance > spell->GetSpellData()->range) { if (Body->GetTarget()->IsEntity()) MoveCloser((Spawn*)Body->GetTarget()); } else { // stop movement if spell can't be cast while moving if (!spell->GetSpellData()->cast_while_moving) Body->CalculateRunningLocation(true); Body->GetZone()->ProcessSpell(spell, Body, Body->GetTarget()); m_spellRecovery = (int32)(Timer::GetCurrentTime2() + (spell->GetSpellData()->cast_time * 10) + (spell->GetSpellData()->recovery * 10) + 2000); // recast time int32 time = Timer::GetCurrentTime2() + (spell->CalculateRecastTimer(Body)); Body->SetRecast(spell, time); string str = "I am casting "; str += spell->GetName(); Body->MessageGroup(str); } return true; } return false; } bool BotBrain::ProcessOutOfCombatSpells() { if (Body->IsStifled() || Body->IsFeared()) return false; if (Body->IsCasting()) return false; if (!HasRecovered()) return false; Spell* spell = Body->GetHealSpell(); if (!spell) spell = Body->GetRezSpell(); if (!spell) spell = Body->GetNextBuffSpell(); if (spell) { // stop movement if spell can't be cast while moving if (!spell->GetSpellData()->cast_while_moving) Body->CalculateRunningLocation(true); // See if we are in range of target, if not move closer float distance = Body->GetDistance(Body->GetTarget()); if (distance > spell->GetSpellData()->range) { if (Body->GetTarget()->IsEntity()) MoveCloser((Spawn*)Body->GetTarget()); } else { Body->GetZone()->ProcessSpell(spell, Body, Body->GetTarget()); m_spellRecovery = (int32)(Timer::GetCurrentTime2() + (spell->GetSpellData()->cast_time * 10) + (spell->GetSpellData()->recovery * 10) + 2000); // recast time int32 time = Timer::GetCurrentTime2() + (spell->CalculateRecastTimer(Body)); Body->SetRecast(spell, time); string str = "I am casting "; str += spell->GetName(); Body->MessageGroup(str); } return true; } return false; }