Mercenaires : trois correctifs issus de l audit du module
1. La re-resolution des sorts passait par une file qui la jetait.
Le correctif 19c0cf03 poussait un BGSType_DelayLevelup apres la creation
de l IA de groupe. Mesure sur quatre mercenaires : deux repares, deux
non. En cause PushScheduleToQueue, qui jette silencieusement une tache
si une du meme type est deja en file, et -- pour ce type precisement --
si IsSettingFinish() est faux. La reparation ne partait qu au hasard.
On appelle desormais OnLevelupToBotAI() directement. L IA de groupe
vient d etre creee : le dynamic_cast qui l ouvre aboutit forcement.
2. Le raccourci de mise en place renvoyait des mercenaires nus.
Signale en jeu : « Kaerbrus n a meme pas d equipement ». Constate en
base : niveau 110, 76 sorts, zero piece portee.
ProcessSetting court-circuite le re-level d un bot deja au bon niveau,
avec ses talents et la bonne specialisation -- et saute du meme coup les
etapes d habillage. Un bot ayant perdu son equipement ne le retrouvait
jamais : a chaque embauche le raccourci concluait que tout allait bien.
On verifie donc qu il porte au moins dix pieces avant de couper.
3. Le rappel n intervenait qu au-dela de 160 metres.
Mesure : au moment d un « !attack », deux des quatre mercenaires se
trouvaient a 100 et 106 metres de leur employeur. Sous le seuil de
rappel, ils acceptaient l ordre et partaient A PIED -- une quinzaine de
secondes de marche, le combat fini avant leur arrivee. Vu du joueur, le
mercenaire « refuse d attaquer ».
Ce seuil convient a un bot de terrain, pas a un membre de groupe. Ramene
a deux fois la portee de recherche, soit 64 metres : bien au-dela des 32
ou l IA engage et des 28 de portee des sorts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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<BotGroupAI*> 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<PlayerBotSession*>(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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user