@@ -242,8 +242,8 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
242242 nextAICheckDelay = 0 ;
243243
244244 // Early return if bot is in invalid state
245- if (!bot || !bot->IsInWorld () || !bot->GetSession () || bot->GetSession ()-> isLogingOut () ||
246- bot->IsDuringRemoveFromWorld ())
245+ if (!bot || !bot->GetSession () || !bot->IsInWorld () || bot->IsBeingTeleported () ||
246+ bot->GetSession ()-> isLogingOut () || bot-> IsDuringRemoveFromWorld ())
247247 return ;
248248
249249 // Handle cheat options (set bot health and power if cheats are enabled)
@@ -713,39 +713,59 @@ void PlayerbotAI::HandleTeleportAck()
713713 if (IsRealPlayer ())
714714 return ;
715715
716+ // Clearing motion generators and stopping movement which prevents
717+ // conflicts between teleport and any active motion (walk, run, swim, flight, etc.)
716718 bot->GetMotionMaster ()->Clear (true );
717719 bot->StopMoving ();
720+
721+ // Near teleport (within map/instance)
718722 if (bot->IsBeingTeleportedNear ())
719723 {
720- // Temporary fix for instance can not enter
721- if (!bot->IsInWorld ())
722- {
723- bot->GetMap ()->AddPlayerToMap (bot);
724- }
725- while (bot->IsInWorld () && bot->IsBeingTeleportedNear ())
726- {
727- Player* plMover = bot->m_mover ->ToPlayer ();
728- if (!plMover)
729- return ;
730- WorldPacket p = WorldPacket (MSG_MOVE_TELEPORT_ACK , 20 );
731- p << plMover->GetPackGUID ();
732- p << (uint32)0 ; // supposed to be flags? not used currently
733- p << (uint32)0 ; // time - not currently used
734- bot->GetSession ()->HandleMoveTeleportAck (p);
735- };
724+ // Previous versions manually added the bot to the map if it was not in the world.
725+ // not needed: HandleMoveTeleportAckOpcode() safely attaches the player to the map
726+ // and clears IsBeingTeleportedNear().
727+
728+ Player* plMover = bot->m_mover ->ToPlayer ();
729+ if (!plMover)
730+ return ;
731+
732+ // Send the near teleport ACK packet
733+ WorldPacket p (MSG_MOVE_TELEPORT_ACK , 20 );
734+ p << plMover->GetPackGUID ();
735+ p << uint32 (0 );
736+ p << uint32 (0 );
737+ bot->GetSession ()->HandleMoveTeleportAck (p);
738+
739+ // Simulate teleport latency and prevent AI from running too early (used cmangos delays)
740+ SetNextCheckDelay (urand (1000 , 2000 ));
736741 }
742+
743+ // Far teleport (worldport / different map)
737744 if (bot->IsBeingTeleportedFar ())
738745 {
739- while (bot->IsBeingTeleportedFar ())
746+ // Handle far teleport ACK:
747+ // Moves the bot to the new map, clears IsBeingTeleportedFar(), updates session/map references
748+ bot->GetSession ()->HandleMoveWorldportAck ();
749+
750+ // Ensure bot now has a valid map. If this fails, there is a core/session bug?
751+ if (!bot->GetMap ())
740752 {
741- bot->GetSession ()-> HandleMoveWorldportAck ( );
753+ LOG_ERROR ( " playerbot " , " Bot {} has no map after worldport ACK " , bot->GetGUID (). ToString () );
742754 }
743- // SetNextCheckDelay(urand(2000, 5000));
755+
756+ // Instance strategies after teleport
744757 if (sPlayerbotAIConfig ->applyInstanceStrategies )
745758 ApplyInstanceStrategies (bot->GetMapId (), true );
759+
760+ // healer DPS strategies if restrictions are enabled
746761 if (sPlayerbotAIConfig ->restrictHealerDPS )
747762 EvaluateHealerDpsStrategy ();
763+
764+ // Reset AI state to to before teleport conditions
748765 Reset (true );
766+
767+ // Slightly longer delay to simulate far teleport latency (used cmangos delays)
768+ SetNextCheckDelay (urand (2000 , 5000 ));
749769 }
750770
751771 SetNextCheckDelay (sPlayerbotAIConfig ->globalCoolDown );
@@ -988,10 +1008,10 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
9881008{
9891009 if (packet.empty ())
9901010 return ;
1011+
9911012 if (!bot || !bot->IsInWorld () || bot->IsDuringRemoveFromWorld ())
992- {
9931013 return ;
994- }
1014+
9951015 switch (packet.GetOpcode ())
9961016 {
9971017 case SMSG_SPELL_FAILURE :
@@ -4103,8 +4123,7 @@ Player* PlayerbotAI::FindNewMaster()
41034123 for (GroupReference* gref = group->GetFirstMember (); gref; gref = gref->next ())
41044124 {
41054125 Player* member = gref->GetSource ();
4106- if (!member || member == bot || !member->IsInWorld () ||
4107- !member->IsInSameRaidWith (bot))
4126+ if (!member || member == bot || !member->IsInWorld () || !member->IsInSameRaidWith (bot))
41084127 continue ;
41094128
41104129 PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI (member);
@@ -4339,6 +4358,11 @@ inline bool ZoneHasRealPlayers(Player* bot)
43394358
43404359bool PlayerbotAI::AllowActive (ActivityType activityType)
43414360{
4361+ // Early return if bot is in invalid state
4362+ if (!bot || !bot->GetSession () || !bot->IsInWorld () || bot->IsBeingTeleported () ||
4363+ bot->GetSession ()->isLogingOut () || bot->IsDuringRemoveFromWorld ())
4364+ return false ;
4365+
43424366 // when botActiveAlone is 100% and smartScale disabled
43434367 if (sPlayerbotAIConfig ->botActiveAlone >= 100 && !sPlayerbotAIConfig ->botActiveAloneSmartScale )
43444368 {
@@ -4429,10 +4453,8 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
44294453 for (GroupReference* gref = group->GetFirstMember (); gref; gref = gref->next ())
44304454 {
44314455 Player* member = gref->GetSource ();
4432- if ((!member || !member->IsInWorld ()) && member->GetMapId () != bot->GetMapId ())
4433- {
4456+ if (!member || !member->IsInWorld () || member->GetMapId () != bot->GetMapId ())
44344457 continue ;
4435- }
44364458
44374459 if (member == bot)
44384460 {
@@ -4483,23 +4505,23 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
44834505 // HasFriend
44844506 if (sPlayerbotAIConfig ->BotActiveAloneForceWhenIsFriend )
44854507 {
4486- if (!bot || !bot->IsInWorld () || !bot->GetGUID ())
4508+ // shouldnt be needed analyse in future
4509+ if (!bot->GetGUID ())
44874510 return false ;
44884511
44894512 for (auto & player : sRandomPlayerbotMgr ->GetPlayers ())
44904513 {
4491- if (!player || !player->IsInWorld ())
4514+ if (!player || !player->GetSession () || !player->IsInWorld () || player->IsDuringRemoveFromWorld () ||
4515+ player->GetSession ()->isLogingOut ())
44924516 continue ;
44934517
4494- Player* connectedPlayer = ObjectAccessor::FindPlayer (player-> GetGUID () );
4495- if (!connectedPlayer )
4518+ PlayerbotAI* playerAI = GET_PLAYERBOT_AI (player);
4519+ if (!playerAI || !playerAI-> IsRealPlayer () )
44964520 continue ;
44974521
4522+ // if a real player has the bot as a friend
44984523 PlayerSocial* social = player->GetSocial ();
4499- if (!social)
4500- continue ;
4501-
4502- if (social->HasFriend (bot->GetGUID ()))
4524+ if (social && social->HasFriend (bot->GetGUID ()))
45034525 return true ;
45044526 }
45054527 }
@@ -4513,7 +4535,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
45134535 }
45144536 }
45154537
4516- // Bots don't need to move using PathGenerator.
4538+ // Bots don't need react to PathGenerator activities
45174539 if (activityType == DETAILED_MOVE_ACTIVITY )
45184540 {
45194541 return false ;
@@ -4549,15 +4571,25 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
45494571
45504572bool PlayerbotAI::AllowActivity (ActivityType activityType, bool checkNow)
45514573{
4552- if (!allowActiveCheckTimer[activityType])
4553- allowActiveCheckTimer[activityType] = time (nullptr );
4574+ const int activityIndex = static_cast <int >(activityType);
45544575
4555- if (!checkNow && time (nullptr ) < (allowActiveCheckTimer[activityType] + 5 ))
4556- return allowActive[activityType];
4576+ // Unknown/out-of-range avoid blocking, added logging for further analysing should not happen in the first place.
4577+ if (activityIndex <= 0 || activityIndex >= MAX_ACTIVITY_TYPE )
4578+ {
4579+ LOG_ERROR (" playerbots" , " AllowActivity received invalid activity type value: {}" , activityIndex);
4580+ return true ;
4581+ }
4582+
4583+ if (!allowActiveCheckTimer[activityIndex])
4584+ allowActiveCheckTimer[activityIndex] = time (nullptr );
4585+
4586+ if (!checkNow && time (nullptr ) < (allowActiveCheckTimer[activityIndex] + 5 ))
4587+ return allowActive[activityIndex];
4588+
4589+ const bool allowed = AllowActive (activityType);
4590+ allowActive[activityIndex] = allowed;
4591+ allowActiveCheckTimer[activityIndex] = time (nullptr );
45574592
4558- bool allowed = AllowActive (activityType);
4559- allowActive[activityType] = allowed;
4560- allowActiveCheckTimer[activityType] = time (nullptr );
45614593 return allowed;
45624594}
45634595
@@ -5341,15 +5373,13 @@ Item* PlayerbotAI::FindStoneFor(Item* weapon) const
53415373 if (!item_template)
53425374 return nullptr ;
53435375
5344- static const std::vector<uint32_t > uPrioritizedSharpStoneIds = {
5345- ADAMANTITE_SHARPENING_STONE , FEL_SHARPENING_STONE , ELEMENTAL_SHARPENING_STONE , DENSE_SHARPENING_STONE ,
5346- SOLID_SHARPENING_STONE , HEAVY_SHARPENING_STONE , COARSE_SHARPENING_STONE , ROUGH_SHARPENING_STONE
5347- };
5376+ static const std::vector<uint32_t > uPrioritizedSharpStoneIds = {
5377+ ADAMANTITE_SHARPENING_STONE , FEL_SHARPENING_STONE , ELEMENTAL_SHARPENING_STONE , DENSE_SHARPENING_STONE ,
5378+ SOLID_SHARPENING_STONE , HEAVY_SHARPENING_STONE , COARSE_SHARPENING_STONE , ROUGH_SHARPENING_STONE };
53485379
5349- static const std::vector<uint32_t > uPrioritizedWeightStoneIds = {
5350- ADAMANTITE_WEIGHTSTONE , FEL_WEIGHTSTONE , DENSE_WEIGHTSTONE , SOLID_WEIGHTSTONE ,
5351- HEAVY_WEIGHTSTONE , COARSE_WEIGHTSTONE , ROUGH_WEIGHTSTONE
5352- };
5380+ static const std::vector<uint32_t > uPrioritizedWeightStoneIds = {
5381+ ADAMANTITE_WEIGHTSTONE , FEL_WEIGHTSTONE , DENSE_WEIGHTSTONE , SOLID_WEIGHTSTONE ,
5382+ HEAVY_WEIGHTSTONE , COARSE_WEIGHTSTONE , ROUGH_WEIGHTSTONE };
53535383
53545384 Item* stone = nullptr ;
53555385 ItemTemplate const * pProto = weapon->GetTemplate ();
@@ -5385,7 +5415,6 @@ static const std::vector<uint32_t> uPrioritizedWeightStoneIds = {
53855415
53865416Item* PlayerbotAI::FindOilFor (Item* weapon) const
53875417{
5388-
53895418 if (!weapon)
53905419 return nullptr ;
53915420
@@ -5394,12 +5423,12 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
53945423 return nullptr ;
53955424
53965425 static const std::vector<uint32_t > uPrioritizedWizardOilIds = {
5397- BRILLIANT_WIZARD_OIL , SUPERIOR_WIZARD_OIL , WIZARD_OIL , LESSER_WIZARD_OIL , MINOR_WIZARD_OIL ,
5398- BRILLIANT_MANA_OIL , SUPERIOR_MANA_OIL , LESSER_MANA_OIL , MINOR_MANA_OIL };
5426+ BRILLIANT_WIZARD_OIL , SUPERIOR_WIZARD_OIL , WIZARD_OIL , LESSER_WIZARD_OIL , MINOR_WIZARD_OIL ,
5427+ BRILLIANT_MANA_OIL , SUPERIOR_MANA_OIL , LESSER_MANA_OIL , MINOR_MANA_OIL };
53995428
54005429 static const std::vector<uint32_t > uPrioritizedManaOilIds = {
5401- BRILLIANT_MANA_OIL , SUPERIOR_MANA_OIL , LESSER_MANA_OIL , MINOR_MANA_OIL ,
5402- BRILLIANT_WIZARD_OIL , SUPERIOR_WIZARD_OIL , WIZARD_OIL , LESSER_WIZARD_OIL , MINOR_WIZARD_OIL };
5430+ BRILLIANT_MANA_OIL , SUPERIOR_MANA_OIL , LESSER_MANA_OIL , MINOR_MANA_OIL , BRILLIANT_WIZARD_OIL ,
5431+ SUPERIOR_WIZARD_OIL , WIZARD_OIL , LESSER_WIZARD_OIL , MINOR_WIZARD_OIL };
54035432
54045433 Item* oil = nullptr ;
54055434 int botClass = bot->getClass ();
@@ -5415,22 +5444,22 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
54155444 prioritizedOils = &uPrioritizedWizardOilIds;
54165445 break ;
54175446 case CLASS_DRUID :
5418- if (specTab == 0 ) // Balance
5447+ if (specTab == 0 ) // Balance
54195448 prioritizedOils = &uPrioritizedWizardOilIds;
5420- else if (specTab == 1 ) // Feral
5449+ else if (specTab == 1 ) // Feral
54215450 prioritizedOils = nullptr ;
5422- else // Restoration (specTab == 2) or any other/unspecified spec
5451+ else // Restoration (specTab == 2) or any other/unspecified spec
54235452 prioritizedOils = &uPrioritizedManaOilIds;
54245453 break ;
54255454 case CLASS_HUNTER :
54265455 prioritizedOils = &uPrioritizedManaOilIds;
54275456 break ;
54285457 case CLASS_PALADIN :
5429- if (specTab == 1 ) // Protection
5458+ if (specTab == 1 ) // Protection
54305459 prioritizedOils = &uPrioritizedWizardOilIds;
5431- else if (specTab == 2 ) // Retribution
5460+ else if (specTab == 2 ) // Retribution
54325461 prioritizedOils = nullptr ;
5433- else // Holy (specTab == 0) or any other/unspecified spec
5462+ else // Holy (specTab == 0) or any other/unspecified spec
54345463 prioritizedOils = &uPrioritizedManaOilIds;
54355464 break ;
54365465 default :
0 commit comments