void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL"); uint64 guid; uint32 spellid; recvPacket >> guid >> spellid; // This opcode is also sent from charmed and possessed units (players and creatures) if (!_player->GetPet() && !_player->GetCharm()) return; Unit* caster = ObjectAccessor::GetUnit(*_player, guid); if (!caster || (caster != _player->GetPet() && caster != _player->GetCharm())) { sLog.outError("HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName()); return; } SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } // do not cast not learned spells if (!caster->HasSpell(spellid) || IsPassiveSpell(spellid)) return; if (spellInfo->StartRecoveryCategory > 0) //Check if spell is affected by GCD if (caster->GetTypeId() == TYPEID_UNIT && caster->ToCreature()->GetGlobalCooldown() > 0) { caster->SendPetCastFail(spellid, SPELL_FAILED_NOT_READY); return; } SpellCastTargets targets; recvPacket >> targets.ReadForCaster(caster); caster->clearUnitState(UNIT_STAT_FOLLOW); Spell *spell = new Spell(caster, spellInfo, spellid == 33395); // water elemental can cast freeze as triggered spell->m_targets = targets; int16 result = spell->PetCanCast(NULL); if (result == -1) { if (caster->GetTypeId() == TYPEID_UNIT) { Creature* pet = caster->ToCreature(); pet->AddCreatureSpellCooldown(spellid); if (pet->isPet()) { Pet* p = (Pet*)pet; p->CheckLearning(spellid); // 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 (p->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(guid); } } spell->prepare(&(spell->m_targets)); } else { caster->SendPetCastFail(spellid, result); if (caster->GetTypeId() == TYPEID_PLAYER) { if (!caster->ToPlayer()->HasSpellCooldown(spellid)) caster->SendPetClearCooldown(spellid); } else { if (!caster->ToCreature()->HasSpellCooldown(spellid)) caster->SendPetClearCooldown(spellid); } spell->finish(false); delete spell; } }
void WorldSession::HandlePetAction( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data, 8+2+2+8); uint64 guid1; uint32 data; uint64 guid2; recv_data >> guid1; //pet guid recv_data >> data; recv_data >> guid2; //tag guid uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data); uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); //delete = 0x07 CastSpell = C1 // used also for charmed creature Unit* pet= ObjectAccessor::GetUnit(*_player, guid1); sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2)) ); if(!pet) { sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid1)) ); return; } if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) { sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName() ); return; } if(!pet->isAlive()) return; if(pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK)) return; CharmInfo *charmInfo = pet->GetCharmInfo(); if(!charmInfo) { sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId()); return; } switch(flag) { case ACT_COMMAND: //0x07 switch(spellid) { case COMMAND_STAY: //flat=1792 //STAY pet->StopMoving(); pet->GetMotionMaster()->Clear(); pet->GetMotionMaster()->MoveIdle(); charmInfo->SetCommandState( COMMAND_STAY ); break; case COMMAND_FOLLOW: //spellid=1792 //FOLLOW pet->AttackStop(); pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); charmInfo->SetCommandState( COMMAND_FOLLOW ); break; case COMMAND_ATTACK: //spellid=1792 //ATTACK { const uint64& selguid = _player->GetSelection(); Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, selguid); if(!TargetUnit) return; // not let attack friendly units. if(GetPlayer()->IsFriendlyTo(TargetUnit)) return; // Not let attack through obstructions if(!pet->IsWithinLOSInMap(TargetUnit)) return; // This is true if pet has no target or has target but targets differs. if(pet->getVictim() != TargetUnit) { if (pet->getVictim()) pet->AttackStop(); if(pet->GetTypeId() != TYPEID_PLAYER) { pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) ((Creature*)pet)->AI()->AttackStart(TargetUnit); //10% chance to play special pet attack talk, else growl if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) pet->SendPetTalk((uint32)PET_TALK_ATTACK); else { // 90% chance for pet and 100% chance for charmed creature pet->SendPetAIReaction(guid1); } } else // charmed player { pet->Attack(TargetUnit,true); pet->SendPetAIReaction(guid1); } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) if(((Creature*)pet)->isPet()) { Pet* p = (Pet*)pet; if(p->getPetType() == HUNTER_PET) _player->RemovePet(p,PET_SAVE_AS_DELETED); else //dismissing a summoned pet is like killing them (this prevents returning a soulshard...) p->setDeathState(CORPSE); } else // charmed _player->Uncharm(); 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 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 { Unit* unit_target = NULL; if (((Creature*)pet)->GetGlobalCooldown() > 0) return; if(guid2) unit_target = ObjectAccessor::GetUnit(*_player,guid2); // do not cast unknown spells SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid ); if(!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } for(uint32 i = 0; i < 3;++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(spellid)) return; pet->clearUnitState(UNIT_STAT_FOLLOW); Spell *spell = new Spell(pet, spellInfo, false); SpellCastResult result = spell->CheckPetCast(unit_target); //auto turn to target unless possessed if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { if(unit_target) { pet->SetInFront(unit_target); if (unit_target->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer( (Player*)unit_target ); } else if(Unit *unit_target2 = spell->m_targets.getUnitTarget()) { pet->SetInFront(unit_target2); if (unit_target2->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer( (Player*)unit_target2 ); } if (Unit* powner = pet->GetCharmerOrOwner()) if(powner->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer((Player*)powner); result = SPELL_CAST_OK; } if(result == SPELL_CAST_OK) { ((Creature*)pet)->AddCreatureSpellCooldown(spellid); unit_target = spell->m_targets.getUnitTarget(); //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*)pet)->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else { pet->SendPetAIReaction(guid1); } if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != unit_target) { if (pet->getVictim()) pet->AttackStop(); pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) ((Creature*)pet)->AI()->AttackStart(unit_target); } } spell->prepare(&(spell->m_targets)); } else { if(pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) Spell::SendCastResult(GetPlayer(),spellInfo,0,result); else pet->SendPetCastFail(spellid, result); if (!((Creature*)pet)->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2) { CharmInfo *charmInfo = pet->GetCharmInfo(); if (!charmInfo) { sLog->outError("WorldSession::HandlePetAction(petGuid: " UI64FMTD ", tagGuid: " UI64FMTD ", spellId: %u, flag: %u): object (entry: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", guid1, guid2, spellid, flag, pet->GetGUIDLow(), pet->GetTypeId()); return; } switch(flag) { case ACT_COMMAND: //0x07 switch(spellid) { case COMMAND_STAY: //flat=1792 //STAY pet->AttackStop(); pet->InterruptNonMeleeSpells(false); pet->GetMotionMaster()->MoveIdle(); charmInfo->SetCommandState(COMMAND_STAY); charmInfo->SetIsCommandAttack(false); charmInfo->SetIsAtStay(true); charmInfo->SetIsFollowing(false); charmInfo->SetIsReturning(false); charmInfo->SaveStayPosition(); break; case COMMAND_FOLLOW: //spellid=1792 //FOLLOW pet->AttackStop(); pet->InterruptNonMeleeSpells(false); pet->GetMotionMaster()->MoveFollow(_player, PET_FOLLOW_DIST, pet->GetFollowAngle()); charmInfo->SetCommandState(COMMAND_FOLLOW); charmInfo->SetIsCommandAttack(false); charmInfo->SetIsAtStay(false); charmInfo->SetIsReturning(true); charmInfo->SetIsFollowing(false); break; case COMMAND_ATTACK: //spellid=1792 //ATTACK { // Can't attack if owner is pacified if (_player->HasAuraType(SPELL_AURA_MOD_PACIFY)) { //pet->SendPetCastFail(spellid, SPELL_FAILED_PACIFIED); //TODO: Send proper error message to client return; } // only place where pet can be player Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, guid2); if (!TargetUnit) return; if (Unit *owner = pet->GetOwner()) if (!owner->canAttack(TargetUnit)) return; // Not let attack through obstructions if (sWorld->getBoolConfig(CONFIG_PET_LOS)) { if (!pet->IsWithinLOSInMap(TargetUnit)) return; } pet->ClearUnitState(UNIT_STAT_FOLLOW); // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != TargetUnit || (pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack())) { if (pet->getVictim()) pet->AttackStop(); if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled) { charmInfo->SetIsCommandAttack(true); charmInfo->SetIsAtStay(false); charmInfo->SetIsFollowing(false); charmInfo->SetIsReturning(false); pet->ToCreature()->AI()->AttackStart(TargetUnit); //10% chance to play special pet attack talk, else growl if (pet->ToCreature()->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) pet->SendPetTalk((uint32)PET_TALK_ATTACK); else { // 90% chance for pet and 100% chance for charmed creature pet->SendPetAIReaction(guid1); } } else // charmed player { if (pet->getVictim() && pet->getVictim() != TargetUnit) pet->AttackStop(); charmInfo->SetIsCommandAttack(true); charmInfo->SetIsAtStay(false); charmInfo->SetIsFollowing(false); charmInfo->SetIsReturning(false); pet->Attack(TargetUnit, true); pet->SendPetAIReaction(guid1); } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) if (pet->GetCharmerGUID() == GetPlayer()->GetGUID()) _player->StopCastingCharm(); else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID()) { ASSERT(pet->GetTypeId() == TYPEID_UNIT); if (pet->isPet()) { if (((Pet*)pet)->getPetType() == HUNTER_PET) GetPlayer()->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED); else //dismissing a summoned pet is like killing them (this prevents returning a soulshard...) pet->setDeathState(CORPSE); } else if (pet->HasUnitTypeMask(UNIT_MASK_MINION)) { ((Minion*)pet)->UnSummon(); } } 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(); case REACT_DEFENSIVE: //recovery case REACT_AGGRESSIVE: //activete if (pet->GetTypeId() == TYPEID_UNIT) pet->ToCreature()->SetReactState(ReactStates(spellid)); break; } break; case ACT_DISABLED: // 0x81 spell (disabled), ignore case ACT_PASSIVE: // 0x01 case ACT_ENABLED: // 0xC1 spell { Unit* unit_target = NULL; if (guid2) unit_target = ObjectAccessor::GetUnit(*_player, guid2); // do not cast unknown spells SpellInfo const *spellInfo = sSpellMgr->GetSpellInfo(spellid); if (!spellInfo) { sLog->outError("WORLD: unknown PET spell id %i", spellid); return; } if (spellInfo->StartRecoveryCategory > 0) if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) { if (spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_AREA_ENEMY_SRC || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY) return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || spellInfo->IsPassive()) return; // Clear the flags as if owner clicked 'attack'. AI will reset them // after AttackStart, even if spell failed if (pet->GetCharmInfo()) { pet->GetCharmInfo()->SetIsAtStay(false); pet->GetCharmInfo()->SetIsCommandAttack(true); pet->GetCharmInfo()->SetIsReturning(false); pet->GetCharmInfo()->SetIsFollowing(false); } Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE); SpellCastResult result = spell->CheckPetCast(unit_target); //auto turn to target unless possessed if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle()) { if (unit_target) { pet->SetInFront(unit_target); if (unit_target->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer((Player*)unit_target); } else if (Unit *unit_target2 = spell->m_targets.GetUnitTarget()) { pet->SetInFront(unit_target2); if (unit_target2->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer((Player*)unit_target2); } if (Unit* powner = pet->GetCharmerOrOwner()) if (powner->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer(powner->ToPlayer()); result = SPELL_CAST_OK; } if (result == SPELL_CAST_OK) { pet->ToCreature()->AddCreatureSpellCooldown(spellid); unit_target = spell->m_targets.GetUnitTarget(); //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 (pet->ToCreature()->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else { pet->SendPetAIReaction(guid1); } if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle()) { // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != unit_target) { if (pet->getVictim()) pet->AttackStop(); pet->GetMotionMaster()->Clear(); if (pet->ToCreature()->IsAIEnabled) pet->ToCreature()->AI()->AttackStart(unit_target); } } spell->prepare(&(spell->m_targets)); } else { if (pet->isPossessed() || pet->IsVehicle()) Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else pet->SendPetCastFail(spellid, result); if (!pet->ToCreature()->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; // reset specific flags in case of spell fail. AI will reset other flags if (pet->GetCharmInfo()) pet->GetCharmInfo()->SetIsCommandAttack(false); } break; } default: sLog->outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
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 (!me->isAlive()) return; if(me->GetIAmABot()) { //don't do anything if eating or drinking, otherwise call UpdateAI if(!me->HasAura(10256) && !me->HasAura(1137) && me->GetBotAI()) me->GetBotAI()->UpdateAI(diff); } Unit* owner = me->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; // me->getVictim() can't be used for check in case stop fighting, me->getVictim() clear at Unit death etc. if (me->getVictim()) { if (_needToStop()) { sLog->outStaticDebug("Pet AI stopped attacking [guid=%u]", me->GetGUIDLow()); _stopAttack(); return; } targetHasCC = _CheckTargetCC(me->getVictim()); DoMeleeAttackIfReady(); } else if (owner && me->GetCharmInfo()) //no victim { Unit *nextTarget = SelectNextTarget(); if (me->HasReactState(REACT_PASSIVE)) _stopAttack(); else if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else if (owner && !me->HasUnitState(UNIT_STAT_FOLLOW)) // no charm info and no victim me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle()); if (!me->GetCharmInfo()) return; // Autocast (casted only in combat or persistent spells in any state) if (!me->HasUnitState(UNIT_STAT_CASTING)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) { uint32 spellID = me->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID); if (!spellInfo) continue; if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(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(me, 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*>(me->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(*me, *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 (!me->HasInArc(M_PI, target)) { me->SetInFront(target); if (target && target->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(target->ToPlayer()); if (owner && owner->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(owner->ToPlayer()); } me->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 Item::ApplyEnchantmentBonus( uint32 Slot, bool Apply ) { if( m_owner == NULL ) return; EnchantmentMap::iterator itr = Enchantments.find( Slot ); if( itr == Enchantments.end() ) return; EnchantEntry* Entry = itr->second.Enchantment; uint32 RandomSuffixAmount = itr->second.RandomSuffix; if( itr->second.BonusApplied == Apply ) return; itr->second.BonusApplied = Apply; if( Apply ) { // Send the enchantment time update packet. SendEnchantTimeUpdate( itr->second.Slot, itr->second.Duration ); } // Apply the visual on the player. uint32 ItemSlot = m_owner->GetItemInterface()->GetInventorySlotByGuid( GetGUID() ) * 16; uint32 VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + ItemSlot; m_owner->SetUInt32Value( VisibleBase + 1 + Slot, Apply ? Entry->Id : 0 ); // Another one of those for loop that where not indented properly god knows what will break // but i made it actually affect the code below it for( uint32 c = 0; c < 3; c++ ) { if( Entry->type[c] ) { // Depending on the enchantment type, take the appropriate course of action. switch( Entry->type[c] ) { case 1: // Trigger spell on melee attack. { if( Apply && Entry->spell[c] != 0 ) { // Create a proc trigger spell ProcTriggerSpell TS; TS.caster = m_owner->GetGUID(); TS.origId = 0; TS.procFlags = PROC_ON_MELEE_ATTACK; TS.procCharges = 0; /* This needs to be modified based on the attack speed of the weapon. * Secondly, need to assign some static chance for instant attacks (ss, * gouge, etc.) */ if( !Entry->min[c] && GetProto()->Class == 2 ) { float speed = (float)GetProto()->Delay; /////// procChance calc /////// float ppm = 0; SpellEntry* sp = dbcSpell.LookupEntry( Entry->spell[c] ); if( sp ) { switch( sp->NameHash ) { case SPELL_HASH_FROSTBRAND_ATTACK: ppm = 9; break; } } if( ppm != 0 ) { float pcount = 60/ppm; float chance = (speed/10) / pcount; TS.procChance = (uint32)chance; } else TS.procChance = (uint32)( speed / 600.0f ); /////////////////////////////// } else TS.procChance = Entry->min[c]; Log.Debug( "Enchant", "Setting procChance to %u%%.", TS.procChance ); TS.deleted = false; TS.spellId = Entry->spell[c]; m_owner->m_procSpells.push_back( TS ); } else { // Remove the proctriggerspell uint32 SpellId; list< struct ProcTriggerSpell >::iterator itr/*, itr2*/; for( itr = m_owner->m_procSpells.begin(); itr != m_owner->m_procSpells.end(); ) { SpellId = itr->spellId; /*itr2 = itr++;*/ if( SpellId == Entry->spell[c] ) { //m_owner->m_procSpells.erase(itr2); itr->deleted = true; } itr++; } } }break; case 2: // Mod damage done. { int32 val = Entry->min[c]; if( RandomSuffixAmount ) val = RANDOM_SUFFIX_MAGIC_CALCULATION( RandomSuffixAmount, GetItemRandomSuffixFactor() ); if( Apply ) m_owner->ModUnsigned32Value( PLAYER_FIELD_MOD_DAMAGE_DONE_POS, val ); else m_owner->ModUnsigned32Value( PLAYER_FIELD_MOD_DAMAGE_DONE_POS, -val ); m_owner->CalcDamage(); }break; case 3: // Cast spell (usually means apply aura) { if( Apply ) { SpellCastTargets targets( m_owner->GetGUID() ); SpellEntry* sp; Spell* spell; if( Entry->spell[c] != 0 ) { sp = dbcSpell.LookupEntry( Entry->spell[c] ); if( sp == NULL ) continue; spell = new Spell( m_owner, sp, true, 0 ); spell->i_caster = this; spell->prepare( &targets ); } } else { if( Entry->spell[c] != 0 ) m_owner->RemoveAura( Entry->spell[c] ); } }break; case 4: // Modify physical resistance { int32 val = Entry->min[c]; if( RandomSuffixAmount ) val = RANDOM_SUFFIX_MAGIC_CALCULATION( RandomSuffixAmount, GetItemRandomSuffixFactor() ); if( Apply ) { m_owner->FlatResistanceModifierPos[Entry->spell[c]] += val; } else { m_owner->FlatResistanceModifierPos[Entry->spell[c]] -= val; } m_owner->CalcResistance( Entry->spell[c] ); }break; case 5: //Modify rating ...order is PLAYER_FIELD_COMBAT_RATING_1 and above { //spellid is enum ITEM_STAT_TYPE //min=max is amount int32 val = Entry->min[c]; if( RandomSuffixAmount ) val = RANDOM_SUFFIX_MAGIC_CALCULATION( RandomSuffixAmount, GetItemRandomSuffixFactor() ); m_owner->ModifyBonuses( Entry->spell[c], Apply ? val : -val ); m_owner->UpdateStats(); }break; case 6: // Rockbiter weapon (increase damage per second... how the hell do you calc that) { if( Apply ) { //m_owner->ModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS, Entry->min[c]); //if i'm not wrong then we should apply DMPS formula for this. This will have somewhat a larger value 28->34 int32 val = Entry->min[c]; if( RandomSuffixAmount ) val = RANDOM_SUFFIX_MAGIC_CALCULATION( RandomSuffixAmount, GetItemRandomSuffixFactor() ); int32 value = GetProto()->Delay * val / 1000; m_owner->ModUnsigned32Value( PLAYER_FIELD_MOD_DAMAGE_DONE_POS, value ); } else { int32 val = Entry->min[c]; if( RandomSuffixAmount ) val = RANDOM_SUFFIX_MAGIC_CALCULATION( RandomSuffixAmount, GetItemRandomSuffixFactor() ); int32 value =- (int32)(GetProto()->Delay * val / 1000 ); m_owner->ModUnsigned32Value( PLAYER_FIELD_MOD_DAMAGE_DONE_POS, value ); } m_owner->CalcDamage(); }break; default: { DEBUG_LOG( "Unknown enchantment type: %u (%u)", Entry->type[c], Entry->Id ); }break; } } } }
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::HandleUseItemOpcode(WorldPacket& recvPacket) { // TODO: add targets.read() check CHECK_PACKET_SIZE(recvPacket,1+1+1+1+8); Player* pUser = _player; uint8 bagIndex, slot; uint8 spell_count; // number of spells at item, not used uint8 cast_count; // next cast if exists (single or not) uint64 item_guid; recvPacket >> bagIndex >> slot >> spell_count >> cast_count >> item_guid; Item *pItem = pUser->GetItemByPos(bagIndex, slot); if(!pItem) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); return; } sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, spell_count: %u , cast_count: %u, Item: %u, data length = %i", bagIndex, slot, spell_count, cast_count, pItem->GetEntry(), recvPacket.size()); ItemPrototype const *proto = pItem->GetProto(); if(!proto) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL ); return; } // some item classes can be used only in equipped state if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped()) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL ); return; } uint8 msg = pUser->CanUseItem(pItem); if( msg != EQUIP_ERR_OK ) { pUser->SendEquipError( msg, pItem, NULL ); return; } // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB) if( proto->Class == ITEM_CLASS_CONSUMABLE && !(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) && pUser->InArena()) { pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL); return; } if (pUser->isInCombat()) { for(int i = 0; i < 5; ++i) { if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId)) { if (IsNonCombatSpell(spellInfo)) { pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT,pItem,NULL); return; } } } } // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory) if( pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM ) { if (!pItem->IsSoulBound()) { pItem->SetState(ITEM_CHANGED, pUser); pItem->SetBinding( true ); } } SpellCastTargets targets; if(!targets.read(&recvPacket, pUser)) return; //Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state. if(!Script->ItemUse(pUser,pItem,targets)) { // no script or script not process request by self // special learning case if(pItem->GetProto()->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN) { uint32 learning_spell_id = pItem->GetProto()->Spells[1].SpellId; SpellEntry const *spellInfo = sSpellStore.LookupEntry(SPELL_ID_GENERIC_LEARN); if(!spellInfo) { sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, SPELL_ID_GENERIC_LEARN); pUser->SendEquipError(EQUIP_ERR_NONE,pItem,NULL); return; } Spell *spell = new Spell(pUser, spellInfo, false); spell->m_CastItem = pItem; spell->m_cast_count = cast_count; //set count of casts spell->m_currentBasePoints[0] = learning_spell_id; spell->prepare(&targets); return; } // use triggered flag only for items with many spell casts and for not first cast int count = 0; for(int i = 0; i < 5; ++i) { _Spell const& spellData = pItem->GetProto()->Spells[i]; // no spell if(!spellData.SpellId) continue; // wrong triggering type if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) continue; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId); if(!spellInfo) { sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, spellData.SpellId); continue; } Spell *spell = new Spell(pUser, spellInfo, (count > 0)); spell->m_CastItem = pItem; spell->m_cast_count = cast_count; //set count of casts spell->prepare(&targets); ++count; } } }
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 WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL"); ObjectGuid guid; uint32 spellid; uint8 cast_count; uint8 unk_flags; // flags (if 0x02 - some additional data are received) recvPacket >> guid >> cast_count >> spellid >> unk_flags; DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, %s, cast_count: %u, spellid %u, unk_flags %u", guid.GetString().c_str(), cast_count, spellid, unk_flags); Creature* pet = _player->GetMap()->GetAnyTypeCreature(guid); if (!pet || (guid != _player->GetPetGuid() && guid != _player->GetCharmGuid())) { sLog.outError("HandlePetCastSpellOpcode: %s isn't pet of %s .", guid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } 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; Aura* triggeredByAura = pet->GetTriggeredByClientAura(spellid); // do not cast not learned spells if ((!triggeredByAura && !pet->HasSpell(spellid)) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(pet); pet->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(pet, spellInfo, triggeredByAura ? true : false, pet->GetObjectGuid(), triggeredByAura ? triggeredByAura->GetSpellProto() : nullptr); spell->m_cast_count = cast_count; // probably pending spell cast spell->m_targets = targets; SpellCastResult result = triggeredByAura ? SPELL_CAST_OK : spell->CheckPetCast(nullptr); if (result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); spell->SpellStart(&(spell->m_targets), triggeredByAura); } else { Unit* owner = pet->GetCharmerOrOwner(); if (owner && owner->GetTypeId() == TYPEID_PLAYER && !triggeredByAura) Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true); if (!pet->HasSpellCooldown(spellid) && !triggeredByAura) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
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 (mover->IsVehicle() && mover->GetCharmerOrOwnerPlayerOrPlayerItself()) { Player *plr = mover->GetCharmerOrOwnerPlayerOrPlayerItself(); if (mover->GetVehicleKit()->GetSeatInfo(plr) && ((mover->GetVehicleKit()->GetSeatInfo(plr)->m_flags & SEAT_FLAG_CAN_ATTACK) || (mover->GetVehicleKit()->GetSeatInfo(plr)->m_flags & 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; } if (mover->GetTypeId()==TYPEID_PLAYER) { // not have spell in spellbook or spell passive and not casted by client if (((((Player*)mover)->GetUInt16Value(PLAYER_FIELD_BYTES2, 0) == 0 && (!((Player*)mover)->HasActiveSpell(spellId) && !triggered)) || IsPassiveSpell(spellInfo)) && spellId != 1843) { 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("WorldSession::HandleCastSpellOpcode: %s try 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; } } 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; } break; } } // client provided targets SpellCastTargets targets; recvPacket >> targets.ReadForCaster(mover); targets.ReadAdditionalData(recvPacket, cast_flags); // 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); }
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); } }
void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) { uint32 spellId; uint8 cast_count, unk_flags; recvPacket >> cast_count; recvPacket >> spellId; recvPacket >> unk_flags; // flags (if 0x02 - some additional data are received) // ignore for remote control state (for player case) Unit* mover = _player->m_mover; if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } // vehicle spells are handled by CMSG_PET_CAST_SPELL, // but player is still able to cast own spells if(_player->GetCharmGUID() && _player->GetCharmGUID() == _player->GetVehicleGUID()) mover = _player; sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u, unk_flags %u, data length = %i", spellId, cast_count, unk_flags, (uint32)recvPacket.size()); 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; } if(mover->GetTypeId()==TYPEID_PLAYER) { // not have spell in spellbook or spell passive and not casted by client if (!((Player*)mover)->HasActiveSpell (spellId) || IsPassiveSpell(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) || IsPassiveSpell(spellId) ) { //cheater? kick? ban? recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } } // client provided targets SpellCastTargets targets; if(!targets.read(&recvPacket,mover)) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } // some spell cast packet including more data (for projectiles?) if (unk_flags & 0x02) { recvPacket.read_skip<float>(); // unk1, coords? recvPacket.read_skip<float>(); // unk1, coords? recvPacket.read_skip<uint8>(); // >> 1 recvPacket.read_skip<uint32>(); // >> MSG_MOVE_STOP MovementInfo movementInfo; ReadMovementInfo(recvPacket, &movementInfo); } // auto-selection buff level base at target level (in spellInfo) if(targets.getUnitTarget()) { SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,targets.getUnitTarget()->getLevel()); // 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(actualSpellInfo) spellInfo = actualSpellInfo; } Spell *spell = new Spell(mover, spellInfo, false); spell->m_cast_count = cast_count; // set count of casts spell->prepare(&targets); }
void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) { // TODO: add targets.read() check Player* pUser = _player; uint8 bagIndex, slot; uint8 spell_count; // number of spells at item, not used recvPacket >> bagIndex >> slot >> spell_count; Item *pItem = pUser->GetItemByPos(bagIndex, slot); if(!pItem) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); return; } sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, spell_count: %u , Item: %u, data length = %i", bagIndex, slot, spell_count, pItem->GetEntry(), (uint32)recvPacket.size()); ItemPrototype const *proto = pItem->GetProto(); if(!proto) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL ); return; } // some item classes can be used only in equipped state if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped()) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL ); return; } uint8 msg = pUser->CanUseItem(pItem); if( msg != EQUIP_ERR_OK ) { pUser->SendEquipError( msg, pItem, NULL ); return; } if (pUser->isInCombat()) { for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId)) { if (IsNonCombatSpell(spellInfo)) { pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT,pItem,NULL); return; } } } } // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory) if( pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM ) { if (!pItem->IsSoulBound()) { pItem->SetState(ITEM_CHANGED, pUser); pItem->SetBinding( true ); } } SpellCastTargets targets; if (!targets.read(&recvPacket, pUser)) return; targets.Update(pUser); if (!pItem->IsTargetValidForItemUse(targets.getUnitTarget())) { // free gray item after use fail pUser->SendEquipError(EQUIP_ERR_NONE, pItem, NULL); // search spell for spell error uint32 spellid = 0; for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { if( proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE || proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) { spellid = proto->Spells[i].SpellId; break; } } // send spell error if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid)) Spell::SendCastResult(_player,spellInfo,SPELL_FAILED_BAD_TARGETS); return; } //Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state. if(!Script->ItemUse(pUser,pItem,targets)) { // no script or script not process request by self // special learning case if(pItem->GetProto()->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN) { uint32 learning_spell_id = pItem->GetProto()->Spells[1].SpellId; SpellEntry const *spellInfo = sSpellStore.LookupEntry(SPELL_ID_GENERIC_LEARN); if(!spellInfo) { sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, SPELL_ID_GENERIC_LEARN); pUser->SendEquipError(EQUIP_ERR_NONE,pItem,NULL); return; } Spell *spell = new Spell(pUser, spellInfo, false); spell->m_CastItem = pItem; spell->m_currentBasePoints[0] = learning_spell_id; spell->prepare(&targets); return; } // use triggered flag only for items with many spell casts and for not first cast int count = 0; for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { _Spell const& spellData = pItem->GetProto()->Spells[i]; // no spell if(!spellData.SpellId) continue; // wrong triggering type if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) continue; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId); if(!spellInfo) { sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, spellData.SpellId); continue; } Spell *spell = new Spell(pUser, spellInfo, (count > 0)); spell->m_CastItem = pItem; spell->prepare(&targets); ++count; } } }
void PetAI::UpdateAI(uint32 diff) { if (!me->IsAlive() || !me->GetCharmInfo()) return; Unit* owner = me->GetCharmerOrOwner(); if (_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else _updateAlliesTimer -= diff; if (me->GetVictim() && me->EnsureVictim()->IsAlive()) { // is only necessary to stop casting, the pet must not exit combat if (!me->GetCurrentSpell(CURRENT_CHANNELED_SPELL) && // ignore channeled spells (Pin, Seduction) me->EnsureVictim()->HasBreakableByDamageCrowdControlAura(me)) { me->InterruptNonMeleeSpells(false); return; } if (NeedToStop()) { TC_LOG_TRACE("scripts.ai.petai", "PetAI::UpdateAI: AI stopped attacking %s", me->GetGUID().ToString().c_str()); StopAttack(); return; } // Check before attacking to prevent pets from leaving stay position if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY)) { if (me->GetCharmInfo()->IsCommandAttack() || (me->GetCharmInfo()->IsAtStay() && me->IsWithinMeleeRange(me->GetVictim()))) DoMeleeAttackIfReady(); } else DoMeleeAttackIfReady(); } else { if (me->HasReactState(REACT_AGGRESSIVE) || me->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 DamageTaken(), OwnerAttackedBy(), OwnerAttacked(), etc. Unit* nextTarget = SelectNextTarget(me->HasReactState(REACT_AGGRESSIVE)); if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else HandleReturnMovement(); } // Autocast (cast only in combat or persistent spells in any state) if (!me->HasUnitState(UNIT_STATE_CASTING)) { TargetSpellList targetSpellStore; for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) { uint32 spellID = me->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) continue; if (me->GetSpellHistory()->HasGlobalCooldown(spellInfo)) continue; // check spell cooldown if (!me->GetSpellHistory()->IsReady(spellInfo)) continue; if (spellInfo->IsPositive()) { if (spellInfo->CanBeUsedInCombat()) { // Check if we're in combat or commanded to attack if (!me->IsInCombat() && !me->GetCharmInfo()->IsCommandAttack()) continue; } Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE); bool spellUsed = false; // Some spells can target enemy or friendly (DK Ghoul's Leap) // Check for enemy first (pet then owner) Unit* target = me->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; } } if (spellInfo->HasEffect(SPELL_EFFECT_JUMP_DEST)) { if (!spellUsed) delete spell; continue; // Pets must only jump to target } // No enemy, check friendly if (!spellUsed) { for (GuidSet::const_iterator tar = _allySet.begin(); tar != _allySet.end(); ++tar) { Unit* ally = ObjectAccessor::GetUnit(*me, *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) delete spell; } else if (me->GetVictim() && CanAttack(me->GetVictim()) && spellInfo->CanBeUsedInCombat()) { Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE); if (spell->CanAutoCast(me->GetVictim())) targetSpellStore.push_back(std::make_pair(me->GetVictim(), spell)); else delete spell; } } // found units to cast on to if (!targetSpellStore.empty()) { TargetSpellList::iterator it = targetSpellStore.begin(); std::advance(it, urand(0, targetSpellStore.size() - 1)); Spell* spell = (*it).second; Unit* target = (*it).first; targetSpellStore.erase(it); SpellCastTargets targets; targets.SetUnitTarget(target); spell->prepare(targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } // Update speed as needed to prevent dropping too far behind and despawning me->UpdateSpeed(MOVE_RUN); me->UpdateSpeed(MOVE_WALK); me->UpdateSpeed(MOVE_FLIGHT); }
void PetAI::UpdateAI(const uint32 diff) { if (!me->isAlive() || !me->GetCharmInfo()) return; Unit* owner = me->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (me->getVictim() && me->getVictim()->isAlive()) { // is only necessary to stop casting, the pet must not exit combat if (me->getVictim()->HasBreakableByDamageCrowdControlAura(me)) { me->InterruptNonMeleeSpells(false); return; } if (_needToStop()) { sLog->outDebug(LOG_FILTER_GENERAL, "Pet AI stopped attacking [guid=%u]", me->GetGUIDLow()); _stopAttack(); return; } // Check before attacking to prevent pets from leaving stay position if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY)) { if (me->GetCharmInfo()->IsCommandAttack() || (me->GetCharmInfo()->IsAtStay() && me->IsWithinMeleeRange(me->getVictim()))) DoMeleeAttackIfReady(); } else DoMeleeAttackIfReady(); } else { if (me->HasReactState(REACT_AGGRESSIVE) || me->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(me->HasReactState(REACT_AGGRESSIVE)); if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else HandleReturnMovement(); } // Autocast (casted only in combat or persistent spells in any state) if (!me->HasUnitState(UNIT_STATE_CASTING)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) { uint32 spellID = me->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) continue; if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) continue; if (spellInfo->IsPositive()) { if (spellInfo->CanBeUsedInCombat()) { // check spell cooldown if (me->HasSpellCooldown(spellInfo->Id)) continue; // Check if we're in combat or commanded to attack if (!me->isInCombat() && !me->GetCharmInfo()->IsCommandAttack()) continue; } Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); bool spellUsed = false; // Some spells can target enemy or friendly (DK Ghoul's Leap) // Check for enemy first (pet then owner) Unit* target = me->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 = ObjectAccessor::GetUnit(*me, *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) delete spell; } else if (me->getVictim() && CanAttack(me->getVictim()) && spellInfo->CanBeUsedInCombat()) { Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); if (spell->CanAutoCast(me->getVictim())) targetSpellStore.push_back(std::make_pair(me->getVictim(), spell)); else 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 (!me->HasInArc(M_PI, target)) { me->SetInFront(target); if (target && target->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(target->ToPlayer()); if (owner && owner->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(owner->ToPlayer()); } me->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) { if (!me->IsAlive()) return; Unit* owner = me->GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; // me->GetVictim() can't be used for check in case stop fighting, me->GetVictim() clear at Unit death etc. if (me->GetVictim()) { // is only necessary to stop casting, the pet must not exit combat if (me->GetVictim()->HasCrowdControlAura(me)) { me->InterruptNonMeleeSpells(false); return; } if (_needToStop()) { TC_LOG_DEBUG("misc", "Pet AI stopped attacking [guid=%u]", me->GetGUIDLow()); _stopAttack(); return; } if (owner && !owner->IsInCombat()) owner->SetInCombatWith(me->GetVictim()); DoMeleeAttackIfReady(); } else if (owner && me->GetCharmInfo()) //no victim { // Only aggressive pets do target search every update. // Defensive pets do target search only in these cases: // * Owner attacks something - handled by OwnerAttacked() // * Owner receives damage - handled by OwnerDamagedBy() // * Pet is in combat and current target dies - handled by KilledUnit() if (me->HasReactState(REACT_AGGRESSIVE)) { Unit* nextTarget = SelectNextTarget(); if (nextTarget) AttackStart(nextTarget); else HandleReturnMovement(); } else HandleReturnMovement(); } else if (owner && !me->HasUnitState(UNIT_STATE_FOLLOW)) // no charm info and no victim me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle()); if (!me->GetCharmInfo()) return; // Autocast (casted only in combat or persistent spells in any state) if (!me->HasUnitState(UNIT_STATE_CASTING)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) { uint32 spellID = me->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) continue; if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(me, spellInfo)) continue; if (spellInfo->IsPositive()) { if (spellInfo->CanBeUsedInCombat()) { // check spell cooldown if (me->HasSpellCooldown(spellInfo->Id)) continue; // Check if we're in combat or commanded to attack if (!me->IsInCombat() && !me->GetCharmInfo()->IsCommandAttack()) continue; } Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); bool spellUsed = false; spell->LoadScripts(); // Some spells can target enemy or friendly (DK Ghoul's Leap) // Check for enemy first (pet then owner) Unit* target = me->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 = ObjectAccessor::GetUnit(*me, *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) delete spell; } else if (me->GetVictim() && CanAttack(me->GetVictim()) && spellInfo->CanBeUsedInCombat()) { Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0); if (spell->CanAutoCast(me->GetVictim())) targetSpellStore.push_back(std::make_pair(me->GetVictim(), spell)); else 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 (!me->HasInArc(M_PI, target)) { me->SetInFront(target); if (target && target->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(target->ToPlayer()); if (owner && owner->GetTypeId() == TYPEID_PLAYER) me->SendUpdateToPlayer(owner->ToPlayer()); } me->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; } // Update speed as needed to prevent dropping too far behind and despawning // This not need to call every update. //me->UpdateSpeed(MOVE_RUN, true); //me->UpdateSpeed(MOVE_WALK, true); //me->UpdateSpeed(MOVE_FLIGHT, true); }
void Item::ApplyEnchantmentBonus(uint32 Slot, bool Apply) { if (m_owner == NULL) return; EnchantmentMap::iterator itr = Enchantments.find(Slot); if (itr == Enchantments.end()) return; EnchantEntry* Entry = itr->second.Enchantment; uint32 RandomSuffixAmount = itr->second.RandomSuffix; if (itr->second.BonusApplied == Apply) return; itr->second.BonusApplied = Apply; if (Apply) { // Send the enchantment time update packet. SendEnchantTimeUpdate(itr->second.Slot, itr->second.Duration); } // Apply the visual on the player. uint32 ItemSlot = m_owner->GetItemInterface()->GetInventorySlotByGuid(GetGUID()) * PLAYER_VISIBLE_ITEM_LENGTH; //VLack: for 3.1.1 "* 18" is a bad idea, now it's "* 2"; but this could have been calculated based on UpdateFields.h! This is PLAYER_VISIBLE_ITEM_LENGTH uint32 VisibleBase = PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + ItemSlot; if (VisibleBase <= PLAYER_VISIBLE_ITEM_19_ENCHANTMENT) m_owner->SetUInt32Value(VisibleBase, Apply ? Entry->Id : 0); //On 3.1 we can't add a Slot to the base now, as we no longer have multiple fields for storing them. This in some cases will try to write for example 3 visuals into one place, but now every item has only one field for this, and as we can't choose which visual to have, we'll accept the last one. else LOG_ERROR("Item::ApplyEnchantmentBonus visual out of range! Tried to address UInt32 field %i !!!", VisibleBase); // Another one of those for loop that where not indented properly god knows what will break // but i made it actually affect the code below it for (uint32 c = 0; c < 3; c++) { if (Entry->type[c]) { // Depending on the enchantment type, take the appropriate course of action. switch (Entry->type[c]) { case 1: // Trigger spell on melee attack. { if (Apply) { if (Entry->spell[c] != 0) m_owner->AddProcTriggerSpell(Entry->spell[c], 0, m_owner->GetGUID(), Entry->min[c], PROC_ON_MELEE_ATTACK, 0, NULL, NULL, this); } else { m_owner->RemoveProcTriggerSpell(Entry->spell[c], m_owner->GetGUID(), GetGUID()); } } break; case 2: // Mod damage done. { int32 val = Entry->min[c]; if (RandomSuffixAmount) val = RANDOM_SUFFIX_MAGIC_CALCULATION(RandomSuffixAmount, GetItemRandomSuffixFactor()); if (Apply) m_owner->ModPosDamageDoneMod(SCHOOL_NORMAL, val); else m_owner->ModPosDamageDoneMod(SCHOOL_NORMAL, -val); m_owner->CalcDamage(); } break; case 3: // Cast spell (usually means apply aura) { if (Apply) { SpellCastTargets targets(m_owner->GetGUID()); SpellEntry* sp; Spell* spell; if (Entry->spell[c] != 0) { sp = dbcSpell.LookupEntryForced(Entry->spell[c]); if (sp == NULL) continue; spell = sSpellFactoryMgr.NewSpell(m_owner, sp, true, 0); spell->i_caster = this; spell->prepare(&targets); } } else { if (Entry->spell[c] != 0) m_owner->RemoveAuraByItemGUID(Entry->spell[c], GetGUID()); } } break; case 4: // Modify physical resistance { int32 val = Entry->min[c]; if (RandomSuffixAmount) val = RANDOM_SUFFIX_MAGIC_CALCULATION(RandomSuffixAmount, GetItemRandomSuffixFactor()); if (Apply) { m_owner->FlatResistanceModifierPos[Entry->spell[c]] += val; } else { m_owner->FlatResistanceModifierPos[Entry->spell[c]] -= val; } m_owner->CalcResistance(Entry->spell[c]); } break; case 5: //Modify rating ...order is PLAYER_FIELD_COMBAT_RATING_1 and above { //spellid is enum ITEM_STAT_TYPE //min=max is amount int32 val = Entry->min[c]; if (RandomSuffixAmount) val = RANDOM_SUFFIX_MAGIC_CALCULATION(RandomSuffixAmount, GetItemRandomSuffixFactor()); m_owner->ModifyBonuses(Entry->spell[c], val, Apply); m_owner->UpdateStats(); } break; case 6: // Rockbiter weapon (increase damage per second... how the hell do you calc that) { if (Apply) { //m_owner->ModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS, Entry->min[c]); //if I'm not wrong then we should apply DMPS formula for this. This will have somewhat a larger value 28->34 int32 val = Entry->min[c]; if (RandomSuffixAmount) val = RANDOM_SUFFIX_MAGIC_CALCULATION(RandomSuffixAmount, GetItemRandomSuffixFactor()); int32 value = GetProto()->Delay * val / 1000; m_owner->ModPosDamageDoneMod(SCHOOL_NORMAL, value); } else { int32 val = Entry->min[c]; if (RandomSuffixAmount) val = RANDOM_SUFFIX_MAGIC_CALCULATION(RandomSuffixAmount, GetItemRandomSuffixFactor()); int32 value = -(int32)(GetProto()->Delay * val / 1000); m_owner->ModPosDamageDoneMod(SCHOOL_NORMAL, value); } m_owner->CalcDamage(); } break; case 7: { if (Apply) { for (uint32 i = 0; i < 3; ++i) OnUseSpellIDs[i] = Entry->spell[i]; } else { for (uint32 i = 0; i < 3; ++i) OnUseSpellIDs[i] = 0; } break; } case 8: { // Adding a prismatic socket to belt, hands, etc is type 8, it has no bonus to apply HERE break; } default: { LOG_ERROR("Unknown enchantment type: %u (%u)", Entry->type[c], Entry->Id); } break; } } } }
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; } 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_MOVE)) 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); 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) && !m_creature->GetObjectGuid().IsVehicle()) { 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); 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); if (inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->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_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(WorldPackets::Spells::CastSpell& cast) { // ignore for remote control state (for player case) Unit* mover = _player->m_mover; if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER) return; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(cast.Cast.SpellID); if (!spellInfo) { TC_LOG_ERROR("network", "WORLD: unknown spell id %u", cast.Cast.SpellID); return; } if (spellInfo->IsPassive()) return; Unit* caster = mover; if (caster->GetTypeId() == TYPEID_UNIT && !caster->ToCreature()->HasSpell(spellInfo->Id)) { // If the vehicle creature does not have the spell but it allows the passenger to cast own spells // change caster to player and let him cast if (!_player->IsOnVehicle(caster) || spellInfo->CheckVehicle(_player) != SPELL_CAST_OK) return; caster = _player; } // check known spell or raid marker spell (which not requires player to know it) if (caster->GetTypeId() == TYPEID_PLAYER && !caster->ToPlayer()->HasActiveSpell(spellInfo->Id) && !spellInfo->HasEffect(SPELL_EFFECT_CHANGE_RAID_MARKER)) return; // Check possible spell cast overrides spellInfo = caster->GetCastSpellInfo(spellInfo); // Client is resending autoshot cast opcode when other spell is cast during shoot rotation // Skip it to prevent "interrupt" message if (spellInfo->IsAutoRepeatRangedSpell() && caster->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL) && caster->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)->m_spellInfo == spellInfo) return; // can't use our own spells when we're in possession of another unit, if (_player->isPossessing()) return; // client provided targets SpellCastTargets targets(caster, cast.Cast); // auto-selection buff level base at target level (in spellInfo) if (targets.GetUnitTarget()) { SpellInfo const* actualSpellInfo = spellInfo->GetAuraRankForLevel(targets.GetUnitTarget()->getLevel()); // if rank not found then function return NULL but in explicit cast case original spell can be cast and later failed with appropriate error message if (actualSpellInfo) spellInfo = actualSpellInfo; } Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE, ObjectGuid::Empty, false); spell->m_cast_count = cast.Cast.CastID; // set count of casts spell->m_misc.Raw.Data[0] = cast.Cast.Misc[0]; spell->m_misc.Raw.Data[1] = cast.Cast.Misc[1]; spell->prepare(&targets); }
void Spells::loadSpells(const QString& spellsFileName) { // unload any previously loaded spells unloadSpells(); // create a QFile on the past in spell file QFile spellsFile(spellsFileName); // open the spell file if possible if (spellsFile.open(IO_ReadOnly)) { // QPtrQueue to temporarily store our Spells until we know the maxSpell QPtrQueue<Spell> spellQueue; spellQueue.setAutoDelete(false); // allocate memory in a QCString to hold the entire file contents QCString textData(spellsFile.size() + 1); // read the file as one big chunk spellsFile.readBlock(textData.data(), textData.size()); // construct a regex to deal with either style line termination QRegExp lineTerm("[\r\n]{1,2}"); uint16_t unicodeIndicator = *(uint16_t*)textData.data(); QString text; if ((unicodeIndicator != 0xfffe) && (unicodeIndicator != 0xfeff)) text = textData; else { #if (QT_VERSION > 0x030100) text = QString::fromUcs2((uint16_t*)textData.data()); #else if (sizeof(QChar) == 2) text.setUnicode(QChar*(textData.data()), textData.size() / 2); else { seqWarn("Spells::loadSpells(): Upgrade your version of Qt to at least 3.1 to properly handle UTF-16 encoded files!"); text = textData; } #endif } // split the file into at the line termination QStringList lines = QStringList::split(lineTerm, text, false); Spell* newSpell; // iterate over the lines and process the spell entries therein. for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it) { newSpell = new Spell(*it); // calculate the maximum spell ID if (newSpell->spell() > m_maxSpell) m_maxSpell = newSpell->spell(); // enqueue the new spell entry for later insertion spellQueue.enqueue(newSpell); } seqInfo("Loaded %d spells from '%s' maxSpell=%#.04x", spellQueue.count(), spellsFileName.latin1(), m_maxSpell); // allocate the spell array // Notes: Yeah, it is slightly sparse, but as of this writing there are // only 126 empty entries, so allocating this way for fastest access m_spells = new Spell*[m_maxSpell + 1]; memset((void*)m_spells, 0, sizeof(Spell*) * (m_maxSpell+1)); // iterate over the queue, removing spells from it and inserting them into // the spells table while (!spellQueue.isEmpty()) { // remove from queue newSpell = spellQueue.dequeue(); // insert into table m_spells[newSpell->spell()] = newSpell; } } else seqWarn("Spells::Spells(): Failed to open: '%s'", spellsFileName.latin1()); }
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: CMSG_PET_CAST_SPELL"); uint64 guid; uint8 castCount; uint32 spellId; uint8 castFlags; recvPacket >> guid >> castCount >> spellId >> castFlags; TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: CMSG_PET_CAST_SPELL, guid: " UI64FMTD ", castCount: %u, spellId %u, castFlags %u", guid, castCount, spellId, castFlags); // This opcode is also sent from charmed and possessed units (players and creatures) if (!_player->GetGuardianPet() && !_player->GetCharm()) return; Unit* caster = ObjectAccessor::GetUnit(*_player, guid); if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm())) { TC_LOG_ERROR(LOG_FILTER_NETWORKIO, "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s (GUID: %u).", uint32(GUID_LOPART(guid)), GetPlayer()->GetName().c_str(), GUID_LOPART(GetPlayer()->GetGUID())); return; } SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) { TC_LOG_ERROR(LOG_FILTER_NETWORKIO, "WORLD: unknown PET spell id %i", spellId); return; } // do not cast not learned spells if (!caster->HasSpell(spellId) || spellInfo->IsPassive()) return; SpellCastTargets targets; targets.Read(recvPacket, caster); HandleClientCastFlags(recvPacket, castFlags, targets); caster->ClearUnitState(UNIT_STATE_FOLLOW); Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE); spell->m_cast_count = castCount; // probably pending spell cast spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(NULL); if (result == SPELL_CAST_OK) { if (Creature* creature = caster->ToCreature()) { creature->AddCreatureSpellCooldown(spellId); if (Pet* pet = creature->ToPet()) { // 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 (pet->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk(PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(guid); } } spell->prepare(&(spell->m_targets)); } else { spell->SendPetCastResult(result); if (caster->GetTypeId() == TYPEID_PLAYER) { if (!caster->ToPlayer()->HasSpellCooldown(spellId)) GetPlayer()->SendClearCooldown(spellId, caster); } else { if (!caster->ToCreature()->HasSpellCooldown(spellId)) GetPlayer()->SendClearCooldown(spellId, caster); } spell->finish(false); delete spell; } }
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 = _player->GetMover(); if (mover != _player && 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()); 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; } Aura* triggeredByAura = mover->GetTriggeredByClientAura(spellId); if (mover->GetTypeId() == TYPEID_PLAYER) { // not have spell in spellbook or spell passive and not casted by client if ((!((Player*)mover)->HasActiveSpell(spellId) && !triggeredByAura) || IsPassiveSpell(spellInfo)) { sLog.outError("World: %s casts spell %u which he shouldn't have", mover->GetGuidStr().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) || IsPassiveSpell(spellInfo)) { // 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; } break; } } // client provided targets SpellCastTargets targets; recvPacket >> targets.ReadForCaster(mover); targets.ReadAdditionalData(recvPacket, cast_flags); // 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, triggeredByAura ? true : false, mover->GetObjectGuid(), triggeredByAura ? triggeredByAura->GetSpellProto() : NULL); spell->m_cast_count = cast_count; // set count of casts spell->m_glyphIndex = glyphIndex; spell->prepare(&targets, triggeredByAura); }
void WorldSession::HandleUseItemOpcode(WorldPacket & recvPacket) { CHECK_INWORLD_RETURN typedef std::list<Aura*> AuraList; Player* p_User = GetPlayer(); LOG_DETAIL("WORLD: got use Item packet, data length = %i", recvPacket.size()); int8 tmp1, slot; uint8 unk; //Alice : added in 3.0.2 uint64 item_guid; uint8 cn; uint32 spellId = 0; uint32 glyphIndex; bool found = false; recvPacket >> tmp1; recvPacket >> slot; recvPacket >> cn; recvPacket >> spellId; recvPacket >> item_guid; recvPacket >> glyphIndex; recvPacket >> unk; Item* tmpItem = NULL; tmpItem = p_User->GetItemInterface()->GetInventoryItem(tmp1, slot); if(!tmpItem) tmpItem = p_User->GetItemInterface()->GetInventoryItem(slot); if(!tmpItem) return; ItemPrototype* itemProto = tmpItem->GetProto(); // only some consumable items can be used in arenas if( ( itemProto->Class == ITEM_CLASS_CONSUMABLE ) && !itemProto->HasFlag( ITEM_FLAG_USEABLE_IN_ARENA ) && ( GetPlayer()->m_bg != NULL ) && IS_ARENA( GetPlayer()->m_bg->GetType() ) ) { GetPlayer()->GetItemInterface()->BuildInventoryChangeError(tmpItem, NULL, INV_ERR_NOT_DURING_ARENA_MATCH); return; } if(tmpItem->IsSoulbound()) // SouldBind item will be used after SouldBind() { if(sScriptMgr.CallScriptedItem(tmpItem, _player)) return; } if(_player->getDeathState() == CORPSE) return; if(itemProto->Bonding == ITEM_BIND_ON_USE) tmpItem->SoulBind(); if(sScriptMgr.CallScriptedItem(tmpItem, _player)) return; if(itemProto->InventoryType != 0 && !_player->GetItemInterface()->IsEquipped(itemProto->ItemId)) //Equipable items cannot be used before they're equipped. Prevents exploits return;//Prevents exploits such as keeping an on-use trinket in your bag and using WPE to use it from your bag in mid-combat. if(itemProto->QuestId) { // Item Starter Quest* qst = QuestStorage.LookupEntry(itemProto->QuestId); if(!qst) return; WorldPacket data; sQuestMgr.BuildQuestDetails(&data, qst, tmpItem, 0, language, _player); SendPacket(&data); } // Let's check if the item even has that spell for(int i = 0; i < 5; ++i) { if(itemProto->Spells[i].Trigger == USE && itemProto->Spells[i].Id == spellId) { found = true; break;//found 1 already } } // Let's see if it is an onuse spellid if(tmpItem->HasOnUseSpellID(spellId)) found = true; // We didn't find the spell, so the player is probably trying to cheat // with an edited itemcache.wdb // // Altough this could also happen after a DB update // if he/she didn't delete his/her cache. if(found == false) { this->Disconnect(); Anticheat_Log->writefromsession(this, "Player tried to use an item with a spell that didn't match the spell in the database."); Anticheat_Log->writefromsession(this, "Possibly corrupted or intentionally altered itemcache.wdb"); Anticheat_Log->writefromsession(this, "Itemid: %lu", itemProto->ItemId); Anticheat_Log->writefromsession(this, "Spellid: %lu", spellId); Anticheat_Log->writefromsession(this, "Player was disconnected"); return; } SpellCastTargets targets(recvPacket, _player->GetGUID()); SpellEntry* spellInfo = dbcSpell.LookupEntryForced(spellId); if(spellInfo == NULL) { LOG_DETAIL("ERROR: WORLD: unknown spell id %i", spellId); return; } if(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_ON_STAND_UP) { if(p_User->CombatStatus.IsInCombat() || p_User->IsMounted()) { _player->GetItemInterface()->BuildInventoryChangeError(tmpItem, NULL, INV_ERR_CANT_DO_IN_COMBAT); return; } if(p_User->GetStandState() != 1) p_User->SetStandState(STANDSTATE_SIT); // loop through the auras and removing existing eating spells } else // cebernic: why not stand up { if(!p_User->CombatStatus.IsInCombat() && !p_User->IsMounted()) { if(p_User->GetStandState()) { p_User->SetStandState(STANDSTATE_STAND); } } } // cebernic: remove stealth on using item if(!(spellInfo->AuraInterruptFlags & ATTRIBUTESEX_NOT_BREAK_STEALTH)) { if(p_User->IsStealth()) p_User->RemoveAllAuraType(SPELL_AURA_MOD_STEALTH); } if(itemProto->RequiredLevel) { if(_player->getLevel() < itemProto->RequiredLevel) { _player->GetItemInterface()->BuildInventoryChangeError(tmpItem, NULL, INV_ERR_ITEM_RANK_NOT_ENOUGH); return; } } if(itemProto->RequiredSkill) { if(!_player->_HasSkillLine(itemProto->RequiredSkill)) { _player->GetItemInterface()->BuildInventoryChangeError(tmpItem, NULL, INV_ERR_ITEM_RANK_NOT_ENOUGH); return; } if(itemProto->RequiredSkillRank) { if(_player->_GetSkillLineCurrent(itemProto->RequiredSkill, false) < itemProto->RequiredSkillRank) { _player->GetItemInterface()->BuildInventoryChangeError(tmpItem, NULL, INV_ERR_ITEM_RANK_NOT_ENOUGH); return; } } } if((itemProto->AllowableClass && !(_player->getClassMask() & itemProto->AllowableClass)) || (itemProto->AllowableRace && !(_player->getRaceMask() & itemProto->AllowableRace))) { _player->GetItemInterface()->BuildInventoryChangeError(tmpItem, NULL, INV_ERR_YOU_CAN_NEVER_USE_THAT_ITEM); return; } if(!_player->Cooldown_CanCast(spellInfo)) { _player->SendCastResult(spellInfo->Id, SPELL_FAILED_NOT_READY, cn, 0); return; } if(_player->m_currentSpell) { _player->SendCastResult(spellInfo->Id, SPELL_FAILED_SPELL_IN_PROGRESS, cn, 0); return; } if(itemProto->ForcedPetId >= 0) { if(itemProto->ForcedPetId == 0) { if(_player->GetGUID() != targets.m_unitTarget) { _player->SendCastResult(spellInfo->Id, SPELL_FAILED_BAD_TARGETS, cn, 0); return; } } else { if(!_player->GetSummon() || _player->GetSummon()->GetEntry() != (uint32)itemProto->ForcedPetId) { _player->SendCastResult(spellInfo->Id, SPELL_FAILED_SPELL_IN_PROGRESS, cn, 0); return; } } } Spell* spell = sSpellFactoryMgr.NewSpell(_player, spellInfo, false, NULL); spell->extra_cast_number = cn; spell->i_caster = tmpItem; spell->m_glyphslot = glyphIndex; //GetPlayer()->setCurrentSpell(spell); spell->prepare(&targets); #ifdef ENABLE_ACHIEVEMENTS _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, itemProto->ItemId, 0, 0); #endif }
void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) { uint32 spellId; recvPacket >> spellId; // ignore for remote control state (for player case) Unit* mover = _player->GetMover(); if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } DEBUG_LOG("WORLD: got cast spell packet, spellId - %u, data length = " SIZEFMTD, spellId, recvPacket.size()); 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; } if (mover->GetTypeId() == TYPEID_PLAYER) { // not have spell in spellbook or spell passive and not casted by client if (!((Player*)mover)->HasActiveSpell(spellId) || 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; } } else { // not have spell in spellbook or spell passive and not casted by client if (!((Creature*)mover)->HasSpell(spellId) || IsPassiveSpell(spellInfo)) { // cheater? kick? ban? recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } } // client provided targets SpellCastTargets targets; recvPacket >> targets.ReadForCaster(_player); // 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(_player, spellInfo, false); spell->prepare(&targets); }
void WorldSession::HandleSpellClick(WorldPacket & recvPacket) { CHECK_INWORLD_RETURN LOG_DETAIL("WORLD: got CMSG_SPELLCLICK packet, data length = %i", recvPacket.size()); if(_player->getDeathState() == CORPSE) return; uint64 target_guid; // this will store the guid of the object we are going to use it's spell. There must be a dbc that indicates what spells a unit has recvPacket >> target_guid; //we have only 1 example atm for entry : 28605 Unit* target_unit = _player->GetMapMgr()->GetUnit(target_guid); if(!target_unit) return; if( !_player->isInRange( target_unit, MAX_INTERACTION_RANGE ) ) return; if( target_unit->IsVehicle() ){ if( target_unit->GetVehicleComponent() != NULL ) target_unit->GetVehicleComponent()->AddPassenger( _player ); return; } uint32 creature_id = target_unit->GetEntry(); uint32 cast_spell_id = 0; if(!_player->HasAurasWithNameHash(SPELL_HASH_LIGHTWELL_RENEW) && target_unit->RemoveAura(59907)) { SpellClickSpell *sp = SpellClickSpellStorage.LookupEntry( creature_id ); if( sp == NULL ){ if( target_unit->IsCreature() ){ Creature *c = TO< Creature* >( target_unit ); sChatHandler.BlueSystemMessage( this, "NPC Id %u ( %s ) has no spellclick spell associated with it.", c->GetProto()->Id, c->GetCreatureInfo()->Name ); LOG_DETAIL("ERROR: Spellclick packet received for creature %u but there is no spell associated with it.", creature_id ); return; } } cast_spell_id = sp->SpellID; target_unit->CastSpell(_player, cast_spell_id, true); if(!target_unit->HasAura(59907)) TO_CREATURE(target_unit)->Despawn(0, 0); //IsCreature() check is not needed, refer to r2387 and r3230 return; } SpellClickSpell *sp = SpellClickSpellStorage.LookupEntry( creature_id ); if( sp == NULL ){ if( target_unit->IsCreature() ){ Creature *c = TO< Creature* >( target_unit ); sChatHandler.BlueSystemMessage( this, "NPC Id %u ( %s ) has no spellclick spell associated with it.", c->GetProto()->Id, c->GetCreatureInfo()->Name ); LOG_DETAIL("ERROR: Spellclick packet received for creature %u but there is no spell associated with it.", creature_id ); return; } } cast_spell_id = sp->SpellID; if(cast_spell_id == 0) return; SpellEntry* spellInfo = dbcSpell.LookupEntryForced(cast_spell_id); if(spellInfo == NULL) return; Spell* spell = sSpellFactoryMgr.NewSpell(_player, spellInfo, false, NULL); SpellCastTargets targets(target_guid); spell->prepare(&targets); }
void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) { sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL"); CHECK_PACKET_SIZE(recvPacket,8+1+4+1); uint64 guid; uint32 spellid; uint8 cast_count; uint8 unk_flags; // flags (if 0x02 - some additional data are received) recvPacket >> guid >> cast_count >> spellid >> unk_flags; sLog.outDebug("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count, spellid, unk_flags); if (!_player->GetPet() && !_player->GetCharm()) return; if (GUID_HIPART(guid) == HIGHGUID_PLAYER) return; Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid); if (!pet || (pet != _player->GetPet() && pet!= _player->GetCharm())) { sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } if (pet->GetGlobalCooldown() > 0) return; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) return; SpellCastTargets targets; if (!targets.read(&recvPacket,pet)) return; pet->clearUnitState(UNIT_STAT_FOLLOW); Spell *spell = new Spell(pet, spellInfo, false); spell->m_cast_count = cast_count; // probably pending spell cast spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(NULL); if (result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); if (pet->isPet()) { //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(((Pet*)pet)->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(guid); } spell->prepare(&(spell->m_targets)); } else { pet->SendPetCastFail(spellid, result); if (!pet->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
void WorldSession::HandleCastSpellOpcode(WorldPacket & recvPacket) { CHECK_INWORLD_RETURN uint32 spellId; uint8 cn, unk; //Alice : Added to 3.0.2 recvPacket >> cn >> spellId >> unk; // check for spell id SpellEntry* spellInfo = dbcSpell.LookupEntryForced(spellId); if(!spellInfo) { LOG_DETAIL("ERROR: WORLD: unknown spell id %i", spellId); return; } if(!_player->isAlive() && _player->GetShapeShift() != FORM_SPIRITOFREDEMPTION && !(spellInfo->Attributes & ATTRIBUTES_DEAD_CASTABLE)) //They're dead, not in spirit of redemption and the spell can't be cast while dead. return; LOG_DETAIL("WORLD: got cast spell packet, spellId - %i (%s), data length = %i", spellId, spellInfo->Name, recvPacket.size()); // Cheat Detection only if player and not from an item // this could f**k up things but meh it's needed ALOT of the newbs are using WPE now // WPE allows them to mod the outgoing packet and basically choose what ever spell they want :( if(!GetPlayer()->HasSpell(spellId)) { sCheatLog.writefromsession(this, "Cast spell %lu but doesn't have that spell.", spellId); LOG_DETAIL("WORLD: Spell isn't cast because player \'%s\' is cheating", GetPlayer()->GetName()); return; } if(spellInfo->Attributes & ATTRIBUTES_PASSIVE) { sCheatLog.writefromsession(this, "Cast passive spell %lu.", spellId); LOG_DETAIL("WORLD: Spell isn't cast because player \'%s\' is cheating", GetPlayer()->GetName()); return; } if(GetPlayer()->GetOnMeleeSpell() != spellId) { //autoshot 75 if((spellInfo->AttributesExB & ATTRIBUTESEXB_ACTIVATE_AUTO_SHOT) /*spellInfo->Attributes == 327698*/) // auto shot.. { //sLog.outString( "HandleSpellCast: Auto Shot-type spell cast (id %u, name %s)" , spellInfo->Id , spellInfo->Name ); Item* weapon = GetPlayer()->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_RANGED); if(!weapon) return; uint32 spellid; switch(weapon->GetProto()->SubClass) { case 2: // bows case 3: // guns case 18: // crossbow spellid = SPELL_RANGED_GENERAL; break; case 16: // thrown spellid = SPELL_RANGED_THROW; break; case 19: // wands spellid = SPELL_RANGED_WAND; break; default: spellid = 0; break; } if(!spellid) spellid = spellInfo->Id; if(!_player->m_onAutoShot) { _player->m_AutoShotTarget = _player->GetSelection(); uint32 duration = _player->GetBaseAttackTime(RANGED); SpellCastTargets targets(recvPacket, GetPlayer()->GetGUID()); if(!targets.m_unitTarget) { LOG_DEBUG("Cancelling auto-shot cast because targets.m_unitTarget is null!"); return; } SpellEntry* sp = dbcSpell.LookupEntry(spellid); _player->m_AutoShotSpell = sp; _player->m_AutoShotDuration = duration; //This will fix fast clicks if(_player->m_AutoShotAttackTimer < 500) _player->m_AutoShotAttackTimer = 500; _player->m_onAutoShot = true; } return; } if(_player->m_currentSpell) { if(_player->m_currentSpell->getState() == SPELL_STATE_CASTING) { // cancel the existing channel spell, cast this one _player->m_currentSpell->cancel(); } else { // send the error message _player->SendCastResult(spellInfo->Id, SPELL_FAILED_SPELL_IN_PROGRESS, cn, 0); return; } } SpellCastTargets targets(recvPacket, GetPlayer()->GetGUID()); // some anticheat stuff if(spellInfo->self_cast_only) { if(targets.m_unitTarget && targets.m_unitTarget != _player->GetGUID()) { // send the error message _player->SendCastResult(spellInfo->Id, SPELL_FAILED_BAD_TARGETS, cn, 0); return; } } Spell* spell = sSpellFactoryMgr.NewSpell(GetPlayer(), spellInfo, false, NULL); spell->extra_cast_number = cn; spell->prepare(&targets); } }
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { sLog->outDetail("WORLD: CMSG_PET_CAST_SPELL"); uint64 guid; uint8 castCount; uint32 spellId; uint8 castFlags; recvPacket >> guid >> castCount >> spellId >> castFlags; sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_PET_CAST_SPELL, guid: " UI64FMTD ", castCount: %u, spellId %u, castFlags %u", guid, castCount, spellId, castFlags); // This opcode is also sent from charmed and possessed units (players and creatures) if (!_player->GetGuardianPet() && !_player->GetCharm()) return; Unit* caster = ObjectAccessor::GetUnit(*_player, guid); if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm())) { sLog->outError("HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)), GetPlayer()->GetName()); return; } SpellInfo const *spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) { sLog->outError("WORLD: unknown PET spell id %i", spellId); return; } if (spellInfo->StartRecoveryCategory > 0) // Check if spell is affected by GCD if (caster->GetTypeId() == TYPEID_UNIT && caster->GetCharmInfo() && caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) { caster->SendPetCastFail(spellId, SPELL_FAILED_NOT_READY); return; } // do not cast not learned spells if (!caster->HasSpell(spellId) || spellInfo->IsPassive()) return; SpellCastTargets targets; targets.Read(recvPacket, caster); HandleClientCastFlags(recvPacket, castFlags, targets); caster->ClearUnitState(UNIT_STAT_FOLLOW); Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE); spell->m_cast_count = castCount; // probably pending spell cast spell->m_targets = targets; // TODO: need to check victim? SpellCastResult result; if (caster->m_movedPlayer) result = spell->CheckPetCast(caster->m_movedPlayer->GetSelectedUnit()); else result = spell->CheckPetCast(NULL); if (result == SPELL_CAST_OK) { if (caster->GetTypeId() == TYPEID_UNIT) { Creature* pet = caster->ToCreature(); pet->AddCreatureSpellCooldown(spellId); if (pet->isPet()) { Pet* p = (Pet*)pet; // 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 (p->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(guid); } } spell->prepare(&(spell->m_targets)); } else { caster->SendPetCastFail(spellId, result); if (caster->GetTypeId() == TYPEID_PLAYER) { if (!caster->ToPlayer()->HasSpellCooldown(spellId)) GetPlayer()->SendClearCooldown(spellId, caster); } else { if (!caster->ToCreature()->HasSpellCooldown(spellId)) GetPlayer()->SendClearCooldown(spellId, caster); } spell->finish(false); delete spell; } }
void WorldSession::HandlePetAction(WorldPacket & recv_data) { uint64 guid1; uint16 spellid; uint16 flag; uint64 guid2; recv_data >> guid1; //pet guid recv_data >> spellid; recv_data >> flag; //delete = 0x0700 CastSpell = C100 recv_data >> guid2; //tag guid // used also for charmed creature Unit* pet= ObjectAccessor::GetUnit(*_player, guid1); sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2))); if (!pet) { sLog.outError("Pet %u not exist.", uint32(GUID_LOPART(guid1))); return; } if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) { sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName()); return; } if (!pet->isAlive()) return; if (pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK)) return; CharmInfo *charmInfo = pet->GetCharmInfo(); if (!charmInfo) { sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId()); return; } switch(flag) { case ACT_COMMAND: //0x0700 // Possessed or shared vision pets are only able to attack //if ((pet->isPossessed() || pet->HasAuraType(SPELL_AURA_BIND_SIGHT)) && spellid != COMMAND_ATTACK) // return; switch(spellid) { case COMMAND_STAY: //flat=1792 //STAY pet->AttackStop(); pet->InterruptNonMeleeSpells(false); pet->GetMotionMaster()->MoveIdle(); charmInfo->SetCommandState(COMMAND_STAY); charmInfo->SetIsCommandAttack(false); charmInfo->SetIsAtStay(true); charmInfo->SetIsFollowing(false); charmInfo->SetIsReturning(false); charmInfo->SaveStayPosition(); break; case COMMAND_FOLLOW: //spellid=1792 //FOLLOW pet->AttackStop(); pet->InterruptNonMeleeSpells(false); pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,pet->GetFollowAngle()); charmInfo->SetCommandState(COMMAND_FOLLOW); charmInfo->SetIsCommandAttack(false); charmInfo->SetIsAtStay(false); charmInfo->SetIsReturning(true); charmInfo->SetIsFollowing(false); break; case COMMAND_ATTACK: //spellid=1792 //ATTACK { // Can't attack if owner is pacified if (_player->HasAuraType(SPELL_AURA_MOD_PACIFY)) { //pet->SendPetCastFail(spellid, SPELL_FAILED_PACIFIED); //TODO: Send proper error message to client return; } // only place where pet can be player Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, guid2); if (!TargetUnit) return; if (!pet->canAttack(TargetUnit)) return; // Not let attack through obstructions if (sWorld.getConfig(CONFIG_PET_LOS)) { if (!pet->IsWithinLOSInMap(TargetUnit)) return; } pet->clearUnitState(UNIT_STAT_FOLLOW); // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != TargetUnit || (pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack())) { if (pet->getVictim()) pet->AttackStop(); if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled) { charmInfo->SetIsCommandAttack(true); charmInfo->SetIsAtStay(false); charmInfo->SetIsFollowing(false); charmInfo->SetIsReturning(false); pet->ToCreature()->AI()->AttackStart(TargetUnit); //10% chance to play special pet attack talk, else growl if (pet->ToCreature()->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) pet->SendPetTalk((uint32)PET_TALK_ATTACK); else { // 90% chance for pet and 100% chance for charmed creature pet->SendPetAIReaction(guid1); } } else // charmed player { if (pet->getVictim() && pet->getVictim() != TargetUnit) pet->AttackStop(); charmInfo->SetIsCommandAttack(true); charmInfo->SetIsAtStay(false); charmInfo->SetIsFollowing(false); charmInfo->SetIsReturning(false); pet->Attack(TargetUnit,true); pet->SendPetAIReaction(guid1); } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) if (pet->ToCreature()->isPet()) { Pet* p = (Pet*)pet; if (p->getPetType() == HUNTER_PET) _player->RemovePet(p,PET_SAVE_AS_DELETED); else //dismissing a summoned pet is like killing them (this prevents returning a soulshard...) p->setDeathState(CORPSE); } else // charmed or possessed _player->Uncharm(); break; default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); } break; case ACT_REACTION: // 0x600 switch(spellid) { case REACT_PASSIVE: //passive pet->AttackStop(); case REACT_DEFENSIVE: //recovery case REACT_AGGRESSIVE: //activete if (pet->GetTypeId() == TYPEID_UNIT) pet->ToCreature()->SetReactState(ReactStates(spellid)); break; } break; case ACT_DISABLED: //0x8100 spell (disabled), ignore case ACT_CAST: //0x0100 case ACT_ENABLED: //0xc100 spell { Unit* unit_target = NULL; if (pet->ToCreature()->GetGlobalCooldown() > 0) return; if (guid2) unit_target = ObjectAccessor::GetUnit(*_player,guid2); // do not cast unknown spells SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } for (uint32 i = 0; i < 3;i++) { if (spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENEMY_SRC || spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENEMY_DST || spellInfo->EffectImplicitTargetA[i] == TARGET_DEST_DYNOBJ_ENEMY) return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) return; // Clear the flags as if owner clicked 'attack'. AI will reset them // after AttackStart, even if spell failed if (pet->GetCharmInfo()) { pet->GetCharmInfo()->SetIsAtStay(false); pet->GetCharmInfo()->SetIsCommandAttack(true); pet->GetCharmInfo()->SetIsReturning(false); pet->GetCharmInfo()->SetIsFollowing(false); } Spell *spell = new Spell(pet, spellInfo, false); int16 result = spell->PetCanCast(unit_target); //auto turn to target unless possessed if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed()) { pet->SetInFront(unit_target); if (unit_target->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer(unit_target->ToPlayer()); if (Unit* powner = pet->GetCharmerOrOwner()) if (powner->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer(powner->ToPlayer()); result = -1; } if (result == -1) { pet->ToCreature()->AddCreatureSpellCooldown(spellid); if (pet->ToCreature()->isPet()) ((Pet*)pet)->CheckLearning(spellid); unit_target = spell->m_targets.getUnitTarget(); //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 (pet->ToCreature()->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else { pet->SendPetAIReaction(guid1); } if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed()) { // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != unit_target) { if (pet->getVictim()) pet->AttackStop(); pet->GetMotionMaster()->Clear(); if (pet->ToCreature()->IsAIEnabled) pet->ToCreature()->AI()->AttackStart(unit_target); } } spell->prepare(&(spell->m_targets)); } else { if (pet->isPossessed()) { WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); data << uint32(spellid) << uint8(2) << uint8(result); switch (result) { case SPELL_FAILED_REQUIRES_SPELL_FOCUS: data << uint32(spellInfo->RequiresSpellFocus); break; case SPELL_FAILED_REQUIRES_AREA: data << uint32(spellInfo->AreaId); break; } SendPacket(&data); } else pet->SendPetCastFail(spellid, result); if (!pet->ToCreature()->HasSpellCooldown(spellid)) pet->SendPetClearCooldown(spellid); spell->finish(false); delete spell; // reset specific flags in case of spell fail. AI will reset other flags if (pet->GetCharmInfo()) pet->GetCharmInfo()->SetIsCommandAttack(false); } break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); } }