diff --git a/src/server/game/AI/PlayerAI/BotAITool.cpp b/src/server/game/AI/PlayerAI/BotAITool.cpp index 4097138..7a8c591 100644 --- a/src/server/game/AI/PlayerAI/BotAITool.cpp +++ b/src/server/game/AI/PlayerAI/BotAITool.cpp @@ -1043,6 +1043,20 @@ void BotAITeleport::Update(uint32 diff, BotBGAIMovement* pMovement) me->SendMessageToSet(&data, me); me->CombatStop(true); me->SetSelection(ObjectGuid::Empty); + // SylvaniaCore : le paquet diffuse ci-dessus porte un opcode CLIENT + // (CMSG_MOVE_FALL_LAND), que les clients voisins ne savent pas lire. + // Resultat : le bot avait bien change de place cote serveur - positions + // identiques en base - mais restait affiche a son ancien emplacement, + // jusqu au prochain passage hors puis dans le champ de vision. On force + // donc la reconstruction de l objet chez les joueurs alentour. + me->DestroyForNearbyPlayers(); + me->UpdateObjectVisibility(true); + // mod-playerbots (PlayerbotAI::HandleTeleportAck) vide le MotionMaster + // et arrete le mouvement a chaque acquittement de teleport. Sans cela, + // le generateur de suivi encore en place fait repartir le bot depuis + // l ancienne trajectoire. + me->GetMotionMaster()->Clear(true); + me->StopMoving(); m_TeleportPositon.m_positionX = m_TeleportPositon.m_positionY = m_TeleportPositon.m_positionZ = 0; m_TeleportStep = 0; } diff --git a/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp b/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp index 7d0e777..2d82383 100644 --- a/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp +++ b/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp @@ -285,6 +285,14 @@ void BotGroupAI::ProcessStopCommand() me->SetSelection(ObjectGuid::Empty); m_SeduceTarget = ObjectGuid::Empty; m_StopFollow = true; + // SylvaniaCore : sans ces trois lignes, l ordre restait sans effet visible. + // Le suivi est un generateur MoveFollow pose sur le MotionMaster ; + // ClearMovement() se contente d appeler StopMoving(), et le generateur, + // toujours en tete de pile, relance la poursuite au tick suivant. Il faut + // vider la pile de mouvement pour que le mercenaire se fige vraiment. + m_Movement->ClearMovement(); + me->GetMotionMaster()->Clear(); + me->StopMoving(); } void BotGroupAI::ProcessSetting() diff --git a/src/server/game/Mercenary/MercenaryMgr.cpp b/src/server/game/Mercenary/MercenaryMgr.cpp index 815c2c4..8eb6fff 100644 --- a/src/server/game/Mercenary/MercenaryMgr.cpp +++ b/src/server/game/Mercenary/MercenaryMgr.cpp @@ -16,6 +16,7 @@ */ #include "MercenaryMgr.h" +#include "BotGroupAI.h" #include "CapitalSiegeMgr.h" #include "Chat.h" #include "Config.h" @@ -161,6 +162,8 @@ bool MercenaryMgr::IsAccountHired(uint32 accountId) const bool MercenaryMgr::FindCandidate(Player* owner, uint8 role, uint32& accountId, uint64& charGuid, bool& alreadyOnline) const { SessionMap const& sessions = sWorld->GetAllSessions(); + std::vector onlineCandidates; + std::vector > offlineCandidates; for (SessionMap::const_iterator it = sessions.begin(); it != sessions.end(); ++it) { @@ -194,7 +197,15 @@ bool MercenaryMgr::FindCandidate(Player* owner, uint8 role, uint32& accountId, u if (FindSpecIndexForRole(bot->getClass(), role) < 0) continue; - accountId = session->GetAccountId(); + onlineCandidates.push_back(session->GetAccountId()); + } + + // Tirage au sort parmi TOUS les candidats, et non premier trouve : la carte + // des sessions est ordonnee par identifiant de compte, si bien qu un choix + // sequentiel ramenait invariablement le meme mercenaire. + if (!onlineCandidates.empty()) + { + accountId = onlineCandidates[urand(0, uint32(onlineCandidates.size()) - 1)]; charGuid = 0; alreadyOnline = true; return true; @@ -235,13 +246,21 @@ bool MercenaryMgr::FindCandidate(Player* owner, uint8 role, uint32& accountId, u if (FindSpecIndexForRole(uint8(charInfo.profession), role) < 0) continue; - accountId = session->GetAccountId(); - charGuid = charInfo.guid; - alreadyOnline = false; - return true; + offlineCandidates.push_back(std::make_pair(session->GetAccountId(), charInfo.guid)); + break; // un seul personnage retenu par compte } } + if (!offlineCandidates.empty()) + { + std::pair const& picked = + offlineCandidates[urand(0, uint32(offlineCandidates.size()) - 1)]; + accountId = picked.first; + charGuid = picked.second; + alreadyOnline = false; + return true; + } + return false; } @@ -518,6 +537,22 @@ void MercenaryMgr::Update(uint32 diff) ReleaseBot(contract); continue; } + + // Materialisation aupres de l employeur, au premier tick qui suit + // l entree dans le groupe. On passe par l ordre « summon » de l IA + // plutot que par un TeleportTo direct : un bot n a pas de client + // pour accuser reception du teleport, c est BotAITeleport qui + // simule cet echange en trois etapes. Un TeleportTo pose ici laisse + // le mercenaire a son ancienne place aux yeux de tout le monde. + if (it->summonPending) + { + if (BotGroupAI* groupAI = dynamic_cast(bot->GetAI())) + { + groupAI->ProcessBotCommand(owner, "summon"); + it->summonPending = false; + } + } + ++it; continue; } @@ -581,16 +616,13 @@ void MercenaryMgr::Update(uint32 diff) it->botGuid = bot->GetGUID(); it->stage = MERC_STAGE_ACTIVE; + // Le mercenaire doit encore se materialiser aupres de son employeur : + // c est fait au tick suivant, une fois que l IA de groupe aura reconnu + // son maitre. Voir la branche MERC_STAGE_ACTIVE. + it->summonPending = true; PlayerBotMgr::SwitchPlayerBotAI(bot, PlayerBotAIType::PBAIT_GROUP, true); - // Le mercenaire se materialise aupres de son employeur. L ajout au - // groupe precede volontairement le teleport : c est ce qui autorise - // l entree dans l instance ou se trouve deja le joueur. - float x, y, z; - owner->GetClosePoint(x, y, z, 2.0f, 3.0f); - bot->TeleportTo(owner->GetMapId(), x, y, z, owner->GetOrientation()); - ChatHandler(owner->GetSession()).PSendSysMessage( "|cff00ff00[Portail]|r %s, %s mercenaire, répond à votre appel.", bot->GetName().c_str(), GetRoleName(it->role)); diff --git a/src/server/game/Mercenary/MercenaryMgr.h b/src/server/game/Mercenary/MercenaryMgr.h index 1baa56c..21dac1a 100644 --- a/src/server/game/Mercenary/MercenaryMgr.h +++ b/src/server/game/Mercenary/MercenaryMgr.h @@ -64,7 +64,7 @@ enum MercenaryResult struct MercenaryContract { MercenaryContract() : accountId(0), role(0), stage(MERC_STAGE_SUMMONING), waitSeconds(0), - pendingRelease(false) { } + pendingRelease(false), summonPending(false) { } uint32 accountId; // compte bot reserve ObjectGuid ownerGuid; // joueur qui a paye @@ -76,6 +76,11 @@ struct MercenaryContract // Contrat rompu par un hook de groupe du core, mais dont la liberation est // differee au prochain Update() : voir MercenaryMgr::OnPlayerLeftGroup(). bool pendingRelease; + + // Le mercenaire est entre dans le groupe mais n a pas encore ete rappele + // aupres de son employeur : l ordre part au tick suivant, quand l IA de + // groupe aura reconnu son maitre. + bool summonPending; }; class TC_GAME_API MercenaryMgr