Bots de groupe : rehabillage a chaque montee de niveau

Seuls les talents et les sorts suivaient la montee de niveau d un bot
(OnLevelupToBotAI : ReupdateTalents + LearnSpells). L equipement, lui, n etait
pose qu une fois, au re-level initial : un mercenaire embauche au niveau 20 et
monte a 25 gardait son stuff de niveau 20 pour tout le contrat et devenait de
plus en plus fragile.

Nouvelle methode PlayerBotSetting::RefreshEquipment() : reprend le seul bloc
equipement du re-level complet -- UnequipFromAll, CheckInventroy,
AddEquipFromAll, UpequipFromAll, SupplementOtherItems -- puis UpdateAllStats
et SaveToDB. On ne repasse volontairement ni par ResetTalents ni par
ActivateSpecialization : la reattribution de specialisation en cours de partie
est le chemin des crashs connus dans Unit::GetAPMultiplier.

ProcessDelayLevelup renvoie desormais false tant que le bot est en combat : le
schedule reste en tete de file et sera retente au tick suivant, plutot que de
desarmer le bot en pleine bagarre. RefreshEquipment reverifie de son cote.

Ce commit emporte aussi ActivateSpecialization (PlayerBotSetting), ecrit lors
d une session precedente, deployé depuis le 15/08 mais jamais commité.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
SylvaniaCore deploy
2026-08-16 13:20:01 +02:00
parent 0dbbb73a24
commit 8a46e5b08d
4 changed files with 74 additions and 1 deletions
@@ -492,6 +492,10 @@ void Player::OnLevelupToBotAI()
{
uint32 type = ReupdateTalents();
m_PlayerBotSetting->LearnSpells();
// Seuls les talents et les sorts suivaient la montee de niveau : le
// bot gardait pour tout le contrat l equipement recu a l embauche et
// devenait de plus en plus fragile.
m_PlayerBotSetting->RefreshEquipment();
pAI->OnLevelUp(type);
}
}
+63 -1
View File
@@ -1742,7 +1742,7 @@ bool PlayerBotSetting::ResetPlayerToLevel(uint32 level, uint32 talent, bool tena
m_Player->GiveLevel(level);
m_Player->SetUInt32Value(PLAYER_XP, 0);
}
m_ResetStep = 1;
m_ResetStep = 0;
m_Finish = false;
m_TenacitySetting = tenacity;
return true;
@@ -1758,6 +1758,32 @@ void PlayerBotSetting::SupplementAmmo()
}
// Rehabille le bot a son niveau courant.
//
// C est exactement le bloc equipement du re-level complet -- les etapes 6 a 10
// de UpdateReset() -- isole pour pouvoir etre rejoue a chaque montee de niveau.
// On ne repasse volontairement NI par ResetTalents NI par ActivateSpecialization :
// la reattribution de specialisation en cours de partie est le chemin des crashs
// connus de PlayerBotSetting, et les talents comme les sorts sont deja tenus a
// jour par OnLevelupToBotAI().
//
// L ordre compte : AddEquipFromAll() remplit m_NeedEquips, UpequipFromAll() le
// consomme et le vide.
void PlayerBotSetting::RefreshEquipment()
{
if (!m_Player || !m_Player->IsInWorld() || m_Player->IsInCombat())
return;
UnequipFromAll();
CheckInventroy();
AddEquipFromAll();
UpequipFromAll();
SupplementOtherItems();
m_Player->UpdateAllStats();
m_Player->SaveToDB();
}
void PlayerBotSetting::UpdateReset()
{
if (m_Finish)
@@ -1767,6 +1793,13 @@ void PlayerBotSetting::UpdateReset()
m_Player->CombatStop(true);
switch (m_ResetStep)
{
case 0:
// Sur son propre tick, avant l effacement des talents : changer de
// specialisation pendant que les stats et les auras sont recalculees
// est precisement ce qui faisait exploser la pile.
ActivateSpecialization();
++m_ResetStep;
break;
case 1:
m_Player->ResetTalents(true);
++m_ResetStep;
@@ -1839,6 +1872,35 @@ void PlayerBotSetting::UpdateReset()
m_TenacitySetting = false;
}
// Bascule le bot sur la specialisation demandee par le re-level, en passant par
// le chemin officiel du core. Une version precedente ecrivait
// PLAYER_FIELD_CURRENT_SPEC_ID a la main en plein re-level et provoquait un
// debordement de pile : ActivateTalentGroup fait le travail complet, y compris
// InitTalentForLevel, les boutons d action, la puissance et les auras de forme.
void PlayerBotSetting::ActivateSpecialization()
{
if (!m_Player)
return;
// 3 signifie garder la specialisation du personnage : rien a faire.
if (m_ActiveTalentType > 2)
return;
uint8 const playerClass = m_Player->getClass();
for (uint32 i = 0; i < sChrSpecializationStore.GetNumRows(); ++i)
{
ChrSpecializationEntry const* spec = sChrSpecializationStore.LookupEntry(i);
if (!spec || spec->ClassID != int8(playerClass) || spec->IsPetSpecialization())
continue;
if (uint32(spec->OrderIndex) != m_ActiveTalentType)
continue;
// ActivateTalentGroup ne fait rien si la specialisation est deja active.
m_Player->ActivateTalentGroup(spec);
return;
}
}
void PlayerBotSetting::LearnTalents()
{
// SylvaniaCore : cette fonction etait un corps vide alors que l etape
@@ -120,7 +120,9 @@ public:
uint32 SwitchPlayerTalent(uint32 talent);
void SupplementAmmo();
void UpdateReset();
void RefreshEquipment();
void LearnSpells();
void ActivateSpecialization();
void LearnTalents();
bool EquipItem(Item* pItem);
@@ -704,6 +704,11 @@ bool PlayerBotSession::ProcessDelayLevelup(BotGlobleSchedule& schedule)
ClearAllSchedule();
return false;
}
// Le rehabillage retire puis rend tout l equipement : hors de question de
// desarmer le bot en pleine bagarre. Renvoyer false laisse le schedule en
// tete de file, il sera retente au tick suivant.
if (player->IsInCombat())
return false;
player->OnLevelupToBotAI();
return true;
}