diff --git a/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp b/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp index 6315f53..4d68f3f 100644 --- a/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp +++ b/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp @@ -264,9 +264,22 @@ void BotGroupAI::ProcessAttackCommand() { Unit* pMasterTarget = m_MasterPlayer->GetSelectedUnit(); if (!pMasterTarget || !pMasterTarget->IsAlive()) + { + TC_LOG_ERROR("botai", "ATTACKDBG %s: ordre recu mais le maitre n a pas de cible vivante.", + me->GetName().c_str()); return; + } if (!me->IsValidAttackTarget(pMasterTarget)) + { + TC_LOG_ERROR("botai", "ATTACKDBG %s: cible %u (entree %u) refusee par IsValidAttackTarget.", + me->GetName().c_str(), pMasterTarget->GetGUID().GetCounter(), + pMasterTarget->ToCreature() ? pMasterTarget->ToCreature()->GetEntry() : 0); return; + } + TC_LOG_ERROR("botai", "ATTACKDBG %s: cible %u (entree %u) retenue, distance %.1f.", + me->GetName().c_str(), pMasterTarget->GetGUID().GetCounter(), + pMasterTarget->ToCreature() ? pMasterTarget->ToCreature()->GetEntry() : 0, + me->GetDistance(pMasterTarget)); me->SetSelection(pMasterTarget->GetGUID()); m_ForceFlee = false; m_StopFollow = false; @@ -1448,7 +1461,26 @@ bool BotGroupAI::TryTeleportToMaster() m_Flee.Clear(); return true; } - else if (me->GetDistance(m_MasterPlayer->GetPosition()) > BOTAI_SEARCH_RANGE * 5) + // DISTANCE_DE_RAPPEL + // + // MESURE (audit du 09/09) : au moment d un « !attack », deux des quatre + // mercenaires se trouvaient a 100 et 106 metres de leur employeur, les + // deux autres a 21 et 28. Les lointains n etaient pas perdus au sens du + // code -- simplement sous le seuil de rappel, qui valait + // BOTAI_SEARCH_RANGE * 5, soit 160 metres. + // + // Ils recevaient donc l ordre, l acceptaient, et partaient A PIED. Cent + // metres de marche, c est une quinzaine de secondes : le combat est + // termine avant leur arrivee, et de l exterieur le mercenaire a + // simplement « refuse d attaquer ». + // + // Ce seuil de 160 convient a un bot de terrain qui vagabonde ; pas a un + // membre de groupe, qui n a aucune raison de s eloigner autant de son + // employeur. On le ramene a deux fois la portee de recherche, soit 64 + // metres -- largement au-dela des 32 metres ou l IA engage le combat et + // des 28 de portee des sorts, donc sans risque de teleporter un lanceur + // de sorts en pleine rotation. + else if (me->GetDistance(m_MasterPlayer->GetPosition()) > BOTAI_SEARCH_RANGE * 2) { m_Teleporting.SetTeleport(m_MasterPlayer, 0); me->SetSelection(ObjectGuid::Empty); @@ -3006,6 +3038,20 @@ Unit* BotGroupAI::GetBotAIValidSelectedUnit() isValid = false; if (!isValid) { + // SONDE ATTACKDBG : nommer le filtre qui rejette, une fois par + // seconde au plus pour ne pas noyer le journal. + if (pTarget && m_AttackDbgTick + 1000 < getMSTime()) + { + m_AttackDbgTick = getMSTime(); + char const* motif = "inconnu"; + if (!pTarget->IsVisible()) motif = "IsVisible"; + else if (!me->InSamePhase(pTarget->GetPhaseShift())) motif = "phase"; + else if (IsNotSelect(pTarget)) motif = "IsNotSelect"; + else if (TargetIsControl(pTarget)) motif = "controle/evade"; + else if (m_FliterCreatures.IsFliterCreature(pTarget->ToCreature())) motif = "filtre"; + TC_LOG_ERROR("botai", "ATTACKDBG %s: cible %u rejetee -- %s.", + me->GetName().c_str(), pTarget->GetGUID().GetCounter(), motif); + } me->AttackStop(); me->SetSelection(ObjectGuid::Empty); return NULL; diff --git a/src/server/game/Mercenary/MercenaryMgr.cpp b/src/server/game/Mercenary/MercenaryMgr.cpp index 7d3044d..6f00e15 100644 --- a/src/server/game/Mercenary/MercenaryMgr.cpp +++ b/src/server/game/Mercenary/MercenaryMgr.cpp @@ -793,15 +793,21 @@ void MercenaryMgr::Update(uint32 diff) // seulement de la creer, la ligne au-dessus. La tache partait avant // sa cible et tombait dans le vide. // - // On en repousse donc une maintenant que l IA existe. Elle attend - // que le bot soit hors combat avant d agir, le rehabillage complet - // faisant partie du meme passage. + // CORRECTION DU CORRECTIF (audit du 09/09) : la premiere version + // poussait un BGSType_DelayLevelup dans la file du bot. Mesure faite + // sur quatre mercenaires : deux repares, deux non. En cause, + // PushScheduleToQueue, qui jette SILENCIEUSEMENT une tache dans deux + // cas -- si une du meme type est deja en file, et, pour ce type + // precisement, si IsSettingFinish() est faux. La reparation ne + // partait donc qu au petit bonheur. + // + // On appelle directement. L IA de groupe vient d etre creee a la + // ligne precedente : le dynamic_cast qui ouvre + // OnLevelupToBotAI() aboutit forcement, et InitializeSpells re-resout + // les poignees contre le niveau, la specialisation et le grimoire + // definitifs. Rien ne peut plus l escamoter. // ============================================================= - if (PlayerBotSession* botSession = dynamic_cast(bot->GetSession())) - { - BotGlobleSchedule reresolution(BotGlobleScheduleType::BGSType_DelayLevelup, bot->GetGUID()); - botSession->PushScheduleToQueue(reresolution); - } + bot->OnLevelupToBotAI(); // Des maintenant, pas au tick suivant : l IA de groupe, en decouvrant // un maitre lointain, armerait le sien vers l employeur, et SetTeleport diff --git a/src/server/game/Server/PlayerBotSession.cpp b/src/server/game/Server/PlayerBotSession.cpp index 581cbd9..a7576a7 100644 --- a/src/server/game/Server/PlayerBotSession.cpp +++ b/src/server/game/Server/PlayerBotSession.cpp @@ -405,7 +405,30 @@ bool PlayerBotSession::ProcessSetting(BotGlobleSchedule& schedule) // heritage de l epoque ou LearnTalents() etait un corps vide. PlayerTalentMap const* talents = player->GetTalentMap(player->GetActiveTalentGroup()); bool const hasTalents = talents && talents->size() >= player->CalculateTalentsTiers(); - if (hasTalents && (schedule.parameter3 >= 4 || (player->FindTalentType() + 1 == schedule.parameter3))) + + // SIGNALE EN JEU : « Kaerbrus n a meme pas d equipement ». + // Constate en base : niveau 110, 76 sorts, ZERO piece portee. + // + // Ce raccourci evite un re-level complet a un bot deja au bon + // niveau, avec ses talents et la bonne specialisation. Mais il + // saute du meme coup les etapes 6 a 10 de UpdateReset(), qui sont + // justement l habillage. Un mercenaire ayant perdu son equipement + // -- contrat precedent interrompu, re-level avorte -- ne le + // retrouvait donc JAMAIS : a chaque embauche, le raccourci + // concluait que tout allait bien et le renvoyait nu. + // + // On verifie donc qu il est reellement habille avant de couper. + // Dix pieces sur les dix-neuf emplacements : de quoi distinguer un + // bot equipe d un bot depouille, sans exiger un sans-faute (la + // seconde babiole et l arme de jet manquent souvent, legitimement). + uint32 piecesPortees = 0; + for (uint8 emplacement = EQUIPMENT_SLOT_START; emplacement < EQUIPMENT_SLOT_END; ++emplacement) + if (player->GetItemByPos(INVENTORY_SLOT_BAG_0, emplacement)) + ++piecesPortees; + bool const correctementEquipe = (piecesPortees >= 10); + + if (hasTalents && correctementEquipe && + (schedule.parameter3 >= 4 || (player->FindTalentType() + 1 == schedule.parameter3))) return true; } }