mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Compare commits
11 Commits
91b217c962
...
cee4a067fa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cee4a067fa | ||
|
|
04af497cd1 | ||
|
|
6b07f9f9c2 | ||
|
|
bbef216838 | ||
|
|
9715c5d214 | ||
|
|
9b335bf6cf | ||
|
|
e60e5add9c | ||
|
|
cbba63acd1 | ||
|
|
86100aa66b | ||
|
|
2e20210fad | ||
|
|
db0b276f43 |
10
README.md
10
README.md
@ -48,6 +48,16 @@ Then build the server following the platform-specific instructions in our **[Ins
|
|||||||
|
|
||||||
> **Testing branch:** A `test-staging` branch is available with the latest features and fixes before they are merged into `master`. To use it, clone with `--branch=test-staging` instead. Note that this branch may contain unstable or breaking changes — use it at your own risk and only if you are comfortable troubleshooting issues.
|
> **Testing branch:** A `test-staging` branch is available with the latest features and fixes before they are merged into `master`. To use it, clone with `--branch=test-staging` instead. Note that this branch may contain unstable or breaking changes — use it at your own risk and only if you are comfortable troubleshooting issues.
|
||||||
|
|
||||||
|
### Required server configuration
|
||||||
|
|
||||||
|
In `worldserver.conf` (AzerothCore core config), set:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
PreloadAllNonInstancedMapGrids = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
This is required for `mod-playerbots`.
|
||||||
|
|
||||||
### Detailed Guides
|
### Detailed Guides
|
||||||
|
|
||||||
| Guide | Description |
|
| Guide | Description |
|
||||||
|
|||||||
@ -343,10 +343,6 @@ AiPlayerbot.MaxWaitForMove = 5000
|
|||||||
# 2 - MoveSplinePath disabled everywhere
|
# 2 - MoveSplinePath disabled everywhere
|
||||||
AiPlayerbot.DisableMoveSplinePath = 0
|
AiPlayerbot.DisableMoveSplinePath = 0
|
||||||
|
|
||||||
# Max search time for movement (higher for better movement on slopes)
|
|
||||||
# Default: 3
|
|
||||||
AiPlayerbot.MaxMovementSearchTime = 3
|
|
||||||
|
|
||||||
# Action expiration time
|
# Action expiration time
|
||||||
AiPlayerbot.ExpireActionTime = 5000
|
AiPlayerbot.ExpireActionTime = 5000
|
||||||
|
|
||||||
|
|||||||
@ -385,32 +385,29 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
float modifiedZ;
|
// Direct dispatch — engine MovePoint(generatePath=true) handles
|
||||||
Movement::PointsArray path =
|
// path-finding internally. Previously called SearchForBestPath
|
||||||
SearchForBestPath(x, y, z, modifiedZ, sPlayerbotAIConfig.maxMovementSearchTime, normal_only);
|
// here to probe ±step around the target z; that helped find
|
||||||
if (modifiedZ == INVALID_HEIGHT)
|
// polygons when the input z was several yards off the navmesh,
|
||||||
return false;
|
// but its "shortest path" preference would shift modifiedZ to
|
||||||
float distance = bot->GetExactDist(x, y, modifiedZ);
|
// an unreachable nearby polygon (upper terrace, ledge above)
|
||||||
|
// and then the engine's straight-spline NOPATH fallback would
|
||||||
|
// air-walk the bot up to it. cmangos doesn't have an
|
||||||
|
// equivalent — single-z PathFinder call is sufficient.
|
||||||
|
float distance = bot->GetExactDist(x, y, z);
|
||||||
if (distance > 0.01f)
|
if (distance > 0.01f)
|
||||||
{
|
{
|
||||||
if (bot->IsSitState())
|
if (bot->IsSitState())
|
||||||
bot->SetStandState(UNIT_STAND_STATE_STAND);
|
bot->SetStandState(UNIT_STAND_STATE_STAND);
|
||||||
|
|
||||||
// if (bot->IsNonMeleeSpellCast(true))
|
DoMovePoint(bot, x, y, z, generatePath, backwards);
|
||||||
// {
|
|
||||||
// bot->CastStop();
|
|
||||||
// botAI->InterruptSpell();
|
|
||||||
// }
|
|
||||||
DoMovePoint(bot, x, y, modifiedZ, generatePath, backwards);
|
|
||||||
float delay = 1000.0f * MoveDelay(distance, backwards);
|
float delay = 1000.0f * MoveDelay(distance, backwards);
|
||||||
if (lessDelay)
|
if (lessDelay)
|
||||||
{
|
|
||||||
delay -= botAI->GetReactDelay();
|
delay -= botAI->GetReactDelay();
|
||||||
}
|
|
||||||
delay = std::max(.0f, delay);
|
delay = std::max(.0f, delay);
|
||||||
delay = std::min((float)sPlayerbotAIConfig.maxWaitForMove, delay);
|
delay = std::min((float)sPlayerbotAIConfig.maxWaitForMove, delay);
|
||||||
AI_VALUE(LastMovement&, "last movement")
|
AI_VALUE(LastMovement&, "last movement")
|
||||||
.Set(mapId, x, y, modifiedZ, bot->GetOrientation(), delay, priority);
|
.Set(mapId, x, y, z, bot->GetOrientation(), delay, priority);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1896,75 +1893,6 @@ PathResult MovementAction::GeneratePath(float x, float y, float z, uint32 accept
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Movement::PointsArray MovementAction::SearchForBestPath(float x, float y, float z, float& modified_z,
|
|
||||||
int maxSearchCount, bool normal_only, float step)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
modified_z = INVALID_HEIGHT;
|
|
||||||
float tempZ = bot->GetMapHeight(x, y, z);
|
|
||||||
PathGenerator gen(bot);
|
|
||||||
gen.CalculatePath(x, y, tempZ);
|
|
||||||
Movement::PointsArray result = gen.GetPath();
|
|
||||||
float min_length = gen.getPathLength();
|
|
||||||
int typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE;
|
|
||||||
if ((gen.GetPathType() & typeOk) && abs(tempZ - z) < 0.5f)
|
|
||||||
{
|
|
||||||
modified_z = tempZ;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
// Start searching
|
|
||||||
if (gen.GetPathType() & typeOk)
|
|
||||||
{
|
|
||||||
modified_z = tempZ;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
int count = 1;
|
|
||||||
for (float delta = step; count < maxSearchCount / 2 + 1; count++, delta += step)
|
|
||||||
{
|
|
||||||
tempZ = bot->GetMapHeight(x, y, z + delta);
|
|
||||||
if (tempZ == INVALID_HEIGHT)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
PathGenerator gen(bot);
|
|
||||||
gen.CalculatePath(x, y, tempZ);
|
|
||||||
if ((gen.GetPathType() & typeOk) && gen.getPathLength() < min_length)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
min_length = gen.getPathLength();
|
|
||||||
result = gen.GetPath();
|
|
||||||
modified_z = tempZ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (float delta = -step; count < maxSearchCount; count++, delta -= step)
|
|
||||||
{
|
|
||||||
tempZ = bot->GetMapHeight(x, y, z + delta);
|
|
||||||
if (tempZ == INVALID_HEIGHT)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
PathGenerator gen(bot);
|
|
||||||
gen.CalculatePath(x, y, tempZ);
|
|
||||||
if ((gen.GetPathType() & typeOk) && gen.getPathLength() < min_length)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
min_length = gen.getPathLength();
|
|
||||||
result = gen.GetPath();
|
|
||||||
modified_z = tempZ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found && normal_only)
|
|
||||||
{
|
|
||||||
modified_z = INVALID_HEIGHT;
|
|
||||||
return Movement::PointsArray{};
|
|
||||||
}
|
|
||||||
if (!found && !normal_only)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool generatePath, bool backwards)
|
void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool generatePath, bool backwards)
|
||||||
{
|
{
|
||||||
if (!unit)
|
if (!unit)
|
||||||
|
|||||||
@ -88,7 +88,7 @@ protected:
|
|||||||
bool FleePosition(Position pos, float radius, uint32 minInterval = 1000);
|
bool FleePosition(Position pos, float radius, uint32 minInterval = 1000);
|
||||||
bool CheckLastFlee(float curAngle, std::list<FleeInfo>& infoList);
|
bool CheckLastFlee(float curAngle, std::list<FleeInfo>& infoList);
|
||||||
|
|
||||||
PathResult GeneratePath(float x, float y, float z, uint32 acceptMask = DEFAULT_PATH_ACCEPT_MASK, bool forceDestination = true);
|
PathResult GeneratePath(float x, float y, float z, uint32 acceptMask = DEFAULT_PATH_ACCEPT_MASK, bool forceDestination = false);
|
||||||
|
|
||||||
bool GetTravelPlan(TravelPlan& plan, WorldPosition destination);
|
bool GetTravelPlan(TravelPlan& plan, WorldPosition destination);
|
||||||
bool ExecuteTravelPlan(TravelPlan& state);
|
bool ExecuteTravelPlan(TravelPlan& state);
|
||||||
@ -115,10 +115,6 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// float SearchBestGroundZForPath(float x, float y, float z, bool generatePath, float range = 20.0f, bool
|
|
||||||
// normal_only = false, float step = 8.0f);
|
|
||||||
const Movement::PointsArray SearchForBestPath(float x, float y, float z, float& modified_z, int maxSearchCount = 5,
|
|
||||||
bool normal_only = false, float step = 8.0f);
|
|
||||||
bool wasMovementRestricted = false;
|
bool wasMovementRestricted = false;
|
||||||
void DoMovePoint(Unit* unit, float x, float y, float z, bool generatePath, bool backwards);
|
void DoMovePoint(Unit* unit, float x, float y, float z, bool generatePath, bool backwards);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -314,6 +314,27 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Probe failed or didn't progress — emit visibility whisper so
|
||||||
|
// the user can see WHY mmap didn't dispatch. Without this the
|
||||||
|
// do-quest action's `MoveRandomNear` nudge appears with no
|
||||||
|
// preceding MoveFar whisper, and the failure mode is invisible.
|
||||||
|
{
|
||||||
|
bool const probeProgressed = !probe.empty() && probe.size() >= 2 &&
|
||||||
|
(dest.GetExactDist(probe.back().GetPositionX(),
|
||||||
|
probe.back().GetPositionY(), probe.back().GetPositionZ()) + 5.0f < disToDest);
|
||||||
|
if (!probeProgressed)
|
||||||
|
{
|
||||||
|
char fails[32];
|
||||||
|
snprintf(fails, sizeof(fails), "mF=%d nF=%d",
|
||||||
|
botAI->rpgInfo.CountRecentAttempts(dest, false),
|
||||||
|
botAI->rpgInfo.CountRecentAttempts(dest, true));
|
||||||
|
char const* reason = (probe.empty() || probe.size() < 2) ? "mmap-empty" : "mmap-noprogress";
|
||||||
|
EmitDebugMove("MoveFar", reason,
|
||||||
|
dest.GetPositionX(), dest.GetPositionY(),
|
||||||
|
dest.GetPositionZ(), fails);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Empty / non-progressing path falls back to dispatching the
|
// Empty / non-progressing path falls back to dispatching the
|
||||||
// destination as a single waypoint. Spline only when target is
|
// destination as a single waypoint. Spline only when target is
|
||||||
// line-of-sight: dispatching a straight line through walls
|
// line-of-sight: dispatching a straight line through walls
|
||||||
@ -330,7 +351,16 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
|||||||
forceNodesOverMmap ? "F-nodes " : "",
|
forceNodesOverMmap ? "F-nodes " : "",
|
||||||
bothExhausted ? "EXHAUST " : "");
|
bothExhausted ? "EXHAUST " : "");
|
||||||
if (!inLOS)
|
if (!inLOS)
|
||||||
|
{
|
||||||
|
char fails[32];
|
||||||
|
snprintf(fails, sizeof(fails), "mF=%d nF=%d",
|
||||||
|
botAI->rpgInfo.CountRecentAttempts(dest, false),
|
||||||
|
botAI->rpgInfo.CountRecentAttempts(dest, true));
|
||||||
|
EmitDebugMove("MoveFar", "spline-blocked",
|
||||||
|
dest.GetPositionX(), dest.GetPositionY(),
|
||||||
|
dest.GetPositionZ(), fails);
|
||||||
return false; // Refuse to dispatch a straight line through geometry.
|
return false; // Refuse to dispatch a straight line through geometry.
|
||||||
|
}
|
||||||
{
|
{
|
||||||
char fails[32];
|
char fails[32];
|
||||||
snprintf(fails, sizeof(fails), "mF=%d nF=%d",
|
snprintf(fails, sizeof(fails), "mF=%d nF=%d",
|
||||||
@ -424,7 +454,7 @@ bool NewRpgBaseAction::MoveRandomNear(float moveStep, MovementPriority priority,
|
|||||||
float dy = y + distance * sin(angle);
|
float dy = y + distance * sin(angle);
|
||||||
float dz = z;
|
float dz = z;
|
||||||
|
|
||||||
PathResult path = GeneratePath(dx, dy, dz, RELAXED_PATH_ACCEPT_MASK);
|
PathResult path = GeneratePath(dx, dy, dz, RELAXED_PATH_ACCEPT_MASK, /*forceDestination=*/false);
|
||||||
|
|
||||||
if (!path.reachable)
|
if (!path.reachable)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -739,14 +739,23 @@ std::vector<WorldPosition> WorldPosition::getPathStepFrom(WorldPosition startPos
|
|||||||
if (tempCreature)
|
if (tempCreature)
|
||||||
delete tempCreature;
|
delete tempCreature;
|
||||||
|
|
||||||
// PathType is a bitmask (PathGenerator.h). Detour can return e.g.
|
// PathType is a bitmask. Two things to handle:
|
||||||
// PATHFIND_INCOMPLETE | PATHFIND_FARFROMPOLY_END (0x84) when the
|
//
|
||||||
// destination is a few yards off the nearest polygon — a strict
|
// 1. AC's PathGenerator can return INCOMPLETE | FARFROMPOLY_END
|
||||||
// `== PATHFIND_INCOMPLETE` check would reject the perfectly usable
|
// (0x84) etc. — strict `== PATHFIND_INCOMPLETE` would reject
|
||||||
// partial path and the chained probe would terminate empty on the
|
// these perfectly usable partial paths. Use bitwise to accept
|
||||||
// very first call. PathGenerator's own internal code uses bitwise
|
// NORMAL/INCOMPLETE plus auxiliary flags.
|
||||||
// tests like `!(_type & PATHFIND_INCOMPLETE)`.
|
//
|
||||||
if (type & (PATHFIND_NORMAL | PATHFIND_INCOMPLETE))
|
// 2. AC's PathGenerator at PathGenerator.cpp:177-188 returns
|
||||||
|
// NORMAL | NOT_USING_PATH for player units when start or end
|
||||||
|
// polygon is INVALID_POLYREF (BuildShortcut → 2-point straight
|
||||||
|
// line through whatever's in the way). cmangos by contrast
|
||||||
|
// returns NOPATH for the same case (PathFinder.cpp:437-441).
|
||||||
|
// To match cmangos's intent (never silently dispatch a
|
||||||
|
// geometry-ignoring shortcut), reject any path with the
|
||||||
|
// NOT_USING_PATH bit set.
|
||||||
|
if ((type & (PATHFIND_NORMAL | PATHFIND_INCOMPLETE))
|
||||||
|
&& !(type & PATHFIND_NOT_USING_PATH))
|
||||||
return fromPointsArray(points);
|
return fromPointsArray(points);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@ -71,7 +71,6 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
globalCoolDown = sConfigMgr->GetOption<int32>("AiPlayerbot.GlobalCooldown", 500);
|
globalCoolDown = sConfigMgr->GetOption<int32>("AiPlayerbot.GlobalCooldown", 500);
|
||||||
maxWaitForMove = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxWaitForMove", 5000);
|
maxWaitForMove = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxWaitForMove", 5000);
|
||||||
disableMoveSplinePath = sConfigMgr->GetOption<int32>("AiPlayerbot.DisableMoveSplinePath", 0);
|
disableMoveSplinePath = sConfigMgr->GetOption<int32>("AiPlayerbot.DisableMoveSplinePath", 0);
|
||||||
maxMovementSearchTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxMovementSearchTime", 3);
|
|
||||||
expireActionTime = sConfigMgr->GetOption<int32>("AiPlayerbot.ExpireActionTime", 5000);
|
expireActionTime = sConfigMgr->GetOption<int32>("AiPlayerbot.ExpireActionTime", 5000);
|
||||||
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 700);
|
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 700);
|
||||||
reactDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReactDelay", 100);
|
reactDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReactDelay", 100);
|
||||||
|
|||||||
@ -84,7 +84,7 @@ public:
|
|||||||
bool EnableICCBuffs;
|
bool EnableICCBuffs;
|
||||||
bool allowAccountBots, allowGuildBots, allowTrustedAccountBots;
|
bool allowAccountBots, allowGuildBots, allowTrustedAccountBots;
|
||||||
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
|
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
|
||||||
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, expireActionTime,
|
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, expireActionTime,
|
||||||
dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay;
|
dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay;
|
||||||
bool dynamicReactDelay;
|
bool dynamicReactDelay;
|
||||||
float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance, fleeDistance,
|
float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance, fleeDistance,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user