Files
mod-playerbots/src/Ai/Base/Trigger/StuckTriggers.cpp
Alex Dcnh 17b8d7f68b Stage1 refactor world position method names (#2126)
# Pull Request

This change replaces the non‑standard
WorldPosition::getX/getY/getZ/getO/getMapId wrappers with the core
getters (GetPositionX/Y/Z, GetOrientation, GetMapId) and removes the
redundant wrappers.
Goal: align the module with AzerothCore conventions, reduce local
adapters, and improve long‑term maintainability.

---

## Design Philosophy

This is a structural cleanup only (coordinate access) and does not alter
any AI behavior or decision logic.
It follows the stability/performance-first philosophy and does not add
branches or extra runtime work.

Before submitting: yes, this change aligns with the principles of
stability, performance, and predictability.

Principles:

- **Stability before intelligence**  
  A stable system is always preferred over a smarter one.

- **Performance is a shared resource**  
  Any increase in bot cost affects all players and all bots.

- **Simple logic scales better than smart logic**  
Predictable behavior under load is more valuable than perfect decisions.

- **Complexity must justify itself**  
  If a feature cannot clearly explain its cost, it should not exist.

- **Defaults must be cheap**  
  Expensive behavior must always be optional and clearly communicated.

- **Bots should look reasonable, not perfect**  
  The goal is believable behavior, not human simulation.

Before submitting, confirm that this change aligns with those
principles.

---

## Feature Evaluation

Please answer the following:

- Minimum logic required: use core getters (GetPositionX/Y/Z, GetMapId,
GetOrientation) wherever coordinates are needed.
- Cheapest implementation: direct call replacement and removal of
redundant wrappers.
- Runtime cost: negligible (same data access, no additional logic).

---

## How to Test the Changes

- No functional testing required (behavior‑neutral refactor).
- Recommended: compile the module and run a normal server startup as
validation.

## Complexity & Impact

Does this change add new decision branches?
- - [x] No
- - [x] Yes (**explain below**)

Does this change increase per-bot or per-tick processing?
- - [x] No
- - [ ] Yes (**describe and justify impact**)

Could this logic scale poorly under load?
- - [x] No
- - [ ] Yes (**explain why**)
---

## Defaults & Configuration

Does this change modify default bot behavior?
- - [x] No
- - [ ] Yes (**explain why**)

If this introduces more advanced or AI-heavy logic:
- - [x] Lightweight mode remains the default
- - [x] More complex behavior is optional and thereby configurable
---

## AI Assistance

Was AI assistance (e.g. ChatGPT or similar tools) used while working on
this change?
- - [ ] No
- - [x] Yes (**explain below**)

If yes, please specify:

- AI tool or model used: Copilot
- Purpose of usage: Translate this PR text from french to English

---

## Final Checklist

- - [x] Stability is not compromised
- - [x] Performance impact is understood, tested, and acceptable
- - [x] Added logic complexity is justified and explained
- - [x] Documentation updated if needed

---

## Notes for Reviewers

This is a core-friendly cleanup only, with no behavioral change.
No additional logic or CPU cost is introduced.
2026-02-13 09:24:42 -08:00

200 lines
5.8 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "StuckTriggers.h"
#include "CellImpl.h"
#include "PathGenerator.h"
#include "Playerbots.h"
#include "MMapFactory.h"
bool MoveStuckTrigger::IsActive()
{
if (botAI->HasActivePlayerMaster())
return false;
if (!botAI->AllowActivity(ALL_ACTIVITY))
return false;
WorldPosition botPos(bot);
LogCalculatedValue<WorldPosition>* posVal =
dynamic_cast<LogCalculatedValue<WorldPosition>*>(context->GetUntypedValue("current position"));
if (posVal->LastChangeDelay() > 5 * MINUTE)
{
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in the same position for {} seconds",
// bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
// bot->GetName(), posVal->LastChangeDelay());
return true;
}
bool longLog = false;
for (auto tPos : posVal->ValueLog())
{
uint32 timePassed = time(0) - tPos.second;
if (timePassed > 10 * MINUTE)
{
if (botPos.fDist(tPos.first) > 50.0f)
return false;
longLog = true;
}
}
if (longLog)
{
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in the same position for 10mins",
// bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
// bot->GetName(), posVal->LastChangeDelay());
}
return longLog;
}
bool MoveLongStuckTrigger::IsActive()
{
if (botAI->HasActivePlayerMaster())
return false;
if (!botAI->AllowActivity(ALL_ACTIVITY))
return false;
WorldPosition botPos(bot);
Cell cell(bot->GetPositionX(), bot->GetPositionY());
GridCoord grid = botPos.getGridCoord();
if (grid.x_coord < 0 || grid.x_coord >= MAX_NUMBER_OF_GRIDS)
{
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in grid {},{} on map {}",
// bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
// bot->GetName(), grid.x_coord, grid.y_coord, botPos.getMapId());
return true;
}
if (grid.y_coord < 0 || grid.y_coord >= MAX_NUMBER_OF_GRIDS)
{
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in grid {},{} on map {}",
// bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
// bot->GetName(), grid.x_coord, grid.y_coord, botPos.getMapId());
return true;
}
if (cell.GridX() > 0 && cell.GridY() > 0 &&
!MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(botPos.GetMapId(), cell.GridX(), cell.GridY()))
{
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in unloaded grid {},{} on map {}",
// bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
// bot->GetName(), grid.x_coord, grid.y_coord, botPos.getMapId());
return true;
}
LogCalculatedValue<WorldPosition>* posVal =
dynamic_cast<LogCalculatedValue<WorldPosition>*>(context->GetUntypedValue("current position"));
if (posVal->LastChangeDelay() > 10 * MINUTE)
{
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in the same position for {} seconds",
// bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
// bot->GetName(), posVal->LastChangeDelay());
return true;
}
MemoryCalculatedValue<uint32>* expVal =
dynamic_cast<MemoryCalculatedValue<uint32>*>(context->GetUntypedValue("experience"));
if (expVal->LastChangeDelay() < 15 * MINUTE)
return false;
bool longLog = false;
for (auto tPos : posVal->ValueLog())
{
uint32 timePassed = time(0) - tPos.second;
if (timePassed > 15 * MINUTE)
{
if (botPos.fDist(tPos.first) > 50.0f)
return false;
longLog = true;
}
}
if (longLog)
{
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in the same position for 15mins",
// bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
// bot->GetName(), posVal->LastChangeDelay());
}
return longLog;
}
bool CombatStuckTrigger::IsActive()
{
if (!bot->IsInCombat())
return false;
if (botAI->HasActivePlayerMaster())
return false;
if (!botAI->AllowActivity(ALL_ACTIVITY))
return false;
WorldPosition botPos(bot);
MemoryCalculatedValue<bool>* combatVal =
dynamic_cast<MemoryCalculatedValue<bool>*>(context->GetUntypedValue("combat::self target"));
if (combatVal->LastChangeDelay() > 5 * MINUTE)
{
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in combat for {} seconds",
// bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
// bot->GetName(), posVal->LastChangeDelay());
return true;
}
return false;
}
bool CombatLongStuckTrigger::IsActive()
{
if (!bot->IsInCombat())
return false;
if (botAI->HasActivePlayerMaster())
return false;
if (!botAI->AllowActivity(ALL_ACTIVITY))
return false;
WorldPosition botPos(bot);
MemoryCalculatedValue<bool>* combatVal =
dynamic_cast<MemoryCalculatedValue<bool>*>(context->GetUntypedValue("combat::self target"));
if (combatVal->LastChangeDelay() > 15 * MINUTE)
{
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in combat for {} seconds",
// bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
// bot->GetName(), posVal->LastChangeDelay());
return true;
}
return false;
}