void TargetedMovementGenerator::spellAtack(Creature &owner,Unit &who,uint32 spellId) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); if(!spellInfo) { sLog.outError("WORLD: unknown spell id %i\n", spellId); return; } owner.StopMoving(); owner->Idle(); if(owner.m_currentSpell) { if(owner.m_currentSpell->m_spellInfo->Id == spellInfo->Id ) return; else { owner.m_currentSpell->cancel(); } } Spell *spell = new Spell(&owner, spellInfo, false, 0); spell->SetAutoRepeat(false); //owner.addUnitState(UNIT_STAT_ATTACKING); //owner.clearUnitState(UNIT_STAT_CHASE); SpellCastTargets targets; targets.setUnitTarget( &who ); spell->prepare(&targets); owner.m_canMove = false; DEBUG_LOG("Spell Attack."); }
bool ChatHandler::HandleStartCommand(const char* args) { Player *chr = m_session->GetPlayer(); if(chr->isInFlight()) { SendSysMessage(LANG_YOU_IN_FLIGHT); return true; } if(chr->isInCombat()) { SendSysMessage(LANG_YOU_IN_COMBAT); return true; } // cast spell Stuck SpellEntry const* sInfo = sSpellStore.LookupEntry(7355); if(!sInfo) return true; Spell *spell = new Spell(chr, sInfo , false, 0); SpellCastTargets targets; targets.setUnitTarget( chr ); spell->prepare(&targets); return true; }
void TargetedMovementGenerator::_spellAtack(Creature &owner, SpellEntry* spellInfo) { if(!spellInfo) return; owner.StopMoving(); owner->Idle(); if(owner.m_currentSpell) { if(owner.m_currentSpell->m_spellInfo->Id == spellInfo->Id ) return; else { owner.m_currentSpell->cancel(); } } Spell *spell = new Spell(&owner, spellInfo, false, 0); spell->SetAutoRepeat(true); //owner.addUnitState(UNIT_STAT_ATTACKING); owner.Attack(&owner); //?? owner.clearUnitState(UNIT_STAT_CHASE); SpellCastTargets targets; targets.setUnitTarget( &i_target ); spell->prepare(&targets); owner.m_canMove = false; DEBUG_LOG("Spell Attack."); }
SpellEntry* Cast(Player*player,Item* item, uint32 spellId) { SpellEntry *spellInfo = sSpellStore.LookupEntry(spellId); if(!spellInfo) { sLog.outError("WORLD: unknown spell id %i", spellId); return NULL; } Spell spell(player, spellInfo, true, 0); SpellCastTargets targets; targets.setUnitTarget( player ); spell.m_CastItem = item; spell.prepare(&targets); return spellInfo; }
void PetAI::AutocastPreparedSpells() { //found units to cast on to if (!m_targetSpellStore.empty()) { uint32 index = urand(0, m_targetSpellStore.size() - 1); Spell* spell = m_targetSpellStore[index].second; Unit* target = m_targetSpellStore[index].first; m_targetSpellStore.erase(m_targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget(target); if (!me->HasInArc(M_PI, target)) { me->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) me->SendCreateUpdateToPlayer((Player*)target); if (m_owner && m_owner->GetTypeId() == TYPEID_PLAYER) me->SendCreateUpdateToPlayer((Player*)m_owner); } me->AddCreatureSpellCooldown(spell->GetSpellEntry()->Id); if (me->isPet()) ((Pet*)me)->CheckLearning(spell->GetSpellEntry()->Id); spell->prepare(&targets); } while (!m_targetSpellStore.empty()) { Spell *temp = m_targetSpellStore.begin()->second; m_targetSpellStore.erase(m_targetSpellStore.begin()); delete temp; } }
bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target) { if (!spellId) return false; if (!target) target = bot; Pet* pet = bot->GetPet(); if (pet && pet->HasSpell(spellId)) { pet->ToggleAutocast(spellId, true); TellMaster("My pet will auto-cast this spell"); return true; } aiObjectContext->GetValue<LastSpellCast&>("last spell cast")->Get().Set(spellId, target->GetObjectGuid(), time(0)); aiObjectContext->GetValue<LastMovement&>("last movement")->Get().Set(NULL); const SpellEntry* const pSpellInfo = sSpellStore.LookupEntry(spellId); MotionMaster &mm = *bot->GetMotionMaster(); if (bot->isMoving() && GetSpellCastTime(pSpellInfo, NULL)) { return false; } if (bot->IsTaxiFlying()) return false; bot->clearUnitState( UNIT_STAT_CHASE ); bot->clearUnitState( UNIT_STAT_FOLLOW ); ObjectGuid oldSel = bot->GetSelectionGuid(); bot->SetSelectionGuid(target->GetObjectGuid()); Spell *spell = new Spell(bot, pSpellInfo, false); SpellCastTargets targets; targets.setUnitTarget(target); WorldObject* faceTo = target; if (pSpellInfo->Targets & TARGET_FLAG_ITEM) { spell->m_CastItem = aiObjectContext->GetValue<Item*>("item for spell", spellId)->Get(); targets.setItemTarget(spell->m_CastItem); } if (pSpellInfo->Effect[0] == SPELL_EFFECT_OPEN_LOCK || pSpellInfo->Effect[0] == SPELL_EFFECT_SKINNING) { LootObject loot = *aiObjectContext->GetValue<LootObject>("loot target"); if (!loot.IsLootPossible(bot)) return false; GameObject* go = GetGameObject(loot.guid); if (go && go->isSpawned()) { WorldPacket* const packetgouse = new WorldPacket(CMSG_GAMEOBJ_USE, 8); *packetgouse << loot.guid; bot->GetSession()->QueuePacket(packetgouse); targets.setGOTarget(go); faceTo = go; } else { Unit* creature = GetUnit(loot.guid); if (creature) { targets.setUnitTarget(creature); faceTo = creature; } } } if (!bot->IsInFront(faceTo, sPlayerbotAIConfig.sightDistance)) { bot->SetFacingTo(bot->GetAngle(faceTo)); SetNextCheckDelay(sPlayerbotAIConfig.globalCoolDown); return false; } WaitForSpellCast(spellId); spell->prepare(&targets); bot->SetSelectionGuid(oldSel); LastSpellCast& lastSpell = aiObjectContext->GetValue<LastSpellCast&>("last spell cast")->Get(); return lastSpell.id == spellId; }
void PetAI::UpdateAI(const uint32 diff) { if (!m_creature->isAlive()) return; Unit* owner = m_creature->GetCharmerOrOwner(); if(m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (inCombat && !m_creature->getVictim()) _stopAttack(); // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. if (m_creature->getVictim()) { if (_needToStop()) { DEBUG_LOG("Pet AI stoped attacking [guid=%u]", m_creature->GetGUIDLow()); _stopAttack(); return; } else if (m_creature->IsStopped() || m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) { // required to be stopped cases if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false)) { if (m_creature->hasUnitState(UNIT_STAT_FOLLOW)) m_creature->InterruptNonMeleeSpells(false); else return; } // not required to be stopped case else if (m_creature->isAttackReady() && m_creature->canReachWithAttack(m_creature->getVictim())) { m_creature->AttackerStateUpdate(m_creature->getVictim()); m_creature->resetAttackTimer(); if (!m_creature->getVictim()) return; //if pet misses its target, it will also be the first in threat list m_creature->getVictim()->AddThreat(m_creature,0.0f); if( _needToStop() ) _stopAttack(); } } } else if (owner && m_creature->GetCharmInfo()) { if (owner->isInCombat() && !(m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY))) { AttackStart(owner->getAttackerForHelper()); } else if(m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) { if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW) ) { m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); } } } if (m_creature->GetGlobalCooldown() == 0 && !m_creature->IsNonMeleeSpellCasted(false)) { //Autocast for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); i++) { uint32 spellID = m_creature->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID); if (!spellInfo) continue; // ignore some combinations of combat state and combat/noncombat spells if (!inCombat) { if (!IsPositiveSpell(spellInfo->Id)) continue; } else { if (IsNonCombatSpell(spellInfo)) continue; } Spell *spell = new Spell(m_creature, spellInfo, false, 0); if (inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim())) { m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(m_creature->getVictim(), spell)); continue; } else { bool spellUsed = false; for(std::set<uint64>::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* Target = ObjectAccessor::GetUnit(*m_creature,*tar); //only buff targets that are in combat, unless the spell can only be cast while out of combat if(!Target) continue; if(spell->CanAutoCast(Target)) { m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell)); spellUsed = true; break; } } if (!spellUsed) delete spell; } } //found units to cast on to if (!m_targetSpellStore.empty()) { uint32 index = urand(0, m_targetSpellStore.size() - 1); Spell* spell = m_targetSpellStore[index].second; Unit* target = m_targetSpellStore[index].first; m_targetSpellStore.erase(m_targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget( target ); if (!m_creature->HasInArc(M_PI, target)) { m_creature->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) m_creature->SendUpdateToPlayer((Player*)target); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_creature->SendUpdateToPlayer( (Player*)owner ); } m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id); if (m_creature->isPet()) ((Pet*)m_creature)->CheckLearning(spell->m_spellInfo->Id); spell->prepare(&targets); } while (!m_targetSpellStore.empty()) { delete m_targetSpellStore.begin()->second; m_targetSpellStore.erase(m_targetSpellStore.begin()); } } }
void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data ) { uint64 guid; uint32 spellId = 0; TrainerSpell *proto=NULL; recv_data >> guid >> spellId; sLog.outDebug( "WORLD: Received CMSG_TRAINER_BUY_SPELL NpcGUID=%u, learn spell id is: %u",uint32(GUID_LOPART(guid)), spellId ); if(!GetPlayer()->isAlive()) return; Creature *unit = ObjectAccessor::Instance().GetCreature(*_player, guid); if(!unit) return; if( unit->IsHostileTo(_player)) // do not talk with enemies return; if(!unit->isCanTrainingOf(_player,true)) return; std::list<TrainerSpell*>::iterator titr; for (titr = unit->GetTspellsBegin(); titr != unit->GetTspellsEnd();titr++) { if((*titr)->spell->Id == spellId) { proto = *titr; break; } } if (proto == NULL) return; SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->spell->EffectTriggerSpell[0]); if(!spellInfo) return; if(_player->HasSpell(spellInfo->Id)) return; if(_player->getLevel() < (proto->reqlevel ? proto->reqlevel : spellInfo->spellLevel)) return; if(proto->reqskill && _player->GetSkillValue(proto->reqskill) < proto->reqskillvalue) return; uint32 prev_id = objmgr.GetPrevSpellInChain(spellInfo->Id); if(prev_id && !_player->HasSpell(prev_id)) return; if(proto->spell->Effect[1] == SPELL_EFFECT_SKILL_STEP) if(!_player->CanLearnProSpell(spellId)) return; if(!proto) { sLog.outErrorDb("TrainerBuySpell: Trainer(%u) has not the spell(%u).", uint32(GUID_LOPART(guid)), spellId); return; } if( _player->GetMoney() >= proto->spellcost ) { WorldPacket data( SMSG_TRAINER_BUY_SUCCEEDED, 12 ); data << guid << spellId; SendPacket( &data ); _player->ModifyMoney( -int32(proto->spellcost) ); if(spellInfo->powerType == 2) { _player->addSpell(spellId,4); // ative = 4 for spell book of hunter's pet return; } Spell *spell; if(proto->spell->SpellVisual == 222) spell = new Spell(_player, proto->spell, false, NULL); else spell = new Spell(unit, proto->spell, false, NULL); SpellCastTargets targets; targets.setUnitTarget( _player ); float u_oprientation = unit->GetOrientation(); // trainer always see at customer in time of training (part of client functionality) unit->SetInFront(_player); spell->prepare(&targets); // trainer always return to original orientation unit->Relocate(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),u_oprientation); } }
void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data ) { uint64 guid; uint32 spellId = OPEN_CHEST; const GameObjectInfo *info; recv_data >> guid; sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", guid); GameObject *obj = ObjectAccessor::Instance().GetGameObject(*_player, guid); if(!obj) return; // uint32 t = obj->GetUInt32Value(GAMEOBJECT_TYPE_ID); //obj->SetUInt32Value(GAMEOBJECT_FLAGS,2); //obj->SetUInt32Value(GAMEOBJECT_FLAGS,2); uint32 t = obj->GetUInt32Value(GAMEOBJECT_TYPE_ID); switch(t) { //door case GAMEOBJECT_TYPE_DOOR: //0 obj->SetUInt32Value(GAMEOBJECT_FLAGS,33); obj->SetUInt32Value(GAMEOBJECT_STATE,0); //open //obj->SetUInt32Value(GAMEOBJECT_TIMESTAMP,0x465EE6D2); //load timestamp obj->SetLootState(GO_CLOSED); obj->SetRespawnTime(5); //close door in 5 seconds return; case GAMEOBJECT_TYPE_QUESTGIVER: // 2 _player->PrepareQuestMenu( guid ); _player->SendPreparedQuest( guid ); return; //Sitting: Wooden bench, chairs enzz case GAMEOBJECT_TYPE_CHAIR: //7 info = obj->GetGOInfo(); if(info) { spellId = info->sound0; //guid=GetPlayer()->GetGUID(); _player->TeleportTo(obj->GetMapId(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(),false,false); //offset 3 is related to the DB _player->SetUInt32Value(UNIT_FIELD_BYTES_1, _player->GetUInt32Value(UNIT_FIELD_BYTES_1) | (3 + spellId) ); return; } break; //big gun, its a spell/aura case GAMEOBJECT_TYPE_GOOBER: //10 //chest locked case GAMEOBJECT_TYPE_SPELLCASTER: //22 obj->SetUInt32Value(GAMEOBJECT_FLAGS,2); info = obj->GetGOInfo(); if(info) { spellId = info->sound0; if (spellId == 0) spellId = info->sound3; guid=_player->GetGUID(); } break; //fishing bobber case GAMEOBJECT_TYPE_FISHINGNODE: //17 { if(_player->GetGUID() != obj->GetOwnerGUID()) return; switch(obj->getLootState()) { case GO_CLOSED: // ready for loot { // 1) skill must beb >= base_zone_skill // 2) if skill == base_zone_skill => 5% chance // 3) chance is liniar dependence from (base_zone_skill-skill) int32 skill = _player->GetSkillValue(SKILL_FISHING); int32 zone_skill = _player->FishingMinSkillForCurrentZone(); int32 chance = skill - zone_skill + 5; int32 roll = irand(1,100); DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll); if(skill >= zone_skill && chance >= roll) { // prevent removing GO at spell cancel _player->RemoveGameObject(obj,false); obj->SetOwnerGUID(_player->GetGUID()); //fish catched _player->UpdateFishingSkill(); _player->SendLoot(obj->GetGUID(),LOOT_FISHING); } else { // fish escaped obj->SetLootState(GO_LOOTED); // can be deleted now WorldPacket data(SMSG_FISH_ESCAPED, 0); SendPacket(&data); } break; } case GO_LOOTED: // nothing to do, wiil be deleted at next update break; default: { obj->SetLootState(GO_LOOTED); WorldPacket data(SMSG_FISH_NOT_HOOKED, 0); SendPacket(&data); break; } } if(_player->m_currentSpell) { _player->m_currentSpell->SendChannelUpdate(0); _player->m_currentSpell->finish(); } return; } obj->CountUseTimes(); case GAMEOBJECT_TYPE_FLAGSTAND: //24 //GB flag info = obj->GetGOInfo(); if(info) { spellId = info->sound0; guid=_player->GetGUID(); } break; case GAMEOBJECT_TYPE_FLAGDROP: //26 //GB flag dropped info = obj->GetGOInfo(); if(info) { spellId = info->sound0; guid=_player->GetGUID(); } break; case GAMEOBJECT_TYPE_CUSTOM_TELEPORTER: info = obj->GetGOInfo(); if(info) { AreaTrigger *fields = objmgr.GetAreaTrigger( info->sound0 ); if(fields) { sLog.outDebug( "Teleporting player %u with coordinates X: %f Y: %f Z: %f Orientation: %f Map: %u\n", _player->GetGUIDLow(), fields->X,fields->Y,fields->Z,fields->Orientation,fields->mapId); _player->TeleportTo(fields->mapId, fields->X,fields->Y,fields->Z,fields->Orientation); sLog.outDebug( "Player %u teleported by %u\n", _player->GetGUIDLow(), info->sound0); } else sLog.outDebug( "Unknown areatrigger_template id %u\n", info->sound0); delete fields; return; } break; default: sLog.outDebug( "Unknown Object Type %u\n", obj->GetUInt32Value(GAMEOBJECT_TYPE_ID)); break; } SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId ); if(!spellInfo) { sLog.outError("WORLD: unknown spell id %i\n", spellId); return; } Spell *spell = new Spell(_player, spellInfo, false, 0); SpellCastTargets targets; targets.setUnitTarget( _player ); targets.m_GOTarget = obj; spell->prepare(&targets); }
void PetAI::UpdateAI(const uint32 diff) { if (!m_creature->isAlive()) return; Unit* owner = m_creature->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; // m_creature->getVictim() can't be used for check in case stop fighting, m_creature->getVictim() clear at Unit death etc. if ( m_creature->getVictim() ) { if ( _needToStop() ) { DEBUG_LOG("Pet AI stoped attacking [guid=%u]", m_creature->GetGUIDLow()); _stopAttack(); return; } DoMeleeAttackIfReady(); } else if (owner && m_creature->GetCharmInfo()) //no victim { Unit *nextTarget = SelectNextTarget(); if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else if (owner && !m_creature->hasUnitState(UNIT_STAT_FOLLOW)) // no charm info and no victim m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, m_creature->GetFollowAngle()); if (!me->GetCharmInfo()) return; // Autocast (casted only in combat or persistent spells in any state) if (m_creature->GetGlobalCooldown() == 0 && !m_creature->hasUnitState(UNIT_STAT_CASTING)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i) { uint32 spellID = m_creature->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID); if (!spellInfo) continue; // ignore some combinations of combat state and combat/noncombat spells if (!me->getVictim()) { // ignore attacking spells, and allow only self/around spells if (!IsPositiveSpell(spellInfo->Id)) continue; // non combat spells allowed // only pet spells have IsNonCombatSpell and not fit this reqs: // Consume Shadows, Lesser Invisibility, so ignore checks for its if (!IsNonCombatSpell(spellInfo)) { // allow only spell without spell cost or with spell cost but not duration limit int32 duration = GetSpellDuration(spellInfo); if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0) continue; // allow only spell without cooldown > duration int32 cooldown = GetSpellRecoveryTime(spellInfo); if (cooldown >= 0 && duration >= 0 && cooldown > duration) continue; } } else { // just ignore non-combat spells if (IsNonCombatSpell(spellInfo)) continue; } Spell *spell = new Spell(m_creature, spellInfo, false, 0); // Fix to allow pets on STAY to autocast if (me->getVictim() && _CanAttack(me->getVictim()) && spell->CanAutoCast(me->getVictim())) { targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(m_creature->getVictim(), spell)); continue; } else { bool spellUsed = false; for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* Target = ObjectAccessor::GetUnit(*m_creature,*tar); //only buff targets that are in combat, unless the spell can only be cast while out of combat if (!Target) continue; if (spell->CanAutoCast(Target)) { targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell)); spellUsed = true; break; } } if (!spellUsed) delete spell; } } //found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget( target ); if ( !m_creature->HasInArc(M_PI, target) ) { m_creature->SetInFront(target); if (target && target->GetTypeId() == TYPEID_PLAYER) m_creature->SendUpdateToPlayer(target->ToPlayer()); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_creature->SendUpdateToPlayer(owner->ToPlayer()); } m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id); spell->prepare(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } }
void PetAI::UpdateAI(const uint32 diff) { // update i_victimGuid if i_pet.getVictim() !=0 and changed if(i_pet.getVictim()) i_victimGuid = i_pet.getVictim()->GetGUID(); // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clearóâ at Unit death etc. if( i_victimGuid ) { if( _needToStop() ) { DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow()); _stopAttack(); // i_victimGuid == 0 && i_pet.getVictim() == NULL now return; } else if( i_pet.IsStopped() ) { SpellEntry *spellInfo; if ( i_pet.m_currentSpell ) { if( i_pet.hasUnitState(UNIT_STAT_FOLLOW) ) i_pet.m_currentSpell->cancel(); else return; } else if( !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && ((Pet*)&i_pet)->HasActState(STATE_RA_AUTOSPELL) && (spellInfo = i_pet.reachWithSpellAttack(i_pet.getVictim()))) { Spell *spell = new Spell(&i_pet, spellInfo, false, 0); spell->SetAutoRepeat(true); SpellCastTargets targets; targets.setUnitTarget( i_pet.getVictim() ); spell->prepare(&targets); i_pet.m_canMove = false; DEBUG_LOG("Spell Attack."); } else if( i_pet.isAttackReady() && i_pet.canReachWithAttack(i_pet.getVictim()) ) { i_pet.AttackerStateUpdate(i_pet.getVictim()); i_pet.resetAttackTimer(); if ( !i_pet.getVictim() ) return; if( _needToStop() ) _stopAttack(); } } } else { if(i_owner && i_owner->isInCombat()) { AttackStart(i_owner->getAttackerForHelper()); } else if(i_owner && ((Pet*)&i_pet)->HasActState(STATE_RA_FOLLOW)) { if (!i_pet.hasUnitState(UNIT_STAT_FOLLOW)) { i_pet.addUnitState(UNIT_STAT_FOLLOW); i_pet->Clear(); i_pet->Mutate(new TargetedMovementGenerator(*i_owner)); } } } }
void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data ) { uint64 guid; uint32 spellId = 0; recv_data >> guid >> spellId; DEBUG_LOG( "WORLD: Received CMSG_TRAINER_BUY_SPELL NpcGUID=%u, learn spell id is: %u",uint32(GUID_LOPART(guid)), spellId ); Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER); if (!unit) { DEBUG_LOG( "WORLD: HandleTrainerBuySpellOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) ); return; } // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); if(!unit->isCanTrainingOf(_player,true)) return; // check present spell in trainer spell list TrainerSpellData const* trainer_spells = unit->GetTrainerSpells(); if(!trainer_spells) return; // not found, cheat? TrainerSpell const* trainer_spell = trainer_spells->Find(spellId); if(!trainer_spell) return; // can't be learn, cheat? Or double learn with lags... if(_player->GetTrainerSpellState(trainer_spell) != TRAINER_SPELL_GREEN) return; SpellEntry const *proto = sSpellStore.LookupEntry(trainer_spell->spell); SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->EffectTriggerSpell[0]); // apply reputation discount uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit))); // check money requirement if(_player->GetMoney() < nSpellCost ) return; _player->ModifyMoney( -int32(nSpellCost) ); WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12); // visual effect on trainer data << uint64(guid); data << uint32(0xB3); // index from SpellVisualKit.dbc SendPacket(&data); data.Initialize(SMSG_PLAY_SPELL_IMPACT, 12); // visual effect on player data << _player->GetObjectGuid(); data << uint32(0x016A); // index from SpellVisualKit.dbc SendPacket(&data); // learn explicitly to prevent lost money at lags, learning spell will be only show spell animation //[-ZERO] _player->learnSpell(trainer_spell->spell, false); data.Initialize(SMSG_TRAINER_BUY_SUCCEEDED, 12); data << uint64(guid); data << uint32(spellId); // should be same as in packet from client SendPacket(&data); Spell *spell; if(proto->SpellVisual == 222) spell = new Spell(_player, proto, false); else spell = new Spell(unit, proto, false); SpellCastTargets targets; targets.setUnitTarget( _player ); spell->prepare(&targets); }
void PetAI::UpdateAI(const uint32 diff) { if (!m_creature->isAlive() || !m_creature->GetCharmInfo()) return; Unit* owner = m_creature->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; // First checking if we have some taunt on us Unit* tauntTarget = NULL; const Unit::AuraList& tauntAuras = m_creature->GetAurasByType(SPELL_AURA_MOD_TAUNT); if (!tauntAuras.empty()) { Unit* caster = NULL; // Auras are pushed_back, last caster will be on the end Unit::AuraList::const_iterator aura = tauntAuras.end(); while (aura != tauntAuras.begin()) { --aura; caster = (*aura)->GetCaster(); if (caster && caster->isTargetableForAttack()) { tauntTarget = caster; break; } } if (tauntTarget) DoAttack(tauntTarget, true); } if (m_creature->getVictim() && m_creature->getVictim()->isAlive()) { if (_needToStop()) { _stopAttack(); return; } if (hasMelee) { // Check before attacking to prevent pets from leaving stay position bool attacked = false; if (m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY)) { if (m_creature->GetCharmInfo()->IsCommandAttack() || (m_creature->GetCharmInfo()->IsAtStay() && m_creature->CanReachWithMeleeAttack(m_creature->getVictim()))) attacked = DoMeleeAttackIfReady(); } else attacked = DoMeleeAttackIfReady(); if (attacked && owner) if (Unit* v = m_creature->getVictim()) // Victim may have died between owner->SetInCombatWith(v); } } else { if (m_creature->HasReactState(REACT_AGGRESSIVE) || m_creature->GetCharmInfo()->IsAtStay()) { // Every update we need to check targets only in certain cases // Aggressive - Allow auto select if owner or pet don't have a target // Stay - Only pick from pet or owner targets / attackers so targets won't run by // while chasing our owner. Don't do auto select. // All other cases (ie: defensive) - Targets are assigned by AttackedBy(), OwnerAttackedBy(), OwnerAttacked(), etc. Unit* nextTarget = SelectNextTarget(m_creature->HasReactState(REACT_AGGRESSIVE)); if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else HandleReturnMovement(); } // Autocast (casted only in combat or persistent spells in any state) if (!m_creature->IsNonMeleeSpellCasted(false)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i) { uint32 spellID = m_creature->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(spellID); if (!spellInfo) continue; if (m_creature->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; // check spell cooldown if (m_creature->HasSpellCooldown(spellInfo->Id)) continue; if (IsPositiveSpell(spellInfo->Id)) { if (!IsNonCombatSpell(spellInfo)) // Can be used in combat. { /* Spells handled here: Dash (1850), Dive (23145), Furious Howl (24604), Tainted Blood (19478) Blood Pact (6307), Fire Shield (11771), Sacrifice ... Consume Shadows (17767) */ // Warlock Sacrifice: do not auto cast if not in combat bool castOnlyInCombat = IsSpellHaveEffect(spellInfo, SPELL_EFFECT_INSTAKILL); if (!castOnlyInCombat) { int32 duration = GetSpellDuration(spellInfo); int32 cooldown = GetSpellRecoveryTime(spellInfo); // Keep this spell for when we will be in combat. if (cooldown >= 0 && duration >= 0 && cooldown > duration) castOnlyInCombat = true; } // 19478 - Tainted Blood, rank 1 enUS if (spellInfo->SpellIconID == 153) castOnlyInCombat = true; // 2947 - Fire Shield, rank 1 enUS // When set to auto-cast, the Imp will cast this on any party members within 30 yds if they receive a melee attack. if (spellInfo->IsFitToFamily<SPELLFAMILY_WARLOCK, CF_WARLOCK_IMP_BUFFS>() && spellInfo->SpellVisual == 289) castOnlyInCombat = false; // Furious Howl: in combat only if (IsSpellHaveAura(spellInfo, SPELL_AURA_MOD_DAMAGE_DONE)) castOnlyInCombat = true; if (castOnlyInCombat && !m_creature->getVictim()) continue; } Spell *spell = new Spell(m_creature, spellInfo, false); bool spellUsed = false; // Some spells can target enemy or friendly (DK Ghoul's Leap) // Check for enemy first (pet then owner) Unit* target = m_creature->getAttackerForHelper(); if (!target && owner) target = owner->getAttackerForHelper(); if (target) { if (CanAttack(target) && spell->CanAutoCast(target)) { targetSpellStore.push_back(std::make_pair(target, spell)); spellUsed = true; } } // No enemy, check friendly if (!spellUsed) { for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* ally = m_creature->GetMap()->GetUnit(*tar); //only buff targets that are in combat, unless the spell can only be cast while out of combat if (!ally) continue; if (spell->CanAutoCast(ally)) { targetSpellStore.push_back(std::make_pair(ally, spell)); spellUsed = true; break; } } } // No valid targets at all if (!spellUsed) spell->Delete(); } else if (m_creature->getVictim() && CanAttack(m_creature->getVictim()) && !IsNonCombatSpell(spellInfo)) { Spell *spell = new Spell(m_creature, spellInfo, false); if (spell->CanAutoCast(m_creature->getVictim())) targetSpellStore.push_back(std::make_pair(m_creature->getVictim(), spell)); else spell->Delete(); } } //found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget(target); if (!m_creature->HasInArc(M_PI_F, target)) { m_creature->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)target); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)owner); } if (((Creature*)m_creature)->IsPet()) ((Pet*)m_creature)->CheckLearning(spell->m_spellInfo->Id); // 10% chance to play special pet attack talk, else growl // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell if (((Creature*)m_creature)->IsPet() && (((Pet*)m_creature)->getPetType() == SUMMON_PET) && (m_creature != target) && (urand(0, 100) < 10)) m_creature->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else m_creature->SendPetAIReaction(); spell->prepare(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) itr->second->Delete(); } // Update speed as needed to prevent dropping too far behind and despawning m_creature->UpdateSpeed(MOVE_RUN, true); m_creature->UpdateSpeed(MOVE_WALK, true); }
void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data,8); uint64 guid; uint32 spellId = OPEN_CHEST; const GameObjectInfo *info; recv_data >> guid; sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", guid); GameObject *obj = ObjectAccessor::GetGameObject(*_player, guid); if(!obj) return; //obj->SetUInt32Value(GAMEOBJECT_FLAGS,2); //obj->SetUInt32Value(GAMEOBJECT_FLAGS,2); // default spell caster is player that use GO Unit* spellCaster = GetPlayer(); // default spell target is player that use GO Unit* spellTarget = GetPlayer(); if (Script->GOHello(_player, obj)) return; switch(obj->GetGoType()) { case GAMEOBJECT_TYPE_DOOR: //0 obj->SetUInt32Value(GAMEOBJECT_FLAGS,33); obj->SetUInt32Value(GAMEOBJECT_STATE,0); //open //obj->SetUInt32Value(GAMEOBJECT_TIMESTAMP,0x465EE6D2); //load timestamp obj->SetLootState(GO_CLOSED); obj->SetRespawnTime(5); //close door in 5 seconds return; case GAMEOBJECT_TYPE_BUTTON: //1 obj->SetUInt32Value(GAMEOBJECT_FLAGS,33); obj->SetUInt32Value(GAMEOBJECT_STATE,0); //open obj->SetLootState(GO_CLOSED); obj->SetRespawnTime(2); //close in 1 seconds // activate script sWorld.ScriptsStart(sButtonScripts, obj->GetDBTableGUIDLow(), spellCaster, obj); return; case GAMEOBJECT_TYPE_QUESTGIVER: //2 _player->PrepareQuestMenu( guid ); _player->SendPreparedQuest( guid ); return; //Sitting: Wooden bench, chairs enzz case GAMEOBJECT_TYPE_CHAIR: //7 info = obj->GetGOInfo(); if(info) { //spellId = info->data0; // this is not a spell or offset _player->TeleportTo(obj->GetMapId(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(),false,false); _player->SetFlag(UNIT_FIELD_BYTES_1,PLAYER_STATE_SIT_LOW_CHAIR); // Using (3 + spellId) was wrong, this is a number of slot for chair/bench, not offset _player->SetStandState(PLAYER_STATE_SIT_LOW_CHAIR); return; } break; //big gun, its a spell/aura case GAMEOBJECT_TYPE_GOOBER: //10 info = obj->GetGOInfo(); spellId = info ? info->data10 : 0; break; case GAMEOBJECT_TYPE_SPELLCASTER: //22 obj->SetUInt32Value(GAMEOBJECT_FLAGS,2); info = obj->GetGOInfo(); if(info) { spellId = info->data0; if (spellId == 0) spellId = info->data3; //guid=_player->GetGUID(); } break; case GAMEOBJECT_TYPE_CAMERA: //13 info = obj->GetGOInfo(); if(info) { uint32 cinematic_id = info->data1; if(cinematic_id) { WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4); data << cinematic_id; _player->GetSession()->SendPacket(&data); } return; } break; //fishing bobber case GAMEOBJECT_TYPE_FISHINGNODE: //17 { if(_player->GetGUID() != obj->GetOwnerGUID()) return; switch(obj->getLootState()) { case GO_CLOSED: // ready for loot { // 1) skill must be >= base_zone_skill // 2) if skill == base_zone_skill => 5% chance // 3) chance is linear dependence from (base_zone_skill-skill) int32 skill = _player->GetSkillValue(SKILL_FISHING); int32 zone_skill = _player->FishingMinSkillForCurrentZone(); int32 chance = skill - zone_skill + 5; int32 roll = irand(1,100); DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll); if(skill >= zone_skill && chance >= roll) { // prevent removing GO at spell cancel _player->RemoveGameObject(obj,false); obj->SetOwnerGUID(_player->GetGUID()); //fish catched _player->UpdateFishingSkill(); _player->SendLoot(obj->GetGUID(),LOOT_FISHING); } else { // fish escaped obj->SetLootState(GO_LOOTED); // can be deleted now WorldPacket data(SMSG_FISH_ESCAPED, 0); SendPacket(&data); } break; } case GO_LOOTED: // nothing to do, will be deleted at next update break; default: { obj->SetLootState(GO_LOOTED); WorldPacket data(SMSG_FISH_NOT_HOOKED, 0); SendPacket(&data); break; } } if(_player->m_currentSpells[CURRENT_CHANNELED_SPELL]) { _player->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0); _player->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(); } return; } case GAMEOBJECT_TYPE_SUMMONING_RITUAL: { Unit* caster = obj->GetOwner(); info = obj->GetGOInfo(); if( !caster || caster->GetTypeId()!=TYPEID_PLAYER ) return; // accept only use by player from same group for caster except caster itself if(((Player*)caster)==GetPlayer() || !((Player*)caster)->IsInSameGroupWith(GetPlayer())) return; obj->AddUse(GetPlayer()); // must 2 group members use GO, or only 1 when it is meeting stone summon if(obj->GetUniqueUseCount() < (info->data0 == 2 ? 1 : 2)) return; // in case summoning ritual caster is GO creator spellCaster = caster; if(!caster->m_currentSpells[CURRENT_CHANNELED_SPELL]) return; spellId = info->data1; // finish spell caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0); caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(); // can be deleted now obj->SetLootState(GO_LOOTED); // go to end function to spell casting break; } case GAMEOBJECT_TYPE_MEETINGSTONE: //23 { info = obj->GetGOInfo(); Player* targetPlayer = ObjectAccessor::FindPlayer(((Player*)spellCaster)->GetSelection()); // accept only use by player from same group for caster except caster itself if(!targetPlayer || targetPlayer == GetPlayer() || !targetPlayer->IsInSameGroupWith(GetPlayer())) return; //required lvl checks! uint8 level = _player->getLevel(); if (level < info->data0 || level > info->data1) return; level = targetPlayer->getLevel(); if (level < info->data0 || level > info->data1) return; spellId = 23598; break; } case GAMEOBJECT_TYPE_FLAGSTAND: // 24 if(_player->InBattleGround() && // in battleground !_player->IsMounted() && // not mounted !_player->HasStealthAura() && // not stealthed !_player->HasInvisibilityAura() && // not invisible _player->isAlive()) // live player { BattleGround *bg = _player->GetBattleGround(); if(!bg) return; // BG flag click // AB: // 15001 // 15002 // 15003 // 15004 // 15005 // WS: // 179830 - Silverwing Flag // 179831 - Warsong Flag // EotS: // 184141 - Netherstorm Flag info = obj->GetGOInfo(); if(info) { switch(info->id) { case 179830: // check if it's correct bg if(bg->GetTypeID() != BATTLEGROUND_WS) return; // check if flag dropped if(((BattleGroundWS*)bg)->GetFlagState(ALLIANCE) != BG_WS_FLAG_STATE_ON_BASE) return; // check if it's correct flag if(((BattleGroundWS*)bg)->m_bgobjects[BG_WS_OBJECT_A_FLAG] != obj->GetGUID()) return; // check player team if(_player->GetTeam() == ALLIANCE) return; spellId = 23335; // Silverwing Flag break; case 179831: // check if it's correct bg if(bg->GetTypeID() != BATTLEGROUND_WS) return; // check if flag dropped if(((BattleGroundWS*)bg)->GetFlagState(HORDE) != BG_WS_FLAG_STATE_ON_BASE) return; // check if it's correct flag if(((BattleGroundWS*)bg)->m_bgobjects[BG_WS_OBJECT_H_FLAG] != obj->GetGUID()) return; // check player team if(_player->GetTeam() == HORDE) return; spellId = 23333; // Warsong Flag break; case 184141: // check if it's correct bg if(bg->GetTypeID() != BATTLEGROUND_EY) return; spellId = 34976; // Netherstorm Flag break; } } } break; case GAMEOBJECT_TYPE_FLAGDROP: // 26 if(_player->InBattleGround() && // in battleground !_player->IsMounted() && // not mounted !_player->HasStealthAura() && // not stealthed !_player->HasInvisibilityAura() && // not invisible _player->isAlive()) // live player { BattleGround *bg = _player->GetBattleGround(); if(!bg) return; // BG flag dropped // WS: // 179785 - Silverwing Flag // 179786 - Warsong Flag // EotS: // 184142 - Netherstorm Flag info = obj->GetGOInfo(); if(info) { switch(info->id) { case 179785: // Silverwing Flag // check if it's correct bg if(bg->GetTypeID() != BATTLEGROUND_WS) return; // check if flag dropped if(((BattleGroundWS*)bg)->GetFlagState(ALLIANCE) != BG_WS_FLAG_STATE_ON_GROUND) return; obj->Delete(); if(_player->GetTeam() == ALLIANCE) { ((BattleGroundWS*)bg)->EventPlayerReturnedFlag(_player); return; } else { _player->CastSpell(_player, 23335, true); return; } break; case 179786: // Warsong Flag // check if it's correct bg if(bg->GetTypeID() != BATTLEGROUND_WS) return; // check if flag dropped if(((BattleGroundWS*)bg)->GetFlagState(HORDE) != BG_WS_FLAG_STATE_ON_GROUND) return; obj->Delete(); if(_player->GetTeam() == HORDE) { ((BattleGroundWS*)bg)->EventPlayerReturnedFlag(_player); return; } else { _player->CastSpell(_player, 23333, true); return; } break; } } obj->Delete(); } break; default: sLog.outDebug("Unknown Object Type %u\n", obj->GetGoType()); break; } if (!spellId) return; SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId ); if(!spellInfo) { sLog.outError("WORLD: unknown spell id %u\n", spellId); return; } Spell *spell = new Spell(spellCaster, spellInfo, false, 0); SpellCastTargets targets; targets.setUnitTarget( spellTarget ); if(obj) targets.setGOTarget( obj ); spell->prepare(&targets); }
void GameObject::Use(Unit* user) { // by default spell caster is user Unit* spellCaster = user; uint32 spellId = 0; switch(GetGoType()) { case GAMEOBJECT_TYPE_DOOR: //0 case GAMEOBJECT_TYPE_BUTTON: //1 //doors/buttons never really despawn, only reset to default state/flags UseDoorOrButton(); // activate script sWorld.ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this); return; case GAMEOBJECT_TYPE_QUESTGIVER: //2 { if(user->GetTypeId()!=TYPEID_PLAYER) return; Player* player = (Player*)user; player->PrepareQuestMenu( GetGUID() ); player->SendPreparedQuest( GetGUID() ); return; } //Sitting: Wooden bench, chairs enzz case GAMEOBJECT_TYPE_CHAIR: //7 { GameObjectInfo const* info = GetGOInfo(); if(!info) return; if(user->GetTypeId()!=TYPEID_PLAYER) return; Player* player = (Player*)user; // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one // check if the db is sane if(info->chair.slots > 0) { float lowestDist = DEFAULT_VISIBILITY_DISTANCE; float x_lowest = GetPositionX(); float y_lowest = GetPositionY(); // the object orientation + 1/2 pi // every slot will be on that straight line float orthogonalOrientation = GetOrientation()+M_PI*0.5f; // find nearest slot for(uint32 i=0; i<info->chair.slots; i++) { // the distance between this slot and the center of the go - imagine a 1D space float relativeDistance = (info->size*i)-(info->size*(info->chair.slots-1)/2.0f); float x_i = GetPositionX() + relativeDistance * cos(orthogonalOrientation); float y_i = GetPositionY() + relativeDistance * sin(orthogonalOrientation); // calculate the distance between the player and this slot float thisDistance = player->GetDistance2d(x_i, y_i); /* debug code. It will spawn a npc on each slot to visualize them. Creature* helper = player->SummonCreature(14496, x_i, y_i, GetPositionZ(), GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10000); std::ostringstream output; output << i << ": thisDist: " << thisDistance; helper->MonsterSay(output.str().c_str(), LANG_UNIVERSAL, 0); */ if(thisDistance <= lowestDist) { lowestDist = thisDistance; x_lowest = x_i; y_lowest = y_i; } } player->TeleportTo(GetMapId(), x_lowest, y_lowest, GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET); } else { // fallback, will always work player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET); } player->SetStandState(PLAYER_STATE_SIT_LOW_CHAIR+info->chair.height); return; } //big gun, its a spell/aura case GAMEOBJECT_TYPE_GOOBER: //10 { GameObjectInfo const* info = GetGOInfo(); if(user->GetTypeId()==TYPEID_PLAYER) { Player* player = (Player*)user; // show page if(info->goober.pageId) { WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8); data << GetGUID(); player->GetSession()->SendPacket(&data); } // possible quest objective for active quests player->CastedCreatureOrGO(info->id, GetGUID(), 0); } // cast this spell later if provided spellId = info->goober.spellId; break; } case GAMEOBJECT_TYPE_CAMERA: //13 { GameObjectInfo const* info = GetGOInfo(); if(!info) return; if(user->GetTypeId()!=TYPEID_PLAYER) return; Player* player = (Player*)user; if(info->camera.cinematicId) { WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4); data << info->camera.cinematicId; player->GetSession()->SendPacket(&data); } return; } //fishing bobber case GAMEOBJECT_TYPE_FISHINGNODE: //17 { if(user->GetTypeId()!=TYPEID_PLAYER) return; Player* player = (Player*)user; if(player->GetGUID() != GetOwnerGUID()) return; switch(getLootState()) { case GO_READY: // ready for loot { // 1) skill must be >= base_zone_skill // 2) if skill == base_zone_skill => 5% chance // 3) chance is linear dependence from (base_zone_skill-skill) uint32 subzone = GetAreaId(); int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone ); if(!zone_skill) zone_skill = objmgr.GetFishingBaseSkillLevel( GetZoneId() ); //provide error, no fishable zone or area should be 0 if(!zone_skill) sLog.outErrorDb("Fishable areaId %u are not properly defined in `skill_fishing_base_level`.",subzone); int32 skill = player->GetSkillValue(SKILL_FISHING); int32 chance = skill - zone_skill + 5; int32 roll = irand(1,100); DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll); if(skill >= zone_skill && chance >= roll) { // prevent removing GO at spell cancel player->RemoveGameObject(this,false); SetOwnerGUID(player->GetGUID()); //fish catched player->UpdateFishingSkill(); GameObject* ok = LookupFishingHoleAround(DEFAULT_VISIBILITY_DISTANCE); if (ok) { player->SendLoot(ok->GetGUID(),LOOT_FISHINGHOLE); SetLootState(GO_JUST_DEACTIVATED); } else player->SendLoot(GetGUID(),LOOT_FISHING); } else { // fish escaped, can be deleted now SetLootState(GO_JUST_DEACTIVATED); WorldPacket data(SMSG_FISH_ESCAPED, 0); player->GetSession()->SendPacket(&data); } break; } case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update break; default: { SetLootState(GO_JUST_DEACTIVATED); WorldPacket data(SMSG_FISH_NOT_HOOKED, 0); player->GetSession()->SendPacket(&data); break; } } if(player->m_currentSpells[CURRENT_CHANNELED_SPELL]) { player->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0); player->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(); } return; } case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18 { if(user->GetTypeId()!=TYPEID_PLAYER) return; Player* player = (Player*)user; Unit* caster = GetOwner(); GameObjectInfo const* info = GetGOInfo(); if( !caster || caster->GetTypeId()!=TYPEID_PLAYER ) return; // accept only use by player from same group for caster except caster itself if(((Player*)caster)==player || !((Player*)caster)->IsInSameRaidWith(player)) return; AddUniqueUse(player); // full amount unique participants including original summoner if(GetUniqueUseCount() < info->summoningRitual.reqParticipants) return; // in case summoning ritual caster is GO creator spellCaster = caster; if(!caster->m_currentSpells[CURRENT_CHANNELED_SPELL]) return; spellId = info->summoningRitual.spellId; // finish spell caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0); caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(); // can be deleted now SetLootState(GO_JUST_DEACTIVATED); // go to end function to spell casting break; } case GAMEOBJECT_TYPE_SPELLCASTER: //22 { SetUInt32Value(GAMEOBJECT_FLAGS,2); GameObjectInfo const* info = GetGOInfo(); if(!info) return; if(info->spellcaster.partyOnly) { Unit* caster = GetOwner(); if( !caster || caster->GetTypeId()!=TYPEID_PLAYER ) return; if(user->GetTypeId()!=TYPEID_PLAYER || !((Player*)user)->IsInSameRaidWith((Player*)caster)) return; } spellId = info->spellcaster.spellId; AddUse(); break; } case GAMEOBJECT_TYPE_MEETINGSTONE: //23 { GameObjectInfo const* info = GetGOInfo(); if(user->GetTypeId()!=TYPEID_PLAYER) return; Player* player = (Player*)user; Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetSelection()); // accept only use by player from same group for caster except caster itself if(!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameGroupWith(player)) return; //required lvl checks! uint8 level = player->getLevel(); if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel) return; level = targetPlayer->getLevel(); if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel) return; spellId = 23598; break; } case GAMEOBJECT_TYPE_FLAGSTAND: // 24 { if(user->GetTypeId()!=TYPEID_PLAYER) return; Player* player = (Player*)user; if( player->isAllowUseBattleGroundObject() ) { // in battleground check BattleGround *bg = player->GetBattleGround(); if(!bg) return; // BG flag click // AB: // 15001 // 15002 // 15003 // 15004 // 15005 bg->EventPlayerClickedOnFlag(player, this); return; //we don;t need to delete flag ... it is despawned! } break; } case GAMEOBJECT_TYPE_FLAGDROP: // 26 { if(user->GetTypeId()!=TYPEID_PLAYER) return; Player* player = (Player*)user; if( player->isAllowUseBattleGroundObject() ) { // in battleground check BattleGround *bg = player->GetBattleGround(); if(!bg) return; // BG flag dropped // WS: // 179785 - Silverwing Flag // 179786 - Warsong Flag // EotS: // 184142 - Netherstorm Flag GameObjectInfo const* info = GetGOInfo(); if(info) { switch(info->id) { case 179785: // Silverwing Flag // check if it's correct bg if(bg->GetTypeID() == BATTLEGROUND_WS) bg->EventPlayerClickedOnFlag(player, this); break; case 179786: // Warsong Flag if(bg->GetTypeID() == BATTLEGROUND_WS) bg->EventPlayerClickedOnFlag(player, this); break; case 184142: // Netherstorm Flag if(bg->GetTypeID() == BATTLEGROUND_EY) bg->EventPlayerClickedOnFlag(player, this); break; } } //this cause to call return, all flags must be deleted here!! spellId = 0; Delete(); } break; } case GAMEOBJECT_TYPE_BARBER_CHAIR: //32 { GameObjectInfo const* info = GetGOInfo(); if(!info) return; if(user->GetTypeId()!=TYPEID_PLAYER) return; Player* player = (Player*)user; // fallback, will always work player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET); WorldPacket data(SMSG_ENABLE_BARBER_SHOP, 0); player->GetSession()->SendPacket(&data); player->SetStandState(PLAYER_STATE_SIT_LOW_CHAIR+info->barberChair.chairheight); return; } default: sLog.outDebug("Unknown Object Type %u", GetGoType()); break; } if(!spellId) return; SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId ); if(!spellInfo) { sLog.outError("WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u )", spellId,GetEntry(),GetGoType()); return; } Spell *spell = new Spell(spellCaster, spellInfo, false); // spell target is user of GO SpellCastTargets targets; targets.setUnitTarget( user ); spell->prepare(&targets); }
void PetAI::UpdateAI(const uint32 diff) { if (!m_unit->isAlive()) return; Creature* creature = (m_unit->GetTypeId() == TYPEID_UNIT) ? static_cast<Creature*>(m_unit) : nullptr; Pet* pet = (creature && creature->IsPet()) ? static_cast<Pet*>(m_unit) : nullptr; Unit* owner = m_unit->GetMaster(); if (!owner) return; Unit* victim = (pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) ? nullptr : m_unit->getVictim(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (inCombat && !victim) { m_unit->AttackStop(true, true); inCombat = false; } CharmInfo* charminfo = m_unit->GetCharmInfo(); MANGOS_ASSERT(charminfo); if (charminfo->GetIsRetreating()) { if (!owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2))) { if (!m_unit->hasUnitState(UNIT_STAT_FOLLOW)) m_unit->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); return; } else charminfo->SetIsRetreating(); } else if (charminfo->GetSpellOpener() != 0) // have opener stored { uint32 minRange = charminfo->GetSpellOpenerMinRange(); if (!(victim = m_unit->getVictim()) || (minRange != 0 && m_unit->IsWithinDistInMap(victim, minRange))) charminfo->SetSpellOpener(); else if (m_unit->IsWithinDistInMap(victim, charminfo->GetSpellOpenerMaxRange()) && m_unit->IsWithinLOSInMap(victim)) { // stop moving m_unit->clearUnitState(UNIT_STAT_MOVING); // auto turn to target m_unit->SetInFront(victim); if (victim->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)victim); if (owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); uint32 spell_id = charminfo->GetSpellOpener(); SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spell_id); Spell* spell = new Spell(m_unit, spellInfo, false); SpellCastResult result = spell->CheckPetCast(victim); if (result == SPELL_CAST_OK) spell->SpellStart(&(spell->m_targets)); else delete spell; charminfo->SetSpellOpener(); } else return; } // Auto cast (casted only in combat or persistent spells in any state) else if (!m_unit->IsNonMeleeSpellCasted(false)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; if (pet) { for (uint8 i = 0; i < pet->GetPetAutoSpellSize(); ++i) { uint32 spellID = pet->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellID); if (!spellInfo) continue; if (!m_unit->IsSpellReady(*spellInfo)) continue; // ignore some combinations of combat state and combat/non combat spells if (!inCombat) { // ignore attacking spells, and allow only self/around spells if (!IsPositiveSpell(spellInfo->Id)) continue; // non combat spells allowed // only pet spells have IsNonCombatSpell and not fit this requirements: // Consume Shadows, Lesser Invisibility, so ignore checks for its if (!IsNonCombatSpell(spellInfo)) { int32 duration = GetSpellDuration(spellInfo); int32 cooldown = GetSpellRecoveryTime(spellInfo); // allow only spell not on cooldown if (cooldown != 0 && duration < cooldown) continue; // not allow instant kill auto casts as full health cost if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_INSTAKILL)) continue; } } // just ignore non-combat spells else if (IsNonCombatSpell(spellInfo)) continue; Spell* spell = new Spell(m_unit, spellInfo, false); if (inCombat && !m_unit->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(victim)) { targetSpellStore.push_back(TargetSpellList::value_type(victim, spell)); continue; } else { bool spellUsed = false; for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* Target = m_unit->GetMap()->GetUnit(*tar); // only buff targets that are in combat, unless the spell can only be cast while out of combat if (!Target) continue; if (spell->CanAutoCast(Target)) { targetSpellStore.push_back(TargetSpellList::value_type(Target, spell)); spellUsed = true; break; } } if (!spellUsed) delete spell; } } } // found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget(target); if (!m_unit->HasInArc(M_PI_F, target)) { m_unit->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)target); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); } spell->SpellStart(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } // Stop here if casting spell (No melee and no movement) if (m_unit->IsNonMeleeSpellCasted(false)) return; // we may get our actions disabled during spell casting, so do entire recheck for victim victim = (pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) ? nullptr : m_unit->getVictim(); if (victim) { // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. // This is needed for charmed creatures, as once their target was reset other effects can trigger threat if (!victim->isTargetableForAttack()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_unit->GetGUIDLow()); m_unit->CombatStop(); inCombat = false; return; } // if pet misses its target, it will also be the first in threat list if ((!creature || !(creature->GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_NO_MELEE)) && m_unit->CanReachWithMeleeAttack(victim)) { if (!m_unit->HasInArc(2 * M_PI_F / 3, victim)) { m_unit->SetInFront(victim); if (victim->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)victim); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); } DoMeleeAttackIfReady(); } else if (!m_unit->hasUnitState(UNIT_STAT_MOVING)) AttackStart(victim); } else if (owner) { CharmInfo* charmInfo = m_unit->GetCharmInfo(); if (owner->isInCombat() && !(charmInfo && charmInfo->HasReactState(REACT_PASSIVE))) AttackStart(owner->getAttackerForHelper()); else { if (charmInfo && charmInfo->HasCommandState(COMMAND_STAY)) { //if stay command is set but we don't have stay pos set then we need to establish current pos as stay position if (!charminfo->IsStayPosSet()) charminfo->SetStayPosition(true); float stayPosX = charminfo->GetStayPosX(); float stayPosY = charminfo->GetStayPosY(); float stayPosZ = charminfo->GetStayPosZ(); if (m_unit->GetPositionX() == stayPosX && m_unit->GetPositionY() == stayPosY && m_unit->GetPositionZ() == stayPosZ) { float StayPosO = charminfo->GetStayPosO(); if (m_unit->hasUnitState(UNIT_STAT_MOVING)) { m_unit->GetMotionMaster()->Clear(false); m_unit->GetMotionMaster()->MoveIdle(); } else if (m_unit->GetOrientation() != StayPosO) m_unit->SetOrientation(StayPosO); } else m_unit->GetMotionMaster()->MovePoint(0, stayPosX, stayPosY, stayPosZ, false); } else if (m_unit->hasUnitState(UNIT_STAT_FOLLOW)) { if (owner->IsWithinDistInMap(m_unit, PET_FOLLOW_DIST)) { m_unit->GetMotionMaster()->Clear(false); m_unit->GetMotionMaster()->MoveIdle(); } } else if (charmInfo && charmInfo->HasCommandState(COMMAND_FOLLOW) && !owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2))) m_unit->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); } } }
void WorldSession::HandlePetAction( WorldPacket & recv_data ) { uint64 guid1; uint16 spellid; uint16 flag; uint64 guid2; WorldPacket data; recv_data >> guid1; //pet guid recv_data >> spellid; recv_data >> flag; //delete = 0x0700 CastSpell = C100 recv_data >> guid2; //tag guid Creature* pet=ObjectAccessor::Instance().GetCreature(*_player,guid1); sLog.outString( "HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.\n", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2)) ); if(!pet) { sLog.outError( "Pet %u not exist.\n", uint32(GUID_LOPART(guid1)) ); return; } if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) { sLog.outError( "HandlePetAction.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid1)),GetPlayer()->GetName() ); return; } switch(flag) { case 1792: //0x0700 switch(spellid) { case 0x0000: //flat=1792 //STAY pet->StopMoving(); (*pet)->Idle(); if(pet->isPet()) { ((Pet*)pet)->AddActState( STATE_RA_STAY ); ((Pet*)pet)->ClearActState( STATE_RA_FOLLOW ); } break; case 0x0001: //spellid=1792 //FOLLOW pet->AttackStop(); pet->addUnitState(UNIT_STAT_FOLLOW); (*pet)->Mutate(new TargetedMovementGenerator(*_player)); if(pet->isPet()) { ((Pet*)pet)->AddActState( STATE_RA_FOLLOW ); ((Pet*)pet)->ClearActState( STATE_RA_STAY ); } break; case 0x0002: //spellid=1792 //ATTACK { pet->clearUnitState(UNIT_STAT_FOLLOW); uint64 selguid = _player->GetSelection(); Unit *TargetUnit = ObjectAccessor::Instance().GetUnit(*_player, selguid); if(TargetUnit == NULL) return; // not let attack friendly units. if( GetPlayer()->IsFriendlyTo(TargetUnit)) return; if(TargetUnit!=pet->getVictim()) pet->AttackStop(); pet->AI().AttackStart(TargetUnit); data.Initialize(SMSG_AI_REACTION); data << guid1 << uint32(00000002); SendPacket(&data); break; } case 3: _player->UnsummonPet(pet); break; default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); } break; case 1536: switch(spellid) { case 0: //passive if(pet->isPet()) { ((Pet*)pet)->AddActState( STATE_RA_PASSIVE ); ((Pet*)pet)->ClearActState( STATE_RA_PROACTIVE | STATE_RA_PROACTIVE ); } break; case 1: //recovery if(pet->isPet()) { ((Pet*)pet)->AddActState( STATE_RA_REACTIVE ); ((Pet*)pet)->ClearActState( STATE_RA_PASSIVE | STATE_RA_PROACTIVE ); } break; case 2: //activete if(pet->isPet()) { ((Pet*)pet)->AddActState( STATE_RA_PROACTIVE ); ((Pet*)pet)->ClearActState( STATE_RA_PASSIVE | STATE_RA_REACTIVE ); } break; } break; case 49408: //0xc100 spell { uint64 selectguid = _player->GetSelection(); Unit* unit_target=ObjectAccessor::Instance().GetUnit(*_player,selectguid); if(!unit_target) return; // do not spell attack of friends if(_player->IsFriendlyTo(unit_target)) return; pet->clearUnitState(UNIT_STAT_FOLLOW); SpellEntry *spellInfo = sSpellStore.LookupEntry(spellid ); if(!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i\n", spellid); return; } Spell *spell = new Spell(pet, spellInfo, false, 0); WPAssert(spell); SpellCastTargets targets; targets.setUnitTarget( unit_target ); spell->prepare(&targets); break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); } }
void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recv_data) { ObjectGuid guid; uint32 spellId = 0; recv_data >> guid >> spellId; DEBUG_LOG("WORLD: Received opcode CMSG_TRAINER_BUY_SPELL Trainer: %s, learn spell id is: %u", guid.GetString().c_str(), spellId); Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER); if (!unit) { DEBUG_LOG("WORLD: HandleTrainerBuySpellOpcode - %s not found or you can't interact with him.", guid.GetString().c_str()); return; } // remove fake death if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); if (!unit->IsTrainerOf(_player, true)) return; // check present spell in trainer spell list TrainerSpellData const* cSpells = unit->GetTrainerSpells(); TrainerSpellData const* tSpells = unit->GetTrainerTemplateSpells(); if (!cSpells && !tSpells) return; // Try find spell in npc_trainer TrainerSpell const* trainer_spell = cSpells ? cSpells->Find(spellId) : NULL; // Not found, try find in npc_trainer_template if (!trainer_spell && tSpells) trainer_spell = tSpells->Find(spellId); // Not found anywhere, cheating? if (!trainer_spell) return; // can't be learn, cheat? Or double learn with lags... uint32 reqLevel = 0; if(!_player->IsSpellFitByClassAndRace(trainer_spell->spell, &reqLevel)) return; reqLevel = trainer_spell->isProvidedReqLevel ? trainer_spell->reqLevel : std::max(reqLevel, trainer_spell->reqLevel); if (_player->GetTrainerSpellState(trainer_spell, reqLevel) != TRAINER_SPELL_GREEN) return; SpellEntry const* proto = sSpellStore.LookupEntry(trainer_spell->spell); SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->EffectTriggerSpell[0]); // apply reputation discount uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit))); // check money requirement if (_player->GetMoney() < nSpellCost) return; _player->ModifyMoney(-int32(nSpellCost)); SendPlaySpellVisual(guid, 0xB3); // visual effect on trainer WorldPacket data(SMSG_PLAY_SPELL_IMPACT, 8 + 4); // visual effect on player data << _player->GetObjectGuid(); data << uint32(0x016A); // index from SpellVisualKit.dbc SendPacket(&data); // learn explicitly to prevent lost money at lags, learning spell will be only show spell animation //[-ZERO] _player->learnSpell(trainer_spell->spell, false); data.Initialize(SMSG_TRAINER_BUY_SUCCEEDED, 12); data << ObjectGuid(guid); data << uint32(spellId); // should be same as in packet from client SendPacket(&data); Spell* spell; if (proto->SpellVisual == 222) spell = new Spell(_player, proto, false); else spell = new Spell(unit, proto, false); SpellCastTargets targets; targets.setUnitTarget(_player); spell->prepare(&targets); }
void PetAI::UpdateAI(const uint32 diff) { if (!m_creature->isAlive()) return; Unit* owner = m_creature->GetCharmerOrOwner(); Unit* victim = m_creature->getVictim(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (inCombat && (!victim || (m_creature->IsPet() && ((Pet*)m_creature)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS))) _stopAttack(); if (((Pet*)m_creature)->GetIsRetreating()) { if (!owner->_IsWithinDist(m_creature, (PET_FOLLOW_DIST * 2), true)) { if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW)) m_creature->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); return; } else ((Pet*)m_creature)->SetIsRetreating(); } else if (((Pet*)m_creature)->GetSpellOpener() != 0) // have opener stored { uint32 minRange = ((Pet*)m_creature)->GetSpellOpenerMinRange(); if (!(victim = m_creature->getVictim()) || (minRange != 0 && m_creature->IsWithinDistInMap(victim, minRange))) ((Pet*)m_creature)->SetSpellOpener(); else if (m_creature->IsWithinDistInMap(victim, ((Pet*)m_creature)->GetSpellOpenerMaxRange()) && m_creature->IsWithinLOSInMap(victim)) { // stop moving m_creature->clearUnitState(UNIT_STAT_MOVING); // auto turn to target m_creature->SetInFront(victim); if (victim->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)victim); if (owner->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)owner); uint32 spell_id = ((Pet*)m_creature)->GetSpellOpener(); SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id); Spell* spell = new Spell(m_creature, spellInfo, false); SpellCastResult result = spell->CheckPetCast(victim); if (result == SPELL_CAST_OK) { m_creature->AddCreatureSpellCooldown(spell_id); spell->SpellStart(&(spell->m_targets)); } else delete spell; ((Pet*)m_creature)->SetSpellOpener(); } else return; } // Autocast (casted only in combat or persistent spells in any state) else if (!m_creature->IsNonMeleeSpellCasted(false)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i) { uint32 spellID = m_creature->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellID); if (!spellInfo) continue; if (m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; // ignore some combinations of combat state and combat/noncombat spells if (!inCombat) { // ignore attacking spells, and allow only self/around spells if (!IsPositiveSpell(spellInfo->Id)) continue; // non combat spells allowed // only pet spells have IsNonCombatSpell and not fit this reqs: // Consume Shadows, Lesser Invisibility, so ignore checks for its if (!IsNonCombatSpell(spellInfo)) { int32 duration = GetSpellDuration(spellInfo); int32 cooldown = GetSpellRecoveryTime(spellInfo); // allow only spell not on cooldown if (cooldown != 0 && duration < cooldown) continue; // not allow instant kill autocasts as full health cost if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_INSTAKILL)) continue; } } // just ignore non-combat spells else if (IsNonCombatSpell(spellInfo)) continue; Spell* spell = new Spell(m_creature, spellInfo, false); if (inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim())) { targetSpellStore.push_back(TargetSpellList::value_type(m_creature->getVictim(), spell)); continue; } else { bool spellUsed = false; for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* Target = m_creature->GetMap()->GetUnit(*tar); // only buff targets that are in combat, unless the spell can only be cast while out of combat if (!Target) continue; if (spell->CanAutoCast(Target)) { targetSpellStore.push_back(TargetSpellList::value_type(Target, spell)); spellUsed = true; break; } } if (!spellUsed) delete spell; } } // found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget(target); if (!m_creature->HasInArc(M_PI_F, target)) { m_creature->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)target); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)owner); } m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id); if (m_creature->IsPet()) ((Pet*)m_creature)->CheckLearning(spell->m_spellInfo->Id); spell->SpellStart(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } else if (m_creature->hasUnitState(UNIT_STAT_FOLLOW_MOVE)) m_creature->InterruptNonMeleeSpells(false); if (victim = m_creature->getVictim()) { // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. if (_needToStop()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_creature->GetGUIDLow()); _stopAttack(); return; } // required to be stopped cases if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false)) { if (m_creature->hasUnitState(UNIT_STAT_FOLLOW_MOVE)) m_creature->InterruptNonMeleeSpells(false); } else if (m_creature->CanReachWithMeleeAttack(victim)) { if (DoMeleeAttackIfReady()) // if pet misses its target, it will also be the first in threat list victim->AddThreat(m_creature); else AttackStart(victim); } } else if (owner && m_creature->GetCharmInfo()) { if (owner->isInCombat() && !(m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY))) { AttackStart(owner->getAttackerForHelper()); } else if (m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) { // The distance is to prevent the pet from running around to reach the owners back when walking towards it // and the reason for increasing it more than the follow distance is to prevent the same thing // from happening when the owner turns and twists (as this increases the distance between them) if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW) && !owner->IsWithinDistInMap(m_creature, (PET_FOLLOW_DIST * 2))) m_creature->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); // This is to stop the pet from following you when you're close to each other, to support the above condition. else if (m_creature->hasUnitState(UNIT_STAT_FOLLOW)) { m_creature->GetMotionMaster()->Clear(false); m_creature->GetMotionMaster()->MoveIdle(); } } } }
void PetAI::UpdateAI(const uint32 diff) { if (!m_creature->isAlive()) return; Unit* owner = m_creature->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (inCombat && (!m_creature->getVictim() || (m_creature->IsPet() && ((Pet*)m_creature)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS))) _stopAttack(); // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. if (m_creature->getVictim()) { if (_needToStop()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_creature->GetGUIDLow()); _stopAttack(); return; } bool meleeReach = m_creature->CanReachWithMeleeAttack(m_creature->getVictim()); if (m_creature->IsStopped() || meleeReach) { // required to be stopped cases if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false)) { if (m_creature->hasUnitState(UNIT_STAT_FOLLOW_MOVE)) m_creature->InterruptNonMeleeSpells(false); else return; } // not required to be stopped case else if (DoMeleeAttackIfReady()) { if (!m_creature->getVictim()) return; // if pet misses its target, it will also be the first in threat list m_creature->getVictim()->AddThreat(m_creature); if (_needToStop()) _stopAttack(); } } } else if (owner && m_creature->GetCharmInfo()) { if (owner->isInCombat() && !(m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY))) { AttackStart(owner->getAttackerForHelper()); } else if (m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) { if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW)) { m_creature->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); } } } // Autocast (casted only in combat or persistent spells in any state) if (!m_creature->IsNonMeleeSpellCasted(false)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i) { uint32 spellID = m_creature->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellID); if (!spellInfo) continue; if (m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; // ignore some combinations of combat state and combat/noncombat spells if (!inCombat) { // ignore attacking spells, and allow only self/around spells if (!IsPositiveSpell(spellInfo->Id)) continue; // non combat spells allowed // only pet spells have IsNonCombatSpell and not fit this reqs: // Consume Shadows, Lesser Invisibility, so ignore checks for its if (!IsNonCombatSpell(spellInfo)) { // allow only spell without spell cost or with spell cost but not duration limit int32 duration = GetSpellDuration(spellInfo); SpellPowerEntry const* spellPower = spellInfo->GetSpellPower(); if (spellPower && (spellPower->manaCost || spellPower->ManaCostPercentage || spellPower->manaPerSecond) && duration > 0) continue; // allow only spell without cooldown > duration int32 cooldown = GetSpellRecoveryTime(spellInfo); if (cooldown >= 0 && duration >= 0 && cooldown > duration) continue; } } else { // just ignore non-combat spells if (IsNonCombatSpell(spellInfo)) continue; } Spell* spell = new Spell(m_creature, spellInfo, false); if (inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim())) { targetSpellStore.push_back(TargetSpellList::value_type(m_creature->getVictim(), spell)); continue; } else { bool spellUsed = false; for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* Target = m_creature->GetMap()->GetUnit(*tar); // only buff targets that are in combat, unless the spell can only be cast while out of combat if (!Target) continue; if (spell->CanAutoCast(Target)) { targetSpellStore.push_back(TargetSpellList::value_type(Target, spell)); spellUsed = true; break; } } if (!spellUsed) delete spell; } } // found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget(target); if (!m_creature->HasInArc(M_PI_F, target)) { m_creature->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)target); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_creature->SendCreateUpdateToPlayer((Player*)owner); } m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id); spell->prepare(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } }
void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) { uint32 spellId, glyphIndex; uint8 cast_count, cast_flags; recvPacket >> cast_count; recvPacket >> spellId >> glyphIndex; recvPacket >> cast_flags; // flags (if 0x02 - some additional data are received) // ignore for remote control state (for player case) Unit* _mover = GetPlayer()->GetMover(); if (_mover != GetPlayer() && _mover->GetTypeId()==TYPEID_PLAYER) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } DEBUG_LOG("WORLD: got cast spell packet, spellId - %u, cast_count: %u, cast_flags %u, data length = " SIZEFMTD, spellId, cast_count, cast_flags, recvPacket.size()); /* process anticheat check */ if (!GetPlayer()->GetAntiCheat()->DoAntiCheatCheck(CHECK_SPELL, spellId, CMSG_CAST_SPELL)) return; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); if(!spellInfo) { sLog.outError("WORLD: unknown spell id %u", spellId); recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } // Players on vehicles may cast many simple spells (like knock) from self Unit* mover = NULL; if (spellInfo->HasAttribute(SPELL_ATTR_EX6_CASTABLE_ON_VEHICLE) && _mover->IsCharmerOrOwnerPlayerOrPlayerItself()) mover = _mover->GetCharmerOrOwnerPlayerOrPlayerItself(); else mover = _mover; // casting own spells on some vehicles if (Player* plr = mover->GetCharmerOrOwnerPlayerOrPlayerItself()) { if (mover->IsVehicle() && (mover != plr)) { if (VehicleKitPtr vehicle = mover->GetVehicleKit()) { if (VehicleSeatEntry const* seatInfo = vehicle->GetSeatInfo(plr)) if (seatInfo->m_flags & (SEAT_FLAG_CAN_ATTACK | SEAT_FLAG_CAN_CAST)) mover = plr; } } } bool triggered = false; SpellEntry const* triggeredBy = NULL; Aura const* triggeredByAura = mover->GetTriggeredByClientAura(spellId); if (triggeredByAura) { triggered = true; triggeredBy = triggeredByAura->GetSpellProto(); cast_count = 0; } Unit::AuraList const& m363auras = mover->GetAurasByType(SPELL_AURA_363); for (Unit::AuraList::const_iterator itr = m363auras.begin(); itr != m363auras.end(); ++itr) { if ((*itr)->GetSpellEffect()->EffectTriggerSpell == spellId) { triggered = true; mover->RemoveSpellAuraHolder((*itr)->GetHolder()); break; } } if (!spellInfo->HasAttribute(SPELL_ATTR_EX8_RAID_MARKER)) { if (mover->GetTypeId()==TYPEID_PLAYER) { // not have spell in spellbook or spell passive and not casted by client if ((!((Player*)mover)->HasActiveSpell(spellId) && !triggered || IsPassiveSpell(spellInfo)) && !sSpellMgr.IsAbilityOfSkillType(spellInfo, SKILL_ARCHAEOLOGY)) { sLog.outError("WorldSession::HandleCastSpellOpcode: %s casts spell %u which he shouldn't have", mover->GetObjectGuid().GetString().c_str(), spellId); //cheater? kick? ban? recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } } else { // not have spell in spellbook or spell passive and not casted by client if (!((Creature*)mover)->HasSpell(spellId) && !triggered || IsPassiveSpell(spellInfo)) { sLog.outError("World: Player %u casts spell %u which he shouldn't have", mover->GetGUIDLow(), spellId); //cheater? kick? ban? recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } } } Unit::AuraList swaps = _mover->GetAurasByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS); Unit::AuraList const& swaps2 = _mover->GetAurasByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_2); if (!swaps2.empty()) swaps.insert(swaps.end(), swaps2.begin(), swaps2.end()); for (Unit::AuraList::const_iterator itr = swaps.begin(); itr != swaps.end(); ++itr) { if ((*itr)->isAffectedOnSpell(spellInfo)) { if (SpellEntry const* newInfo = sSpellStore.LookupEntry((*itr)->GetModifier()->m_amount)) { spellInfo = newInfo; spellId = newInfo->Id; // Force triggered to spells mirrored by Dark Simulacrum if ((*itr)->GetId() == 77616) triggered = true; } break; } } // Lifebloom if (spellId == 33763) { // search Tree of Life (Passive) if (Aura* aura = _mover->GetAura(81098, EFFECT_INDEX_1)) { if (SpellEntry const* newInfo = sSpellStore.LookupEntry(aura->GetModifier()->m_amount)) { spellInfo = newInfo; spellId = newInfo->Id; } } } // client provided targets SpellCastTargets targets; recvPacket >> targets.ReadForCaster(mover); targets.ReadAdditionalData(recvPacket, cast_flags); // Multi-Shot hack if (!targets.getUnitTarget() && spellId == 2643) { if (Unit* target = _player->GetMap()->GetUnit(_player->GetSelectionGuid())) targets.setUnitTarget(target); } // auto-selection buff level base at target level (in spellInfo) if (Unit* target = targets.getUnitTarget()) { // if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message if (SpellEntry const *actualSpellInfo = sSpellMgr.SelectAuraRankForLevel(spellInfo, target->getLevel())) spellInfo = actualSpellInfo; } Spell *spell = new Spell(mover, spellInfo, triggered, mover->GetObjectGuid(), triggeredBy); spell->m_cast_count = cast_count; // set count of casts spell->m_glyphIndex = glyphIndex; spell->prepare(&targets, triggeredByAura); }