/* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. */ #include "MoveToTravelTargetAction.h" #include "ChooseRpgTargetAction.h" #include "LootObjectStack.h" #include "Playerbots.h" bool MoveToTravelTargetAction::Execute(Event /*event*/) { TravelTarget* target = AI_VALUE(TravelTarget*, "travel target"); WorldPosition botLocation(bot); WorldLocation location = *target->getPosition(); Group* group = bot->GetGroup(); if (group && !urand(0, 1) && bot == botAI->GetGroupLeader() && !bot->IsInCombat()) { for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); if (member == bot) continue; if (!member->IsAlive()) continue; if (!member->isMoving()) continue; PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member); if (memberBotAI && !memberBotAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) continue; WorldPosition memberPos(member); WorldPosition targetPos = *target->getPosition(); float memberDistance = botLocation.distance(memberPos); if (memberDistance < 50.0f) continue; if (memberDistance > sPlayerbotAIConfig.reactDistance * 20) continue; // float memberAngle = botLocation.getAngleBetween(targetPos, memberPos); // if (botLocation.getMapId() == targetPos.getMapId() && botLocation.getMapId() == memberPos.getMapId() && // memberAngle < static_cast(M_PI) / 2) //We are heading that direction anyway. // continue; if (!urand(0, 5)) { std::ostringstream out; if (botAI->GetMaster() && !bot->GetGroup()->IsMember(botAI->GetMaster()->GetGUID())) out << "Waiting a bit for "; else out << "Please hurry up "; out << member->GetName(); botAI->TellMasterNoFacing(out); } target->setExpireIn(target->getTimeLeft() + sPlayerbotAIConfig.maxWaitForMove); botAI->SetNextCheckDelay(sPlayerbotAIConfig.maxWaitForMove); return true; } } float maxDistance = target->getDestination()->getRadiusMin(); // Spread bots around the target but keep the offset stable per // (bot, destination) pair. Previously the angle and radius were // re-rolled every time the action re-entered (i.e. every tick the // bot wasn't already moving), which made bots oscillate between // two random points around the same quest POI instead of // committing to one approach. uint32 botLow = bot->GetGUID().GetCounter(); int32 destSeed = static_cast(location.GetPositionX()) * 73856093 ^ static_cast(location.GetPositionY()) * 19349663; uint32 seed = botLow ^ static_cast(destSeed); float angle = 2.0f * static_cast(M_PI) * static_cast(seed % 1000) / 1000.0f; float mod = 0.5f + static_cast((seed / 1000) % 1000) / 2000.0f; // [0.5, 1.0] if (target->getMaxTravelTime() > target->getTimeLeft()) // The bot is late. Speed it up. { // distance = sPlayerbotAIConfig.fleeDistance; // angle = bot->GetAngle(location.GetPositionX(), location.GetPositionY()); // location = botLocation.getLocation(); } float x = location.GetPositionX(); float y = location.GetPositionY(); float z = location.GetPositionZ(); float mapId = location.GetMapId(); x += cos(angle) * maxDistance * mod; y += sin(angle) * maxDistance * mod; bool canMove = false; if (bot->IsWithinLOS(x, y, z)) canMove = MoveNear(mapId, x, y, z, 0); else canMove = MoveTo(mapId, x, y, z, false, false); if (!canMove && !target->isForced()) { target->incRetry(true); if (target->isMaxRetry(true)) target->setStatus(TRAVEL_STATUS_COOLDOWN); } else target->setRetry(true); return canMove; } bool MoveToTravelTargetAction::isUseful() { if (!botAI->AllowActivity(TRAVEL_ACTIVITY)) return false; if (!context->GetValue("travel target")->Get()->isTraveling()) return false; if (bot->HasUnitState(UNIT_STATE_IN_FLIGHT)) return false; if (bot->IsFlying()) return false; if (bot->isMoving()) return false; if (!AI_VALUE(bool, "can move around")) return false; LootObject loot = AI_VALUE(LootObject, "loot target"); if (loot.IsLootPossible(bot)) return false; if (!ChooseRpgTargetAction::isFollowValid(bot, *context->GetValue("travel target")->Get()->getPosition())) return false; return true; }