Skip to content

Commit 4b7b095

Browse files
authored
Merge pull request mod-playerbots#2205 from mod-playerbots/test-staging
Test staging to master
2 parents 299e439 + a473432 commit 4b7b095

32 files changed

Lines changed: 1500 additions & 1755 deletions

PULL_REQUEST_TEMPLATE.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,21 @@ any impact on performance, you may skip these question. If necessary, a maintain
4343
## Impact Assessment
4444
<!-- As a generic test, before and after measure of pmon (playerbot pmon tick) can help you here. -->
4545
- Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots?
46-
- [ ] No, not at all
47-
- [ ] Minimal impact (**explain below**)
48-
- [ ] Moderate impact (**explain below**)
46+
- - [ ] No, not at all
47+
- - [ ] Minimal impact (**explain below**)
48+
- - [ ] Moderate impact (**explain below**)
4949

5050

5151

5252
- Does this change modify default bot behavior?
53-
- [ ] No
54-
- [ ] Yes (**explain why**)
53+
- - [ ] No
54+
- - [ ] Yes (**explain why**)
5555

5656

5757

5858
- Does this change add new decision branches or increase maintenance complexity?
59-
- [ ] No
60-
- [ ] Yes (**explain below**)
59+
- - [ ] No
60+
- - [ ] Yes (**explain below**)
6161

6262

6363

@@ -68,8 +68,8 @@ the message is in a translatable format, and list in the table the message_key a
6868
Search for GetBotTextOrDefault in the codebase for examples.
6969
-->
7070
Does this change add bot messages to translate?
71-
- [ ] No
72-
- [ ] Yes (**list messages in the table**)
71+
- - [ ] No
72+
- - [ ] Yes (**list messages in the table**)
7373

7474
| Message key | Default message |
7575
| --------------- | ------------------ |
@@ -82,8 +82,8 @@ AI assistance is allowed, but all submitted code must be fully understood, revie
8282
We expect contributors to be honest about what they do and do not understand.
8383
-->
8484
Was AI assistance used while working on this change?
85-
- [ ] No
86-
- [ ] Yes (**explain below**)
85+
- - [ ] No
86+
- - [ ] Yes (**explain below**)
8787
<!--
8888
If yes, please specify:
8989
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code generation).
@@ -94,10 +94,10 @@ If yes, please specify:
9494

9595
## Final Checklist
9696

97-
- [ ] Stability is not compromised.
98-
- [ ] Performance impact is understood, tested, and acceptable.
99-
- [ ] Added logic complexity is justified and explained.
100-
- [ ] Documentation updated if needed (Conf comments, WiKi commands).
97+
- - [ ] Stability is not compromised.
98+
- - [ ] Performance impact is understood, tested, and acceptable.
99+
- - [ ] Added logic complexity is justified and explained.
100+
- - [ ] Documentation updated if needed (Conf comments, WiKi commands).
101101

102102
## Notes for Reviewers
103103
<!-- Anything else that's helpful to review or test your pull request. -->

src/Ai/Base/Actions/FollowActions.cpp

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,211 @@
55

66
#include "FollowActions.h"
77

8+
#include <algorithm>
9+
#include <cmath>
10+
#include <array>
11+
812
#include "Event.h"
913
#include "Formations.h"
1014
#include "LastMovementValue.h"
15+
#include "MotionMaster.h"
1116
#include "PlayerbotAI.h"
1217
#include "Playerbots.h"
1318
#include "ServerFacade.h"
19+
#include "Transport.h"
20+
#include "Map.h"
21+
22+
namespace
23+
{
24+
Transport* GetTransportForPosTolerant(Map* map, WorldObject* ref, uint32 phaseMask, float x, float y, float z)
25+
{
26+
if (!map || !ref)
27+
return nullptr;
28+
29+
std::array<float, 4> const probes = { z, z + 0.5f, z + 1.5f, z - 0.5f };
30+
for (float const pz : probes)
31+
{
32+
if (Transport* t = map->GetTransportForPos(phaseMask, x, y, pz, ref))
33+
return t;
34+
}
35+
36+
return nullptr;
37+
}
38+
39+
// Attempts to find a point on the leader's transport that is closer to the bot,
40+
// by probing along the segment from master -> bot and returning the last point
41+
// that is still detected as being on the expected transport.
42+
bool FindBoardingPointOnTransport(Map* map, Transport* expectedTransport, WorldObject* ref,
43+
float masterX, float masterY, float masterZ,
44+
float botX, float botY, float botZ,
45+
float& outX, float& outY, float& outZ)
46+
{
47+
if (!map || !expectedTransport || !ref)
48+
return false;
49+
50+
uint32 const phaseMask = ref->GetPhaseMask();
51+
52+
// Ensure master is actually detected on that transport (tolerant).
53+
if (GetTransportForPosTolerant(map, ref, phaseMask, masterX, masterY, masterZ) != expectedTransport)
54+
return false;
55+
56+
// The raycast in GetTransportForPos starts at (z + 2). Probe with a safe Z.
57+
float const probeZ = std::max(masterZ, botZ);
58+
59+
// Adaptive step count: small platforms need tighter sampling.
60+
float const dx2 = botX - masterX;
61+
float const dy2 = botY - masterY;
62+
float const dist2d = std::sqrt(dx2 * dx2 + dy2 * dy2);
63+
int32 const steps = std::clamp(static_cast<int32>(dist2d / 0.75f), 10, 28);
64+
65+
float const dx = (botX - masterX) / static_cast<float>(steps);
66+
float const dy = (botY - masterY) / static_cast<float>(steps);
67+
68+
// Master must actually be on the expected transport for this to work.
69+
if (map->GetTransportForPos(ref->GetPhaseMask(), masterX, masterY, probeZ, ref) != expectedTransport)
70+
return false;
71+
72+
float lastX = masterX;
73+
float lastY = masterY;
74+
bool found = false;
75+
76+
for (int32 i = 1; i <= steps; ++i)
77+
{
78+
float const px = masterX + dx * i;
79+
float const py = masterY + dy * i;
80+
81+
Transport* const t = GetTransportForPosTolerant(map, ref, phaseMask, px, py, probeZ);
82+
if (t != expectedTransport)
83+
break;
84+
85+
lastX = px;
86+
lastY = py;
87+
found = true;
88+
}
89+
90+
if (!found)
91+
return false;
92+
93+
outX = lastX;
94+
outY = lastY;
95+
outZ = masterZ; // keep deck-level Z to encourage stepping onto the platform/boat
96+
return true;
97+
}
98+
}
1499

15100
bool FollowAction::Execute(Event /*event*/)
16101
{
17102
Formation* formation = AI_VALUE(Formation*, "formation");
18103
std::string const target = formation->GetTargetName();
19104

105+
// Transport handling for moving transports only (boats/zeppelins).
106+
Player* master = botAI->GetMaster();
107+
if (master && master->IsInWorld() && bot->IsInWorld() && bot->GetMapId() == master->GetMapId())
108+
{
109+
Map* map = master->GetMap();
110+
uint32 const mapId = bot->GetMapId();
111+
Transport* transport = nullptr;
112+
bool masterOnTransport = false;
113+
114+
if (master->GetTransport())
115+
{
116+
transport = master->GetTransport();
117+
masterOnTransport = true;
118+
}
119+
else if (map)
120+
{
121+
transport = GetTransportForPosTolerant(map, master, master->GetPhaseMask(),
122+
master->GetPositionX(), master->GetPositionY(), master->GetPositionZ());
123+
masterOnTransport = (transport != nullptr);
124+
}
125+
126+
// Ignore static transports (elevators/trams): only keep boats/zeppelins here.
127+
if (transport && transport->IsStaticTransport())
128+
transport = nullptr;
129+
130+
if (transport && map && bot->GetTransport() != transport)
131+
{
132+
float const botProbeZ = std::max(bot->GetPositionZ(), transport->GetPositionZ());
133+
Transport* botSurfaceTransport = GetTransportForPosTolerant(map, bot, bot->GetPhaseMask(),
134+
bot->GetPositionX(), bot->GetPositionY(), botProbeZ);
135+
136+
if (botSurfaceTransport == transport)
137+
{
138+
transport->AddPassenger(bot, true);
139+
bot->StopMovingOnCurrentPos();
140+
return true;
141+
}
142+
143+
float const boardingAssistDistance = 60.0f;
144+
float const dist2d = ServerFacade::instance().GetDistance2d(bot, master);
145+
bool const inAssist = ServerFacade::instance().IsDistanceLessOrEqualThan(dist2d, boardingAssistDistance);
146+
147+
if (inAssist)
148+
{
149+
float destX = masterOnTransport ? master->GetPositionX() : transport->GetPositionX();
150+
float destY = masterOnTransport ? master->GetPositionY() : transport->GetPositionY();
151+
float destZ = masterOnTransport ? master->GetPositionZ() : transport->GetPositionZ();
152+
float edgeX = 0.0f;
153+
float edgeY = 0.0f;
154+
float edgeZ = 0.0f;
155+
156+
if (masterOnTransport &&
157+
FindBoardingPointOnTransport(map, transport, master,
158+
master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(),
159+
bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
160+
edgeX, edgeY, edgeZ))
161+
{
162+
destX = edgeX;
163+
destY = edgeY;
164+
destZ = edgeZ;
165+
}
166+
167+
MovementPriority const priority = botAI->GetState() == BOT_STATE_COMBAT
168+
? MovementPriority::MOVEMENT_COMBAT
169+
: MovementPriority::MOVEMENT_NORMAL;
170+
171+
bool const movingAllowed = IsMovingAllowed(mapId, destX, destY, destZ);
172+
bool const dupMove = IsDuplicateMove(mapId, destX, destY, destZ);
173+
bool const waiting = IsWaitingForLastMove(priority);
174+
175+
if (movingAllowed && !dupMove && !waiting)
176+
{
177+
if (bot->IsSitState())
178+
bot->SetStandState(UNIT_STAND_STATE_STAND);
179+
180+
if (bot->IsNonMeleeSpellCast(true))
181+
{
182+
bot->CastStop();
183+
botAI->InterruptSpell();
184+
}
185+
186+
if (MotionMaster* mm = bot->GetMotionMaster())
187+
{
188+
mm->MovePoint(
189+
/*id*/ 0,
190+
/*coords*/ destX, destY, destZ,
191+
/*forcedMovement*/ FORCED_MOVEMENT_NONE,
192+
/*speed*/ 0.0f,
193+
/*orientation*/ 0.0f,
194+
/*generatePath*/ false,
195+
/*forceDestination*/ false);
196+
}
197+
else
198+
return false;
199+
200+
float delay = 1000.0f * MoveDelay(bot->GetExactDist(destX, destY, destZ));
201+
delay = std::clamp(delay, 0.0f, static_cast<float>(sPlayerbotAIConfig.maxWaitForMove));
202+
203+
AI_VALUE(LastMovement&, "last movement")
204+
.Set(mapId, destX, destY, destZ, bot->GetOrientation(), delay, priority);
205+
ClearIdleState();
206+
return true;
207+
}
208+
}
209+
}
210+
}
211+
// end unified transport handling
212+
20213
bool moved = false;
21214
if (!target.empty())
22215
{

src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ class GrobbulusRotateAction : public RotateAroundTheCenterPointAction
3737
uint32 GetCurrWaypoint() override;
3838
};
3939

40-
class GrobblulusMoveCenterAction : public MoveInsideAction
40+
class GrobbulusMoveCenterAction : public MoveInsideAction
4141
{
4242
public:
43-
GrobblulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {}
43+
GrobbulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {}
4444
};
4545

4646
class GrobbulusMoveAwayAction : public MovementAction
@@ -173,26 +173,26 @@ class RazuviousTargetAction : public AttackAction
173173
RazuviousBossHelper helper;
174174
};
175175

176-
class HorsemanAttractAlternativelyAction : public AttackAction
176+
class FourHorsemenAttractAlternativelyAction : public AttackAction
177177
{
178178
public:
179-
HorsemanAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attract alternatively"), helper(ai)
179+
FourHorsemenAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "four horsemen attract alternatively"), helper(ai)
180180
{
181181
}
182182
bool Execute(Event event) override;
183183

184184
protected:
185-
FourhorsemanBossHelper helper;
185+
FourHorsemenBossHelper helper;
186186
};
187187

188-
class HorsemanAttactInOrderAction : public AttackAction
188+
class FourHorsemenAttackInOrderAction : public AttackAction
189189
{
190190
public:
191-
HorsemanAttactInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attact in order"), helper(ai) {}
191+
FourHorsemenAttackInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "four horsemen attack in order"), helper(ai) {}
192192
bool Execute(Event event) override;
193193

194194
protected:
195-
FourhorsemanBossHelper helper;
195+
FourHorsemenBossHelper helper;
196196
};
197197

198198
// class SapphironGroundMainTankPositionAction : public MovementAction

src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Anubrekhan.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
#include "RaidNaxxActions.h"
2-
31
#include "ObjectGuid.h"
42
#include "Playerbots.h"
3+
#include "RaidNaxxActions.h"
54

65
bool AnubrekhanChooseTargetAction::Execute(Event /*event*/)
76
{
@@ -66,13 +65,10 @@ bool AnubrekhanPositionAction::Execute(Event /*event*/)
6665
{
6766
uint32 nearest = FindNearestWaypoint();
6867
uint32 next_point;
69-
if (inPhase)
70-
next_point = (nearest + 1) % intervals;
71-
else
72-
next_point = nearest;
68+
next_point = (nearest + 1) % intervals;
7369

74-
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), false, false,
75-
false, false, MovementPriority::MOVEMENT_COMBAT);
70+
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second,
71+
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
7672
}
7773
else
7874
return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT);

src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_FourHorseman.cpp renamed to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_FourHorsemen.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#include "Playerbots.h"
44

5-
bool HorsemanAttractAlternativelyAction::Execute(Event /*event*/)
5+
bool FourHorsemenAttractAlternativelyAction::Execute(Event /*event*/)
66
{
77
if (!helper.UpdateBossAI())
88
return false;
@@ -13,13 +13,13 @@ bool HorsemanAttractAlternativelyAction::Execute(Event /*event*/)
1313
return true;
1414

1515
Unit* attackTarget = helper.CurrentAttackTarget();
16-
if (context->GetValue<Unit*>("current target")->Get() != attackTarget)
16+
if (attackTarget && context->GetValue<Unit*>("current target")->Get() != attackTarget)
1717
return Attack(attackTarget);
1818

1919
return false;
2020
}
2121

22-
bool HorsemanAttactInOrderAction::Execute(Event /*event*/)
22+
bool FourHorsemenAttackInOrderAction::Execute(Event /*event*/)
2323
{
2424
if (!helper.UpdateBossAI())
2525
return false;

src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Sapphiron.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt()
7070
if (!group)
7171
return false;
7272

73-
Group::MemberSlotList const& slots = group->GetMemberSlots();
7473
Player* playerWithIcebolt = nullptr;
7574
float minDistance;
7675
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())

0 commit comments

Comments
 (0)