void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) { uint16 opcode = recvPacket.GetOpcode(); Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plrMover = mover->ToPlayer(); // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) { recvPacket.rfinish(); // prevent warnings spam return; } // Sometime, client send movement packet after teleporation with position before teleportation, so we ignore 3 first movement packet after teleporation // TODO: find a better way to check that, may be a new CMSG send by client ? /*if (plrMover && plrMover->GetIgnoreMovementCount() && opcode != CMSG_CAST_SPELL) { plrMover->SetIgnoreMovementCount(plrMover->GetIgnoreMovementCount() - 1); recvPacket.rfinish(); // prevent warnings spam return; }*/ /* extract packet */ MovementInfo movementInfo; GetPlayer()->ReadMovementInfo(recvPacket, &movementInfo); if (!mover) return; // prevent tampered movement data if (movementInfo.guid != mover->GetGUID()) { sLog->outDebug(LOG_FILTER_NETWORKIO, "HandleMovementOpcodes: guid error"); return; } if (!movementInfo.pos.IsPositionValid()) { sLog->outDebug(LOG_FILTER_NETWORKIO, "HandleMovementOpcodes: Invalid Position"); return; } /* handle special cases */ if (movementInfo.t_guid) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.t_pos.GetPositionX() > 50 || movementInfo.t_pos.GetPositionY() > 50 || movementInfo.t_pos.GetPositionZ() > 50) { recvPacket.rfinish(); // prevent warnings spam return; } if (!SkyMistCore::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.t_pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.t_pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.t_pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.t_pos.GetOrientation())) { recvPacket.rfinish(); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plrMover) { if (!plrMover->GetTransport()) { // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just dismount if the guid can be found in the transport list for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } } else if (plrMover->GetTransport()->GetGUID() != movementInfo.t_guid) { bool foundNewTransport = false; plrMover->m_transport->RemovePassenger(plrMover); for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { foundNewTransport = true; plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } if (!foundNewTransport) { plrMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; } } } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.t_guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.t_guid = 0; } } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; movementInfo.t_guid = 0LL; } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == CMSG_MOVE_FALL_LAND && plrMover && !plrMover->isInFlight()) plrMover->HandleFall(movementInfo); // now client does not include swimming flag in case of jumping under water. if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); //if (plrMover) // sAnticheatMgr->StartHackDetection(plrMover, movementInfo, opcode); uint32 mstime = getMSTime(); if (m_clientTimeDelay == 0) m_clientTimeDelay = mstime - movementInfo.time; movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; movementInfo.guid = mover->GetGUID(); mover->m_movementInfo = movementInfo; // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle() if (mover->GetVehicle()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); return; } mover->UpdatePosition(movementInfo.pos); /* process position-change */ WorldPacket data(SMSG_MOVE_UPDATE, recvPacket.size()); //movementInfo.Alive32 = movementInfo.time; // hack, but it's work in 505 in this way ... mover->WriteMovementInfo(data); mover->SendMessageToSet(&data, _player); if (plrMover) // nothing is charmed, or player charmed { // Clear unit emote state. plrMover->ClearEmotes(); plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); AreaTableEntry const* zone = GetAreaEntryByAreaID(plrMover->GetAreaId()); float depth = zone ? zone->MaxDepth : -500.0f; if (movementInfo.pos.GetPositionZ() < depth) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away // TODO: discard movement packets after the player is rooted if (plrMover->isAlive()) { plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (!plrMover->isAlive()) plrMover->KillPlayer(); } } } } }
/// Process queued scripts void Map::ScriptsProcess() { if (m_scriptSchedule.empty()) return; ///- Process overdue queued scripts ScriptScheduleMap::iterator iter = m_scriptSchedule.begin(); // ok as multimap is a *sorted* associative container while (!m_scriptSchedule.empty() && (iter->first <= sWorld->GetGameTime())) { ScriptAction const& step = iter->second; Object* source = NULL; if (step.sourceGUID) { switch (GUID_HIPART(step.sourceGUID)) { case HIGHGUID_ITEM: // as well as HIGHGUID_CONTAINER if (Player* player = GetPlayer(step.ownerGUID)) source = player->GetItemByGuid(step.sourceGUID); break; case HIGHGUID_UNIT: case HIGHGUID_VEHICLE: source = GetCreature(step.sourceGUID); break; case HIGHGUID_PET: source = GetPet(step.sourceGUID); break; case HIGHGUID_PLAYER: source = GetPlayer(step.sourceGUID); break; case HIGHGUID_TRANSPORT: case HIGHGUID_GAMEOBJECT: source = GetGameObject(step.sourceGUID); break; case HIGHGUID_CORPSE: source = GetCorpse(step.sourceGUID); break; case HIGHGUID_MO_TRANSPORT: { GameObject* go = GetGameObject(step.sourceGUID); source = go ? go->ToTransport() : NULL; break; } default: sLog->outError("%s source with unsupported high guid (GUID: " UI64FMTD ", high guid: %u).", step.script->GetDebugInfo().c_str(), step.sourceGUID, GUID_HIPART(step.sourceGUID)); break; } } WorldObject* target = NULL; if (step.targetGUID) { switch (GUID_HIPART(step.targetGUID)) { case HIGHGUID_UNIT: case HIGHGUID_VEHICLE: target = GetCreature(step.targetGUID); break; case HIGHGUID_PET: target = GetPet(step.targetGUID); break; case HIGHGUID_PLAYER: // empty GUID case also target = GetPlayer(step.targetGUID); break; case HIGHGUID_TRANSPORT: case HIGHGUID_GAMEOBJECT: target = GetGameObject(step.targetGUID); break; case HIGHGUID_CORPSE: target = GetCorpse(step.targetGUID); break; case HIGHGUID_MO_TRANSPORT: { GameObject* go = GetGameObject(step.targetGUID); target = go ? go->ToTransport() : NULL; break; } default: sLog->outError("%s target with unsupported high guid (GUID: " UI64FMTD ", high guid: %u).", step.script->GetDebugInfo().c_str(), step.targetGUID, GUID_HIPART(step.targetGUID)); break; } } switch (step.script->command) { case SCRIPT_COMMAND_TALK: if (step.script->Talk.ChatType > CHAT_TYPE_WHISPER && step.script->Talk.ChatType != CHAT_MSG_RAID_BOSS_WHISPER) { sLog->outError("%s invalid chat type (%u) specified, skipping.", step.script->GetDebugInfo().c_str(), step.script->Talk.ChatType); break; } if (step.script->Talk.Flags & SF_TALK_USE_PLAYER) { if (Player* player = _GetScriptPlayerSourceOrTarget(source, target, step.script)) { LocaleConstant loc_idx = player->GetSession()->GetSessionDbLocaleIndex(); std::string text(sObjectMgr->GetTrinityString(step.script->Talk.TextID, loc_idx)); switch (step.script->Talk.ChatType) { case CHAT_TYPE_SAY: player->Say(text, LANG_UNIVERSAL); break; case CHAT_TYPE_YELL: player->Yell(text, LANG_UNIVERSAL); break; case CHAT_TYPE_TEXT_EMOTE: case CHAT_TYPE_BOSS_EMOTE: player->TextEmote(text); break; case CHAT_TYPE_WHISPER: case CHAT_MSG_RAID_BOSS_WHISPER: { uint64 targetGUID = target ? target->GetGUID() : 0; if (!targetGUID || !IS_PLAYER_GUID(targetGUID)) sLog->outError("%s attempt to whisper to non-player unit, skipping.", step.script->GetDebugInfo().c_str()); else player->Whisper(text, LANG_UNIVERSAL, targetGUID); break; } default: break; // must be already checked at load } } } else { // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { uint64 targetGUID = target ? target->GetGUID() : 0; switch (step.script->Talk.ChatType) { case CHAT_TYPE_SAY: cSource->MonsterSay(step.script->Talk.TextID, LANG_UNIVERSAL, target); break; case CHAT_TYPE_YELL: cSource->MonsterYell(step.script->Talk.TextID, LANG_UNIVERSAL, target); break; case CHAT_TYPE_TEXT_EMOTE: cSource->MonsterTextEmote(step.script->Talk.TextID, target); break; case CHAT_TYPE_BOSS_EMOTE: cSource->MonsterTextEmote(step.script->Talk.TextID, target, true); break; case CHAT_TYPE_WHISPER: if (!targetGUID || !IS_PLAYER_GUID(targetGUID)) sLog->outError("%s attempt to whisper to non-player unit, skipping.", step.script->GetDebugInfo().c_str()); else cSource->MonsterWhisper(step.script->Talk.TextID, target->ToPlayer()); break; case CHAT_MSG_RAID_BOSS_WHISPER: if (!targetGUID || !IS_PLAYER_GUID(targetGUID)) sLog->outError("%s attempt to raidbosswhisper to non-player unit, skipping.", step.script->GetDebugInfo().c_str()); else cSource->MonsterWhisper(step.script->Talk.TextID, target->ToPlayer(), true); break; default: break; // must be already checked at load } } } break; case SCRIPT_COMMAND_EMOTE: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { if (step.script->Emote.Flags & SF_EMOTE_USE_STATE) cSource->SetUInt32Value(UNIT_NPC_EMOTESTATE, step.script->Emote.EmoteID); else cSource->HandleEmoteCommand(step.script->Emote.EmoteID); } break; case SCRIPT_COMMAND_FIELD_SET: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { // Validate field number. if (step.script->FieldSet.FieldID <= OBJECT_FIELD_ENTRY || step.script->FieldSet.FieldID >= cSource->GetValuesCount()) sLog->outError("%s wrong field %u (max count: %u) in object (TypeId: %u, Entry: %u, GUID: %u) specified, skipping.", step.script->GetDebugInfo().c_str(), step.script->FieldSet.FieldID, cSource->GetValuesCount(), cSource->GetTypeId(), cSource->GetEntry(), cSource->GetGUIDLow()); else cSource->SetUInt32Value(step.script->FieldSet.FieldID, step.script->FieldSet.FieldValue); } break; case SCRIPT_COMMAND_MOVE_TO: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { Unit * unit = (Unit*)cSource; if (step.script->MoveTo.TravelTime != 0) { float speed = unit->GetDistance(step.script->MoveTo.DestX, step.script->MoveTo.DestY, step.script->MoveTo.DestZ) / ((float)step.script->MoveTo.TravelTime * 0.001f); unit->MonsterMoveWithSpeed(step.script->MoveTo.DestX, step.script->MoveTo.DestY, step.script->MoveTo.DestZ, speed); } else unit->NearTeleportTo(step.script->MoveTo.DestX, step.script->MoveTo.DestY, step.script->MoveTo.DestZ, unit->GetOrientation()); } break; case SCRIPT_COMMAND_FLAG_SET: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { // Validate field number. if (step.script->FlagToggle.FieldID <= OBJECT_FIELD_ENTRY || step.script->FlagToggle.FieldID >= cSource->GetValuesCount()) sLog->outError("%s wrong field %u (max count: %u) in object (TypeId: %u, Entry: %u, GUID: %u) specified, skipping.", step.script->GetDebugInfo().c_str(), step.script->FlagToggle.FieldID, cSource->GetValuesCount(), cSource->GetTypeId(), cSource->GetEntry(), cSource->GetGUIDLow()); else cSource->SetFlag(step.script->FlagToggle.FieldID, step.script->FlagToggle.FieldValue); } break; case SCRIPT_COMMAND_FLAG_REMOVE: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { // Validate field number. if (step.script->FlagToggle.FieldID <= OBJECT_FIELD_ENTRY || step.script->FlagToggle.FieldID >= cSource->GetValuesCount()) sLog->outError("%s wrong field %u (max count: %u) in object (TypeId: %u, Entry: %u, GUID: %u) specified, skipping.", step.script->GetDebugInfo().c_str(), step.script->FlagToggle.FieldID, cSource->GetValuesCount(), cSource->GetTypeId(), cSource->GetEntry(), cSource->GetGUIDLow()); else cSource->RemoveFlag(step.script->FlagToggle.FieldID, step.script->FlagToggle.FieldValue); } break; case SCRIPT_COMMAND_TELEPORT_TO: if (step.script->TeleportTo.Flags & SF_TELEPORT_USE_CREATURE) { // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script, true)) cSource->NearTeleportTo(step.script->TeleportTo.DestX, step.script->TeleportTo.DestY, step.script->TeleportTo.DestZ, step.script->TeleportTo.Orientation); } else { // Source or target must be Player. if (Player* player = _GetScriptPlayerSourceOrTarget(source, target, step.script)) player->TeleportTo(step.script->TeleportTo.MapID, step.script->TeleportTo.DestX, step.script->TeleportTo.DestY, step.script->TeleportTo.DestZ, step.script->TeleportTo.Orientation); } break; case SCRIPT_COMMAND_QUEST_EXPLORED: { if (!source) { sLog->outError("%s source object is NULL.", step.script->GetDebugInfo().c_str()); break; } if (!target) { sLog->outError("%s target object is NULL.", step.script->GetDebugInfo().c_str()); break; } // when script called for item spell casting then target == (unit or GO) and source is player WorldObject* worldObject; Player* player = target->ToPlayer(); if (player) { if (source->GetTypeId() != TYPEID_UNIT && source->GetTypeId() != TYPEID_GAMEOBJECT && source->GetTypeId() != TYPEID_PLAYER) { sLog->outError("%s source is not unit, gameobject or player (TypeId: %u, Entry: %u, GUID: %u), skipping.", step.script->GetDebugInfo().c_str(), source->GetTypeId(), source->GetEntry(), source->GetGUIDLow()); break; } worldObject = dynamic_cast<WorldObject*>(source); } else { player = source->ToPlayer(); if (player) { if (target->GetTypeId() != TYPEID_UNIT && target->GetTypeId() != TYPEID_GAMEOBJECT && target->GetTypeId() != TYPEID_PLAYER) { sLog->outError("%s target is not unit, gameobject or player (TypeId: %u, Entry: %u, GUID: %u), skipping.", step.script->GetDebugInfo().c_str(), target->GetTypeId(), target->GetEntry(), target->GetGUIDLow()); break; } worldObject = dynamic_cast<WorldObject*>(target); } else { sLog->outError("%s neither source nor target is player (source: TypeId: %u, Entry: %u, GUID: %u; target: TypeId: %u, Entry: %u, GUID: %u), skipping.", step.script->GetDebugInfo().c_str(), source->GetTypeId(), source->GetEntry(), source->GetGUIDLow(), target->GetTypeId(), target->GetEntry(), target->GetGUIDLow()); break; } } // quest id and flags checked at script loading if ((worldObject->GetTypeId() != TYPEID_UNIT || ((Unit*)worldObject)->IsAlive()) && (step.script->QuestExplored.Distance == 0 || worldObject->IsWithinDistInMap(player, float(step.script->QuestExplored.Distance)))) player->AreaExploredOrEventHappens(step.script->QuestExplored.QuestID); else player->FailQuest(step.script->QuestExplored.QuestID); break; } case SCRIPT_COMMAND_KILL_CREDIT: // Source or target must be Player. if (Player* player = _GetScriptPlayerSourceOrTarget(source, target, step.script)) { if (step.script->KillCredit.Flags & SF_KILLCREDIT_REWARD_GROUP) player->RewardPlayerAndGroupAtEvent(step.script->KillCredit.CreatureEntry, player); else player->KilledMonsterCredit(step.script->KillCredit.CreatureEntry, 0); } break; case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: if (!step.script->RespawnGameobject.GOGuid) { sLog->outError("%s gameobject guid (datalong) is not specified.", step.script->GetDebugInfo().c_str()); break; } // Source or target must be WorldObject. if (WorldObject* pSummoner = _GetScriptWorldObject(source, true, step.script)) { GameObject* pGO = _FindGameObject(pSummoner, step.script->RespawnGameobject.GOGuid); if (!pGO) { sLog->outError("%s gameobject was not found (guid: %u).", step.script->GetDebugInfo().c_str(), step.script->RespawnGameobject.GOGuid); break; } if (pGO->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE || pGO->GetGoType() == GAMEOBJECT_TYPE_DOOR || pGO->GetGoType() == GAMEOBJECT_TYPE_BUTTON || pGO->GetGoType() == GAMEOBJECT_TYPE_TRAP) { sLog->outError("%s can not be used with gameobject of type %u (guid: %u).", step.script->GetDebugInfo().c_str(), uint32(pGO->GetGoType()), step.script->RespawnGameobject.GOGuid); break; } // Check that GO is not spawned if (!pGO->isSpawned()) { int32 nTimeToDespawn = std::max(5, int32(step.script->RespawnGameobject.DespawnDelay)); pGO->SetLootState(GO_READY); pGO->SetRespawnTime(nTimeToDespawn); pGO->GetMap()->AddToMap(pGO); } } break; case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: { // Source must be WorldObject. if (WorldObject* pSummoner = _GetScriptWorldObject(source, true, step.script)) { if (!step.script->TempSummonCreature.CreatureEntry) sLog->outError("%s creature entry (datalong) is not specified.", step.script->GetDebugInfo().c_str()); else { uint32 entry = step.script->TempSummonCreature.CreatureEntry; float x = step.script->TempSummonCreature.PosX; float y = step.script->TempSummonCreature.PosY; float z = step.script->TempSummonCreature.PosZ; float o = step.script->TempSummonCreature.Orientation; if (step.script->TempSummonCreature.CheckIfExists) if (Unit* trigger = pSummoner->SummonTrigger(x, y, z, o, 1)) if (trigger->FindNearestCreature(entry, 60.0f)) break; if (!pSummoner->SummonCreature(entry, x, y, z, o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, step.script->TempSummonCreature.DespawnDelay)) sLog->outError("%s creature was not spawned (entry: %u).", step.script->GetDebugInfo().c_str(), step.script->TempSummonCreature.CreatureEntry); } } break; } case SCRIPT_COMMAND_OPEN_DOOR: case SCRIPT_COMMAND_CLOSE_DOOR: _ScriptProcessDoor(source, target, step.script); break; case SCRIPT_COMMAND_ACTIVATE_OBJECT: // Source must be Unit. if (Unit* unit = _GetScriptUnit(source, true, step.script)) { // Target must be GameObject. if (!target) { sLog->outError("%s target object is NULL.", step.script->GetDebugInfo().c_str()); break; } if (target->GetTypeId() != TYPEID_GAMEOBJECT) { sLog->outError("%s target object is not gameobject (TypeId: %u, Entry: %u, GUID: %u), skipping.", step.script->GetDebugInfo().c_str(), target->GetTypeId(), target->GetEntry(), target->GetGUIDLow()); break; } if (GameObject* pGO = target->ToGameObject()) pGO->Use(unit); } break; case SCRIPT_COMMAND_REMOVE_AURA: { // Source (datalong2 != 0) or target (datalong2 == 0) must be Unit. bool bReverse = step.script->RemoveAura.Flags & SF_REMOVEAURA_REVERSE; if (Unit* unit = _GetScriptUnit(bReverse ? source : target, bReverse, step.script)) unit->RemoveAurasDueToSpell(step.script->RemoveAura.SpellID); break; } case SCRIPT_COMMAND_CAST_SPELL: { // TODO: Allow gameobjects to be targets and casters if (!source && !target) { sLog->outError("%s source and target objects are NULL.", step.script->GetDebugInfo().c_str()); break; } Unit* uSource = NULL; Unit* uTarget = NULL; // source/target cast spell at target/source (script->datalong2: 0: s->t 1: s->s 2: t->t 3: t->s switch (step.script->CastSpell.Flags) { case SF_CASTSPELL_SOURCE_TO_TARGET: // source -> target uSource = source ? source->ToUnit() : NULL; uTarget = target ? target->ToUnit() : NULL; break; case SF_CASTSPELL_SOURCE_TO_SOURCE: // source -> source uSource = source ? source->ToUnit() : NULL; uTarget = uSource; break; case SF_CASTSPELL_TARGET_TO_TARGET: // target -> target uSource = target ? target->ToUnit() : NULL; uTarget = uSource; break; case SF_CASTSPELL_TARGET_TO_SOURCE: // target -> source uSource = target ? target->ToUnit() : NULL; uTarget = source ? source->ToUnit() : NULL; break; case SF_CASTSPELL_SEARCH_CREATURE: // source -> creature with entry uSource = source ? source->ToUnit() : NULL; uTarget = uSource ? GetClosestCreatureWithEntry(uSource, abs(step.script->CastSpell.CreatureEntry), step.script->CastSpell.SearchRadius) : NULL; break; } if (!uSource || !uSource->isType(TYPEMASK_UNIT)) { sLog->outError("%s no source unit found for spell %u", step.script->GetDebugInfo().c_str(), step.script->CastSpell.SpellID); break; } if (!uTarget || !uTarget->isType(TYPEMASK_UNIT)) { sLog->outError("%s no target unit found for spell %u", step.script->GetDebugInfo().c_str(), step.script->CastSpell.SpellID); break; } bool triggered = (step.script->CastSpell.Flags != 4) ? step.script->CastSpell.CreatureEntry & SF_CASTSPELL_TRIGGERED : step.script->CastSpell.CreatureEntry < 0; uSource->CastSpell(uTarget, step.script->CastSpell.SpellID, triggered); break; } case SCRIPT_COMMAND_PLAY_SOUND: // Source must be WorldObject. if (WorldObject* object = _GetScriptWorldObject(source, true, step.script)) { // PlaySound.Flags bitmask: 0/1=anyone/target Player* player = NULL; if (step.script->PlaySound.Flags & SF_PLAYSOUND_TARGET_PLAYER) { // Target must be Player. player = _GetScriptPlayer(target, false, step.script); if (!target) break; } // PlaySound.Flags bitmask: 0/2=without/with distance dependent if (step.script->PlaySound.Flags & SF_PLAYSOUND_DISTANCE_SOUND) object->PlayDistanceSound(step.script->PlaySound.SoundID, player); else object->PlayDirectSound(step.script->PlaySound.SoundID, player); } break; case SCRIPT_COMMAND_CREATE_ITEM: // Target or source must be Player. if (Player* pReceiver = _GetScriptPlayerSourceOrTarget(source, target, step.script)) { ItemPosCountVec dest; InventoryResult msg = pReceiver->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, step.script->CreateItem.ItemEntry, step.script->CreateItem.Amount); if (msg == EQUIP_ERR_OK) { if (Item* item = pReceiver->StoreNewItem(dest, step.script->CreateItem.ItemEntry, true)) pReceiver->SendNewItem(item, step.script->CreateItem.Amount, false, true); } else pReceiver->SendEquipError(msg, NULL, NULL, step.script->CreateItem.ItemEntry); } break; case SCRIPT_COMMAND_DESPAWN_SELF: // Target or source must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script, true)) cSource->DespawnOrUnsummon(step.script->DespawnSelf.DespawnDelay); break; case SCRIPT_COMMAND_LOAD_PATH: // Source must be Unit. if (Unit* unit = _GetScriptUnit(source, true, step.script)) { if (!sWaypointMgr->GetPath(step.script->LoadPath.PathID)) sLog->outError("%s source object has an invalid path (%u), skipping.", step.script->GetDebugInfo().c_str(), step.script->LoadPath.PathID); else unit->GetMotionMaster()->MovePath(step.script->LoadPath.PathID, step.script->LoadPath.IsRepeatable); } break; case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: { if (!step.script->CallScript.CreatureEntry) { sLog->outError("%s creature entry is not specified, skipping.", step.script->GetDebugInfo().c_str()); break; } if (!step.script->CallScript.ScriptID) { sLog->outError("%s script id is not specified, skipping.", step.script->GetDebugInfo().c_str()); break; } Creature* cTarget = NULL; WorldObject* wSource = dynamic_cast <WorldObject*> (source); if (wSource) //using grid searcher { CellCoord p(Trinity::ComputeCellCoord(wSource->GetPositionX(), wSource->GetPositionY())); Cell cell(p); Trinity::CreatureWithDbGUIDCheck target_check(wSource, step.script->CallScript.CreatureEntry); Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(wSource, cTarget, target_check); TypeContainerVisitor<Trinity::CreatureSearcher <Trinity::CreatureWithDbGUIDCheck>, GridTypeMapContainer > unit_checker(checker); cell.Visit(p, unit_checker, *wSource->GetMap(), *wSource, wSource->GetGridActivationRange()); } else //check hashmap holders { if (CreatureData const* data = sObjectMgr->GetCreatureData(step.script->CallScript.CreatureEntry)) cTarget = ObjectAccessor::GetObjectInWorld<Creature>(data->mapid, data->posX, data->posY, MAKE_NEW_GUID(step.script->CallScript.CreatureEntry, data->id, HIGHGUID_UNIT), cTarget); } if (!cTarget) { sLog->outError("%s target was not found (entry: %u)", step.script->GetDebugInfo().c_str(), step.script->CallScript.CreatureEntry); break; } //Lets choose our ScriptMap map ScriptMapMap* datamap = GetScriptsMapByType(ScriptsType(step.script->CallScript.ScriptType)); //if no scriptmap present... if (!datamap) { sLog->outError("%s unknown scriptmap (%u) specified, skipping.", step.script->GetDebugInfo().c_str(), step.script->CallScript.ScriptType); break; } // Insert script into schedule but do not start it ScriptsStart(*datamap, step.script->CallScript.ScriptID, cTarget, NULL); break; } case SCRIPT_COMMAND_KILL: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { if (cSource->isDead()) sLog->outError("%s creature is already dead (Entry: %u, GUID: %u)", step.script->GetDebugInfo().c_str(), cSource->GetEntry(), cSource->GetGUIDLow()); else { cSource->setDeathState(JUST_DIED); if (step.script->Kill.RemoveCorpse == 1) cSource->RemoveCorpse(); } } break; case SCRIPT_COMMAND_ORIENTATION: // Source must be Unit. if (Unit* sourceUnit = _GetScriptUnit(source, true, step.script)) { if (step.script->Orientation.Flags & SF_ORIENTATION_FACE_TARGET) { // Target must be Unit. Unit* targetUnit = _GetScriptUnit(target, false, step.script); if (!targetUnit) break; sourceUnit->SetFacingToObject(targetUnit); } else sourceUnit->SetFacingTo(step.script->Orientation.Orientation); } break; case SCRIPT_COMMAND_EQUIP: // Source must be Creature. if (Creature* cSource = _GetScriptCreature(source, true, step.script)) cSource->LoadEquipment(step.script->Equip.EquipmentID); break; case SCRIPT_COMMAND_MODEL: // Source must be Creature. if (Creature* cSource = _GetScriptCreature(source, true, step.script)) cSource->SetDisplayId(step.script->Model.ModelID); break; case SCRIPT_COMMAND_CLOSE_GOSSIP: // Source must be Player. if (Player* player = _GetScriptPlayer(source, true, step.script)) player->PlayerTalkClass->SendCloseGossip(); break; case SCRIPT_COMMAND_PLAYMOVIE: // Source must be Player. if (Player* player = _GetScriptPlayer(source, true, step.script)) player->SendMovieStart(step.script->PlayMovie.MovieID); break; default: sLog->outError("Unknown script command %s.", step.script->GetDebugInfo().c_str()); break; } m_scriptSchedule.erase(iter); iter = m_scriptSchedule.begin(); sScriptMgr->DecreaseScheduledScriptCount(); } }
void WorldSession::HandleMovementOpcodes(WorldPacket & recvData) { uint16 opcode = recvData.GetOpcode(); Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plrMover = mover->ToPlayer(); // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) { recvData.rfinish(); // prevent warnings spam return; } /* extract packet */ uint64 guid; recvData.readPackGUID(guid); MovementInfo movementInfo; movementInfo.guid = guid; ReadMovementInfo(recvData, &movementInfo); recvData.rfinish(); // prevent warnings spam // prevent tampered movement data if (guid != mover->GetGUID()) return; if (!movementInfo.pos.IsPositionValid()) { recvData.rfinish(); // prevent warnings spam return; } /* handle special cases */ if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.t_pos.GetPositionX() > 50 || movementInfo.t_pos.GetPositionY() > 50 || movementInfo.t_pos.GetPositionZ() > 50) { recvData.rfinish(); // prevent warnings spam return; } if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.t_pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.t_pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.t_pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.t_pos.GetOrientation())) { recvData.rfinish(); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plrMover) { if (!plrMover->GetTransport()) { // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just dismount if the guid can be found in the transport list for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } } else if (plrMover->GetTransport()->GetGUID() != movementInfo.t_guid) { bool foundNewTransport = false; plrMover->m_transport->RemovePassenger(plrMover); for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { foundNewTransport = true; plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } if (!foundNewTransport) { plrMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; } } } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.t_guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.flags &= ~MOVEMENTFLAG_ONTRANSPORT; } } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->isInFlight()) plrMover->HandleFall(movementInfo); if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } /*----------------------*/ /* process position-change */ WorldPacket data(opcode, recvData.size()); movementInfo.time = getMSTime(); movementInfo.guid = mover->GetGUID(); WriteMovementInfo(&data, &movementInfo); mover->SendMessageToSet(&data, _player); mover->m_movementInfo = movementInfo; // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle() if (mover->GetVehicle()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); return; } mover->UpdatePosition(movementInfo.pos); if (plrMover) // nothing is charmed, or player charmed { plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); if (movementInfo.pos.GetPositionZ() < -500.0f) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away // TODO: discard movement packets after the player is rooted if (plrMover->isAlive()) { plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (!plrMover->isAlive()) plrMover->KillPlayer(); } } } } }
void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recv_data) { DEBUG_LOG("WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); Player* player = GetPlayer(); ObjectGuid lguid = player->GetLootGuid(); Loot* loot; uint8 lootSlot; Item* pItem = NULL; recv_data >> lootSlot; switch (lguid.GetHigh()) { case HIGHGUID_GAMEOBJECT: { GameObject* go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGuid() != _player->GetObjectGuid() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) { player->SendLootRelease(lguid); return; } loot = &go->loot; break; } case HIGHGUID_ITEM: { pItem = player->GetItemByGuid(lguid); if (!pItem || !pItem->HasGeneratedLoot()) { player->SendLootRelease(lguid); return; } loot = &pItem->loot; break; } case HIGHGUID_CORPSE: { Corpse* bones = player->GetMap()->GetCorpse(lguid); if (!bones) { player->SendLootRelease(lguid); return; } loot = &bones->loot; break; } case HIGHGUID_UNIT: { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = pCreature && pCreature->IsAlive() == (player->getClass() == CLASS_ROGUE && pCreature->lootForPickPocketed); if (!ok_loot || !pCreature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) { player->SendLootRelease(lguid); return; } loot = &pCreature->loot; break; } default: { sLog.outError("%s is unsupported for looting.", lguid.GetString().c_str()); return; } } QuestItem* qitem = NULL; QuestItem* ffaitem = NULL; QuestItem* conditem = NULL; LootItem* item = loot->LootItemInSlot(lootSlot, player, &qitem, &ffaitem, &conditem); if (!item) { player->SendEquipError(EQUIP_ERR_ALREADY_LOOTED, NULL, NULL); return; } Group * group = player->GetGroup(); /* Checking group conditions to be sure the player has the permissions to loot. */ if(group) { Creature * pCreature = player->GetMap()->GetCreature(lguid); switch(group->GetLootMethod()) { case GROUP_LOOT: case NEED_BEFORE_GREED: { if(item->winner && item->winner != player->GetObjectGuid() && !item->is_underthreshold && group->IsRollDoneForItem(pCreature, item)) { player->SendEquipError(EQUIP_ERR_LOOT_CANT_LOOT_THAT_NOW, NULL, NULL, item->itemid); return; } break; } case MASTER_LOOT: { if((item->winner && item->winner != player->GetObjectGuid()) || (!item->winner && !item->is_underthreshold && !item->freeforall)) { player->SendEquipError(EQUIP_ERR_LOOT_CANT_LOOT_THAT_NOW, NULL, NULL, item->itemid); return; } break; } } } // questitems use the blocked field for other purposes if (!qitem && item->is_blocked) { player->SendLootRelease(lguid); return; } if (pItem) { pItem->SetLootState(ITEM_LOOT_CHANGED); } ItemPosCountVec dest; InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item->itemid, item->count); if (msg == EQUIP_ERR_OK) { Item* newitem = player->StoreNewItem(dest, item->itemid, true, item->randomPropertyId); if (qitem) { qitem->is_looted = true; // freeforall is 1 if everyone's supposed to get the quest item. if (item->freeforall || loot->GetPlayerQuestItems().size() == 1) { player->SendNotifyLootItemRemoved(lootSlot); } else { loot->NotifyQuestItemRemoved(qitem->index); } } else { if (ffaitem) { // freeforall case, notify only one player of the removal ffaitem->is_looted = true; player->SendNotifyLootItemRemoved(lootSlot); } else { // not freeforall, notify everyone if (conditem) { conditem->is_looted = true; } loot->NotifyItemRemoved(lootSlot); } } // if only one person is supposed to loot the item, then set it to looted if (!item->freeforall) { item->is_looted = true; } --loot->unlootedCount; player->SendNewItem(newitem, uint32(item->count), false, false, true); } else { player->SendEquipError(msg, NULL, NULL, item->itemid); } }
void WorldSession::DoLootRelease(uint64 lguid) { Player *player = GetPlayer(); Loot *loot; player->SetLootGUID(0); player->SendLootRelease(lguid); player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING); if (!player->IsInWorld()) return; if (IS_GAMEOBJECT_GUID(lguid)) { GameObject* go = GetPlayer()->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) return; loot = &go->loot; if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR) { // locked doors are opened with spelleffect openlock, prevent remove its as looted go->UseDoorOrButton(); } else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE) { if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE) { // The fishing hole used once more go->AddUse(); // if the max usage is reached, will be despawned in next tick if (go->GetUseCount() >= go->GetGOValue()->FishingHole.MaxOpens) go->SetLootState(GO_JUST_DEACTIVATED); else go->SetLootState(GO_READY); } else go->SetLootState(GO_JUST_DEACTIVATED); loot->clear(); } else { // not fully looted object go->SetLootState(GO_ACTIVATED, player); // if the round robin player release, reset it. if (player->GetGUID() == loot->roundRobinPlayer) { if (Group* group = player->GetGroup()) { if (group->GetLootMethod() != MASTER_LOOT) { loot->roundRobinPlayer = 0; } } else loot->roundRobinPlayer = 0; } } } else if (IS_CORPSE_GUID(lguid)) // ONLY remove insignia at BG { Corpse* corpse = ObjectAccessor::GetCorpse(*player, lguid); if (!corpse || !corpse->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) return; loot = &corpse->loot; if (loot->isLooted()) { loot->clear(); corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE); } } else if (IS_ITEM_GUID(lguid)) { Item* pItem = player->GetItemByGuid(lguid); if (!pItem) return; ItemTemplate const* proto = pItem->GetTemplate(); // destroy only 5 items from stack in case prospecting and milling if (proto->Flags & (ITEM_PROTO_FLAG_PROSPECTABLE | ITEM_PROTO_FLAG_MILLABLE)) { pItem->m_lootGenerated = false; pItem->loot.clear(); uint32 count = pItem->GetCount(); // >=5 checked in spell code, but will work for cheating cases also with removing from another stacks. if (count > 5) count = 5; player->DestroyItemCount(pItem, count, true); } else { if (pItem->loot.isLooted()) // Only delete item if no loot or money (unlooted loot is saved to db) player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); } return; // item can be looted only single player } else { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) return; loot = &creature->loot; if (loot->isLooted()) { // skip pickpocketing loot for speed, skinning timer reduction is no-op in fact if (!creature->IsAlive()) creature->AllLootRemovedFromCorpse(); creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); loot->clear(); } else { // if the round robin player release, reset it. if (player->GetGUID() == loot->roundRobinPlayer) { if (Group* group = player->GetGroup()) { if (group->GetLootMethod() != MASTER_LOOT) { loot->roundRobinPlayer = 0; group->SendLooter(creature, NULL); // force update of dynamic flags, otherwise other group's players still not able to loot. creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS); } } else loot->roundRobinPlayer = 0; } } } //Player is not looking at loot list, he doesn't need to see updates on the loot list loot->RemoveLooter(player->GetGUID()); }
void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket & recv_data) { sLog.outDebug("WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); Player *player = GetPlayer(); uint64 lguid = player->GetLootGUID(); Loot *loot; uint8 lootSlot; recv_data >> lootSlot; if (IS_GAMEOBJECT_GUID(lguid)) { GameObject *go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))) { player->SendLootRelease(lguid); return; } loot = &go->loot; } else if (IS_ITEM_GUID(lguid)) { Item *pItem = player->GetItemByGuid(lguid); if (!pItem) { player->SendLootRelease(lguid); return; } loot = &pItem->loot; } else if (IS_CORPSE_GUID(lguid)) { Corpse *bones = ObjectAccessor::GetCorpse(*player, lguid); if (!bones) { player->SendLootRelease(lguid); return; } loot = &bones->loot; } else { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass() == CLASS_ROGUE && pCreature->lootForPickPocketed); if (!ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) { player->SendLootRelease(lguid); return; } loot = &pCreature->loot; } QuestItem *qitem = NULL; QuestItem *ffaitem = NULL; QuestItem *conditem = NULL; LootItem *item = loot->LootItemInSlot(lootSlot,player,&qitem,&ffaitem,&conditem); if (!item) { player->SendEquipError(EQUIP_ERR_ALREADY_LOOTED, NULL, NULL); return; } // questitems use the blocked field for other purposes if (!qitem && item->is_blocked) { player->SendLootRelease(lguid); return; } ItemPosCountVec dest; uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item->itemid, item->count); if (msg == EQUIP_ERR_OK) { Item * newitem = player->StoreNewItem(dest, item->itemid, true, item->randomPropertyId); if (qitem) { qitem->is_looted = true; //freeforall is 1 if everyone's supposed to get the quest item. if (item->freeforall || loot->GetPlayerQuestItems().size() == 1) player->SendNotifyLootItemRemoved(lootSlot); else loot->NotifyQuestItemRemoved(qitem->index); } else { if (ffaitem) { //freeforall case, notify only one player of the removal ffaitem->is_looted=true; player->SendNotifyLootItemRemoved(lootSlot); } else { //not freeforall, notify everyone if (conditem) conditem->is_looted=true; loot->NotifyItemRemoved(lootSlot); } } //if only one person is supposed to loot the item, then set it to looted if (!item->freeforall) item->is_looted = true; --loot->unlootedCount; player->SendNewItem(newitem, uint32(item->count), false, false, true); } else player->SendEquipError(msg, NULL, NULL); }
/// Process queued scripts void Map::ScriptsProcess() { if (m_scriptSchedule.empty()) return; ///- Process overdue queued scripts ScriptScheduleMap::iterator iter = m_scriptSchedule.begin(); // ok as multimap is a *sorted* associative container while (!m_scriptSchedule.empty() && (iter->first <= GameTime::GetGameTime())) { ScriptAction const& step = iter->second; Object* source = nullptr; if (step.sourceGUID) { switch (step.sourceGUID.GetHigh()) { case HighGuid::Item: // as well as HighGuid::Container if (Player* player = GetPlayer(step.ownerGUID)) source = player->GetItemByGuid(step.sourceGUID); break; case HighGuid::Unit: case HighGuid::Vehicle: source = GetCreature(step.sourceGUID); break; case HighGuid::Pet: source = GetPet(step.sourceGUID); break; case HighGuid::Player: source = GetPlayer(step.sourceGUID); break; case HighGuid::Transport: case HighGuid::GameObject: source = GetGameObject(step.sourceGUID); break; case HighGuid::Corpse: source = GetCorpse(step.sourceGUID); break; case HighGuid::Mo_Transport: source = GetTransport(step.sourceGUID); break; default: TC_LOG_ERROR("scripts", "%s source with unsupported high guid %s.", step.script->GetDebugInfo().c_str(), step.sourceGUID.ToString().c_str()); break; } } WorldObject* target = nullptr; if (step.targetGUID) { switch (step.targetGUID.GetHigh()) { case HighGuid::Unit: case HighGuid::Vehicle: target = GetCreature(step.targetGUID); break; case HighGuid::Pet: target = GetPet(step.targetGUID); break; case HighGuid::Player: // empty GUID case also target = GetPlayer(step.targetGUID); break; case HighGuid::Transport: case HighGuid::GameObject: target = GetGameObject(step.targetGUID); break; case HighGuid::Corpse: target = GetCorpse(step.targetGUID); break; case HighGuid::Mo_Transport: target = GetTransport(step.targetGUID); break; default: TC_LOG_ERROR("scripts", "%s target with unsupported high guid %s.", step.script->GetDebugInfo().c_str(), step.targetGUID.ToString().c_str()); break; } } switch (step.script->command) { case SCRIPT_COMMAND_TALK: { if (step.script->Talk.ChatType > CHAT_TYPE_BOSS_WHISPER) { TC_LOG_ERROR("scripts", "%s invalid chat type (%u) specified, skipping.", step.script->GetDebugInfo().c_str(), step.script->Talk.ChatType); break; } if (step.script->Talk.Flags & SF_TALK_USE_PLAYER) source = _GetScriptPlayerSourceOrTarget(source, target, step.script); else source = _GetScriptCreatureSourceOrTarget(source, target, step.script); if (source) { Unit* sourceUnit = source->ToUnit(); if (!sourceUnit) { TC_LOG_ERROR("scripts", "%s source object (%s) is not an unit, skipping.", step.script->GetDebugInfo().c_str(), source->GetGUID().ToString().c_str()); break; } switch (step.script->Talk.ChatType) { case CHAT_TYPE_SAY: sourceUnit->Say(step.script->Talk.TextID, target); break; case CHAT_TYPE_YELL: sourceUnit->Yell(step.script->Talk.TextID, target); break; case CHAT_TYPE_TEXT_EMOTE: case CHAT_TYPE_BOSS_EMOTE: sourceUnit->TextEmote(step.script->Talk.TextID, target, step.script->Talk.ChatType == CHAT_TYPE_BOSS_EMOTE); break; case CHAT_TYPE_WHISPER: case CHAT_TYPE_BOSS_WHISPER: { Player* receiver = target ? target->ToPlayer() : nullptr; if (!receiver) TC_LOG_ERROR("scripts", "%s attempt to whisper to non-player unit, skipping.", step.script->GetDebugInfo().c_str()); else sourceUnit->Whisper(step.script->Talk.TextID, receiver, step.script->Talk.ChatType == CHAT_TYPE_BOSS_WHISPER); break; } default: break; // must be already checked at load } } break; } case SCRIPT_COMMAND_EMOTE: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { if (step.script->Emote.Flags & SF_EMOTE_USE_STATE) cSource->SetUInt32Value(UNIT_NPC_EMOTESTATE, step.script->Emote.EmoteID); else cSource->HandleEmoteCommand(step.script->Emote.EmoteID); } break; case SCRIPT_COMMAND_FIELD_SET: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { // Validate field number. if (step.script->FieldSet.FieldID <= OBJECT_FIELD_ENTRY || step.script->FieldSet.FieldID >= cSource->GetValuesCount()) TC_LOG_ERROR("scripts", "%s wrong field %u (max count: %u) in object (TypeId: %u, %s) specified, skipping.", step.script->GetDebugInfo().c_str(), step.script->FieldSet.FieldID, cSource->GetValuesCount(), cSource->GetTypeId(), cSource->GetGUID().ToString().c_str()); else cSource->SetUInt32Value(step.script->FieldSet.FieldID, step.script->FieldSet.FieldValue); } break; case SCRIPT_COMMAND_MOVE_TO: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { Unit* unit = (Unit*)cSource; if (step.script->MoveTo.TravelTime != 0) { float speed = unit->GetDistance(step.script->MoveTo.DestX, step.script->MoveTo.DestY, step.script->MoveTo.DestZ) / ((float)step.script->MoveTo.TravelTime * 0.001f); unit->MonsterMoveWithSpeed(step.script->MoveTo.DestX, step.script->MoveTo.DestY, step.script->MoveTo.DestZ, speed); } else unit->NearTeleportTo(step.script->MoveTo.DestX, step.script->MoveTo.DestY, step.script->MoveTo.DestZ, unit->GetOrientation()); } break; case SCRIPT_COMMAND_FLAG_SET: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { // Validate field number. if (step.script->FlagToggle.FieldID <= OBJECT_FIELD_ENTRY || step.script->FlagToggle.FieldID >= cSource->GetValuesCount()) TC_LOG_ERROR("scripts", "%s wrong field %u (max count: %u) in object (TypeId: %u, Entry: %u, GUID: %u) specified, skipping.", step.script->GetDebugInfo().c_str(), step.script->FlagToggle.FieldID, cSource->GetValuesCount(), cSource->GetTypeId(), cSource->GetEntry(), cSource->GetGUID().GetCounter()); else cSource->SetFlag(step.script->FlagToggle.FieldID, step.script->FlagToggle.FieldValue); } break; case SCRIPT_COMMAND_FLAG_REMOVE: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { // Validate field number. if (step.script->FlagToggle.FieldID <= OBJECT_FIELD_ENTRY || step.script->FlagToggle.FieldID >= cSource->GetValuesCount()) TC_LOG_ERROR("scripts", "%s wrong field %u (max count: %u) in object (TypeId: %u, Entry: %u, GUID: %u) specified, skipping.", step.script->GetDebugInfo().c_str(), step.script->FlagToggle.FieldID, cSource->GetValuesCount(), cSource->GetTypeId(), cSource->GetEntry(), cSource->GetGUID().GetCounter()); else cSource->RemoveFlag(step.script->FlagToggle.FieldID, step.script->FlagToggle.FieldValue); } break; case SCRIPT_COMMAND_TELEPORT_TO: if (step.script->TeleportTo.Flags & SF_TELEPORT_USE_CREATURE) { // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script, true)) cSource->NearTeleportTo(step.script->TeleportTo.DestX, step.script->TeleportTo.DestY, step.script->TeleportTo.DestZ, step.script->TeleportTo.Orientation); } else { // Source or target must be Player. if (Player* player = _GetScriptPlayerSourceOrTarget(source, target, step.script)) player->TeleportTo(step.script->TeleportTo.MapID, step.script->TeleportTo.DestX, step.script->TeleportTo.DestY, step.script->TeleportTo.DestZ, step.script->TeleportTo.Orientation); } break; case SCRIPT_COMMAND_QUEST_EXPLORED: { if (!source) { TC_LOG_ERROR("scripts", "%s source object is NULL.", step.script->GetDebugInfo().c_str()); break; } if (!target) { TC_LOG_ERROR("scripts", "%s target object is NULL.", step.script->GetDebugInfo().c_str()); break; } // when script called for item spell casting then target == (unit or GO) and source is player WorldObject* worldObject; Player* player = target->ToPlayer(); if (player) { if (source->GetTypeId() != TYPEID_UNIT && source->GetTypeId() != TYPEID_GAMEOBJECT && source->GetTypeId() != TYPEID_PLAYER) { TC_LOG_ERROR("scripts", "%s source is not unit, gameobject or player (TypeId: %u, Entry: %u, GUID: %u), skipping.", step.script->GetDebugInfo().c_str(), source->GetTypeId(), source->GetEntry(), source->GetGUID().GetCounter()); break; } worldObject = dynamic_cast<WorldObject*>(source); } else { player = source->ToPlayer(); if (player) { if (target->GetTypeId() != TYPEID_UNIT && target->GetTypeId() != TYPEID_GAMEOBJECT && target->GetTypeId() != TYPEID_PLAYER) { TC_LOG_ERROR("scripts", "%s target is not unit, gameobject or player (TypeId: %u, Entry: %u, GUID: %u), skipping.", step.script->GetDebugInfo().c_str(), target->GetTypeId(), target->GetEntry(), target->GetGUID().GetCounter()); break; } worldObject = dynamic_cast<WorldObject*>(target); } else { TC_LOG_ERROR("scripts", "%s neither source nor target is player (source: TypeId: %u, Entry: %u, GUID: %u; target: TypeId: %u, Entry: %u, GUID: %u), skipping.", step.script->GetDebugInfo().c_str(), source->GetTypeId(), source->GetEntry(), source->GetGUID().GetCounter(), target->GetTypeId(), target->GetEntry(), target->GetGUID().GetCounter()); break; } } // quest id and flags checked at script loading if ((worldObject->GetTypeId() != TYPEID_UNIT || ((Unit*)worldObject)->IsAlive()) && (step.script->QuestExplored.Distance == 0 || worldObject->IsWithinDistInMap(player, float(step.script->QuestExplored.Distance)))) player->AreaExploredOrEventHappens(step.script->QuestExplored.QuestID); else player->FailQuest(step.script->QuestExplored.QuestID); break; } case SCRIPT_COMMAND_KILL_CREDIT: // Source or target must be Player. if (Player* player = _GetScriptPlayerSourceOrTarget(source, target, step.script)) { if (step.script->KillCredit.Flags & SF_KILLCREDIT_REWARD_GROUP) player->RewardPlayerAndGroupAtEvent(step.script->KillCredit.CreatureEntry, player); else player->KilledMonsterCredit(step.script->KillCredit.CreatureEntry); } break; case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: if (!step.script->RespawnGameobject.GOGuid) { TC_LOG_ERROR("scripts", "%s gameobject guid (datalong) is not specified.", step.script->GetDebugInfo().c_str()); break; } // Source or target must be WorldObject. if (WorldObject* pSummoner = _GetScriptWorldObject(source, true, step.script)) { GameObject* pGO = _FindGameObject(pSummoner, step.script->RespawnGameobject.GOGuid); if (!pGO) { TC_LOG_ERROR("scripts", "%s gameobject was not found (guid: %u).", step.script->GetDebugInfo().c_str(), step.script->RespawnGameobject.GOGuid); break; } if (pGO->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE || pGO->GetGoType() == GAMEOBJECT_TYPE_DOOR || pGO->GetGoType() == GAMEOBJECT_TYPE_BUTTON || pGO->GetGoType() == GAMEOBJECT_TYPE_TRAP) { TC_LOG_ERROR("scripts", "%s can not be used with gameobject of type %u (guid: %u).", step.script->GetDebugInfo().c_str(), uint32(pGO->GetGoType()), step.script->RespawnGameobject.GOGuid); break; } // Check that GO is not spawned if (!pGO->isSpawned()) { int32 nTimeToDespawn = std::max(5, int32(step.script->RespawnGameobject.DespawnDelay)); pGO->SetLootState(GO_READY); pGO->SetRespawnTime(nTimeToDespawn); pGO->GetMap()->AddToMap(pGO); } } break; case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: { // Source must be WorldObject. if (WorldObject* pSummoner = _GetScriptWorldObject(source, true, step.script)) { if (!step.script->TempSummonCreature.CreatureEntry) TC_LOG_ERROR("scripts", "%s creature entry (datalong) is not specified.", step.script->GetDebugInfo().c_str()); else { float x = step.script->TempSummonCreature.PosX; float y = step.script->TempSummonCreature.PosY; float z = step.script->TempSummonCreature.PosZ; float o = step.script->TempSummonCreature.Orientation; if (!pSummoner->SummonCreature(step.script->TempSummonCreature.CreatureEntry, x, y, z, o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, step.script->TempSummonCreature.DespawnDelay)) TC_LOG_ERROR("scripts", "%s creature was not spawned (entry: %u).", step.script->GetDebugInfo().c_str(), step.script->TempSummonCreature.CreatureEntry); } } break; } case SCRIPT_COMMAND_OPEN_DOOR: case SCRIPT_COMMAND_CLOSE_DOOR: _ScriptProcessDoor(source, target, step.script); break; case SCRIPT_COMMAND_ACTIVATE_OBJECT: // Source must be Unit. if (Unit* unit = _GetScriptUnit(source, true, step.script)) { // Target must be GameObject. if (!target) { TC_LOG_ERROR("scripts", "%s target object is NULL.", step.script->GetDebugInfo().c_str()); break; } if (target->GetTypeId() != TYPEID_GAMEOBJECT) { TC_LOG_ERROR("scripts", "%s target object is not gameobject (TypeId: %u, Entry: %u, GUID: %u), skipping.", step.script->GetDebugInfo().c_str(), target->GetTypeId(), target->GetEntry(), target->GetGUID().GetCounter()); break; } if (GameObject* pGO = target->ToGameObject()) pGO->Use(unit); } break; case SCRIPT_COMMAND_REMOVE_AURA: { // Source (datalong2 != 0) or target (datalong2 == 0) must be Unit. bool bReverse = step.script->RemoveAura.Flags & SF_REMOVEAURA_REVERSE; if (Unit* unit = _GetScriptUnit(bReverse ? source : target, bReverse, step.script)) unit->RemoveAurasDueToSpell(step.script->RemoveAura.SpellID); break; } case SCRIPT_COMMAND_CAST_SPELL: { /// @todo Allow gameobjects to be targets and casters if (!source && !target) { TC_LOG_ERROR("scripts", "%s source and target objects are NULL.", step.script->GetDebugInfo().c_str()); break; } Unit* uSource = nullptr; Unit* uTarget = nullptr; // source/target cast spell at target/source (script->datalong2: 0: s->t 1: s->s 2: t->t 3: t->s) switch (step.script->CastSpell.Flags) { case SF_CASTSPELL_SOURCE_TO_TARGET: // source -> target uSource = source ? source->ToUnit() : nullptr; uTarget = target ? target->ToUnit() : nullptr; break; case SF_CASTSPELL_SOURCE_TO_SOURCE: // source -> source uSource = source ? source->ToUnit() : nullptr; uTarget = uSource; break; case SF_CASTSPELL_TARGET_TO_TARGET: // target -> target uSource = target ? target->ToUnit() : nullptr; uTarget = uSource; break; case SF_CASTSPELL_TARGET_TO_SOURCE: // target -> source uSource = target ? target->ToUnit() : nullptr; uTarget = source ? source->ToUnit() : nullptr; break; case SF_CASTSPELL_SEARCH_CREATURE: // source -> creature with entry uSource = source ? source->ToUnit() : nullptr; uTarget = uSource ? uSource->FindNearestCreature(abs(step.script->CastSpell.CreatureEntry), step.script->CastSpell.SearchRadius) : nullptr; break; } if (!uSource || !uSource->isType(TYPEMASK_UNIT)) { TC_LOG_ERROR("scripts", "%s no source unit found for spell %u", step.script->GetDebugInfo().c_str(), step.script->CastSpell.SpellID); break; } if (!uTarget || !uTarget->isType(TYPEMASK_UNIT)) { TC_LOG_ERROR("scripts", "%s no target unit found for spell %u", step.script->GetDebugInfo().c_str(), step.script->CastSpell.SpellID); break; } bool triggered = (step.script->CastSpell.Flags != 4) ? step.script->CastSpell.CreatureEntry & SF_CASTSPELL_TRIGGERED : step.script->CastSpell.CreatureEntry < 0; uSource->CastSpell(uTarget, step.script->CastSpell.SpellID, triggered); break; } case SCRIPT_COMMAND_PLAY_SOUND: // Source must be WorldObject. if (WorldObject* object = _GetScriptWorldObject(source, true, step.script)) { // PlaySound.Flags bitmask: 0/1=anyone/target Player* player = nullptr; if (step.script->PlaySound.Flags & SF_PLAYSOUND_TARGET_PLAYER) { // Target must be Player. player = _GetScriptPlayer(target, false, step.script); if (!target) break; } // PlaySound.Flags bitmask: 0/2=without/with distance dependent if (step.script->PlaySound.Flags & SF_PLAYSOUND_DISTANCE_SOUND) object->PlayDistanceSound(step.script->PlaySound.SoundID, player); else object->PlayDirectSound(step.script->PlaySound.SoundID, player); } break; case SCRIPT_COMMAND_CREATE_ITEM: // Target or source must be Player. if (Player* pReceiver = _GetScriptPlayerSourceOrTarget(source, target, step.script)) { ItemPosCountVec dest; InventoryResult msg = pReceiver->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, step.script->CreateItem.ItemEntry, step.script->CreateItem.Amount); if (msg == EQUIP_ERR_OK) { if (Item* item = pReceiver->StoreNewItem(dest, step.script->CreateItem.ItemEntry, true)) pReceiver->SendNewItem(item, step.script->CreateItem.Amount, false, true); } else pReceiver->SendEquipError(msg, nullptr, nullptr, step.script->CreateItem.ItemEntry); } break; case SCRIPT_COMMAND_DESPAWN_SELF: // Target or source must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script, true)) cSource->DespawnOrUnsummon(step.script->DespawnSelf.DespawnDelay); break; case SCRIPT_COMMAND_LOAD_PATH: // Source must be Unit. if (Unit* unit = _GetScriptUnit(source, true, step.script)) { if (!sWaypointMgr->GetPath(step.script->LoadPath.PathID)) TC_LOG_ERROR("scripts", "%s source object has an invalid path (%u), skipping.", step.script->GetDebugInfo().c_str(), step.script->LoadPath.PathID); else unit->GetMotionMaster()->MovePath(step.script->LoadPath.PathID, step.script->LoadPath.IsRepeatable != 0); } break; case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: { if (!step.script->CallScript.CreatureEntry) { TC_LOG_ERROR("scripts", "%s creature entry is not specified, skipping.", step.script->GetDebugInfo().c_str()); break; } if (!step.script->CallScript.ScriptID) { TC_LOG_ERROR("scripts", "%s script id is not specified, skipping.", step.script->GetDebugInfo().c_str()); break; } Creature* cTarget = nullptr; auto creatureBounds = _creatureBySpawnIdStore.equal_range(step.script->CallScript.CreatureEntry); if (creatureBounds.first != creatureBounds.second) { // Prefer alive (last respawned) creature auto creatureItr = std::find_if(creatureBounds.first, creatureBounds.second, [](Map::CreatureBySpawnIdContainer::value_type const& pair) { return pair.second->IsAlive(); }); cTarget = creatureItr != creatureBounds.second ? creatureItr->second : creatureBounds.first->second; } if (!cTarget) { TC_LOG_ERROR("scripts", "%s target was not found (entry: %u)", step.script->GetDebugInfo().c_str(), step.script->CallScript.CreatureEntry); break; } //Lets choose our ScriptMap map ScriptMapMap* datamap = GetScriptsMapByType(ScriptsType(step.script->CallScript.ScriptType)); //if no scriptmap present... if (!datamap) { TC_LOG_ERROR("scripts", "%s unknown scriptmap (%u) specified, skipping.", step.script->GetDebugInfo().c_str(), step.script->CallScript.ScriptType); break; } // Insert script into schedule but do not start it ScriptsStart(*datamap, step.script->CallScript.ScriptID, cTarget, nullptr); break; } case SCRIPT_COMMAND_KILL: // Source or target must be Creature. if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script)) { if (cSource->isDead()) TC_LOG_ERROR("scripts", "%s creature is already dead (Entry: %u, GUID: %u)", step.script->GetDebugInfo().c_str(), cSource->GetEntry(), cSource->GetGUID().GetCounter()); else { cSource->setDeathState(JUST_DIED); if (step.script->Kill.RemoveCorpse == 1) cSource->RemoveCorpse(); } } break; case SCRIPT_COMMAND_ORIENTATION: // Source must be Unit. if (Unit* sourceUnit = _GetScriptUnit(source, true, step.script)) { if (step.script->Orientation.Flags & SF_ORIENTATION_FACE_TARGET) { // Target must be Unit. Unit* targetUnit = _GetScriptUnit(target, false, step.script); if (!targetUnit) break; sourceUnit->SetFacingToObject(targetUnit); } else sourceUnit->SetFacingTo(step.script->Orientation.Orientation); } break; case SCRIPT_COMMAND_EQUIP: // Source must be Creature. if (Creature* cSource = _GetScriptCreature(source, true, step.script)) cSource->LoadEquipment(step.script->Equip.EquipmentID); break; case SCRIPT_COMMAND_MODEL: // Source must be Creature. if (Creature* cSource = _GetScriptCreature(source, true, step.script)) cSource->SetDisplayId(step.script->Model.ModelID); break; case SCRIPT_COMMAND_CLOSE_GOSSIP: // Source must be Player. if (Player* player = _GetScriptPlayer(source, true, step.script)) player->PlayerTalkClass->SendCloseGossip(); break; case SCRIPT_COMMAND_PLAYMOVIE: // Source must be Player. if (Player* player = _GetScriptPlayer(source, true, step.script)) player->SendMovieStart(step.script->PlayMovie.MovieID); break; case SCRIPT_COMMAND_MOVEMENT: // Source must be Creature. if (Creature* cSource = _GetScriptCreature(source, true, step.script)) { if (!cSource->IsAlive()) return; cSource->GetMotionMaster()->MoveIdle(); switch (step.script->Movement.MovementType) { case RANDOM_MOTION_TYPE: cSource->GetMotionMaster()->MoveRandom((float)step.script->Movement.MovementDistance); break; case WAYPOINT_MOTION_TYPE: cSource->GetMotionMaster()->MovePath(step.script->Movement.Path, false); break; } } break; default: TC_LOG_ERROR("scripts", "Unknown script command %s.", step.script->GetDebugInfo().c_str()); break; } m_scriptSchedule.erase(iter); iter = m_scriptSchedule.begin(); sMapMgr->DecreaseScheduledScriptCount(); } }
void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket & recvData) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); Player* player = GetPlayer(); uint64 lguid = player->GetLootGUID(); Loot* loot = NULL; uint8 lootSlot = 0; uint8 linkedLootSlot = 255; uint32 count = recvData.ReadBits(23); std::vector<ObjectGuid> guids; guids.resize(count); uint8 bitOrder[8] = { 2, 1, 5, 7, 4, 3, 0, 6 }; for (uint32 i = 0; i < count; i++) recvData.ReadBitInOrder(guids[i], bitOrder); recvData.FlushBits(); for (uint32 i = 0; i < count; i++) { recvData.ReadByteSeq(guids[i][0]); recvData.ReadByteSeq(guids[i][3]); recvData >> lootSlot; recvData.ReadByteSeq(guids[i][7]); recvData.ReadByteSeq(guids[i][2]); recvData.ReadByteSeq(guids[i][4]); recvData.ReadByteSeq(guids[i][1]); recvData.ReadByteSeq(guids[i][6]); recvData.ReadByteSeq(guids[i][5]); linkedLootSlot = 0xFF; if (IS_GAMEOBJECT_GUID(lguid)) { GameObject* go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) { player->SendLootRelease(lguid); return; } loot = &go->loot; } else if (IS_ITEM_GUID(lguid)) { Item* pItem = player->GetItemByGuid(lguid); if (!pItem) { player->SendLootRelease(lguid); return; } loot = &pItem->loot; } else if (IS_CORPSE_GUID(lguid)) { Corpse* bones = ObjectAccessor::GetCorpse(*player, lguid); if (!bones) { player->SendLootRelease(lguid); return; } loot = &bones->loot; } else { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); bool lootAllowed = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); if (!lootAllowed || (!creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE) && !_player->HasSpell(125048))) { player->SendLootRelease(lguid); return; } loot = &creature->loot; if (loot->isLinkedLoot(lootSlot)) { LinkedLootInfo linkedLootInfo = loot->getLinkedLoot(lootSlot); creature = player->GetCreature(*player, linkedLootInfo.creatureGUID); if (!creature) { player->SendLootRelease(lguid); return; } loot = &creature->loot; linkedLootSlot = lootSlot; lootSlot = linkedLootInfo.slot; } } player->StoreLootItem(lootSlot, loot, linkedLootSlot); } }
void WorldSession::HandleMovementOpcodes(WorldPacket & recv_data) { uint16 opcode = recv_data.GetOpcode(); recv_data.hexlike(); Unit *mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player *plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL; // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plMover && plMover->IsBeingTeleported()) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } /* extract packet */ uint64 guid; recv_data.readPackGUID(guid); MovementInfo movementInfo; movementInfo.guid = guid; ReadMovementInfo(recv_data, &movementInfo); recv_data.rpos(recv_data.wpos()); // prevent warnings spam // prevent tampered movement data if (guid != mover->GetGUID()) return; if (!movementInfo.pos.IsPositionValid()) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } /* handle special cases */ if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.t_pos.GetPositionX() > 50 || movementInfo.t_pos.GetPositionY() > 50 || movementInfo.t_pos.GetPositionZ() > 50) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.t_pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.t_pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.t_pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.t_pos.GetOrientation())) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plMover && !plMover->GetTransport()) { // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list for (MapManager::TransportSet::const_iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { plMover->m_transport = (*iter); (*iter)->AddPassenger(plMover); break; } } } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject *go = mover->GetMap()->GetGameObject(movementInfo.t_guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.flags &= ~MOVEMENTFLAG_ONTRANSPORT; } } else if (plMover && plMover->GetTransport()) // if we were on a transport, leave { plMover->m_transport->RemovePassenger(plMover); plMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->isInFlight()) plMover->HandleFall(movementInfo); if (plMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plMover->IsInWater()) { // now client not include swimming flag in case jumping under water plMover->SetInWater(!plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } /*----------------------*/ // ANTICHEAT CHECKS if (sWorld.getBoolConfig(CONFIG_ANTICHEAT_ENABLE)) { /*********************/ /* Exceptions: In Flight On Transport Being Teleported Can't free move Is GameMaster **********************/ // preventing escape from JAIL! MUAHAHHAHA if (plMover && plMover->GetAreaId() == 876 && !plMover->isGameMaster()) { if (plMover->GetBaseMap()->GetAreaId(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ()) != 876) { plMover->TeleportTo(1,16226.5f,16403.6f,-64.5f,3.2f); return; } } if (plMover && !plMover->isInFlight() && !plMover->GetTransport() && !plMover->IsBeingTeleported() && plMover->CanFreeMove() && !plMover->isGameMaster()) { // speed hack detection called! plMover->SpeedHackDetection(plMover->GetLastPacket(), movementInfo,opcode, plMover->GetLastSpeedRate()); if (plMover->isAlive()) plMover->WalkOnWaterHackDetection(plMover->GetLastPacket(),movementInfo); // fly hack detection called! plMover->FlyHackDetection(plMover->GetLastPacket(),movementInfo); } // save packet time for next control. if (plMover) { uint8 uiMoveType = 0; if (plMover->IsFlying()) uiMoveType = MOVE_FLIGHT; else if (plMover->IsUnderWater()) uiMoveType = MOVE_SWIM; else uiMoveType = MOVE_RUN; plMover->SaveLastPacket(movementInfo); plMover->SetLastSpeedRate(plMover->GetSpeedRate(UnitMoveType(uiMoveType)));//plMover->GetSpeed(UnitMoveType(uiMoveType)));//Rate(UnitMoveType(uiMoveType))); plMover->SetLastOpcode(opcode); } } /* process position-change */ WorldPacket data(opcode, recv_data.size()); movementInfo.time = getMSTime(); movementInfo.guid = mover->GetGUID(); WriteMovementInfo(&data, &movementInfo); mover->SendMessageToSet(&data, _player); mover->m_movementInfo = movementInfo; // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle() if (mover->GetVehicle()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); return; } mover->SetPosition(movementInfo.pos); if (plMover) // nothing is charmed, or player charmed { plMover->UpdateFallInformationIfNeed(movementInfo, opcode); if (movementInfo.pos.GetPositionZ() < -500.0f) { if (!(plMover->InBattleground() && plMover->GetBattleground() && plMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away // TODO: discard movement packets after the player is rooted if (plMover->isAlive()) { plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // pl can be alive if GM/etc if (!plMover->isAlive()) { // change the death state to CORPSE to prevent the death timer from // starting in the next player update plMover->KillPlayer(); plMover->BuildPlayerRepop(); } } // cancel the death timer here if started plMover->RepopAtGraveyard(); } } } }
void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) { uint16 opcode = recvPacket.GetOpcode(); Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plrMover = mover->ToPlayer(); // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) { recvPacket.rfinish(); // prevent warnings spam return; } /* extract packet */ MovementInfo movementInfo; GetPlayer()->ReadMovementInfo(recvPacket, &movementInfo); if(plrMover && !plrMover->isGameMaster() && plrMover->GetAntiHackLastPos().IsPositionValid() && movementInfo.t_guid == 0 && !(movementInfo.flags & MOVEMENTFLAG_FALLING)) { Position lPos = plrMover->GetAntiHackLastPos(); float dist = movementInfo.pos.GetExactDist(&lPos); uint32 dt = GetMSTimeDiffToNow(plrMover->GetAntiHackLastTime()); if(dt != 0 && dist != 0) { UnitMoveType mType; if(movementInfo.flags & MOVEMENTFLAG_WALKING && !(movementInfo.flags & MOVEMENTFLAG_FLYING)) { mType = MOVE_WALK; } else if(movementInfo.flags & MOVEMENTFLAG_SWIMMING) { if(movementInfo.flags & MOVEMENTFLAG_BACKWARD) mType = MOVE_SWIM_BACK; else mType = MOVE_SWIM; } else if(movementInfo.flags & MOVEMENTFLAG_FLYING) { if(movementInfo.flags & MOVEMENTFLAG_BACKWARD) mType = MOVE_FLIGHT_BACK; else mType = MOVE_FLIGHT; } else { if(movementInfo.flags & MOVEMENTFLAG_BACKWARD) mType = MOVE_RUN_BACK; else mType = MOVE_RUN; } float vitesse = dist / (float)(dt / float(IN_MILLISECONDS)), vsimu = plrMover->GetSpeed(mType); int ecart_relatif = floor(((vitesse - vsimu) / vsimu) * 100); if(ecart_relatif > 100) plrMover->ReportSpeedHack(vitesse); else plrMover->ResetSpeedHackReport(); if(movementInfo.flags & MOVEMENTFLAG_FLYING) { if(!plrMover->HasAuraType(SPELL_AURA_FLY) && !plrMover->HasAuraType(SPELL_AURA_MOUNTED) && !plrMover->m_mover->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)) { plrMover->ReportFlyHack(); } else plrMover->ResetFlyHackReport(); } if(movementInfo.flags & MOVEMENTFLAG_WATERWALKING) { if(!plrMover->HasAuraType(SPELL_AURA_WATER_WALK) && !plrMover->HasAuraType(SPELL_AURA_GHOST)) { plrMover->ReportWWHack(); } else plrMover->ResetWWHackReport(); } /*if(movementInfo.flags & MOVEMENTFLAG_JUMPING) { ss << " jumping (vitesse Z :" << movementInfo.j_zspeed << " XY : " << movementInfo.j_xyspeed << ")"; }*/ } } if(plrMover) { plrMover->SetAntiHackLastPos(movementInfo.pos); plrMover->SetAntiHackLastTime(getMSTime()); } // prevent tampered movement data if (movementInfo.guid != mover->GetGUID()) { sLog->outError(LOG_FILTER_NETWORKIO, "HandleMovementOpcodes: guid error"); sLog->outError(LOG_FILTER_NETWORKIO, "moveguid : %lu, mover : %lu, opcode : %u", movementInfo.guid, mover->GetGUID(), opcode); return; } if (!movementInfo.pos.IsPositionValid()) { sLog->outError(LOG_FILTER_NETWORKIO, "HandleMovementOpcodes: Invalid Position"); return; } /* handle special cases */ if (movementInfo.t_guid) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.t_pos.GetPositionX() > 50 || movementInfo.t_pos.GetPositionY() > 50 || movementInfo.t_pos.GetPositionZ() > 50) { recvPacket.rfinish(); // prevent warnings spam return; } if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.t_pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.t_pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.t_pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.t_pos.GetOrientation())) { recvPacket.rfinish(); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plrMover) { if (!plrMover->GetTransport()) { // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just dismount if the guid can be found in the transport list for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } } else if (plrMover->GetTransport()->GetGUID() != movementInfo.t_guid) { bool foundNewTransport = false; plrMover->m_transport->RemovePassenger(plrMover); for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { foundNewTransport = true; plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } if (!foundNewTransport) { plrMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; } } } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.t_guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.t_guid = 0; } } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->isInFlight()) plrMover->HandleFall(movementInfo); if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } movementInfo.time = getMSTime(); movementInfo.guid = mover->GetGUID(); mover->m_movementInfo = movementInfo; /*----------------------*/ /* process position-change */ // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle() if (mover->GetVehicle()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); return; } mover->UpdatePosition(movementInfo.pos); WorldPacket data(SMSG_PLAYER_MOVE, recvPacket.size()); _player->WriteMovementInfo(data); mover->SendMessageToSet(&data, _player); if (plrMover) // nothing is charmed, or player charmed { plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); AreaTableEntry const* zone = GetAreaEntryByAreaID(plrMover->GetAreaId()); float depth = zone ? zone->MaxDepth : -500.0f; if (movementInfo.pos.GetPositionZ() < depth) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away // TODO: discard movement packets after the player is rooted if (plrMover->isAlive()) { plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (!plrMover->isAlive()) plrMover->KillPlayer(); } } } } }
GameObject* Garrison::Plot::CreateGameObject(Map* map, GarrisonFactionIndex faction) { uint32 entry = EmptyGameObjectId; if (BuildingInfo.PacketInfo) { GarrPlotInstanceEntry const* plotInstance = sGarrPlotInstanceStore.AssertEntry(PacketInfo.GarrPlotInstanceID); GarrPlotEntry const* plot = sGarrPlotStore.AssertEntry(plotInstance->GarrPlotID); GarrBuildingEntry const* building = sGarrBuildingStore.AssertEntry(BuildingInfo.PacketInfo->GarrBuildingID); entry = faction == GARRISON_FACTION_INDEX_HORDE ? plot->HordeConstructionGameObjectID : plot->AllianceConstructionGameObjectID; if (BuildingInfo.PacketInfo->Active || !entry) entry = faction == GARRISON_FACTION_INDEX_HORDE ? building->HordeGameObjectID : building->AllianceGameObjectID; } if (!sObjectMgr->GetGameObjectTemplate(entry)) { TC_LOG_ERROR("garrison", "Garrison attempted to spawn gameobject whose template doesn't exist (%u)", entry); return nullptr; } Position const& pos = PacketInfo.PlotPos; GameObject* building = new GameObject(); if (!building->Create(entry, map, 0, pos, G3D::Quat(), 255, GO_STATE_READY)) { delete building; return nullptr; } if (BuildingInfo.CanActivate() && BuildingInfo.PacketInfo && !BuildingInfo.PacketInfo->Active) { if (FinalizeGarrisonPlotGOInfo const* finalizeInfo = sGarrisonMgr.GetPlotFinalizeGOInfo(PacketInfo.GarrPlotInstanceID)) { Position const& pos2 = finalizeInfo->FactionInfo[faction].Pos; GameObject* finalizer = new GameObject(); if (finalizer->Create(finalizeInfo->FactionInfo[faction].GameObjectId, map, 0, pos2, G3D::Quat(), 255, GO_STATE_READY)) { // set some spell id to make the object delete itself after use finalizer->SetSpellId(finalizer->GetGOInfo()->goober.spell); finalizer->SetRespawnTime(0); if (uint16 animKit = finalizeInfo->FactionInfo[faction].AnimKitId) finalizer->SetAnimKitId(animKit, false); map->AddToMap(finalizer); } else delete finalizer; } } if (building->GetGoType() == GAMEOBJECT_TYPE_GARRISON_BUILDING && building->GetGOInfo()->garrisonBuilding.SpawnMap) { for (CellObjectGuidsMap::value_type const& cellGuids : sObjectMgr->GetMapObjectGuids(building->GetGOInfo()->garrisonBuilding.SpawnMap, map->GetSpawnMode())) { for (ObjectGuid::LowType spawnId : cellGuids.second.creatures) if (Creature* spawn = BuildingSpawnHelper<Creature, &Creature::SetHomePosition>(building, spawnId, map)) BuildingInfo.Spawns.insert(spawn->GetGUID()); for (ObjectGuid::LowType spawnId : cellGuids.second.gameobjects) if (GameObject* spawn = BuildingSpawnHelper<GameObject, &GameObject::RelocateStationaryPosition>(building, spawnId, map)) BuildingInfo.Spawns.insert(spawn->GetGUID()); } } BuildingInfo.Guid = building->GetGUID(); return building; }
void WorldSession::HandleAutostoreLootItemOpcode(WorldPackets::Loot::LootItem& packet) { Player* player = GetPlayer(); AELootResult aeResult; AELootResult* aeResultPtr = player->GetAELootView().size() > 1 ? &aeResult : nullptr; /// @todo Implement looting by LootObject guid for (WorldPackets::Loot::LootRequest const& req : packet.Loot) { Loot* loot = nullptr; ObjectGuid lguid = player->GetLootWorldObjectGUID(req.Object); if (lguid.IsGameObject()) { GameObject* go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) { player->SendLootRelease(lguid); continue; } loot = &go->loot; } else if (lguid.IsItem()) { Item* pItem = player->GetItemByGuid(lguid); if (!pItem) { player->SendLootRelease(lguid); continue; } loot = &pItem->loot; } else if (lguid.IsCorpse()) { Corpse* bones = ObjectAccessor::GetCorpse(*player, lguid); if (!bones) { player->SendLootRelease(lguid); continue; } loot = &bones->loot; } else { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); if (!lootAllowed || !creature->IsWithinDistInMap(_player, AELootCreatureCheck::LootDistance)) { player->SendLootError(req.Object, lguid, lootAllowed ? LOOT_ERROR_TOO_FAR : LOOT_ERROR_DIDNT_KILL); continue; } loot = &creature->loot; } player->StoreLootItem(req.LootListID - 1, loot, aeResultPtr); // If player is removing the last LootItem, delete the empty container. if (loot->isLooted() && lguid.IsItem()) player->GetSession()->DoLootRelease(lguid); } if (aeResultPtr) { for (AELootResult::ResultValue const& resultValue : aeResult) { player->SendNewItem(resultValue.item, resultValue.count, false, false, true); player->UpdateCriteria(CRITERIA_TYPE_LOOT_ITEM, resultValue.item->GetEntry(), resultValue.count); player->UpdateCriteria(CRITERIA_TYPE_LOOT_TYPE, resultValue.item->GetEntry(), resultValue.count, resultValue.lootType); player->UpdateCriteria(CRITERIA_TYPE_LOOT_EPIC_ITEM, resultValue.item->GetEntry(), resultValue.count); } } }
void WorldSession::HandleMovementOpcodes(WorldPacket& recvData) { uint16 opcode = recvData.GetOpcode(); Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plrMover = mover->ToPlayer(); // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) { recvData.rfinish(); // prevent warnings spam return; } /* extract packet */ ObjectGuid guid; recvData >> guid.ReadAsPacked(); MovementInfo movementInfo; movementInfo.guid = guid; ReadMovementInfo(recvData, &movementInfo); recvData.rfinish(); // prevent warnings spam // prevent tampered movement data if (guid != mover->GetGUID()) return; if (!movementInfo.pos.IsPositionValid()) { recvData.rfinish(); // prevent warnings spam return; } /* handle special cases */ if (movementInfo.HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT)) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.transport.pos.GetPositionX() > 50 || movementInfo.transport.pos.GetPositionY() > 50 || movementInfo.transport.pos.GetPositionZ() > 50) { recvData.rfinish(); // prevent warnings spam return; } if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.transport.pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.transport.pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.transport.pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.transport.pos.GetOrientation())) { recvData.rfinish(); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plrMover) { if (!plrMover->GetTransport()) { if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) transport->AddPassenger(plrMover); } else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) { plrMover->GetTransport()->RemovePassenger(plrMover); if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) transport->AddPassenger(plrMover); else movementInfo.transport.Reset(); } } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.RemoveMovementFlag(MOVEMENTFLAG_ONTRANSPORT); } } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave { plrMover->GetTransport()->RemovePassenger(plrMover); movementInfo.transport.Reset(); } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight()) plrMover->HandleFall(movementInfo); if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } uint32 mstime = getMSTime(); /*----------------------*/ if (m_clientTimeDelay == 0) m_clientTimeDelay = mstime - movementInfo.time; /* process position-change */ WorldPacket data(opcode, recvData.size()); movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; movementInfo.guid = mover->GetGUID(); WriteMovementInfo(&data, &movementInfo); mover->SendMessageToSet(&data, _player); mover->m_movementInfo = movementInfo; // Some vehicles allow the passenger to turn by himself if (Vehicle* vehicle = mover->GetVehicle()) { if (VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(mover)) { if (seat->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING) { if (movementInfo.pos.GetOrientation() != mover->GetOrientation()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING); } } } return; } mover->UpdatePosition(movementInfo.pos); if (plrMover) // nothing is charmed, or player charmed { if (plrMover->IsSitState() && (movementInfo.flags & (MOVEMENTFLAG_MASK_MOVING | MOVEMENTFLAG_MASK_TURNING))) plrMover->SetStandState(UNIT_STAND_STATE_STAND); plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); if (movementInfo.pos.GetPositionZ() < plrMover->GetMap()->GetMinHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY())) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away /// @todo discard movement packets after the player is rooted if (plrMover->IsAlive()) { plrMover->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (!plrMover->IsAlive()) plrMover->KillPlayer(); } } } } }
void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) { uint16 opcode = recvPacket.GetOpcode(); Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plrMover = mover->ToPlayer(); // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) { recvPacket.rfinish(); // prevent warnings spam return; } /* extract packet */ MovementInfo movementInfo; GetPlayer()->ReadMovementInfo(recvPacket, &movementInfo); // prevent tampered movement data if (movementInfo.guid != mover->GetGUID()) { SF_LOG_ERROR("network", "HandleMovementOpcodes: guid error"); return; } if (!movementInfo.pos.IsPositionValid()) { SF_LOG_ERROR("network", "HandleMovementOpcodes: Invalid Position"); return; } /* handle special cases */ if (movementInfo.transport.guid) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.transport.pos.GetPositionX() > 50 || movementInfo.transport.pos.GetPositionY() > 50 || movementInfo.transport.pos.GetPositionZ() > 50) { recvPacket.rfinish(); // prevent warnings spam return; } if (!Skyfire::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.transport.pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.transport.pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.transport.pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.transport.pos.GetOrientation())) { recvPacket.rfinish(); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plrMover) { if (!plrMover->GetTransport()) { if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) { plrMover->m_transport = transport; transport->AddPassenger(plrMover); } } else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) { bool foundNewTransport = false; plrMover->m_transport->RemovePassenger(plrMover); if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) { foundNewTransport = true; plrMover->m_transport = transport; transport->AddPassenger(plrMover); } if (!foundNewTransport) { plrMover->m_transport = NULL; movementInfo.ResetTransport(); } } } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.transport.guid = 0; } } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; movementInfo.ResetTransport(); } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight()) plrMover->HandleFall(movementInfo); if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } uint32 mstime = getMSTime(); /*----------------------*/ if (m_clientTimeDelay == 0) m_clientTimeDelay = mstime - movementInfo.time; /* process position-change */ movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; movementInfo.guid = mover->GetGUID(); mover->m_movementInfo = movementInfo; /*----------------------*/ /* process position-change */ // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle() if (mover->GetVehicle()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); return; } mover->UpdatePosition(movementInfo.pos); WorldPacket data(SMSG_PLAYER_MOVE, recvPacket.size()); mover->WriteMovementInfo(data); mover->SendMessageToSet(&data, _player); if (plrMover) // nothing is charmed, or player charmed { plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); AreaTableEntry const* zone = GetAreaEntryByAreaID(plrMover->GetAreaId()); float depth = zone ? zone->MaxDepth : -500.0f; if (movementInfo.pos.GetPositionZ() < depth) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away /// @todo discard movement packets after the player is rooted if (plrMover->IsAlive()) { plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (!plrMover->IsAlive()) plrMover->KillPlayer(); } } } } }
void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data ) { DEBUG_LOG("WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); Player *player = GetPlayer(); ObjectGuid lguid = player->GetLootGUID(); Loot *loot; uint8 lootSlot; Item* pItem = NULL; recv_data >> lootSlot; switch( lguid.GetHigh()) { case HIGHGUID_GAMEOBJECT: { GameObject *go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGuid() != _player->GetObjectGuid() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))) { player->SendLootRelease(lguid); return; } loot = &go->loot; break; } case HIGHGUID_ITEM: { pItem = player->GetItemByGuid( lguid ); if (!pItem || !pItem->HasGeneratedLoot()) { player->SendLootRelease(lguid); return; } loot = &pItem->loot; break; } case HIGHGUID_CORPSE: { Corpse *bones = player->GetMap()->GetCorpse(lguid); if (!bones) { player->SendLootRelease(lguid); return; } loot = &bones->loot; break; } case HIGHGUID_UNIT: case HIGHGUID_VEHICLE: { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed); if( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) { player->SendLootRelease(lguid); return; } loot = &pCreature->loot; break; } default: { sLog.outError("%s is unsupported for looting.",lguid.GetString().c_str()); return; } } QuestItem *qitem = NULL; QuestItem *ffaitem = NULL; QuestItem *conditem = NULL; LootItem *item = loot->LootItemInSlot(lootSlot,player,&qitem,&ffaitem,&conditem); if(!item) { player->SendEquipError( EQUIP_ERR_ALREADY_LOOTED, NULL, NULL ); return; } // questitems use the blocked field for other purposes if (!qitem && item->is_blocked) { player->SendLootRelease(lguid); return; } if (pItem) pItem->SetLootState(ITEM_LOOT_CHANGED); ItemPosCountVec dest; InventoryResult msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count ); if ( msg == EQUIP_ERR_OK ) { AllowedLooterSet* looters = item->GetAllowedLooters(); Item * newitem = player->StoreNewItem( dest, item->itemid, true, item->randomPropertyId, (looters->size() > 1) ? looters : NULL); if (qitem) { qitem->is_looted = true; //freeforall is 1 if everyone's supposed to get the quest item. if (item->freeforall || loot->GetPlayerQuestItems().size() == 1) player->SendNotifyLootItemRemoved(lootSlot); else loot->NotifyQuestItemRemoved(qitem->index); } else { if (ffaitem) { //freeforall case, notify only one player of the removal ffaitem->is_looted=true; player->SendNotifyLootItemRemoved(lootSlot); } else { //not freeforall, notify everyone if(conditem) conditem->is_looted=true; loot->NotifyItemRemoved(lootSlot); } } //if only one person is supposed to loot the item, then set it to looted if (!item->freeforall) item->is_looted = true; --loot->unlootedCount; player->SendNewItem(newitem, uint32(item->count), false, false, true); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item->count); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count); } else player->SendEquipError( msg, NULL, NULL, item->itemid ); }
void PlayerbotMgr::HandleMasterIncomingPacket(const WorldPacket& packet) { switch (packet.GetOpcode()) { // if master is logging out, log out all bots case CMSG_LOGOUT_REQUEST: { LogoutAllBots(); return; } // If master inspects one of his bots, give the master useful info in chat window // such as inventory that can be equipped case CMSG_INSPECT: { WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid guid; p >> guid; Player* const bot = GetPlayerBot(guid); if (bot) bot->GetPlayerbotAI()->SendNotEquipList(*bot); return; } // handle emotes from the master //case CMSG_EMOTE: case CMSG_TEXT_EMOTE: { WorldPacket p(packet); p.rpos(0); // reset reader uint32 emoteNum; p >> emoteNum; /* std::ostringstream out; out << "emote is: " << emoteNum; ChatHandler ch(m_master); ch.SendSysMessage(out.str().c_str()); */ switch (emoteNum) { case TEXTEMOTE_BOW: { // Buff anyone who bows before me. Useful for players not in bot's group // How do I get correct target??? //Player* const pPlayer = GetPlayerBot(m_master->GetSelection()); //if (pPlayer->GetPlayerbotAI()->GetClassAI()) // pPlayer->GetPlayerbotAI()->GetClassAI()->BuffPlayer(pPlayer); return; } /* case TEXTEMOTE_BONK: { Player* const pPlayer = GetPlayerBot(m_master->GetSelection()); if (!pPlayer || !pPlayer->GetPlayerbotAI()) return; PlayerbotAI* const pBot = pPlayer->GetPlayerbotAI(); ChatHandler ch(m_master); { std::ostringstream out; out << "time(0): " << time(0) << " m_ignoreAIUpdatesUntilTime: " << pBot->m_ignoreAIUpdatesUntilTime; ch.SendSysMessage(out.str().c_str()); } { std::ostringstream out; out << "m_TimeDoneEating: " << pBot->m_TimeDoneEating << " m_TimeDoneDrinking: " << pBot->m_TimeDoneDrinking; ch.SendSysMessage(out.str().c_str()); } { std::ostringstream out; out << "m_CurrentlyCastingSpellId: " << pBot->m_CurrentlyCastingSpellId; ch.SendSysMessage(out.str().c_str()); } { std::ostringstream out; out << "IsBeingTeleported() " << pBot->GetPlayer()->IsBeingTeleported(); ch.SendSysMessage(out.str().c_str()); } { std::ostringstream out; bool tradeActive = (pBot->GetPlayer()->GetTrader()) ? true : false; out << "tradeActive: " << tradeActive; ch.SendSysMessage(out.str().c_str()); } { std::ostringstream out; out << "IsCharmed() " << pBot->getPlayer()->isCharmed(); ch.SendSysMessage(out.str().c_str()); } return; } */ case TEXTEMOTE_EAT: case TEXTEMOTE_DRINK: { for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; bot->GetPlayerbotAI()->Feast(); } return; } // emote to attack selected target case TEXTEMOTE_POINT: { ObjectGuid attackOnGuid = m_master->GetSelectionGuid(); if (attackOnGuid.IsEmpty()) return; Unit* thingToAttack = ObjectAccessor::GetUnit(*m_master, attackOnGuid); if (!thingToAttack) return; Player *bot = 0; for (PlayerBotMap::iterator itr = m_playerBots.begin(); itr != m_playerBots.end(); ++itr) { bot = itr->second; if (!bot->IsFriendlyTo(thingToAttack) && bot->IsWithinLOSInMap(thingToAttack)) bot->GetPlayerbotAI()->GetCombatTarget(thingToAttack); } return; } // emote to stay case TEXTEMOTE_STAND: { Player* const bot = GetPlayerBot(m_master->GetSelectionGuid()); if (bot) bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_STAY); else for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_STAY); } return; } // 324 is the followme emote (not defined in enum) // if master has bot selected then only bot follows, else all bots follow case 324: case TEXTEMOTE_WAVE: { Player* const bot = GetPlayerBot(m_master->GetSelectionGuid()); if (bot) bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_FOLLOW, m_master); else for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_FOLLOW, m_master); } return; } } return; } /* EMOTE ends here */ case CMSG_GAMEOBJ_USE: // not sure if we still need this one case CMSG_GAMEOBJ_REPORT_USE: { WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid objGUID; p >> objGUID; GameObject *obj = (m_master && m_master->GetMap()) ? m_master->GetMap()->GetGameObject(objGUID) : NULL; if (!obj) return; for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; if (obj->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) bot->GetPlayerbotAI()->TurnInQuests(obj); // add other go types here, i.e.: // GAMEOBJECT_TYPE_CHEST - loot quest items of chest } } break; // if master talks to an NPC case CMSG_GOSSIP_HELLO: case CMSG_QUESTGIVER_HELLO: { WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid npcGUID; p >> npcGUID; WorldObject* pNpc = (m_master && m_master->GetMap()) ? m_master->GetMap()->GetWorldObject(npcGUID) : NULL; if (!pNpc) return; // for all master's bots for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; bot->GetPlayerbotAI()->TurnInQuests(pNpc); } return; } // if master accepts a quest, bots should also try to accept quest case CMSG_QUESTGIVER_ACCEPT_QUEST: { WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid guid; uint32 quest; p >> guid >> quest; Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest); if (qInfo) for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; if (bot->GetQuestStatus(quest) == QUEST_STATUS_COMPLETE) bot->GetPlayerbotAI()->TellMaster("I already completed that quest."); else if (!bot->CanTakeQuest(qInfo, false)) { if (!bot->SatisfyQuestStatus(qInfo, false)) bot->GetPlayerbotAI()->TellMaster("I already have that quest."); else bot->GetPlayerbotAI()->TellMaster("I can't take that quest."); } else if (!bot->SatisfyQuestLog(false)) bot->GetPlayerbotAI()->TellMaster("My quest log is full."); else if (!bot->CanAddQuest(qInfo, false)) bot->GetPlayerbotAI()->TellMaster("I can't take that quest because it requires that I take items, but my bags are full!"); else { p.rpos(0); // reset reader bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(p); bot->GetPlayerbotAI()->TellMaster("Got the quest."); } } return; } case CMSG_LOOT_ROLL: { WorldPacket p(packet); //WorldPacket packet for CMSG_LOOT_ROLL, (8+4+1) ObjectGuid Guid; uint32 NumberOfPlayers; uint8 rollType; p.rpos(0); //reset packet pointer p >> Guid; //guid of the item rolled p >> NumberOfPlayers; //number of players invited to roll p >> rollType; //need,greed or pass on roll for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { uint32 choice = urand(0, 3); //returns 0,1,2 or 3 Player* const bot = it->second; if (!bot) return; Group* group = bot->GetGroup(); if (!group) return; group->CountRollVote(bot, Guid, NumberOfPlayers, RollVote(choice)); switch (choice) { case ROLL_NEED: bot->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED, 1); break; case ROLL_GREED: bot->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED, 1); break; } } return; } case CMSG_REPAIR_ITEM: { WorldPacket p(packet); // WorldPacket packet for CMSG_REPAIR_ITEM, (8+8+1) sLog.outDebug("PlayerbotMgr: CMSG_REPAIR_ITEM"); ObjectGuid npcGUID; uint64 itemGUID; uint8 guildBank; p.rpos(0); //reset packet pointer p >> npcGUID; p >> itemGUID; // Not used for bot but necessary opcode data retrieval p >> guildBank; // Flagged if guild repair selected for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; if (!bot) return; Group* group = bot->GetGroup(); // check if bot is a member of group if (!group) return; Creature *unit = bot->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_REPAIR); if (!unit) // Check if NPC can repair bot or not { sLog.outDebug("PlayerbotMgr: HandleRepairItemOpcode - Unit (GUID: %s) not found or you can't interact with him.", npcGUID.GetString().c_str()); return; } // remove fake death if (bot->hasUnitState(UNIT_STAT_DIED)) bot->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); // reputation discount float discountMod = bot->GetReputationPriceDiscount(unit); uint32 TotalCost = 0; if (itemGUID) // Handle redundant feature (repair individual item) for bot { sLog.outDebug("ITEM: Repair single item is not applicable for %s", bot->GetName()); continue; } else // Handle feature (repair all items) for bot { sLog.outDebug("ITEM: Repair all items, npcGUID = %s", npcGUID.GetString().c_str()); TotalCost = bot->DurabilityRepairAll(true, discountMod, guildBank > 0 ? true : false); } if (guildBank) // Handle guild repair { uint32 GuildId = bot->GetGuildId(); if (!GuildId) return; Guild *pGuild = sGuildMgr.GetGuildById(GuildId); if (!pGuild) return; pGuild->LogBankEvent(GUILD_BANK_LOG_REPAIR_MONEY, 0, bot->GetGUIDLow(), TotalCost); pGuild->SendMoneyInfo(bot->GetSession(), bot->GetGUIDLow()); } } return; } case CMSG_SPIRIT_HEALER_ACTIVATE: { // sLog.outDebug("SpiritHealer is resurrecting the Player %s",m_master->GetName()); for (PlayerBotMap::iterator itr = m_playerBots.begin(); itr != m_playerBots.end(); ++itr) { Player* const bot = itr->second; Group *grp = bot->GetGroup(); if (grp) grp->RemoveMember(bot->GetObjectGuid(), 1); } return; } /* case CMSG_NAME_QUERY: case MSG_MOVE_START_FORWARD: case MSG_MOVE_STOP: case MSG_MOVE_SET_FACING: case MSG_MOVE_START_STRAFE_LEFT: case MSG_MOVE_START_STRAFE_RIGHT: case MSG_MOVE_STOP_STRAFE: case MSG_MOVE_START_BACKWARD: case MSG_MOVE_HEARTBEAT: case CMSG_STANDSTATECHANGE: case CMSG_QUERY_TIME: case CMSG_CREATURE_QUERY: case CMSG_GAMEOBJECT_QUERY: case MSG_MOVE_JUMP: case MSG_MOVE_FALL_LAND: return; default: { const char* oc = LookupOpcodeName(packet.GetOpcode()); // ChatHandler ch(m_master); // ch.SendSysMessage(oc); std::ostringstream out; out << "masterin: " << oc; sLog.outError(out.str().c_str()); } */ } }
void WorldSession::DoLootRelease(uint64 lguid) { Player *player = GetPlayer(); Loot *loot; player->SetLootGUID(0); player->SendLootRelease(lguid); player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING); if (!player->IsInWorld()) return; if (IS_GAMEOBJECT_GUID(lguid)) { GameObject *go = GetPlayer()->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))) return; loot = &go->loot; if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR) { // locked doors are opened with spelleffect openlock, prevent remove its as looted go->UseDoorOrButton(); } else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE) { // GO is mineral vein? so it is not removed after its looted if (go->GetGoType() == GAMEOBJECT_TYPE_CHEST) { uint32 go_min = go->GetGOInfo()->chest.minSuccessOpens; uint32 go_max = go->GetGOInfo()->chest.maxSuccessOpens; // only vein pass this check if (go_min != 0 && go_max > go_min) { float amount_rate = sWorld.getRate(RATE_MINING_AMOUNT); float min_amount = go_min*amount_rate; float max_amount = go_max*amount_rate; go->AddUse(); float uses = float(go->GetUseCount()); if (uses < max_amount) { if (uses >= min_amount) { float chance_rate = sWorld.getRate(RATE_MINING_NEXT); int32 ReqValue = 175; LockEntry const *lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId); if (lockInfo) ReqValue = lockInfo->requiredminingskill; float skill = float(player->GetSkillValue(SKILL_MINING))/(ReqValue+25); double chance = pow(0.8*chance_rate,4*(1/double(max_amount))*double(uses)); if (roll_chance_f(100*chance+skill)) { go->SetLootState(GO_READY); } else // not have more uses go->SetLootState(GO_JUST_DEACTIVATED); } else // 100% chance until min uses go->SetLootState(GO_READY); } else // max uses already go->SetLootState(GO_JUST_DEACTIVATED); } else // not vein go->SetLootState(GO_JUST_DEACTIVATED); } else if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE) { // The fishing hole used once more go->AddUse(); // if the max usage is reached, will be despawned in next tick if (go->GetUseCount() >= irand(go->GetGOInfo()->fishinghole.minSuccessOpens,go->GetGOInfo()->fishinghole.maxSuccessOpens)) { go->SetLootState(GO_JUST_DEACTIVATED); } else go->SetLootState(GO_READY); } else // not chest (or vein/herb/etc) go->SetLootState(GO_JUST_DEACTIVATED); loot->clear(); } else // not fully looted object go->SetLootState(GO_ACTIVATED); } else if (IS_CORPSE_GUID(lguid)) // ONLY remove insignia at BG { Corpse *corpse = ObjectAccessor::GetCorpse(*player, lguid); if (!corpse || !corpse->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) return; loot = &corpse->loot; if (loot->isLooted()) { loot->clear(); corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE); } } else if (IS_ITEM_GUID(lguid)) { Item *pItem = player->GetItemByGuid(lguid); if (!pItem) return; if ((pItem->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP) && pItem->GetProto()->Class == ITEM_CLASS_TRADE_GOODS && pItem->GetCount() >= 5) { pItem->m_lootGenerated = false; pItem->loot.clear(); uint32 count = 5; player->DestroyItemCount(pItem, count, true); } else // FIXME: item must not be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or cheating possible. player->DestroyItem(pItem->GetBagSlot(),pItem->GetSlot(), true); return; // item can be looted only single player } else { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass() == CLASS_ROGUE && pCreature->lootForPickPocketed); if (!ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) return; loot = &pCreature->loot; // update next looter if (Player *recipient = pCreature->GetLootRecipient()) if (Group* group = recipient->GetGroup()) if (group->GetLooterGuid() == player->GetGUID()) group->UpdateLooterGuid(pCreature); if (loot->isLooted()) { // skip pickpocketing loot for speed, skinning timer redunction is no-op in fact if (!pCreature->isAlive()) pCreature->AllLootRemovedFromCorpse(); pCreature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); loot->clear(); } } //Player is not looking at loot list, he doesn't need to see updates on the loot list loot->RemoveLooter(player->GetGUID()); }
void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); Player* player = GetPlayer(); uint64 guid = player->GetLootGUID(); Loot* loot = NULL; uint8 count = 0; if (IS_GAMEOBJECT_GUID(guid)) { GameObject* go = player->GetMap()->GetGameObject(guid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) { player->SendLootRelease(guid); return; } loot = &go->loot; } else if (IS_ITEM_GUID(guid)) { Item* pItem = player->GetItemByGuid(guid); if (!pItem) { player->SendLootRelease(guid); return; } loot = &pItem->loot; } else if (IS_CORPSE_GUID(guid)) { Corpse* bones = ObjectAccessor::GetCorpse(*player, guid); if (!bones) { player->SendLootRelease(guid); return; } loot = &bones->loot; } else { Creature* creature = GetPlayer()->GetMap()->GetCreature(guid); bool lootAllowed = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) { player->SendLootRelease(guid); return; } loot = &creature->loot; } uint32 Count = recvData.ReadBits(23); std::map<uint32, ObjectGuid> Guids; for (uint32 i = 0 ; i < Count ; ++i) { (Guids[i])[2] = recvData.ReadBit(); (Guids[i])[7] = recvData.ReadBit(); (Guids[i])[0] = recvData.ReadBit(); (Guids[i])[6] = recvData.ReadBit(); (Guids[i])[5] = recvData.ReadBit(); (Guids[i])[3] = recvData.ReadBit(); (Guids[i])[1] = recvData.ReadBit(); (Guids[i])[4] = recvData.ReadBit(); } for (uint32 i = 0 ; i < Count ; ++i) { recvData.ReadByteSeq((Guids[i])[0]); recvData.ReadByteSeq((Guids[i])[4]); recvData.ReadByteSeq((Guids[i])[1]); recvData.ReadByteSeq((Guids[i])[7]); recvData.ReadByteSeq((Guids[i])[6]); recvData.ReadByteSeq((Guids[i])[5]); recvData.ReadByteSeq((Guids[i])[3]); recvData.ReadByteSeq((Guids[i])[2]); uint8 lootSlot; recvData >> lootSlot; player->StoreLootItem(lootSlot, loot, guid); } // If player is removing the last LootItem, delete the empty container. if (loot->isLooted() && IS_ITEM_GUID(guid)) player->GetSession()->DoLootRelease(guid); }
void WorldSession::DoLootRelease(uint64 lguid) { Player *player = GetPlayer(); Loot *loot; player->SetLootGUID(0); player->SendLootRelease(lguid); player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING); if (!player->IsInWorld()) return; if (IS_GAMEOBJECT_GUID(lguid)) { GameObject* go = GetPlayer()->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) return; loot = &go->loot; if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR) { // locked doors are opened with spelleffect openlock, prevent remove its as looted go->UseDoorOrButton(); } else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE) { // GO is mineral vein? so it is not removed after its looted if (go->GetGoType() == GAMEOBJECT_TYPE_CHEST) { uint32 go_min = go->GetGOInfo()->chest.minSuccessOpens; uint32 go_max = go->GetGOInfo()->chest.maxSuccessOpens; // only vein pass this check if (go_min != 0 && go_max > go_min) { float amount_rate = sWorld->getRate(RATE_MINING_AMOUNT); float min_amount = go_min*amount_rate; float max_amount = go_max*amount_rate; go->AddUse(); float uses = float(go->GetUseCount()); if (uses < max_amount) { if (uses >= min_amount) { float chance_rate = sWorld->getRate(RATE_MINING_NEXT); int32 ReqValue = 175; LockEntry const* lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId); if (lockInfo) ReqValue = lockInfo->Skill[0]; float skill = float(player->GetSkillValue(SKILL_MINING))/(ReqValue+25); double chance = pow(0.8*chance_rate, 4*(1/double(max_amount))*double(uses)); if (roll_chance_f((float)(100*chance+skill))) { go->SetLootState(GO_READY); } else // not have more uses go->SetLootState(GO_JUST_DEACTIVATED); } else // 100% chance until min uses go->SetLootState(GO_READY); } else // max uses already go->SetLootState(GO_JUST_DEACTIVATED); } else // not vein go->SetLootState(GO_JUST_DEACTIVATED); } else if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE) { // The fishing hole used once more go->AddUse(); // if the max usage is reached, will be despawned in next tick if (go->GetUseCount() >= urand(go->GetGOInfo()->fishinghole.minSuccessOpens, go->GetGOInfo()->fishinghole.maxSuccessOpens)) { go->SetLootState(GO_JUST_DEACTIVATED); } else go->SetLootState(GO_READY); } else // not chest (or vein/herb/etc) go->SetLootState(GO_JUST_DEACTIVATED); loot->clear(); } else { // not fully looted object go->SetLootState(GO_ACTIVATED, player); // if the round robin player release, reset it. if (player->GetGUID() == loot->roundRobinPlayer) { if (Group* group = player->GetGroup()) { if (group->GetLootMethod() != MASTER_LOOT) { loot->roundRobinPlayer = 0; } } else loot->roundRobinPlayer = 0; } } } else if (IS_CORPSE_GUID(lguid)) // ONLY remove insignia at BG { Corpse* corpse = ObjectAccessor::GetCorpse(*player, lguid); if (!corpse || !corpse->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) return; loot = &corpse->loot; if (loot->isLooted()) { loot->clear(); corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE); } } else if (IS_ITEM_GUID(lguid)) { Item* pItem = player->GetItemByGuid(lguid); if (!pItem) return; ItemTemplate const* proto = pItem->GetTemplate(); // destroy only 5 items from stack in case prospecting and milling if (proto->Flags & (ITEM_PROTO_FLAG_PROSPECTABLE | ITEM_PROTO_FLAG_MILLABLE)) { pItem->m_lootGenerated = false; pItem->loot.clear(); uint32 count = pItem->GetCount(); // >=5 checked in spell code, but will work for cheating cases also with removing from another stacks. if (count > 5) count = 5; player->DestroyItemCount(pItem, count, true); } else // FIXME: item must not be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or cheating possible. player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); return; // item can be looted only single player } else { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); bool lootAllowed = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) return; loot = &creature->loot; if (loot->isLooted()) { // skip pickpocketing loot for speed, skinning timer reduction is no-op in fact if (!creature->isAlive()) creature->AllLootRemovedFromCorpse(); creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); loot->clear(); } else { // if the round robin player release, reset it. if (player->GetGUID() == loot->roundRobinPlayer) { if (Group* group = player->GetGroup()) { if (group->GetLootMethod() != MASTER_LOOT) { loot->roundRobinPlayer = 0; group->SendLooter(creature, NULL); // force update of dynamic flags, otherwise other group's players still not able to loot. creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS); } } else loot->roundRobinPlayer = 0; } } } //Player is not looking at loot list, he doesn't need to see updates on the loot list loot->RemoveLooter(player->GetGUID()); }
void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket & recv_data) { sLog->outDebug("WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); Player* player = GetPlayer(); uint64 lguid = player->GetLootGUID(); Loot* loot = NULL; uint8 lootSlot = 0; recv_data >> lootSlot; if (IS_GAMEOBJECT_GUID(lguid)) { GameObject *go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))) { player->SendLootRelease(lguid); return; } loot = &go->loot; } else if (IS_ITEM_GUID(lguid)) { Item *pItem = player->GetItemByGuid(lguid); if (!pItem) { player->SendLootRelease(lguid); return; } loot = &pItem->loot; } else if (IS_CORPSE_GUID(lguid)) { Corpse *bones = ObjectAccessor::GetCorpse(*player, lguid); if (!bones) { player->SendLootRelease(lguid); return; } loot = &bones->loot; } else { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass() == CLASS_ROGUE && pCreature->lootForPickPocketed); if (!ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) { player->SendLootRelease(lguid); return; } loot = &pCreature->loot; } player->StoreLootItem(lootSlot, loot); }
void WorldSession::DoLootRelease(ObjectGuid lguid) { Player* player = GetPlayer(); Loot* loot; player->SetLootGuid(ObjectGuid()); player->SendLootRelease(lguid); player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING); if (!player->IsInWorld()) { return; } switch (lguid.GetHigh()) { case HIGHGUID_GAMEOBJECT: { GameObject* go = GetPlayer()->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGuid() != _player->GetObjectGuid() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) { return; } loot = &go->loot; if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR) { // locked doors are opened with spelleffect openlock, prevent remove its as looted go->UseDoorOrButton(); } else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE) { // GO is mineral vein? so it is not removed after its looted if (go->GetGoType() == GAMEOBJECT_TYPE_CHEST) { uint32 go_min = go->GetGOInfo()->chest.minSuccessOpens; uint32 go_max = go->GetGOInfo()->chest.maxSuccessOpens; // only vein pass this check if (go_min != 0 && go_max > go_min) { float amount_rate = sWorld.getConfig(CONFIG_FLOAT_RATE_MINING_AMOUNT); float min_amount = go_min * amount_rate; float max_amount = go_max * amount_rate; go->AddUse(); float uses = float(go->GetUseCount()); if (uses < max_amount) { if (uses >= min_amount) { float chance_rate = sWorld.getConfig(CONFIG_FLOAT_RATE_MINING_NEXT); int32 ReqValue = 175; LockEntry const* lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId); if (lockInfo) { ReqValue = lockInfo->Skill[0]; } float skill = float(player->GetSkillValue(SKILL_MINING)) / (ReqValue + 25); double chance = pow(0.8 * chance_rate, 4 * (1 / double(max_amount)) * double(uses)); if (roll_chance_f(float(100.0f * chance + skill))) { go->SetLootState(GO_READY); } else // not have more uses { go->SetLootState(GO_JUST_DEACTIVATED); } } else // 100% chance until min uses { go->SetLootState(GO_READY); } } else // max uses already { go->SetLootState(GO_JUST_DEACTIVATED); } } else // not vein { go->SetLootState(GO_JUST_DEACTIVATED); } } else if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE) { // The fishing hole used once more go->AddUse(); // if the max usage is reached, will be despawned at next tick if (go->GetUseCount() >= urand(go->GetGOInfo()->fishinghole.minSuccessOpens, go->GetGOInfo()->fishinghole.maxSuccessOpens)) { go->SetLootState(GO_JUST_DEACTIVATED); } else { go->SetLootState(GO_READY); } } else // not chest (or vein/herb/etc) { go->SetLootState(GO_JUST_DEACTIVATED); } loot->clear(); } else // not fully looted object { go->SetLootState(GO_ACTIVATED); } go->SetGoState(GO_STATE_READY); break; } /* Only used for removing insignia in battlegrounds */ case HIGHGUID_CORPSE: { /* Get pointer to corpse */ Corpse* corpse = _player->GetMap()->GetCorpse(lguid); /* If corpse is invalid or not in a valid position, dont allow looting */ if (!corpse || !corpse->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) { return; } loot = &corpse->loot; if (loot->isLooted()) { loot->clear(); corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE); } break; } case HIGHGUID_ITEM: { Item* pItem = player->GetItemByGuid(lguid); if (!pItem) { return; } switch (pItem->loot.loot_type) { // temporary loot in stacking items, clear loot state, no auto loot move case LOOT_PROSPECTING: { uint32 count = pItem->GetCount(); // >=5 checked in spell code, but will work for cheating cases also with removing from another stacks. if (count > 5) count = 5; // reset loot for allow repeat looting if stack > 5 pItem->loot.clear(); pItem->SetLootState(ITEM_LOOT_REMOVED); player->DestroyItemCount(pItem, count, true); break; } // temporary loot, auto loot move case LOOT_DISENCHANTING: { if (!pItem->loot.isLooted()) { player->AutoStoreLoot(pItem->loot); } // can be lost if no space pItem->loot.clear(); pItem->SetLootState(ITEM_LOOT_REMOVED); player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); break; } // normal persistence loot default: { // must be destroyed only if no loot if (pItem->loot.isLooted()) { pItem->SetLootState(ITEM_LOOT_REMOVED); player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); } break; } } return; // item can be looted only single player } case HIGHGUID_UNIT: { /* Get creature pointer */ Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = (pCreature && // The creature exists (we dont have a null pointer) pCreature->IsAlive() == // Creature is alive and we're a rogue and creature can be pickpocketed (player->getClass() == CLASS_ROGUE && pCreature->lootForPickPocketed)); if (!ok_loot || !pCreature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) { return; } /* Copy creature loot to loot variable */ loot = &pCreature->loot; /* Update for other players. */ if(!loot->isLooted()) { Group const* group = pCreature->GetGroupLootRecipient(); if (group && !pCreature->hasBeenLootedOnce) { // Checking whether it has been looted once by the designed looter (master loot case). switch (group->GetLootMethod()) { case FREE_FOR_ALL: case NEED_BEFORE_GREED: case ROUND_ROBIN: case GROUP_LOOT: { pCreature->hasBeenLootedOnce = true; break; } case MASTER_LOOT: { pCreature->hasBeenLootedOnce = (group->GetLooterGuid() == player->GetObjectGuid()); break; } } pCreature->MarkFlagUpdateForClient(UNIT_DYNAMIC_FLAGS); } } /* We've completely looted the creature, mark it as available for skinning */ if (loot->isLooted() && !pCreature->IsAlive()) { /* Update Creature: for example skinning after normal loot */ pCreature->PrepareBodyLootState(); pCreature->AllLootRemovedFromCorpse(); } break; } default: { sLog.outError("%s is unsupported for looting.", lguid.GetString().c_str()); return; } } // Player is not looking at loot list, he doesn't need to see updates on the loot list loot->RemoveLooter(player->GetObjectGuid()); }
bool WorldSession::HandleMovementInfo(MovementInfo &movementInfo, const uint16 opcode, const size_t packSize, Unit *mover) { ASSERT(mover != NULL); if (movementInfo.guid != mover->GetGUID() || !movementInfo.pos.IsPositionValid()) return false; Player* plrMover = mover->ToPlayer(); if (plrMover && plrMover->IsBeingTeleported()) return false; if (plrMover && plrMover->HasUnitState(UNIT_STATE_CONTROLLED_BY_SERVER) && !CanMovementBeProcessed(opcode)) return false; movementInfo.Sanitize(mover); if (movementInfo.t_guid) { if (movementInfo.t_pos.GetPositionX() > 50.0f || movementInfo.t_pos.GetPositionY() > 50.0f || movementInfo.t_pos.GetPositionZ() > 50.0f) return false; // moved out of transport size if (!Trinity::IsValidMapCoord( movementInfo.pos.GetPositionX() + movementInfo.t_pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.t_pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.t_pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.t_pos.GetOrientation()) ) return false; // just bad coords if (plrMover) { if (!plrMover->GetTransport()) // didn't have transport. now have { for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } } else if (plrMover->GetTransport()->GetGUID() != movementInfo.t_guid) // changes transport { bool foundNewTransport = false; plrMover->m_transport->RemovePassenger(plrMover); for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { foundNewTransport = true; plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } if (!foundNewTransport) { plrMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; } } else if (movementInfo.pos.m_positionX != movementInfo.t_pos.m_positionX) plrMover->GetTransport()->UpdatePosition(&movementInfo); } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.t_guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.t_guid = 0; } } else if (plrMover && plrMover->GetTransport()) // had transport, no loger have { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; } if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->isInFlight()) plrMover->HandleFall(movementInfo); if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } if (plrMover) sAnticheatMgr->StartHackDetection(plrMover, movementInfo, opcode); WorldPacket data(movementInfo.GetSMSGOpcodeForCMSG(Opcodes(opcode)), packSize); movementInfo.time = getMSTime(); movementInfo.guid = mover->GetGUID(); movementInfo.WriteToPacket(data); _player->SendMessageToSet(&data, _player); mover->m_movementInfo = movementInfo; if (mover->GetVehicle()) // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle() { mover->SetOrientation(movementInfo.pos.GetOrientation()); return true; } // Remove pet when starting to fly if (plrMover && plrMover->IsFlying()) plrMover->UnsummonPetTemporaryIfAny(); mover->UpdatePosition(movementInfo.pos); if (plrMover) { plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); float underMapValueZ, upperLimitValueZ; bool check = false; switch (plrMover->GetMapId()) { case 617: // Dalaran Arena underMapValueZ = 3.0f; upperLimitValueZ = 30.0f; break; case 562: // Blades Edge Arena underMapValueZ = -1.0f; upperLimitValueZ = 22.0f; break; case 559: // Nagrand Arena underMapValueZ = -1.0f; upperLimitValueZ = 21.0f; break; case 572: // Ruins of Lordaeron underMapValueZ = -1.0f; upperLimitValueZ = 45.0f; break; case 618: // Ring of Valor underMapValueZ = 28.0f; upperLimitValueZ = 60.0f; break; case 566: // Eye of the storm underMapValueZ = 1000.0f; upperLimitValueZ = MAX_HEIGHT; break; default: AreaTableEntry const* zone = GetAreaEntryByAreaID(plrMover->GetAreaId()); underMapValueZ = zone ? zone->MaxDepth : -500.0f; upperLimitValueZ = MAX_HEIGHT; break; } check = movementInfo.pos.GetPositionZ() < underMapValueZ || movementInfo.pos.GetPositionZ() > upperLimitValueZ; if (check && plrMover->isAlive() && !(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); if (!plrMover->isAlive()) plrMover->KillPlayer(); // prevent death timer plrMover->RepopAtGraveyard(); } } return true; }
bool TerrainInfo::CheckPathAccurate(float srcX, float srcY, float srcZ, float& dstX, float& dstY, float& dstZ, Unit* mover, bool onlyLOS ) const { float tstX = dstX; float tstY = dstY; float tstZ = dstZ; // check by standart way. may be not need path checking? if (!mover && CheckPath(srcX, srcY, srcZ, tstX, tstY, tstZ) && IsNextZcoordOK(tstX, tstY, tstZ, 5.0f)) { DEBUG_LOG("TerrainInfo::CheckPathAccurate vmaps hit! delta is %f %f %f",dstX - tstX,dstY - tstY,dstZ - tstZ); dstX = tstX; dstY = tstY; dstZ = tstZ + 0.1f; return true; } const float distance = sqrt((dstY - srcY)*(dstY - srcY) + (dstX - srcX)*(dstX - srcX)); const float DELTA = 0.5f; const uint8 numChecks = ceil(fabs(distance/DELTA)); const float DELTA_X = (dstX-srcX)/numChecks; const float DELTA_Y = (dstY-srcY)/numChecks; const float DELTA_Z = (dstZ-srcZ)/numChecks; float lastGoodX = srcX; float lastGoodY = srcY; float lastGoodZ = srcZ; uint32 errorsCount = 0; uint32 goodCount = 0; uint32 vmaperrorsCount = 0; std::set<GameObject*> inLOSGOList; bool bGOCheck = false; if (mover) { std::list<GameObject*> tempTargetGOList; MaNGOS::GameObjectInRangeCheck check(mover, tstX, tstY, tstZ, distance + 2.0f *mover->GetObjectBoundingRadius()); MaNGOS::GameObjectListSearcher<MaNGOS::GameObjectInRangeCheck> searcher(tempTargetGOList, check); Cell::VisitAllObjects(mover, searcher, 2*mover->GetObjectBoundingRadius()); if (!tempTargetGOList.empty()) { for(std::list<GameObject*>::iterator iter = tempTargetGOList.begin(); iter != tempTargetGOList.end(); ++iter) { GameObject* pGo = *iter; if (!pGo || !pGo->IsInWorld()) continue; // Not require check GO's, if his not in path // first fast check if (pGo->GetPositionX() > std::max(srcX, dstX) || pGo->GetPositionX() < std::min(srcX, dstX) || pGo->GetPositionY() > std::max(srcY, dstY) || pGo->GetPositionY() < std::min(srcY, dstY)) continue; // don't check very small and very large objects if (pGo->GetDeterminativeSize(true) < mover->GetObjectBoundingRadius() * 0.5f || pGo->GetDeterminativeSize(false) > mover->GetObjectBoundingRadius() * 100.0f) continue; // second check by angle float angle = mover->GetAngle(pGo) - mover->GetAngle(dstX, dstY); if (abs(sin(angle)) * pGo->GetExactDist2d(srcX, srcY) > pGo->GetObjectBoundingRadius() * 0.5f) continue; bool bLOSBreak = false; switch (pGo->GetGoType()) { case GAMEOBJECT_TYPE_TRAP: case GAMEOBJECT_TYPE_SPELL_FOCUS: case GAMEOBJECT_TYPE_MO_TRANSPORT: case GAMEOBJECT_TYPE_CAMERA: case GAMEOBJECT_TYPE_FISHINGNODE: case GAMEOBJECT_TYPE_SUMMONING_RITUAL: case GAMEOBJECT_TYPE_SPELLCASTER: case GAMEOBJECT_TYPE_FISHINGHOLE: case GAMEOBJECT_TYPE_CAPTURE_POINT: break; case GAMEOBJECT_TYPE_DOOR: if (pGo->isSpawned() && pGo->GetGoState() == GO_STATE_READY) bLOSBreak = true; break; case GAMEOBJECT_TYPE_TRANSPORT: if (pGo->isSpawned() && pGo->GetGoState() == GO_STATE_ACTIVE) bLOSBreak = true; break; case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: if (!pGo->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED)) bLOSBreak = true; break; default: if (pGo->isSpawned()) bLOSBreak = true; break; } if (bLOSBreak) inLOSGOList.insert(pGo); } } if (!inLOSGOList.empty()) bGOCheck = true; } //Going foward until max distance for (uint8 i = 1; i < numChecks; ++i) { float prevX = srcX + (float(i-1)*DELTA_X); float prevY = srcY + (float(i-1)*DELTA_Y); float prevZ = GetHeight(prevX, prevY, srcZ + 5.0f); tstX = srcX + (float(i)*DELTA_X); tstY = srcY + (float(i)*DELTA_Y); tstZ = GetHeight(tstX, tstY, srcZ + 5.0f); MaNGOS::NormalizeMapCoord(tstX); MaNGOS::NormalizeMapCoord(tstY); if (tstZ <= INVALID_HEIGHT) break; tstZ += (0.5f + DELTA_Z); if (!CheckPath(prevX, prevY, prevZ, tstX, tstY, tstZ)) { ++vmaperrorsCount; ++errorsCount; goodCount = 0; } else if (!IsNextZcoordOK(tstX, tstY, tstZ, 5.0f)) { ++errorsCount; goodCount = 0; } else if (mover && bGOCheck) { bool bError = false; for(std::set<GameObject*>::const_iterator iter = inLOSGOList.begin(); iter != inLOSGOList.end(); ++iter) { if (!(*iter) || (*iter)->GetDistance2d(tstX, tstY) > (*iter)->GetObjectBoundingRadius()) continue; // DEBUG_LOG("TerrainInfo::CheckPathAccurate GO %s in LOS found, %f %f %f ",(*iter)->GetObjectGuid().GetString().c_str(),tstX,tstY,tstZ); bError = true; break; } if (bError) { ++errorsCount; goodCount = 0; } else ++goodCount; } else { ++goodCount; } // DEBUG_LOG("TerrainInfo::CheckPathAccurate test data %f %f %f good=%u, errors=%u vmap=%u",tstX,tstY,tstZ, goodCount, errorsCount, vmaperrorsCount); if (!errorsCount) { lastGoodX = prevX; lastGoodY = prevY; lastGoodZ = prevZ; } else if (onlyLOS) break; if (errorsCount && goodCount > 10) { --errorsCount; goodCount -= 10; } } if (errorsCount) { dstX = lastGoodX; dstY = lastGoodY; dstZ = GetHeight(lastGoodX, lastGoodY, lastGoodZ+2.0f) + 0.5f; } else { dstX = tstX; dstY = tstY; dstZ = GetHeight(tstX, tstY, tstZ+2.0f) + 0.5f; } return (errorsCount == 0); }
void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket & recv_data) { bool isCurrency = recv_data.GetOpcode() == CMSG_AUTOSTORE_LOOT_CURRENCY; sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); Player* player = GetPlayer(); uint64 lguid = player->GetLootGUID(); Loot* loot = NULL; uint8 lootSlot = 0; uint32 objEntry = 0; uint32 objType = 0; recv_data >> lootSlot; if (IS_GAMEOBJECT_GUID(lguid)) { GameObject* go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) { player->SendLootRelease(lguid); return; } objEntry = go->GetGOInfo()->entry; objType = 3; loot = &go->loot; } else if (IS_ITEM_GUID(lguid)) { Item* pItem = player->GetItemByGuid(lguid); if (!pItem) { player->SendLootRelease(lguid); return; } objEntry = pItem->GetTemplate()->ItemId; objType = 2; loot = &pItem->loot; } else if (IS_CORPSE_GUID(lguid)) { Corpse* bones = ObjectAccessor::GetCorpse(*player, lguid); if (!bones) { player->SendLootRelease(lguid); return; } loot = &bones->loot; } else { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); if (!ok_loot || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) { player->SendLootRelease(lguid); return; } objEntry = creature->GetCreatureTemplate()->Entry; objType = 1; loot = &creature->loot; } if (isCurrency && loot) { uint8 currencys = 0; std::list<CurrencyLoot> temp = sObjectMgr->GetCurrencyLoot(objEntry, objType); for (std::list<CurrencyLoot>::iterator i = temp.begin(); i != temp.end(); ++i) { if (currencys == lootSlot) player->SetCurrency(i->CurrencyId, i->CurrencyAmount * 100); ++currencys; } } else if (loot) player->StoreLootItem(lootSlot, loot); }
void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData) { TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); Player* player = GetPlayer(); uint64 lguid = player->GetLootGUID(); Loot* loot = NULL; uint8 lootSlot = 0; recvData >> lootSlot; if (IS_GAMEOBJECT_GUID(lguid)) { GameObject* go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) { player->SendLootRelease(lguid); return; } loot = &go->loot; } else if (IS_ITEM_GUID(lguid)) { Item* pItem = player->GetItemByGuid(lguid); if (!pItem) { player->SendLootRelease(lguid); return; } loot = &pItem->loot; } else if (IS_CORPSE_GUID(lguid)) { Corpse* bones = ObjectAccessor::GetCorpse(*player, lguid); if (!bones) { player->SendLootRelease(lguid); return; } loot = &bones->loot; } else { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) { player->SendLootRelease(lguid); return; } loot = &creature->loot; } player->StoreLootItem(lootSlot, loot); // If player is removing the last LootItem, delete the empty container. if (loot->isLooted() && IS_ITEM_GUID(lguid)) player->GetSession()->DoLootRelease(lguid); }
void PlayerbotMgr::HandleMasterIncomingPacket(const WorldPacket& packet) { switch (packet.GetOpcode()) { // handle emotes from the master //case CMSG_EMOTE: case CMSG_TEXT_EMOTE: { WorldPacket p(packet); p.rpos(0); // reset reader uint32 emoteNum; p >> emoteNum; switch (emoteNum) { case TEXTEMOTE_BOW: { // Buff anyone who bows before me. Useful for players not in bot's group // How do I get correct target??? //Player* const pPlayer = GetPlayerBot(m_master->GetSelection()); //if (pPlayer->GetPlayerbotAI()->GetClassAI()) // pPlayer->GetPlayerbotAI()->GetClassAI()->BuffPlayer(pPlayer); return; } /* case TEXTEMOTE_BONK: { Player* const pPlayer = GetPlayerBot(m_master->GetSelection()); if (!pPlayer || !pPlayer->GetPlayerbotAI()) return; PlayerbotAI* const pBot = pPlayer->GetPlayerbotAI(); ChatHandler ch(m_master); { << " m_ignoreAIUpdatesUntilTime: " << pBot->m_ignoreAIUpdatesUntilTime; ch.SendSysMessage(out.str().c_str()); } { << " m_TimeDoneDrinking: " << pBot->m_TimeDoneDrinking; ch.SendSysMessage(out.str().c_str()); } { ch.SendSysMessage(out.str().c_str()); } { ch.SendSysMessage(out.str().c_str()); } { bool tradeActive = (pBot->GetPlayer()->GetTrader()) ? true : false; ch.SendSysMessage(out.str().c_str()); } { ch.SendSysMessage(out.str().c_str()); } return; } */ case TEXTEMOTE_EAT: case TEXTEMOTE_DRINK: { if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); bot->GetPlayerbotAI()->Feast(); } return; } // emote to attack selected target case TEXTEMOTE_POINT: { ObjectGuid attackOnGuid = m_master->GetSelectionGuid(); if (attackOnGuid.IsEmpty()) return; Unit* thingToAttack = ObjectAccessor::GetUnit(*m_master, attackOnGuid); if (!thingToAttack) return; Player *bot = 0; if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { bot = itr->getSource(); if (!bot->IsFriendlyTo(thingToAttack) && bot->IsWithinLOSInMap(thingToAttack)) bot->GetPlayerbotAI()->AttackStart(thingToAttack); } return; } // emote to stay case TEXTEMOTE_STAND: { Player* const bot = GetPlayerBot(m_master->GetSelectionGuid().GetRawValue()); if (bot) bot->GetPlayerbotAI()->SetFollowTarget(m_master); else { if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); bot->GetPlayerbotAI()->SetFollowTarget(m_master); } } return; } // 324 is the followme emote (not defined in enum) // if master has bot selected then only bot follows, else all bots follow case 324: case TEXTEMOTE_WAVE: { Player* const bot = GetPlayerBot(m_master->GetSelectionGuid().GetRawValue()); if (bot) bot->GetPlayerbotAI()->SetFollowTarget(m_master); else { if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); bot->GetPlayerbotAI()->SetFollowTarget(m_master); } } return; } } return; } /* EMOTE ends here */ case CMSG_GAMEOBJ_USE: // not sure if we still need this one case CMSG_GAMEOBJ_REPORT_USE: { WorldPacket p(packet); p.rpos(0); // reset reader uint64 objGUID; p >> objGUID; GameObject *obj = m_master->GetMap()->GetGameObject(objGUID); if (!obj) return; if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); if (obj->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) bot->GetPlayerbotAI()->TurnInQuests(obj); // add other go types here, i.e.: // GAMEOBJECT_TYPE_CHEST - loot quest items of chest } return; } break; // if master talks to an NPC case CMSG_GOSSIP_HELLO: case CMSG_QUESTGIVER_HELLO: { WorldPacket p(packet); p.rpos(0); // reset reader uint64 npcGUID; p >> npcGUID; WorldObject* pNpc = m_master->GetMap()->GetWorldObject(npcGUID); if (!pNpc) return; if (!m_master->GetGroup()) return; // for all master's bots for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); bot->GetPlayerbotAI()->TurnInQuests(pNpc); } return; } // if master accepts a quest, bots should also try to accept quest case CMSG_QUESTGIVER_ACCEPT_QUEST: { WorldPacket p(packet); p.rpos(0); // reset reader uint64 guid; uint32 quest; p >> guid >> quest; Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest); if (qInfo) { if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); if (bot->GetQuestStatus(quest) == QUEST_STATUS_COMPLETE) continue; if (!bot->CanTakeQuest(qInfo, false)) continue; if (!bot->SatisfyQuestLog(false)) continue; if (!bot->CanAddQuest(qInfo, false)) continue; p.rpos(0); // reset reader bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(p); } } return; } case CMSG_AREATRIGGER: { WorldPacket p(packet); for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); p.rpos(0); // reset reader bot->GetSession()->HandleAreaTriggerOpcode(p); } return; } case CMSG_QUESTGIVER_COMPLETE_QUEST: { WorldPacket p(packet); p.rpos(0); // reset reader uint32 quest; ObjectGuid npcGUID; p >> npcGUID >> quest; WorldObject* pNpc = m_master->GetMap()->GetWorldObject(npcGUID); if (!pNpc) return; // for all master's bots for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); bot->GetPlayerbotAI()->TurnInQuests(pNpc); } return; } case CMSG_LOOT_ROLL: { WorldPacket p(packet); //WorldPacket packet for CMSG_LOOT_ROLL, (8+4+1) uint64 Guid; uint32 NumberOfPlayers; uint8 rollType; p.rpos(0); //reset packet pointer p >> Guid; //guid of the item rolled p >> NumberOfPlayers; //number of players invited to roll p >> rollType; //need,greed or pass on roll if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { uint32 choice = urand(0, 3); //returns 0,1,2 or 3 Player* const bot = itr->getSource(); if(!bot) return; Group* group = bot->GetGroup(); if (!group) return; group->CountRollVote(bot, Guid, NumberOfPlayers, RollVote(choice)); switch (choice) { case ROLL_NEED: bot->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED, 1); break; case ROLL_GREED: bot->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED, 1); break; } } return; } case CMSG_REPAIR_ITEM: { WorldPacket p(packet); // WorldPacket packet for CMSG_REPAIR_ITEM, (8+8+1) ObjectGuid npcGUID; uint64 itemGUID; uint8 guildBank; p.rpos(0); //reset packet pointer p >> npcGUID; p >> itemGUID; // Not used for bot but necessary opcode data retrieval p >> guildBank; // Flagged if guild repair selected if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); if (!bot) return; Group* group = bot->GetGroup(); // check if bot is a member of group if (!group) return; Creature *unit = bot->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_REPAIR); if (!unit) // Check if NPC can repair bot or not return; // remove fake death if (bot->hasUnitState(UNIT_STAT_DIED)) bot->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); // reputation discount float discountMod = bot->GetReputationPriceDiscount(unit); uint32 TotalCost = 0; if (!itemGUID) // Handle redundant feature (repair individual item) for bot TotalCost = bot->DurabilityRepairAll(true, discountMod, guildBank > 0 ? true : false); if (guildBank) // Handle guild repair { uint32 GuildId = bot->GetGuildId(); if (!GuildId) return; Guild* pGuild = sGuildMgr.GetGuildById(GuildId); if (!pGuild) return; pGuild->LogBankEvent(GUILD_BANK_LOG_REPAIR_MONEY, 0, bot->GetGUIDLow(), TotalCost); pGuild->SendMoneyInfo(bot->GetSession(), bot->GetGUIDLow()); } } return; } case CMSG_SPIRIT_HEALER_ACTIVATE: { if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); Group *grp = bot->GetGroup(); if (grp) grp->RemoveMember(bot->GetObjectGuid(), 1); } return; } } }
void PlayerbotMgr::HandleMasterIncomingPacket(const WorldPacket& packet) { switch (packet.GetOpcode()) { case CMSG_LOGOUT_REQUEST: { PlayerbotMgr::RemoveAllBotsInGroup(m_master); return; } case CMSG_INSPECT: { WorldPacket p(packet); p.rpos(0); uint64 guid; p >> guid; Player* const bot = GetPlayerBot(guid); if (bot)bot->GetPlayerbotAI()->SendNotEquipList(*bot); return; } case CMSG_EMOTE: case CMSG_TEXT_EMOTE: { WorldPacket p(packet); p.rpos(0); uint32 emoteNum; p >> emoteNum; switch (emoteNum) { case TEXTEMOTE_EAT: case TEXTEMOTE_DRINK: { if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); bot->GetPlayerbotAI()->Feast(); } return; } case TEXTEMOTE_POINT: { uint64 attackOnGuid = m_master->GetSelection(); if (!attackOnGuid)return; Unit* thingToAttack = ObjectAccessor::GetUnit(*m_master, attackOnGuid); if (!thingToAttack)return; Player *bot = 0; if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { bot = itr->getSource(); if (!bot->IsFriendlyTo(thingToAttack) && bot->IsWithinLOSInMap(thingToAttack)) bot->GetPlayerbotAI()->GetCombatTarget(thingToAttack); } return; } case TEXTEMOTE_STAND: { Player* const bot = GetPlayerBot(m_master->GetSelection()); if (bot) bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_STAY); else { if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_STAY); } } return; } case 324: case TEXTEMOTE_WAVE: { Player* const bot = GetPlayerBot(m_master->GetSelection()); if (bot) bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_FOLLOW, m_master); else { if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_FOLLOW, m_master); } } return; } } return; } case CMSG_GAMEOBJ_USE: { WorldPacket p(packet); p.rpos(0); uint64 objGUID; p >> objGUID; GameObject *obj = m_master->GetMap()->GetGameObject(objGUID); if (!obj) return; if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); if (obj->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) bot->GetPlayerbotAI()->TurnInQuests(obj); } return; } case CMSG_GOSSIP_HELLO: case CMSG_QUESTGIVER_HELLO: { WorldPacket p(packet); p.rpos(0); uint64 npcGUID; p >> npcGUID; WorldObject* pNpc = m_master->GetMap()->GetWorldObject(npcGUID); if (!pNpc) return; if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); bot->GetPlayerbotAI()->TurnInQuests(pNpc); } return; } case CMSG_QUESTGIVER_ACCEPT_QUEST: { WorldPacket p(packet); p.rpos(0); uint64 guid; uint32 quest; p >> guid >> quest; Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest); if (qInfo) { if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); if (bot->GetQuestStatus(quest) == QUEST_STATUS_COMPLETE) bot->GetPlayerbotAI()->TellMaster("J'ai deja fini cette quete."); else if (!bot->CanTakeQuest(qInfo, false)) { if (!bot->SatisfyQuestStatus(qInfo, false)) bot->GetPlayerbotAI()->TellMaster("Je suis deja sur cette quete."); else bot->GetPlayerbotAI()->TellMaster("Je ne peux pas prendre cette quete."); } else if (!bot->SatisfyQuestLog(false)) bot->GetPlayerbotAI()->TellMaster("Mon journal de quete est plein."); else if (!bot->CanAddQuest(qInfo, false)) bot->GetPlayerbotAI()->TellMaster("Je ne peux pas prendre cette quete car je dois ramasser des objets pour la terminer et mes sacs sont pleins :("); else { p.rpos(0); bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(p); bot->GetPlayerbotAI()->TellMaster("J'ai pris la quete."); } } } return; } case CMSG_LOOT_ROLL: { WorldPacket p(packet); uint64 Guid; uint32 NumberOfPlayers; uint8 rollType; p.rpos(0); p >> Guid; p >> NumberOfPlayers; p >> rollType; if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { uint32 choice = urand(0,3); Player* const bot = itr->getSource(); if(!bot) return; Group* group = bot->GetGroup(); if(!group) return; group->CountRollVote(bot, Guid, NumberOfPlayers, RollVote(choice)); switch (choice) { case ROLL_NEED: bot->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED, 1); break; case ROLL_GREED: bot->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED, 1); break; } } return; } case CMSG_REPAIR_ITEM: { WorldPacket p(packet); uint64 npcGUID, itemGUID; uint8 guildBank; p.rpos(0); p >> npcGUID; p >> itemGUID; p >> guildBank; if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); if (!bot) return; Group* group = bot->GetGroup(); if (!group) return; Creature *unit = bot->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_REPAIR); if (!unit) return; if (bot->hasUnitState(UNIT_STAT_DIED)) bot->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); float discountMod = bot->GetReputationPriceDiscount(unit); uint32 TotalCost = 0; if (itemGUID) continue; else TotalCost = bot->DurabilityRepairAll(true, discountMod, guildBank > 0 ?true:false); if (guildBank) { uint32 GuildId = bot->GetGuildId(); if (!GuildId) return; Guild *pGuild = sObjectMgr.GetGuildById(GuildId); if (!pGuild) return; pGuild->LogBankEvent(GUILD_BANK_LOG_REPAIR_MONEY, 0, bot->GetGUIDLow(), TotalCost); pGuild->SendMoneyInfo(bot->GetSession(), bot->GetGUIDLow()); } } return; } case CMSG_SPIRIT_HEALER_ACTIVATE: { if (!m_master->GetGroup()) return; for (GroupReference *itr = m_master->GetGroup()->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* const bot = itr->getSource(); Group *grp = bot->GetGroup(); if (grp) grp->RemoveMember(bot->GetGUID(), 1); } return; } } }
void WorldSession::DoLootRelease(ObjectGuid lguid) { Player *player = GetPlayer(); Loot *loot; player->SetLootGUID(ObjectGuid()); player->SendLootRelease(lguid); player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING); if(!player->IsInWorld()) return; switch(lguid.GetHigh()) { case HIGHGUID_GAMEOBJECT: { GameObject *go = GetPlayer()->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO if (!go || ((go->GetOwnerGuid() != _player->GetObjectGuid() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))) return; loot = &go->loot; if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR) { // locked doors are opened with spelleffect openlock, prevent remove its as looted go->UseDoorOrButton(); } else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE) { // GO is mineral vein? so it is not removed after its looted if(go->GetGoType() == GAMEOBJECT_TYPE_CHEST) { uint32 go_min = go->GetGOInfo()->chest.minSuccessOpens; uint32 go_max = go->GetGOInfo()->chest.maxSuccessOpens; // only vein pass this check if(go_min != 0 && go_max > go_min) { float amount_rate = sWorld.getConfig(CONFIG_FLOAT_RATE_MINING_AMOUNT); float min_amount = go_min*amount_rate; float max_amount = go_max*amount_rate; go->AddUse(); float uses = float(go->GetUseCount()); if(uses < max_amount) { if(uses >= min_amount) { float chance_rate = sWorld.getConfig(CONFIG_FLOAT_RATE_MINING_NEXT); int32 ReqValue = 175; LockEntry const *lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId); if(lockInfo) ReqValue = lockInfo->Skill[0]; float skill = float(player->GetSkillValue(SKILL_MINING))/(ReqValue+25); double chance = pow(0.8*chance_rate,4*(1/double(max_amount))*double(uses)); if(roll_chance_f(float(100.0f*chance+skill))) { go->SetLootState(GO_READY); } else // not have more uses go->SetLootState(GO_JUST_DEACTIVATED); } else // 100% chance until min uses go->SetLootState(GO_READY); } else // max uses already go->SetLootState(GO_JUST_DEACTIVATED); } else // not vein go->SetLootState(GO_JUST_DEACTIVATED); } else if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE) { // The fishing hole used once more go->AddUse(); // if the max usage is reached, will be despawned at next tick if (go->GetUseCount() >= urand(go->GetGOInfo()->fishinghole.minSuccessOpens,go->GetGOInfo()->fishinghole.maxSuccessOpens)) { go->SetLootState(GO_JUST_DEACTIVATED); } else go->SetLootState(GO_READY); } else // not chest (or vein/herb/etc) go->SetLootState(GO_JUST_DEACTIVATED); loot->clear(); } else // not fully looted object go->SetLootState(GO_ACTIVATED); break; } case HIGHGUID_CORPSE: // ONLY remove insignia at BG { Corpse *corpse = _player->GetMap()->GetCorpse(lguid); if (!corpse || !corpse->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) return; loot = &corpse->loot; if (loot->isLooted()) { loot->clear(); corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE); } break; } case HIGHGUID_ITEM: { Item *pItem = player->GetItemByGuid(lguid ); if (!pItem) return; switch (pItem->loot.loot_type) { // temporary loot in stacking items, clear loot state, no auto loot move case LOOT_MILLING: case LOOT_PROSPECTING: { uint32 count = pItem->GetCount(); // >=5 checked in spell code, but will work for cheating cases also with removing from another stacks. if(count > 5) count = 5; // reset loot for allow repeat looting if stack > 5 pItem->loot.clear(); pItem->SetLootState(ITEM_LOOT_REMOVED); player->DestroyItemCount(pItem, count, true); break; } // temporary loot, auto loot move case LOOT_DISENCHANTING: { if (!pItem->loot.isLooted()) player->AutoStoreLoot(pItem->loot); // can be lost if no space pItem->loot.clear(); pItem->SetLootState(ITEM_LOOT_REMOVED); player->DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), true); break; } // normal persistence loot default: { // must be destroyed only if no loot if (pItem->loot.isLooted()) { pItem->SetLootState(ITEM_LOOT_REMOVED); player->DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), true); } break; } } return; // item can be looted only single player } case HIGHGUID_UNIT: case HIGHGUID_VEHICLE: { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed); if ( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) return; loot = &pCreature->loot; // update next looter if(Group* group = pCreature->GetGroupLootRecipient()) if (group->GetLooterGuid() == player->GetObjectGuid()) group->UpdateLooterGuid(pCreature); if (loot->isLooted()) { // for example skinning after normal loot pCreature->PrepareBodyLootState(); if(!pCreature->isAlive()) pCreature->AllLootRemovedFromCorpse(); } break; } default: { sLog.outError("%s is unsupported for looting.", lguid.GetString().c_str()); return; } } //Player is not looking at loot list, he doesn't need to see updates on the loot list loot->RemoveLooter(player->GetGUID()); }
void PlayerbotMgr::HandleMasterIncomingPacket(const WorldPacket& packet) { switch (packet.GetOpcode()) { case CMSG_ACTIVATETAXI: { WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid guid; std::vector<uint32> nodes; nodes.resize(2); uint8 delay = 9; p >> guid >> nodes[0] >> nodes[1]; DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_ACTIVATETAXI from %d to %d", nodes[0], nodes[1]); for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { delay = delay + 3; Player* const bot = it->second; if (!bot) return; Group* group = bot->GetGroup(); if (!group) continue; Unit *target = ObjectAccessor::GetUnit(*bot, guid); bot->GetPlayerbotAI()->SetIgnoreUpdateTime(delay); bot->GetMotionMaster()->Clear(true); bot->GetMotionMaster()->MoveFollow(target, INTERACTION_DISTANCE, bot->GetOrientation()); bot->GetPlayerbotAI()->GetTaxi(guid, nodes); } return; } case CMSG_ACTIVATETAXIEXPRESS: { WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid guid; uint32 node_count; uint8 delay = 9; p >> guid; p.read_skip<uint32>(); p >> node_count; std::vector<uint32> nodes; for (uint32 i = 0; i < node_count; ++i) { uint32 node; p >> node; nodes.push_back(node); } if (nodes.empty()) return; DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_ACTIVATETAXIEXPRESS from %d to %d", nodes.front(), nodes.back()); for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { delay = delay + 3; Player* const bot = it->second; if (!bot) return; Group* group = bot->GetGroup(); if (!group) continue; Unit *target = ObjectAccessor::GetUnit(*bot, guid); bot->GetPlayerbotAI()->SetIgnoreUpdateTime(delay); bot->GetMotionMaster()->Clear(true); bot->GetMotionMaster()->MoveFollow(target, INTERACTION_DISTANCE, bot->GetOrientation()); bot->GetPlayerbotAI()->GetTaxi(guid, nodes); } return; } case CMSG_MOVE_SPLINE_DONE: { DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_MOVE_SPLINE_DONE"); WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid guid; // used only for proper packet read MovementInfo movementInfo; // used only for proper packet read p >> guid.ReadAsPacked(); p >> movementInfo; p >> Unused<uint32>(); // unk for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; if (!bot) return; // in taxi flight packet received in 2 case: // 1) end taxi path in far (multi-node) flight // 2) switch from one map to other in case multi-map taxi path // we need process only (1) uint32 curDest = bot->m_taxi.GetTaxiDestination(); if (!curDest) return; TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest); // far teleport case if (curDestNode && curDestNode->map_id != bot->GetMapId()) { if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) { // short preparations to continue flight FlightPathMovementGenerator* flight = (FlightPathMovementGenerator *) (bot->GetMotionMaster()->top()); flight->Interrupt(*bot); // will reset at map landing flight->SetCurrentNodeAfterTeleport(); TaxiPathNodeEntry const& node = flight->GetPath()[flight->GetCurrentNode()]; flight->SkipCurrentNode(); bot->TeleportTo(curDestNode->map_id, node.x, node.y, node.z, bot->GetOrientation()); } return; } uint32 destinationnode = bot->m_taxi.NextTaxiDestination(); if (destinationnode > 0) // if more destinations to go { // current source node for next destination uint32 sourcenode = bot->m_taxi.GetTaxiSource(); // Add to taximask middle hubs in taxicheat mode (to prevent having player with disabled taxicheat and not having back flight path) if (bot->isTaxiCheater()) if (bot->m_taxi.SetTaximaskNode(sourcenode)) { WorldPacket data(SMSG_NEW_TAXI_PATH, 0); bot->GetSession()->SendPacket(&data); } DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_MOVE_SPLINE_DONE Taxi has to go from %u to %u", sourcenode, destinationnode); uint32 mountDisplayId = sObjectMgr.GetTaxiMountDisplayId(sourcenode, bot->GetTeam()); uint32 path, cost; sObjectMgr.GetTaxiPath(sourcenode, destinationnode, path, cost); if (path && mountDisplayId) bot->GetSession()->SendDoFlight(mountDisplayId, path, 1); // skip start fly node else bot->m_taxi.ClearTaxiDestinations(); // clear problematic path and next } else /* std::ostringstream out; out << "Destination reached" << bot->GetName(); ChatHandler ch(m_master); ch.SendSysMessage(out.str().c_str()); */ bot->m_taxi.ClearTaxiDestinations(); // Destination, clear source node } return; } // if master is logging out, log out all bots case CMSG_LOGOUT_REQUEST: { LogoutAllBots(); return; } // If master inspects one of his bots, give the master useful info in chat window // such as inventory that can be equipped case CMSG_INSPECT: { WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid guid; p >> guid; Player* const bot = GetPlayerBot(guid); if (bot) bot->GetPlayerbotAI()->SendNotEquipList(*bot); return; } // handle emotes from the master //case CMSG_EMOTE: case CMSG_TEXT_EMOTE: { WorldPacket p(packet); p.rpos(0); // reset reader uint32 emoteNum; p >> emoteNum; /* std::ostringstream out; out << "emote is: " << emoteNum; ChatHandler ch(m_master); ch.SendSysMessage(out.str().c_str()); */ switch (emoteNum) { case TEXTEMOTE_BOW: { // Buff anyone who bows before me. Useful for players not in bot's group // How do I get correct target??? //Player* const pPlayer = GetPlayerBot(m_master->GetSelection()); //if (pPlayer->GetPlayerbotAI()->GetClassAI()) // pPlayer->GetPlayerbotAI()->GetClassAI()->BuffPlayer(pPlayer); return; } /* case TEXTEMOTE_BONK: { Player* const pPlayer = GetPlayerBot(m_master->GetSelection()); if (!pPlayer || !pPlayer->GetPlayerbotAI()) return; PlayerbotAI* const pBot = pPlayer->GetPlayerbotAI(); ChatHandler ch(m_master); { std::ostringstream out; out << "CurrentTime: " << CurrentTime() << " m_ignoreAIUpdatesUntilTime: " << pBot->m_ignoreAIUpdatesUntilTime; ch.SendSysMessage(out.str().c_str()); } { std::ostringstream out; out << "m_TimeDoneEating: " << pBot->m_TimeDoneEating << " m_TimeDoneDrinking: " << pBot->m_TimeDoneDrinking; ch.SendSysMessage(out.str().c_str()); } { std::ostringstream out; out << "m_CurrentlyCastingSpellId: " << pBot->m_CurrentlyCastingSpellId; ch.SendSysMessage(out.str().c_str()); } { std::ostringstream out; out << "IsBeingTeleported() " << pBot->GetPlayer()->IsBeingTeleported(); ch.SendSysMessage(out.str().c_str()); } { std::ostringstream out; bool tradeActive = (pBot->GetPlayer()->GetTrader()) ? true : false; out << "tradeActive: " << tradeActive; ch.SendSysMessage(out.str().c_str()); } { std::ostringstream out; out << "IsCharmed() " << pBot->getPlayer()->isCharmed(); ch.SendSysMessage(out.str().c_str()); } return; } */ case TEXTEMOTE_EAT: case TEXTEMOTE_DRINK: return; // emote to attack selected target case TEXTEMOTE_POINT: { ObjectGuid attackOnGuid = m_master->GetSelectionGuid(); if (!attackOnGuid) return; Unit* thingToAttack = ObjectAccessor::GetUnit(*m_master, attackOnGuid); if (!thingToAttack) return; Player* bot = 0; for (PlayerBotMap::iterator itr = m_playerBots.begin(); itr != m_playerBots.end(); ++itr) { bot = itr->second; if (!bot->IsFriendlyTo(thingToAttack)) { if (!bot->IsWithinLOSInMap(thingToAttack)) { bot->GetPlayerbotAI()->TellMaster("Trying to attack something, but I'm too far away!"); bot->GetPlayerbotAI()->DoTeleport(*m_master); } if (bot->IsWithinLOSInMap(thingToAttack)) bot->GetPlayerbotAI()->Attack(thingToAttack); } } return; } // emote to stay case TEXTEMOTE_STAND: { Player* const bot = GetPlayerBot(m_master->GetSelectionGuid()); if (bot) bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_STAY); else for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_STAY); } return; } // 324 is the followme emote (not defined in enum) // if master has bot selected then only bot follows, else all bots follow case 324: case TEXTEMOTE_WAVE: { Player* const bot = GetPlayerBot(m_master->GetSelectionGuid()); if (bot) bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_FOLLOW, m_master); else for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_FOLLOW, m_master); } return; } } return; } /* EMOTE ends here */ case CMSG_GAMEOBJ_USE: // not sure if we still need this one { DEBUG_LOG("PlayerbotMgr: CMSG_GAMEOBJ_USE"); WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid objGUID; p >> objGUID; for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; GameObject *obj = m_master->GetMap()->GetGameObject(objGUID); if (!obj) return; // add other go types here, i.e.: // GAMEOBJECT_TYPE_CHEST - loot quest items of chest if (obj->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) { bot->GetPlayerbotAI()->TurnInQuests(obj); // auto accept every available quest this NPC has bot->PrepareQuestMenu(objGUID); QuestMenu& questMenu = bot->PlayerTalkClass->GetQuestMenu(); for (uint32 iI = 0; iI < questMenu.MenuItemCount(); ++iI) { QuestMenuItem const& qItem = questMenu.GetItem(iI); uint32 questID = qItem.m_qId; if (!bot->GetPlayerbotAI()->AddQuest(questID, obj)) DEBUG_LOG("Couldn't take quest"); } } } } break; case CMSG_QUESTGIVER_HELLO: { DEBUG_LOG("PlayerbotMgr: CMSG_QUESTGIVER_HELLO"); WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid npcGUID; p >> npcGUID; WorldObject* pNpc = m_master->GetMap()->GetWorldObject(npcGUID); if (!pNpc) return; // for all master's bots for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; bot->GetPlayerbotAI()->TurnInQuests(pNpc); } return; } // if master accepts a quest, bots should also try to accept quest case CMSG_QUESTGIVER_ACCEPT_QUEST: { WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid guid; uint32 quest; uint32 unk1; p >> guid >> quest; DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_QUESTGIVER_ACCEPT_QUEST npc = %s, quest = %u", guid.GetString().c_str(), quest); Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest); if (qInfo) for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; if (bot->GetQuestStatus(quest) == QUEST_STATUS_COMPLETE) bot->GetPlayerbotAI()->TellMaster("I already completed that quest."); else if (!bot->CanTakeQuest(qInfo, false)) { if (!bot->SatisfyQuestStatus(qInfo, false)) bot->GetPlayerbotAI()->TellMaster("I already have that quest."); else bot->GetPlayerbotAI()->TellMaster("I can't take that quest."); } else if (!bot->SatisfyQuestLog(false)) bot->GetPlayerbotAI()->TellMaster("My quest log is full."); else if (!bot->CanAddQuest(qInfo, false)) bot->GetPlayerbotAI()->TellMaster("I can't take that quest because it requires that I take items, but my bags are full!"); else { p.rpos(0); // reset reader bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(p); bot->GetPlayerbotAI()->TellMaster("Got the quest."); // build needed items if quest contains any for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++) if (qInfo->ReqItemCount[i] > 0) { bot->GetPlayerbotAI()->SetQuestNeedItems(); break; } // build needed creatures if quest contains any for (int i = 0; i < QUEST_OBJECTIVES_COUNT; i++) if (qInfo->ReqCreatureOrGOCount[i] > 0) { bot->GetPlayerbotAI()->SetQuestNeedCreatures(); break; } } } return; } case CMSG_AREATRIGGER: { WorldPacket p(packet); for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; p.rpos(0); // reset reader bot->GetSession()->HandleAreaTriggerOpcode(p); } return; } case CMSG_QUESTGIVER_COMPLETE_QUEST: { WorldPacket p(packet); p.rpos(0); // reset reader uint32 quest; ObjectGuid npcGUID; p >> npcGUID >> quest; DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %s, quest = %u", npcGUID.GetString().c_str(), quest); WorldObject* pNpc = m_master->GetMap()->GetWorldObject(npcGUID); if (!pNpc) return; // for all master's bots for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; bot->GetPlayerbotAI()->TurnInQuests(pNpc); } return; } case CMSG_LOOT_ROLL: { WorldPacket p(packet); //WorldPacket packet for CMSG_LOOT_ROLL, (8+4+1) ObjectGuid Guid; uint32 itemSlot; uint8 rollType; p.rpos(0); //reset packet pointer p >> Guid; //guid of the lootable target p >> itemSlot; //loot index p >> rollType; //need,greed or pass on roll for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { uint32 choice = 0; Player* const bot = it->second; if (!bot) return; Group* group = bot->GetGroup(); if (!group) return; // check that the bot did not already vote if (rollType >= ROLL_NOT_EMITED_YET) return; Loot* loot = sLootMgr.GetLoot(bot, Guid); if (!loot) { sLog.outError("LootMgr::PlayerVote> Error cannot get loot object info!"); return; } LootItem* lootItem = loot->GetLootItemInSlot(itemSlot); ItemPrototype const *pProto = lootItem->itemProto; if (!pProto) return; if (bot->GetPlayerbotAI()->CanStore()) { if (bot->CanUseItem(pProto) == EQUIP_ERR_OK && bot->GetPlayerbotAI()->IsItemUseful(lootItem->itemId)) choice = 1; // Need else choice = 2; // Greed } else choice = 0; // Pass sLootMgr.PlayerVote(bot, Guid, itemSlot, RollVote(choice)); } return; } // Handle GOSSIP activate actions, prior to GOSSIP select menu actions case CMSG_GOSSIP_HELLO: { DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_GOSSIP_HELLO"); WorldPacket p(packet); //WorldPacket packet for CMSG_GOSSIP_HELLO, (8) ObjectGuid guid; p.rpos(0); //reset packet pointer p >> guid; for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; if (!bot) continue; Creature *pCreature = bot->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); if (!pCreature) { DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_GOSSIP_HELLO %s not found or you can't interact with him.", guid.GetString().c_str()); continue; } GossipMenuItemsMapBounds pMenuItemBounds = sObjectMgr.GetGossipMenuItemsMapBounds(pCreature->GetCreatureInfo()->GossipMenuId); for (GossipMenuItemsMap::const_iterator itr = pMenuItemBounds.first; itr != pMenuItemBounds.second; ++itr) { uint32 npcflags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS); if (!(itr->second.npc_option_npcflag & npcflags)) continue; switch (itr->second.option_id) { case GOSSIP_OPTION_TAXIVENDOR: { //bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_TAXIVENDOR"); bot->GetSession()->SendLearnNewTaxiNode(pCreature); break; } case GOSSIP_OPTION_QUESTGIVER: { //bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_QUESTGIVER"); bot->GetPlayerbotAI()->TurnInQuests(pCreature); break; } case GOSSIP_OPTION_VENDOR: { //bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_VENDOR"); if (!botConfig.GetBoolDefault("PlayerbotAI.SellGarbage", true)) return; bot->GetPlayerbotAI()->SellGarbage(); break; } case GOSSIP_OPTION_STABLEPET: { //bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_STABLEPET"); break; } case GOSSIP_OPTION_AUCTIONEER: { //bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_AUCTIONEER"); break; } case GOSSIP_OPTION_BANKER: { //bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_BANKER"); break; } case GOSSIP_OPTION_INNKEEPER: { //bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_INNKEEPER"); break; } } } } return; } case CMSG_SPIRIT_HEALER_ACTIVATE: { // DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_SPIRIT_HEALER_ACTIVATE SpiritHealer is resurrecting the Player %s",m_master->GetName()); for (PlayerBotMap::iterator itr = m_playerBots.begin(); itr != m_playerBots.end(); ++itr) { Player* const bot = itr->second; Group *grp = bot->GetGroup(); if (grp) grp->RemoveMember(bot->GetObjectGuid(), 1); } return; } case CMSG_LIST_INVENTORY: { if (!botConfig.GetBoolDefault("PlayerbotAI.SellGarbage", true)) return; WorldPacket p(packet); p.rpos(0); // reset reader ObjectGuid npcGUID; p >> npcGUID; Object* const pNpc = (WorldObject *) m_master->GetObjectByTypeMask(npcGUID, TYPEMASK_CREATURE_OR_GAMEOBJECT); if (!pNpc) return; // for all master's bots for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; if (!bot->IsInMap(static_cast<WorldObject *>(pNpc))) { bot->GetPlayerbotAI()->TellMaster("I'm too far away to sell items!"); continue; } else bot->GetPlayerbotAI()->SellGarbage(); } return; } /* case CMSG_NAME_QUERY: case MSG_MOVE_START_FORWARD: case MSG_MOVE_STOP: case MSG_MOVE_SET_FACING: case MSG_MOVE_START_STRAFE_LEFT: case MSG_MOVE_START_STRAFE_RIGHT: case MSG_MOVE_STOP_STRAFE: case MSG_MOVE_START_BACKWARD: case MSG_MOVE_HEARTBEAT: case CMSG_STANDSTATECHANGE: case CMSG_QUERY_TIME: case CMSG_CREATURE_QUERY: case CMSG_GAMEOBJECT_QUERY: case MSG_MOVE_JUMP: case MSG_MOVE_FALL_LAND: return;*/ default: { /*const char* oc = LookupOpcodeName(packet.GetOpcode()); // ChatHandler ch(m_master); // ch.SendSysMessage(oc); std::ostringstream out; out << "masterin: " << oc; sLog.outError(out.str().c_str()); */ } } }
void WorldSession::HandleMovementOpcodes(WorldPacket& recvData) { uint16 opcode = recvData.GetOpcode(); Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plrMover = mover->ToPlayer(); // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) { recvData.rfinish(); // prevent warnings spam return; } /* extract packet */ uint64 guid; recvData.readPackGUID(guid); MovementInfo movementInfo; movementInfo.guid = guid; ReadMovementInfo(recvData, &movementInfo); recvData.rfinish(); // prevent warnings spam // prevent tampered movement data if (guid != mover->GetGUID()) return; if (!movementInfo.pos.IsPositionValid()) { recvData.rfinish(); // prevent warnings spam return; } // Tempfix for Drink Exploit (Thx jetwanx) if (mover->IsSitState() && movementInfo.GetMovementFlags() & (MOVEMENTFLAG_MASK_MOVING | MOVEMENTFLAG_MASK_TURNING)) mover->SetStandState(UNIT_STAND_STATE_STAND); /* handle special cases */ if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.transport.pos.GetPositionX() > 50 || movementInfo.transport.pos.GetPositionY() > 50 || movementInfo.transport.pos.GetPositionZ() > 50) { recvData.rfinish(); // prevent warnings spam return; } if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.transport.pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.transport.pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.transport.pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.transport.pos.GetOrientation())) { recvData.rfinish(); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plrMover) { if (!plrMover->GetTransport()) { // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just dismount if the guid can be found in the transport list for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.transport.guid) { plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } } else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) { bool foundNewTransport = false; plrMover->m_transport->RemovePassenger(plrMover); for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.transport.guid) { foundNewTransport = true; plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } if (!foundNewTransport) { plrMover->m_transport = NULL; movementInfo.transport.Reset(); } } } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.flags &= ~MOVEMENTFLAG_ONTRANSPORT; } } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; movementInfo.transport.Reset(); } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight()) plrMover->HandleFall(movementInfo); if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } /*----------------------*/ /* process position-change */ WorldPacket data(opcode, recvData.size()); movementInfo.time = getMSTime(); movementInfo.guid = mover->GetGUID(); WriteMovementInfo(&data, &movementInfo); mover->SendMessageToSet(&data, _player); mover->m_movementInfo = movementInfo; // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle() if (mover->GetVehicle()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); return; } mover->UpdatePosition(movementInfo.pos); if (plrMover) // nothing is charmed, or player charmed { plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); float underMapValueZ; switch (plrMover->GetMapId()) { case 617: underMapValueZ = 3.0f; break; // Dalaran Sewers case 618: underMapValueZ = 28.0f; break; // Ring of Valor case 562: underMapValueZ = -10.0f; break; // Blade Edge Arena case 559: underMapValueZ = -18.0f; break; // Nagrand arena case 572: underMapValueZ = 28.0f; break; // Lordearon case 571: underMapValueZ = -400.0f; break; // Northrend default: underMapValueZ = -500.0f; break; } if (movementInfo.pos.GetPositionZ() < underMapValueZ) { if (underMapValueZ != -500) // For Custom Arena Zone { // Hackfix for ArenaUnderZ -> Teleport WHY TRINITY BREAK IT! if (plrMover->GetMapId() == 572) // Lordaeron Arena plrMover->TeleportTo(572, 1286.14868f, 1667.32f, 41.0f, 1.6f); // if (plrMover->GetMapId() == 559) // Nagrand Arena plrMover->TeleportTo(559, 4052.79868f, 2926.32f, 16.0f, 1.6f); if (plrMover->GetMapId() == 562) // Blade Edge arena 6237.797363 261.146088 2 0.7 plrMover->TeleportTo(562, 6237.79768f, 261.142f, 2.0f, 4.0f); if (plrMover->GetMapId() == 617) // Dalaran Arena 1292.31384 790.40667 8 plrMover->TeleportTo(617, 1292.34868f, 790.40f, 8.5f, 1.6f); } else if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away /// @todo discard movement packets after the player is rooted if (plrMover->IsAlive()) { plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (!plrMover->IsAlive()) plrMover->KillPlayer(); } } } } }