/** * NeedGroupBuff() * return boolean Returns true if more than two targets in the bot's group need the group buff. * * params:groupBuffSpellId uint32 the spell ID of the group buff like Arcane Brillance * params:singleBuffSpellId uint32 the spell ID of the single target buff equivalent of the group buff like Arcane Intellect for group buff Arcane Brillance * return false if false is returned, the bot is expected to perform a buff check for the single target buff of the group buff. * */ bool PlayerbotClassAI::NeedGroupBuff(uint32 groupBuffSpellId, uint32 singleBuffSpellId) { if (!m_bot) return false; uint8 numberOfGroupTargets = 0; // Check group players to avoid using regeant and mana with an expensive group buff // when only two players or less need it if (m_bot->GetGroup()) { Group::MemberSlotList const& groupSlot = m_bot->GetGroup()->GetMemberSlots(); for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) { Player *groupMember = sObjectMgr.GetPlayer(itr->guid); if (!groupMember || !groupMember->isAlive()) continue; // Check if group member needs buff if (!groupMember->HasAura(groupBuffSpellId, EFFECT_INDEX_0) && !groupMember->HasAura(singleBuffSpellId, EFFECT_INDEX_0)) numberOfGroupTargets++; // Don't forget about pet Pet * pet = groupMember->GetPet(); if (pet && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE) && (pet->HasAura(groupBuffSpellId, EFFECT_INDEX_0) || pet->HasAura(singleBuffSpellId, EFFECT_INDEX_0))) numberOfGroupTargets++; } // treshold set to 2 targets because beyond that value, the group buff cost is cheaper in mana if (numberOfGroupTargets < 3) return false; // In doubt, buff everyone return true; } else return false; // no group, no group buff }
// TODO: this and mage's BuffHelper are identical and thus could probably go in PlayerbotClassAI.cpp somewhere bool PlayerbotPriestAI::BuffHelper(PlayerbotAI* ai, uint32 spellId, Unit *target) { if (!ai) return false; if (spellId == 0) return false; if (!target) return false; Pet * pet = target->GetPet(); if (pet && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE) && ai->Buff(spellId, pet)) return true; if (ai->Buff(spellId, target)) return true; return false; }
bool PlayerbotDruidAI::BuffPlayer(Player* target) { PlayerbotAI * ai = GetAI(); Pet * pet = target->GetPet(); if (pet && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE)) { if (ai->Buff(MARK_OF_THE_WILD, pet, &(PlayerbotDruidAI::GoBuffForm))) return true; else if (ai->Buff(THORNS, pet, &(PlayerbotDruidAI::GoBuffForm))) return true; } if (ai->Buff(MARK_OF_THE_WILD, target, &(PlayerbotDruidAI::GoBuffForm))) return true; else if (ai->Buff(THORNS, target, &(PlayerbotDruidAI::GoBuffForm))) return true; else return false; }
/** * BuffHelper * BuffHelper is a static function, takes an AI, spellId (ignored for paladin) and a target and attempts to buff them as well as their pets as * best as possible. * * Return bool - returns true if a buff took place. */ bool PlayerbotPaladinAI::BuffHelper(PlayerbotAI* ai, uint32 spellId, Unit* target) { if (!ai) return false; if (spellId == 0) return false; if (!target) return false; PlayerbotPaladinAI* c = (PlayerbotPaladinAI*) ai->GetClassAI(); uint32 bigSpellId = 0; Pet* pet = target->GetPet(); uint32 petSpellId = 0, petBigSpellId = 0; // See which buff is appropriate according to class // TODO: take into account other paladins in the group switch (target->getClass()) { case CLASS_DRUID: case CLASS_SHAMAN: case CLASS_PALADIN: spellId = c->BLESSING_OF_MIGHT; if (!spellId) { spellId = c->BLESSING_OF_KINGS; if (!spellId) { spellId = c->BLESSING_OF_WISDOM; if (!spellId) { spellId = c->BLESSING_OF_SANCTUARY; if (!spellId) return false; } } } break; case CLASS_DEATH_KNIGHT: case CLASS_HUNTER: case CLASS_ROGUE: case CLASS_WARRIOR: spellId = c->BLESSING_OF_MIGHT; if (!spellId) { spellId = c->BLESSING_OF_KINGS; if (!spellId) { spellId = c->BLESSING_OF_SANCTUARY; if (!spellId) return false; } } break; case CLASS_WARLOCK: case CLASS_PRIEST: case CLASS_MAGE: spellId = c->BLESSING_OF_WISDOM; if (!spellId) { spellId = c->BLESSING_OF_KINGS; if (!spellId) { spellId = c->BLESSING_OF_SANCTUARY; if (!spellId) return false; } } break; default: // PET /** Hunter pet if (pet && ai->CanReceiveSpecificSpell(SPELL_BLESSING, pet) && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE)) { petSpellId = c->BLESSING_OF_MIGHT; if (!petSpellId) { petSpellId = c->BLESSING_OF_KINGS; if (!petSpellId) petSpellId = c->BLESSING_OF_SANCTUARY; } }*/ if (/*pet && ai->CanReceiveSpecificSpell(SPELL_BLESSING, pet) && */!target->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE)) { if (target->GetPowerType() == POWER_MANA) spellId = c->BLESSING_OF_WISDOM; else spellId = c->BLESSING_OF_MIGHT; if (!spellId) { spellId = c->BLESSING_OF_KINGS; if (!spellId) spellId = c->BLESSING_OF_SANCTUARY; } } } /*if (petSpellId == c->BLESSING_OF_MIGHT) petBigSpellId = c->GREATER_BLESSING_OF_MIGHT; else if (petSpellId == c->BLESSING_OF_WISDOM) petBigSpellId = c->GREATER_BLESSING_OF_WISDOM; else if (petSpellId == c->BLESSING_OF_KINGS) petBigSpellId = c->GREATER_BLESSING_OF_KINGS; else if (petSpellId == c->BLESSING_OF_SANCTUARY) petBigSpellId = c->GREATER_BLESSING_OF_SANCTUARY;*/ if (spellId == c->BLESSING_OF_MIGHT) bigSpellId = c->GREATER_BLESSING_OF_MIGHT; else if (spellId == c->BLESSING_OF_WISDOM) bigSpellId = c->GREATER_BLESSING_OF_WISDOM; else if (spellId == c->BLESSING_OF_KINGS) bigSpellId = c->GREATER_BLESSING_OF_KINGS; else if (spellId == c->BLESSING_OF_SANCTUARY) bigSpellId = c->GREATER_BLESSING_OF_SANCTUARY; if (pet && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE) && ai->HasSpellReagents(petBigSpellId) && ai->Buff(petBigSpellId, pet)) return true; if (ai->HasSpellReagents(bigSpellId) && ai->Buff(bigSpellId, target)) return true; if ((pet && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE) && ai->Buff(petSpellId, pet)) || ai->Buff(spellId, target)) return true; return false; }
void WorldSession::HandlePetAction(WorldPacket& recv_data) { ObjectGuid petGuid; uint32 data; ObjectGuid targetGuid; recv_data >> petGuid; recv_data >> data; recv_data >> targetGuid; uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data); uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); // delete = 0x07 CastSpell = C1 DETAIL_LOG("HandlePetAction: %s flag is %u, spellid is %u, target %s.", petGuid.GetString().c_str(), uint32(flag), spellid, targetGuid.GetString().c_str()); // used also for charmed creature/player Unit* petUnit = _player->GetMap()->GetUnit(petGuid); if (!petUnit) { sLog.outError("HandlePetAction: %s not exist.", petGuid.GetString().c_str()); return; } if (_player->GetObjectGuid() != petUnit->GetCharmerOrOwnerGuid()) { sLog.outError("HandlePetAction: %s isn't controlled by %s.", petGuid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } if (!petUnit->isAlive()) return; CharmInfo* charmInfo = petUnit->GetCharmInfo(); if (!charmInfo) { sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", petUnit->GetGUIDLow(), petUnit->GetTypeId()); return; } Pet* pet = nullptr; Creature* creature = nullptr; if (petUnit->GetTypeId() == TYPEID_UNIT) { creature = static_cast<Creature*>(petUnit); if (creature->IsPet()) { pet = static_cast<Pet*>(petUnit); if (pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) return; } } if (!pet && petUnit->hasUnitState(UNIT_STAT_CONTROLLED)) { // possess case if (flag != uint8(ACT_COMMAND)) { sLog.outError("PetHAndler: unknown PET flag Action %i and spellid %i. For possessed %s", uint32(flag), spellid, petUnit->GetGuidStr().c_str()); return; } switch (spellid) { case COMMAND_STAY: case COMMAND_FOLLOW: charmInfo->SetCommandState(CommandStates(spellid)); break; case COMMAND_ATTACK: { Unit* targetUnit = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; if (targetUnit && targetUnit != petUnit && targetUnit->isTargetableForAttack()) { _player->SetInCombatState(true, targetUnit); // This is true if pet has no target or has target but targets differs. if (petUnit->getVictim() != targetUnit) petUnit->Attack(targetUnit, true); } break; } case COMMAND_ABANDON: _player->Uncharm(); break; default: sLog.outError("PetHandler: Not allowed action %i and spellid %i. Pet %s owner is %s", uint32(flag), spellid, petUnit->GetGuidStr().c_str(), _player->GetGuidStr().c_str()); break; } return; } // only real pet should go there if (!pet) { sLog.outError("PetHandler: A not pet trying to do unknown Action %i and spellid %i. Pet %s owner is %s", uint32(flag), spellid, petUnit->GetGuidStr().c_str(), _player->GetGuidStr().c_str()); return; } switch (flag) { case ACT_COMMAND: // 0x07 switch (spellid) { case COMMAND_STAY: // flat=1792 // STAY { if (!petUnit->hasUnitState(UNIT_STAT_CONTROLLED)) { petUnit->StopMoving(); petUnit->GetMotionMaster()->Clear(); } petUnit->AttackStop(true, true); pet->SetIsRetreating(); pet->SetStayPosition(true); pet->SetSpellOpener(); charmInfo->SetCommandState(COMMAND_STAY); break; } case COMMAND_FOLLOW: // spellid=1792 // FOLLOW { if (!petUnit->hasUnitState(UNIT_STAT_CONTROLLED)) { petUnit->StopMoving(); petUnit->GetMotionMaster()->Clear(); pet->SetIsRetreating(true); } petUnit->AttackStop(true, true); pet->SetStayPosition(); pet->SetSpellOpener(); charmInfo->SetCommandState(COMMAND_FOLLOW); break; } case COMMAND_ATTACK: // spellid=1792 // ATTACK { pet->SetIsRetreating(); pet->SetSpellOpener(); Unit* targetUnit = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; if (targetUnit && targetUnit != petUnit && targetUnit->isTargetableForAttack() && targetUnit->isInAccessablePlaceFor((Creature*)petUnit)) { _player->SetInCombatState(true, targetUnit); // This is true if pet has no target or has target but targets differs. if (petUnit->getVictim() != targetUnit) { petUnit->AttackStop(); if (!petUnit->hasUnitState(UNIT_STAT_CONTROLLED)) { petUnit->GetMotionMaster()->Clear(); pet->AI()->AttackStart(targetUnit); // 10% chance to play special warlock pet attack talk, else growl if (pet->getPetType() == SUMMON_PET && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); } else pet->Attack(targetUnit, true); } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) { if (pet->getPetType() == HUNTER_PET) pet->Unsummon(PET_SAVE_AS_DELETED, _player); else // dismissing a summoned pet is like killing them (this prevents returning a soulshard...) pet->SetDeathState(CORPSE); pet->SetStayPosition(); break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } break; case ACT_REACTION: // 0x6 switch (spellid) { case REACT_PASSIVE: // passive { pet->AttackStop(true, true); pet->SetSpellOpener(); } case REACT_DEFENSIVE: // recovery case REACT_AGGRESSIVE: // activete { charmInfo->SetReactState(ReactStates(spellid)); break; } } break; case ACT_DISABLED: // 0x81 spell (disabled), ignore case ACT_PASSIVE: // 0x01 case ACT_ENABLED: // 0xC1 spell { pet->SetIsRetreating(); pet->SetSpellOpener(); Unit* unit_target = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; // do not cast unknown spells SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; for (int i = 0; i < MAX_EFFECT_INDEX; ++i) { if (spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; _player->SetInCombatState(true, unit_target); pet->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(pet, spellInfo, false); SpellCastResult result = spell->CheckPetCast(unit_target); const SpellRangeEntry* sRange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); if (unit_target && !(pet->IsWithinDistInMap(unit_target, sRange->maxRange) && pet->IsWithinLOSInMap(unit_target)) && !(GetPlayer()->IsFriendlyTo(unit_target) || pet->HasAuraType(SPELL_AURA_MOD_POSSESS))) { pet->SetSpellOpener(spellid, sRange->minRange, sRange->maxRange); spell->finish(false); delete spell; pet->AttackStop(); if (!pet->hasUnitState(UNIT_STAT_CONTROLLED)) { pet->GetMotionMaster()->Clear(); pet->AI()->AttackStart(unit_target); // 10% chance to play special warlock pet attack talk, else growl if (pet->IsPet() && pet->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); } else petUnit->Attack(unit_target, true); return; } // auto turn to target unless possessed if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->hasUnitState(UNIT_STAT_CONTROLLED)) { if (unit_target) { pet->SetInFront(unit_target); if (unit_target->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)unit_target); } else if (Unit* unit_target2 = spell->m_targets.getUnitTarget()) { pet->SetInFront(unit_target2); if (unit_target2->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)unit_target2); } if (Unit* powner = pet->GetCharmerOrOwner()) if (powner->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)powner); result = SPELL_CAST_OK; } if (result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); unit_target = spell->m_targets.getUnitTarget(); pet->SetSpellOpener(); spell->SpellStart(&(spell->m_targets)); } else { if (pet->hasUnitState(UNIT_STAT_CONTROLLED)) Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else { Unit* owner = pet->GetCharmerOrOwner(); if (owner && owner->GetTypeId() == TYPEID_PLAYER) Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true); } if (!pet->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); pet->SetSpellOpener(); spell->finish(false); delete spell; } break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }