void WorldSession::HandlePetAbandon(WorldPacket& recvData) { uint64 guid; recvData >> guid; //pet guid TC_LOG_INFO("network", "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid)); if (!_player->IsInWorld()) return; // pet/charmed Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); if (pet) { if (pet->IsPet()) _player->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED); else if (pet->GetGUID() == _player->GetCharmGUID()) _player->StopCastingCharm(); } }
void WorldSession::HandlePetAbandon(WorldPacket& recvData) { ObjectGuid guid; recvData >> guid; //pet guid TC_LOG_DEBUG("network", "WORLD: Received CMSG_PET_ABANDON %s", guid.ToString().c_str()); if (!_player->IsInWorld()) return; // pet/charmed Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); if (pet) { if (pet->IsPet()) _player->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED); else if (pet->GetGUID() == _player->GetCharmGUID()) _player->StopCastingCharm(); } }
void WorldSession::SendPetNameQuery(ObjectGuid petGuid, uint64 petNumber) { Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, petGuid); if (!pet) { WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (8 + 1)); data.WriteBit(0); data.FlushBits(); data << uint64(petNumber); _player->GetSession()->SendPacket(&data); return; } WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (8 + 1 + 1 + 5 + pet->GetName().size() + 4)); data.WriteBit(1); // has data bool declinedNames = pet->IsPet() && ((Pet*)pet)->GetDeclinedNames(); for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) { if (declinedNames) data.WriteBits(((Pet*)pet)->GetDeclinedNames()->name[i].size(), 7); else data.WriteBits(0, 7); } data.WriteBit(0); // unknown data.WriteBits(pet->GetName().size(), 8); data.FlushBits(); if (declinedNames) for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) data.WriteString(((Pet*)pet)->GetDeclinedNames()->name[i]); data.WriteString(pet->GetName()); data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP)); data << uint64(petNumber); _player->GetSession()->SendPacket(&data); }
bool RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff) { if (creature.hasUnitState(UNIT_STAT_NOT_MOVE)) { i_nextMoveTime.Update(i_nextMoveTime.GetExpiry()); // Expire the timer creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } i_nextMoveTime.Update(diff); if (i_destinationHolder.HasArrived() && !creature.IsStopped() && !creature.CanFly()) creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); if (!i_destinationHolder.HasArrived() && creature.IsStopped()) creature.addUnitState(UNIT_STAT_ROAMING_MOVE); CreatureTraveller traveller(creature); if (i_destinationHolder.UpdateTraveller(traveller, diff, false, true)) { if (!IsActive(creature)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost if (i_nextMoveTime.Passed()) { if (creature.CanFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); else creature.AddSplineFlag(SPLINEFLAG_WALKMODE); _setRandomLocation(creature); } else if (creature.IsPet() && creature.GetOwner() && !creature.IsWithinDist(creature.GetOwner(), PET_FOLLOW_DIST+2.5f)) { creature.AddSplineFlag(SPLINEFLAG_WALKMODE); _setRandomLocation(creature); } } return true; }
void WorldSession::SendPetNameQuery(ObjectGuid petguid, uint32 petnumber) { Creature* pet = _player->GetMap()->GetAnyTypeCreature(petguid); if (!pet || !pet->GetCharmInfo() || pet->GetCharmInfo()->GetPetNumber() != petnumber) { WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+1+4+1)); data << uint32(petnumber); data << uint8(0); data << uint32(0); data << uint8(0); _player->GetSession()->SendPacket(&data); return; } char const* name = pet->GetName(); // creature pets have localization like other creatures if (!pet->GetOwnerGuid().IsPlayer()) { int loc_idx = GetSessionDbLocaleIndex(); sObjectMgr.GetCreatureLocaleStrings(pet->GetEntry(), loc_idx, &name); } WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+strlen(name)+1)); data << uint32(petnumber); data << name; data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP)); if (pet->IsPet() && ((Pet*)pet)->GetDeclinedNames()) { data << uint8(1); for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) data << ((Pet*)pet)->GetDeclinedNames()->name[i]; } else data << uint8(0); _player->GetSession()->SendPacket(&data); }
void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket) { DETAIL_LOG("CMSG_PET_SPELL_AUTOCAST"); ObjectGuid guid; uint32 spellid; uint8 state; // 1 for on, 0 for off recvPacket >> guid >> spellid >> state; Unit* petUnit = _player->GetMap()->GetUnit(guid); if (!petUnit || (guid != _player->GetPetGuid() && !_player->HasCharm(guid))) { sLog.outError("HandlePetSpellAutocastOpcode. %s isn't pet of %s .", guid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } Creature* petCreature = petUnit->GetTypeId() == TYPEID_UNIT ? static_cast<Creature*>(petUnit) : nullptr; Pet* pet = (petCreature && petCreature->IsPet()) ? static_cast<Pet*>(petUnit) : nullptr; // do not add not learned spells/ passive spells if (!petUnit->HasSpell(spellid) || !IsAutocastable(spellid)) return; CharmInfo* charmInfo = petUnit->GetCharmInfo(); if (!charmInfo) { sLog.outError("WorldSession::HandlePetSpellAutocastOpcod: %s is considered pet-like but doesn't have a charminfo!", guid.GetString().c_str()); return; } if (petUnit->HasCharmer()) // state can be used as boolean petUnit->GetCharmInfo()->ToggleCreatureAutocast(spellid, state != 0); else if (pet) pet->ToggleAutocast(spellid, state != 0); charmInfo->SetSpellAutocast(spellid, state != 0); }
void WorldSession::SendPetNameQuery(ObjectGuid petguid, uint32 petnumber) { Creature* pet = _player->GetMap()->GetAnyTypeCreature(petguid); if (!pet || !pet->GetCharmInfo() || pet->GetCharmInfo()->GetPetNumber() != petnumber) return; std::string name = pet->GetName(); // creature pets have localization like other creatures if (!pet->GetOwnerGuid().IsPlayer()) { int loc_idx = GetSessionDbLocaleIndex(); if (loc_idx >= 0) { if (CreatureLocale const *cl = sObjectMgr.GetCreatureLocale(pet->GetEntry())) { if (cl->Name.size() > size_t(loc_idx) && !cl->Name[loc_idx].empty()) name = cl->Name[loc_idx]; } } } WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+name.size()+1)); data << uint32(petnumber); data << name.c_str(); data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP)); if (pet->IsPet() && ((Pet*)pet)->GetDeclinedNames()) { data << uint8(1); for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) data << ((Pet*)pet)->GetDeclinedNames()->name[i]; } else data << uint8(0); _player->GetSession()->SendPacket(&data); }
/* Kazzak abilities to heal himself ( 75K heal ) when he kills an enemy: Should only be triggered on enemy above lvl 50. (To prevent exploit by summoning low lvl player and make them die via the shadow bolt volley to heal Kazzak). */ void KilledUnit(Unit* victim) { Player* affectedPlayer; Creature* creature; switch (victim->GetTypeId()) { /** Prevent Kazzak to use his healing spell if player got a level lower than 50 */ case TYPEID_PLAYER: affectedPlayer = victim->GetAffectingPlayer(); if (!affectedPlayer) return; if (affectedPlayer->getLevel() < 50) return; break; case TYPEID_UNIT: /** Kazzak can use his healing ability on Totem and Pets */ creature = victim->ToCreature(); if (!creature->IsPet() && !creature->IsTotem()) return; /** Prevent Kazzak to use his healing spell if the pet got a level lower than 50 */ if (creature->getLevel() < 50) return; break; default: return; break; } DoCastSpellIfCan(m_creature, SPELL_CAPTURESOUL); DoScriptText(SAY_KILL, m_creature); }
void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber) { Creature* pet = _player->GetMap()->GetAnyTypeCreature(petguid); if(!pet || !pet->GetCharmInfo() || pet->GetCharmInfo()->GetPetNumber() != petnumber) return; std::string name = pet->GetName(); WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+name.size()+1)); data << uint32(petnumber); data << name.c_str(); data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP)); if( pet->IsPet() && ((Pet*)pet)->GetDeclinedNames() ) { data << uint8(1); for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) data << ((Pet*)pet)->GetDeclinedNames()->name[i]; } else data << uint8(0); _player->GetSession()->SendPacket(&data); }
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->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; } 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_STATE_MOVING); Spell* spell = new Spell(caster, spellInfo, false); spell->m_targets = targets; SpellCastResult 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; 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) { 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->GetMasterGuid()) { 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) { if (petUnit->hasUnitState(UNIT_STAT_POSSESSED)) { // 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 && petUnit->CanAttack(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_DISMISS: _player->BreakCharmOutgoing(petUnit); 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; } } if (!petUnit->HasCharmer()) return; } switch (flag) { case ACT_COMMAND: // 0x07 switch (spellid) { case COMMAND_STAY: // flat=1792 // STAY { if (!petUnit->hasUnitState(UNIT_STAT_POSSESSED)) { petUnit->StopMoving(); petUnit->GetMotionMaster()->Clear(); } petUnit->AttackStop(true, true); charmInfo->SetCommandState(COMMAND_STAY); break; } case COMMAND_FOLLOW: // spellid=1792 // FOLLOW { if (!petUnit->hasUnitState(UNIT_STAT_POSSESSED)) { petUnit->StopMoving(); petUnit->GetMotionMaster()->Clear(); charmInfo->SetIsRetreating(true); } petUnit->AttackStop(true, true); charmInfo->SetCommandState(COMMAND_FOLLOW); break; } case COMMAND_ATTACK: // spellid=1792 // ATTACK { charmInfo->SetIsRetreating(); charmInfo->SetSpellOpener(); Unit* targetUnit = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; if (targetUnit && targetUnit != petUnit && petUnit->CanAttack(targetUnit) && targetUnit->isInAccessablePlaceFor((Creature*)petUnit)) { // 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_POSSESSED)) { petUnit->GetMotionMaster()->Clear(); petUnit->AI()->AttackStart(targetUnit); if (pet) { // 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 petUnit->Attack(targetUnit, true); } } break; } case COMMAND_DISMISS: // dismiss permanent pet, remove temporary pet, uncharm unit { if (pet) { pet->PlayDismissSound(); // No action for Hunter pets, Hunters must use their Dismiss Pet spell if (pet->getPetType() != HUNTER_PET) pet->ForcedDespawn(); } else { // dismissing a summoned pet is like killing them (this prevents returning a soulshard...) if (creature && creature->IsTemporarySummon()) creature->ForcedDespawn(); else _player->BreakCharmOutgoing(petUnit); } charmInfo->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 { petUnit->AttackStop(true, true); charmInfo->SetSpellOpener(); } case REACT_DEFENSIVE: // recovery case REACT_AGGRESSIVE: // activete { petUnit->AI()->SetReactState(ReactStates(spellid)); break; } } break; case ACT_DISABLED: // 0x81 spell (disabled), ignore case ACT_PASSIVE: // 0x01 case ACT_ENABLED: // 0xC1 spell { charmInfo->SetIsRetreating(); charmInfo->SetSpellOpener(); Unit* unit_target = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; // do not cast unknown spells SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } // do not cast not learned spells if (IsPassiveSpell(spellInfo) || !petUnit->HasSpell(spellid)) return; if (!petUnit->IsSpellReady(*spellInfo)) return; for (unsigned int i : spellInfo->EffectImplicitTargetA) { if (i == TARGET_ENUM_UNITS_ENEMY_AOE_AT_SRC_LOC || i == TARGET_ENUM_UNITS_ENEMY_AOE_AT_DEST_LOC || i == TARGET_ENUM_UNITS_ENEMY_AOE_AT_DYNOBJ_LOC) return; } petUnit->clearUnitState(UNIT_STAT_MOVING); uint32 flags = TRIGGERED_NONE; if (!petUnit->hasUnitState(UNIT_STAT_POSSESSED)) flags |= TRIGGERED_PET_CAST; Spell* spell = new Spell(petUnit, spellInfo, flags); SpellCastResult result = spell->CheckPetCast(unit_target); const SpellRangeEntry* sRange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); if (unit_target && !(petUnit->IsWithinDistInMap(unit_target, sRange->maxRange) && petUnit->IsWithinLOSInMap(unit_target)) && petUnit->CanAttackNow(unit_target)) { charmInfo->SetSpellOpener(spellid, sRange->minRange, sRange->maxRange); spell->finish(false); delete spell; petUnit->AttackStop(); if (!petUnit->hasUnitState(UNIT_STAT_POSSESSED)) { petUnit->GetMotionMaster()->Clear(); petUnit->AI()->AttackStart(unit_target); // 10% chance to play special warlock pet attack talk, else growl if (pet && pet->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10)) petUnit->SendPetTalk((uint32)PET_TALK_ATTACK); petUnit->SendPetAIReaction(); } else petUnit->Attack(unit_target, true); return; } // auto turn to target unless possessed if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !petUnit->hasUnitState(UNIT_STAT_POSSESSED)) { if (unit_target) { petUnit->SetInFront(unit_target); if (unit_target->GetTypeId() == TYPEID_PLAYER) petUnit->SendCreateUpdateToPlayer((Player*)unit_target); } else if (Unit* unit_target2 = spell->m_targets.getUnitTarget()) { petUnit->SetInFront(unit_target2); if (unit_target2->GetTypeId() == TYPEID_PLAYER) petUnit->SendCreateUpdateToPlayer((Player*)unit_target2); } if (Unit* powner = petUnit->GetMaster()) if (powner->GetTypeId() == TYPEID_PLAYER) petUnit->SendCreateUpdateToPlayer((Player*)powner); result = SPELL_CAST_OK; } if (result == SPELL_CAST_OK) { charmInfo->SetSpellOpener(); spell->SpellStart(&(spell->m_targets)); } else { if (creature && creature->IsSpellReady(*spellInfo)) GetPlayer()->SendClearCooldown(spellid, petUnit); charmInfo->SetSpellOpener(); spell->finish(false); delete spell; } break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
void PetAI::UpdateAI(const uint32 diff) { if (!m_unit->isAlive()) return; Creature* creature = (m_unit->GetTypeId() == TYPEID_UNIT) ? static_cast<Creature*>(m_unit) : nullptr; Pet* pet = (creature && creature->IsPet()) ? static_cast<Pet*>(m_unit) : nullptr; Unit* owner = m_unit->GetMaster(); if (!owner) return; Unit* victim = (pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) ? nullptr : m_unit->getVictim(); if (m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; if (inCombat && !victim) { m_unit->AttackStop(true, true); inCombat = false; } CharmInfo* charminfo = m_unit->GetCharmInfo(); MANGOS_ASSERT(charminfo); if (charminfo->GetIsRetreating()) { if (!owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2))) { if (!m_unit->hasUnitState(UNIT_STAT_FOLLOW)) m_unit->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); return; } else charminfo->SetIsRetreating(); } else if (charminfo->GetSpellOpener() != 0) // have opener stored { uint32 minRange = charminfo->GetSpellOpenerMinRange(); if (!(victim = m_unit->getVictim()) || (minRange != 0 && m_unit->IsWithinDistInMap(victim, minRange))) charminfo->SetSpellOpener(); else if (m_unit->IsWithinDistInMap(victim, charminfo->GetSpellOpenerMaxRange()) && m_unit->IsWithinLOSInMap(victim)) { // stop moving m_unit->clearUnitState(UNIT_STAT_MOVING); // auto turn to target m_unit->SetInFront(victim); if (victim->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)victim); if (owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); uint32 spell_id = charminfo->GetSpellOpener(); SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spell_id); Spell* spell = new Spell(m_unit, spellInfo, false); SpellCastResult result = spell->CheckPetCast(victim); if (result == SPELL_CAST_OK) spell->SpellStart(&(spell->m_targets)); else delete spell; charminfo->SetSpellOpener(); } else return; } // Auto cast (casted only in combat or persistent spells in any state) else if (!m_unit->IsNonMeleeSpellCasted(false)) { typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList; TargetSpellList targetSpellStore; if (pet) { for (uint8 i = 0; i < pet->GetPetAutoSpellSize(); ++i) { uint32 spellID = pet->GetPetAutoSpellOnPos(i); if (!spellID) continue; SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellID); if (!spellInfo) continue; if (!m_unit->IsSpellReady(*spellInfo)) continue; // ignore some combinations of combat state and combat/non combat spells if (!inCombat) { // ignore attacking spells, and allow only self/around spells if (!IsPositiveSpell(spellInfo->Id)) continue; // non combat spells allowed // only pet spells have IsNonCombatSpell and not fit this requirements: // Consume Shadows, Lesser Invisibility, so ignore checks for its if (!IsNonCombatSpell(spellInfo)) { int32 duration = GetSpellDuration(spellInfo); int32 cooldown = GetSpellRecoveryTime(spellInfo); // allow only spell not on cooldown if (cooldown != 0 && duration < cooldown) continue; // not allow instant kill auto casts as full health cost if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_INSTAKILL)) continue; } } // just ignore non-combat spells else if (IsNonCombatSpell(spellInfo)) continue; Spell* spell = new Spell(m_unit, spellInfo, false); if (inCombat && !m_unit->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(victim)) { targetSpellStore.push_back(TargetSpellList::value_type(victim, spell)); continue; } else { bool spellUsed = false; for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { Unit* Target = m_unit->GetMap()->GetUnit(*tar); // only buff targets that are in combat, unless the spell can only be cast while out of combat if (!Target) continue; if (spell->CanAutoCast(Target)) { targetSpellStore.push_back(TargetSpellList::value_type(Target, spell)); spellUsed = true; break; } } if (!spellUsed) delete spell; } } } // found units to cast on to if (!targetSpellStore.empty()) { uint32 index = urand(0, targetSpellStore.size() - 1); Spell* spell = targetSpellStore[index].second; Unit* target = targetSpellStore[index].first; targetSpellStore.erase(targetSpellStore.begin() + index); SpellCastTargets targets; targets.setUnitTarget(target); if (!m_unit->HasInArc(M_PI_F, target)) { m_unit->SetInFront(target); if (target->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)target); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); } spell->SpellStart(&targets); } // deleted cached Spell objects for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) delete itr->second; } // Stop here if casting spell (No melee and no movement) if (m_unit->IsNonMeleeSpellCasted(false)) return; // we may get our actions disabled during spell casting, so do entire recheck for victim victim = (pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) ? nullptr : m_unit->getVictim(); if (victim) { // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. // This is needed for charmed creatures, as once their target was reset other effects can trigger threat if (!victim->isTargetableForAttack()) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_unit->GetGUIDLow()); m_unit->CombatStop(); inCombat = false; return; } // if pet misses its target, it will also be the first in threat list if ((!creature || !(creature->GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_NO_MELEE)) && m_unit->CanReachWithMeleeAttack(victim)) { if (!m_unit->HasInArc(2 * M_PI_F / 3, victim)) { m_unit->SetInFront(victim); if (victim->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)victim); if (owner && owner->GetTypeId() == TYPEID_PLAYER) m_unit->SendCreateUpdateToPlayer((Player*)owner); } DoMeleeAttackIfReady(); } else if (!m_unit->hasUnitState(UNIT_STAT_MOVING)) AttackStart(victim); } else if (owner) { CharmInfo* charmInfo = m_unit->GetCharmInfo(); if (owner->isInCombat() && !(charmInfo && charmInfo->HasReactState(REACT_PASSIVE))) AttackStart(owner->getAttackerForHelper()); else { if (charmInfo && charmInfo->HasCommandState(COMMAND_STAY)) { //if stay command is set but we don't have stay pos set then we need to establish current pos as stay position if (!charminfo->IsStayPosSet()) charminfo->SetStayPosition(true); float stayPosX = charminfo->GetStayPosX(); float stayPosY = charminfo->GetStayPosY(); float stayPosZ = charminfo->GetStayPosZ(); if (m_unit->GetPositionX() == stayPosX && m_unit->GetPositionY() == stayPosY && m_unit->GetPositionZ() == stayPosZ) { float StayPosO = charminfo->GetStayPosO(); if (m_unit->hasUnitState(UNIT_STAT_MOVING)) { m_unit->GetMotionMaster()->Clear(false); m_unit->GetMotionMaster()->MoveIdle(); } else if (m_unit->GetOrientation() != StayPosO) m_unit->SetOrientation(StayPosO); } else m_unit->GetMotionMaster()->MovePoint(0, stayPosX, stayPosY, stayPosZ, false); } else if (m_unit->hasUnitState(UNIT_STAT_FOLLOW)) { if (owner->IsWithinDistInMap(m_unit, PET_FOLLOW_DIST)) { m_unit->GetMotionMaster()->Clear(false); m_unit->GetMotionMaster()->MoveIdle(); } } else if (charmInfo && charmInfo->HasCommandState(COMMAND_FOLLOW) && !owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2))) m_unit->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); } } }
bool ChatHandler::HandleDeleteCommand(const char* args, WorldSession *m_session) { Creature* unit = getSelectedCreature(m_session, false); if(!unit) { SystemMessage(m_session, "You should select a creature."); return true; } else if(unit->IsPet() || unit->IsSummon()) { SystemMessage(m_session, "You can't delete playerpets."); return true; } if( unit->m_spawn != NULL && !m_session->CanUseCommand('z') ) { SystemMessage(m_session, "You do not have permission to do that. Please contact higher staff for removing of saved spawns."); return true; } if(unit->IsVehicle()) { Vehicle* veh = TO_VEHICLE(unit); for(int i = 0; i < 8; i++) { if(!veh->GetPassenger(i)) continue; // Remove any players if(veh->GetPassenger(i)->IsPlayer()) veh->RemovePassenger(veh->GetPassenger(i)); else // Remove any units. veh->GetPassenger(i)->RemoveFromWorld(true); } } sWorld.LogGM(m_session, "used npc delete, sqlid %u, creature %s, pos %f %f %f", unit->m_spawn ? unit->m_spawn->id : 0, unit->GetCreatureInfo() ? unit->GetCreatureInfo()->Name : "wtfbbqhax", unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); BlueSystemMessage(m_session, "Deleted creature ID %u", unit->spawnid); unit->DeleteFromDB(); if(!unit->IsInWorld()) return true; MapMgr* unitMgr = unit->GetMapMgr(); if(unit->m_spawn) { uint32 cellx = unitMgr->GetPosX(unit->m_spawn->x); uint32 celly = unitMgr->GetPosX(unit->m_spawn->y); if(cellx <= _sizeX && celly <= _sizeY ) { CellSpawns *c = unitMgr->GetBaseMap()->GetSpawnsList(cellx, celly); if( c != NULL ) { CreatureSpawnList::iterator itr, itr2; for(itr = c->CreatureSpawns.begin(); itr != c->CreatureSpawns.end();) { itr2 = itr++; if((*itr2) == unit->m_spawn) { c->CreatureSpawns.erase(itr2); delete unit->m_spawn; break; } } } } } unit->RemoveFromWorld(false, true); if(unit->IsVehicle()) TO_VEHICLE(unit)->Destruct(); else unit->Destruct(); m_session->GetPlayer()->SetSelection(NULL); return true; }
void WorldSession::HandlePetSetAction(WorldPacket& recv_data) { DETAIL_LOG("HandlePetSetAction. CMSG_PET_SET_ACTION"); ObjectGuid petGuid; uint8 count; recv_data >> petGuid; Creature* pet = _player->GetMap()->GetAnyTypeCreature(petGuid); if (!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) { sLog.outError("HandlePetSetAction: Unknown pet or pet owner."); return; } CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) { sLog.outError("WorldSession::HandlePetSetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId()); return; } // pet can have action bar disabled if (pet->IsPet() && charmInfo->HasState(CHARM_STATE_ACTION,ACTIONS_DISABLE)) return; // if pet is chained - used first pet action bar if (((Pet*)pet)->GetPetCounter()) return; count = (recv_data.size() == 24) ? 2 : 1; uint32 position[2]; uint32 data[2]; bool move_command = false; for (uint8 i = 0; i < count; ++i) { recv_data >> position[i]; recv_data >> data[i]; uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]); // ignore invalid position if (position[i] >= MAX_UNIT_ACTION_BAR_INDEX) return; // in the normal case, command and reaction buttons can only be moved, not removed // at moving count ==2, at removing count == 1 // ignore attempt to remove command|reaction buttons (not possible at normal case) if (act_state == ACT_COMMAND || act_state == ACT_REACTION) { if (count == 1) return; move_command = true; } } // check swap (at command->spell swap client remove spell first in another packet, so check only command move correctness) if (move_command) { uint8 act_state_0 = UNIT_ACTION_BUTTON_TYPE(data[0]); if (act_state_0 == ACT_COMMAND || act_state_0 == ACT_REACTION) { uint32 spell_id_0 = UNIT_ACTION_BUTTON_ACTION(data[0]); UnitActionBarEntry const* actionEntry_1 = charmInfo->GetActionBarEntry(position[1]); if (!actionEntry_1 || spell_id_0 != actionEntry_1->GetAction() || act_state_0 != actionEntry_1->GetType()) return; } uint8 act_state_1 = UNIT_ACTION_BUTTON_TYPE(data[1]); if (act_state_1 == ACT_COMMAND || act_state_1 == ACT_REACTION) { uint32 spell_id_1 = UNIT_ACTION_BUTTON_ACTION(data[1]); UnitActionBarEntry const* actionEntry_0 = charmInfo->GetActionBarEntry(position[0]); if (!actionEntry_0 || spell_id_1 != actionEntry_0->GetAction() || act_state_1 != actionEntry_0->GetType()) return; } } for (uint8 i = 0; i < count; ++i) { uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(data[i]); uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]); DETAIL_LOG("Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position[i], spell_id, uint32(act_state)); // if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add if ((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && ((Pet*)pet)->HasSpell(spell_id)) { // sign for autocast on/off if (act_state == ACT_ENABLED || act_state == ACT_DISABLED) { bool enabled = act_state == ACT_ENABLED; if (pet->isCharmed()) charmInfo->ToggleCreatureAutocast(spell_id, enabled); else { GuidSet const& groupPets = _player->GetPets(); if (!groupPets.empty()) { for (GuidSet::const_iterator itr = groupPets.begin(); itr != groupPets.end(); ++itr) { if (Pet* pPet = GetPlayer()->GetMap()->GetPet(*itr)) { if (pPet->IsInWorld()) pPet->ToggleAutocast(spell_id, enabled); } } } } } charmInfo->SetActionBar(position[i], spell_id, ActiveStates(act_state)); } } }
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; } 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_STATE_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::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL"); ObjectGuid guid; uint32 spellid; recvPacket >> guid >> spellid; DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, %s, spellid %u", guid.GetString().c_str(), spellid); 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(), GetPlayer()->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; // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(pet); pet->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(pet, spellInfo, false); spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(nullptr); if (result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); if (pet->IsPet()) ((Pet*)pet)->CheckLearning(spellid); spell->SpellStart(&(spell->m_targets)); } 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); spell->finish(false); delete spell; } }
void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) { DETAIL_LOG( "HandlePetSetAction. CMSG_PET_SET_ACTION" ); uint64 petguid; uint8 count; recv_data >> petguid; // FIXME: charmed case //Pet* pet = ObjectAccessor::Instance().GetPet(petguid); if(ObjectAccessor::FindPlayer(petguid)) return; Creature* pet = _player->GetMap()->GetAnyTypeCreature(petguid); if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) { sLog.outError( "HandlePetSetAction: Unknown pet or pet owner." ); return; } // pet can have action bar disabled if(pet->IsPet() && ((Pet*)pet)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) return; CharmInfo *charmInfo = pet->GetCharmInfo(); if(!charmInfo) { sLog.outError("WorldSession::HandlePetSetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId()); return; } count = (recv_data.size() == 24) ? 2 : 1; uint32 position[2]; uint32 data[2]; bool move_command = false; for(uint8 i = 0; i < count; ++i) { recv_data >> position[i]; recv_data >> data[i]; uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]); //ignore invalid position if(position[i] >= MAX_UNIT_ACTION_BAR_INDEX) return; // in the normal case, command and reaction buttons can only be moved, not removed // at moving count ==2, at removing count == 1 // ignore attempt to remove command|reaction buttons (not possible at normal case) if (act_state == ACT_COMMAND || act_state == ACT_REACTION) { if (count == 1) return; move_command = true; } } // check swap (at command->spell swap client remove spell first in another packet, so check only command move correctness) if (move_command) { uint8 act_state_0 = UNIT_ACTION_BUTTON_TYPE(data[0]); if(act_state_0 == ACT_COMMAND || act_state_0 == ACT_REACTION) { uint32 spell_id_0 = UNIT_ACTION_BUTTON_ACTION(data[0]); UnitActionBarEntry const* actionEntry_1 = charmInfo->GetActionBarEntry(position[1]); if (!actionEntry_1 || spell_id_0 != actionEntry_1->GetAction() || act_state_0 != actionEntry_1->GetType()) return; } uint8 act_state_1 = UNIT_ACTION_BUTTON_TYPE(data[1]); if(act_state_1 == ACT_COMMAND || act_state_1 == ACT_REACTION) { uint32 spell_id_1 = UNIT_ACTION_BUTTON_ACTION(data[1]); UnitActionBarEntry const* actionEntry_0 = charmInfo->GetActionBarEntry(position[0]); if (!actionEntry_0 || spell_id_1 != actionEntry_0->GetAction() || act_state_1 != actionEntry_0->GetType()) return; } } for(uint8 i = 0; i < count; ++i) { uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(data[i]); uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]); DETAIL_LOG( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position[i], spell_id, uint32(act_state)); //if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !pet->HasSpell(spell_id))) { //sign for autocast if(act_state == ACT_ENABLED && spell_id) { if(pet->isCharmed()) charmInfo->ToggleCreatureAutocast(spell_id, true); else ((Pet*)pet)->ToggleAutocast(spell_id, true); } //sign for no/turn off autocast else if(act_state == ACT_DISABLED && spell_id) { if(pet->isCharmed()) charmInfo->ToggleCreatureAutocast(spell_id, false); else ((Pet*)pet)->ToggleAutocast(spell_id, false); } charmInfo->SetActionBar(position[i],spell_id,ActiveStates(act_state)); } } }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature) { if (creature.HasUnitState(UNIT_STATE_CASTING) && !creature.CanMoveDuringChannel()) { creature.CastStop(); return; } float X, Y, Z, nx, ny, nz, ori, dist; creature.GetHomePosition(X, Y, Z, ori); Map const* map = creature.GetBaseMap(); // For 2D/3D system selection //bool is_land_ok = creature.canWalk(); //bool is_water_ok = creature.canSwim(); bool is_air_ok = creature.canFly(); const float angle = rand_norm() * (M_PI * 2); const float range = rand_norm() * wander_distance; const float distanceX = range * cos(angle); const float distanceY = range * sin(angle); nx = X + distanceX; ny = Y + distanceY; // prevent invalid coordinates generation Oregon::NormalizeMapCoord(nx); Oregon::NormalizeMapCoord(ny); dist = (nx - X) * (nx - X) + (ny - Y) * (ny - Y); if (is_air_ok) // 3D system above ground and above water (flying mode) { // Limit height change const float distanceZ = rand_norm() * sqrtf(dist) / 2.0f; nz = Z + distanceZ; float tz = map->GetHeight(nx, ny, nz - 2.0f, false); // Map check only, vmap needed here but need to alter vmaps checks for height. float wz = map->GetWaterLevel(nx, ny); // Problem here, we must fly above the ground and water, not under. Let's try on next tick if (tz >= nz || wz >= nz) return; } //else if (is_water_ok) // 3D system under water and above ground (swimming mode) else // 2D only { dist = dist >= 100.0f ? 10.0f : sqrtf(dist); // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE) // The fastest way to get an accurate result 90% of the time. // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. nz = map->GetHeight(nx, ny, Z + dist - 2.0f, false); if (fabs(nz - Z) > dist) // Map check { nz = map->GetHeight(nx, ny, Z - 2.0f, true); // Vmap Horizontal or above if (fabs(nz - Z) > dist) { // Vmap Higher nz = map->GetHeight(nx, ny, Z + dist - 2.0f, true); // let's forget this bad coords where a z cannot be find and retry at next tick if (fabs(nz - Z) > dist) return; } } } if (is_air_ok) i_nextMoveTime.Reset(0); else { if (roll_chance_i(MOVEMENT_RANDOM_MMGEN_CHANCE_NO_BREAK)) i_nextMoveTime.Reset(urand(5000, 10000)); else i_nextMoveTime.Reset(urand(50, 400)); } creature.AddUnitState(UNIT_STATE_ROAMING); Movement::MoveSplineInit init(creature); init.MoveTo(nx, ny, nz, true); if (creature.IsPet() && creature.GetOwner() && !creature.IsWithinDist(creature.GetOwner(), PET_FOLLOW_DIST + 2.5f)) init.SetWalk(false); else init.SetWalk(true); init.Launch(); if (roll_chance_i(MOVEMENT_RANDOM_MMGEN_CHANCE_NO_BREAK)) i_nextMoveTime.Reset(50); else i_nextMoveTime.Reset(urand(1500, 10000)); // Keep a short wait time }
static bool HandlePetCreateCommand(ChatHandler* handler, char const* /*args*/) { Player* player = handler->GetSession()->GetPlayer(); Creature* creatureTarget = handler->getSelectedCreature(); if (!creatureTarget || creatureTarget->IsPet() || creatureTarget->GetTypeId() == TYPEID_PLAYER) { handler->PSendSysMessage(LANG_SELECT_CREATURE); handler->SetSentErrorMessage(true); return false; } CreatureTemplate const* creatureTemplate = creatureTarget->GetCreatureTemplate(); // Creatures with family CREATURE_FAMILY_NONE crashes the server if (creatureTemplate->family == CREATURE_FAMILY_NONE) { handler->PSendSysMessage("This creature cannot be tamed. Family id: 0 (CREATURE_FAMILY_NONE)."); handler->SetSentErrorMessage(true); return false; } if (player->GetPetGUID()) { handler->PSendSysMessage("You already have a pet"); handler->SetSentErrorMessage(true); return false; } // Everything looks OK, create new pet Pet* pet = new Pet(player, HUNTER_PET); if (!pet->CreateBaseAtCreature(creatureTarget)) { delete pet; handler->PSendSysMessage("Error 1"); return false; } creatureTarget->setDeathState(JUST_DIED); creatureTarget->RemoveCorpse(); creatureTarget->SetHealth(0); // just for nice GM-mode view pet->SetGuidValue(UNIT_FIELD_CREATEDBY, player->GetGUID()); pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, player->getFaction()); if (!pet->InitStatsForLevel(creatureTarget->getLevel())) { TC_LOG_ERROR("misc", "InitStatsForLevel() in EffectTameCreature failed! Pet deleted."); handler->PSendSysMessage("Error 2"); delete pet; return false; } // prepare visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, creatureTarget->getLevel()-1); pet->GetCharmInfo()->SetPetNumber(sObjectMgr->GeneratePetNumber(), true); // this enables pet details window (Shift+P) pet->InitPetCreateSpells(); pet->SetFullHealth(); pet->GetMap()->AddToMap(pet->ToCreature()); // visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, creatureTarget->getLevel()); player->SetMinion(pet, true); pet->SavePetToDB(PET_SAVE_AS_CURRENT); player->PetSpellInitialize(); return true; }
void WorldSession::HandlePetAction(WorldPacket& recv_data) { ObjectGuid petGuid; uint32 data; ObjectGuid targetGuid; float x, y, z; recv_data >> petGuid; recv_data >> data; recv_data >> targetGuid; recv_data >> x >> y >> z; 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* pet = _player->GetMap()->GetUnit(petGuid); if (!pet) { sLog.outError("HandlePetAction: %s not exist.", petGuid.GetString().c_str()); return; } if (_player->GetObjectGuid() != pet->GetCharmerOrOwnerGuid()) { sLog.outError("HandlePetAction: %s isn't controlled by %s.", petGuid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } if (!pet->isAlive()) return; if (pet->GetTypeId() == TYPEID_PLAYER && pet->GetCharmer()->GetTypeId() == TYPEID_PLAYER) { // controller player cannot use controlled player's spells if (flag != (ACT_COMMAND || ACT_REACTION)) return; } else if (((Creature*)pet)->IsPet()) { // pet can have action bar disabled if (((Pet*)pet)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) 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->AttackStop(true, true); pet->GetMotionMaster()->Clear(); ((Pet*)pet)->SetStayPosition(true); ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); charmInfo->SetCommandState(COMMAND_STAY); break; } case COMMAND_FOLLOW: // spellid=1792 // FOLLOW { pet->StopMoving(); pet->AttackStop(true, true); pet->GetMotionMaster()->Clear(); ((Pet*)pet)->SetStayPosition(); ((Pet*)pet)->SetIsRetreating(true); ((Pet*)pet)->SetSpellOpener(); charmInfo->SetCommandState(COMMAND_FOLLOW); break; } case COMMAND_ATTACK: // spellid=1792 // ATTACK { ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); Unit* targetUnit = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; if (targetUnit && targetUnit != pet && targetUnit->isTargetableForAttack() && targetUnit->isInAccessablePlaceFor((Creature*)pet)) { _player->SetInCombatState(true, targetUnit); // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != targetUnit) { pet->AttackStop(); pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) { ((Creature*)pet)->AI()->AttackStart(targetUnit); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)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) { Creature* petC = (Creature*)pet; if (petC->IsPet()) { Pet* p = (Pet*)petC; if (p->getPetType() == HUNTER_PET) p->Unsummon(PET_SAVE_AS_DELETED, _player); else // dismissing a summoned pet is like killing them (this prevents returning a soulshard...) p->SetDeathState(CORPSE); } else // charmed _player->Uncharm(); if (petC->IsTemporarySummon()) // special case when pet was temporary summon through DoSummonPossesed { petC->ForcedDespawn(); return; } ((Pet*)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*)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*)pet)->SetIsRetreating(); ((Pet*)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) { SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(i)); if (!spellEffect) continue; if (spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; 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*)pet)->SetSpellOpener(spellid, sRange->minRange, sRange->maxRange); spell->finish(false); delete spell; pet->AttackStop(); pet->GetMotionMaster()->Clear(); ((Creature*)pet)->AI()->AttackStart(unit_target); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); return; } // 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->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) { ((Creature*)pet)->AddCreatureSpellCooldown(spellid); unit_target = spell->m_targets.getUnitTarget(); 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) { pet->AttackStop(); pet->GetMotionMaster()->Clear(); _player->SetInCombatState(true, unit_target); if (((Creature*)pet)->AI()) { ((Creature*)pet)->AI()->AttackStart(unit_target); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); } else pet->Attack(unit_target, true); } } ((Pet*)pet)->SetSpellOpener(); spell->SpellStart(&(spell->m_targets)); } else { if (pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) 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 (!((Creature*)pet)->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); ((Pet*)pet)->SetSpellOpener(); spell->finish(false); delete spell; } break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
bool ChatHandler::HandleWaypointAddFlyCommand(const char* args, WorldSession* m_session) { uint64 guid = m_session->GetPlayer()->GetSelection(); if(guid == 0) { SystemMessage(m_session, "No Selection"); return true; } AIInterface* ai = NULL; Creature* pCreature = NULL; Player* p = m_session->GetPlayer(); if(p->waypointunit != NULL) { SystemMessage(m_session, "Using Previous Unit."); ai = p->waypointunit; if(!ai) { SystemMessage(m_session, "Invalid Creature, please select another one."); return true; } pCreature = TO_CREATURE(ai->GetUnit()); if(!pCreature || pCreature->IsPet()) { SystemMessage(m_session, "Invalid Creature, please select another one."); return true; } } else { pCreature = m_session->GetPlayer()->GetMapMgr()->GetCreature(GET_LOWGUID_PART(guid)); if(!pCreature || pCreature->IsPet()) { SystemMessage(m_session, "You should select a creature."); return true; } ai = pCreature->GetAIInterface(); } char* pWaitTime = strtok((char*)args, " "); uint32 WaitTime = (pWaitTime) ? atoi(pWaitTime) : 10000; char* pForwardEmoteId = strtok(NULL, " "); uint32 ForwardEmoteId = (pForwardEmoteId) ? atoi(pForwardEmoteId) : 0; char* pBackwardEmoteId = strtok(NULL, " "); uint32 BackwardEmoteId = (pBackwardEmoteId) ? atoi(pBackwardEmoteId) : 0; char* pForwardSkinId = strtok(NULL, " "); uint32 ForwardSkinId = (pForwardSkinId) ? atoi(pForwardSkinId) : pCreature->GetNativeDisplayId(); char* pBackwardSkinId = strtok(NULL, " "); uint32 BackwardSkinId = (pBackwardSkinId) ? atoi(pBackwardSkinId) : pCreature->GetNativeDisplayId(); char* pForwardEmoteOneShot = strtok(NULL, " "); uint32 ForwardEmoteOneShot = (pForwardEmoteOneShot) ? atoi(pForwardEmoteOneShot) : 1; char* pBackwardEmoteOneShot = strtok(NULL, " "); uint32 BackwardEmoteOneShot = (pBackwardEmoteOneShot) ? atoi(pBackwardEmoteOneShot) : 1; WayPoint* wp = new WayPoint; bool showing = ai->m_WayPointsShowing; wp->id = (uint32)ai->GetWayPointsCount() + 1; wp->x = p->GetPositionX(); wp->y = p->GetPositionY(); wp->z = p->GetPositionZ(); wp->waittime = WaitTime; wp->flags = 768; wp->forwardemoteoneshot = (ForwardEmoteOneShot > 0) ? true : false; wp->forwardemoteid = ForwardEmoteId; wp->backwardemoteoneshot = (BackwardEmoteOneShot > 0) ? true : false; wp->backwardemoteid = BackwardEmoteId; wp->forwardskinid = ForwardSkinId; wp->backwardskinid = BackwardSkinId; if(showing) ai->hideWayPoints(p); if(ai->addWayPointUnsafe(wp)) { ai->saveWayPoints(); SystemMessage(m_session, "Waypoint %u added.", wp->id); } else { SystemMessage(m_session, "An error occurred while adding the Waypoint"); delete wp; } if(showing) ai->showWayPoints(p, ai->m_WayPointsShowBackwards); return true; }
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL"); ObjectGuid guid; uint32 spellid; recvPacket >> guid >> spellid; DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, %s, spellid %u", guid.GetString().c_str(), spellid); Unit* petUnit = _player->GetMap()->GetUnit(guid); if (!petUnit || (guid != _player->GetPetGuid() && !_player->HasCharm(guid))) { sLog.outError("HandlePetSpellAutocastOpcode. %s isn't pet of %s .", guid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } //TODO: all those typecasting are probably not needed anymore Creature* petCreature = petUnit->GetTypeId() == TYPEID_UNIT ? static_cast<Creature*>(petUnit) : nullptr; Pet* pet = (petCreature && petCreature->IsPet()) ? static_cast<Pet*>(petUnit) : nullptr; SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (!petUnit->IsSpellReady(*spellInfo)) return; // do not cast not learned spells if (!petUnit->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(petUnit); petUnit->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(petUnit, spellInfo, TRIGGERED_PET_CAST); spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(nullptr); if (result == SPELL_CAST_OK) { if (pet) pet->CheckLearning(spellid); spell->SpellStart(&(spell->m_targets)); } else { petUnit->SendPetCastFail(spellid, result); if (petCreature && petCreature->IsSpellReady(spellid)) GetPlayer()->SendClearCooldown(spellid, petUnit); spell->finish(false); delete spell; } }
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); Unit* petUnit = _player->GetMap()->GetUnit(guid); if (!petUnit || (guid != _player->GetPetGuid() && guid != _player->GetCharmGuid())) { sLog.outError("HandlePetSpellAutocastOpcode. %s isn't pet of %s .", guid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } Creature* petCreature = petUnit->GetTypeId() == TYPEID_UNIT ? static_cast<Creature*>(petUnit) : nullptr; Pet* pet = (petCreature && petCreature->IsPet()) ? static_cast<Pet*>(petUnit) : nullptr; SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (petUnit->GetCharmInfo() && petUnit->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; Aura* triggeredByAura = petUnit->GetTriggeredByClientAura(spellid); // do not cast not learned spells if ((!triggeredByAura && !petUnit->HasSpell(spellid)) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(petUnit); petUnit->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(petUnit, spellInfo, triggeredByAura ? true : false, petUnit->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) { if (pet) pet->AddCreatureSpellCooldown(spellid); spell->SpellStart(&(spell->m_targets), triggeredByAura); } else { Unit* owner = petUnit->GetCharmerOrOwner(); if (owner && owner->GetTypeId() == TYPEID_PLAYER && !triggeredByAura) Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true); if (pet && !pet->HasSpellCooldown(spellid) && !triggeredByAura) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
/// Only _static_ data send in this packet !!! void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data ) { uint32 entry; ObjectGuid guid; recv_data >> entry; recv_data >> guid; Creature *unit = _player->GetMap()->GetAnyTypeCreature(guid); //if (unit == NULL) // sLog.outDebug( "WORLD: HandleCreatureQueryOpcode - (%u) NO SUCH UNIT! (GUID: %u, ENTRY: %u)", uint32(GUID_LOPART(guid)), guid, entry ); CreatureInfo const *ci = ObjectMgr::GetCreatureTemplate(entry); if (ci) { std::string Name, SubName; Name = ci->Name; SubName = ci->SubName; int loc_idx = GetSessionDbLocaleIndex(); if (loc_idx >= 0) { CreatureLocale const *cl = sObjectMgr.GetCreatureLocale(entry); if (cl) { if (cl->Name.size() > size_t(loc_idx) && !cl->Name[loc_idx].empty()) Name = cl->Name[loc_idx]; if (cl->SubName.size() > size_t(loc_idx) && !cl->SubName[loc_idx].empty()) SubName = cl->SubName[loc_idx]; } } DETAIL_LOG("WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u.", ci->Name, entry); // guess size WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 100 ); data << uint32(entry); // creature entry data << Name; data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty data << SubName; data << uint32(ci->type_flags); // flags if (unit) data << uint32(((unit->IsPet()) ? 0 : ci->type)); //CreatureType.dbc wdbFeild8 else data << uint32(ci->type); data << uint32(ci->family); // CreatureFamily.dbc data << uint32(ci->rank); // Creature Rank (elite, boss, etc) data << uint32(0); // unknown wdbFeild11 data << uint32(ci->PetSpellDataId); // Id from CreatureSpellData.dbc wdbField12 if (unit) data << unit->GetUInt32Value(UNIT_FIELD_DISPLAYID); //DisplayID wdbFeild13 else data << uint32(Creature::ChooseDisplayId(ci)); // workaround, way to manage models must be fixed data << uint16(ci->civilian); //wdbFeild14 SendPacket( &data ); DEBUG_LOG( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE" ); } else { DEBUG_LOG("WORLD: CMSG_CREATURE_QUERY - Guid: %s Entry: %u NO CREATURE INFO!", guid.GetString().c_str(), entry); WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 4 ); data << uint32(entry | 0x80000000); SendPacket( &data ); DEBUG_LOG( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE" ); } }
void FollowMovementGenerator<Creature>::_updateWalkMode(Creature &u) { if (i_target.isValid() && u.IsPet()) u.UpdateWalkMode(i_target.getTarget()); }
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(), GetPlayer()->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; // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(pet); pet->clearUnitState(UNIT_STAT_MOVING); 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(); } spell->prepare(&(spell->m_targets)); } else { pet->SendPetCastFail(spellid, result); if (!pet->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
void PathInfo::BuildPolyPath(PathNode startPos, PathNode endPos) { // *** getting start/end poly logic *** float distToStartPoly, distToEndPoly; float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x}; float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x}; dtPolyRef startPoly = getPolyByLocation(startPoint, &distToStartPoly); dtPolyRef endPoly = getPolyByLocation(endPoint, &distToEndPoly); // we have a hole in our mesh // make shortcut path and mark it as NOPATH ( with flying exception ) // its up to caller how he will use this info if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF) { PATH_DEBUG("++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n"); BuildShortcut(); m_type = (m_sourceUnit->GetTypeId() == TYPEID_UNIT && ((Creature*)m_sourceUnit)->CanFly()) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; return; } // we may need a better number here bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f); if (farFromPoly) { PATH_DEBUG("++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly); bool buildShotrcut = false; if (m_sourceUnit->GetTypeId() == TYPEID_UNIT) { Creature* owner = (Creature*)m_sourceUnit; PathNode p = (distToStartPoly > 7.0f) ? startPos : endPos; if (m_sourceUnit->GetTerrain()->IsUnderWater(p.x, p.y, p.z)) { PATH_DEBUG("++ BuildPolyPath :: underWater case\n"); if (owner->CanSwim() || owner->IsPet()) buildShotrcut = true; } else { PATH_DEBUG("++ BuildPolyPath :: flying case\n"); if (owner->CanFly()) buildShotrcut = true; } } if (buildShotrcut) { BuildShortcut(); m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); return; } else { float closestPoint[VERTEX_SIZE]; // we may want to use closestPointOnPolyBoundary instead if (DT_SUCCESS == m_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint)) { dtVcopy(endPoint, closestPoint); setActualEndPosition(PathNode(endPoint[2],endPoint[0],endPoint[1])); } m_type = PATHFIND_INCOMPLETE; } } // *** poly path generating logic *** // start and end are on same polygon // just need to move in straight line if (startPoly == endPoly) { PATH_DEBUG("++ BuildPolyPath :: (startPoly == endPoly)\n"); BuildShortcut(); m_pathPolyRefs = new dtPolyRef[1]; m_pathPolyRefs[0] = startPoly; m_polyLength = 1; m_type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL; PATH_DEBUG("++ BuildPolyPath :: path type %d\n", m_type); return; } // look for startPoly/endPoly in current path // TODO: we can merge it with getPathPolyByPosition() loop bool startPolyFound = false; bool endPolyFound = false; uint32 pathStartIndex, pathEndIndex; if (m_polyLength) { for (pathStartIndex = 0; pathStartIndex < m_polyLength; ++pathStartIndex) if (m_pathPolyRefs[pathStartIndex] == startPoly) { startPolyFound = true; break; } for (pathEndIndex = m_polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex) if (m_pathPolyRefs[pathEndIndex] == endPoly) { endPolyFound = true; break; } } if (startPolyFound && endPolyFound) { PATH_DEBUG("++ BuildPolyPath :: (startPolyFound && endPolyFound)\n"); // we moved along the path and the target did not move out of our old poly-path // our path is a simple subpath case, we have all the data we need // just "cut" it out m_polyLength = pathEndIndex - pathStartIndex + 1; dtPolyRef* newPolyRefs = new dtPolyRef[m_polyLength]; memcpy(newPolyRefs, m_pathPolyRefs+pathStartIndex, m_polyLength*sizeof(dtPolyRef)); delete [] m_pathPolyRefs; m_pathPolyRefs = newPolyRefs; } else if (startPolyFound && !endPolyFound) { PATH_DEBUG("++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n"); // we are moving on the old path but target moved out // so we have atleast part of poly-path ready m_polyLength -= pathStartIndex; // try to adjust the suffix of the path instead of recalculating entire length // at given interval the target cannot get too far from its last location // thus we have less poly to cover // sub-path of optimal path is optimal // take ~80% of the original length // TODO : play with the values here uint32 prefixPolyLength = uint32(m_polyLength*0.8f + 0.5f); dtPolyRef prefixPathPolys[MAX_PATH_LENGTH]; memcpy(prefixPathPolys, m_pathPolyRefs+pathStartIndex, prefixPolyLength*sizeof(dtPolyRef)); dtPolyRef suffixStartPoly = prefixPathPolys[prefixPolyLength-1]; // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data float suffixEndPoint[VERTEX_SIZE]; if (DT_SUCCESS != m_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint)) { // suffixStartPoly is invalid somehow, or the navmesh is broken => error state sLog.outError("%u's Path Build failed: invalid polyRef in path", m_sourceUnit->GetGUID()); BuildShortcut(); m_type = PATHFIND_NOPATH; return; } // generate suffix dtQueryFilter filter = createFilter(); dtPolyRef suffixPathPolys[MAX_PATH_LENGTH]; uint32 suffixPolyLength = 0; dtStatus dtResult = m_navMeshQuery->findPath( suffixStartPoly, // start polygon endPoly, // end polygon suffixEndPoint, // start position endPoint, // end position &filter, // polygon search filter suffixPathPolys, // [out] path (int*)&suffixPolyLength, MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path if (!suffixPolyLength || dtResult != DT_SUCCESS) { // this is probably an error state, but we'll leave it // and hopefully recover on the next Update // we still need to copy our preffix sLog.outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUID()); } PATH_DEBUG("++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n",m_polyLength, prefixPolyLength, suffixPolyLength); // new path = prefix + suffix - overlap m_polyLength = prefixPolyLength + suffixPolyLength - 1; delete [] m_pathPolyRefs; m_pathPolyRefs = new dtPolyRef[m_polyLength]; // copy the part of the old path we keep - prefix memcpy(m_pathPolyRefs, prefixPathPolys, prefixPolyLength*sizeof(dtPolyRef)); // copy the newly created suffix - skip first poly, we have it at prefix end if (suffixPathPolys) memcpy(m_pathPolyRefs+prefixPolyLength, suffixPathPolys+1, (suffixPolyLength-1)*sizeof(dtPolyRef)); } else { PATH_DEBUG("++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n"); // either we have no path at all -> first run // or something went really wrong -> we aren't moving along the path to the target // just generate new path // free and invalidate old path data clear(); dtQueryFilter filter = createFilter(); // use special filter so we use proper terrain types dtPolyRef pathPolys[MAX_PATH_LENGTH]; m_polyLength = 0; dtStatus dtResult = m_navMeshQuery->findPath( startPoly, // start polygon endPoly, // end polygon startPoint, // start position endPoint, // end position &filter, // polygon search filter pathPolys, // [out] path (int*)&m_polyLength, MAX_PATH_LENGTH); // max number of polygons in output path if (!m_polyLength || dtResult != DT_SUCCESS) { // only happens if we passed bad data to findPath(), or navmesh is messed up sLog.outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUID()); BuildShortcut(); m_type = PATHFIND_NOPATH; return; } m_pathPolyRefs = new dtPolyRef[m_polyLength]; memcpy(m_pathPolyRefs, pathPolys, m_polyLength*sizeof(dtPolyRef)); } // by now we know what type of path we can get if (m_pathPolyRefs[m_polyLength - 1] == endPoly && !(m_type & PATHFIND_INCOMPLETE)) m_type = PATHFIND_NORMAL; else m_type = PATHFIND_INCOMPLETE; // generate the point-path out of our up-to-date poly-path BuildPointPath(startPoint, endPoint); }
void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) { DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL"); recvPacket.hexlike(); recvPacket.print_storage(); //2 - 0 - 0 - 43 - 129 - 0 - 80 - 241 | - 42 - 211 - 253 - 0 | - 0 | - 2 |- 96 - 0 - 0 - 0 | - 0 - 26 //- 164 - 59 - 196 - 174 - 98 - 131 | - 194 - 182 - 171 - 218| - 67 - 0 - 48 - 93| - 0 - 196 - 32 //- 177| - 242 - 193 - 22 - 110 - 224 - 67 - 203 - 166 | - 68 - 61 - 133 - 1| - 240 - 66 - 1 - 183 | //- 0 - 0 - 0 - 217| - 2 - 43 - 129 - 80 - 241 - 0 - 10 - 0 - 0 - 0 - 0 - 76 - 109 - 175 - 0 //- 238 - 115 - 58 - 196 - 20 - 110 - 121 - 194 - 187 - 107 - 217 - 67 - 32 - 44 - 27 - 62 - 217 //- 1 - 36 - 129 - 80 - 241 - 0 - 0 - 160 - 64 - 0 - 0 - 160 - 64 - 0 - 0 - 160 - 64 - 192 - 233 //- 172 - 62 - 4 - 0 - 0 - 0 - 7 - 230 - 0 - 0 - 0 - //5 - 0 - 0 - 43 - 129 - 0 - 80 - 241 | - 85 - 211 - 253 - 0 | - 0 | - 2 | - 96 - 0 - 0 - 0 | - 0 - 69 - 60 - 61 //- 196 - 171 - 248 - 107| - 194 - 8 - 236 - 218 | - 67 - 0 - 177 - 11 | - 46 - 196 - 89 - 16 | - 14 - 195 //- 5 - 38 - 231 - 67 - 23 - 221 | - 110 - 62 - 15 - 3 | - 240 - 66 -| 1 - 183 | - 0 - 0 - 0 - 217 | - 5 - 43 //- 129 - 80 - 241 - 0 - 10 - 0 - 0 - 0 - 0 - 233 - 41 - 203 - 0 - 106 - 207 - 59 - 196 - 179 - 173 - 83 //- 194 - 8 - 108 - 217 - 67 - 127 - 153 - 170 - 64 - 217 - 4 - 36 - 129 - 80 - 241 - 0 - 0 - 160 - 64 //- 0 - 0 - 160 - 64 - 0 - 0 - 160 - 64 - 7 - 77 - 175 - 64 - 4 - 0 - 0 - 0 - 7 - 195 - 0 - 0 - 0 - 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); if (guid.IsPlayer()) return; 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(), GetPlayer()->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; // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(pet); pet->clearUnitState(UNIT_STAT_MOVING); 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(); } spell->prepare(&(spell->m_targets)); } else { pet->SendPetCastFail(spellid, result); if (!pet->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
bool ChatHandler::HandleDeleteCommand(const char* args, WorldSession* m_session) { uint64 guid = m_session->GetPlayer()->GetSelection(); if(guid == 0) { SystemMessage(m_session, "No selection."); return true; } Creature* unit = m_session->GetPlayer()->GetMapMgr()->GetCreature(GET_LOWGUID_PART(guid)); if(!unit) { SystemMessage(m_session, "You should select a creature."); return true; } if(unit->IsPet()) { SystemMessage(m_session, "You can't delete a pet."); return true; } sGMLog.writefromsession(m_session, "used npc delete, sqlid %u, creature %s, pos %f %f %f", unit->GetSQL_id(), unit->GetCreatureInfo()->Name, unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); unit->GetAIInterface()->hideWayPoints(m_session->GetPlayer()); unit->DeleteFromDB(); if(unit->IsSummon()) { unit->Delete(); } else { if(unit->m_spawn) { uint32 cellx = uint32(((_maxX - unit->m_spawn->x) / _cellSize)); uint32 celly = uint32(((_maxY - unit->m_spawn->y) / _cellSize)); if(cellx <= _sizeX && celly <= _sizeY) { CellSpawns* sp = unit->GetMapMgr()->GetBaseMap()->GetSpawnsList(cellx, celly); if(sp != NULL) { for(CreatureSpawnList::iterator itr = sp->CreatureSpawns.begin(); itr != sp->CreatureSpawns.end(); ++itr) if((*itr) == unit->m_spawn) { sp->CreatureSpawns.erase(itr); break; } } delete unit->m_spawn; unit->m_spawn = NULL; } } unit->RemoveFromWorld(false, true); } BlueSystemMessage(m_session, "Creature deleted"); return true; }